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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * sun4v console driver 31 */ 32 33 #include <sys/errno.h> 34 #include <sys/stat.h> 35 #include <sys/kmem.h> 36 #include <sys/conf.h> 37 #include <sys/termios.h> 38 #include <sys/modctl.h> 39 #include <sys/kbio.h> 40 #include <sys/stropts.h> 41 #include <sys/stream.h> 42 #include <sys/strsun.h> 43 #include <sys/sysmacros.h> 44 #include <sys/promif.h> 45 #include <sys/ddi.h> 46 #include <sys/sunddi.h> 47 #include <sys/cyclic.h> 48 #include <sys/intr.h> 49 #include <sys/spl.h> 50 #include <sys/qcn.h> 51 #include <sys/hypervisor_api.h> 52 53 /* dev_ops and cb_ops for device driver */ 54 static int qcn_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 55 static int qcn_attach(dev_info_t *, ddi_attach_cmd_t); 56 static int qcn_detach(dev_info_t *, ddi_detach_cmd_t); 57 static int qcn_open(queue_t *, dev_t *, int, int, cred_t *); 58 static int qcn_close(queue_t *, int, cred_t *); 59 static int qcn_wput(queue_t *, mblk_t *); 60 static int qcn_wsrv(queue_t *); 61 static int qcn_rsrv(queue_t *); 62 63 /* other internal qcn routines */ 64 static void qcn_ioctl(queue_t *, mblk_t *); 65 static void qcn_reioctl(void *); 66 static void qcn_ack(mblk_t *, mblk_t *, uint_t); 67 static void qcn_start(void); 68 static int qcn_transmit(queue_t *, mblk_t *); 69 static void qcn_flush(void); 70 static uint_t qcn_hi_intr(caddr_t arg); 71 static uint_t qcn_soft_intr(caddr_t arg1, caddr_t arg2); 72 73 static boolean_t abort_charseq_recognize(uchar_t); 74 75 static qcn_t *qcn_state; 76 static uchar_t qcn_stopped = B_FALSE; 77 static int qcn_timeout_period = 20; /* time out in seconds */ 78 size_t qcn_input_dropped; /* dropped input character counter */ 79 80 #ifdef QCN_POLLING 81 static void qcn_poll_handler(void *unused); 82 static cyc_time_t qcn_poll_time; 83 static cyc_handler_t qcn_poll_cychandler = { 84 qcn_poll_handler, 85 NULL, 86 CY_LOW_LEVEL /* XXX need softint to make this high */ 87 }; 88 static cyclic_id_t qcn_poll_cycid = CYCLIC_NONE; 89 static uint64_t qcn_poll_interval = 5; /* milli sec */ 90 static uint64_t sb_interval = 0; 91 #endif 92 93 #define QCN_MI_IDNUM 0xABCE 94 #define QCN_MI_HIWAT 8192 95 #define QCN_MI_LOWAT 128 96 97 /* streams structures */ 98 static struct module_info minfo = { 99 QCN_MI_IDNUM, /* mi_idnum */ 100 "qcn", /* mi_idname */ 101 0, /* mi_minpsz */ 102 INFPSZ, /* mi_maxpsz */ 103 QCN_MI_HIWAT, /* mi_hiwat */ 104 QCN_MI_LOWAT /* mi_lowat */ 105 }; 106 107 static struct qinit rinit = { 108 putq, /* qi_putp */ 109 qcn_rsrv, /* qi_srvp */ 110 qcn_open, /* qi_qopen */ 111 qcn_close, /* qi_qclose */ 112 NULL, /* qi_qadmin */ 113 &minfo, /* qi_minfo */ 114 NULL /* qi_mstat */ 115 }; 116 117 static struct qinit winit = { 118 qcn_wput, /* qi_putp */ 119 qcn_wsrv, /* qi_srvp */ 120 qcn_open, /* qi_qopen */ 121 qcn_close, /* qi_qclose */ 122 NULL, /* qi_qadmin */ 123 &minfo, /* qi_minfo */ 124 NULL /* qi_mstat */ 125 }; 126 127 static struct streamtab qcnstrinfo = { 128 &rinit, 129 &winit, 130 NULL, 131 NULL 132 }; 133 134 /* standard device driver structures */ 135 static struct cb_ops qcn_cb_ops = { 136 nulldev, /* open() */ 137 nulldev, /* close() */ 138 nodev, /* strategy() */ 139 nodev, /* print() */ 140 nodev, /* dump() */ 141 nodev, /* read() */ 142 nodev, /* write() */ 143 nodev, /* ioctl() */ 144 nodev, /* devmap() */ 145 nodev, /* mmap() */ 146 nodev, /* segmap() */ 147 nochpoll, /* poll() */ 148 ddi_prop_op, /* prop_op() */ 149 &qcnstrinfo, /* cb_str */ 150 D_NEW | D_MP /* cb_flag */ 151 }; 152 153 static struct dev_ops qcn_ops = { 154 DEVO_REV, 155 0, /* refcnt */ 156 qcn_getinfo, /* getinfo() */ 157 nulldev, /* identify() */ 158 nulldev, /* probe() */ 159 qcn_attach, /* attach() */ 160 qcn_detach, /* detach() */ 161 nodev, /* reset() */ 162 &qcn_cb_ops, /* cb_ops */ 163 (struct bus_ops *)NULL, /* bus_ops */ 164 NULL /* power() */ 165 }; 166 167 static struct modldrv modldrv = { 168 &mod_driverops, 169 "sun4v console driver v%I%", 170 &qcn_ops 171 }; 172 173 static struct modlinkage modlinkage = { 174 MODREV_1, 175 (void*)&modldrv, 176 NULL 177 }; 178 179 180 /* driver configuration routines */ 181 int 182 _init(void) 183 { 184 int error; 185 186 qcn_state = kmem_zalloc(sizeof (qcn_t), KM_SLEEP); 187 188 error = mod_install(&modlinkage); 189 if (error != 0) 190 kmem_free(qcn_state, sizeof (qcn_t)); 191 192 return (error); 193 } 194 195 int 196 _fini(void) 197 { 198 /* can't remove console driver */ 199 return (EBUSY); 200 } 201 202 int 203 _info(struct modinfo *modinfop) 204 { 205 return (mod_info(&modlinkage, modinfop)); 206 } 207 208 static int 209 qcn_add_intrs(void) 210 { 211 dev_info_t *devinfo = qcn_state->qcn_dip; 212 int actual, count = 0; 213 int x, y, rc, inum = 0; 214 215 216 /* get number of interrupts */ 217 rc = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_FIXED, &count); 218 if ((rc != DDI_SUCCESS) || (count == 0)) { 219 return (DDI_FAILURE); 220 } 221 222 /* Allocate an array of interrupt handles */ 223 qcn_state->qcn_intr_size = count * sizeof (ddi_intr_handle_t); 224 qcn_state->qcn_htable = kmem_zalloc(qcn_state->qcn_intr_size, KM_SLEEP); 225 226 /* call ddi_intr_alloc() */ 227 rc = ddi_intr_alloc(devinfo, qcn_state->qcn_htable, 228 DDI_INTR_TYPE_FIXED, inum, count, &actual, 229 DDI_INTR_ALLOC_STRICT); 230 231 if ((rc != DDI_SUCCESS) || (actual == 0)) { 232 kmem_free(qcn_state->qcn_htable, qcn_state->qcn_intr_size); 233 return (DDI_FAILURE); 234 } 235 236 if (actual < count) { 237 for (x = 0; x < actual; x++) { 238 (void) ddi_intr_free(qcn_state->qcn_htable[x]); 239 } 240 241 kmem_free(qcn_state->qcn_htable, qcn_state->qcn_intr_size); 242 return (DDI_FAILURE); 243 } 244 245 qcn_state->qcn_intr_cnt = actual; 246 247 /* Get intr priority */ 248 if (ddi_intr_get_pri(qcn_state->qcn_htable[0], 249 &qcn_state->qcn_intr_pri) != DDI_SUCCESS) { 250 for (x = 0; x < actual; x++) { 251 (void) ddi_intr_free(qcn_state->qcn_htable[x]); 252 } 253 254 kmem_free(qcn_state->qcn_htable, qcn_state->qcn_intr_size); 255 return (DDI_FAILURE); 256 } 257 258 /* Call ddi_intr_add_handler() */ 259 for (x = 0; x < actual; x++) { 260 if (ddi_intr_add_handler(qcn_state->qcn_htable[x], 261 (ddi_intr_handler_t *)qcn_hi_intr, 262 (caddr_t)qcn_state, NULL) != DDI_SUCCESS) { 263 264 for (y = 0; y < x; y++) { 265 (void) ddi_intr_remove_handler( 266 qcn_state->qcn_htable[y]); 267 } 268 269 for (y = 0; y < actual; y++) { 270 (void) ddi_intr_free(qcn_state->qcn_htable[y]); 271 } 272 273 kmem_free(qcn_state->qcn_htable, 274 qcn_state->qcn_intr_size); 275 return (DDI_FAILURE); 276 } 277 } 278 279 return (DDI_SUCCESS); 280 } 281 282 static void 283 qcn_remove_intrs(void) 284 { 285 int x; 286 for (x = 0; x < qcn_state->qcn_intr_cnt; x++) { 287 (void) ddi_intr_disable(qcn_state->qcn_htable[x]); 288 (void) ddi_intr_remove_handler(qcn_state->qcn_htable[x]); 289 (void) ddi_intr_free(qcn_state->qcn_htable[x]); 290 } 291 kmem_free(qcn_state->qcn_htable, qcn_state->qcn_intr_size); 292 } 293 294 static void 295 qcn_intr_enable(void) 296 { 297 int x; 298 299 for (x = 0; x < qcn_state->qcn_intr_cnt; x++) { 300 (void) ddi_intr_enable(qcn_state->qcn_htable[x]); 301 } 302 } 303 304 /* 305 * qcn_attach is called at startup time. 306 * There is only once instance of this driver. 307 */ 308 static int 309 qcn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 310 { 311 extern int ddi_create_internal_pathname(dev_info_t *, char *, 312 int, minor_t); 313 uint_t soft_prip; 314 315 #ifdef QCN_POLLING 316 char *binding_name; 317 #endif 318 if (cmd != DDI_ATTACH) 319 return (DDI_FAILURE); 320 321 if (ddi_create_internal_pathname(dip, "qcn", 322 S_IFCHR, 0) != DDI_SUCCESS) 323 return (DDI_FAILURE); 324 325 qcn_state->qcn_soft_pend = 0; 326 qcn_state->qcn_hangup = 0; 327 qcn_state->qcn_rbuf_overflow = 0; 328 329 /* prepare some data structures in soft state */ 330 331 qcn_state->qcn_dip = dip; 332 333 qcn_state->qcn_polling = 0; 334 335 #ifdef QCN_POLLING 336 /* 337 * This test is for the sole purposes of allowing 338 * the console to work on older firmware releases. 339 */ 340 binding_name = ddi_binding_name(qcn_state->qcn_dip); 341 if (strcmp(binding_name, "qcn") == 0) 342 qcn_state->qcn_polling = 1; 343 344 if (qcn_state->qcn_polling) { 345 qcn_poll_time.cyt_when = 0ull; 346 qcn_poll_time.cyt_interval = 347 qcn_poll_interval * 1000ull * 1000ull; 348 mutex_enter(&cpu_lock); 349 qcn_poll_cycid = cyclic_add(&qcn_poll_cychandler, 350 &qcn_poll_time); 351 mutex_exit(&cpu_lock); 352 } 353 #endif 354 355 if (!qcn_state->qcn_polling) { 356 if (qcn_add_intrs() != DDI_SUCCESS) { 357 cmn_err(CE_WARN, "qcn_attach: add_intr failed\n"); 358 return (DDI_FAILURE); 359 } 360 if (ddi_intr_add_softint(dip, &qcn_state->qcn_softint_hdl, 361 DDI_INTR_SOFTPRI_MAX, qcn_soft_intr, 362 (caddr_t)qcn_state) != DDI_SUCCESS) { 363 cmn_err(CE_WARN, "qcn_attach: add_soft_intr failed\n"); 364 qcn_remove_intrs(); 365 return (DDI_FAILURE); 366 } 367 if (ddi_intr_get_softint_pri(qcn_state->qcn_softint_hdl, 368 &soft_prip) != DDI_SUCCESS) { 369 cmn_err(CE_WARN, "qcn_attach: softint_pri failed\n"); 370 (void) ddi_intr_remove_softint( 371 qcn_state->qcn_softint_hdl); 372 qcn_remove_intrs(); 373 return (DDI_FAILURE); 374 } 375 qcn_state->qcn_soft_pri = 376 (ddi_iblock_cookie_t)(uint64_t)soft_prip; 377 378 mutex_init(&qcn_state->qcn_hi_lock, NULL, MUTEX_DRIVER, 379 (void *)(qcn_state->qcn_intr_pri)); 380 mutex_init(&qcn_state->qcn_softlock, NULL, MUTEX_DRIVER, 381 (void *)(qcn_state->qcn_soft_pri)); 382 } 383 384 mutex_init(&qcn_state->qcn_lock, NULL, MUTEX_DRIVER, NULL); 385 386 /* 387 * Enable interrupts 388 */ 389 if (!qcn_state->qcn_polling) { 390 qcn_intr_enable(); 391 } 392 #ifdef QCN_DEBUG 393 prom_printf("qcn_attach(): qcn driver attached\n"); 394 #endif 395 396 return (DDI_SUCCESS); 397 398 } 399 400 /* ARGSUSED */ 401 static int 402 qcn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 403 { 404 405 if (cmd != DDI_DETACH) 406 return (DDI_FAILURE); 407 408 409 #ifdef QCN_DEBUG 410 prom_printf("qcn_detach(): QCN driver detached\n"); 411 #endif 412 413 #ifdef QCN_POLLING 414 if (qcn_state->qcn_polling) { 415 mutex_enter(&cpu_lock); 416 if (qcn_poll_cycid != CYCLIC_NONE) 417 cyclic_remove(qcn_poll_cycid); 418 qcn_poll_cycid = CYCLIC_NONE; 419 mutex_exit(&cpu_lock); 420 } 421 #endif 422 423 if (!qcn_state->qcn_polling) 424 qcn_remove_intrs(); 425 426 return (DDI_SUCCESS); 427 } 428 429 /* ARGSUSED */ 430 static int 431 qcn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 432 { 433 int error = DDI_FAILURE; 434 int instance = 0; 435 switch (infocmd) { 436 case DDI_INFO_DEVT2DEVINFO: 437 if (qcn_state) { 438 #ifdef QCN_DEBUG 439 prom_printf("qcn_getinfo(): devt2dip %lx\n", arg); 440 #endif 441 *result = (void *)qcn_state->qcn_dip; 442 error = DDI_SUCCESS; 443 } 444 break; 445 446 case DDI_INFO_DEVT2INSTANCE: 447 #ifdef QCN_DEBUG 448 prom_printf("qcn_getinfo(): devt2instance %lx\n", arg); 449 #endif 450 if (getminor((dev_t)arg) == 0) { 451 *result = (void *)instance; 452 error = DDI_SUCCESS; 453 } 454 break; 455 } 456 457 return (error); 458 } 459 460 /* streams open & close */ 461 /* ARGSUSED */ 462 static int 463 qcn_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp) 464 { 465 tty_common_t *tty; 466 int unit = getminor(*devp); 467 468 #ifdef QCN_DEBUG 469 prom_printf("qcn_open(): minor %x\n", unit); 470 #endif 471 472 if (unit != 0) 473 return (ENXIO); 474 475 /* stream already open */ 476 if (q->q_ptr != NULL) 477 return (DDI_SUCCESS); 478 479 if (!qcn_state) { 480 cmn_err(CE_WARN, "qcn_open: console was not configured by " 481 "autoconfig\n"); 482 return (ENXIO); 483 } 484 485 mutex_enter(&qcn_state->qcn_lock); 486 tty = &(qcn_state->qcn_tty); 487 488 tty->t_readq = q; 489 tty->t_writeq = WR(q); 490 491 /* Link the RD and WR Q's */ 492 q->q_ptr = WR(q)->q_ptr = (caddr_t)qcn_state; 493 qcn_state->qcn_readq = RD(q); 494 qcn_state->qcn_writeq = WR(q); 495 qprocson(q); 496 497 mutex_exit(&qcn_state->qcn_lock); 498 499 #ifdef QCN_DEBUG 500 prom_printf("qcn_open: opened as dev %lx\n", *devp); 501 #endif 502 503 return (DDI_SUCCESS); 504 } 505 506 /* ARGSUSED */ 507 static int 508 qcn_close(queue_t *q, int flag, cred_t *credp) 509 { 510 511 ASSERT(qcn_state == q->q_ptr); 512 513 if (qcn_state->qcn_wbufcid != 0) { 514 unbufcall(qcn_state->qcn_wbufcid); 515 } 516 ttycommon_close(&qcn_state->qcn_tty); 517 518 qprocsoff(q); 519 q->q_ptr = WR(q)->q_ptr = NULL; 520 qcn_state->qcn_readq = NULL; 521 qcn_state->qcn_writeq = NULL; 522 523 return (DDI_SUCCESS); 524 } 525 526 /* 527 * Put procedure for write queue. 528 * Respond to M_IOCTL, M_DATA and M_FLUSH messages here; 529 * It put's the data onto internal qcn_output_q. 530 */ 531 static int 532 qcn_wput(queue_t *q, mblk_t *mp) 533 { 534 535 #ifdef QCN_DEBUG 536 struct iocblk *iocp; 537 int i; 538 #endif 539 540 ASSERT(qcn_state == q->q_ptr); 541 542 if (!mp->b_datap) { 543 cmn_err(CE_PANIC, "qcn_wput: null datap"); 544 } 545 546 #ifdef QCN_DEBUG 547 prom_printf("qcn_wput(): QCN wput q=%X mp=%X rd=%X wr=%X type=%X\n", 548 q, mp, mp->b_rptr, mp->b_wptr, mp->b_datap->db_type); 549 #endif 550 551 mutex_enter(&qcn_state->qcn_lock); 552 553 switch (mp->b_datap->db_type) { 554 case M_IOCTL: 555 case M_CTL: 556 #ifdef QCN_DEBUG 557 iocp = (struct iocblk *)mp->b_rptr; 558 prom_printf("qcn_wput(): M_IOCTL cmd=%X TIOC=%X\n", 559 iocp->ioc_cmd, TIOC); 560 #endif 561 switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 562 case TCSETSW: 563 case TCSETSF: 564 case TCSETAW: 565 case TCSETAF: 566 case TCSBRK: 567 /* 568 * The change do not take effect until all 569 * output queued before them is drained. 570 * Put this message on the queue, so that 571 * "qcn_start" will see it when it's done 572 * with the output before it. Poke the start 573 * routine, just in case. 574 */ 575 (void) putq(q, mp); 576 qcn_start(); 577 break; 578 default: 579 qcn_ioctl(q, mp); 580 } 581 break; 582 583 case M_FLUSH: 584 if (*mp->b_rptr & FLUSHW) { 585 flushq(q, FLUSHDATA); 586 *mp->b_rptr &= ~FLUSHW; 587 } 588 if (*mp->b_rptr & FLUSHR) { 589 flushq(RD(q), FLUSHDATA); 590 qreply(q, mp); 591 } else { 592 freemsg(mp); 593 } 594 break; 595 596 case M_STOP: 597 qcn_stopped = B_TRUE; 598 freemsg(mp); 599 break; 600 601 case M_START: 602 qcn_stopped = B_FALSE; 603 freemsg(mp); 604 qenable(q); /* Start up delayed messages */ 605 break; 606 607 case M_DATA: 608 /* 609 * Queue the message up to be transmitted, 610 * and poke the start routine. 611 */ 612 #ifdef QCN_DEBUG 613 if (mp->b_rptr < mp->b_wptr) { 614 prom_printf("qcn_wput(): DATA q=%X mp=%X rd=%X wr=%X\n", 615 q, mp, mp->b_rptr, mp->b_wptr); 616 prom_printf("qcn_wput(): ["); 617 for (i = 0; i < mp->b_wptr-mp->b_rptr; i++) { 618 prom_printf("%c", *(mp->b_rptr+i)); 619 } 620 prom_printf("]\n"); 621 } 622 #endif /* QCN_DEBUG */ 623 (void) putq(q, mp); 624 qcn_start(); 625 break; 626 627 default: 628 freemsg(mp); 629 } 630 631 mutex_exit(&qcn_state->qcn_lock); 632 return (0); 633 } 634 635 /* 636 * Process an "ioctl" message sent down to us. 637 */ 638 static void 639 qcn_ioctl(queue_t *q, mblk_t *mp) 640 { 641 struct iocblk *iocp; 642 tty_common_t *tty; 643 mblk_t *datamp; 644 int data_size; 645 int error = 0; 646 647 #ifdef QCN_DEBUG 648 prom_printf("qcn_ioctl(): q=%X mp=%X\n", q, mp); 649 #endif 650 651 iocp = (struct iocblk *)mp->b_rptr; 652 tty = &(qcn_state->qcn_tty); 653 654 if (tty->t_iocpending != NULL) { 655 freemsg(tty->t_iocpending); 656 tty->t_iocpending = NULL; 657 } 658 data_size = ttycommon_ioctl(tty, q, mp, &error); 659 if (data_size != 0) { 660 if (qcn_state->qcn_wbufcid) 661 unbufcall(qcn_state->qcn_wbufcid); 662 /* call qcn_reioctl() */ 663 qcn_state->qcn_wbufcid = 664 bufcall(data_size, BPRI_HI, qcn_reioctl, qcn_state); 665 return; 666 } 667 668 if (error < 0) { 669 iocp = (struct iocblk *)mp->b_rptr; 670 /* 671 * "ttycommon_ioctl" didn't do anything; we process it here. 672 */ 673 error = 0; 674 switch (iocp->ioc_cmd) { 675 case TCSBRK: 676 case TIOCSBRK: 677 case TIOCCBRK: 678 case TIOCMSET: 679 case TIOCMBIS: 680 case TIOCMBIC: 681 if (iocp->ioc_count != TRANSPARENT) 682 qcn_ack(mp, NULL, 0); 683 else 684 mcopyin(mp, NULL, sizeof (int), NULL); 685 break; 686 687 case TIOCMGET: 688 datamp = allocb(sizeof (int), BPRI_MED); 689 if (datamp == NULL) { 690 error = EAGAIN; 691 break; 692 } 693 694 *(int *)datamp->b_rptr = 0; 695 696 if (iocp->ioc_count != TRANSPARENT) 697 qcn_ack(mp, datamp, sizeof (int)); 698 else 699 mcopyout(mp, NULL, sizeof (int), NULL, datamp); 700 break; 701 702 default: 703 error = EINVAL; 704 break; 705 } 706 } 707 if (error != 0) { 708 iocp->ioc_count = 0; 709 iocp->ioc_error = error; 710 mp->b_datap->db_type = M_IOCNAK; 711 } 712 qreply(q, mp); 713 } 714 715 static void 716 qcn_reioctl(void *unit) 717 { 718 queue_t *q; 719 mblk_t *mp; 720 qcn_t *qcnp = (qcn_t *)unit; 721 722 if (!qcnp->qcn_wbufcid) 723 return; 724 725 qcnp->qcn_wbufcid = 0; 726 if ((q = qcnp->qcn_tty.t_writeq) == NULL) 727 return; 728 729 if ((mp = qcnp->qcn_tty.t_iocpending) == NULL) 730 return; 731 732 qcnp->qcn_tty.t_iocpending = NULL; 733 qcn_ioctl(q, mp); 734 } 735 736 static void 737 qcn_ack(mblk_t *mp, mblk_t *dp, uint_t size) 738 { 739 struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 740 741 mp->b_datap->db_type = M_IOCACK; 742 iocp->ioc_count = size; 743 iocp->ioc_error = 0; 744 iocp->ioc_rval = 0; 745 if (mp->b_cont != NULL) 746 freeb(mp->b_cont); 747 if (dp != NULL) { 748 mp->b_cont = dp; 749 dp->b_wptr += size; 750 } else 751 mp->b_cont = NULL; 752 } 753 754 static void 755 qcn_start(void) 756 { 757 758 queue_t *q; 759 mblk_t *mp; 760 int rv; 761 762 ASSERT(MUTEX_HELD(&qcn_state->qcn_lock)); 763 764 /* 765 * read stream queue and remove data from the queue and 766 * transmit them if possible 767 */ 768 q = qcn_state->qcn_writeq; 769 ASSERT(q != NULL); 770 while (mp = getq(q)) { 771 if (mp->b_datap->db_type == M_IOCTL) { 772 /* 773 * These are those IOCTLs queued up 774 * do it now 775 */ 776 qcn_ioctl(q, mp); 777 continue; 778 } 779 /* 780 * M_DATA 781 */ 782 rv = qcn_transmit(q, mp); 783 if (rv == EBUSY || rv == EAGAIN) 784 return; 785 } 786 } 787 788 static int 789 qcn_transmit(queue_t *q, mblk_t *mp) 790 { 791 caddr_t buf; 792 mblk_t *bp; 793 size_t len; 794 long i; 795 796 #ifdef QCN_DEBUG 797 prom_printf("qcn_transmit(): q=%X mp=%X\n", q, mp); 798 #endif 799 do { 800 bp = mp; 801 len = bp->b_wptr - bp->b_rptr; 802 buf = (caddr_t)bp->b_rptr; 803 804 for (i = 0; i < len; i++) { 805 if (hv_cnputchar(buf[i]) == -1) 806 break; 807 } 808 if (i != len) { 809 bp->b_rptr += i; 810 (void) putbq(q, mp); 811 return (EAGAIN); 812 } 813 mp = bp->b_cont; 814 freeb(bp); 815 } while (mp != NULL); 816 817 return (0); 818 } 819 820 /* 821 * called when SC first establishes console connection 822 * drop all the data on the output queue 823 */ 824 static void 825 qcn_flush(void) 826 { 827 queue_t *q; 828 mblk_t *mp; 829 830 ASSERT(MUTEX_HELD(&qcn_state->qcn_lock)); 831 832 q = qcn_state->qcn_writeq; 833 834 prom_printf("qcn_flush(): WARNING console output is dropped time=%x\n", 835 gethrestime_sec()); 836 while (mp = getq(q)) 837 freemsg(mp); 838 } 839 840 static void 841 qcn_trigger_softint(void) 842 { 843 844 if (mutex_tryenter(&qcn_state->qcn_softlock)) { 845 if (!qcn_state->qcn_soft_pend) { 846 qcn_state->qcn_soft_pend = 1; 847 mutex_exit(&qcn_state->qcn_softlock); 848 (void) ddi_intr_trigger_softint( 849 qcn_state->qcn_softint_hdl, NULL); 850 } else 851 mutex_exit(&qcn_state->qcn_softlock); 852 } 853 } 854 855 /*ARGSUSED*/ 856 static uint_t 857 qcn_soft_intr(caddr_t arg1, caddr_t arg2) 858 { 859 mblk_t *mp; 860 int cc; 861 862 mutex_enter(&qcn_state->qcn_softlock); 863 mutex_enter(&qcn_state->qcn_hi_lock); 864 if ((cc = RING_CNT(qcn_state)) <= 0) { 865 mutex_exit(&qcn_state->qcn_hi_lock); 866 goto out; 867 } 868 869 if ((mp = allocb(cc, BPRI_MED)) == NULL) { 870 qcn_input_dropped += cc; 871 cmn_err(CE_WARN, "qcn_intr: allocb" 872 "failed (console input dropped)"); 873 mutex_exit(&qcn_state->qcn_hi_lock); 874 goto out; 875 } 876 877 do { 878 /* put console input onto stream */ 879 *(char *)mp->b_wptr++ = RING_GET(qcn_state); 880 } while (--cc); 881 882 if (qcn_state->qcn_rbuf_overflow) { 883 mutex_exit(&qcn_state->qcn_hi_lock); 884 cmn_err(CE_WARN, "qcn: Ring buffer overflow\n"); 885 mutex_enter(&qcn_state->qcn_hi_lock); 886 qcn_state->qcn_rbuf_overflow = 0; 887 } 888 889 mutex_exit(&qcn_state->qcn_hi_lock); 890 if (qcn_state->qcn_readq) { 891 putnext(qcn_state->qcn_readq, mp); 892 } 893 out: 894 /* 895 * If there are pending transmits because hypervisor 896 * returned EWOULDBLOCK poke start now. 897 */ 898 899 if (qcn_state->qcn_writeq != NULL) { 900 if (qcn_state->qcn_hangup) { 901 (void) putctl(qcn_state->qcn_readq, M_HANGUP); 902 flushq(qcn_state->qcn_writeq, FLUSHDATA); 903 qcn_state->qcn_hangup = 0; 904 } else { 905 mutex_enter(&qcn_state->qcn_lock); 906 qcn_start(); 907 mutex_exit(&qcn_state->qcn_lock); 908 } 909 } 910 qcn_state->qcn_soft_pend = 0; 911 mutex_exit(&qcn_state->qcn_softlock); 912 return (DDI_INTR_CLAIMED); 913 } 914 915 /*ARGSUSED*/ 916 static uint_t 917 qcn_hi_intr(caddr_t arg) 918 { 919 int64_t rv; 920 uint8_t buf; 921 922 mutex_enter(&qcn_state->qcn_hi_lock); 923 /* LINTED: E_CONSTANT_CONDITION */ 924 while (1) { 925 rv = hv_cngetchar(&buf); 926 if (rv == H_BREAK) { 927 if (abort_enable != KIOCABORTALTERNATE) 928 abort_sequence_enter((char *)NULL); 929 } 930 931 if (rv == H_HUP) { 932 qcn_state->qcn_hangup = 1; 933 } 934 935 if (rv != H_EOK) 936 goto out; 937 938 if (abort_enable == KIOCABORTALTERNATE) { 939 if (abort_charseq_recognize(buf)) { 940 abort_sequence_enter((char *)NULL); 941 } 942 } 943 944 /* put console input onto stream */ 945 if (RING_POK(qcn_state, 1)) { 946 RING_PUT(qcn_state, buf); 947 } else { 948 qcn_state->qcn_rbuf_overflow++; 949 } 950 } 951 out: 952 mutex_exit(&qcn_state->qcn_hi_lock); 953 qcn_trigger_softint(); 954 955 return (DDI_INTR_CLAIMED); 956 } 957 958 #ifdef QCN_POLLING 959 /*ARGSUSED*/ 960 static void 961 qcn_poll_handler(void *unused) 962 { 963 mblk_t *mp; 964 int64_t rv; 965 uint8_t buf; 966 int qcn_writeq_flush = 0; 967 968 /* LINTED: E_CONSTANT_CONDITION */ 969 while (1) { 970 rv = hv_cngetchar(&buf); 971 if (rv == H_BREAK) { 972 if (abort_enable != KIOCABORTALTERNATE) 973 abort_sequence_enter((char *)NULL); 974 } 975 976 if (rv == H_HUP) { 977 if (qcn_state->qcn_readq) { 978 (void) putctl(qcn_state->qcn_readq, M_HANGUP); 979 qcn_writeq_flush = 1; 980 } 981 goto out; 982 } 983 984 if (rv != H_EOK) 985 goto out; 986 987 if (abort_enable == KIOCABORTALTERNATE) { 988 if (abort_charseq_recognize(buf)) { 989 abort_sequence_enter((char *)NULL); 990 } 991 } 992 993 /* put console input onto stream */ 994 if (qcn_state->qcn_readq) { 995 if ((mp = allocb(1, BPRI_MED)) == NULL) { 996 qcn_input_dropped++; 997 cmn_err(CE_WARN, "qcn_intr: allocb" 998 "failed (console input dropped)"); 999 return; 1000 } 1001 *(char *)mp->b_wptr++ = buf; 1002 putnext(qcn_state->qcn_readq, mp); 1003 } 1004 } 1005 out: 1006 /* 1007 * If there are pending transmits because hypervisor 1008 * returned EWOULDBLOCK poke start now. 1009 */ 1010 1011 mutex_enter(&qcn_state->qcn_lock); 1012 if (qcn_state->qcn_writeq != NULL) { 1013 if (qcn_writeq_flush) { 1014 flushq(qcn_state->qcn_writeq, FLUSHDATA); 1015 } else { 1016 qcn_start(); 1017 } 1018 } 1019 mutex_exit(&qcn_state->qcn_lock); 1020 } 1021 #endif 1022 1023 /* 1024 * Check for abort character sequence, copied from zs_async.c 1025 */ 1026 #define CNTRL(c) ((c)&037) 1027 1028 static boolean_t 1029 abort_charseq_recognize(uchar_t ch) 1030 { 1031 static int state = 0; 1032 static char sequence[] = { '\r', '~', CNTRL('b') }; 1033 1034 if (ch == sequence[state]) { 1035 if (++state >= sizeof (sequence)) { 1036 state = 0; 1037 return (B_TRUE); 1038 } 1039 } else { 1040 state = (ch == sequence[0]) ? 1 : 0; 1041 } 1042 return (B_FALSE); 1043 } 1044 1045 1046 static int 1047 qcn_rsrv(queue_t *q) 1048 { 1049 mblk_t *mp; 1050 1051 if (qcn_stopped == B_TRUE) 1052 return (0); 1053 1054 mutex_enter(&qcn_state->qcn_lock); 1055 1056 while ((mp = getq(q)) != NULL) { 1057 if (canputnext(q)) 1058 putnext(q, mp); 1059 else if (mp->b_datap->db_type >= QPCTL) 1060 (void) putbq(q, mp); 1061 } 1062 1063 mutex_exit(&qcn_state->qcn_lock); 1064 1065 return (0); 1066 } 1067 1068 /* ARGSUSED */ 1069 static int 1070 qcn_wsrv(queue_t *q) 1071 { 1072 if (qcn_stopped == B_TRUE) 1073 return (0); 1074 1075 mutex_enter(&qcn_state->qcn_lock); 1076 1077 if (qcn_state->qcn_writeq != NULL) 1078 qcn_start(); 1079 1080 mutex_exit(&qcn_state->qcn_lock); 1081 1082 return (0); 1083 } 1084