103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 503831d35Sstevel * Common Development and Distribution License (the "License"). 603831d35Sstevel * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 2103831d35Sstevel 2203831d35Sstevel /* 23*07d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel 2803831d35Sstevel /* 2903831d35Sstevel * Serengeti console driver, see sys/sgcn.h for more information 3003831d35Sstevel * This driver uses the QPAIR form of STREAMS Perimeters to serialize access 3103831d35Sstevel * to the read and write STREAMS queues. 3203831d35Sstevel */ 3303831d35Sstevel 3403831d35Sstevel #include <sys/errno.h> 3503831d35Sstevel #include <sys/stat.h> 3603831d35Sstevel #include <sys/kmem.h> 3703831d35Sstevel #include <sys/conf.h> 3803831d35Sstevel #include <sys/termios.h> 3903831d35Sstevel #include <sys/modctl.h> 4003831d35Sstevel #include <sys/kbio.h> 4103831d35Sstevel #include <sys/stropts.h> 4203831d35Sstevel #include <sys/stream.h> 4303831d35Sstevel #include <sys/strsun.h> 4403831d35Sstevel #include <sys/sysmacros.h> 4503831d35Sstevel #include <sys/promif.h> 4603831d35Sstevel #include <sys/prom_plat.h> 4703831d35Sstevel #include <sys/sgsbbc.h> 4803831d35Sstevel #include <sys/sgsbbc_iosram.h> 4903831d35Sstevel #include <sys/sgcn.h> 5003831d35Sstevel #include <sys/serengeti.h> 5103831d35Sstevel #include <sys/ddi.h> 5203831d35Sstevel #include <sys/sunddi.h> 5303831d35Sstevel #include <sys/strsubr.h> 5403831d35Sstevel 5503831d35Sstevel /* 5603831d35Sstevel * Here we define several macros for accessing console IOSRAM 5703831d35Sstevel */ 5803831d35Sstevel 5903831d35Sstevel #define POINTER(base, field) ((caddr_t)&base.field) 6003831d35Sstevel #define OFFSETOF(base, field) ((caddr_t)&base.field - (caddr_t)&base) 6103831d35Sstevel 6203831d35Sstevel #define RW_CONSOLE_READ 0xAAAA 6303831d35Sstevel #define RW_CONSOLE_WRITE 0xBBBB 6403831d35Sstevel 6503831d35Sstevel #define CONSOLE_READ(buf, len) sgcn_rw(RW_CONSOLE_READ, buf, len) 6603831d35Sstevel #define CONSOLE_WRITE(buf, len) sgcn_rw(RW_CONSOLE_WRITE, buf, len) 6703831d35Sstevel 6803831d35Sstevel #define SGCN_MI_IDNUM 0xABCD 6903831d35Sstevel #define SGCN_MI_HIWAT 2048*2048 7003831d35Sstevel #define SGCN_MI_LOWAT 128 7103831d35Sstevel 7203831d35Sstevel /* dev_ops and cb_ops for device driver */ 7303831d35Sstevel static int sgcn_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 7403831d35Sstevel static int sgcn_attach(dev_info_t *, ddi_attach_cmd_t); 7503831d35Sstevel static int sgcn_detach(dev_info_t *, ddi_detach_cmd_t); 7603831d35Sstevel static int sgcn_open(queue_t *, dev_t *, int, int, cred_t *); 7703831d35Sstevel static int sgcn_close(queue_t *, int, cred_t *); 7803831d35Sstevel static int sgcn_wput(queue_t *, mblk_t *); 7903831d35Sstevel static int sgcn_wsrv(queue_t *); 8003831d35Sstevel static int sgcn_rsrv(queue_t *); 8103831d35Sstevel 8203831d35Sstevel /* interrupt handlers */ 8303831d35Sstevel static void sgcn_data_in_handler(caddr_t); 8403831d35Sstevel static void sgcn_space_2_out_handler(caddr_t); 8503831d35Sstevel static void sgcn_break_handler(caddr_t); 8603831d35Sstevel 8703831d35Sstevel /* other internal sgcn routines */ 8803831d35Sstevel static void sgcn_ioctl(queue_t *, mblk_t *); 8903831d35Sstevel static void sgcn_reioctl(void *); 9003831d35Sstevel static void sgcn_start(void); 9103831d35Sstevel static int sgcn_transmit(queue_t *, mblk_t *); 9203831d35Sstevel static void sgcn_flush(void); 9303831d35Sstevel static int sgcn_read_header(int, cnsram_header *); 9403831d35Sstevel static int sgcn_rw(int, caddr_t, int); 9503831d35Sstevel static void sgcn_log_error(int, int); 9603831d35Sstevel 9703831d35Sstevel /* circular buffer routines */ 9803831d35Sstevel static int circular_buffer_write(int, int, int, int, caddr_t, int); 9903831d35Sstevel static int circular_buffer_read(int, int, int, int, caddr_t, int); 10003831d35Sstevel 10103831d35Sstevel static boolean_t abort_charseq_recognize(uchar_t); 10203831d35Sstevel static void sg_abort_seq_handler(char *); 10303831d35Sstevel 10403831d35Sstevel static sgcn_t *sgcn_state; 10503831d35Sstevel static uchar_t sgcn_stopped = FALSE; 10603831d35Sstevel static int sgcn_timeout_period = 20; /* time out in seconds */ 10703831d35Sstevel 10803831d35Sstevel /* streams structures */ 10903831d35Sstevel static struct module_info minfo = { 11003831d35Sstevel SGCN_MI_IDNUM, /* mi_idnum */ 11103831d35Sstevel "sgcn", /* mi_idname */ 11203831d35Sstevel 0, /* mi_minpsz */ 11303831d35Sstevel INFPSZ, /* mi_maxpsz */ 11403831d35Sstevel SGCN_MI_HIWAT, /* mi_hiwat */ 11503831d35Sstevel SGCN_MI_LOWAT /* mi_lowat */ 11603831d35Sstevel }; 11703831d35Sstevel 11803831d35Sstevel static struct qinit rinit = { 11903831d35Sstevel putq, /* qi_putp */ 12003831d35Sstevel sgcn_rsrv, /* qi_srvp */ 12103831d35Sstevel sgcn_open, /* qi_qopen */ 12203831d35Sstevel sgcn_close, /* qi_qclose */ 12303831d35Sstevel NULL, /* qi_qadmin */ 12403831d35Sstevel &minfo, /* qi_minfo */ 12503831d35Sstevel NULL /* qi_mstat */ 12603831d35Sstevel }; 12703831d35Sstevel 12803831d35Sstevel static struct qinit winit = { 12903831d35Sstevel sgcn_wput, /* qi_putp */ 13003831d35Sstevel sgcn_wsrv, /* qi_srvp */ 13103831d35Sstevel sgcn_open, /* qi_qopen */ 13203831d35Sstevel sgcn_close, /* qi_qclose */ 13303831d35Sstevel NULL, /* qi_qadmin */ 13403831d35Sstevel &minfo, /* qi_minfo */ 13503831d35Sstevel NULL /* qi_mstat */ 13603831d35Sstevel }; 13703831d35Sstevel 13803831d35Sstevel static struct streamtab sgcnstrinfo = { 13903831d35Sstevel &rinit, 14003831d35Sstevel &winit, 14103831d35Sstevel NULL, 14203831d35Sstevel NULL 14303831d35Sstevel }; 14403831d35Sstevel 14503831d35Sstevel /* standard device driver structures */ 14603831d35Sstevel static struct cb_ops sgcn_cb_ops = { 14703831d35Sstevel nulldev, /* open() */ 14803831d35Sstevel nulldev, /* close() */ 14903831d35Sstevel nodev, /* strategy() */ 15003831d35Sstevel nodev, /* print() */ 15103831d35Sstevel nodev, /* dump() */ 15203831d35Sstevel nodev, /* read() */ 15303831d35Sstevel nodev, /* write() */ 15403831d35Sstevel nodev, /* ioctl() */ 15503831d35Sstevel nodev, /* devmap() */ 15603831d35Sstevel nodev, /* mmap() */ 15703831d35Sstevel nodev, /* segmap() */ 15803831d35Sstevel nochpoll, /* poll() */ 15903831d35Sstevel ddi_prop_op, /* prop_op() */ 16003831d35Sstevel &sgcnstrinfo, /* cb_str */ 16103831d35Sstevel D_MP | D_MTQPAIR /* cb_flag */ 16203831d35Sstevel }; 16303831d35Sstevel 16403831d35Sstevel static struct dev_ops sgcn_ops = { 16503831d35Sstevel DEVO_REV, 16603831d35Sstevel 0, /* refcnt */ 16703831d35Sstevel sgcn_getinfo, /* getinfo() */ 16803831d35Sstevel nulldev, /* identify() */ 16903831d35Sstevel nulldev, /* probe() */ 17003831d35Sstevel sgcn_attach, /* attach() */ 17103831d35Sstevel sgcn_detach, /* detach() */ 17203831d35Sstevel nodev, /* reset() */ 17303831d35Sstevel &sgcn_cb_ops, /* cb_ops */ 17403831d35Sstevel (struct bus_ops *)NULL, /* bus_ops */ 17519397407SSherry Moore NULL, /* power() */ 17619397407SSherry Moore ddi_quiesce_not_supported, /* quiesce */ 17703831d35Sstevel }; 17803831d35Sstevel 17903831d35Sstevel static struct modldrv modldrv = { 18003831d35Sstevel &mod_driverops, 18119397407SSherry Moore "Serengeti console driver", 18203831d35Sstevel &sgcn_ops 18303831d35Sstevel }; 18403831d35Sstevel 18503831d35Sstevel static struct modlinkage modlinkage = { 18603831d35Sstevel MODREV_1, 18703831d35Sstevel (void*)&modldrv, 18803831d35Sstevel NULL 18903831d35Sstevel }; 19003831d35Sstevel 19103831d35Sstevel 19203831d35Sstevel /* driver configuration routines */ 19303831d35Sstevel int 19403831d35Sstevel _init(void) 19503831d35Sstevel { 19603831d35Sstevel int error; 19703831d35Sstevel 19803831d35Sstevel sgcn_state = kmem_zalloc(sizeof (sgcn_t), KM_SLEEP); 19903831d35Sstevel 20003831d35Sstevel error = mod_install(&modlinkage); 20103831d35Sstevel 20203831d35Sstevel if (error == 0) { 20303831d35Sstevel mutex_init(&sgcn_state->sgcn_lock, NULL, MUTEX_DRIVER, NULL); 20403831d35Sstevel } else { 20503831d35Sstevel kmem_free(sgcn_state, sizeof (sgcn_t)); 20603831d35Sstevel } 20703831d35Sstevel 20803831d35Sstevel return (error); 20903831d35Sstevel } 21003831d35Sstevel 21103831d35Sstevel int 21203831d35Sstevel _fini(void) 21303831d35Sstevel { 21403831d35Sstevel /* can't remove console driver */ 21503831d35Sstevel return (EBUSY); 21603831d35Sstevel } 21703831d35Sstevel 21803831d35Sstevel int 21903831d35Sstevel _info(struct modinfo *modinfop) 22003831d35Sstevel { 22103831d35Sstevel return (mod_info(&modlinkage, modinfop)); 22203831d35Sstevel } 22303831d35Sstevel 22403831d35Sstevel /* 22503831d35Sstevel * sgcn_attach is called at startup time. 22603831d35Sstevel * There is only once instance of this driver. 22703831d35Sstevel */ 22803831d35Sstevel static int 22903831d35Sstevel sgcn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 23003831d35Sstevel { 23103831d35Sstevel extern int ddi_create_internal_pathname( 23203831d35Sstevel dev_info_t *, char *, int, minor_t); 23303831d35Sstevel cnsram_header header; 23403831d35Sstevel int rv; 23503831d35Sstevel 23603831d35Sstevel if (cmd != DDI_ATTACH) 23703831d35Sstevel return (DDI_FAILURE); 23803831d35Sstevel 23903831d35Sstevel if (ddi_create_internal_pathname(dip, "sgcn", S_IFCHR, 0) 24003831d35Sstevel != DDI_SUCCESS) 24103831d35Sstevel return (DDI_FAILURE); 24203831d35Sstevel 24303831d35Sstevel /* prepare some data structures in soft state */ 24403831d35Sstevel mutex_enter(&sgcn_state->sgcn_lock); 24503831d35Sstevel 24603831d35Sstevel sgcn_state->sgcn_dip = dip; 24703831d35Sstevel 24803831d35Sstevel mutex_exit(&sgcn_state->sgcn_lock); 24903831d35Sstevel 25003831d35Sstevel /* 25103831d35Sstevel * We need to verify IOSRAM is intact at startup time. If by 25203831d35Sstevel * any chance IOSRAM is corrupted, that means SC is not ready. 25303831d35Sstevel * All we can do is stopping. 25403831d35Sstevel */ 25503831d35Sstevel rv = iosram_read(SBBC_CONSOLE_KEY, 0, (caddr_t)&header, 25603831d35Sstevel sizeof (cnsram_header)); 25703831d35Sstevel if (rv != 0) 25803831d35Sstevel cmn_err(CE_PANIC, "sgcn_attach(): Reading from IOSRAM failed"); 25903831d35Sstevel if (header.cnsram_magic != CNSRAM_MAGIC) 26003831d35Sstevel cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM console buffer"); 26103831d35Sstevel if (!header.cnsram_in_end && !header.cnsram_in_begin) 26203831d35Sstevel cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM input buffer"); 26303831d35Sstevel if (!header.cnsram_out_end && !header.cnsram_out_begin) 26403831d35Sstevel cmn_err(CE_PANIC, "sgcn_attach(): Wrong IOSRAM output buffer"); 26503831d35Sstevel /* 26603831d35Sstevel * XXX need to add extra check for version no. 26703831d35Sstevel */ 26803831d35Sstevel 26903831d35Sstevel /* Allocate console input buffer */ 27003831d35Sstevel sgcn_state->sgcn_inbuf_size = 27103831d35Sstevel header.cnsram_in_end - header.cnsram_in_begin; 27203831d35Sstevel sgcn_state->sgcn_inbuf = 27303831d35Sstevel kmem_alloc(sgcn_state->sgcn_inbuf_size, KM_SLEEP); 27403831d35Sstevel #ifdef SGCN_DEBUG 27503831d35Sstevel prom_printf("Allocated %d(0x%X) bytes for console\n", 27603831d35Sstevel sgcn_state->sgcn_inbuf_size); 27703831d35Sstevel #endif 27803831d35Sstevel 27903831d35Sstevel (void) prom_serengeti_set_console_input(SGCN_CLNT_STR); 28003831d35Sstevel 28103831d35Sstevel abort_seq_handler = sg_abort_seq_handler; 28203831d35Sstevel 28303831d35Sstevel #ifdef SGCN_DEBUG 28403831d35Sstevel prom_printf("sgcn_attach(): SGCN driver attached\n"); 28503831d35Sstevel #endif 28603831d35Sstevel return (DDI_SUCCESS); 28703831d35Sstevel 28803831d35Sstevel } 28903831d35Sstevel 29003831d35Sstevel /* ARGSUSED */ 29103831d35Sstevel static int 29203831d35Sstevel sgcn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 29303831d35Sstevel { 29403831d35Sstevel 29503831d35Sstevel if (cmd == DDI_DETACH) 29603831d35Sstevel return (DDI_FAILURE); 29703831d35Sstevel 29803831d35Sstevel #ifdef SGCN_DEBUG 29903831d35Sstevel prom_printf("sgcn_detach(): SGCN driver detached\n"); 30003831d35Sstevel #endif 30103831d35Sstevel return (DDI_SUCCESS); 30203831d35Sstevel } 30303831d35Sstevel 30403831d35Sstevel /* ARGSUSED */ 30503831d35Sstevel static int 30603831d35Sstevel sgcn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 30703831d35Sstevel { 30803831d35Sstevel int error = DDI_FAILURE; 30903831d35Sstevel 31003831d35Sstevel switch (infocmd) { 31103831d35Sstevel case DDI_INFO_DEVT2DEVINFO: 31203831d35Sstevel if (sgcn_state) { 31303831d35Sstevel *result = (void *) sgcn_state->sgcn_dip; 31403831d35Sstevel error = DDI_SUCCESS; 31503831d35Sstevel } 31603831d35Sstevel break; 31703831d35Sstevel 31803831d35Sstevel case DDI_INFO_DEVT2INSTANCE: 31903831d35Sstevel if (getminor((dev_t)arg) == 0) { 32003831d35Sstevel *result = (void *)0; 32103831d35Sstevel error = DDI_SUCCESS; 32203831d35Sstevel } 32303831d35Sstevel break; 32403831d35Sstevel } 32503831d35Sstevel 32603831d35Sstevel return (error); 32703831d35Sstevel } 32803831d35Sstevel 32903831d35Sstevel /* streams open & close */ 33003831d35Sstevel /* ARGSUSED */ 33103831d35Sstevel static int 33203831d35Sstevel sgcn_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp) 33303831d35Sstevel { 33403831d35Sstevel tty_common_t *tty; 33503831d35Sstevel int unit = getminor(*devp); 33603831d35Sstevel 33703831d35Sstevel if (unit != 0) 33803831d35Sstevel return (ENXIO); 33903831d35Sstevel 34003831d35Sstevel /* stream already open */ 34103831d35Sstevel if (q->q_ptr) { 34203831d35Sstevel return (DDI_SUCCESS); 34303831d35Sstevel } 34403831d35Sstevel 34503831d35Sstevel if (!sgcn_state) { 34603831d35Sstevel cmn_err(CE_WARN, "sgcn_open(): sgcn is not configured by\ 34703831d35Sstevel autoconfig\n"); 34803831d35Sstevel return (ENXIO); 34903831d35Sstevel } 35003831d35Sstevel 35103831d35Sstevel mutex_enter(&sgcn_state->sgcn_lock); 35203831d35Sstevel tty = &(sgcn_state->sgcn_tty); 35303831d35Sstevel 35403831d35Sstevel tty->t_readq = q; 35503831d35Sstevel tty->t_writeq = WR(q); 35603831d35Sstevel 35703831d35Sstevel /* Link the RD and WR Q's */ 35803831d35Sstevel 35903831d35Sstevel q->q_ptr = WR(q)->q_ptr = (caddr_t)sgcn_state; 36003831d35Sstevel sgcn_state->sgcn_readq = RD(q); 36103831d35Sstevel sgcn_state->sgcn_writeq = WR(q); 36203831d35Sstevel qprocson(q); 36303831d35Sstevel 36403831d35Sstevel mutex_exit(&sgcn_state->sgcn_lock); 36503831d35Sstevel 36603831d35Sstevel /* initialize interrupt handler */ 367*07d06da5SSurya Prakki (void) iosram_reg_intr(SBBC_CONSOLE_IN, 36803831d35Sstevel (sbbc_intrfunc_t)sgcn_data_in_handler, NULL, 36903831d35Sstevel &sgcn_state->sgcn_sbbc_in_state, 37003831d35Sstevel &sgcn_state->sgcn_sbbc_in_lock); 371*07d06da5SSurya Prakki (void) iosram_reg_intr(SBBC_CONSOLE_SPACE_OUT, 37203831d35Sstevel (sbbc_intrfunc_t)sgcn_space_2_out_handler, NULL, 37303831d35Sstevel &sgcn_state->sgcn_sbbc_outspace_state, 37403831d35Sstevel &sgcn_state->sgcn_sbbc_outspace_lock); 375*07d06da5SSurya Prakki (void) iosram_reg_intr(SBBC_CONSOLE_BRK, 37603831d35Sstevel (sbbc_intrfunc_t)sgcn_break_handler, NULL, 37703831d35Sstevel &sgcn_state->sgcn_sbbc_brk_state, 37803831d35Sstevel &sgcn_state->sgcn_sbbc_brk_lock); 37903831d35Sstevel 38003831d35Sstevel return (DDI_SUCCESS); 38103831d35Sstevel } 38203831d35Sstevel 38303831d35Sstevel /* ARGSUSED */ 38403831d35Sstevel static int 38503831d35Sstevel sgcn_close(queue_t *q, int flag, cred_t *credp) 38603831d35Sstevel { 38703831d35Sstevel int ret; 38803831d35Sstevel 38903831d35Sstevel ASSERT(sgcn_state == q->q_ptr); 39003831d35Sstevel 39103831d35Sstevel if (sgcn_state->sgcn_wbufcid != 0) { 39203831d35Sstevel unbufcall(sgcn_state->sgcn_wbufcid); 39303831d35Sstevel } 39403831d35Sstevel 39503831d35Sstevel ret = iosram_unreg_intr(SBBC_CONSOLE_BRK); 39603831d35Sstevel ASSERT(ret == 0); 39703831d35Sstevel 39803831d35Sstevel ret = iosram_unreg_intr(SBBC_CONSOLE_SPACE_OUT); 39903831d35Sstevel ASSERT(ret == 0); 40003831d35Sstevel 40103831d35Sstevel ret = iosram_unreg_intr(SBBC_CONSOLE_IN); 40203831d35Sstevel ASSERT(ret == 0); 40303831d35Sstevel 40403831d35Sstevel ttycommon_close(&sgcn_state->sgcn_tty); 40503831d35Sstevel 40603831d35Sstevel qprocsoff(q); 40703831d35Sstevel q->q_ptr = WR(q)->q_ptr = NULL; 40803831d35Sstevel sgcn_state->sgcn_readq = NULL; 40903831d35Sstevel sgcn_state->sgcn_writeq = NULL; 41003831d35Sstevel 41103831d35Sstevel return (DDI_SUCCESS); 41203831d35Sstevel } 41303831d35Sstevel 41403831d35Sstevel /* 41503831d35Sstevel * Put procedure for write queue. 41603831d35Sstevel * Respond to M_IOCTL, M_DATA and M_FLUSH messages here; 41703831d35Sstevel * It put's the data onto internal sgcn_output_q. 41803831d35Sstevel */ 41903831d35Sstevel static int 42003831d35Sstevel sgcn_wput(queue_t *q, mblk_t *mp) 42103831d35Sstevel { 42203831d35Sstevel 42303831d35Sstevel #ifdef SGCN_DEBUG 42403831d35Sstevel struct iocblk *iocp; 42503831d35Sstevel int i; 42603831d35Sstevel #endif 42703831d35Sstevel 42803831d35Sstevel ASSERT(sgcn_state == q->q_ptr); 42903831d35Sstevel 43003831d35Sstevel if (!mp->b_datap) { 43103831d35Sstevel cmn_err(CE_PANIC, "sgcn_wput(): null datap"); 43203831d35Sstevel } 43303831d35Sstevel 43403831d35Sstevel #ifdef SGCN_DEBUG 43503831d35Sstevel prom_printf("sgcn_wput(): SGCN wput q=%X mp=%X rd=%X wr=%X type=%X\n", 43603831d35Sstevel q, mp, mp->b_rptr, mp->b_wptr, mp->b_datap->db_type); 43703831d35Sstevel #endif 43803831d35Sstevel 43903831d35Sstevel switch (mp->b_datap->db_type) { 44003831d35Sstevel case M_IOCTL: 44103831d35Sstevel case M_CTL: 44203831d35Sstevel #ifdef SGCN_DEBUG 44303831d35Sstevel iocp = (struct iocblk *)mp->b_rptr; 44403831d35Sstevel prom_printf("sgcn_wput(): M_IOCTL cmd=%X TIOC=%X\n", 44503831d35Sstevel iocp->ioc_cmd, TIOC); 44603831d35Sstevel #endif 44703831d35Sstevel switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 44803831d35Sstevel case TCSETSW: 44903831d35Sstevel case TCSETSF: 45003831d35Sstevel case TCSETAW: 45103831d35Sstevel case TCSETAF: 45203831d35Sstevel case TCSBRK: 45303831d35Sstevel /* 45403831d35Sstevel * The change do not take effect until all 45503831d35Sstevel * output queued before them is drained. 45603831d35Sstevel * Put this message on the queue, so that 45703831d35Sstevel * "sgcn_start" will see it when it's done 45803831d35Sstevel * with the output before it. Poke the start 45903831d35Sstevel * routine, just in case. 46003831d35Sstevel */ 461*07d06da5SSurya Prakki (void) putq(q, mp); 46203831d35Sstevel sgcn_start(); 46303831d35Sstevel break; 46403831d35Sstevel default: 46503831d35Sstevel sgcn_ioctl(q, mp); 46603831d35Sstevel } 46703831d35Sstevel break; 46803831d35Sstevel 46903831d35Sstevel case M_FLUSH: 47003831d35Sstevel if (*mp->b_rptr & FLUSHW) { 47103831d35Sstevel flushq(q, FLUSHDATA); 47203831d35Sstevel *mp->b_rptr &= ~FLUSHW; 47303831d35Sstevel } 47403831d35Sstevel if (*mp->b_rptr & FLUSHR) { 47503831d35Sstevel flushq(RD(q), FLUSHDATA); 47603831d35Sstevel qreply(q, mp); 47703831d35Sstevel } else { 47803831d35Sstevel freemsg(mp); 47903831d35Sstevel } 48003831d35Sstevel break; 48103831d35Sstevel 48203831d35Sstevel case M_STOP: 48303831d35Sstevel sgcn_stopped = TRUE; 48403831d35Sstevel freemsg(mp); 48503831d35Sstevel break; 48603831d35Sstevel 48703831d35Sstevel case M_START: 48803831d35Sstevel sgcn_stopped = FALSE; 48903831d35Sstevel freemsg(mp); 49003831d35Sstevel qenable(q); /* Start up delayed messages */ 49103831d35Sstevel break; 49203831d35Sstevel 49303831d35Sstevel case M_DATA: 49403831d35Sstevel /* 49503831d35Sstevel * Queue the message up to be transmitted, 49603831d35Sstevel * and poke the start routine. 49703831d35Sstevel */ 49803831d35Sstevel #ifdef SGCN_DEBUG 49903831d35Sstevel if (mp->b_rptr < mp->b_wptr) { 50003831d35Sstevel prom_printf("sgcn_wput(): DATA q=%X mp=%X rd=%X wr=%X\n", 50103831d35Sstevel q, mp, mp->b_rptr, mp->b_wptr); 50203831d35Sstevel prom_printf("sgcn_wput(): [[[[["); 50303831d35Sstevel for (i = 0; i < mp->b_wptr-mp->b_rptr; i++) { 50403831d35Sstevel prom_printf("%c", *(mp->b_rptr+i)); 50503831d35Sstevel } 50603831d35Sstevel prom_printf("]]]]]\n"); 50703831d35Sstevel } 50803831d35Sstevel #endif /* SGCN_DEBUG */ 50903831d35Sstevel (void) putq(q, mp); 51003831d35Sstevel sgcn_start(); 51103831d35Sstevel break; 51203831d35Sstevel 51303831d35Sstevel default: 51403831d35Sstevel freemsg(mp); 51503831d35Sstevel } 51603831d35Sstevel 51703831d35Sstevel return (0); 51803831d35Sstevel } 51903831d35Sstevel 52003831d35Sstevel /* 52103831d35Sstevel * Process an "ioctl" message sent down to us. 52203831d35Sstevel */ 52303831d35Sstevel static void 52403831d35Sstevel sgcn_ioctl(queue_t *q, mblk_t *mp) 52503831d35Sstevel { 52603831d35Sstevel struct iocblk *iocp; 52703831d35Sstevel tty_common_t *tty; 52803831d35Sstevel mblk_t *datamp; 52903831d35Sstevel int data_size; 53003831d35Sstevel int error = 0; 53103831d35Sstevel 53203831d35Sstevel #ifdef SGCN_DEBUG 53303831d35Sstevel prom_printf("sgcn_ioctl(): q=%X mp=%X\n", q, mp); 53403831d35Sstevel #endif 53503831d35Sstevel iocp = (struct iocblk *)mp->b_rptr; 53603831d35Sstevel tty = &(sgcn_state->sgcn_tty); 53703831d35Sstevel 53803831d35Sstevel if (tty->t_iocpending != NULL) { 53903831d35Sstevel freemsg(tty->t_iocpending); 54003831d35Sstevel tty->t_iocpending = NULL; 54103831d35Sstevel } 54203831d35Sstevel data_size = ttycommon_ioctl(tty, q, mp, &error); 54303831d35Sstevel if (data_size != 0) { 54403831d35Sstevel if (sgcn_state->sgcn_wbufcid) 54503831d35Sstevel unbufcall(sgcn_state->sgcn_wbufcid); 54603831d35Sstevel /* call sgcn_reioctl() */ 54703831d35Sstevel sgcn_state->sgcn_wbufcid = 54803831d35Sstevel bufcall(data_size, BPRI_HI, sgcn_reioctl, sgcn_state); 54903831d35Sstevel return; 55003831d35Sstevel } 55103831d35Sstevel 55203831d35Sstevel if (error < 0) { 55303831d35Sstevel iocp = (struct iocblk *)mp->b_rptr; 55403831d35Sstevel /* 55503831d35Sstevel * "ttycommon_ioctl" didn't do anything; we process it here. 55603831d35Sstevel */ 55703831d35Sstevel error = 0; 55803831d35Sstevel switch (iocp->ioc_cmd) { 55903831d35Sstevel case TCSBRK: 56003831d35Sstevel case TIOCSBRK: 56103831d35Sstevel case TIOCCBRK: 56203831d35Sstevel case TIOCMSET: 56303831d35Sstevel case TIOCMBIS: 56403831d35Sstevel case TIOCMBIC: 56503831d35Sstevel if (iocp->ioc_count != TRANSPARENT) 56603831d35Sstevel mioc2ack(mp, NULL, 0, 0); 56703831d35Sstevel else 56803831d35Sstevel mcopyin(mp, NULL, sizeof (int), NULL); 56903831d35Sstevel break; 57003831d35Sstevel 57103831d35Sstevel case TIOCMGET: 57203831d35Sstevel datamp = allocb(sizeof (int), BPRI_MED); 57303831d35Sstevel if (datamp == NULL) { 57403831d35Sstevel error = EAGAIN; 57503831d35Sstevel break; 57603831d35Sstevel } 57703831d35Sstevel 57803831d35Sstevel *(int *)datamp->b_rptr = 0; 57903831d35Sstevel 58003831d35Sstevel if (iocp->ioc_count != TRANSPARENT) 58103831d35Sstevel mioc2ack(mp, datamp, sizeof (int), 0); 58203831d35Sstevel else 58303831d35Sstevel mcopyout(mp, NULL, sizeof (int), NULL, datamp); 58403831d35Sstevel break; 58503831d35Sstevel 58603831d35Sstevel default: 58703831d35Sstevel error = EINVAL; 58803831d35Sstevel break; 58903831d35Sstevel } 59003831d35Sstevel } 59103831d35Sstevel if (error != 0) { 59203831d35Sstevel iocp->ioc_count = 0; 59303831d35Sstevel iocp->ioc_error = error; 59403831d35Sstevel mp->b_datap->db_type = M_IOCNAK; 59503831d35Sstevel } 59603831d35Sstevel qreply(q, mp); 59703831d35Sstevel } 59803831d35Sstevel 59903831d35Sstevel static void 60003831d35Sstevel sgcn_reioctl(void *unit) 60103831d35Sstevel { 60203831d35Sstevel queue_t *q; 60303831d35Sstevel mblk_t *mp; 60403831d35Sstevel sgcn_t *sgcnp = (sgcn_t *)unit; 60503831d35Sstevel 60603831d35Sstevel if (!sgcnp->sgcn_wbufcid) { 60703831d35Sstevel return; 60803831d35Sstevel } 60903831d35Sstevel sgcnp->sgcn_wbufcid = 0; 61003831d35Sstevel if ((q = sgcnp->sgcn_tty.t_writeq) == NULL) { 61103831d35Sstevel return; 61203831d35Sstevel } 61303831d35Sstevel 61403831d35Sstevel if ((mp = sgcnp->sgcn_tty.t_iocpending) != NULL) { 61503831d35Sstevel sgcnp->sgcn_tty.t_iocpending = NULL; 61603831d35Sstevel sgcn_ioctl(q, mp); 61703831d35Sstevel } 61803831d35Sstevel } 61903831d35Sstevel 62003831d35Sstevel static void 62103831d35Sstevel sgcn_start() 62203831d35Sstevel { 62303831d35Sstevel 62403831d35Sstevel queue_t *q; 62503831d35Sstevel mblk_t *mp; 62603831d35Sstevel int retval; 62703831d35Sstevel 62803831d35Sstevel /* 62903831d35Sstevel * read stream queue and remove data from the queue and 63003831d35Sstevel * transmit them if possible 63103831d35Sstevel */ 63203831d35Sstevel q = sgcn_state->sgcn_writeq; 63303831d35Sstevel ASSERT(q != NULL); 63403831d35Sstevel while (mp = getq(q)) { 63503831d35Sstevel switch (mp->b_datap->db_type) { 63603831d35Sstevel case M_IOCTL: 63703831d35Sstevel /* 63803831d35Sstevel * These are those IOCTLs queued up 63903831d35Sstevel * do it now 64003831d35Sstevel */ 64103831d35Sstevel sgcn_ioctl(q, mp); 64203831d35Sstevel continue; 64303831d35Sstevel default: 64403831d35Sstevel /* 64503831d35Sstevel * M_DATA 64603831d35Sstevel * Copy it from stream queue buffer to 64703831d35Sstevel * sgcn buffer 64803831d35Sstevel */ 64903831d35Sstevel retval = sgcn_transmit(q, mp); 65003831d35Sstevel 65103831d35Sstevel if (retval == EBUSY) { 65203831d35Sstevel /* 65303831d35Sstevel * Console output buffer is full for 65403831d35Sstevel * sgcn_timeout_period seconds, assume 65503831d35Sstevel * SC is dead, drop all console output 65603831d35Sstevel * data from stream queue. 65703831d35Sstevel */ 65803831d35Sstevel if (sgcn_state->sgcn_sc_active < 65903831d35Sstevel gethrestime_sec() - sgcn_timeout_period) 66003831d35Sstevel sgcn_flush(); 66103831d35Sstevel return; 66203831d35Sstevel } else if (retval == EAGAIN) { 66303831d35Sstevel /* 66403831d35Sstevel * Console output just became full 66503831d35Sstevel * return 66603831d35Sstevel */ 66703831d35Sstevel mutex_enter(&sgcn_state->sgcn_lock); 66803831d35Sstevel sgcn_state->sgcn_sc_active = gethrestime_sec(); 66903831d35Sstevel mutex_exit(&sgcn_state->sgcn_lock); 67003831d35Sstevel return; 67103831d35Sstevel } else { 67203831d35Sstevel /* send more console output */ 67303831d35Sstevel mutex_enter(&sgcn_state->sgcn_lock); 67403831d35Sstevel sgcn_state->sgcn_sc_active = gethrestime_sec(); 67503831d35Sstevel mutex_exit(&sgcn_state->sgcn_lock); 67603831d35Sstevel } 67703831d35Sstevel } /* switch */ 67803831d35Sstevel } 67903831d35Sstevel 68003831d35Sstevel } 68103831d35Sstevel 68203831d35Sstevel static int 68303831d35Sstevel sgcn_transmit(queue_t *q, mblk_t *mp) 68403831d35Sstevel { 68503831d35Sstevel caddr_t buf; 68603831d35Sstevel mblk_t *bp; 68703831d35Sstevel int len, oldlen; 68803831d35Sstevel 68903831d35Sstevel #ifdef SGCN_DEBUG 69003831d35Sstevel prom_printf("sgcn_transmit(): q=%X mp=%X\n", q, mp); 69103831d35Sstevel #endif 69203831d35Sstevel do { 69303831d35Sstevel bp = mp; 69403831d35Sstevel oldlen = len = bp->b_wptr - bp->b_rptr; 69503831d35Sstevel buf = (caddr_t)bp->b_rptr; 69603831d35Sstevel len = CONSOLE_WRITE(buf, len); 69703831d35Sstevel if (len > 0) 698*07d06da5SSurya Prakki (void) iosram_send_intr(SBBC_CONSOLE_OUT); 69903831d35Sstevel if (len >= 0 && len < oldlen) { 70003831d35Sstevel /* IOSRAM is full, we are not done with mp yet */ 70103831d35Sstevel bp->b_rptr += len; 70203831d35Sstevel (void) putbq(q, mp); 70303831d35Sstevel if (len) 70403831d35Sstevel return (EAGAIN); 70503831d35Sstevel else 70603831d35Sstevel return (EBUSY); 70703831d35Sstevel } 70803831d35Sstevel mp = bp->b_cont; 70903831d35Sstevel freeb(bp); 71003831d35Sstevel } while (mp); 71103831d35Sstevel 71203831d35Sstevel return (0); 71303831d35Sstevel } 71403831d35Sstevel 71503831d35Sstevel /* 71603831d35Sstevel * called when SC first establishes console connection 71703831d35Sstevel * drop all the data on the output queue 71803831d35Sstevel */ 71903831d35Sstevel static void 72003831d35Sstevel sgcn_flush() 72103831d35Sstevel { 72203831d35Sstevel queue_t *q; 72303831d35Sstevel mblk_t *mp; 72403831d35Sstevel 72503831d35Sstevel q = sgcn_state->sgcn_writeq; 72603831d35Sstevel 72703831d35Sstevel prom_printf("sgcn_flush(): WARNING console output is dropped " 72803831d35Sstevel "time=%lX\n", gethrestime_sec()); 72903831d35Sstevel while (mp = getq(q)) { 73003831d35Sstevel freemsg(mp); 73103831d35Sstevel } 73203831d35Sstevel 73303831d35Sstevel } 73403831d35Sstevel 73503831d35Sstevel uint64_t sgcn_input_dropped; 73603831d35Sstevel 73703831d35Sstevel /* 73803831d35Sstevel * Interrupt handlers 73903831d35Sstevel * All handlers register with SBBC driver and must follow SBBC interrupt 74003831d35Sstevel * delivery conventions. 74103831d35Sstevel */ 74203831d35Sstevel /* 74303831d35Sstevel * SC sends an interrupt when new data comes in 74403831d35Sstevel */ 74503831d35Sstevel /* ARGSUSED */ 74603831d35Sstevel void 74703831d35Sstevel sgcn_data_in_handler(caddr_t arg) 74803831d35Sstevel { 74903831d35Sstevel caddr_t buf = sgcn_state->sgcn_inbuf; 75003831d35Sstevel int i, len; 75103831d35Sstevel mblk_t *mp; 75203831d35Sstevel 75303831d35Sstevel /* 75403831d35Sstevel * change interrupt state so that SBBC won't trigger 75503831d35Sstevel * another one. 75603831d35Sstevel */ 75703831d35Sstevel mutex_enter(&sgcn_state->sgcn_sbbc_in_lock); 75803831d35Sstevel sgcn_state->sgcn_sbbc_in_state = SBBC_INTR_RUNNING; 75903831d35Sstevel mutex_exit(&sgcn_state->sgcn_sbbc_in_lock); 76003831d35Sstevel 76103831d35Sstevel /* update sgcn_state for SC activity information */ 76203831d35Sstevel mutex_enter(&sgcn_state->sgcn_lock); 76303831d35Sstevel sgcn_state->sgcn_sc_active = gethrestime_sec(); 76403831d35Sstevel mutex_exit(&sgcn_state->sgcn_lock); 76503831d35Sstevel 76603831d35Sstevel /* enter our perimeter */ 76703831d35Sstevel entersq(sgcn_state->sgcn_readq->q_syncq, SQ_CALLBACK); 76803831d35Sstevel 76903831d35Sstevel for (;;) { 77003831d35Sstevel 77103831d35Sstevel /* read from console input IOSRAM */ 77203831d35Sstevel len = CONSOLE_READ(buf, sgcn_state->sgcn_inbuf_size); 77303831d35Sstevel 77403831d35Sstevel if (len <= 0) { 77503831d35Sstevel 77603831d35Sstevel mutex_enter(&sgcn_state->sgcn_sbbc_in_lock); 77703831d35Sstevel 77803831d35Sstevel len = CONSOLE_READ(buf, sgcn_state->sgcn_inbuf_size); 77903831d35Sstevel 78003831d35Sstevel if (len <= 0) { 78103831d35Sstevel sgcn_state->sgcn_sbbc_in_state = SBBC_INTR_IDLE; 78203831d35Sstevel mutex_exit(&sgcn_state->sgcn_sbbc_in_lock); 78303831d35Sstevel 78403831d35Sstevel /* leave our perimeter */ 78503831d35Sstevel leavesq(sgcn_state->sgcn_readq->q_syncq, 78603831d35Sstevel SQ_CALLBACK); 78703831d35Sstevel return; 78803831d35Sstevel } else { 78903831d35Sstevel mutex_exit(&sgcn_state->sgcn_sbbc_in_lock); 79003831d35Sstevel } 79103831d35Sstevel 79203831d35Sstevel } 79303831d35Sstevel 794*07d06da5SSurya Prakki (void) iosram_send_intr(SBBC_CONSOLE_SPACE_IN); 79503831d35Sstevel 79603831d35Sstevel if (abort_enable == KIOCABORTALTERNATE) { 79703831d35Sstevel for (i = 0; i < len; i ++) { 79803831d35Sstevel if (abort_charseq_recognize(buf[i])) 79903831d35Sstevel abort_sequence_enter((char *)NULL); 80003831d35Sstevel } 80103831d35Sstevel } 80203831d35Sstevel 80303831d35Sstevel /* put console input onto stream */ 80403831d35Sstevel if (sgcn_state->sgcn_readq) { 80503831d35Sstevel if ((mp = allocb(len, BPRI_MED)) == (mblk_t *)NULL) { 80603831d35Sstevel sgcn_input_dropped += len; 80703831d35Sstevel cmn_err(CE_WARN, 80803831d35Sstevel "sgcn_data_in_handler(): allocb failed" 80903831d35Sstevel " (console input dropped.)"); 81003831d35Sstevel } else { 81103831d35Sstevel bcopy(buf, mp->b_wptr, len); 81203831d35Sstevel mp->b_wptr += len; 81303831d35Sstevel putnext(sgcn_state->sgcn_readq, mp); 81403831d35Sstevel } 81503831d35Sstevel } 81603831d35Sstevel } 81703831d35Sstevel 81803831d35Sstevel } 81903831d35Sstevel 82003831d35Sstevel /* 82103831d35Sstevel * SC sends an interrupt when it takes output data 82203831d35Sstevel * from a full IOSRAM 82303831d35Sstevel */ 82403831d35Sstevel /* ARGSUSED */ 82503831d35Sstevel void 82603831d35Sstevel sgcn_space_2_out_handler(caddr_t arg) 82703831d35Sstevel { 82803831d35Sstevel /* 82903831d35Sstevel * change interrupt state so that SBBC won't trigger 83003831d35Sstevel * another one. 83103831d35Sstevel */ 83203831d35Sstevel mutex_enter(&sgcn_state->sgcn_sbbc_outspace_lock); 83303831d35Sstevel sgcn_state->sgcn_sbbc_outspace_state = SBBC_INTR_RUNNING; 83403831d35Sstevel mutex_exit(&sgcn_state->sgcn_sbbc_outspace_lock); 83503831d35Sstevel 83603831d35Sstevel mutex_enter(&sgcn_state->sgcn_lock); 83703831d35Sstevel sgcn_state->sgcn_sc_active = gethrestime_sec(); 83803831d35Sstevel mutex_exit(&sgcn_state->sgcn_lock); 83903831d35Sstevel 84003831d35Sstevel if (sgcn_state->sgcn_writeq != NULL) 84103831d35Sstevel qenable(sgcn_state->sgcn_writeq); 84203831d35Sstevel 84303831d35Sstevel /* restore interrupt state */ 84403831d35Sstevel mutex_enter(&sgcn_state->sgcn_sbbc_outspace_lock); 84503831d35Sstevel sgcn_state->sgcn_sbbc_outspace_state = SBBC_INTR_IDLE; 84603831d35Sstevel mutex_exit(&sgcn_state->sgcn_sbbc_outspace_lock); 84703831d35Sstevel } 84803831d35Sstevel 84903831d35Sstevel /* 85003831d35Sstevel * SC sends an interrupt when it detects BREAK sequence 85103831d35Sstevel */ 85203831d35Sstevel /* ARGSUSED */ 85303831d35Sstevel void 85403831d35Sstevel sgcn_break_handler(caddr_t arg) 85503831d35Sstevel { 85603831d35Sstevel /* 85703831d35Sstevel * change interrupt state so that SBBC won't trigger 85803831d35Sstevel * another one. 85903831d35Sstevel */ 86003831d35Sstevel mutex_enter(&sgcn_state->sgcn_sbbc_brk_lock); 86103831d35Sstevel sgcn_state->sgcn_sbbc_brk_state = SBBC_INTR_RUNNING; 86203831d35Sstevel mutex_exit(&sgcn_state->sgcn_sbbc_brk_lock); 86303831d35Sstevel 86403831d35Sstevel if (abort_enable != KIOCABORTALTERNATE) 86503831d35Sstevel abort_sequence_enter((char *)NULL); 86603831d35Sstevel 86703831d35Sstevel /* restore interrupt state */ 86803831d35Sstevel mutex_enter(&sgcn_state->sgcn_sbbc_brk_lock); 86903831d35Sstevel sgcn_state->sgcn_sbbc_brk_state = SBBC_INTR_IDLE; 87003831d35Sstevel mutex_exit(&sgcn_state->sgcn_sbbc_brk_lock); 87103831d35Sstevel } 87203831d35Sstevel 87303831d35Sstevel /* 87403831d35Sstevel * reporting errors in console driver sgcn. 87503831d35Sstevel * since we can not trust console driver at this time, we need to 87603831d35Sstevel * log errors in other system logs 87703831d35Sstevel * error codes: 87803831d35Sstevel * EIO - iosram interface failed 87903831d35Sstevel * EPROTO - IOSRAM is corrupted 88003831d35Sstevel * EINVAL - invalid argument 88103831d35Sstevel */ 88203831d35Sstevel #define SGCN_MAX_ERROR 100 88303831d35Sstevel static void 88403831d35Sstevel sgcn_log_error(int when, int what) 88503831d35Sstevel { 88603831d35Sstevel char error_msg[256], error_code[256]; 88703831d35Sstevel static uint_t error_counter = 0; 88803831d35Sstevel 88903831d35Sstevel error_counter ++; 89003831d35Sstevel 89103831d35Sstevel if (error_counter > SGCN_MAX_ERROR) { 89203831d35Sstevel error_counter = 0; 893*07d06da5SSurya Prakki (void) strcpy(error_msg, "!Too many sgcn errors"); 89403831d35Sstevel } else { 89503831d35Sstevel (void) sprintf(error_code, "Error %d", what); 89603831d35Sstevel 89703831d35Sstevel (void) sprintf(error_msg, "!%s at %s", 89803831d35Sstevel (what == EIO) ? "IOSRAM interface failed" : 89903831d35Sstevel (what == EPROTO) ? "IOSRAM corrupted" : 90003831d35Sstevel (what == EINVAL) ? "Invalid argument" : 90103831d35Sstevel error_code, 90203831d35Sstevel (when == RW_CONSOLE_READ) ? "console input" : 90303831d35Sstevel (when == RW_CONSOLE_WRITE) ? "console output, dropped" : 90403831d35Sstevel "console I/O"); 90503831d35Sstevel } 90603831d35Sstevel 90703831d35Sstevel cmn_err(CE_WARN, error_msg); 90803831d35Sstevel } 90903831d35Sstevel 91003831d35Sstevel static int 91103831d35Sstevel sgcn_read_header(int rw, cnsram_header *header) 91203831d35Sstevel { 91303831d35Sstevel int rv; 91403831d35Sstevel 91503831d35Sstevel /* check IOSRAM contents and read pointers */ 91603831d35Sstevel rv = iosram_read(SBBC_CONSOLE_KEY, 0, (caddr_t)header, 91703831d35Sstevel sizeof (cnsram_header)); 91803831d35Sstevel if (rv != 0) { 91903831d35Sstevel return (-1); 92003831d35Sstevel } 92103831d35Sstevel 92203831d35Sstevel /* 92303831d35Sstevel * Since the header is read in a byte-by-byte fashion 92403831d35Sstevel * using ddi_rep_get8, we need to re-read the producer 92503831d35Sstevel * or consumer pointer as integer in case it has changed 92603831d35Sstevel * after part of the previous value has been read. 92703831d35Sstevel */ 92803831d35Sstevel if (rw == RW_CONSOLE_READ) { 92903831d35Sstevel rv = iosram_read(SBBC_CONSOLE_KEY, 93003831d35Sstevel OFFSETOF((*header), cnsram_in_wrptr), 93103831d35Sstevel POINTER((*header), cnsram_in_wrptr), 93203831d35Sstevel sizeof (header->cnsram_in_wrptr)); 93303831d35Sstevel } else if (rw == RW_CONSOLE_WRITE) { 93403831d35Sstevel rv = iosram_read(SBBC_CONSOLE_KEY, 93503831d35Sstevel OFFSETOF((*header), cnsram_out_rdptr), 93603831d35Sstevel POINTER((*header), cnsram_out_rdptr), 93703831d35Sstevel sizeof (header->cnsram_out_rdptr)); 93803831d35Sstevel } else 93903831d35Sstevel rv = -1; 94003831d35Sstevel 94103831d35Sstevel return (rv); 94203831d35Sstevel } 94303831d35Sstevel 94403831d35Sstevel static int 94503831d35Sstevel sgcn_rw(int rw, caddr_t buf, int len) 94603831d35Sstevel { 94703831d35Sstevel cnsram_header header; 94803831d35Sstevel int rv, size, nbytes; 94903831d35Sstevel 95003831d35Sstevel #ifdef SGCN_DEBUG 95103831d35Sstevel prom_printf("sgcn_rw() rw = %X buf = %p len = %d\n", 95203831d35Sstevel rw, buf, len); 95303831d35Sstevel #endif /* SGCN_DEBUG */ 95403831d35Sstevel if (len == 0) 95503831d35Sstevel return (0); 95603831d35Sstevel 95703831d35Sstevel /* sanity check */ 95803831d35Sstevel if (buf == NULL || len < 0) { 95903831d35Sstevel sgcn_log_error(rw, EINVAL); 96003831d35Sstevel return (-1); 96103831d35Sstevel } 96203831d35Sstevel 96303831d35Sstevel /* check IOSRAM contents and read pointers */ 96403831d35Sstevel rv = sgcn_read_header(rw, &header); 96503831d35Sstevel if (rv != 0) { 96603831d35Sstevel sgcn_log_error(rw, EIO); 96703831d35Sstevel return (-1); 96803831d35Sstevel } 96903831d35Sstevel if (header.cnsram_magic != CNSRAM_MAGIC) { 97003831d35Sstevel sgcn_log_error(rw, EPROTO); 97103831d35Sstevel return (-1); 97203831d35Sstevel } 97303831d35Sstevel 97403831d35Sstevel if (rw == RW_CONSOLE_READ) 97503831d35Sstevel size = header.cnsram_in_end - header.cnsram_in_begin; 97603831d35Sstevel else if (rw == RW_CONSOLE_WRITE) 97703831d35Sstevel size = header.cnsram_out_end - header.cnsram_out_begin; 97803831d35Sstevel if (size < 0) { 97903831d35Sstevel sgcn_log_error(rw, EPROTO); 98003831d35Sstevel return (-1); 98103831d35Sstevel } 98203831d35Sstevel 98303831d35Sstevel if (rw == RW_CONSOLE_READ) 98403831d35Sstevel nbytes = circular_buffer_read( 98503831d35Sstevel header.cnsram_in_begin, 98603831d35Sstevel header.cnsram_in_end, 98703831d35Sstevel header.cnsram_in_rdptr, 98803831d35Sstevel header.cnsram_in_wrptr, buf, len); 98903831d35Sstevel else if (rw == RW_CONSOLE_WRITE) 99003831d35Sstevel nbytes = circular_buffer_write( 99103831d35Sstevel header.cnsram_out_begin, 99203831d35Sstevel header.cnsram_out_end, 99303831d35Sstevel header.cnsram_out_rdptr, 99403831d35Sstevel header.cnsram_out_wrptr, buf, len); 99503831d35Sstevel 99603831d35Sstevel /* 99703831d35Sstevel * error log was done in circular buffer routines, 99803831d35Sstevel * no need to call sgcn_log_error() here 99903831d35Sstevel */ 100003831d35Sstevel if (nbytes < 0) 100103831d35Sstevel return (-1); 100203831d35Sstevel 100303831d35Sstevel if (nbytes == 0) 100403831d35Sstevel return (0); 100503831d35Sstevel 100603831d35Sstevel if (rw == RW_CONSOLE_READ) { 100703831d35Sstevel header.cnsram_in_rdptr = 100803831d35Sstevel (header.cnsram_in_rdptr - header.cnsram_in_begin 100903831d35Sstevel + nbytes) 101003831d35Sstevel % size + header.cnsram_in_begin; 101103831d35Sstevel rv = iosram_write(SBBC_CONSOLE_KEY, 101203831d35Sstevel OFFSETOF(header, cnsram_in_rdptr), 101303831d35Sstevel POINTER(header, cnsram_in_rdptr), 101403831d35Sstevel sizeof (header.cnsram_in_rdptr)); 101503831d35Sstevel } else if (rw == RW_CONSOLE_WRITE) { 101603831d35Sstevel header.cnsram_out_wrptr = 101703831d35Sstevel (header.cnsram_out_wrptr - header.cnsram_out_begin 101803831d35Sstevel + nbytes) 101903831d35Sstevel % size + header.cnsram_out_begin; 102003831d35Sstevel rv = iosram_write(SBBC_CONSOLE_KEY, 102103831d35Sstevel OFFSETOF(header, cnsram_out_wrptr), 102203831d35Sstevel POINTER(header, cnsram_out_wrptr), 102303831d35Sstevel sizeof (header.cnsram_out_wrptr)); 102403831d35Sstevel } 102503831d35Sstevel if (rv != 0) { 102603831d35Sstevel sgcn_log_error(rw, EIO); 102703831d35Sstevel return (-1); 102803831d35Sstevel } 102903831d35Sstevel 103003831d35Sstevel return (nbytes); 103103831d35Sstevel } 103203831d35Sstevel 103303831d35Sstevel /* 103403831d35Sstevel * Circular buffer interfaces 103503831d35Sstevel * 103603831d35Sstevel * See sgcn.h for circular buffer structure 103703831d35Sstevel * 103803831d35Sstevel * The circular buffer is empty when read ptr == write ptr 103903831d35Sstevel * and is full when read ptr is one ahead of write ptr 104003831d35Sstevel */ 104103831d35Sstevel /* 104203831d35Sstevel * Write to circular buffer in IOSRAM 104303831d35Sstevel * input: 104403831d35Sstevel * buf buffer in main memory, contains data to be written 104503831d35Sstevel * len length of data in bytes 104603831d35Sstevel * begin, end, rd, wr buffer pointers 104703831d35Sstevel * return value: 104803831d35Sstevel * actual bytes written. 104903831d35Sstevel */ 105003831d35Sstevel static int 105103831d35Sstevel circular_buffer_write(int begin, int end, int rd, int wr, caddr_t buf, int len) 105203831d35Sstevel { 105303831d35Sstevel int size, space, space_at_end; 105403831d35Sstevel int rv = 0; 105503831d35Sstevel 105603831d35Sstevel size = end - begin; 105703831d35Sstevel if (size <= 0) { 105803831d35Sstevel rv = EINVAL; 105903831d35Sstevel goto out; 106003831d35Sstevel } 106103831d35Sstevel 106203831d35Sstevel if ((len = ((len >= size) ? (size-1) : len)) == 0) 106303831d35Sstevel return (0); /* The buffer's full, so just return 0 now. */ 106403831d35Sstevel 106503831d35Sstevel space = (rd - wr + size - 1) % size; 106603831d35Sstevel len = min(len, space); 106703831d35Sstevel space_at_end = end - wr; 106803831d35Sstevel 106903831d35Sstevel if (rd > wr || rd <= wr && space_at_end >= len) { /* one piece */ 107003831d35Sstevel /* write console data */ 107103831d35Sstevel rv = iosram_write(SBBC_CONSOLE_KEY, wr, buf, len); 107203831d35Sstevel if (rv != 0) goto out; 107303831d35Sstevel } else { /* break into two pieces because of circular buffer */ 107403831d35Sstevel /* write console data */ 107503831d35Sstevel if (space_at_end) { 107603831d35Sstevel rv = iosram_write(SBBC_CONSOLE_KEY, 107703831d35Sstevel wr, buf, space_at_end); 107803831d35Sstevel if (rv != 0) goto out; 107903831d35Sstevel } 108003831d35Sstevel if (len - space_at_end) { 108103831d35Sstevel rv = iosram_write(SBBC_CONSOLE_KEY, 108203831d35Sstevel begin, buf+space_at_end, len-space_at_end); 108303831d35Sstevel if (rv != 0) goto out; 108403831d35Sstevel } 108503831d35Sstevel } 108603831d35Sstevel return (len); 108703831d35Sstevel out: 108803831d35Sstevel sgcn_log_error(RW_CONSOLE_WRITE, rv); 108903831d35Sstevel return (-1); 109003831d35Sstevel } 109103831d35Sstevel 109203831d35Sstevel /* 109303831d35Sstevel * Read from circular buffer in IOSRAM 109403831d35Sstevel * input: 109503831d35Sstevel * buf preallocated buffer in memory 109603831d35Sstevel * len size of buf 109703831d35Sstevel * begin, end, rd, wr buffer pointers 109803831d35Sstevel * return value: 109903831d35Sstevel * actual bytes read 110003831d35Sstevel */ 110103831d35Sstevel /* ARGSUSED */ 110203831d35Sstevel static int 110303831d35Sstevel circular_buffer_read(int begin, int end, int rd, int wr, caddr_t buf, int len) 110403831d35Sstevel { 110503831d35Sstevel int size, nbytes, nbytes_at_end; 110603831d35Sstevel int rv = 0; 110703831d35Sstevel 110803831d35Sstevel size = end - begin; 110903831d35Sstevel if (size <= 0) { 111003831d35Sstevel rv = EINVAL; 111103831d35Sstevel goto out; 111203831d35Sstevel } 111303831d35Sstevel nbytes = (wr - rd + size) % size; 111403831d35Sstevel 111503831d35Sstevel nbytes = min(nbytes, len); 111603831d35Sstevel 111703831d35Sstevel if (wr > rd) { /* one piece */ 111803831d35Sstevel rv = iosram_read(SBBC_CONSOLE_KEY, rd, buf, nbytes); 111903831d35Sstevel if (rv != 0) goto out; 112003831d35Sstevel } else { /* break into two pieces because of circular buffer */ 112103831d35Sstevel nbytes_at_end = min(nbytes, end - rd); 112203831d35Sstevel /* read console data */ 112303831d35Sstevel if (nbytes_at_end) { 112403831d35Sstevel rv = iosram_read(SBBC_CONSOLE_KEY, 112503831d35Sstevel rd, buf, nbytes_at_end); 112603831d35Sstevel if (rv != 0) goto out; 112703831d35Sstevel } 112803831d35Sstevel if (nbytes-nbytes_at_end) { 112903831d35Sstevel rv = iosram_read(SBBC_CONSOLE_KEY, 113003831d35Sstevel begin, buf+nbytes_at_end, nbytes-nbytes_at_end); 113103831d35Sstevel if (rv != 0) goto out; 113203831d35Sstevel } 113303831d35Sstevel } 113403831d35Sstevel return (nbytes); 113503831d35Sstevel out: 113603831d35Sstevel sgcn_log_error(RW_CONSOLE_READ, rv); 113703831d35Sstevel return (-1); 113803831d35Sstevel } 113903831d35Sstevel 114003831d35Sstevel /* 114103831d35Sstevel * Check for abort character sequence, copied from zs_async.c 114203831d35Sstevel */ 114303831d35Sstevel #define CNTRL(c) ((c)&037) 114403831d35Sstevel 114503831d35Sstevel static boolean_t 114603831d35Sstevel abort_charseq_recognize(uchar_t ch) 114703831d35Sstevel { 114803831d35Sstevel static int state = 0; 114903831d35Sstevel static char sequence[] = { '\r', '~', CNTRL('b') }; 115003831d35Sstevel 115103831d35Sstevel if (ch == sequence[state]) { 115203831d35Sstevel if (++state >= sizeof (sequence)) { 115303831d35Sstevel state = 0; 115403831d35Sstevel return (B_TRUE); 115503831d35Sstevel } 115603831d35Sstevel } else { 115703831d35Sstevel state = (ch == sequence[0]) ? 1 : 0; 115803831d35Sstevel } 115903831d35Sstevel return (B_FALSE); 116003831d35Sstevel } 116103831d35Sstevel 116203831d35Sstevel static void 116303831d35Sstevel sg_abort_seq_handler(char *msg) 116403831d35Sstevel { 116503831d35Sstevel char key_switch; 116603831d35Sstevel int rv; 116703831d35Sstevel 116803831d35Sstevel /* read virtual keyswitch position from IOSRAM */ 116903831d35Sstevel rv = iosram_read(SBBC_KEYSWITCH_KEY, 0, &key_switch, 1); 117003831d35Sstevel if (rv != 0) { 117103831d35Sstevel /* default to not secure if read failed */ 117203831d35Sstevel cmn_err(CE_NOTE, "!Read keyswitch failed (%d)", rv); 117303831d35Sstevel key_switch = 0; 117403831d35Sstevel } 117503831d35Sstevel if (key_switch & SG_KEYSWITCH_POSN_SECURE) { 117603831d35Sstevel cmn_err(CE_NOTE, "!Keyswitch is in secure mode"); 117703831d35Sstevel } else { 117803831d35Sstevel debug_enter(msg); 117903831d35Sstevel } 118003831d35Sstevel } 118103831d35Sstevel 118203831d35Sstevel static int 118303831d35Sstevel sgcn_rsrv(queue_t *q) 118403831d35Sstevel { 118503831d35Sstevel mblk_t *mp; 118603831d35Sstevel 118703831d35Sstevel if (sgcn_stopped == TRUE) { 118803831d35Sstevel return (0); 118903831d35Sstevel } 119003831d35Sstevel 119103831d35Sstevel mutex_enter(&sgcn_state->sgcn_lock); 119203831d35Sstevel sgcn_state->sgcn_sc_active = gethrestime_sec(); 119303831d35Sstevel mutex_exit(&sgcn_state->sgcn_lock); 119403831d35Sstevel 119503831d35Sstevel while ((mp = getq(q)) != NULL) { 119603831d35Sstevel if (canputnext(q)) { 119703831d35Sstevel putnext(q, mp); 119803831d35Sstevel } else if (mp->b_datap->db_type >= QPCTL) { 1199*07d06da5SSurya Prakki (void) putbq(q, mp); 120003831d35Sstevel } 120103831d35Sstevel } 120203831d35Sstevel 120303831d35Sstevel return (0); 120403831d35Sstevel } 120503831d35Sstevel 120603831d35Sstevel /* ARGSUSED */ 120703831d35Sstevel static int 120803831d35Sstevel sgcn_wsrv(queue_t *q) 120903831d35Sstevel { 121003831d35Sstevel if (sgcn_stopped == TRUE) 121103831d35Sstevel return (0); 121203831d35Sstevel 121303831d35Sstevel mutex_enter(&sgcn_state->sgcn_lock); 121403831d35Sstevel sgcn_state->sgcn_sc_active = gethrestime_sec(); 121503831d35Sstevel mutex_exit(&sgcn_state->sgcn_lock); 121603831d35Sstevel 121703831d35Sstevel if (sgcn_state->sgcn_writeq != NULL) 121803831d35Sstevel sgcn_start(); 121903831d35Sstevel 122003831d35Sstevel return (0); 122103831d35Sstevel } 1222