xref: /titanic_50/usr/src/uts/sun4u/starfire/cvc/cvc.c (revision 7a286c471efbab8562f7655a82931904703fffe0)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*
28  * MT STREAMS Virtual Console Device Driver
29  */
30 
31 #include <sys/types.h>
32 #include <sys/sysmacros.h>
33 #include <sys/processor.h>
34 #include <sys/cpuvar.h>
35 #include <sys/open.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/signal.h>
39 #include <sys/cred.h>
40 #include <sys/user.h>
41 #include <sys/proc.h>
42 #include <sys/vnode.h>
43 #include <sys/uio.h>
44 #include <sys/buf.h>
45 #include <sys/file.h>
46 #include <sys/kmem.h>
47 #include <sys/vmem.h>
48 #include <sys/stat.h>
49 #include <sys/stream.h>
50 #include <sys/stropts.h>
51 #include <sys/strsubr.h>
52 #include <sys/strsun.h>
53 #include <sys/tty.h>
54 #include <sys/ptyvar.h>
55 #include <sys/poll.h>
56 #include <sys/debug.h>
57 #include <sys/conf.h>
58 
59 #include <sys/starfire.h>
60 #include <sys/mman.h>
61 #include <vm/seg_kmem.h>
62 
63 #include <sys/ddi.h>
64 #include <sys/sunddi.h>
65 #include <sys/errno.h>
66 #include <sys/modctl.h>
67 #include <sys/cpu_sgnblk_defs.h>
68 #include <sys/cvc.h>
69 #include <sys/cpu_sgn.h>
70 
71 extern void	prom_printf(char *fmt, ...);
72 
73 static int	cvc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
74 static int	cvc_attach(dev_info_t *, ddi_attach_cmd_t);
75 static int	cvc_detach(dev_info_t *, ddi_detach_cmd_t);
76 static int	cvc_open(register queue_t *, dev_t *, int, int, cred_t *);
77 static int	cvc_close(queue_t *, int, cred_t *);
78 static int	cvc_wput(queue_t *, mblk_t *);
79 static int	cvc_wsrv(queue_t *);
80 static void	cvc_ioctl(queue_t *, mblk_t *);
81 static void	cvc_ack(mblk_t *, mblk_t *, uint_t);
82 static void	cvc_reioctl(void *);
83 static void	cvc_input_daemon(void);
84 static void	cvc_putc(register int);
85 static void	cvc_flush_buf(void *);
86 static void	cvc_bbsram_ops(volatile uchar_t *);
87 
88 static caddr_t	cvc_iobuf_mapin(processorid_t);
89 static void	cvc_iobuf_mapout(processorid_t);
90 	void	cvc_assign_iocpu(processorid_t);
91 
92 /*
93  * Private copy of devinfo pointer; cvc_info uses it.
94  */
95 static dev_info_t	*cvcdip;
96 
97 /*
98  * This buffer is used to manage mapping in the I/O buffer that CVC
99  * uses when communicating with the SSP Client (netcon_server) via bbsram.
100  */
101 static caddr_t	cvc_iobufp[NCPU];
102 
103 typedef struct cvc_s {
104 	bufcall_id_t	cvc_wbufcid;
105 	tty_common_t	cvc_tty;
106 } cvc_t;
107 
108 cvc_t	cvc_common_tty;
109 
110 static struct module_info cvcm_info = {
111 	1313,		/* mi_idnum Bad luck number  ;-) */
112 	"cvc",		/* mi_idname */
113 	0,		/* mi_minpsz */
114 	INFPSZ,		/* mi_maxpsz */
115 	2048,		/* mi_hiwat */
116 	2048		/* mi_lowat */
117 };
118 
119 static struct qinit cvcrinit = {
120 	NULL,		/* qi_putp */
121 	NULL,		/* qi_srvp */
122 	cvc_open,	/* qi_qopen */
123 	cvc_close,	/* qi_qclose */
124 	NULL,		/* qi_qadmin */
125 	&cvcm_info,	/* qi_minfo */
126 	NULL		/* qi_mstat */
127 };
128 
129 static struct qinit cvcwinit = {
130 	cvc_wput,	/* qi_putp */
131 	cvc_wsrv,	/* qi_srvp */
132 	cvc_open,	/* qi_qopen */
133 	cvc_close,	/* qi_qclose */
134 	NULL,		/* qi_qadmin */
135 	&cvcm_info,	/* qi_minfo */
136 	NULL		/* qi_mstat */
137 };
138 
139 struct streamtab	cvcinfo = {
140 	&cvcrinit,	/* st_rdinit */
141 	&cvcwinit,	/* st_wrinit */
142 	NULL,		/* st_muxrinit */
143 	NULL		/* st_muxwrinit */
144 };
145 
146 #define	TIMEOUT_DELAY		100000
147 
148 #define	BBSRAM_INPUT_BUF	((volatile char *)(cvc_iobufp[cvc_iocpu] \
149 					+ BBSRAM_INPUT_COUNT_OFF))
150 
151 #define	BBSRAM_OUTPUT_BUF	((volatile char *)(cvc_iobufp[cvc_iocpu] \
152 					+ BBSRAM_OUTPUT_COUNT_OFF))
153 
154 #define	BBSRAM_INPUT_COUNT	(*((volatile short *)BBSRAM_INPUT_BUF))
155 
156 #define	BBSRAM_OUTPUT_COUNT	(*((volatile short *)BBSRAM_OUTPUT_BUF))
157 
158 #define	CVC_OUT_MAXSPIN	1024
159 
160 /* The bbsram control reg is located at the end of the I/O buffers */
161 #define	BBSRAM_CONTROL_REG	((volatile uchar_t *)(cvc_iobufp[cvc_iocpu] \
162 					+ CVC_IN_SIZE + CVC_OUT_SIZE))
163 
164 static krwlock_t	cvclock;	/* lock protecting everything here */
165 static queue_t		*cvcinput_q;	/* queue for console input */
166 static queue_t		*cvcoutput_q;	/* queue for console output */
167 static int		cvc_instance = -1;
168 static int		cvc_stopped = 0;
169 static int		cvc_suspended = 0;
170 static int		cvc_hangup_ok = 0;
171 
172 static kthread_id_t	cvc_input_daemon_thread;
173 static kmutex_t		cvcmutex;	/* protects input */
174 static kmutex_t		cvc_buf_mutex;	/* protects internal output buffer */
175 static kmutex_t		cvc_bbsram_input_mutex; /* protects BBSRAM inp buff */
176 static int		input_ok = 0;	/* true when stream is valid */
177 static int		stop_bbsram = 1; /* true when BBSRAM is not usable */
178 static int		stop_timeout = 0;
179 static uchar_t		cvc_output_buffer[MAX_XFER_OUTPUT]; /* output buffer */
180 static ushort_t		cvc_output_count = 0;
181 static int		via_bbsram = 0; /* toggle switch */
182 static timeout_id_t	cvc_timeout_id = (timeout_id_t)-1;
183 static processorid_t	cvc_iocpu = -1;	/* cpu id of cpu zero */
184 
185 /*
186  * Module linkage information for the kernel.
187  */
188 
189 DDI_DEFINE_STREAM_OPS(cvcops, nulldev, nulldev, cvc_attach, cvc_detach,
190 		    nodev, cvc_info, (D_MTPERQ | D_MP), &cvcinfo,
191 		    ddi_quiesce_not_supported);
192 
193 static struct modldrv modldrv = {
194 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
195 	"CVC driver 'cvc'",
196 	&cvcops,	/* driver ops */
197 };
198 
199 static struct modlinkage modlinkage = {
200 	MODREV_1,
201 	&modldrv,
202 	NULL
203 };
204 
205 int
206 _init(void)
207 {
208 	int	status;
209 
210 	status = mod_install(&modlinkage);
211 	if (status == 0) {
212 		mutex_init(&cvcmutex, NULL, MUTEX_DEFAULT, NULL);
213 	}
214 	return (status);
215 }
216 
217 int
218 _fini(void)
219 {
220 	return (EBUSY);
221 }
222 
223 int
224 _info(struct modinfo *modinfop)
225 {
226 	return (mod_info(&modlinkage, modinfop));
227 }
228 
229 /*
230  * DDI glue routines.
231  */
232 
233 /* ARGSUSED */
234 static int
235 cvc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
236 {
237 	static char	been_here = 0;
238 
239 	if (cmd == DDI_RESUME) {
240 		cvc_suspended = 0;
241 		return (DDI_SUCCESS);
242 	}
243 
244 	mutex_enter(&cvcmutex);
245 	if (!been_here) {
246 		been_here = 1;
247 		mutex_init(&cvc_buf_mutex, NULL, MUTEX_DEFAULT, NULL);
248 		mutex_init(&cvc_bbsram_input_mutex, NULL, MUTEX_DEFAULT, NULL);
249 		rw_init(&cvclock, NULL, RW_DRIVER, NULL);
250 		rw_enter(&cvclock, RW_WRITER);
251 		cvc_timeout_id = timeout(cvc_flush_buf, NULL,
252 		    drv_usectohz(TIMEOUT_DELAY));
253 		rw_exit(&cvclock);
254 		cvc_instance = ddi_get_instance(devi);
255 	} else {
256 #if defined(DEBUG)
257 		cmn_err(CE_NOTE,
258 		    "cvc_attach: called multiple times!! (instance = %d)",
259 		    ddi_get_instance(devi));
260 #endif /* DEBUG */
261 		return (DDI_SUCCESS);
262 	}
263 	mutex_exit(&cvcmutex);
264 
265 	if (ddi_create_minor_node(devi, "cvc", S_IFCHR,
266 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
267 		ddi_remove_minor_node(devi, NULL);
268 		return (-1);
269 	}
270 	cvcdip = devi;
271 	cvcinput_q = NULL;
272 	cvcoutput_q = NULL;
273 	return (DDI_SUCCESS);
274 }
275 
276 static int
277 cvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
278 {
279 	if (cmd == DDI_SUSPEND) {
280 		cvc_suspended = 1;
281 	} else {
282 		if (cmd != DDI_DETACH) {
283 			return (DDI_FAILURE);
284 		}
285 		/*
286 		 * XXX this doesn't even begin to address the detach
287 		 * issues - it doesn't terminate the outstanding thread,
288 		 * it doesn't clean up mutexes, kill the timeout routine
289 		 * etc.
290 		 */
291 		if (cvc_instance == ddi_get_instance(dip)) {
292 			ddi_remove_minor_node(dip, NULL);
293 		}
294 	}
295 	return (DDI_SUCCESS);
296 }
297 
298 /* ARGSUSED */
299 static int
300 cvc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
301 {
302 	register int error;
303 
304 	switch (infocmd) {
305 	case DDI_INFO_DEVT2DEVINFO:
306 		if (cvcdip == NULL) {
307 			error = DDI_FAILURE;
308 		} else {
309 			*result = (void *)cvcdip;
310 			error = DDI_SUCCESS;
311 		}
312 		break;
313 	case DDI_INFO_DEVT2INSTANCE:
314 		*result = (void *)0;
315 		error = DDI_SUCCESS;
316 		break;
317 	default:
318 		error = DDI_FAILURE;
319 	}
320 	return (error);
321 }
322 
323 /* ARGSUSED */
324 static int
325 cvc_open(register queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
326 {
327 	register int		unit = getminor(*devp);
328 	register int		err = 0;
329 	tty_common_t		*tty;
330 	cvc_t			*cp;
331 	static int		input_daemon_started;
332 
333 	if (unit != 0)
334 		return (ENXIO);
335 
336 	if (q->q_ptr)
337 		return (0);
338 
339 	cp = (cvc_t *)&cvc_common_tty;
340 	bzero((caddr_t)cp, sizeof (cvc_t));
341 	cp->cvc_wbufcid = 0;
342 	tty = &cp->cvc_tty;
343 	tty->t_readq = q;
344 	tty->t_writeq = WR(q);
345 	WR(q)->q_ptr = q->q_ptr = (caddr_t)cp;
346 	cvcinput_q = RD(q);		/* save for cvc_redir */
347 	qprocson(q);
348 	mutex_enter(&cvcmutex);
349 	input_ok = 1;
350 	if (!input_daemon_started) {
351 		extern struct cpu	*SIGBCPU;	/* bugid4141050 */
352 		extern cpu_sgnblk_t	*cpu_sgnblkp[];
353 
354 		input_daemon_started = 1;
355 		mutex_exit(&cvcmutex);
356 
357 		ASSERT(cpu_sgnblkp[SIGBCPU->cpu_id] != NULL);
358 		cvc_assign_iocpu(SIGBCPU->cpu_id);
359 
360 		cvc_input_daemon_thread = thread_create(NULL, 0,
361 		    cvc_input_daemon, NULL, 0, &p0, TS_RUN, minclsyspri);
362 	} else {
363 		mutex_exit(&cvcmutex);
364 	}
365 #ifdef lint
366 	cvc_input_daemon_thread = cvc_input_daemon_thread;
367 #endif
368 	return (err);
369 }
370 
371 /* ARGSUSED */
372 static int
373 cvc_close(queue_t *q, int flag, cred_t *crp)
374 {
375 	register int		err = 0;
376 	register cvc_t		*cp;
377 
378 	mutex_enter(&cvcmutex);
379 	input_ok = 0;
380 	mutex_exit(&cvcmutex);
381 
382 	cp = q->q_ptr;
383 	if (cp->cvc_wbufcid != 0) {
384 		unbufcall(cp->cvc_wbufcid);
385 	}
386 	ttycommon_close(&cp->cvc_tty);
387 	WR(q)->q_ptr = q->q_ptr = NULL;
388 	cvcinput_q = NULL;
389 	bzero((caddr_t)cp, sizeof (cvc_t));
390 	qprocsoff(q);
391 	return (err);
392 }
393 
394 
395 /*
396  * cvc_wput()
397  *	cn driver does a strwrite of console output data to rconsvp which
398  *	has been set by consconfig. The data enters the cvc stream at the
399  *	streamhead and flows thru ttycompat and ldterm which have been
400  *	pushed on the stream.  Console output data gets sent out either
401  *	by cvcredir (if there is a cvcd running) or bbsram (if there
402  *	isn't).
403  *	Data is sent to the cvcredir via it's read q which is cvcoutput_q
404  *	and was set in cvc_register().
405  */
406 static int
407 cvc_wput(register queue_t *q, register mblk_t *mp)
408 {
409 	int		error = 0;
410 
411 	rw_enter(&cvclock, RW_READER);
412 	switch (mp->b_datap->db_type) {
413 
414 		case M_IOCTL:
415 		case M_CTL:
416 			cvc_ioctl(q, mp);
417 			break;
418 
419 		case M_FLUSH:
420 			if (*mp->b_rptr & FLUSHW) {
421 				/*
422 				 * Flush our write queue.
423 				 */
424 				flushq(q, FLUSHDATA);
425 				*mp->b_rptr &= ~FLUSHW;
426 			}
427 			if (*mp->b_rptr & FLUSHR) {
428 				flushq(RD(q), FLUSHDATA);
429 				qreply(q, mp);
430 			} else
431 				freemsg(mp);
432 			break;
433 
434 		case M_STOP:
435 			cvc_stopped = 1;
436 			freemsg(mp);
437 			break;
438 
439 		case M_START:
440 			cvc_stopped = 0;
441 			freemsg(mp);
442 			qenable(q);  /* Start up delayed messages */
443 			break;
444 
445 		case M_READ:
446 			/*
447 			 * ldterm handles this (VMIN/VTIME processing).
448 			 */
449 			freemsg(mp);
450 			break;
451 		default:
452 			cmn_err(CE_WARN, "cvc_wput: illegal mblk = 0x%p", mp);
453 			cmn_err(CE_WARN, "cvc_wput: type = 0x%x",
454 			    mp->b_datap->db_type);
455 			/* FALLTHROUGH */
456 #ifdef lint
457 			break;
458 #endif
459 
460 		case M_DATA:
461 			if (cvc_stopped == 1 || cvc_suspended == 1) {
462 				(void) putq(q, mp);
463 				break;
464 			}
465 			if (cvcoutput_q != NULL && !via_bbsram) {
466 				/*
467 				 * Send it up past cvcredir module.
468 				 */
469 				putnext(cvcoutput_q, mp);
470 			} else {
471 				char	*msgp, c;
472 				mblk_t	*mp2 = mp;
473 				int count;
474 
475 				while (mp2 != NULL) {
476 					count = mp2->b_wptr - mp2->b_rptr;
477 					msgp = (char *)mp2->b_rptr;
478 					while (count > 0) {
479 						count--;
480 						if ((c = *msgp++) != '\0') {
481 							/* don't print NULs */
482 							cvc_putc(c);
483 						}
484 					}
485 					mp2 = mp2->b_cont;
486 				}
487 				freemsg(mp);
488 			}
489 			break;
490 
491 	}
492 	rw_exit(&cvclock);
493 	return (error);
494 }
495 
496 static int cvc_wsrv_count = 0;
497 
498 static int
499 cvc_wsrv(queue_t *q)
500 {
501 	register mblk_t *mp;
502 
503 	cvc_wsrv_count++;
504 
505 	if (cvc_stopped == 1 || cvc_suspended == 1) {
506 		return (0);
507 	}
508 
509 	rw_enter(&cvclock, RW_READER);
510 	while ((mp = getq(q)) != NULL) {
511 		if (cvcoutput_q != NULL && !via_bbsram) {
512 			/*
513 			 * Send it up past cvcredir module.
514 			 */
515 			putnext(cvcoutput_q, mp);
516 		} else {
517 			char    *msgp, c;
518 			mblk_t  *mp2 = mp;
519 			int count;
520 
521 			while (mp2 != NULL) {
522 				count = mp2->b_wptr - mp2->b_rptr;
523 				msgp = (char *)mp2->b_rptr;
524 				while (count > 0) {
525 					count--;
526 					if ((c = *msgp++) != '\0') {
527 						/* don't print NULs */
528 						cvc_putc(c);
529 					}
530 				}
531 				mp2 = mp2->b_cont;
532 			}
533 			freemsg(mp);
534 		}
535 	}
536 	rw_exit(&cvclock);
537 	return (0);
538 }
539 
540 
541 /*
542  * cvc_ioctl()
543  *	handle normal console ioctls.
544  */
545 static void
546 cvc_ioctl(register queue_t *q, register mblk_t *mp)
547 {
548 	register struct iocblk		*iocp;
549 	register tty_common_t		*tty;
550 	register cvc_t			*cp;
551 	int				datasize;
552 	int				error = 0;
553 	mblk_t				*tmp;
554 
555 	cp = q->q_ptr;
556 	tty = &cp->cvc_tty;
557 	if (tty->t_iocpending != NULL) {
558 		freemsg(tty->t_iocpending);
559 		tty->t_iocpending = NULL;
560 	}
561 	datasize = ttycommon_ioctl(tty, q, mp, &error);
562 	if (datasize != 0) {
563 		if (cp->cvc_wbufcid)
564 			unbufcall(cp->cvc_wbufcid);
565 		cp->cvc_wbufcid = bufcall(datasize, BPRI_HI, cvc_reioctl, cp);
566 		return;
567 	}
568 	if (error < 0) {
569 		iocp = (struct iocblk *)mp->b_rptr;
570 		/*
571 		 * "ttycommon_ioctl" didn't do anything; we process it here.
572 		 */
573 		error = 0;
574 		switch (iocp->ioc_cmd) {
575 
576 		/*
577 		 *  Set modem bit ioctls.  These are NOPs for us, since we
578 		 * dont control any hardware.
579 		 */
580 		case TCSBRK:
581 		case TIOCSBRK:
582 		case TIOCCBRK:
583 		case TIOCMSET:
584 		case TIOCMBIS:
585 		case TIOCMBIC:
586 			if (iocp->ioc_count != TRANSPARENT) {
587 				mioc2ack(mp, NULL, 0, 0);
588 			} else {
589 				mcopyin(mp, NULL, sizeof (int), NULL);
590 			}
591 			/* qreply done below */
592 			break;
593 
594 		/*
595 		 *  Get modem bits, we return 0 in mblk.
596 		 */
597 		case TIOCMGET:
598 			tmp = allocb(sizeof (int), BPRI_MED);
599 			if (tmp == NULL) {
600 				miocnak(q, mp, 0, EAGAIN);
601 				return;
602 			}
603 			*(int *)tmp->b_rptr = 0;
604 
605 			if (iocp->ioc_count != TRANSPARENT)
606 				mioc2ack(mp, tmp, sizeof (int), 0);
607 			else
608 				mcopyout(mp, NULL, sizeof (int), NULL, tmp);
609 			/* qreply done below */
610 			break;
611 
612 		default:
613 			/*
614 			 * If we don't understand it, it's an error. NAK it.
615 			 */
616 			error = EINVAL;
617 			break;
618 		}
619 	}
620 	if (error != 0) {
621 		iocp->ioc_error = error;
622 		mp->b_datap->db_type = M_IOCNAK;
623 	}
624 	qreply(q, mp);
625 
626 }
627 
628 
629 /*
630  * cvc_redir()
631  *	called from cvcredir:cvcr_wput() to handle console input
632  *	data. This routine puts the cvcredir write (downstream) data
633  *	onto the cvc read (upstream) queues.  Note that if `mp' is
634  *	an M_IOCTL, then it may be reused by the caller to send back
635  *	an M_IOCACK or M_IOCNAK.
636  */
637 int
638 cvc_redir(mblk_t *mp)
639 {
640 	register struct iocblk	*iocp;
641 	register tty_common_t	*tty;
642 	register cvc_t		*cp;
643 	struct winsize		*ws;
644 	int			error;
645 
646 	if (cvcinput_q == NULL) {
647 		cmn_err(CE_WARN, "cvc_redir: cvcinput_q NULL!");
648 		return (EINVAL);
649 	}
650 
651 	if (DB_TYPE(mp) != M_IOCTL) {
652 		putnext(cvcinput_q, mp);
653 		return (0);
654 	}
655 
656 	iocp = (struct iocblk *)mp->b_rptr;
657 	if (iocp->ioc_cmd == TIOCSWINSZ) {
658 		error = miocpullup(mp, sizeof (struct winsize));
659 		if (error != 0)
660 			return (error);
661 
662 		ws = (struct winsize *)mp->b_cont->b_rptr;
663 		cp = cvcinput_q->q_ptr;
664 		tty = &cp->cvc_tty;
665 		mutex_enter(&tty->t_excl);
666 		if (bcmp(&tty->t_size, ws, sizeof (struct winsize)) != 0) {
667 			tty->t_size = *ws;
668 			mutex_exit(&tty->t_excl);
669 			(void) putnextctl1(cvcinput_q, M_PCSIG, SIGWINCH);
670 		} else
671 			mutex_exit(&tty->t_excl);
672 	} else {
673 		/*
674 		 * It must be a CVC_DISCONNECT, send hangup.
675 		 */
676 		ASSERT(iocp->ioc_cmd == CVC_DISCONNECT);
677 		if (cvc_hangup_ok)
678 			(void) putnextctl(cvcinput_q, M_HANGUP);
679 	}
680 
681 	return (0);
682 }
683 
684 
685 /*
686  * cvc_register()
687  *	called from cvcredir to register it's queues.  cvc
688  *	receives data from cn via the streamhead and sends it to cvcredir
689  *	via pointers to cvcredir's queues.
690  */
691 int
692 cvc_register(queue_t *q)
693 {
694 	int error = -1;
695 
696 	if (cvcinput_q == NULL)
697 		cmn_err(CE_WARN, "cvc_register: register w/ no console open!");
698 	rw_enter(&cvclock, RW_WRITER);
699 	if (cvcoutput_q == NULL) {
700 		cvcoutput_q = RD(q);  /* Make sure its the upstream q */
701 		qprocson(cvcoutput_q);	/* must be done within cvclock */
702 		error = 0;
703 	} else {
704 		/*
705 		 * cmn_err will call us, so release lock.
706 		 */
707 		rw_exit(&cvclock);
708 		if (cvcoutput_q == q)
709 			cmn_err(CE_WARN, "cvc_register: duplicate q!");
710 		else
711 			cmn_err(CE_WARN, "cvc_register: nondup q = 0x%p",
712 			    q);
713 		return (error);
714 	}
715 
716 	/*
717 	 * Unless "via_bbsram" is set, i/o will be going through cvcd, so
718 	 * stop flushing output to BBSRAM.
719 	 */
720 	if ((cvc_timeout_id != (timeout_id_t)-1) && (!via_bbsram)) {
721 		stop_timeout = 1;
722 		(void) untimeout(cvc_timeout_id);
723 		cvc_timeout_id = (timeout_id_t)-1;
724 		cvc_hangup_ok = 1;
725 	}
726 	rw_exit(&cvclock);
727 	return (error);
728 }
729 
730 
731 /*
732  * cvc_unregister()
733  *	called from cvcredir to clear pointers to its queues.
734  *	cvcredir no longer wants to send or receive data.
735  */
736 void
737 cvc_unregister(queue_t *q)
738 {
739 	rw_enter(&cvclock, RW_WRITER);
740 	if (q == cvcoutput_q) {
741 		qprocsoff(cvcoutput_q);	/* must be done within cvclock */
742 		cvcoutput_q = NULL;
743 	} else {
744 		rw_exit(&cvclock);
745 		cmn_err(CE_WARN, "cvc_unregister: q = 0x%p not registered", q);
746 		return;
747 	}
748 
749 	/*
750 	 * i/o will not be going through cvcd, start flushing output to
751 	 * BBSRAM
752 	 */
753 	if (cvc_timeout_id == (timeout_id_t)-1) {
754 		stop_timeout = 0;
755 		cvc_timeout_id = timeout(cvc_flush_buf, NULL,
756 		    drv_usectohz(TIMEOUT_DELAY));
757 	}
758 	rw_exit(&cvclock);
759 }
760 
761 /*
762  * cvc_reioctl()
763  *	Retry an "ioctl", now that "bufcall" claims we may be able
764  *	to allocate the buffer we need.
765  */
766 static void
767 cvc_reioctl(void *unit)
768 {
769 	register queue_t	*q;
770 	register mblk_t		*mp;
771 	register cvc_t		*cp = (cvc_t *)unit;
772 
773 	/*
774 	 * The bufcall is no longer pending.
775 	 */
776 	if (!cp->cvc_wbufcid) {
777 		return;
778 	}
779 	cp->cvc_wbufcid = 0;
780 	if ((q = cp->cvc_tty.t_writeq) == NULL) {
781 		return;
782 	}
783 	if ((mp = cp->cvc_tty.t_iocpending) != NULL) {
784 		/* not pending any more */
785 		cp->cvc_tty.t_iocpending = NULL;
786 		cvc_ioctl(q, mp);
787 	}
788 }
789 
790 
791 /*
792  * cvc_bbsram_ops()
793  *	Process commands sent to cvc from netcon_server via BBSRAM
794  */
795 static void
796 cvc_bbsram_ops(volatile unsigned char *op_reg)
797 {
798 	uchar_t	 op;
799 
800 	if ((op = *op_reg) == 0)
801 		return;
802 
803 	ASSERT(MUTEX_HELD(&cvc_bbsram_input_mutex));
804 
805 	switch (op) {
806 	case CVC_BBSRAM_BREAK:		/* A console break (L1-A) */
807 		abort_sequence_enter((char *)NULL);
808 		break;
809 	case CVC_BBSRAM_DISCONNECT:	/* Break connection, hang up */
810 		if (cvcinput_q && cvc_hangup_ok)
811 			(void) putnextctl(cvcinput_q, M_HANGUP);
812 		break;
813 	case CVC_BBSRAM_VIA_NET:	/* console via network */
814 		via_bbsram = 0;
815 		/*
816 		 * stop periodic flushing of output to BBSRAM
817 		 * only if cvcredir/cvcd are present
818 		 */
819 		rw_enter(&cvclock, RW_WRITER);
820 		if (cvcoutput_q != NULL) {
821 			stop_timeout = 1;
822 			if (cvc_timeout_id != (timeout_id_t)-1) {
823 				(void) untimeout(cvc_timeout_id);
824 				cvc_timeout_id = (timeout_id_t)-1;
825 			}
826 		}
827 		rw_exit(&cvclock);
828 		break;
829 	case CVC_BBSRAM_VIA_BBSRAM:	/* console via bbsram */
830 		via_bbsram = 1;
831 		/* start periodic flushing of ouput to BBSRAM */
832 		rw_enter(&cvclock, RW_WRITER);
833 		if (cvc_timeout_id == (timeout_id_t)-1) {
834 			stop_timeout = 0;
835 			cvc_timeout_id = timeout(cvc_flush_buf,
836 			    NULL, drv_usectohz(TIMEOUT_DELAY));
837 		}
838 		rw_exit(&cvclock);
839 		break;
840 	case CVC_BBSRAM_CLOSE_NET:
841 		/*
842 		 * Send a hangup control message upstream to cvcd
843 		 * thru cvcredir.  This is an attempt to close
844 		 * out any existing network connection(if any).
845 		 * cvcoutput_q should point to the cvcredir's read
846 		 * queue.
847 		 */
848 		rw_enter(&cvclock, RW_READER);
849 		if (cvcoutput_q != NULL) {
850 			(void) putnextctl(cvcoutput_q, M_HANGUP);
851 		}
852 		rw_exit(&cvclock);
853 		break;
854 	default:
855 		cmn_err(CE_WARN, "cvc: unknown BBSRAM opcode %d\n",
856 		    (unsigned int)op);
857 		break;
858 	}
859 	*op_reg = 0;
860 }
861 
862 
863 /*
864  * cvc_putc()
865  *	Put a single character out to BBSRAM if space available.
866  */
867 static void
868 cvc_putc(register int c)
869 {
870 	static int	output_lost = 0;
871 
872 	if (c == '\n')
873 		cvc_putc('\r');
874 
875 	mutex_enter(&cvc_buf_mutex);
876 	/*
877 	 * Just exit if the buffer is already full.
878 	 * It will be up to cvc_flush_buf() to flush the buffer.
879 	 */
880 	if (cvc_output_count == MAX_XFER_OUTPUT) {
881 		output_lost = 1;
882 		mutex_exit(&cvc_buf_mutex);
883 		return;
884 	}
885 	if (output_lost)
886 		prom_printf("WARNING: overflow of cvc output buffer, "
887 		    "output lost!");
888 	output_lost = 0;
889 	cvc_output_buffer[cvc_output_count] = (unsigned char)c;
890 	cvc_output_count++;
891 	if ((cvc_output_count == MAX_XFER_OUTPUT) || (c == '\n')) {
892 		/* flush cvc's internal output buffer to BBSRAM */
893 
894 		/*
895 		 * Wait for the BBSRAM output buffer to be emptied.
896 		 * This may hang if netcon_server isn't running on the SSP
897 		 */
898 		int maxspin = CVC_OUT_MAXSPIN;
899 		while ((BBSRAM_OUTPUT_COUNT != 0) && --maxspin) {
900 			if (stop_bbsram) {
901 				mutex_exit(&cvc_buf_mutex);
902 				return;
903 			}
904 			DELAY(1000);
905 		}
906 		bcopy((caddr_t)cvc_output_buffer,
907 		    (caddr_t)(BBSRAM_OUTPUT_BUF - cvc_output_count),
908 		    cvc_output_count);
909 
910 		BBSRAM_OUTPUT_COUNT = cvc_output_count;
911 		cvc_output_count = 0;
912 	}
913 	mutex_exit(&cvc_buf_mutex);
914 }
915 
916 
917 /*
918  * cvc_flush_buf()
919  *	Flush cvc's internal output buffer to BBSRAM at regular intervals.
920  *	This should only be done if cvcd is not running or the user (via the cvc
921  *	application on the SSP) has requested that i/o go through BBSRAM.
922  */
923 /* ARGSUSED */
924 static void
925 cvc_flush_buf(void *notused)
926 {
927 	if (stop_timeout)
928 		return;
929 
930 	mutex_enter(&cvc_buf_mutex);
931 	if (cvc_output_count != 0) {
932 		/*
933 		 * Wait for the BBSRAM output buffer to be emptied.
934 		 * This may hang if netcon_server isn't running on the SSP.
935 		 */
936 		int maxspin = CVC_OUT_MAXSPIN;
937 		while ((BBSRAM_OUTPUT_COUNT != 0) && --maxspin) {
938 			if (stop_bbsram)
939 				goto exit;
940 			DELAY(1000);
941 		}
942 
943 		bcopy((caddr_t)cvc_output_buffer,
944 		    (caddr_t)BBSRAM_OUTPUT_BUF - cvc_output_count,
945 		    cvc_output_count);
946 
947 		BBSRAM_OUTPUT_COUNT = cvc_output_count;
948 		cvc_output_count = 0;
949 	}
950 exit:
951 	mutex_exit(&cvc_buf_mutex);
952 	/* rw_enter(&cvclock, RW_WRITER); */
953 	cvc_timeout_id = timeout(cvc_flush_buf, NULL,
954 	    drv_usectohz(TIMEOUT_DELAY));
955 	/* rw_exit(&cvclock); */
956 }
957 
958 
959 /*
960  * cvc_getstr()
961  *	Poll BBSRAM for console input while available.
962  */
963 static void
964 cvc_getstr(char *cp)
965 {
966 	short		count;
967 	volatile char	*lp;
968 
969 	mutex_enter(&cvc_bbsram_input_mutex);
970 	/* Poll BBSRAM for input */
971 	do {
972 		if (stop_bbsram) {
973 			*cp = '\0';	/* set string to zero-length */
974 			mutex_exit(&cvc_bbsram_input_mutex);
975 			return;
976 		}
977 		/*
978 		 * Use a smaller delay between checks of BBSRAM for input
979 		 * when cvcd/cvcredir are not running or "via_bbsram" has
980 		 * been set.
981 		 * We don't go away completely when i/o is going through the
982 		 * network via cvcd since a command may be sent via BBSRAM
983 		 * to switch if the network is down or hung.
984 		 */
985 		if ((cvcoutput_q == NULL) || (via_bbsram))
986 			delay(drv_usectohz(100000));
987 		else
988 			delay(drv_usectohz(1000000));
989 		cvc_bbsram_ops(BBSRAM_CONTROL_REG);
990 		count = BBSRAM_INPUT_COUNT;
991 	} while (count == 0);
992 
993 	lp = BBSRAM_INPUT_BUF - count;
994 
995 	while (count--) {
996 		*cp++ = *lp++;
997 	}
998 	*cp = '\0';
999 
1000 	BBSRAM_INPUT_COUNT = 0;
1001 	mutex_exit(&cvc_bbsram_input_mutex);
1002 }
1003 
1004 
1005 /*
1006  * cvc_input_daemon()
1007  *	this function runs as a separate kernel thread and polls BBSRAM for
1008  *	input, and possibly put it on read stream for the console.
1009  *	There are two poll rates (implemented in cvc_getstr):
1010  *		 100 000 uS (10 Hz) - no cvcd communications || via_bbsram
1011  *		1000 000 uS ( 1 Hz) - cvcd communications
1012  * 	This continues to run even if there are network console communications
1013  *	in order to handle out-of-band signaling.
1014  */
1015 static void
1016 cvc_input_daemon(void)
1017 {
1018 	char		linebuf[MAX_XFER_INPUT];
1019 	char		*cp;
1020 	mblk_t		*mbp;
1021 	int		c;
1022 	int		dropped_read = 0;
1023 
1024 	for (;;) {
1025 		cvc_getstr(linebuf);
1026 
1027 		mbp = allocb(strlen(linebuf), BPRI_MED);
1028 		if (mbp == NULL) {	/* drop it & go on if no buffer */
1029 			if (!dropped_read) {
1030 				cmn_err(CE_WARN,
1031 				    "cvc_input_daemon: "
1032 				    "dropping BBSRAM reads\n");
1033 			}
1034 			dropped_read++;
1035 			continue;
1036 		}
1037 		if (dropped_read) {
1038 			cmn_err(CE_WARN,
1039 			    "cvc_input_daemon: dropped %d BBSRAM reads\n",
1040 			    dropped_read);
1041 			dropped_read = 0;
1042 		}
1043 
1044 		for (cp = linebuf; *cp != '\0'; cp++) {
1045 			c = (int)*cp;
1046 			if (c == '\r')
1047 				c = '\n';
1048 			c &= 0177;
1049 			*mbp->b_wptr = (char)c;
1050 			mbp->b_wptr++;
1051 		}
1052 		mutex_enter(&cvcmutex);
1053 		if (input_ok) {
1054 			if (cvcinput_q == NULL) {
1055 				cmn_err(CE_WARN,
1056 				    "cvc_input_daemon: cvcinput_q is NULL!");
1057 			} else {
1058 				putnext(cvcinput_q, mbp);
1059 			}
1060 		} else {
1061 			freemsg(mbp);
1062 		}
1063 		mutex_exit(&cvcmutex);
1064 	}
1065 
1066 	/* NOTREACHED */
1067 }
1068 
1069 
1070 /*
1071  * cvc_bbsram_stop()
1072  *	Prevents accesses to BBSRAM. used by cvc_assign_iocpu() when
1073  *	mapping in BBSRAM to a virtual address.
1074  */
1075 static void
1076 cvc_bbsram_stop(void)
1077 {
1078 	stop_bbsram = 1;
1079 	mutex_enter(&cvc_bbsram_input_mutex);
1080 	mutex_enter(&cvc_buf_mutex);
1081 }
1082 
1083 
1084 /*
1085  * cvc_bbsram_start()
1086  *	Allow accesses to BBSRAM, used by cvc_assign_iocpu() after
1087  *	BBSRAM has been mapped to a virtual address.
1088  */
1089 static void
1090 cvc_bbsram_start(void)
1091 {
1092 	stop_bbsram = 0;
1093 	mutex_exit(&cvc_buf_mutex);
1094 	mutex_exit(&cvc_bbsram_input_mutex);
1095 }
1096 
1097 
1098 /*
1099  * cvc_assign_iocpu()
1100  *	Map in BBSRAM to a virtual address
1101  *	This called by the kernel with the cpu id of cpu zero.
1102  */
1103 void
1104 cvc_assign_iocpu(processorid_t newcpu)
1105 {
1106 	processorid_t	oldcpu = cvc_iocpu;
1107 
1108 	if (newcpu == oldcpu)
1109 		return;
1110 
1111 	cvc_iobufp[newcpu] = cvc_iobuf_mapin(newcpu);
1112 
1113 	cvc_bbsram_stop();
1114 
1115 	cvc_iocpu = newcpu;
1116 
1117 	cvc_bbsram_start();
1118 
1119 	if (oldcpu != -1)
1120 		cvc_iobuf_mapout(oldcpu);
1121 }
1122 
1123 
1124 /*
1125  * cvc_iobuf_mapin()
1126  *	Map in the cvc bbsram i/o buffer into kernel space.
1127  */
1128 static caddr_t
1129 cvc_iobuf_mapin(processorid_t cpu_id)
1130 {
1131 	caddr_t	cvaddr;
1132 	uint64_t cvc_iobuf_physaddr;
1133 	pfn_t pfn;
1134 	uint_t num_pages;
1135 	extern cpu_sgnblk_t *cpu_sgnblkp[];
1136 
1137 	ASSERT(cpu_sgnblkp[cpu_id] != NULL);
1138 
1139 	/*
1140 	 * First construct the physical base address of the bbsram
1141 	 * in Starfire PSI space associated with this cpu in question.
1142 	 */
1143 	cvc_iobuf_physaddr = STARFIRE_UPAID2UPS(cpu_id) | STARFIRE_PSI_BASE;
1144 
1145 	/*
1146 	 * Next add the cvc i/o buffer offset obtained from the
1147 	 * sigblock to get cvc iobuf physical address
1148 	 */
1149 	cvc_iobuf_physaddr += cpu_sgnblkp[cpu_id]->sigb_cvc_off;
1150 
1151 	/* Get the page frame number */
1152 	pfn = (cvc_iobuf_physaddr >> MMU_PAGESHIFT);
1153 
1154 	/* Calculate how many pages we need to map in */
1155 	num_pages = mmu_btopr(((uint_t)(cvc_iobuf_physaddr
1156 	    & MMU_PAGEOFFSET) + sizeof (sigb_cvc_t)));
1157 
1158 	/*
1159 	 * Map in the cvc iobuf
1160 	 */
1161 	cvaddr = vmem_alloc(heap_arena, ptob(num_pages), VM_SLEEP);
1162 
1163 	hat_devload(kas.a_hat, cvaddr, mmu_ptob(num_pages), pfn,
1164 	    PROT_READ | PROT_WRITE, HAT_LOAD_LOCK);
1165 
1166 	return ((caddr_t)(cvaddr + (uint_t)(cvc_iobuf_physaddr
1167 	    & MMU_PAGEOFFSET)));
1168 }
1169 
1170 
1171 /*
1172  * cvc_iobuf_mapout()
1173  *	Map out the cvc iobuf from kernel space
1174  */
1175 static void
1176 cvc_iobuf_mapout(processorid_t cpu_id)
1177 {
1178 	caddr_t	cvaddr;
1179 	size_t	num_pages;
1180 
1181 	if ((cvaddr = cvc_iobufp[cpu_id]) == 0) {
1182 		/* already unmapped - return */
1183 		return;
1184 	}
1185 
1186 	/* Calculate how many pages we need to map out */
1187 	num_pages = mmu_btopr(((size_t)((uint64_t)cvaddr & MMU_PAGEOFFSET) +
1188 	    sizeof (sigb_cvc_t)));
1189 
1190 	/* Get cvaddr to the start of the page boundary */
1191 	cvaddr = (caddr_t)(((uint64_t)cvaddr & MMU_PAGEMASK));
1192 
1193 	hat_unload(kas.a_hat, cvaddr, mmu_ptob(num_pages), HAT_UNLOAD_UNLOCK);
1194 	vmem_free(heap_arena, cvaddr, ptob(num_pages));
1195 
1196 	cvc_iobufp[cpu_id] = NULL;
1197 }
1198