1 /*- 2 * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 /* Driver for VirtIO console devices. */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/malloc.h> 36 #include <sys/module.h> 37 #include <sys/kdb.h> 38 #include <sys/lock.h> 39 #include <sys/mutex.h> 40 #include <sys/sglist.h> 41 #include <sys/sysctl.h> 42 #include <sys/taskqueue.h> 43 #include <sys/queue.h> 44 45 #include <sys/conf.h> 46 #include <sys/cons.h> 47 #include <sys/tty.h> 48 49 #include <machine/bus.h> 50 #include <machine/resource.h> 51 #include <sys/bus.h> 52 53 #include <dev/virtio/virtio.h> 54 #include <dev/virtio/virtqueue.h> 55 #include <dev/virtio/console/virtio_console.h> 56 57 #include "virtio_if.h" 58 59 #define VTCON_MAX_PORTS 32 60 #define VTCON_TTY_PREFIX "V" 61 #define VTCON_BULK_BUFSZ 128 62 63 /* 64 * The buffer cannot cross more than one page boundary due to the 65 * size of the sglist segment array used. 66 */ 67 CTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE); 68 69 struct vtcon_softc; 70 struct vtcon_softc_port; 71 72 struct vtcon_port { 73 struct mtx vtcport_mtx; 74 struct vtcon_softc *vtcport_sc; 75 struct vtcon_softc_port *vtcport_scport; 76 struct tty *vtcport_tty; 77 struct virtqueue *vtcport_invq; 78 struct virtqueue *vtcport_outvq; 79 int vtcport_id; 80 int vtcport_flags; 81 #define VTCON_PORT_FLAG_GONE 0x01 82 #define VTCON_PORT_FLAG_CONSOLE 0x02 83 84 #if defined(KDB) 85 int vtcport_alt_break_state; 86 #endif 87 }; 88 89 #define VTCON_PORT_LOCK(_port) mtx_lock(&(_port)->vtcport_mtx) 90 #define VTCON_PORT_UNLOCK(_port) mtx_unlock(&(_port)->vtcport_mtx) 91 92 struct vtcon_softc_port { 93 struct vtcon_softc *vcsp_sc; 94 struct vtcon_port *vcsp_port; 95 struct virtqueue *vcsp_invq; 96 struct virtqueue *vcsp_outvq; 97 }; 98 99 struct vtcon_softc { 100 device_t vtcon_dev; 101 struct mtx vtcon_mtx; 102 uint64_t vtcon_features; 103 uint32_t vtcon_max_ports; 104 uint32_t vtcon_flags; 105 #define VTCON_FLAG_DETACHED 0x01 106 #define VTCON_FLAG_SIZE 0x02 107 #define VTCON_FLAG_MULTIPORT 0x04 108 109 /* 110 * Ports can be added and removed during runtime, but we have 111 * to allocate all the virtqueues during attach. This array is 112 * indexed by the port ID. 113 */ 114 struct vtcon_softc_port *vtcon_ports; 115 116 struct task vtcon_ctrl_task; 117 struct virtqueue *vtcon_ctrl_rxvq; 118 struct virtqueue *vtcon_ctrl_txvq; 119 struct mtx vtcon_ctrl_tx_mtx; 120 }; 121 122 #define VTCON_LOCK(_sc) mtx_lock(&(_sc)->vtcon_mtx) 123 #define VTCON_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_mtx) 124 #define VTCON_LOCK_ASSERT(_sc) \ 125 mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED) 126 #define VTCON_LOCK_ASSERT_NOTOWNED(_sc) \ 127 mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED) 128 129 #define VTCON_CTRL_TX_LOCK(_sc) mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx) 130 #define VTCON_CTRL_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx) 131 132 #define VTCON_ASSERT_VALID_PORTID(_sc, _id) \ 133 KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports, \ 134 ("%s: port ID %d out of range", __func__, _id)) 135 136 #define VTCON_FEATURES VIRTIO_CONSOLE_F_MULTIPORT 137 138 static struct virtio_feature_desc vtcon_feature_desc[] = { 139 { VIRTIO_CONSOLE_F_SIZE, "ConsoleSize" }, 140 { VIRTIO_CONSOLE_F_MULTIPORT, "MultiplePorts" }, 141 { VIRTIO_CONSOLE_F_EMERG_WRITE, "EmergencyWrite" }, 142 143 { 0, NULL } 144 }; 145 146 static int vtcon_modevent(module_t, int, void *); 147 static void vtcon_drain_all(void); 148 149 static int vtcon_probe(device_t); 150 static int vtcon_attach(device_t); 151 static int vtcon_detach(device_t); 152 static int vtcon_config_change(device_t); 153 154 static void vtcon_setup_features(struct vtcon_softc *); 155 static void vtcon_negotiate_features(struct vtcon_softc *); 156 static int vtcon_alloc_scports(struct vtcon_softc *); 157 static int vtcon_alloc_virtqueues(struct vtcon_softc *); 158 static void vtcon_read_config(struct vtcon_softc *, 159 struct virtio_console_config *); 160 161 static void vtcon_determine_max_ports(struct vtcon_softc *, 162 struct virtio_console_config *); 163 static void vtcon_destroy_ports(struct vtcon_softc *); 164 static void vtcon_stop(struct vtcon_softc *); 165 166 static int vtcon_ctrl_event_enqueue(struct vtcon_softc *, 167 struct virtio_console_control *); 168 static int vtcon_ctrl_event_create(struct vtcon_softc *); 169 static void vtcon_ctrl_event_requeue(struct vtcon_softc *, 170 struct virtio_console_control *); 171 static int vtcon_ctrl_event_populate(struct vtcon_softc *); 172 static void vtcon_ctrl_event_drain(struct vtcon_softc *); 173 static int vtcon_ctrl_init(struct vtcon_softc *); 174 static void vtcon_ctrl_deinit(struct vtcon_softc *); 175 static void vtcon_ctrl_port_add_event(struct vtcon_softc *, int); 176 static void vtcon_ctrl_port_remove_event(struct vtcon_softc *, int); 177 static void vtcon_ctrl_port_console_event(struct vtcon_softc *, int); 178 static void vtcon_ctrl_port_open_event(struct vtcon_softc *, int); 179 static void vtcon_ctrl_port_name_event(struct vtcon_softc *, int, 180 const char *, size_t); 181 static void vtcon_ctrl_process_event(struct vtcon_softc *, 182 struct virtio_console_control *, void *, size_t); 183 static void vtcon_ctrl_task_cb(void *, int); 184 static void vtcon_ctrl_event_intr(void *); 185 static void vtcon_ctrl_poll(struct vtcon_softc *, 186 struct virtio_console_control *control); 187 static void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t, 188 uint16_t, uint16_t); 189 190 static int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t); 191 static int vtcon_port_create_buf(struct vtcon_port *); 192 static void vtcon_port_requeue_buf(struct vtcon_port *, void *); 193 static int vtcon_port_populate(struct vtcon_port *); 194 static void vtcon_port_destroy(struct vtcon_port *); 195 static int vtcon_port_create(struct vtcon_softc *, int); 196 static void vtcon_port_drain_bufs(struct virtqueue *); 197 static void vtcon_port_drain(struct vtcon_port *); 198 static void vtcon_port_teardown(struct vtcon_port *); 199 static void vtcon_port_change_size(struct vtcon_port *, uint16_t, 200 uint16_t); 201 static void vtcon_port_update_console_size(struct vtcon_softc *); 202 static void vtcon_port_enable_intr(struct vtcon_port *); 203 static void vtcon_port_disable_intr(struct vtcon_port *); 204 static void vtcon_port_in(struct vtcon_port *); 205 static void vtcon_port_intr(void *); 206 static void vtcon_port_out(struct vtcon_port *, void *, int); 207 static void vtcon_port_submit_event(struct vtcon_port *, uint16_t, 208 uint16_t); 209 210 static int vtcon_tty_open(struct tty *); 211 static void vtcon_tty_close(struct tty *); 212 static void vtcon_tty_outwakeup(struct tty *); 213 static void vtcon_tty_free(void *); 214 215 static void vtcon_get_console_size(struct vtcon_softc *, uint16_t *, 216 uint16_t *); 217 218 static void vtcon_enable_interrupts(struct vtcon_softc *); 219 static void vtcon_disable_interrupts(struct vtcon_softc *); 220 221 static int vtcon_pending_free; 222 223 static struct ttydevsw vtcon_tty_class = { 224 .tsw_flags = 0, 225 .tsw_open = vtcon_tty_open, 226 .tsw_close = vtcon_tty_close, 227 .tsw_outwakeup = vtcon_tty_outwakeup, 228 .tsw_free = vtcon_tty_free, 229 }; 230 231 static device_method_t vtcon_methods[] = { 232 /* Device methods. */ 233 DEVMETHOD(device_probe, vtcon_probe), 234 DEVMETHOD(device_attach, vtcon_attach), 235 DEVMETHOD(device_detach, vtcon_detach), 236 237 /* VirtIO methods. */ 238 DEVMETHOD(virtio_config_change, vtcon_config_change), 239 240 DEVMETHOD_END 241 }; 242 243 static driver_t vtcon_driver = { 244 "vtcon", 245 vtcon_methods, 246 sizeof(struct vtcon_softc) 247 }; 248 static devclass_t vtcon_devclass; 249 250 DRIVER_MODULE(virtio_console, virtio_pci, vtcon_driver, vtcon_devclass, 251 vtcon_modevent, 0); 252 MODULE_VERSION(virtio_console, 1); 253 MODULE_DEPEND(virtio_console, virtio, 1, 1, 1); 254 255 static int 256 vtcon_modevent(module_t mod, int type, void *unused) 257 { 258 int error; 259 260 switch (type) { 261 case MOD_LOAD: 262 error = 0; 263 break; 264 case MOD_QUIESCE: 265 error = 0; 266 break; 267 case MOD_UNLOAD: 268 vtcon_drain_all(); 269 error = 0; 270 break; 271 case MOD_SHUTDOWN: 272 error = 0; 273 break; 274 default: 275 error = EOPNOTSUPP; 276 break; 277 } 278 279 return (error); 280 } 281 282 static void 283 vtcon_drain_all(void) 284 { 285 int first; 286 287 for (first = 1; vtcon_pending_free != 0; first = 0) { 288 if (first != 0) { 289 printf("virtio_console: Waiting for all detached TTY " 290 "devices to have open fds closed.\n"); 291 } 292 pause("vtcondra", hz); 293 } 294 } 295 296 static int 297 vtcon_probe(device_t dev) 298 { 299 300 if (virtio_get_device_type(dev) != VIRTIO_ID_CONSOLE) 301 return (ENXIO); 302 303 device_set_desc(dev, "VirtIO Console Adapter"); 304 305 return (BUS_PROBE_DEFAULT); 306 } 307 308 static int 309 vtcon_attach(device_t dev) 310 { 311 struct vtcon_softc *sc; 312 struct virtio_console_config concfg; 313 int error; 314 315 sc = device_get_softc(dev); 316 sc->vtcon_dev = dev; 317 318 mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF); 319 mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF); 320 321 virtio_set_feature_desc(dev, vtcon_feature_desc); 322 vtcon_setup_features(sc); 323 324 vtcon_read_config(sc, &concfg); 325 vtcon_determine_max_ports(sc, &concfg); 326 327 error = vtcon_alloc_scports(sc); 328 if (error) { 329 device_printf(dev, "cannot allocate softc port structures\n"); 330 goto fail; 331 } 332 333 error = vtcon_alloc_virtqueues(sc); 334 if (error) { 335 device_printf(dev, "cannot allocate virtqueues\n"); 336 goto fail; 337 } 338 339 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 340 TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc); 341 error = vtcon_ctrl_init(sc); 342 if (error) 343 goto fail; 344 } else { 345 error = vtcon_port_create(sc, 0); 346 if (error) 347 goto fail; 348 if (sc->vtcon_flags & VTCON_FLAG_SIZE) 349 vtcon_port_update_console_size(sc); 350 } 351 352 error = virtio_setup_intr(dev, INTR_TYPE_TTY); 353 if (error) { 354 device_printf(dev, "cannot setup virtqueue interrupts\n"); 355 goto fail; 356 } 357 358 vtcon_enable_interrupts(sc); 359 360 vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID, 361 VIRTIO_CONSOLE_DEVICE_READY, 1); 362 363 fail: 364 if (error) 365 vtcon_detach(dev); 366 367 return (error); 368 } 369 370 static int 371 vtcon_detach(device_t dev) 372 { 373 struct vtcon_softc *sc; 374 375 sc = device_get_softc(dev); 376 377 VTCON_LOCK(sc); 378 sc->vtcon_flags |= VTCON_FLAG_DETACHED; 379 if (device_is_attached(dev)) 380 vtcon_stop(sc); 381 VTCON_UNLOCK(sc); 382 383 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 384 taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task); 385 vtcon_ctrl_deinit(sc); 386 } 387 388 vtcon_destroy_ports(sc); 389 mtx_destroy(&sc->vtcon_mtx); 390 mtx_destroy(&sc->vtcon_ctrl_tx_mtx); 391 392 return (0); 393 } 394 395 static int 396 vtcon_config_change(device_t dev) 397 { 398 struct vtcon_softc *sc; 399 400 sc = device_get_softc(dev); 401 402 /* 403 * When the multiport feature is negotiated, all configuration 404 * changes are done through control virtqueue events. 405 */ 406 if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) { 407 if (sc->vtcon_flags & VTCON_FLAG_SIZE) 408 vtcon_port_update_console_size(sc); 409 } 410 411 return (0); 412 } 413 414 static void 415 vtcon_negotiate_features(struct vtcon_softc *sc) 416 { 417 device_t dev; 418 uint64_t features; 419 420 dev = sc->vtcon_dev; 421 features = VTCON_FEATURES; 422 423 sc->vtcon_features = virtio_negotiate_features(dev, features); 424 } 425 426 static void 427 vtcon_setup_features(struct vtcon_softc *sc) 428 { 429 device_t dev; 430 431 dev = sc->vtcon_dev; 432 433 vtcon_negotiate_features(sc); 434 435 if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE)) 436 sc->vtcon_flags |= VTCON_FLAG_SIZE; 437 if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT)) 438 sc->vtcon_flags |= VTCON_FLAG_MULTIPORT; 439 } 440 441 #define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg) \ 442 if (virtio_with_feature(_dev, _feature)) { \ 443 virtio_read_device_config(_dev, \ 444 offsetof(struct virtio_console_config, _field), \ 445 &(_cfg)->_field, sizeof((_cfg)->_field)); \ 446 } 447 448 static void 449 vtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg) 450 { 451 device_t dev; 452 453 dev = sc->vtcon_dev; 454 455 bzero(concfg, sizeof(struct virtio_console_config)); 456 457 VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg); 458 VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg); 459 VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg); 460 } 461 462 #undef VTCON_GET_CONFIG 463 464 static int 465 vtcon_alloc_scports(struct vtcon_softc *sc) 466 { 467 struct vtcon_softc_port *scport; 468 int max, i; 469 470 max = sc->vtcon_max_ports; 471 472 sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max, 473 M_DEVBUF, M_NOWAIT | M_ZERO); 474 if (sc->vtcon_ports == NULL) 475 return (ENOMEM); 476 477 for (i = 0; i < max; i++) { 478 scport = &sc->vtcon_ports[i]; 479 scport->vcsp_sc = sc; 480 } 481 482 return (0); 483 } 484 485 static int 486 vtcon_alloc_virtqueues(struct vtcon_softc *sc) 487 { 488 device_t dev; 489 struct vq_alloc_info *info; 490 struct vtcon_softc_port *scport; 491 int i, idx, portidx, nvqs, error; 492 493 dev = sc->vtcon_dev; 494 495 nvqs = sc->vtcon_max_ports * 2; 496 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 497 nvqs += 2; 498 499 info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT); 500 if (info == NULL) 501 return (ENOMEM); 502 503 for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) { 504 505 if (i == 1) { 506 /* The control virtqueues are after the first port. */ 507 VQ_ALLOC_INFO_INIT(&info[idx], 0, 508 vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq, 509 "%s-control rx", device_get_nameunit(dev)); 510 VQ_ALLOC_INFO_INIT(&info[idx+1], 0, 511 NULL, sc, &sc->vtcon_ctrl_txvq, 512 "%s-control tx", device_get_nameunit(dev)); 513 continue; 514 } 515 516 scport = &sc->vtcon_ports[portidx]; 517 518 VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr, 519 scport, &scport->vcsp_invq, "%s-port%d in", 520 device_get_nameunit(dev), i); 521 VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL, 522 NULL, &scport->vcsp_outvq, "%s-port%d out", 523 device_get_nameunit(dev), i); 524 525 portidx++; 526 } 527 528 error = virtio_alloc_virtqueues(dev, 0, nvqs, info); 529 free(info, M_TEMP); 530 531 return (error); 532 } 533 534 static void 535 vtcon_determine_max_ports(struct vtcon_softc *sc, 536 struct virtio_console_config *concfg) 537 { 538 539 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 540 sc->vtcon_max_ports = 541 min(concfg->max_nr_ports, VTCON_MAX_PORTS); 542 if (sc->vtcon_max_ports == 0) 543 sc->vtcon_max_ports = 1; 544 } else 545 sc->vtcon_max_ports = 1; 546 } 547 548 static void 549 vtcon_destroy_ports(struct vtcon_softc *sc) 550 { 551 struct vtcon_softc_port *scport; 552 struct vtcon_port *port; 553 struct virtqueue *vq; 554 int i; 555 556 if (sc->vtcon_ports == NULL) 557 return; 558 559 VTCON_LOCK(sc); 560 for (i = 0; i < sc->vtcon_max_ports; i++) { 561 scport = &sc->vtcon_ports[i]; 562 563 port = scport->vcsp_port; 564 if (port != NULL) { 565 scport->vcsp_port = NULL; 566 VTCON_PORT_LOCK(port); 567 VTCON_UNLOCK(sc); 568 vtcon_port_teardown(port); 569 VTCON_LOCK(sc); 570 } 571 572 vq = scport->vcsp_invq; 573 if (vq != NULL) 574 vtcon_port_drain_bufs(vq); 575 } 576 VTCON_UNLOCK(sc); 577 578 free(sc->vtcon_ports, M_DEVBUF); 579 sc->vtcon_ports = NULL; 580 } 581 582 static void 583 vtcon_stop(struct vtcon_softc *sc) 584 { 585 586 vtcon_disable_interrupts(sc); 587 virtio_stop(sc->vtcon_dev); 588 } 589 590 static int 591 vtcon_ctrl_event_enqueue(struct vtcon_softc *sc, 592 struct virtio_console_control *control) 593 { 594 struct sglist_seg segs[2]; 595 struct sglist sg; 596 struct virtqueue *vq; 597 int error; 598 599 vq = sc->vtcon_ctrl_rxvq; 600 601 sglist_init(&sg, 2, segs); 602 error = sglist_append(&sg, control, 603 sizeof(struct virtio_console_control) + VTCON_BULK_BUFSZ); 604 KASSERT(error == 0, ("%s: error %d adding control to sglist", 605 __func__, error)); 606 607 return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg)); 608 } 609 610 static int 611 vtcon_ctrl_event_create(struct vtcon_softc *sc) 612 { 613 struct virtio_console_control *control; 614 int error; 615 616 control = malloc( 617 sizeof(struct virtio_console_control) + VTCON_BULK_BUFSZ, 618 M_DEVBUF, M_ZERO | M_NOWAIT); 619 620 if (control == NULL) 621 return (ENOMEM); 622 623 error = vtcon_ctrl_event_enqueue(sc, control); 624 if (error) 625 free(control, M_DEVBUF); 626 627 return (error); 628 } 629 630 static void 631 vtcon_ctrl_event_requeue(struct vtcon_softc *sc, 632 struct virtio_console_control *control) 633 { 634 int error; 635 636 bzero(control, sizeof(struct virtio_console_control) + 637 VTCON_BULK_BUFSZ); 638 639 error = vtcon_ctrl_event_enqueue(sc, control); 640 KASSERT(error == 0, 641 ("%s: cannot requeue control buffer %d", __func__, error)); 642 } 643 644 static int 645 vtcon_ctrl_event_populate(struct vtcon_softc *sc) 646 { 647 struct virtqueue *vq; 648 int nbufs, error; 649 650 vq = sc->vtcon_ctrl_rxvq; 651 error = ENOSPC; 652 653 for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 654 error = vtcon_ctrl_event_create(sc); 655 if (error) 656 break; 657 } 658 659 if (nbufs > 0) { 660 virtqueue_notify(vq); 661 error = 0; 662 } 663 664 return (error); 665 } 666 667 static void 668 vtcon_ctrl_event_drain(struct vtcon_softc *sc) 669 { 670 struct virtio_console_control *control; 671 struct virtqueue *vq; 672 int last; 673 674 vq = sc->vtcon_ctrl_rxvq; 675 last = 0; 676 677 if (vq == NULL) 678 return; 679 680 VTCON_LOCK(sc); 681 while ((control = virtqueue_drain(vq, &last)) != NULL) 682 free(control, M_DEVBUF); 683 VTCON_UNLOCK(sc); 684 } 685 686 static int 687 vtcon_ctrl_init(struct vtcon_softc *sc) 688 { 689 int error; 690 691 error = vtcon_ctrl_event_populate(sc); 692 693 return (error); 694 } 695 696 static void 697 vtcon_ctrl_deinit(struct vtcon_softc *sc) 698 { 699 700 vtcon_ctrl_event_drain(sc); 701 } 702 703 static void 704 vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id) 705 { 706 device_t dev; 707 int error; 708 709 dev = sc->vtcon_dev; 710 711 /* This single thread only way for ports to be created. */ 712 if (sc->vtcon_ports[id].vcsp_port != NULL) { 713 device_printf(dev, "%s: adding port %d, but already exists\n", 714 __func__, id); 715 return; 716 } 717 718 error = vtcon_port_create(sc, id); 719 if (error) { 720 device_printf(dev, "%s: cannot create port %d: %d\n", 721 __func__, id, error); 722 vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0); 723 return; 724 } 725 } 726 727 static void 728 vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id) 729 { 730 device_t dev; 731 struct vtcon_softc_port *scport; 732 struct vtcon_port *port; 733 734 dev = sc->vtcon_dev; 735 scport = &sc->vtcon_ports[id]; 736 737 VTCON_LOCK(sc); 738 port = scport->vcsp_port; 739 if (port == NULL) { 740 VTCON_UNLOCK(sc); 741 device_printf(dev, "%s: remove port %d, but does not exist\n", 742 __func__, id); 743 return; 744 } 745 746 scport->vcsp_port = NULL; 747 VTCON_PORT_LOCK(port); 748 VTCON_UNLOCK(sc); 749 vtcon_port_teardown(port); 750 } 751 752 static void 753 vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id) 754 { 755 device_t dev; 756 struct vtcon_softc_port *scport; 757 struct vtcon_port *port; 758 759 dev = sc->vtcon_dev; 760 scport = &sc->vtcon_ports[id]; 761 762 VTCON_LOCK(sc); 763 port = scport->vcsp_port; 764 if (port == NULL) { 765 VTCON_UNLOCK(sc); 766 device_printf(dev, "%s: console port %d, but does not exist\n", 767 __func__, id); 768 return; 769 } 770 771 VTCON_PORT_LOCK(port); 772 VTCON_UNLOCK(sc); 773 port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE; 774 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 775 VTCON_PORT_UNLOCK(port); 776 } 777 778 static void 779 vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id) 780 { 781 device_t dev; 782 struct vtcon_softc_port *scport; 783 struct vtcon_port *port; 784 785 dev = sc->vtcon_dev; 786 scport = &sc->vtcon_ports[id]; 787 788 VTCON_LOCK(sc); 789 port = scport->vcsp_port; 790 if (port == NULL) { 791 VTCON_UNLOCK(sc); 792 device_printf(dev, "%s: open port %d, but does not exist\n", 793 __func__, id); 794 return; 795 } 796 797 VTCON_PORT_LOCK(port); 798 VTCON_UNLOCK(sc); 799 vtcon_port_enable_intr(port); 800 VTCON_PORT_UNLOCK(port); 801 } 802 803 static void 804 vtcon_ctrl_port_name_event(struct vtcon_softc *sc, int id, const char *name, 805 size_t len) 806 { 807 device_t dev; 808 struct vtcon_softc_port *scport; 809 struct vtcon_port *port; 810 811 dev = sc->vtcon_dev; 812 scport = &sc->vtcon_ports[id]; 813 814 port = scport->vcsp_port; 815 if (port == NULL) { 816 device_printf(dev, "%s: name port %d, but does not exist\n", 817 __func__, id); 818 return; 819 } 820 821 tty_makealias(port->vtcport_tty, "vtcon/%*s", (int)len, name); 822 } 823 824 static void 825 vtcon_ctrl_process_event(struct vtcon_softc *sc, 826 struct virtio_console_control *control, void *payload, size_t plen) 827 { 828 device_t dev; 829 int id; 830 831 dev = sc->vtcon_dev; 832 id = control->id; 833 834 if (id < 0 || id >= sc->vtcon_max_ports) { 835 device_printf(dev, "%s: invalid port ID %d\n", __func__, id); 836 return; 837 } 838 839 switch (control->event) { 840 case VIRTIO_CONSOLE_PORT_ADD: 841 vtcon_ctrl_port_add_event(sc, id); 842 break; 843 844 case VIRTIO_CONSOLE_PORT_REMOVE: 845 vtcon_ctrl_port_remove_event(sc, id); 846 break; 847 848 case VIRTIO_CONSOLE_CONSOLE_PORT: 849 vtcon_ctrl_port_console_event(sc, id); 850 break; 851 852 case VIRTIO_CONSOLE_RESIZE: 853 break; 854 855 case VIRTIO_CONSOLE_PORT_OPEN: 856 vtcon_ctrl_port_open_event(sc, id); 857 break; 858 859 case VIRTIO_CONSOLE_PORT_NAME: 860 if (payload != NULL && plen > 0) 861 vtcon_ctrl_port_name_event(sc, id, 862 (const char *)payload, plen); 863 break; 864 } 865 } 866 867 static void 868 vtcon_ctrl_task_cb(void *xsc, int pending) 869 { 870 struct vtcon_softc *sc; 871 struct virtqueue *vq; 872 struct virtio_console_control *control; 873 int detached; 874 uint32_t len; 875 size_t plen; 876 void *payload; 877 878 sc = xsc; 879 vq = sc->vtcon_ctrl_rxvq; 880 881 VTCON_LOCK(sc); 882 883 while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) { 884 control = virtqueue_dequeue(vq, &len); 885 payload = NULL; 886 plen = 0; 887 888 if (control == NULL) 889 break; 890 891 if (len > sizeof(*control)) { 892 payload = (void *)(control + 1); 893 plen = len - sizeof(*control); 894 } 895 896 VTCON_UNLOCK(sc); 897 vtcon_ctrl_process_event(sc, control, payload, plen); 898 VTCON_LOCK(sc); 899 vtcon_ctrl_event_requeue(sc, control); 900 } 901 902 if (!detached) { 903 virtqueue_notify(vq); 904 if (virtqueue_enable_intr(vq) != 0) 905 taskqueue_enqueue(taskqueue_thread, 906 &sc->vtcon_ctrl_task); 907 } 908 909 VTCON_UNLOCK(sc); 910 } 911 912 static void 913 vtcon_ctrl_event_intr(void *xsc) 914 { 915 struct vtcon_softc *sc; 916 917 sc = xsc; 918 919 /* 920 * Only some events require us to potentially block, but it 921 * easier to just defer all event handling to the taskqueue. 922 */ 923 taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task); 924 } 925 926 static void 927 vtcon_ctrl_poll(struct vtcon_softc *sc, 928 struct virtio_console_control *control) 929 { 930 struct sglist_seg segs[2]; 931 struct sglist sg; 932 struct virtqueue *vq; 933 int error; 934 935 vq = sc->vtcon_ctrl_txvq; 936 937 sglist_init(&sg, 2, segs); 938 error = sglist_append(&sg, control, 939 sizeof(struct virtio_console_control)); 940 KASSERT(error == 0, ("%s: error %d adding control to sglist", 941 __func__, error)); 942 943 /* 944 * We cannot use the softc lock to serialize access to this 945 * virtqueue since this is called from the tty layer with the 946 * port lock held. Acquiring the softc would violate our lock 947 * ordering. 948 */ 949 VTCON_CTRL_TX_LOCK(sc); 950 KASSERT(virtqueue_empty(vq), 951 ("%s: virtqueue is not emtpy", __func__)); 952 error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0); 953 if (error == 0) { 954 virtqueue_notify(vq); 955 virtqueue_poll(vq, NULL); 956 } 957 VTCON_CTRL_TX_UNLOCK(sc); 958 } 959 960 static void 961 vtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid, 962 uint16_t event, uint16_t value) 963 { 964 struct virtio_console_control control; 965 966 if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) 967 return; 968 969 control.id = portid; 970 control.event = event; 971 control.value = value; 972 973 vtcon_ctrl_poll(sc, &control); 974 } 975 976 static int 977 vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len) 978 { 979 struct sglist_seg segs[2]; 980 struct sglist sg; 981 struct virtqueue *vq; 982 int error; 983 984 vq = port->vtcport_invq; 985 986 sglist_init(&sg, 2, segs); 987 error = sglist_append(&sg, buf, len); 988 KASSERT(error == 0, 989 ("%s: error %d adding buffer to sglist", __func__, error)); 990 991 error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg); 992 993 return (error); 994 } 995 996 static int 997 vtcon_port_create_buf(struct vtcon_port *port) 998 { 999 void *buf; 1000 int error; 1001 1002 buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT); 1003 if (buf == NULL) 1004 return (ENOMEM); 1005 1006 error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 1007 if (error) 1008 free(buf, M_DEVBUF); 1009 1010 return (error); 1011 } 1012 1013 static void 1014 vtcon_port_requeue_buf(struct vtcon_port *port, void *buf) 1015 { 1016 int error; 1017 1018 error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 1019 KASSERT(error == 0, 1020 ("%s: cannot requeue input buffer %d", __func__, error)); 1021 } 1022 1023 static int 1024 vtcon_port_populate(struct vtcon_port *port) 1025 { 1026 struct virtqueue *vq; 1027 int nbufs, error; 1028 1029 vq = port->vtcport_invq; 1030 error = ENOSPC; 1031 1032 for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 1033 error = vtcon_port_create_buf(port); 1034 if (error) 1035 break; 1036 } 1037 1038 if (nbufs > 0) { 1039 virtqueue_notify(vq); 1040 error = 0; 1041 } 1042 1043 return (error); 1044 } 1045 1046 static void 1047 vtcon_port_destroy(struct vtcon_port *port) 1048 { 1049 1050 port->vtcport_sc = NULL; 1051 port->vtcport_scport = NULL; 1052 port->vtcport_invq = NULL; 1053 port->vtcport_outvq = NULL; 1054 port->vtcport_id = -1; 1055 mtx_destroy(&port->vtcport_mtx); 1056 free(port, M_DEVBUF); 1057 } 1058 1059 static int 1060 vtcon_port_init_vqs(struct vtcon_port *port) 1061 { 1062 struct vtcon_softc_port *scport; 1063 int error; 1064 1065 scport = port->vtcport_scport; 1066 1067 port->vtcport_invq = scport->vcsp_invq; 1068 port->vtcport_outvq = scport->vcsp_outvq; 1069 1070 /* 1071 * Free any data left over from when this virtqueue was in use by a 1072 * prior port. We have not yet notified the host that the port is 1073 * ready, so assume nothing in the virtqueue can be for us. 1074 */ 1075 vtcon_port_drain(port); 1076 1077 KASSERT(virtqueue_empty(port->vtcport_invq), 1078 ("%s: in virtqueue is not empty", __func__)); 1079 KASSERT(virtqueue_empty(port->vtcport_outvq), 1080 ("%s: out virtqueue is not empty", __func__)); 1081 1082 error = vtcon_port_populate(port); 1083 if (error) 1084 return (error); 1085 1086 return (0); 1087 } 1088 1089 static int 1090 vtcon_port_create(struct vtcon_softc *sc, int id) 1091 { 1092 device_t dev; 1093 struct vtcon_softc_port *scport; 1094 struct vtcon_port *port; 1095 int error; 1096 1097 dev = sc->vtcon_dev; 1098 scport = &sc->vtcon_ports[id]; 1099 1100 VTCON_ASSERT_VALID_PORTID(sc, id); 1101 MPASS(scport->vcsp_port == NULL); 1102 1103 port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO); 1104 if (port == NULL) 1105 return (ENOMEM); 1106 1107 port->vtcport_sc = sc; 1108 port->vtcport_scport = scport; 1109 port->vtcport_id = id; 1110 mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF); 1111 port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port, 1112 &port->vtcport_mtx); 1113 1114 error = vtcon_port_init_vqs(port); 1115 if (error) { 1116 VTCON_PORT_LOCK(port); 1117 vtcon_port_teardown(port); 1118 return (error); 1119 } 1120 1121 VTCON_LOCK(sc); 1122 VTCON_PORT_LOCK(port); 1123 scport->vcsp_port = port; 1124 vtcon_port_enable_intr(port); 1125 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1); 1126 VTCON_PORT_UNLOCK(port); 1127 VTCON_UNLOCK(sc); 1128 1129 tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX, 1130 device_get_unit(dev), id); 1131 1132 return (0); 1133 } 1134 1135 static void 1136 vtcon_port_drain_bufs(struct virtqueue *vq) 1137 { 1138 void *buf; 1139 int last; 1140 1141 last = 0; 1142 1143 while ((buf = virtqueue_drain(vq, &last)) != NULL) 1144 free(buf, M_DEVBUF); 1145 } 1146 1147 static void 1148 vtcon_port_drain(struct vtcon_port *port) 1149 { 1150 1151 vtcon_port_drain_bufs(port->vtcport_invq); 1152 } 1153 1154 static void 1155 vtcon_port_teardown(struct vtcon_port *port) 1156 { 1157 struct tty *tp; 1158 1159 tp = port->vtcport_tty; 1160 1161 port->vtcport_flags |= VTCON_PORT_FLAG_GONE; 1162 1163 if (tp != NULL) { 1164 atomic_add_int(&vtcon_pending_free, 1); 1165 tty_rel_gone(tp); 1166 } else 1167 vtcon_port_destroy(port); 1168 } 1169 1170 static void 1171 vtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows) 1172 { 1173 struct tty *tp; 1174 struct winsize sz; 1175 1176 tp = port->vtcport_tty; 1177 1178 if (tp == NULL) 1179 return; 1180 1181 bzero(&sz, sizeof(struct winsize)); 1182 sz.ws_col = cols; 1183 sz.ws_row = rows; 1184 1185 tty_set_winsize(tp, &sz); 1186 } 1187 1188 static void 1189 vtcon_port_update_console_size(struct vtcon_softc *sc) 1190 { 1191 struct vtcon_port *port; 1192 struct vtcon_softc_port *scport; 1193 uint16_t cols, rows; 1194 1195 vtcon_get_console_size(sc, &cols, &rows); 1196 1197 /* 1198 * For now, assume the first (only) port is the console. Note 1199 * QEMU does not implement this feature yet. 1200 */ 1201 scport = &sc->vtcon_ports[0]; 1202 1203 VTCON_LOCK(sc); 1204 port = scport->vcsp_port; 1205 1206 if (port != NULL) { 1207 VTCON_PORT_LOCK(port); 1208 VTCON_UNLOCK(sc); 1209 vtcon_port_change_size(port, cols, rows); 1210 VTCON_PORT_UNLOCK(port); 1211 } else 1212 VTCON_UNLOCK(sc); 1213 } 1214 1215 static void 1216 vtcon_port_enable_intr(struct vtcon_port *port) 1217 { 1218 1219 /* 1220 * NOTE: The out virtqueue is always polled, so its interrupt 1221 * kept disabled. 1222 */ 1223 virtqueue_enable_intr(port->vtcport_invq); 1224 } 1225 1226 static void 1227 vtcon_port_disable_intr(struct vtcon_port *port) 1228 { 1229 1230 if (port->vtcport_invq != NULL) 1231 virtqueue_disable_intr(port->vtcport_invq); 1232 if (port->vtcport_outvq != NULL) 1233 virtqueue_disable_intr(port->vtcport_outvq); 1234 } 1235 1236 static void 1237 vtcon_port_in(struct vtcon_port *port) 1238 { 1239 struct virtqueue *vq; 1240 struct tty *tp; 1241 char *buf; 1242 uint32_t len; 1243 int i, deq; 1244 1245 tp = port->vtcport_tty; 1246 vq = port->vtcport_invq; 1247 1248 again: 1249 deq = 0; 1250 1251 while ((buf = virtqueue_dequeue(vq, &len)) != NULL) { 1252 for (i = 0; i < len; i++) { 1253 #if defined(KDB) 1254 if (port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE) 1255 kdb_alt_break(buf[i], 1256 &port->vtcport_alt_break_state); 1257 #endif 1258 ttydisc_rint(tp, buf[i], 0); 1259 } 1260 vtcon_port_requeue_buf(port, buf); 1261 deq++; 1262 } 1263 ttydisc_rint_done(tp); 1264 1265 if (deq > 0) 1266 virtqueue_notify(vq); 1267 1268 if (virtqueue_enable_intr(vq) != 0) 1269 goto again; 1270 } 1271 1272 static void 1273 vtcon_port_intr(void *scportx) 1274 { 1275 struct vtcon_softc_port *scport; 1276 struct vtcon_softc *sc; 1277 struct vtcon_port *port; 1278 1279 scport = scportx; 1280 sc = scport->vcsp_sc; 1281 1282 VTCON_LOCK(sc); 1283 port = scport->vcsp_port; 1284 if (port == NULL) { 1285 VTCON_UNLOCK(sc); 1286 return; 1287 } 1288 VTCON_PORT_LOCK(port); 1289 VTCON_UNLOCK(sc); 1290 if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0) 1291 vtcon_port_in(port); 1292 VTCON_PORT_UNLOCK(port); 1293 } 1294 1295 static void 1296 vtcon_port_out(struct vtcon_port *port, void *buf, int bufsize) 1297 { 1298 struct sglist_seg segs[2]; 1299 struct sglist sg; 1300 struct virtqueue *vq; 1301 int error; 1302 1303 vq = port->vtcport_outvq; 1304 KASSERT(virtqueue_empty(vq), 1305 ("%s: port %p out virtqueue not emtpy", __func__, port)); 1306 1307 sglist_init(&sg, 2, segs); 1308 error = sglist_append(&sg, buf, bufsize); 1309 KASSERT(error == 0, ("%s: error %d adding buffer to sglist", 1310 __func__, error)); 1311 1312 error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0); 1313 if (error == 0) { 1314 virtqueue_notify(vq); 1315 virtqueue_poll(vq, NULL); 1316 } 1317 } 1318 1319 static void 1320 vtcon_port_submit_event(struct vtcon_port *port, uint16_t event, 1321 uint16_t value) 1322 { 1323 struct vtcon_softc *sc; 1324 1325 sc = port->vtcport_sc; 1326 1327 vtcon_ctrl_send_control(sc, port->vtcport_id, event, value); 1328 } 1329 1330 static int 1331 vtcon_tty_open(struct tty *tp) 1332 { 1333 struct vtcon_port *port; 1334 1335 port = tty_softc(tp); 1336 1337 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1338 return (ENXIO); 1339 1340 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 1341 1342 return (0); 1343 } 1344 1345 static void 1346 vtcon_tty_close(struct tty *tp) 1347 { 1348 struct vtcon_port *port; 1349 1350 port = tty_softc(tp); 1351 1352 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1353 return; 1354 1355 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); 1356 } 1357 1358 static void 1359 vtcon_tty_outwakeup(struct tty *tp) 1360 { 1361 struct vtcon_port *port; 1362 char buf[VTCON_BULK_BUFSZ]; 1363 int len; 1364 1365 port = tty_softc(tp); 1366 1367 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1368 return; 1369 1370 while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0) 1371 vtcon_port_out(port, buf, len); 1372 } 1373 1374 static void 1375 vtcon_tty_free(void *xport) 1376 { 1377 struct vtcon_port *port; 1378 1379 port = xport; 1380 1381 vtcon_port_destroy(port); 1382 atomic_subtract_int(&vtcon_pending_free, 1); 1383 } 1384 1385 static void 1386 vtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows) 1387 { 1388 struct virtio_console_config concfg; 1389 1390 KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE, 1391 ("%s: size feature not negotiated", __func__)); 1392 1393 vtcon_read_config(sc, &concfg); 1394 1395 *cols = concfg.cols; 1396 *rows = concfg.rows; 1397 } 1398 1399 static void 1400 vtcon_enable_interrupts(struct vtcon_softc *sc) 1401 { 1402 struct vtcon_softc_port *scport; 1403 struct vtcon_port *port; 1404 int i; 1405 1406 VTCON_LOCK(sc); 1407 1408 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 1409 virtqueue_enable_intr(sc->vtcon_ctrl_rxvq); 1410 1411 for (i = 0; i < sc->vtcon_max_ports; i++) { 1412 scport = &sc->vtcon_ports[i]; 1413 1414 port = scport->vcsp_port; 1415 if (port == NULL) 1416 continue; 1417 1418 VTCON_PORT_LOCK(port); 1419 vtcon_port_enable_intr(port); 1420 VTCON_PORT_UNLOCK(port); 1421 } 1422 1423 VTCON_UNLOCK(sc); 1424 } 1425 1426 static void 1427 vtcon_disable_interrupts(struct vtcon_softc *sc) 1428 { 1429 struct vtcon_softc_port *scport; 1430 struct vtcon_port *port; 1431 int i; 1432 1433 VTCON_LOCK_ASSERT(sc); 1434 1435 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 1436 virtqueue_disable_intr(sc->vtcon_ctrl_rxvq); 1437 1438 for (i = 0; i < sc->vtcon_max_ports; i++) { 1439 scport = &sc->vtcon_ports[i]; 1440 1441 port = scport->vcsp_port; 1442 if (port == NULL) 1443 continue; 1444 1445 VTCON_PORT_LOCK(port); 1446 vtcon_port_disable_intr(port); 1447 VTCON_PORT_UNLOCK(port); 1448 } 1449 } 1450