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