11708Sstevel /*
21708Sstevel * CDDL HEADER START
31708Sstevel *
41708Sstevel * The contents of this file are subject to the terms of the
57656SSherry.Moore@Sun.COM * Common Development and Distribution License (the "License").
67656SSherry.Moore@Sun.COM * 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 */
21*11311SSurya.Prakki@Sun.COM
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 * MT STREAMS Virtual Console Device Driver
301708Sstevel */
311708Sstevel
321708Sstevel #include <sys/types.h>
331708Sstevel #include <sys/open.h>
341708Sstevel #include <sys/param.h>
351708Sstevel #include <sys/systm.h>
361708Sstevel #include <sys/signal.h>
371708Sstevel #include <sys/cred.h>
381708Sstevel #include <sys/user.h>
391708Sstevel #include <sys/proc.h>
401708Sstevel #include <sys/disp.h>
411708Sstevel #include <sys/vnode.h>
421708Sstevel #include <sys/uio.h>
431708Sstevel #include <sys/buf.h>
441708Sstevel #include <sys/file.h>
451708Sstevel #include <sys/kmem.h>
461708Sstevel #include <sys/stat.h>
471708Sstevel #include <sys/stream.h>
481708Sstevel #include <sys/stropts.h>
491708Sstevel #include <sys/strsubr.h>
501708Sstevel #include <sys/strsun.h>
511708Sstevel #include <sys/tty.h>
521708Sstevel #include <sys/ptyvar.h>
531708Sstevel #include <sys/poll.h>
541708Sstevel #include <sys/debug.h>
551708Sstevel #include <sys/conf.h>
561708Sstevel #include <sys/ddi.h>
571708Sstevel #include <sys/sunddi.h>
581708Sstevel #include <sys/errno.h>
591708Sstevel #include <sys/modctl.h>
601708Sstevel
611708Sstevel #include <sys/sc_cvc.h>
621708Sstevel #include <sys/sc_cvcio.h>
631708Sstevel #include <sys/iosramio.h>
641708Sstevel
651708Sstevel static int cvc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
661708Sstevel static int cvc_attach(dev_info_t *, ddi_attach_cmd_t);
671708Sstevel static int cvc_detach(dev_info_t *, ddi_detach_cmd_t);
681708Sstevel static int cvc_open(register queue_t *, dev_t *, int, int, cred_t *);
691708Sstevel static int cvc_close(queue_t *, int, cred_t *);
701708Sstevel static int cvc_wput(queue_t *, mblk_t *);
711708Sstevel static int cvc_wsrv(queue_t *);
721708Sstevel static void cvc_ioctl(queue_t *, mblk_t *);
731708Sstevel static void cvc_reioctl(void *);
741708Sstevel static void cvc_input_daemon(void);
751708Sstevel static void cvc_send_to_iosram(mblk_t **chainpp);
761708Sstevel static void cvc_flush_queue(void *);
771708Sstevel static void cvc_iosram_ops(uint8_t);
781708Sstevel static void cvc_getstr(char *cp);
791708Sstevel static void cvc_win_resize(int clear_flag);
801708Sstevel
811708Sstevel #define ESUCCESS 0
821708Sstevel #ifndef TRUE
831708Sstevel #define TRUE 1
841708Sstevel #define FALSE 0
851708Sstevel #endif
861708Sstevel
871708Sstevel /*
881708Sstevel * Private copy of devinfo pointer; cvc_info uses it.
891708Sstevel */
901708Sstevel static dev_info_t *cvcdip;
911708Sstevel
921708Sstevel /*
931708Sstevel * This structure reflects the layout of data in CONI and CONO. If you are
941708Sstevel * going to add fields that don't get written into those chunks, be sure to
951708Sstevel * place them _after_ the buffer field.
961708Sstevel */
971708Sstevel typedef struct cvc_buf {
981708Sstevel ushort_t count;
991708Sstevel uchar_t buffer[MAX_XFER_COUTPUT];
1001708Sstevel } cvc_buf_t;
1011708Sstevel
1021708Sstevel typedef struct cvc_s {
1031708Sstevel bufcall_id_t cvc_wbufcid;
1041708Sstevel tty_common_t cvc_tty;
1051708Sstevel } cvc_t;
1061708Sstevel
1071708Sstevel cvc_t cvc_common_tty;
1081708Sstevel
1091708Sstevel static struct module_info cvcm_info = {
1101708Sstevel 1313, /* mi_idnum Bad luck number ;-) */
1111708Sstevel "cvc", /* mi_idname */
1121708Sstevel 0, /* mi_minpsz */
1131708Sstevel INFPSZ, /* mi_maxpsz */
1141708Sstevel 2048, /* mi_hiwat */
1151708Sstevel 2048 /* mi_lowat */
1161708Sstevel };
1171708Sstevel
1181708Sstevel static struct qinit cvcrinit = {
1191708Sstevel NULL, /* qi_putp */
1201708Sstevel NULL, /* qi_srvp */
1211708Sstevel cvc_open, /* qi_qopen */
1221708Sstevel cvc_close, /* qi_qclose */
1231708Sstevel NULL, /* qi_qadmin */
1241708Sstevel &cvcm_info, /* qi_minfo */
1251708Sstevel NULL /* qi_mstat */
1261708Sstevel };
1271708Sstevel
1281708Sstevel static struct qinit cvcwinit = {
1291708Sstevel cvc_wput, /* qi_putp */
1301708Sstevel cvc_wsrv, /* qi_srvp */
1311708Sstevel cvc_open, /* qi_qopen */
1321708Sstevel cvc_close, /* qi_qclose */
1331708Sstevel NULL, /* qi_qadmin */
1341708Sstevel &cvcm_info, /* qi_minfo */
1351708Sstevel NULL /* qi_mstat */
1361708Sstevel };
1371708Sstevel
1381708Sstevel struct streamtab cvcinfo = {
1391708Sstevel &cvcrinit, /* st_rdinit */
1401708Sstevel &cvcwinit, /* st_wrinit */
1411708Sstevel NULL, /* st_muxrinit */
1421708Sstevel NULL /* st_muxwrinit */
1431708Sstevel };
1441708Sstevel
1451708Sstevel static krwlock_t cvclock; /* lock protecting everything here */
1461708Sstevel static queue_t *cvcinput_q; /* queue for console input */
1471708Sstevel static queue_t *cvcoutput_q; /* queue for console output */
1481708Sstevel static int cvc_instance = -1;
1491708Sstevel static int cvc_stopped = 0;
1501708Sstevel static int cvc_suspended = 0;
1511708Sstevel
1521708Sstevel kthread_id_t cvc_input_daemon_thread; /* just to aid debugging */
1531708Sstevel static kmutex_t cvcmutex; /* protects input */
1541708Sstevel static kmutex_t cvc_iosram_input_mutex; /* protects IOSRAM inp buff */
1551708Sstevel static int input_ok = 0; /* true when stream is valid */
1561708Sstevel
1571708Sstevel static int via_iosram = 0; /* toggle switch */
1581708Sstevel static timeout_id_t cvc_timeout_id = (timeout_id_t)-1;
1591708Sstevel static int input_daemon_started = 0;
1601708Sstevel
1611708Sstevel /* debugging functions */
1621708Sstevel #ifdef DEBUG
1631708Sstevel uint32_t cvc_dbg_flags = 0x0;
1641708Sstevel static void cvc_dbg(uint32_t flag, char *fmt,
1651708Sstevel uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5);
1661708Sstevel #endif
1671708Sstevel
1681708Sstevel /*
1691708Sstevel * Module linkage information for the kernel.
1701708Sstevel */
1711708Sstevel
1721708Sstevel DDI_DEFINE_STREAM_OPS(cvcops, nulldev, nulldev, cvc_attach, cvc_detach,
1737656SSherry.Moore@Sun.COM nodev, cvc_info, (D_NEW|D_MTPERQ|D_MP), &cvcinfo,
1747656SSherry.Moore@Sun.COM ddi_quiesce_not_supported);
1751708Sstevel
1761708Sstevel extern int nodev(), nulldev();
1771708Sstevel extern struct mod_ops mod_driverops;
1781708Sstevel
1791708Sstevel static struct modldrv modldrv = {
1801708Sstevel &mod_driverops, /* Type of module. This one is a pseudo driver */
1817656SSherry.Moore@Sun.COM "CVC driver 'cvc'",
1821708Sstevel &cvcops, /* driver ops */
1831708Sstevel };
1841708Sstevel
1851708Sstevel static struct modlinkage modlinkage = {
1861708Sstevel MODREV_1,
1871708Sstevel &modldrv,
1881708Sstevel NULL
1891708Sstevel };
1901708Sstevel
1911708Sstevel int
_init()1921708Sstevel _init()
1931708Sstevel {
1941708Sstevel int status;
1951708Sstevel
1961708Sstevel status = mod_install(&modlinkage);
1971708Sstevel if (status == 0) {
1981708Sstevel mutex_init(&cvcmutex, NULL, MUTEX_DEFAULT, NULL);
1991708Sstevel }
2001708Sstevel return (status);
2011708Sstevel }
2021708Sstevel
2031708Sstevel int
_fini()2041708Sstevel _fini()
2051708Sstevel {
2061708Sstevel return (EBUSY);
2071708Sstevel }
2081708Sstevel
2091708Sstevel int
_info(struct modinfo * modinfop)2101708Sstevel _info(struct modinfo *modinfop)
2111708Sstevel {
2121708Sstevel return (mod_info(&modlinkage, modinfop));
2131708Sstevel }
2141708Sstevel
2151708Sstevel /*
2161708Sstevel * DDI glue routines.
2171708Sstevel */
2181708Sstevel
2191708Sstevel /* ARGSUSED */
2201708Sstevel static int
cvc_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)2211708Sstevel cvc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2221708Sstevel {
2231708Sstevel static char been_here = 0;
2241708Sstevel
2251708Sstevel if (cmd == DDI_RESUME) {
2261708Sstevel cvc_suspended = 0;
2271708Sstevel if (cvcinput_q != NULL) {
2281708Sstevel qenable(WR(cvcinput_q));
2291708Sstevel }
2301708Sstevel return (DDI_SUCCESS);
2311708Sstevel }
2321708Sstevel
2331708Sstevel mutex_enter(&cvcmutex);
2341708Sstevel if (!been_here) {
2351708Sstevel been_here = 1;
2361708Sstevel mutex_init(&cvc_iosram_input_mutex, NULL, MUTEX_DEFAULT, NULL);
2371708Sstevel rw_init(&cvclock, NULL, RW_DRIVER, NULL);
2381708Sstevel cvc_instance = ddi_get_instance(devi);
2391708Sstevel } else {
2401708Sstevel #if defined(DEBUG)
2411708Sstevel cmn_err(CE_NOTE,
2427656SSherry.Moore@Sun.COM "cvc_attach: called multiple times!! (instance = %d)",
2437656SSherry.Moore@Sun.COM ddi_get_instance(devi));
2441708Sstevel #endif /* DEBUG */
2451708Sstevel mutex_exit(&cvcmutex);
2461708Sstevel return (DDI_SUCCESS);
2471708Sstevel }
2481708Sstevel mutex_exit(&cvcmutex);
2491708Sstevel
2501708Sstevel if (ddi_create_minor_node(devi, "cvc", S_IFCHR,
2511708Sstevel 0, NULL, NULL) == DDI_FAILURE) {
2521708Sstevel ddi_remove_minor_node(devi, NULL);
2531708Sstevel return (-1);
2541708Sstevel }
2551708Sstevel cvcdip = devi;
2561708Sstevel cvcinput_q = NULL;
2571708Sstevel cvcoutput_q = NULL;
2581708Sstevel
2591708Sstevel CVC_DBG0(CVC_DBG_ATTACH, "Attached");
2601708Sstevel
2611708Sstevel return (DDI_SUCCESS);
2621708Sstevel }
2631708Sstevel
2641708Sstevel static int
cvc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2651708Sstevel cvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2661708Sstevel {
2671708Sstevel if (cmd == DDI_SUSPEND) {
2681708Sstevel cvc_suspended = 1;
2691708Sstevel } else {
2701708Sstevel if (cmd != DDI_DETACH) {
2711708Sstevel return (DDI_FAILURE);
2721708Sstevel }
2731708Sstevel /*
2741708Sstevel * XXX this doesn't even begin to address the detach
2751708Sstevel * issues - it doesn't terminate the outstanding thread,
2761708Sstevel * it doesn't clean up mutexes, kill the timeout routine
2771708Sstevel * etc.
2781708Sstevel */
2791708Sstevel if (cvc_instance == ddi_get_instance(dip)) {
2801708Sstevel ddi_remove_minor_node(dip, NULL);
2811708Sstevel }
2821708Sstevel }
2831708Sstevel
2841708Sstevel CVC_DBG0(CVC_DBG_DETACH, "Detached");
2851708Sstevel
2861708Sstevel return (DDI_SUCCESS);
2871708Sstevel }
2881708Sstevel
2891708Sstevel /* ARGSUSED */
2901708Sstevel static int
cvc_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)2911708Sstevel cvc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
2921708Sstevel {
2931708Sstevel register int error;
2941708Sstevel
2951708Sstevel switch (infocmd) {
2961708Sstevel case DDI_INFO_DEVT2DEVINFO:
2971708Sstevel if (cvcdip == NULL) {
2981708Sstevel error = DDI_FAILURE;
2991708Sstevel } else {
3001708Sstevel *result = (void *)cvcdip;
3011708Sstevel error = DDI_SUCCESS;
3021708Sstevel }
3031708Sstevel break;
3041708Sstevel case DDI_INFO_DEVT2INSTANCE:
3051708Sstevel *result = (void *)0;
3061708Sstevel error = DDI_SUCCESS;
3071708Sstevel break;
3081708Sstevel default:
3091708Sstevel error = DDI_FAILURE;
3101708Sstevel }
3111708Sstevel return (error);
3121708Sstevel }
3131708Sstevel
3141708Sstevel /* ARGSUSED */
3151708Sstevel static int
cvc_open(register queue_t * q,dev_t * devp,int flag,int sflag,cred_t * crp)3161708Sstevel cvc_open(register queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
3171708Sstevel {
3181708Sstevel register int unit = getminor(*devp);
3191708Sstevel register int err = DDI_SUCCESS;
3201708Sstevel tty_common_t *tty;
3211708Sstevel cvc_t *cp;
3221708Sstevel
3231708Sstevel if (unit != 0)
3241708Sstevel return (ENXIO);
3251708Sstevel
3261708Sstevel if (q->q_ptr)
3271708Sstevel return (0);
3281708Sstevel
3291708Sstevel cp = (cvc_t *)&cvc_common_tty;
3301708Sstevel bzero((caddr_t)cp, sizeof (cvc_t));
3311708Sstevel cp->cvc_wbufcid = 0;
3321708Sstevel tty = &cp->cvc_tty;
3331708Sstevel tty->t_readq = q;
3341708Sstevel tty->t_writeq = WR(q);
3351708Sstevel WR(q)->q_ptr = q->q_ptr = (caddr_t)cp;
3361708Sstevel cvcinput_q = RD(q); /* save for cvc_redir */
3371708Sstevel qprocson(q);
3381708Sstevel mutex_enter(&cvcmutex);
3391708Sstevel input_ok = 1;
3401708Sstevel
3411708Sstevel /*
3421708Sstevel * Start the thread that handles input polling if it hasn't been started
3431708Sstevel * previously.
3441708Sstevel */
3451708Sstevel if (!input_daemon_started) {
3461708Sstevel input_daemon_started = 1;
3471708Sstevel mutex_exit(&cvcmutex);
3481708Sstevel
3491708Sstevel cvc_input_daemon_thread = thread_create(NULL, 0,
3501708Sstevel cvc_input_daemon, NULL, 0, &p0, TS_RUN, minclsyspri);
3511708Sstevel CVC_DBG0(CVC_DBG_IOSRAM_RD, "Started input daemon");
3521708Sstevel } else {
3531708Sstevel mutex_exit(&cvcmutex);
3541708Sstevel }
3551708Sstevel
3561708Sstevel /*
3571708Sstevel * Set the console window size.
3581708Sstevel */
3591708Sstevel mutex_enter(&cvc_iosram_input_mutex);
3601708Sstevel cvc_win_resize(FALSE);
3611708Sstevel mutex_exit(&cvc_iosram_input_mutex);
3621708Sstevel
3631708Sstevel CVC_DBG0(CVC_DBG_OPEN, "Plumbed successfully");
3641708Sstevel
3651708Sstevel return (err);
3661708Sstevel }
3671708Sstevel
3681708Sstevel /* ARGSUSED */
3691708Sstevel static int
cvc_close(queue_t * q,int flag,cred_t * crp)3701708Sstevel cvc_close(queue_t *q, int flag, cred_t *crp)
3711708Sstevel {
3721708Sstevel register int err = DDI_SUCCESS;
3731708Sstevel register cvc_t *cp;
3741708Sstevel
3751708Sstevel mutex_enter(&cvcmutex);
3761708Sstevel input_ok = 0;
3771708Sstevel mutex_exit(&cvcmutex);
3781708Sstevel
3791708Sstevel cp = q->q_ptr;
3801708Sstevel if (cp->cvc_wbufcid != 0) {
3811708Sstevel unbufcall(cp->cvc_wbufcid);
3821708Sstevel }
3831708Sstevel ttycommon_close(&cp->cvc_tty);
3841708Sstevel WR(q)->q_ptr = q->q_ptr = NULL;
3851708Sstevel cvcinput_q = NULL;
3861708Sstevel bzero((caddr_t)cp, sizeof (cvc_t));
3871708Sstevel qprocsoff(q);
3881708Sstevel
3891708Sstevel CVC_DBG0(CVC_DBG_CLOSE, "Un-plumbed successfully");
3901708Sstevel
3911708Sstevel return (err);
3921708Sstevel }
3931708Sstevel
3941708Sstevel
3951708Sstevel /*
3961708Sstevel * cvc_wput()
3971708Sstevel * cn driver does a strwrite of console output data to rconsvp which has
3981708Sstevel * been set by consconfig. The data enters the cvc stream at the streamhead
3991708Sstevel * and flows thru ttycompat and ldterm which have been pushed on the
4001708Sstevel * stream. Console output data gets sent out either to cvcredir, if the
4011708Sstevel * network path is available and selected, or to IOSRAM otherwise. Data is
4021708Sstevel * sent to cvcredir via its read queue (cvcoutput_q, which gets set in
4031708Sstevel * cvc_register()). If the IOSRAM path is selected, or if previous mblks
4041708Sstevel * are currently queued up for processing, the new mblk will be queued
4051708Sstevel * and handled later on by cvc_wsrv.
4061708Sstevel */
4071708Sstevel static int
cvc_wput(queue_t * q,mblk_t * mp)4081708Sstevel cvc_wput(queue_t *q, mblk_t *mp)
4091708Sstevel {
4101708Sstevel int error = 0;
4111708Sstevel
4121708Sstevel rw_enter(&cvclock, RW_READER);
4131708Sstevel
4141708Sstevel CVC_DBG2(CVC_DBG_WPUT, "mp 0x%x db_type 0x%x",
4157656SSherry.Moore@Sun.COM mp, mp->b_datap->db_type);
4161708Sstevel
4171708Sstevel switch (mp->b_datap->db_type) {
4181708Sstevel
4191708Sstevel case M_IOCTL:
4201708Sstevel case M_CTL: {
4211708Sstevel struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
4221708Sstevel
4231708Sstevel switch (iocp->ioc_cmd) {
4241708Sstevel /*
4251708Sstevel * These ioctls are only supposed to be
4261708Sstevel * processed after everything else that is
4271708Sstevel * already queued awaiting processing, so throw
4281708Sstevel * them on the queue and let cvc_wsrv handle
4291708Sstevel * them.
4301708Sstevel */
4311708Sstevel case TCSETSW:
4321708Sstevel case TCSETSF:
4331708Sstevel case TCSETAW:
4341708Sstevel case TCSETAF:
4351708Sstevel case TCSBRK:
436*11311SSurya.Prakki@Sun.COM (void) putq(q, mp);
4371708Sstevel break;
4381708Sstevel
4391708Sstevel default:
4401708Sstevel cvc_ioctl(q, mp);
4411708Sstevel }
4421708Sstevel break;
4431708Sstevel }
4441708Sstevel
4451708Sstevel case M_FLUSH:
4461708Sstevel if (*mp->b_rptr & FLUSHW) {
4471708Sstevel /*
4481708Sstevel * Flush our write queue.
4491708Sstevel */
4501708Sstevel flushq(q, FLUSHDATA);
4511708Sstevel *mp->b_rptr &= ~FLUSHW;
4521708Sstevel }
4531708Sstevel if (*mp->b_rptr & FLUSHR) {
4541708Sstevel flushq(RD(q), FLUSHDATA);
4551708Sstevel qreply(q, mp);
4561708Sstevel } else
4571708Sstevel freemsg(mp);
4581708Sstevel break;
4591708Sstevel
4601708Sstevel case M_STOP:
4611708Sstevel cvc_stopped = 1;
4621708Sstevel freemsg(mp);
4631708Sstevel break;
4641708Sstevel
4651708Sstevel case M_START:
4661708Sstevel cvc_stopped = 0;
4671708Sstevel freemsg(mp);
4681708Sstevel qenable(q); /* Start up delayed messages */
4691708Sstevel break;
4701708Sstevel
4711708Sstevel case M_READ:
4721708Sstevel /*
4731708Sstevel * ldterm handles this (VMIN/VTIME processing).
4741708Sstevel */
4751708Sstevel freemsg(mp);
4761708Sstevel break;
4771708Sstevel
4781708Sstevel default:
4791708Sstevel cmn_err(CE_WARN, "cvc_wput: unexpected mblk type - mp ="
480*11311SSurya.Prakki@Sun.COM " 0x%p, type = 0x%x", (void *)mp,
481*11311SSurya.Prakki@Sun.COM mp->b_datap->db_type);
4821708Sstevel freemsg(mp);
4831708Sstevel break;
4841708Sstevel
4851708Sstevel case M_DATA:
4861708Sstevel /*
4871708Sstevel * If there are other mblks queued up for transmission,
4881708Sstevel * or we're using IOSRAM either because cvcredir hasn't
4891708Sstevel * registered yet or because we were configured that
4901708Sstevel * way, or cvc has been stopped or suspended, place this
4911708Sstevel * mblk on the input queue for future processing.
4921708Sstevel * Otherwise, hand it off to cvcredir for transmission
4931708Sstevel * via the network.
4941708Sstevel */
4951708Sstevel if (q->q_first != NULL || cvcoutput_q == NULL ||
4961708Sstevel via_iosram || cvc_stopped == 1 ||
4971708Sstevel cvc_suspended == 1) {
4981708Sstevel (void) putq(q, mp);
4991708Sstevel } else {
5001708Sstevel /*
5011708Sstevel * XXX - should canputnext be called here?
5021708Sstevel * Starfire's cvc doesn't do that, and it
5031708Sstevel * appears to work anyway.
5041708Sstevel */
5051708Sstevel (void) putnext(cvcoutput_q, mp);
5061708Sstevel }
5071708Sstevel break;
5081708Sstevel
5091708Sstevel }
5101708Sstevel rw_exit(&cvclock);
5111708Sstevel return (error);
5121708Sstevel }
5131708Sstevel
5141708Sstevel /*
5151708Sstevel * cvc_wsrv()
5161708Sstevel * cvc_wsrv handles mblks that have been queued by cvc_wput either because
5171708Sstevel * the IOSRAM path was selected or the queue contained preceding mblks. To
5181708Sstevel * optimize processing (particularly if the IOSRAM path is selected), all
5191708Sstevel * mblks are pulled off of the queue and chained together. Then, if there
5201708Sstevel * are any mblks on the chain, they are either forwarded to cvcredir or
5211708Sstevel * sent for IOSRAM processing as appropriate given current circumstances.
5221708Sstevel * IOSRAM processing may not be able to handle all of the data in the
5231708Sstevel * chain, in which case the remaining data is placed back on the queue and
5241708Sstevel * a timeout routine is registered to reschedule cvc_wsrv in the future.
5251708Sstevel * Automatic scheduling of the queue is disabled (noenable(q)) while
5261708Sstevel * cvc_wsrv is running to avoid superfluous calls.
5271708Sstevel */
5281708Sstevel static int
cvc_wsrv(queue_t * q)5291708Sstevel cvc_wsrv(queue_t *q)
5301708Sstevel {
5311708Sstevel mblk_t *total_mp = NULL;
5321708Sstevel mblk_t *mp;
5331708Sstevel
5341708Sstevel if (cvc_stopped == 1 || cvc_suspended == 1) {
5351708Sstevel return (0);
5361708Sstevel }
5371708Sstevel
5381708Sstevel rw_enter(&cvclock, RW_READER);
5391708Sstevel noenable(q);
5401708Sstevel
5411708Sstevel /*
5421708Sstevel * If there's already a timeout registered for scheduling this routine
5431708Sstevel * in the future, it's a safe bet that we don't want to run right now.
5441708Sstevel */
5451708Sstevel if (cvc_timeout_id != (timeout_id_t)-1) {
5461708Sstevel enableok(q);
5471708Sstevel rw_exit(&cvclock);
5481708Sstevel return (0);
5491708Sstevel }
5501708Sstevel
5511708Sstevel /*
5521708Sstevel * Start by linking all of the queued M_DATA mblks into a single chain
5531708Sstevel * so we can flush as much as possible to IOSRAM (if we choose that
5541708Sstevel * route).
5551708Sstevel */
5561708Sstevel while ((mp = getq(q)) != NULL) {
5571708Sstevel /*
5581708Sstevel * Technically, certain IOCTLs are supposed to be processed only
5591708Sstevel * after all preceding data has completely "drained". In an
5601708Sstevel * attempt to support that, we delay processing of those IOCTLs
5611708Sstevel * until this point. It is still possible that an IOCTL will be
5621708Sstevel * processed before all preceding data is drained, for instance
5631708Sstevel * in the case where not all of the preceding data would fit
5641708Sstevel * into IOSRAM and we have to place it back on the queue.
5651708Sstevel * However, since none of these IOCTLs really appear to have any
5661708Sstevel * relevance for cvc, and we weren't supporting delayed
5671708Sstevel * processing at _all_ previously, this partial implementation
5681708Sstevel * should suffice. (Fully implementing the delayed IOCTL
5691708Sstevel * processing would be unjustifiably difficult given the nature
5701708Sstevel * of the underlying IOSRAM console protocol.)
5711708Sstevel */
5721708Sstevel if (mp->b_datap->db_type == M_IOCTL) {
5731708Sstevel cvc_ioctl(q, mp);
5741708Sstevel continue;
5751708Sstevel }
5761708Sstevel
5771708Sstevel /*
5781708Sstevel * We know that only M_IOCTL and M_DATA blocks are placed on our
5791708Sstevel * queue. Since this block isn't an M_IOCTL, it must be M_DATA.
5801708Sstevel */
5811708Sstevel if (total_mp != NULL) {
5821708Sstevel linkb(total_mp, mp);
5831708Sstevel } else {
5841708Sstevel total_mp = mp;
5851708Sstevel }
5861708Sstevel }
5871708Sstevel
5881708Sstevel /*
5891708Sstevel * Do we actually have anything to do?
5901708Sstevel */
5911708Sstevel if (total_mp == NULL) {
5921708Sstevel enableok(q);
5931708Sstevel rw_exit(&cvclock);
5941708Sstevel return (0);
5951708Sstevel }
5961708Sstevel
5971708Sstevel /*
5981708Sstevel * Yes, we do, so send the data to either cvcredir or IOSRAM as
5991708Sstevel * appropriate. In the latter case, we might not be able to transmit
6001708Sstevel * everything right now, so re-queue the remainder.
6011708Sstevel */
6021708Sstevel if (cvcoutput_q != NULL && !via_iosram) {
6031708Sstevel CVC_DBG0(CVC_DBG_NETWORK_WR, "Sending to cvcredir.");
6041708Sstevel /*
6051708Sstevel * XXX - should canputnext be called here? Starfire's cvc
6061708Sstevel * doesn't do that, and it appears to work anyway.
6071708Sstevel */
6081708Sstevel (void) putnext(cvcoutput_q, total_mp);
6091708Sstevel } else {
6101708Sstevel CVC_DBG0(CVC_DBG_IOSRAM_WR, "Send to IOSRAM.");
6111708Sstevel cvc_send_to_iosram(&total_mp);
6121708Sstevel if (total_mp != NULL) {
6131708Sstevel (void) putbq(q, total_mp);
6141708Sstevel }
6151708Sstevel }
6161708Sstevel
6171708Sstevel /*
6181708Sstevel * If there is still data queued at this point, make sure the queue
6191708Sstevel * gets scheduled again after an appropriate delay (which has been
6201708Sstevel * somewhat arbitrarily selected as half of the SC's input polling
6211708Sstevel * frequency).
6221708Sstevel */
6231708Sstevel enableok(q);
6241708Sstevel if (q->q_first != NULL) {
6251708Sstevel if (cvc_timeout_id == (timeout_id_t)-1) {
6261708Sstevel cvc_timeout_id = timeout(cvc_flush_queue,
6271708Sstevel NULL, drv_usectohz(CVC_IOSRAM_POLL_USECS / 2));
6281708Sstevel }
6291708Sstevel }
6301708Sstevel rw_exit(&cvclock);
6311708Sstevel return (0);
6321708Sstevel }
6331708Sstevel
6341708Sstevel
6351708Sstevel /*
6361708Sstevel * cvc_ioctl()
6371708Sstevel * handle normal console ioctls.
6381708Sstevel */
6391708Sstevel static void
cvc_ioctl(register queue_t * q,register mblk_t * mp)6401708Sstevel cvc_ioctl(register queue_t *q, register mblk_t *mp)
6411708Sstevel {
6421708Sstevel register cvc_t *cp = q->q_ptr;
6431708Sstevel int datasize;
6441708Sstevel int error = 0;
6451708Sstevel
6461708Sstevel /*
6471708Sstevel * Let ttycommon_ioctl take the first shot at processing the ioctl. If
6481708Sstevel * it fails because it can't allocate memory, schedule processing of the
6491708Sstevel * ioctl later when a proper buffer is available. The mblk that
6501708Sstevel * couldn't be processed will have been stored in the tty structure by
6511708Sstevel * ttycommon_ioctl.
6521708Sstevel */
6531708Sstevel datasize = ttycommon_ioctl(&cp->cvc_tty, q, mp, &error);
6541708Sstevel if (datasize != 0) {
6551708Sstevel if (cp->cvc_wbufcid) {
6561708Sstevel unbufcall(cp->cvc_wbufcid);
6571708Sstevel }
6581708Sstevel cp->cvc_wbufcid = bufcall(datasize, BPRI_HI, cvc_reioctl, cp);
6591708Sstevel return;
6601708Sstevel }
6611708Sstevel
6621708Sstevel /*
6631708Sstevel * ttycommon_ioctl didn't do anything, but there's nothing we really
6641708Sstevel * support either with the exception of TCSBRK, which is supported
6651708Sstevel * only to appear a bit more like a serial device for software that
6661708Sstevel * expects TCSBRK to work.
6671708Sstevel */
6681708Sstevel if (error != 0) {
6691708Sstevel struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
6701708Sstevel
6711708Sstevel if (iocp->ioc_cmd == TCSBRK) {
6721708Sstevel miocack(q, mp, 0, 0);
6731708Sstevel } else {
6741708Sstevel miocnak(q, mp, 0, EINVAL);
6751708Sstevel }
6761708Sstevel } else {
6771708Sstevel qreply(q, mp);
6781708Sstevel }
6791708Sstevel }
6801708Sstevel
6811708Sstevel
6821708Sstevel /*
6831708Sstevel * cvc_redir()
6841708Sstevel * called from cvcredir:cvcr_wput() to handle console input
6851708Sstevel * data. This routine puts the cvcredir write (downstream) data
6861708Sstevel * onto the cvc read (upstream) queues.
6871708Sstevel */
6881708Sstevel int
cvc_redir(mblk_t * mp)6891708Sstevel cvc_redir(mblk_t *mp)
6901708Sstevel {
6911708Sstevel register struct iocblk *iocp;
6921708Sstevel int rv = 1;
6931708Sstevel
6941708Sstevel /*
6951708Sstevel * This function shouldn't be called if cvcredir hasn't registered yet.
6961708Sstevel */
6971708Sstevel if (cvcinput_q == NULL) {
6981708Sstevel /*
6991708Sstevel * Need to let caller know that it may be necessary for them to
7001708Sstevel * free the message buffer, so return 0.
7011708Sstevel */
7021708Sstevel CVC_DBG0(CVC_DBG_REDIR, "redirection not enabled");
7031708Sstevel cmn_err(CE_WARN, "cvc_redir: cvcinput_q NULL!");
7041708Sstevel return (0);
7051708Sstevel }
7061708Sstevel
7071708Sstevel CVC_DBG1(CVC_DBG_REDIR, "type 0x%x", mp->b_datap->db_type);
7081708Sstevel if (mp->b_datap->db_type == M_DATA) {
7091708Sstevel /*
7101708Sstevel * XXX - should canputnext be called here? Starfire's cvc
7111708Sstevel * doesn't do that, and it appears to work anyway.
7121708Sstevel */
7131708Sstevel CVC_DBG1(CVC_DBG_NETWORK_RD, "Sending mp 0x%x", mp);
7141708Sstevel (void) putnext(cvcinput_q, mp);
7151708Sstevel } else if (mp->b_datap->db_type == M_IOCTL) {
7161708Sstevel /*
7171708Sstevel * The cvcredir driver filters out ioctl mblks we wouldn't
7181708Sstevel * understand, so we don't have to check for every conceivable
7191708Sstevel * ioc_cmd. However, additional ioctls may be supported (again)
7201708Sstevel * some day, so the code is structured to check the value even
7211708Sstevel * though there's only one that is currently supported.
7221708Sstevel */
7231708Sstevel iocp = (struct iocblk *)mp->b_rptr;
7241708Sstevel if (iocp->ioc_cmd == CVC_DISCONNECT) {
7251708Sstevel (void) putnextctl(cvcinput_q, M_HANGUP);
7261708Sstevel }
7271708Sstevel } else {
7281708Sstevel /*
7291708Sstevel * Since we don't know what this mblk is, we're not going to
7301708Sstevel * process it.
7311708Sstevel */
7321708Sstevel CVC_DBG1(CVC_DBG_REDIR, "unrecognized mblk type: %d",
7331708Sstevel mp->b_datap->db_type);
7341708Sstevel rv = 0;
7351708Sstevel }
7361708Sstevel
7371708Sstevel return (rv);
7381708Sstevel }
7391708Sstevel
7401708Sstevel
7411708Sstevel /*
7421708Sstevel * cvc_register()
7431708Sstevel * called from cvcredir to register it's queues. cvc
7441708Sstevel * receives data from cn via the streamhead and sends it to cvcredir
7451708Sstevel * via pointers to cvcredir's queues.
7461708Sstevel */
7471708Sstevel int
cvc_register(queue_t * q)7481708Sstevel cvc_register(queue_t *q)
7491708Sstevel {
7501708Sstevel int error = -1;
7511708Sstevel
7521708Sstevel if (cvcinput_q == NULL)
7531708Sstevel cmn_err(CE_WARN, "cvc_register: register w/ no console open!");
7541708Sstevel rw_enter(&cvclock, RW_WRITER);
7551708Sstevel if (cvcoutput_q == NULL) {
7561708Sstevel cvcoutput_q = RD(q); /* Make sure its the upstream q */
7571708Sstevel qprocson(cvcoutput_q); /* must be done within cvclock */
7581708Sstevel error = 0;
7591708Sstevel } else {
7601708Sstevel /*
7611708Sstevel * cmn_err will call us, so release lock.
7621708Sstevel */
7631708Sstevel rw_exit(&cvclock);
7641708Sstevel if (cvcoutput_q == q)
7651708Sstevel cmn_err(CE_WARN, "cvc_register: duplicate q!");
7661708Sstevel else
7671708Sstevel cmn_err(CE_WARN, "cvc_register: nondup q = 0x%p",
768*11311SSurya.Prakki@Sun.COM (void *)q);
7691708Sstevel return (error);
7701708Sstevel }
7711708Sstevel rw_exit(&cvclock);
7721708Sstevel return (error);
7731708Sstevel }
7741708Sstevel
7751708Sstevel
7761708Sstevel /*
7771708Sstevel * cvc_unregister()
7781708Sstevel * called from cvcredir to clear pointers to its queues.
7791708Sstevel * cvcredir no longer wants to send or receive data.
7801708Sstevel */
7811708Sstevel void
cvc_unregister(queue_t * q)7821708Sstevel cvc_unregister(queue_t *q)
7831708Sstevel {
7841708Sstevel rw_enter(&cvclock, RW_WRITER);
7851708Sstevel if (q == cvcoutput_q) {
7861708Sstevel qprocsoff(cvcoutput_q); /* must be done within cvclock */
7871708Sstevel cvcoutput_q = NULL;
7881708Sstevel } else {
7891708Sstevel rw_exit(&cvclock);
790*11311SSurya.Prakki@Sun.COM cmn_err(CE_WARN, "cvc_unregister: q = 0x%p not registered",
791*11311SSurya.Prakki@Sun.COM (void *)q);
7921708Sstevel return;
7931708Sstevel }
7941708Sstevel rw_exit(&cvclock);
7951708Sstevel }
7961708Sstevel
7971708Sstevel
7981708Sstevel /*
7991708Sstevel * cvc_reioctl()
8001708Sstevel * Retry an "ioctl", now that "bufcall" claims we may be able
8011708Sstevel * to allocate the buffer we need.
8021708Sstevel */
8031708Sstevel static void
cvc_reioctl(void * unit)8041708Sstevel cvc_reioctl(void *unit)
8051708Sstevel {
8061708Sstevel register queue_t *q;
8071708Sstevel register mblk_t *mp;
8081708Sstevel register cvc_t *cp = (cvc_t *)unit;
8091708Sstevel
8101708Sstevel /*
8111708Sstevel * The bufcall is no longer pending.
8121708Sstevel */
8131708Sstevel if (!cp->cvc_wbufcid) {
8141708Sstevel return;
8151708Sstevel }
8161708Sstevel cp->cvc_wbufcid = 0;
8171708Sstevel if ((q = cp->cvc_tty.t_writeq) == NULL) {
8181708Sstevel return;
8191708Sstevel }
8201708Sstevel if ((mp = cp->cvc_tty.t_iocpending) != NULL) {
8211708Sstevel /* not pending any more */
8221708Sstevel cp->cvc_tty.t_iocpending = NULL;
8231708Sstevel cvc_ioctl(q, mp);
8241708Sstevel }
8251708Sstevel }
8261708Sstevel
8271708Sstevel
8281708Sstevel /*
8291708Sstevel * cvc_iosram_ops()
8301708Sstevel * Process commands sent to cvc from netcon_server via IOSRAM
8311708Sstevel */
8321708Sstevel static void
cvc_iosram_ops(uint8_t op)8331708Sstevel cvc_iosram_ops(uint8_t op)
8341708Sstevel {
8351708Sstevel int rval = ESUCCESS;
8361708Sstevel static uint8_t stale_op = 0;
8371708Sstevel
8381708Sstevel ASSERT(MUTEX_HELD(&cvc_iosram_input_mutex));
8391708Sstevel
8401708Sstevel CVC_DBG1(CVC_DBG_IOSRAM_CNTL, "cntl msg 0x%x", op);
8411708Sstevel
8421708Sstevel /*
8431708Sstevel * If this is a repeated notice of a command that was previously
8441708Sstevel * processed but couldn't be cleared due to EAGAIN (tunnel switch in
8451708Sstevel * progress), just clear the data_valid flag and return.
8461708Sstevel */
8471708Sstevel if (op == stale_op) {
8481708Sstevel if (iosram_set_flag(IOSRAM_KEY_CONC, IOSRAM_DATA_INVALID,
8491708Sstevel IOSRAM_INT_NONE) == 0) {
8501708Sstevel stale_op = 0;
8511708Sstevel }
8521708Sstevel return;
8531708Sstevel }
8541708Sstevel stale_op = 0;
8551708Sstevel
8561708Sstevel switch (op) {
8571708Sstevel case CVC_IOSRAM_BREAK: /* A console break (L1-A) */
8581708Sstevel abort_sequence_enter((char *)NULL);
8591708Sstevel break;
8601708Sstevel
8611708Sstevel case CVC_IOSRAM_DISCONNECT: /* Break connection, hang up */
8621708Sstevel if (cvcinput_q)
8631708Sstevel (void) putnextctl(cvcinput_q, M_HANGUP);
8641708Sstevel break;
8651708Sstevel
8661708Sstevel case CVC_IOSRAM_VIA_NET: /* console via network */
8671708Sstevel via_iosram = 0;
8681708Sstevel break;
8691708Sstevel
8701708Sstevel case CVC_IOSRAM_VIA_IOSRAM: /* console via iosram */
8711708Sstevel via_iosram = 1;
8721708Sstevel /*
8731708Sstevel * Tell cvcd to close any network connection it has.
8741708Sstevel */
8751708Sstevel rw_enter(&cvclock, RW_READER);
8761708Sstevel if (cvcoutput_q != NULL) {
8771708Sstevel (void) putnextctl(cvcoutput_q, M_HANGUP);
8781708Sstevel }
8791708Sstevel rw_exit(&cvclock);
8801708Sstevel break;
8811708Sstevel
8821708Sstevel case CVC_IOSRAM_WIN_RESIZE: /* console window size data */
8831708Sstevel /*
8841708Sstevel * In the case of window resizing, we don't want to
8851708Sstevel * record a stale_op value because we should always use
8861708Sstevel * the most recent winsize info, which could change
8871708Sstevel * between the time that we fail to clear the flag and
8881708Sstevel * the next time we try to process the command. So,
8891708Sstevel * we'll just let cvc_win_resize clear the data_valid
8901708Sstevel * flag itself (hence the TRUE parameter) and not worry
8911708Sstevel * about whether or not it succeeds.
8921708Sstevel */
8931708Sstevel cvc_win_resize(TRUE);
8941708Sstevel return;
8951708Sstevel /* NOTREACHED */
8961708Sstevel
8971708Sstevel default:
8981708Sstevel cmn_err(CE_WARN, "cvc: unknown IOSRAM opcode %d", op);
8991708Sstevel break;
9001708Sstevel }
9011708Sstevel
9021708Sstevel /*
9031708Sstevel * Clear CONC's data_valid flag to indicate that the chunk is available
9041708Sstevel * for further communications. If the flag can't be cleared due to an
9051708Sstevel * error, record the op value so we'll know to ignore it when we see it
9061708Sstevel * on the next poll.
9071708Sstevel */
9081708Sstevel rval = iosram_set_flag(IOSRAM_KEY_CONC, IOSRAM_DATA_INVALID,
9091708Sstevel IOSRAM_INT_NONE);
9101708Sstevel if (rval != 0) {
9111708Sstevel stale_op = op;
9121708Sstevel if (rval != EAGAIN) {
9131708Sstevel cmn_err(CE_WARN,
9141708Sstevel "cvc_iosram_ops: set flag for cntlbuf ret %d",
9151708Sstevel rval);
9161708Sstevel }
9171708Sstevel }
9181708Sstevel }
9191708Sstevel
9201708Sstevel
9211708Sstevel /*
9221708Sstevel * cvc_send_to_iosram()
9231708Sstevel * Flush as much data as possible to the CONO chunk. If successful, free
9241708Sstevel * any mblks that were completely transmitted, update the b_rptr field in
9251708Sstevel * the first remaining mblk if it was partially transmitted, and update the
9261708Sstevel * caller's pointer to the new head of the mblk chain. Since the software
9271708Sstevel * that will be pulling this data out of IOSRAM (dxs on the SC) is just
9281708Sstevel * polling at some frequency, we avoid attempts to flush data to IOSRAM any
9291708Sstevel * faster than a large divisor of that polling frequency.
9301708Sstevel *
9311708Sstevel * Note that "cvc_buf_t out" is only declared "static" to keep it from
9321708Sstevel * being allocated on the stack. Allocating 1K+ structures on the stack
9331708Sstevel * seems rather antisocial.
9341708Sstevel */
9351708Sstevel static void
cvc_send_to_iosram(mblk_t ** chainpp)9361708Sstevel cvc_send_to_iosram(mblk_t **chainpp)
9371708Sstevel {
9381708Sstevel int rval;
9391708Sstevel uint8_t dvalid;
9401708Sstevel uchar_t *cp;
9411708Sstevel mblk_t *mp;
9421708Sstevel mblk_t *last_empty_mp;
9431708Sstevel static clock_t last_flush = (clock_t)-1;
9441708Sstevel static cvc_buf_t out; /* see note above about static */
9451708Sstevel
9461708Sstevel ASSERT(chainpp != NULL);
9471708Sstevel
9481708Sstevel /*
9491708Sstevel * We _do_ have something to do, right?
9501708Sstevel */
9511708Sstevel if (*chainpp == NULL) {
9521708Sstevel return;
9531708Sstevel }
9541708Sstevel
9551708Sstevel /*
9561708Sstevel * We can actually increase throughput by throttling back on attempts to
9571708Sstevel * flush data to IOSRAM, since trying to write every little bit of data
9581708Sstevel * as it shows up will actually generate more delays waiting for the SC
9591708Sstevel * to pick up each of those bits. Instead, we'll avoid attempting to
9601708Sstevel * write data to IOSRAM any faster than half of the polling frequency we
9611708Sstevel * expect the SC to be using.
9621708Sstevel */
9631708Sstevel if (ddi_get_lbolt() - last_flush <
9641708Sstevel drv_usectohz(CVC_IOSRAM_POLL_USECS / 2)) {
9651708Sstevel return;
9661708Sstevel }
9671708Sstevel
9681708Sstevel /*
9691708Sstevel * If IOSRAM is inaccessible or the CONO chunk still holds data that
9701708Sstevel * hasn't been picked up by the SC, there's nothing we can do right now.
9711708Sstevel */
9721708Sstevel rval = iosram_get_flag(IOSRAM_KEY_CONO, &dvalid, NULL);
9731708Sstevel if ((rval != 0) || (dvalid == IOSRAM_DATA_VALID)) {
9741708Sstevel if ((rval != 0) && (rval != EAGAIN)) {
9751708Sstevel cmn_err(CE_WARN, "cvc_send_to_iosram: get_flag ret %d",
9761708Sstevel rval);
9771708Sstevel }
9781708Sstevel return;
9791708Sstevel }
9801708Sstevel
9811708Sstevel /*
9821708Sstevel * Copy up to MAX_XFER_COUTPUT chars from the mblk chain into a buffer.
9831708Sstevel * Don't change any of the mblks just yet, since we can't be certain
9841708Sstevel * that we'll be successful in writing data to the CONO chunk.
9851708Sstevel */
9861708Sstevel out.count = 0;
9871708Sstevel mp = *chainpp;
9881708Sstevel cp = mp->b_rptr;
9891708Sstevel last_empty_mp = NULL;
9901708Sstevel while ((mp != NULL) && (out.count < MAX_XFER_COUTPUT)) {
9911708Sstevel /*
9921708Sstevel * Process as many of the characters in the current mblk as
9931708Sstevel * possible.
9941708Sstevel */
9951708Sstevel while ((cp != mp->b_wptr) && (out.count < MAX_XFER_COUTPUT)) {
9961708Sstevel out.buffer[out.count++] = *cp++;
9971708Sstevel }
9981708Sstevel
9991708Sstevel /*
10001708Sstevel * Did we process that entire mblk? If so, move on to the next
10011708Sstevel * one. If not, we're done filling the buffer even if there's
10021708Sstevel * space left, because apparently there wasn't room to process
10031708Sstevel * the next character.
10041708Sstevel */
10051708Sstevel if (cp != mp->b_wptr) {
10061708Sstevel break;
10071708Sstevel }
10081708Sstevel
10091708Sstevel /*
10101708Sstevel * When this loop terminates, last_empty_mp will point to the
10111708Sstevel * last mblk that was completely processed, mp will point to the
10121708Sstevel * following mblk (or NULL if no more mblks exist), and cp will
10131708Sstevel * point to the first untransmitted character in the mblk
10141708Sstevel * pointed to by mp. We'll need this data to update the mblk
10151708Sstevel * chain if all of the data is successfully transmitted.
10161708Sstevel */
10171708Sstevel last_empty_mp = mp;
10181708Sstevel mp = mp->b_cont;
10191708Sstevel cp = (mp != NULL) ? mp->b_rptr : NULL;
10201708Sstevel }
10211708Sstevel
10221708Sstevel /*
10231708Sstevel * If we succeeded in preparing some data, try to transmit it through
10241708Sstevel * IOSRAM. First write the count and the data, which can be done in a
10251708Sstevel * single operation thanks to the buffer structure we use, then set the
10261708Sstevel * data_valid flag if the first step succeeded.
10271708Sstevel */
10281708Sstevel if (out.count != 0) {
10291708Sstevel rval = iosram_wr(IOSRAM_KEY_CONO, COUNT_OFFSET,
10301708Sstevel CONSBUF_COUNT_SIZE + out.count, (caddr_t)&out);
10311708Sstevel if ((rval != 0) && (rval != EAGAIN)) {
10321708Sstevel cmn_err(CE_WARN, "cvc_putc: write ret %d", rval);
10331708Sstevel }
10341708Sstevel
10351708Sstevel /* if the data write succeeded, set the data_valid flag */
10361708Sstevel if (rval == 0) {
10371708Sstevel rval = iosram_set_flag(IOSRAM_KEY_CONO,
10381708Sstevel IOSRAM_DATA_VALID, IOSRAM_INT_NONE);
10391708Sstevel if ((rval != 0) && (rval != EAGAIN)) {
10401708Sstevel cmn_err(CE_WARN,
10411708Sstevel "cvc_putc: set flags for outbuf ret %d",
10421708Sstevel rval);
10431708Sstevel }
10441708Sstevel }
10451708Sstevel
10461708Sstevel /*
10471708Sstevel * If we successfully transmitted any data, modify the caller's
10481708Sstevel * mblk chain to remove the data that was transmitted, freeing
10491708Sstevel * all mblks that were completely processed.
10501708Sstevel */
10511708Sstevel if (rval == 0) {
10521708Sstevel last_flush = ddi_get_lbolt();
10531708Sstevel
10541708Sstevel /*
10551708Sstevel * If any data is left over, update the b_rptr field of
10561708Sstevel * the first remaining mblk in case some of its data was
10571708Sstevel * processed.
10581708Sstevel */
10591708Sstevel if (mp != NULL) {
10601708Sstevel mp->b_rptr = cp;
10611708Sstevel }
10621708Sstevel
10631708Sstevel /*
10641708Sstevel * If any mblks have been emptied, unlink them from the
10651708Sstevel * residual chain, free them, and update the caller's
10661708Sstevel * mblk pointer.
10671708Sstevel */
10681708Sstevel if (last_empty_mp != NULL) {
10691708Sstevel last_empty_mp->b_cont = NULL;
10701708Sstevel freemsg(*chainpp);
10711708Sstevel *chainpp = mp;
10721708Sstevel }
10731708Sstevel }
10741708Sstevel }
10751708Sstevel }
10761708Sstevel
10771708Sstevel
10781708Sstevel /*
10791708Sstevel * cvc_flush_queue()
10801708Sstevel * Tell the STREAMS subsystem to schedule cvc_wsrv to process the queue we
10811708Sstevel * use to gather console output.
10821708Sstevel */
10831708Sstevel /* ARGSUSED */
10841708Sstevel static void
cvc_flush_queue(void * notused)10851708Sstevel cvc_flush_queue(void *notused)
10861708Sstevel {
10871708Sstevel rw_enter(&cvclock, RW_WRITER);
10881708Sstevel if (cvcinput_q != NULL) {
10891708Sstevel qenable(WR(cvcinput_q));
10901708Sstevel }
10911708Sstevel
10921708Sstevel cvc_timeout_id = (timeout_id_t)-1;
10931708Sstevel rw_exit(&cvclock);
10941708Sstevel }
10951708Sstevel
10961708Sstevel
10971708Sstevel /*
10981708Sstevel * cvc_getstr()
10991708Sstevel * Poll IOSRAM for console input while available.
11001708Sstevel */
11011708Sstevel static void
cvc_getstr(char * cp)11021708Sstevel cvc_getstr(char *cp)
11031708Sstevel {
11041708Sstevel short count;
11051708Sstevel uint8_t command = 0;
11061708Sstevel int rval = ESUCCESS;
11071708Sstevel uint8_t dvalid = IOSRAM_DATA_INVALID;
11081708Sstevel uint8_t intrpending = 0;
11091708Sstevel
11101708Sstevel mutex_enter(&cvc_iosram_input_mutex);
11111708Sstevel while (dvalid == IOSRAM_DATA_INVALID) {
11121708Sstevel /*
11131708Sstevel * Check the CONC data_valid flag to see if a control message is
11141708Sstevel * available.
11151708Sstevel */
11161708Sstevel rval = iosram_get_flag(IOSRAM_KEY_CONC, &dvalid, &intrpending);
11171708Sstevel if ((rval != 0) && (rval != EAGAIN)) {
11181708Sstevel cmn_err(CE_WARN,
11191708Sstevel "cvc_getstr: get flag for cntl ret %d", rval);
11201708Sstevel }
11211708Sstevel
11221708Sstevel /*
11231708Sstevel * If a control message is available, try to read and process
11241708Sstevel * it.
11251708Sstevel */
11261708Sstevel if ((dvalid == IOSRAM_DATA_VALID) && (rval == 0)) {
11271708Sstevel /* read the control reg offset */
11281708Sstevel rval = iosram_rd(IOSRAM_KEY_CONC,
11291708Sstevel CVC_CTL_OFFSET(command), CVC_CTL_SIZE(command),
11301708Sstevel (caddr_t)&command);
11311708Sstevel if ((rval != 0) && (rval != EAGAIN)) {
11321708Sstevel cmn_err(CE_WARN,
11331708Sstevel "cvc_getstr: read for command ret %d",
11341708Sstevel rval);
11351708Sstevel }
11361708Sstevel
11371708Sstevel /* process the cntl msg and clear the data_valid flag */
11381708Sstevel if (rval == 0) {
11391708Sstevel cvc_iosram_ops(command);
11401708Sstevel }
11411708Sstevel }
11421708Sstevel
11431708Sstevel /*
11441708Sstevel * Check the CONI data_valid flag to see if console input data
11451708Sstevel * is available.
11461708Sstevel */
11471708Sstevel rval = iosram_get_flag(IOSRAM_KEY_CONI, &dvalid, &intrpending);
11481708Sstevel if ((rval != 0) && (rval != EAGAIN)) {
11497656SSherry.Moore@Sun.COM cmn_err(CE_WARN,
11507656SSherry.Moore@Sun.COM "cvc_getstr: get flag for inbuf ret %d",
11511708Sstevel rval);
11521708Sstevel }
11531708Sstevel if ((rval != 0) || (dvalid != IOSRAM_DATA_VALID)) {
11541708Sstevel goto retry;
11551708Sstevel }
11561708Sstevel
11571708Sstevel /*
11581708Sstevel * Try to read the count.
11591708Sstevel */
11601708Sstevel rval = iosram_rd(IOSRAM_KEY_CONI, COUNT_OFFSET,
11611708Sstevel CONSBUF_COUNT_SIZE, (caddr_t)&count);
11621708Sstevel if (rval != 0) {
11631708Sstevel if (rval != EAGAIN) {
11641708Sstevel cmn_err(CE_WARN,
11651708Sstevel "cvc_getstr: read for count ret %d", rval);
11661708Sstevel }
11671708Sstevel goto retry;
11681708Sstevel }
11691708Sstevel
11701708Sstevel /*
11711708Sstevel * If there is data to be read, try to read it.
11721708Sstevel */
11731708Sstevel if (count != 0) {
11741708Sstevel rval = iosram_rd(IOSRAM_KEY_CONI, DATA_OFFSET, count,
11751708Sstevel (caddr_t)cp);
11761708Sstevel if (rval != 0) {
11771708Sstevel if (rval != EAGAIN) {
11781708Sstevel cmn_err(CE_WARN,
11791708Sstevel "cvc_getstr: read for count ret %d",
11801708Sstevel rval);
11811708Sstevel }
11821708Sstevel goto retry;
11831708Sstevel }
11841708Sstevel cp[count] = '\0';
11851708Sstevel }
11861708Sstevel
11871708Sstevel /*
11881708Sstevel * Try to clear the data_valid flag to indicate that whatever
11891708Sstevel * was in CONI was read successfully. If successful, and some
11901708Sstevel * data was read, break out of the loop to return to the caller.
11911708Sstevel */
11921708Sstevel rval = iosram_set_flag(IOSRAM_KEY_CONI, IOSRAM_DATA_INVALID,
11931708Sstevel IOSRAM_INT_NONE);
11941708Sstevel if (rval != 0) {
11951708Sstevel if (rval != EAGAIN) {
11961708Sstevel cmn_err(CE_WARN,
11971708Sstevel "cvc_getstr: set flag for inbuf ret %d",
11981708Sstevel rval);
11991708Sstevel }
12001708Sstevel } else if (count != 0) {
12011708Sstevel CVC_DBG1(CVC_DBG_IOSRAM_RD, "Read 0x%x", count);
12021708Sstevel break;
12031708Sstevel }
12041708Sstevel
12051708Sstevel /*
12061708Sstevel * Use a smaller delay between checks of IOSRAM for input
12071708Sstevel * when cvcd/cvcredir are not running or "via_iosram" has
12081708Sstevel * been set.
12091708Sstevel * We don't go away completely when i/o is going through the
12101708Sstevel * network via cvcd since a command may be sent via IOSRAM
12111708Sstevel * to switch if the network is down or hung.
12121708Sstevel */
12131708Sstevel retry:
12141708Sstevel if ((cvcoutput_q == NULL) || (via_iosram))
12151708Sstevel delay(drv_usectohz(CVC_IOSRAM_POLL_USECS));
12161708Sstevel else
12171708Sstevel delay(drv_usectohz(CVC_IOSRAM_POLL_USECS * 10));
12181708Sstevel
12191708Sstevel }
12201708Sstevel
12211708Sstevel mutex_exit(&cvc_iosram_input_mutex);
12221708Sstevel }
12231708Sstevel
12241708Sstevel
12251708Sstevel /*
12261708Sstevel * cvc_input_daemon()
12271708Sstevel * this function runs as a separate kernel thread and polls IOSRAM for
12281708Sstevel * input, and possibly put it on read stream for the console.
12291708Sstevel * There are two poll rates (implemented in cvc_getstr):
12301708Sstevel * 100 000 uS (10 Hz) - no cvcd communications || via_iosram
12311708Sstevel * 1000 000 uS ( 1 Hz) - cvcd communications
12321708Sstevel * This continues to run even if there are network console communications
12331708Sstevel * in order to handle out-of-band signaling.
12341708Sstevel */
12351708Sstevel /* ARGSUSED */
12361708Sstevel static void
cvc_input_daemon(void)12371708Sstevel cvc_input_daemon(void)
12381708Sstevel {
12391708Sstevel char linebuf[MAX_XFER_CINPUT + 1];
12401708Sstevel char *cp;
12411708Sstevel mblk_t *mbp;
12421708Sstevel int c;
12431708Sstevel int dropped_read = 0;
12441708Sstevel
12451708Sstevel for (;;) {
12461708Sstevel cvc_getstr(linebuf);
12471708Sstevel
12481708Sstevel mbp = allocb(strlen(linebuf), BPRI_MED);
12491708Sstevel if (mbp == NULL) { /* drop it & go on if no buffer */
12501708Sstevel if (!dropped_read) {
12511708Sstevel cmn_err(CE_WARN, "cvc_input_daemon: "
12521708Sstevel "dropping IOSRAM reads");
12531708Sstevel }
12541708Sstevel dropped_read++;
12551708Sstevel continue;
12561708Sstevel }
12571708Sstevel
12581708Sstevel if (dropped_read) {
12591708Sstevel cmn_err(CE_WARN,
12601708Sstevel "cvc_input_daemon: dropped %d IOSRAM reads",
12611708Sstevel dropped_read);
12621708Sstevel dropped_read = 0;
12631708Sstevel }
12641708Sstevel
12651708Sstevel for (cp = linebuf; *cp != '\0'; cp++) {
12661708Sstevel c = (int)*cp;
12671708Sstevel if (c == '\r')
12681708Sstevel c = '\n';
12691708Sstevel c &= 0177;
12701708Sstevel *mbp->b_wptr = (char)c;
12711708Sstevel mbp->b_wptr++;
12721708Sstevel }
12731708Sstevel mutex_enter(&cvcmutex);
12741708Sstevel if (input_ok) {
12751708Sstevel if (cvcinput_q == NULL) {
12761708Sstevel cmn_err(CE_WARN,
12771708Sstevel "cvc_input_daemon: cvcinput_q is NULL!");
12781708Sstevel } else {
12791708Sstevel /*
12801708Sstevel * XXX - should canputnext be called here?
12811708Sstevel * Starfire's cvc doesn't do that, and it
12821708Sstevel * appears to work anyway.
12831708Sstevel */
12841708Sstevel (void) putnext(cvcinput_q, mbp);
12851708Sstevel }
12861708Sstevel } else {
12871708Sstevel freemsg(mbp);
12881708Sstevel }
12891708Sstevel mutex_exit(&cvcmutex);
12901708Sstevel }
12911708Sstevel
12921708Sstevel /* NOTREACHED */
12931708Sstevel }
12941708Sstevel
12951708Sstevel /*
12961708Sstevel * cvc_win_resize()
12971708Sstevel * cvc_win_resize will read winsize data from the CONC IOSRAM chunk and set
12981708Sstevel * the console window size accordingly. If indicated by the caller, CONC's
12991708Sstevel * data_valid flag will also be cleared. The flag isn't cleared in all
13001708Sstevel * cases because we need to process winsize data at startup without waiting
13011708Sstevel * for a command.
13021708Sstevel */
13031708Sstevel static void
cvc_win_resize(int clear_flag)13041708Sstevel cvc_win_resize(int clear_flag)
13051708Sstevel {
13061708Sstevel int rval;
13071708Sstevel uint16_t rows;
13081708Sstevel uint16_t cols;
13091708Sstevel uint16_t xpixels;
13101708Sstevel uint16_t ypixels;
13111708Sstevel tty_common_t *tty;
13121708Sstevel cvc_t *cp;
13131708Sstevel struct winsize ws;
13141708Sstevel
13151708Sstevel /*
13161708Sstevel * Start by reading the new window size out of the CONC chunk and, if
13171708Sstevel * requested, clearing CONC's data_valid flag. If any of that fails,
13181708Sstevel * return immediately. (Note that the rather bulky condition in the
13191708Sstevel * two "if" statements takes advantage of C's short-circuit logic
13201708Sstevel * evaluation)
13211708Sstevel */
13221708Sstevel if (((rval = iosram_rd(IOSRAM_KEY_CONC, CVC_CTL_OFFSET(winsize_rows),
13231708Sstevel CVC_CTL_SIZE(winsize_rows), (caddr_t)&rows)) != 0) ||
13241708Sstevel ((rval = iosram_rd(IOSRAM_KEY_CONC, CVC_CTL_OFFSET(winsize_cols),
13251708Sstevel CVC_CTL_SIZE(winsize_cols), (caddr_t)&cols)) != 0) ||
13261708Sstevel ((rval = iosram_rd(IOSRAM_KEY_CONC,
13271708Sstevel CVC_CTL_OFFSET(winsize_xpixels), CVC_CTL_SIZE(winsize_xpixels),
13281708Sstevel (caddr_t)&xpixels)) != 0) || ((rval = iosram_rd(IOSRAM_KEY_CONC,
13291708Sstevel CVC_CTL_OFFSET(winsize_ypixels), CVC_CTL_SIZE(winsize_ypixels),
13301708Sstevel (caddr_t)&ypixels)) != 0)) {
13311708Sstevel if (rval != EAGAIN) {
13321708Sstevel cmn_err(CE_WARN,
13331708Sstevel "cvc_win_resize: read for ctlbuf ret %d", rval);
13341708Sstevel }
13351708Sstevel return;
13361708Sstevel }
13371708Sstevel
13381708Sstevel if (clear_flag && ((rval = iosram_set_flag(IOSRAM_KEY_CONC,
13391708Sstevel IOSRAM_DATA_INVALID, IOSRAM_INT_NONE)) != 0)) {
13401708Sstevel if (rval != EAGAIN) {
13411708Sstevel cmn_err(CE_WARN,
13421708Sstevel "cvc_win_resize: set_flag for ctlbuf ret %d", rval);
13431708Sstevel }
13441708Sstevel return;
13451708Sstevel }
13461708Sstevel
13471708Sstevel /*
13481708Sstevel * Copy the parameters from IOSRAM to a winsize struct.
13491708Sstevel */
13501708Sstevel ws.ws_row = rows;
13511708Sstevel ws.ws_col = cols;
13521708Sstevel ws.ws_xpixel = xpixels;
13531708Sstevel ws.ws_ypixel = ypixels;
13541708Sstevel
13551708Sstevel /*
13561708Sstevel * This code was taken from Starfire, and it appears to work correctly.
13571708Sstevel * However, since the original developer felt it necessary to add the
13581708Sstevel * following comment, it's probably worth preserving:
13591708Sstevel *
13601708Sstevel * XXX I hope this is safe...
13611708Sstevel */
13621708Sstevel cp = cvcinput_q->q_ptr;
13631708Sstevel tty = &cp->cvc_tty;
13641708Sstevel mutex_enter(&tty->t_excl);
13651708Sstevel if (bcmp((caddr_t)&tty->t_size, (caddr_t)&ws,
13661708Sstevel sizeof (struct winsize))) {
13671708Sstevel tty->t_size = ws;
13681708Sstevel mutex_exit(&tty->t_excl);
13691708Sstevel (void) putnextctl1(cvcinput_q, M_PCSIG,
13701708Sstevel SIGWINCH);
13711708Sstevel } else {
13721708Sstevel mutex_exit(&tty->t_excl);
13731708Sstevel }
13741708Sstevel }
13751708Sstevel
13761708Sstevel #ifdef DEBUG
13771708Sstevel
13781708Sstevel void
cvc_dbg(uint32_t flag,char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)13791708Sstevel cvc_dbg(uint32_t flag, char *fmt,
13801708Sstevel uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
13811708Sstevel {
13821708Sstevel char *s = NULL;
13831708Sstevel char buf[256];
13841708Sstevel
13851708Sstevel if (cvc_dbg_flags && ((cvc_dbg_flags & flag) == flag)) {
13861708Sstevel switch (flag) {
13871708Sstevel case CVC_DBG_ATTACH:
13881708Sstevel s = "attach";
13891708Sstevel break;
13901708Sstevel case CVC_DBG_DETACH:
13911708Sstevel s = "detach";
13921708Sstevel break;
13931708Sstevel case CVC_DBG_OPEN:
13941708Sstevel s = "open";
13951708Sstevel break;
13961708Sstevel case CVC_DBG_CLOSE:
13971708Sstevel s = "close";
13981708Sstevel break;
13991708Sstevel case CVC_DBG_IOCTL:
14001708Sstevel s = "ioctl";
14011708Sstevel break;
14021708Sstevel case CVC_DBG_REDIR:
14031708Sstevel s = "redir";
14041708Sstevel break;
14051708Sstevel case CVC_DBG_WPUT:
14061708Sstevel s = "wput";
14071708Sstevel break;
14081708Sstevel case CVC_DBG_WSRV:
14091708Sstevel s = "wsrv";
14101708Sstevel break;
14111708Sstevel case CVC_DBG_IOSRAM_WR:
14121708Sstevel s = "iosram_wr";
14131708Sstevel break;
14141708Sstevel case CVC_DBG_IOSRAM_RD:
14151708Sstevel s = "iosram_rd";
14161708Sstevel break;
14171708Sstevel case CVC_DBG_NETWORK_WR:
14181708Sstevel s = "network_wr";
14191708Sstevel break;
14201708Sstevel case CVC_DBG_NETWORK_RD:
14211708Sstevel s = "network_rd";
14221708Sstevel break;
14231708Sstevel case CVC_DBG_IOSRAM_CNTL:
14241708Sstevel s = "iosram_cntlmsg";
14251708Sstevel break;
14261708Sstevel default:
14271708Sstevel s = "Unknown debug flag";
14281708Sstevel break;
14291708Sstevel }
14301708Sstevel
1431*11311SSurya.Prakki@Sun.COM (void) sprintf(buf, "!%s_%s(%d): %s", ddi_driver_name(cvcdip),
1432*11311SSurya.Prakki@Sun.COM s, cvc_instance, fmt);
14331708Sstevel cmn_err(CE_NOTE, buf, a1, a2, a3, a4, a5);
14341708Sstevel }
14351708Sstevel }
14361708Sstevel
14371708Sstevel #endif /* DEBUG */
1438