16f744ddeSBryan Venteicher /*- 26f744ddeSBryan Venteicher * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org> 36f744ddeSBryan Venteicher * All rights reserved. 46f744ddeSBryan Venteicher * 56f744ddeSBryan Venteicher * Redistribution and use in source and binary forms, with or without 66f744ddeSBryan Venteicher * modification, are permitted provided that the following conditions 76f744ddeSBryan Venteicher * are met: 86f744ddeSBryan Venteicher * 1. Redistributions of source code must retain the above copyright 96f744ddeSBryan Venteicher * notice unmodified, this list of conditions, and the following 106f744ddeSBryan Venteicher * disclaimer. 116f744ddeSBryan Venteicher * 2. Redistributions in binary form must reproduce the above copyright 126f744ddeSBryan Venteicher * notice, this list of conditions and the following disclaimer in the 136f744ddeSBryan Venteicher * documentation and/or other materials provided with the distribution. 146f744ddeSBryan Venteicher * 156f744ddeSBryan Venteicher * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 166f744ddeSBryan Venteicher * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 176f744ddeSBryan Venteicher * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 186f744ddeSBryan Venteicher * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 196f744ddeSBryan Venteicher * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 206f744ddeSBryan Venteicher * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 216f744ddeSBryan Venteicher * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 226f744ddeSBryan Venteicher * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 236f744ddeSBryan Venteicher * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 246f744ddeSBryan Venteicher * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 256f744ddeSBryan Venteicher */ 266f744ddeSBryan Venteicher 276f744ddeSBryan Venteicher /* Driver for VirtIO console devices. */ 286f744ddeSBryan Venteicher 296f744ddeSBryan Venteicher #include <sys/cdefs.h> 306f744ddeSBryan Venteicher __FBSDID("$FreeBSD$"); 316f744ddeSBryan Venteicher 326f744ddeSBryan Venteicher #include <sys/param.h> 33a019e26cSBryan Venteicher #include <sys/ctype.h> 346f744ddeSBryan Venteicher #include <sys/systm.h> 356f744ddeSBryan Venteicher #include <sys/kernel.h> 366f744ddeSBryan Venteicher #include <sys/malloc.h> 376f744ddeSBryan Venteicher #include <sys/module.h> 38b84b3efdSBryan Venteicher #include <sys/kdb.h> 396f744ddeSBryan Venteicher #include <sys/lock.h> 406f744ddeSBryan Venteicher #include <sys/mutex.h> 416f744ddeSBryan Venteicher #include <sys/sglist.h> 426f744ddeSBryan Venteicher #include <sys/sysctl.h> 436f744ddeSBryan Venteicher #include <sys/taskqueue.h> 446f744ddeSBryan Venteicher #include <sys/queue.h> 456f744ddeSBryan Venteicher 466f744ddeSBryan Venteicher #include <sys/conf.h> 476f744ddeSBryan Venteicher #include <sys/cons.h> 486f744ddeSBryan Venteicher #include <sys/tty.h> 496f744ddeSBryan Venteicher 506f744ddeSBryan Venteicher #include <machine/bus.h> 516f744ddeSBryan Venteicher #include <machine/resource.h> 526f744ddeSBryan Venteicher #include <sys/bus.h> 536f744ddeSBryan Venteicher 546f744ddeSBryan Venteicher #include <dev/virtio/virtio.h> 556f744ddeSBryan Venteicher #include <dev/virtio/virtqueue.h> 566f744ddeSBryan Venteicher #include <dev/virtio/console/virtio_console.h> 576f744ddeSBryan Venteicher 586f744ddeSBryan Venteicher #include "virtio_if.h" 596f744ddeSBryan Venteicher 6004434c94SBryan Venteicher #define VTCON_MAX_PORTS 32 616f744ddeSBryan Venteicher #define VTCON_TTY_PREFIX "V" 62a019e26cSBryan Venteicher #define VTCON_TTY_ALIAS_PREFIX "vtcon" 636f744ddeSBryan Venteicher #define VTCON_BULK_BUFSZ 128 64a019e26cSBryan Venteicher #define VTCON_CTRL_BUFSZ 128 656f744ddeSBryan Venteicher 6604434c94SBryan Venteicher /* 67a019e26cSBryan Venteicher * The buffers cannot cross more than one page boundary due to the 6804434c94SBryan Venteicher * size of the sglist segment array used. 6904434c94SBryan Venteicher */ 7004434c94SBryan Venteicher CTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE); 71a019e26cSBryan Venteicher CTASSERT(VTCON_CTRL_BUFSZ <= PAGE_SIZE); 72a019e26cSBryan Venteicher 73a019e26cSBryan Venteicher CTASSERT(sizeof(struct virtio_console_config) <= VTCON_CTRL_BUFSZ); 7404434c94SBryan Venteicher 756f744ddeSBryan Venteicher struct vtcon_softc; 7604434c94SBryan Venteicher struct vtcon_softc_port; 776f744ddeSBryan Venteicher 786f744ddeSBryan Venteicher struct vtcon_port { 796f744ddeSBryan Venteicher struct mtx vtcport_mtx; 8004434c94SBryan Venteicher struct vtcon_softc *vtcport_sc; 8104434c94SBryan Venteicher struct vtcon_softc_port *vtcport_scport; 826f744ddeSBryan Venteicher struct tty *vtcport_tty; 836f744ddeSBryan Venteicher struct virtqueue *vtcport_invq; 846f744ddeSBryan Venteicher struct virtqueue *vtcport_outvq; 8504434c94SBryan Venteicher int vtcport_id; 8604434c94SBryan Venteicher int vtcport_flags; 8704434c94SBryan Venteicher #define VTCON_PORT_FLAG_GONE 0x01 88b84b3efdSBryan Venteicher #define VTCON_PORT_FLAG_CONSOLE 0x02 89a019e26cSBryan Venteicher #define VTCON_PORT_FLAG_ALIAS 0x04 90b84b3efdSBryan Venteicher 91b84b3efdSBryan Venteicher #if defined(KDB) 92b84b3efdSBryan Venteicher int vtcport_alt_break_state; 93b84b3efdSBryan Venteicher #endif 946f744ddeSBryan Venteicher }; 956f744ddeSBryan Venteicher 9604434c94SBryan Venteicher #define VTCON_PORT_LOCK(_port) mtx_lock(&(_port)->vtcport_mtx) 9704434c94SBryan Venteicher #define VTCON_PORT_UNLOCK(_port) mtx_unlock(&(_port)->vtcport_mtx) 9804434c94SBryan Venteicher 9904434c94SBryan Venteicher struct vtcon_softc_port { 10004434c94SBryan Venteicher struct vtcon_softc *vcsp_sc; 10104434c94SBryan Venteicher struct vtcon_port *vcsp_port; 10204434c94SBryan Venteicher struct virtqueue *vcsp_invq; 10304434c94SBryan Venteicher struct virtqueue *vcsp_outvq; 10404434c94SBryan Venteicher }; 1056f744ddeSBryan Venteicher 1066f744ddeSBryan Venteicher struct vtcon_softc { 1076f744ddeSBryan Venteicher device_t vtcon_dev; 1086f744ddeSBryan Venteicher struct mtx vtcon_mtx; 1096f744ddeSBryan Venteicher uint64_t vtcon_features; 1106f744ddeSBryan Venteicher uint32_t vtcon_max_ports; 111b84b3efdSBryan Venteicher uint32_t vtcon_flags; 112b84b3efdSBryan Venteicher #define VTCON_FLAG_DETACHED 0x01 113b84b3efdSBryan Venteicher #define VTCON_FLAG_SIZE 0x02 114b84b3efdSBryan Venteicher #define VTCON_FLAG_MULTIPORT 0x04 1156f744ddeSBryan Venteicher 1166f744ddeSBryan Venteicher /* 1176f744ddeSBryan Venteicher * Ports can be added and removed during runtime, but we have 1186f744ddeSBryan Venteicher * to allocate all the virtqueues during attach. This array is 1196f744ddeSBryan Venteicher * indexed by the port ID. 1206f744ddeSBryan Venteicher */ 12104434c94SBryan Venteicher struct vtcon_softc_port *vtcon_ports; 122b84b3efdSBryan Venteicher 123b84b3efdSBryan Venteicher struct task vtcon_ctrl_task; 124b84b3efdSBryan Venteicher struct virtqueue *vtcon_ctrl_rxvq; 125b84b3efdSBryan Venteicher struct virtqueue *vtcon_ctrl_txvq; 126b84b3efdSBryan Venteicher struct mtx vtcon_ctrl_tx_mtx; 1276f744ddeSBryan Venteicher }; 1286f744ddeSBryan Venteicher 12904434c94SBryan Venteicher #define VTCON_LOCK(_sc) mtx_lock(&(_sc)->vtcon_mtx) 13004434c94SBryan Venteicher #define VTCON_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_mtx) 13104434c94SBryan Venteicher #define VTCON_LOCK_ASSERT(_sc) \ 13204434c94SBryan Venteicher mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED) 1336f744ddeSBryan Venteicher #define VTCON_LOCK_ASSERT_NOTOWNED(_sc) \ 13404434c94SBryan Venteicher mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED) 13504434c94SBryan Venteicher 13604434c94SBryan Venteicher #define VTCON_CTRL_TX_LOCK(_sc) mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx) 13704434c94SBryan Venteicher #define VTCON_CTRL_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx) 1386f744ddeSBryan Venteicher 1396f744ddeSBryan Venteicher #define VTCON_ASSERT_VALID_PORTID(_sc, _id) \ 1406f744ddeSBryan Venteicher KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports, \ 1416f744ddeSBryan Venteicher ("%s: port ID %d out of range", __func__, _id)) 1426f744ddeSBryan Venteicher 14304434c94SBryan Venteicher #define VTCON_FEATURES VIRTIO_CONSOLE_F_MULTIPORT 1446f744ddeSBryan Venteicher 1456f744ddeSBryan Venteicher static struct virtio_feature_desc vtcon_feature_desc[] = { 1466f744ddeSBryan Venteicher { VIRTIO_CONSOLE_F_SIZE, "ConsoleSize" }, 1476f744ddeSBryan Venteicher { VIRTIO_CONSOLE_F_MULTIPORT, "MultiplePorts" }, 148b84b3efdSBryan Venteicher { VIRTIO_CONSOLE_F_EMERG_WRITE, "EmergencyWrite" }, 1496f744ddeSBryan Venteicher 1506f744ddeSBryan Venteicher { 0, NULL } 1516f744ddeSBryan Venteicher }; 1526f744ddeSBryan Venteicher 1536f744ddeSBryan Venteicher static int vtcon_modevent(module_t, int, void *); 15404434c94SBryan Venteicher static void vtcon_drain_all(void); 1556f744ddeSBryan Venteicher 1566f744ddeSBryan Venteicher static int vtcon_probe(device_t); 1576f744ddeSBryan Venteicher static int vtcon_attach(device_t); 1586f744ddeSBryan Venteicher static int vtcon_detach(device_t); 1596f744ddeSBryan Venteicher static int vtcon_config_change(device_t); 1606f744ddeSBryan Venteicher 16104434c94SBryan Venteicher static void vtcon_setup_features(struct vtcon_softc *); 1626f744ddeSBryan Venteicher static void vtcon_negotiate_features(struct vtcon_softc *); 16304434c94SBryan Venteicher static int vtcon_alloc_scports(struct vtcon_softc *); 1646f744ddeSBryan Venteicher static int vtcon_alloc_virtqueues(struct vtcon_softc *); 1656f744ddeSBryan Venteicher static void vtcon_read_config(struct vtcon_softc *, 1666f744ddeSBryan Venteicher struct virtio_console_config *); 1676f744ddeSBryan Venteicher 1686f744ddeSBryan Venteicher static void vtcon_determine_max_ports(struct vtcon_softc *, 1696f744ddeSBryan Venteicher struct virtio_console_config *); 17004434c94SBryan Venteicher static void vtcon_destroy_ports(struct vtcon_softc *); 1716f744ddeSBryan Venteicher static void vtcon_stop(struct vtcon_softc *); 1726f744ddeSBryan Venteicher 17304434c94SBryan Venteicher static int vtcon_ctrl_event_enqueue(struct vtcon_softc *, 1746f744ddeSBryan Venteicher struct virtio_console_control *); 17504434c94SBryan Venteicher static int vtcon_ctrl_event_create(struct vtcon_softc *); 17604434c94SBryan Venteicher static void vtcon_ctrl_event_requeue(struct vtcon_softc *, 1776f744ddeSBryan Venteicher struct virtio_console_control *); 17804434c94SBryan Venteicher static int vtcon_ctrl_event_populate(struct vtcon_softc *); 17904434c94SBryan Venteicher static void vtcon_ctrl_event_drain(struct vtcon_softc *); 1806f744ddeSBryan Venteicher static int vtcon_ctrl_init(struct vtcon_softc *); 1816f744ddeSBryan Venteicher static void vtcon_ctrl_deinit(struct vtcon_softc *); 1826f744ddeSBryan Venteicher static void vtcon_ctrl_port_add_event(struct vtcon_softc *, int); 1836f744ddeSBryan Venteicher static void vtcon_ctrl_port_remove_event(struct vtcon_softc *, int); 1846f744ddeSBryan Venteicher static void vtcon_ctrl_port_console_event(struct vtcon_softc *, int); 1856f744ddeSBryan Venteicher static void vtcon_ctrl_port_open_event(struct vtcon_softc *, int); 186cb981343SJakub Wojciech Klama static void vtcon_ctrl_port_name_event(struct vtcon_softc *, int, 187cb981343SJakub Wojciech Klama const char *, size_t); 18804434c94SBryan Venteicher static void vtcon_ctrl_process_event(struct vtcon_softc *, 189cb981343SJakub Wojciech Klama struct virtio_console_control *, void *, size_t); 1906f744ddeSBryan Venteicher static void vtcon_ctrl_task_cb(void *, int); 19104434c94SBryan Venteicher static void vtcon_ctrl_event_intr(void *); 19204434c94SBryan Venteicher static void vtcon_ctrl_poll(struct vtcon_softc *, 19304434c94SBryan Venteicher struct virtio_console_control *control); 19404434c94SBryan Venteicher static void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t, 19504434c94SBryan Venteicher uint16_t, uint16_t); 1966f744ddeSBryan Venteicher 19704434c94SBryan Venteicher static int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t); 19804434c94SBryan Venteicher static int vtcon_port_create_buf(struct vtcon_port *); 19904434c94SBryan Venteicher static void vtcon_port_requeue_buf(struct vtcon_port *, void *); 2006f744ddeSBryan Venteicher static int vtcon_port_populate(struct vtcon_port *); 2016f744ddeSBryan Venteicher static void vtcon_port_destroy(struct vtcon_port *); 20204434c94SBryan Venteicher static int vtcon_port_create(struct vtcon_softc *, int); 203a019e26cSBryan Venteicher static void vtcon_port_dev_alias(struct vtcon_port *, const char *, 204a019e26cSBryan Venteicher size_t); 20504434c94SBryan Venteicher static void vtcon_port_drain_bufs(struct virtqueue *); 20604434c94SBryan Venteicher static void vtcon_port_drain(struct vtcon_port *); 20704434c94SBryan Venteicher static void vtcon_port_teardown(struct vtcon_port *); 2086f744ddeSBryan Venteicher static void vtcon_port_change_size(struct vtcon_port *, uint16_t, 2096f744ddeSBryan Venteicher uint16_t); 21004434c94SBryan Venteicher static void vtcon_port_update_console_size(struct vtcon_softc *); 2116f744ddeSBryan Venteicher static void vtcon_port_enable_intr(struct vtcon_port *); 2126f744ddeSBryan Venteicher static void vtcon_port_disable_intr(struct vtcon_port *); 21304434c94SBryan Venteicher static void vtcon_port_in(struct vtcon_port *); 21404434c94SBryan Venteicher static void vtcon_port_intr(void *); 21504434c94SBryan Venteicher static void vtcon_port_out(struct vtcon_port *, void *, int); 21604434c94SBryan Venteicher static void vtcon_port_submit_event(struct vtcon_port *, uint16_t, 2176f744ddeSBryan Venteicher uint16_t); 2186f744ddeSBryan Venteicher 2196f744ddeSBryan Venteicher static int vtcon_tty_open(struct tty *); 2206f744ddeSBryan Venteicher static void vtcon_tty_close(struct tty *); 2216f744ddeSBryan Venteicher static void vtcon_tty_outwakeup(struct tty *); 2226f744ddeSBryan Venteicher static void vtcon_tty_free(void *); 2236f744ddeSBryan Venteicher 2246f744ddeSBryan Venteicher static void vtcon_get_console_size(struct vtcon_softc *, uint16_t *, 2256f744ddeSBryan Venteicher uint16_t *); 2266f744ddeSBryan Venteicher 2276f744ddeSBryan Venteicher static void vtcon_enable_interrupts(struct vtcon_softc *); 2286f744ddeSBryan Venteicher static void vtcon_disable_interrupts(struct vtcon_softc *); 2296f744ddeSBryan Venteicher 2306f744ddeSBryan Venteicher static int vtcon_pending_free; 2316f744ddeSBryan Venteicher 2326f744ddeSBryan Venteicher static struct ttydevsw vtcon_tty_class = { 2336f744ddeSBryan Venteicher .tsw_flags = 0, 2346f744ddeSBryan Venteicher .tsw_open = vtcon_tty_open, 2356f744ddeSBryan Venteicher .tsw_close = vtcon_tty_close, 2366f744ddeSBryan Venteicher .tsw_outwakeup = vtcon_tty_outwakeup, 2376f744ddeSBryan Venteicher .tsw_free = vtcon_tty_free, 2386f744ddeSBryan Venteicher }; 2396f744ddeSBryan Venteicher 2406f744ddeSBryan Venteicher static device_method_t vtcon_methods[] = { 2416f744ddeSBryan Venteicher /* Device methods. */ 2426f744ddeSBryan Venteicher DEVMETHOD(device_probe, vtcon_probe), 2436f744ddeSBryan Venteicher DEVMETHOD(device_attach, vtcon_attach), 2446f744ddeSBryan Venteicher DEVMETHOD(device_detach, vtcon_detach), 2456f744ddeSBryan Venteicher 2466f744ddeSBryan Venteicher /* VirtIO methods. */ 2476f744ddeSBryan Venteicher DEVMETHOD(virtio_config_change, vtcon_config_change), 2486f744ddeSBryan Venteicher 2496f744ddeSBryan Venteicher DEVMETHOD_END 2506f744ddeSBryan Venteicher }; 2516f744ddeSBryan Venteicher 2526f744ddeSBryan Venteicher static driver_t vtcon_driver = { 2536f744ddeSBryan Venteicher "vtcon", 2546f744ddeSBryan Venteicher vtcon_methods, 2556f744ddeSBryan Venteicher sizeof(struct vtcon_softc) 2566f744ddeSBryan Venteicher }; 2576f744ddeSBryan Venteicher static devclass_t vtcon_devclass; 2586f744ddeSBryan Venteicher 2596f744ddeSBryan Venteicher DRIVER_MODULE(virtio_console, virtio_pci, vtcon_driver, vtcon_devclass, 2606f744ddeSBryan Venteicher vtcon_modevent, 0); 2616f744ddeSBryan Venteicher MODULE_VERSION(virtio_console, 1); 2626f744ddeSBryan Venteicher MODULE_DEPEND(virtio_console, virtio, 1, 1, 1); 2636f744ddeSBryan Venteicher 264*0f6040f0SConrad Meyer VIRTIO_SIMPLE_PNPTABLE(virtio_console, VIRTIO_ID_CONSOLE, 265*0f6040f0SConrad Meyer "VirtIO Console Adapter"); 266*0f6040f0SConrad Meyer VIRTIO_SIMPLE_PNPINFO(virtio_pci, virtio_console); 267*0f6040f0SConrad Meyer 2686f744ddeSBryan Venteicher static int 2696f744ddeSBryan Venteicher vtcon_modevent(module_t mod, int type, void *unused) 2706f744ddeSBryan Venteicher { 2716f744ddeSBryan Venteicher int error; 2726f744ddeSBryan Venteicher 2736f744ddeSBryan Venteicher switch (type) { 2746f744ddeSBryan Venteicher case MOD_LOAD: 2756f744ddeSBryan Venteicher error = 0; 2766f744ddeSBryan Venteicher break; 2776f744ddeSBryan Venteicher case MOD_QUIESCE: 27804434c94SBryan Venteicher error = 0; 27904434c94SBryan Venteicher break; 2806f744ddeSBryan Venteicher case MOD_UNLOAD: 28104434c94SBryan Venteicher vtcon_drain_all(); 28204434c94SBryan Venteicher error = 0; 2836f744ddeSBryan Venteicher break; 2846f744ddeSBryan Venteicher case MOD_SHUTDOWN: 2856f744ddeSBryan Venteicher error = 0; 2866f744ddeSBryan Venteicher break; 2876f744ddeSBryan Venteicher default: 2886f744ddeSBryan Venteicher error = EOPNOTSUPP; 2896f744ddeSBryan Venteicher break; 2906f744ddeSBryan Venteicher } 2916f744ddeSBryan Venteicher 2926f744ddeSBryan Venteicher return (error); 2936f744ddeSBryan Venteicher } 2946f744ddeSBryan Venteicher 29504434c94SBryan Venteicher static void 29604434c94SBryan Venteicher vtcon_drain_all(void) 29704434c94SBryan Venteicher { 29804434c94SBryan Venteicher int first; 29904434c94SBryan Venteicher 30004434c94SBryan Venteicher for (first = 1; vtcon_pending_free != 0; first = 0) { 30104434c94SBryan Venteicher if (first != 0) { 30204434c94SBryan Venteicher printf("virtio_console: Waiting for all detached TTY " 30304434c94SBryan Venteicher "devices to have open fds closed.\n"); 30404434c94SBryan Venteicher } 30504434c94SBryan Venteicher pause("vtcondra", hz); 30604434c94SBryan Venteicher } 30704434c94SBryan Venteicher } 30804434c94SBryan Venteicher 3096f744ddeSBryan Venteicher static int 3106f744ddeSBryan Venteicher vtcon_probe(device_t dev) 3116f744ddeSBryan Venteicher { 312*0f6040f0SConrad Meyer return (VIRTIO_SIMPLE_PROBE(dev, virtio_console)); 3136f744ddeSBryan Venteicher } 3146f744ddeSBryan Venteicher 3156f744ddeSBryan Venteicher static int 3166f744ddeSBryan Venteicher vtcon_attach(device_t dev) 3176f744ddeSBryan Venteicher { 3186f744ddeSBryan Venteicher struct vtcon_softc *sc; 3196f744ddeSBryan Venteicher struct virtio_console_config concfg; 3206f744ddeSBryan Venteicher int error; 3216f744ddeSBryan Venteicher 3226f744ddeSBryan Venteicher sc = device_get_softc(dev); 3236f744ddeSBryan Venteicher sc->vtcon_dev = dev; 3246f744ddeSBryan Venteicher 32504434c94SBryan Venteicher mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF); 32604434c94SBryan Venteicher mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF); 3276f744ddeSBryan Venteicher 3286f744ddeSBryan Venteicher virtio_set_feature_desc(dev, vtcon_feature_desc); 32904434c94SBryan Venteicher vtcon_setup_features(sc); 3306f744ddeSBryan Venteicher 3316f744ddeSBryan Venteicher vtcon_read_config(sc, &concfg); 3326f744ddeSBryan Venteicher vtcon_determine_max_ports(sc, &concfg); 3336f744ddeSBryan Venteicher 33404434c94SBryan Venteicher error = vtcon_alloc_scports(sc); 33504434c94SBryan Venteicher if (error) { 33604434c94SBryan Venteicher device_printf(dev, "cannot allocate softc port structures\n"); 33704434c94SBryan Venteicher goto fail; 33804434c94SBryan Venteicher } 33904434c94SBryan Venteicher 3406f744ddeSBryan Venteicher error = vtcon_alloc_virtqueues(sc); 3416f744ddeSBryan Venteicher if (error) { 3426f744ddeSBryan Venteicher device_printf(dev, "cannot allocate virtqueues\n"); 3436f744ddeSBryan Venteicher goto fail; 3446f744ddeSBryan Venteicher } 3456f744ddeSBryan Venteicher 34604434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 34704434c94SBryan Venteicher TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc); 3486f744ddeSBryan Venteicher error = vtcon_ctrl_init(sc); 349b84b3efdSBryan Venteicher if (error) 350b84b3efdSBryan Venteicher goto fail; 351b84b3efdSBryan Venteicher } else { 35204434c94SBryan Venteicher error = vtcon_port_create(sc, 0); 3536f744ddeSBryan Venteicher if (error) 3546f744ddeSBryan Venteicher goto fail; 355b84b3efdSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_SIZE) 356b84b3efdSBryan Venteicher vtcon_port_update_console_size(sc); 357b84b3efdSBryan Venteicher } 3586f744ddeSBryan Venteicher 3596f744ddeSBryan Venteicher error = virtio_setup_intr(dev, INTR_TYPE_TTY); 3606f744ddeSBryan Venteicher if (error) { 3616f744ddeSBryan Venteicher device_printf(dev, "cannot setup virtqueue interrupts\n"); 3626f744ddeSBryan Venteicher goto fail; 3636f744ddeSBryan Venteicher } 3646f744ddeSBryan Venteicher 3656f744ddeSBryan Venteicher vtcon_enable_interrupts(sc); 3666f744ddeSBryan Venteicher 36704434c94SBryan Venteicher vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID, 3686f744ddeSBryan Venteicher VIRTIO_CONSOLE_DEVICE_READY, 1); 3696f744ddeSBryan Venteicher 3706f744ddeSBryan Venteicher fail: 3716f744ddeSBryan Venteicher if (error) 3726f744ddeSBryan Venteicher vtcon_detach(dev); 3736f744ddeSBryan Venteicher 3746f744ddeSBryan Venteicher return (error); 3756f744ddeSBryan Venteicher } 3766f744ddeSBryan Venteicher 3776f744ddeSBryan Venteicher static int 3786f744ddeSBryan Venteicher vtcon_detach(device_t dev) 3796f744ddeSBryan Venteicher { 3806f744ddeSBryan Venteicher struct vtcon_softc *sc; 3816f744ddeSBryan Venteicher 3826f744ddeSBryan Venteicher sc = device_get_softc(dev); 3836f744ddeSBryan Venteicher 3846f744ddeSBryan Venteicher VTCON_LOCK(sc); 3856f744ddeSBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_DETACHED; 3866f744ddeSBryan Venteicher if (device_is_attached(dev)) 3876f744ddeSBryan Venteicher vtcon_stop(sc); 3886f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 3896f744ddeSBryan Venteicher 39004434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 3916f744ddeSBryan Venteicher taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task); 3926f744ddeSBryan Venteicher vtcon_ctrl_deinit(sc); 39304434c94SBryan Venteicher } 3946f744ddeSBryan Venteicher 39504434c94SBryan Venteicher vtcon_destroy_ports(sc); 39604434c94SBryan Venteicher mtx_destroy(&sc->vtcon_mtx); 39704434c94SBryan Venteicher mtx_destroy(&sc->vtcon_ctrl_tx_mtx); 3986f744ddeSBryan Venteicher 3996f744ddeSBryan Venteicher return (0); 4006f744ddeSBryan Venteicher } 4016f744ddeSBryan Venteicher 4026f744ddeSBryan Venteicher static int 4036f744ddeSBryan Venteicher vtcon_config_change(device_t dev) 4046f744ddeSBryan Venteicher { 4056f744ddeSBryan Venteicher struct vtcon_softc *sc; 4066f744ddeSBryan Venteicher 4076f744ddeSBryan Venteicher sc = device_get_softc(dev); 4086f744ddeSBryan Venteicher 4096f744ddeSBryan Venteicher /* 41004434c94SBryan Venteicher * When the multiport feature is negotiated, all configuration 41104434c94SBryan Venteicher * changes are done through control virtqueue events. 4126f744ddeSBryan Venteicher */ 41304434c94SBryan Venteicher if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) { 41404434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_SIZE) 41504434c94SBryan Venteicher vtcon_port_update_console_size(sc); 4166f744ddeSBryan Venteicher } 4176f744ddeSBryan Venteicher 4186f744ddeSBryan Venteicher return (0); 4196f744ddeSBryan Venteicher } 4206f744ddeSBryan Venteicher 4216f744ddeSBryan Venteicher static void 4226f744ddeSBryan Venteicher vtcon_negotiate_features(struct vtcon_softc *sc) 4236f744ddeSBryan Venteicher { 4246f744ddeSBryan Venteicher device_t dev; 4256f744ddeSBryan Venteicher uint64_t features; 4266f744ddeSBryan Venteicher 4276f744ddeSBryan Venteicher dev = sc->vtcon_dev; 4286f744ddeSBryan Venteicher features = VTCON_FEATURES; 4296f744ddeSBryan Venteicher 4306f744ddeSBryan Venteicher sc->vtcon_features = virtio_negotiate_features(dev, features); 4316f744ddeSBryan Venteicher } 4326f744ddeSBryan Venteicher 43304434c94SBryan Venteicher static void 43404434c94SBryan Venteicher vtcon_setup_features(struct vtcon_softc *sc) 43504434c94SBryan Venteicher { 43604434c94SBryan Venteicher device_t dev; 43704434c94SBryan Venteicher 43804434c94SBryan Venteicher dev = sc->vtcon_dev; 43904434c94SBryan Venteicher 44004434c94SBryan Venteicher vtcon_negotiate_features(sc); 44104434c94SBryan Venteicher 44204434c94SBryan Venteicher if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE)) 44304434c94SBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_SIZE; 44404434c94SBryan Venteicher if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT)) 44504434c94SBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_MULTIPORT; 44604434c94SBryan Venteicher } 44704434c94SBryan Venteicher 4486f744ddeSBryan Venteicher #define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg) \ 4496f744ddeSBryan Venteicher if (virtio_with_feature(_dev, _feature)) { \ 4506f744ddeSBryan Venteicher virtio_read_device_config(_dev, \ 4516f744ddeSBryan Venteicher offsetof(struct virtio_console_config, _field), \ 4526f744ddeSBryan Venteicher &(_cfg)->_field, sizeof((_cfg)->_field)); \ 4536f744ddeSBryan Venteicher } 4546f744ddeSBryan Venteicher 4556f744ddeSBryan Venteicher static void 4566f744ddeSBryan Venteicher vtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg) 4576f744ddeSBryan Venteicher { 4586f744ddeSBryan Venteicher device_t dev; 4596f744ddeSBryan Venteicher 4606f744ddeSBryan Venteicher dev = sc->vtcon_dev; 4616f744ddeSBryan Venteicher 4626f744ddeSBryan Venteicher bzero(concfg, sizeof(struct virtio_console_config)); 4636f744ddeSBryan Venteicher 4646f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg); 4656f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg); 4666f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg); 4676f744ddeSBryan Venteicher } 4686f744ddeSBryan Venteicher 4696f744ddeSBryan Venteicher #undef VTCON_GET_CONFIG 4706f744ddeSBryan Venteicher 4716f744ddeSBryan Venteicher static int 47204434c94SBryan Venteicher vtcon_alloc_scports(struct vtcon_softc *sc) 47304434c94SBryan Venteicher { 47404434c94SBryan Venteicher struct vtcon_softc_port *scport; 475ac2fffa4SPedro F. Giffuni int max, i; 47604434c94SBryan Venteicher 47704434c94SBryan Venteicher max = sc->vtcon_max_ports; 47804434c94SBryan Venteicher 479ac2fffa4SPedro F. Giffuni sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max, 48004434c94SBryan Venteicher M_DEVBUF, M_NOWAIT | M_ZERO); 48104434c94SBryan Venteicher if (sc->vtcon_ports == NULL) 48204434c94SBryan Venteicher return (ENOMEM); 48304434c94SBryan Venteicher 48404434c94SBryan Venteicher for (i = 0; i < max; i++) { 48504434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 48604434c94SBryan Venteicher scport->vcsp_sc = sc; 48704434c94SBryan Venteicher } 48804434c94SBryan Venteicher 48904434c94SBryan Venteicher return (0); 49004434c94SBryan Venteicher } 49104434c94SBryan Venteicher 49204434c94SBryan Venteicher static int 4936f744ddeSBryan Venteicher vtcon_alloc_virtqueues(struct vtcon_softc *sc) 4946f744ddeSBryan Venteicher { 4956f744ddeSBryan Venteicher device_t dev; 4966f744ddeSBryan Venteicher struct vq_alloc_info *info; 49704434c94SBryan Venteicher struct vtcon_softc_port *scport; 498ac2fffa4SPedro F. Giffuni int i, idx, portidx, nvqs, error; 4996f744ddeSBryan Venteicher 5006f744ddeSBryan Venteicher dev = sc->vtcon_dev; 5016f744ddeSBryan Venteicher 5026f744ddeSBryan Venteicher nvqs = sc->vtcon_max_ports * 2; 5036f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 5046f744ddeSBryan Venteicher nvqs += 2; 5056f744ddeSBryan Venteicher 506ac2fffa4SPedro F. Giffuni info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT); 5076f744ddeSBryan Venteicher if (info == NULL) 5086f744ddeSBryan Venteicher return (ENOMEM); 5096f744ddeSBryan Venteicher 5106f744ddeSBryan Venteicher for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) { 5116f744ddeSBryan Venteicher 5126f744ddeSBryan Venteicher if (i == 1) { 5136f744ddeSBryan Venteicher /* The control virtqueues are after the first port. */ 5146f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx], 0, 51504434c94SBryan Venteicher vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq, 5166f744ddeSBryan Venteicher "%s-control rx", device_get_nameunit(dev)); 5176f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx+1], 0, 5186f744ddeSBryan Venteicher NULL, sc, &sc->vtcon_ctrl_txvq, 5196f744ddeSBryan Venteicher "%s-control tx", device_get_nameunit(dev)); 5206f744ddeSBryan Venteicher continue; 5216f744ddeSBryan Venteicher } 5226f744ddeSBryan Venteicher 52304434c94SBryan Venteicher scport = &sc->vtcon_ports[portidx]; 5246f744ddeSBryan Venteicher 52504434c94SBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr, 52604434c94SBryan Venteicher scport, &scport->vcsp_invq, "%s-port%d in", 52704434c94SBryan Venteicher device_get_nameunit(dev), i); 5286f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL, 52904434c94SBryan Venteicher NULL, &scport->vcsp_outvq, "%s-port%d out", 53004434c94SBryan Venteicher device_get_nameunit(dev), i); 5316f744ddeSBryan Venteicher 5326f744ddeSBryan Venteicher portidx++; 5336f744ddeSBryan Venteicher } 5346f744ddeSBryan Venteicher 5356f744ddeSBryan Venteicher error = virtio_alloc_virtqueues(dev, 0, nvqs, info); 5366f744ddeSBryan Venteicher free(info, M_TEMP); 5376f744ddeSBryan Venteicher 5386f744ddeSBryan Venteicher return (error); 5396f744ddeSBryan Venteicher } 5406f744ddeSBryan Venteicher 5416f744ddeSBryan Venteicher static void 5426f744ddeSBryan Venteicher vtcon_determine_max_ports(struct vtcon_softc *sc, 5436f744ddeSBryan Venteicher struct virtio_console_config *concfg) 5446f744ddeSBryan Venteicher { 5456f744ddeSBryan Venteicher 5466f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 5476f744ddeSBryan Venteicher sc->vtcon_max_ports = 5486f744ddeSBryan Venteicher min(concfg->max_nr_ports, VTCON_MAX_PORTS); 5496f744ddeSBryan Venteicher if (sc->vtcon_max_ports == 0) 5506f744ddeSBryan Venteicher sc->vtcon_max_ports = 1; 5516f744ddeSBryan Venteicher } else 5526f744ddeSBryan Venteicher sc->vtcon_max_ports = 1; 5536f744ddeSBryan Venteicher } 5546f744ddeSBryan Venteicher 5556f744ddeSBryan Venteicher static void 55604434c94SBryan Venteicher vtcon_destroy_ports(struct vtcon_softc *sc) 5576f744ddeSBryan Venteicher { 55804434c94SBryan Venteicher struct vtcon_softc_port *scport; 55904434c94SBryan Venteicher struct vtcon_port *port; 56004434c94SBryan Venteicher struct virtqueue *vq; 56104434c94SBryan Venteicher int i; 5626f744ddeSBryan Venteicher 56304434c94SBryan Venteicher if (sc->vtcon_ports == NULL) 56404434c94SBryan Venteicher return; 56504434c94SBryan Venteicher 56604434c94SBryan Venteicher VTCON_LOCK(sc); 56704434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 56804434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 56904434c94SBryan Venteicher 57004434c94SBryan Venteicher port = scport->vcsp_port; 57104434c94SBryan Venteicher if (port != NULL) { 57204434c94SBryan Venteicher scport->vcsp_port = NULL; 57304434c94SBryan Venteicher VTCON_PORT_LOCK(port); 57404434c94SBryan Venteicher VTCON_UNLOCK(sc); 57504434c94SBryan Venteicher vtcon_port_teardown(port); 57604434c94SBryan Venteicher VTCON_LOCK(sc); 5776f744ddeSBryan Venteicher } 5786f744ddeSBryan Venteicher 57904434c94SBryan Venteicher vq = scport->vcsp_invq; 58004434c94SBryan Venteicher if (vq != NULL) 58104434c94SBryan Venteicher vtcon_port_drain_bufs(vq); 5826f744ddeSBryan Venteicher } 58304434c94SBryan Venteicher VTCON_UNLOCK(sc); 58404434c94SBryan Venteicher 58504434c94SBryan Venteicher free(sc->vtcon_ports, M_DEVBUF); 58604434c94SBryan Venteicher sc->vtcon_ports = NULL; 5876f744ddeSBryan Venteicher } 5886f744ddeSBryan Venteicher 5896f744ddeSBryan Venteicher static void 5906f744ddeSBryan Venteicher vtcon_stop(struct vtcon_softc *sc) 5916f744ddeSBryan Venteicher { 5926f744ddeSBryan Venteicher 5936f744ddeSBryan Venteicher vtcon_disable_interrupts(sc); 5946f744ddeSBryan Venteicher virtio_stop(sc->vtcon_dev); 5956f744ddeSBryan Venteicher } 5966f744ddeSBryan Venteicher 5976f744ddeSBryan Venteicher static int 59804434c94SBryan Venteicher vtcon_ctrl_event_enqueue(struct vtcon_softc *sc, 5996f744ddeSBryan Venteicher struct virtio_console_control *control) 6006f744ddeSBryan Venteicher { 60104434c94SBryan Venteicher struct sglist_seg segs[2]; 6026f744ddeSBryan Venteicher struct sglist sg; 6036f744ddeSBryan Venteicher struct virtqueue *vq; 60404434c94SBryan Venteicher int error; 6056f744ddeSBryan Venteicher 6066f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 6076f744ddeSBryan Venteicher 60804434c94SBryan Venteicher sglist_init(&sg, 2, segs); 609a019e26cSBryan Venteicher error = sglist_append(&sg, control, VTCON_CTRL_BUFSZ); 61004434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding control to sglist", 61104434c94SBryan Venteicher __func__, error)); 6126f744ddeSBryan Venteicher 61304434c94SBryan Venteicher return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg)); 6146f744ddeSBryan Venteicher } 6156f744ddeSBryan Venteicher 6166f744ddeSBryan Venteicher static int 61704434c94SBryan Venteicher vtcon_ctrl_event_create(struct vtcon_softc *sc) 6186f744ddeSBryan Venteicher { 6196f744ddeSBryan Venteicher struct virtio_console_control *control; 6206f744ddeSBryan Venteicher int error; 6216f744ddeSBryan Venteicher 622a019e26cSBryan Venteicher control = malloc(VTCON_CTRL_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT); 6236f744ddeSBryan Venteicher if (control == NULL) 6246f744ddeSBryan Venteicher return (ENOMEM); 6256f744ddeSBryan Venteicher 62604434c94SBryan Venteicher error = vtcon_ctrl_event_enqueue(sc, control); 6276f744ddeSBryan Venteicher if (error) 6286f744ddeSBryan Venteicher free(control, M_DEVBUF); 6296f744ddeSBryan Venteicher 6306f744ddeSBryan Venteicher return (error); 6316f744ddeSBryan Venteicher } 6326f744ddeSBryan Venteicher 6336f744ddeSBryan Venteicher static void 63404434c94SBryan Venteicher vtcon_ctrl_event_requeue(struct vtcon_softc *sc, 6356f744ddeSBryan Venteicher struct virtio_console_control *control) 6366f744ddeSBryan Venteicher { 6376f744ddeSBryan Venteicher int error; 6386f744ddeSBryan Venteicher 639a019e26cSBryan Venteicher bzero(control, VTCON_CTRL_BUFSZ); 6406f744ddeSBryan Venteicher 64104434c94SBryan Venteicher error = vtcon_ctrl_event_enqueue(sc, control); 6426f744ddeSBryan Venteicher KASSERT(error == 0, 6436f744ddeSBryan Venteicher ("%s: cannot requeue control buffer %d", __func__, error)); 6446f744ddeSBryan Venteicher } 6456f744ddeSBryan Venteicher 6466f744ddeSBryan Venteicher static int 64704434c94SBryan Venteicher vtcon_ctrl_event_populate(struct vtcon_softc *sc) 6486f744ddeSBryan Venteicher { 6496f744ddeSBryan Venteicher struct virtqueue *vq; 6506f744ddeSBryan Venteicher int nbufs, error; 6516f744ddeSBryan Venteicher 6526f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 6536f744ddeSBryan Venteicher error = ENOSPC; 6546f744ddeSBryan Venteicher 6556f744ddeSBryan Venteicher for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 65604434c94SBryan Venteicher error = vtcon_ctrl_event_create(sc); 6576f744ddeSBryan Venteicher if (error) 6586f744ddeSBryan Venteicher break; 6596f744ddeSBryan Venteicher } 6606f744ddeSBryan Venteicher 6616f744ddeSBryan Venteicher if (nbufs > 0) { 6626f744ddeSBryan Venteicher virtqueue_notify(vq); 6636f744ddeSBryan Venteicher error = 0; 6646f744ddeSBryan Venteicher } 6656f744ddeSBryan Venteicher 6666f744ddeSBryan Venteicher return (error); 6676f744ddeSBryan Venteicher } 6686f744ddeSBryan Venteicher 6696f744ddeSBryan Venteicher static void 67004434c94SBryan Venteicher vtcon_ctrl_event_drain(struct vtcon_softc *sc) 6716f744ddeSBryan Venteicher { 6726f744ddeSBryan Venteicher struct virtio_console_control *control; 6736f744ddeSBryan Venteicher struct virtqueue *vq; 6746f744ddeSBryan Venteicher int last; 6756f744ddeSBryan Venteicher 6766f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 6776f744ddeSBryan Venteicher last = 0; 6786f744ddeSBryan Venteicher 6796f744ddeSBryan Venteicher if (vq == NULL) 6806f744ddeSBryan Venteicher return; 6816f744ddeSBryan Venteicher 68204434c94SBryan Venteicher VTCON_LOCK(sc); 6836f744ddeSBryan Venteicher while ((control = virtqueue_drain(vq, &last)) != NULL) 6846f744ddeSBryan Venteicher free(control, M_DEVBUF); 68504434c94SBryan Venteicher VTCON_UNLOCK(sc); 68604434c94SBryan Venteicher } 68704434c94SBryan Venteicher 68804434c94SBryan Venteicher static int 68904434c94SBryan Venteicher vtcon_ctrl_init(struct vtcon_softc *sc) 69004434c94SBryan Venteicher { 69104434c94SBryan Venteicher int error; 69204434c94SBryan Venteicher 69304434c94SBryan Venteicher error = vtcon_ctrl_event_populate(sc); 69404434c94SBryan Venteicher 69504434c94SBryan Venteicher return (error); 6966f744ddeSBryan Venteicher } 6976f744ddeSBryan Venteicher 6986f744ddeSBryan Venteicher static void 6996f744ddeSBryan Venteicher vtcon_ctrl_deinit(struct vtcon_softc *sc) 7006f744ddeSBryan Venteicher { 7016f744ddeSBryan Venteicher 70204434c94SBryan Venteicher vtcon_ctrl_event_drain(sc); 7036f744ddeSBryan Venteicher } 7046f744ddeSBryan Venteicher 7056f744ddeSBryan Venteicher static void 7066f744ddeSBryan Venteicher vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id) 7076f744ddeSBryan Venteicher { 7086f744ddeSBryan Venteicher device_t dev; 7096f744ddeSBryan Venteicher int error; 7106f744ddeSBryan Venteicher 7116f744ddeSBryan Venteicher dev = sc->vtcon_dev; 7126f744ddeSBryan Venteicher 71304434c94SBryan Venteicher /* This single thread only way for ports to be created. */ 71404434c94SBryan Venteicher if (sc->vtcon_ports[id].vcsp_port != NULL) { 7156f744ddeSBryan Venteicher device_printf(dev, "%s: adding port %d, but already exists\n", 7166f744ddeSBryan Venteicher __func__, id); 7176f744ddeSBryan Venteicher return; 7186f744ddeSBryan Venteicher } 7196f744ddeSBryan Venteicher 72004434c94SBryan Venteicher error = vtcon_port_create(sc, id); 7216f744ddeSBryan Venteicher if (error) { 7226f744ddeSBryan Venteicher device_printf(dev, "%s: cannot create port %d: %d\n", 7236f744ddeSBryan Venteicher __func__, id, error); 724b84b3efdSBryan Venteicher vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0); 7256f744ddeSBryan Venteicher return; 7266f744ddeSBryan Venteicher } 7276f744ddeSBryan Venteicher } 7286f744ddeSBryan Venteicher 7296f744ddeSBryan Venteicher static void 7306f744ddeSBryan Venteicher vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id) 7316f744ddeSBryan Venteicher { 7326f744ddeSBryan Venteicher device_t dev; 73304434c94SBryan Venteicher struct vtcon_softc_port *scport; 7346f744ddeSBryan Venteicher struct vtcon_port *port; 7356f744ddeSBryan Venteicher 7366f744ddeSBryan Venteicher dev = sc->vtcon_dev; 73704434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 7386f744ddeSBryan Venteicher 73904434c94SBryan Venteicher VTCON_LOCK(sc); 74004434c94SBryan Venteicher port = scport->vcsp_port; 7416f744ddeSBryan Venteicher if (port == NULL) { 74204434c94SBryan Venteicher VTCON_UNLOCK(sc); 7436f744ddeSBryan Venteicher device_printf(dev, "%s: remove port %d, but does not exist\n", 7446f744ddeSBryan Venteicher __func__, id); 7456f744ddeSBryan Venteicher return; 7466f744ddeSBryan Venteicher } 7476f744ddeSBryan Venteicher 74804434c94SBryan Venteicher scport->vcsp_port = NULL; 74904434c94SBryan Venteicher VTCON_PORT_LOCK(port); 75004434c94SBryan Venteicher VTCON_UNLOCK(sc); 75104434c94SBryan Venteicher vtcon_port_teardown(port); 7526f744ddeSBryan Venteicher } 7536f744ddeSBryan Venteicher 7546f744ddeSBryan Venteicher static void 7556f744ddeSBryan Venteicher vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id) 7566f744ddeSBryan Venteicher { 757b84b3efdSBryan Venteicher device_t dev; 758b84b3efdSBryan Venteicher struct vtcon_softc_port *scport; 759b84b3efdSBryan Venteicher struct vtcon_port *port; 7606f744ddeSBryan Venteicher 761b84b3efdSBryan Venteicher dev = sc->vtcon_dev; 762b84b3efdSBryan Venteicher scport = &sc->vtcon_ports[id]; 763b84b3efdSBryan Venteicher 764b84b3efdSBryan Venteicher VTCON_LOCK(sc); 765b84b3efdSBryan Venteicher port = scport->vcsp_port; 766b84b3efdSBryan Venteicher if (port == NULL) { 767b84b3efdSBryan Venteicher VTCON_UNLOCK(sc); 768b84b3efdSBryan Venteicher device_printf(dev, "%s: console port %d, but does not exist\n", 76904434c94SBryan Venteicher __func__, id); 770b84b3efdSBryan Venteicher return; 771b84b3efdSBryan Venteicher } 772b84b3efdSBryan Venteicher 773b84b3efdSBryan Venteicher VTCON_PORT_LOCK(port); 774b84b3efdSBryan Venteicher VTCON_UNLOCK(sc); 775b84b3efdSBryan Venteicher port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE; 776b84b3efdSBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 777b84b3efdSBryan Venteicher VTCON_PORT_UNLOCK(port); 7786f744ddeSBryan Venteicher } 7796f744ddeSBryan Venteicher 7806f744ddeSBryan Venteicher static void 7816f744ddeSBryan Venteicher vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id) 7826f744ddeSBryan Venteicher { 7836f744ddeSBryan Venteicher device_t dev; 78404434c94SBryan Venteicher struct vtcon_softc_port *scport; 7856f744ddeSBryan Venteicher struct vtcon_port *port; 7866f744ddeSBryan Venteicher 7876f744ddeSBryan Venteicher dev = sc->vtcon_dev; 78804434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 7896f744ddeSBryan Venteicher 79004434c94SBryan Venteicher VTCON_LOCK(sc); 79104434c94SBryan Venteicher port = scport->vcsp_port; 7926f744ddeSBryan Venteicher if (port == NULL) { 79304434c94SBryan Venteicher VTCON_UNLOCK(sc); 7946f744ddeSBryan Venteicher device_printf(dev, "%s: open port %d, but does not exist\n", 7956f744ddeSBryan Venteicher __func__, id); 7966f744ddeSBryan Venteicher return; 7976f744ddeSBryan Venteicher } 7986f744ddeSBryan Venteicher 79904434c94SBryan Venteicher VTCON_PORT_LOCK(port); 80004434c94SBryan Venteicher VTCON_UNLOCK(sc); 8016f744ddeSBryan Venteicher vtcon_port_enable_intr(port); 80204434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 8036f744ddeSBryan Venteicher } 8046f744ddeSBryan Venteicher 8056f744ddeSBryan Venteicher static void 806cb981343SJakub Wojciech Klama vtcon_ctrl_port_name_event(struct vtcon_softc *sc, int id, const char *name, 807cb981343SJakub Wojciech Klama size_t len) 808cb981343SJakub Wojciech Klama { 809cb981343SJakub Wojciech Klama device_t dev; 810cb981343SJakub Wojciech Klama struct vtcon_softc_port *scport; 811cb981343SJakub Wojciech Klama struct vtcon_port *port; 812cb981343SJakub Wojciech Klama 813cb981343SJakub Wojciech Klama dev = sc->vtcon_dev; 814cb981343SJakub Wojciech Klama scport = &sc->vtcon_ports[id]; 815cb981343SJakub Wojciech Klama 816a019e26cSBryan Venteicher /* 817a019e26cSBryan Venteicher * The VirtIO specification says the NUL terminator is not included in 818a019e26cSBryan Venteicher * the length, but QEMU includes it. Adjust the length if needed. 819a019e26cSBryan Venteicher */ 820a019e26cSBryan Venteicher if (name == NULL || len == 0) 821a019e26cSBryan Venteicher return; 822a019e26cSBryan Venteicher if (name[len - 1] == '\0') { 823a019e26cSBryan Venteicher len--; 824a019e26cSBryan Venteicher if (len == 0) 825a019e26cSBryan Venteicher return; 826a019e26cSBryan Venteicher } 827a019e26cSBryan Venteicher 828a019e26cSBryan Venteicher VTCON_LOCK(sc); 829cb981343SJakub Wojciech Klama port = scport->vcsp_port; 830cb981343SJakub Wojciech Klama if (port == NULL) { 831a019e26cSBryan Venteicher VTCON_UNLOCK(sc); 832cb981343SJakub Wojciech Klama device_printf(dev, "%s: name port %d, but does not exist\n", 833cb981343SJakub Wojciech Klama __func__, id); 834cb981343SJakub Wojciech Klama return; 835cb981343SJakub Wojciech Klama } 836cb981343SJakub Wojciech Klama 837a019e26cSBryan Venteicher VTCON_PORT_LOCK(port); 838a019e26cSBryan Venteicher VTCON_UNLOCK(sc); 839a019e26cSBryan Venteicher vtcon_port_dev_alias(port, name, len); 840a019e26cSBryan Venteicher VTCON_PORT_UNLOCK(port); 841cb981343SJakub Wojciech Klama } 842cb981343SJakub Wojciech Klama 843cb981343SJakub Wojciech Klama static void 84404434c94SBryan Venteicher vtcon_ctrl_process_event(struct vtcon_softc *sc, 845a019e26cSBryan Venteicher struct virtio_console_control *control, void *data, size_t data_len) 8466f744ddeSBryan Venteicher { 8476f744ddeSBryan Venteicher device_t dev; 8486f744ddeSBryan Venteicher int id; 8496f744ddeSBryan Venteicher 8506f744ddeSBryan Venteicher dev = sc->vtcon_dev; 8516f744ddeSBryan Venteicher id = control->id; 8526f744ddeSBryan Venteicher 8536f744ddeSBryan Venteicher if (id < 0 || id >= sc->vtcon_max_ports) { 8546f744ddeSBryan Venteicher device_printf(dev, "%s: invalid port ID %d\n", __func__, id); 8556f744ddeSBryan Venteicher return; 8566f744ddeSBryan Venteicher } 8576f744ddeSBryan Venteicher 8586f744ddeSBryan Venteicher switch (control->event) { 8596f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_ADD: 8606f744ddeSBryan Venteicher vtcon_ctrl_port_add_event(sc, id); 8616f744ddeSBryan Venteicher break; 8626f744ddeSBryan Venteicher 8636f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_REMOVE: 8646f744ddeSBryan Venteicher vtcon_ctrl_port_remove_event(sc, id); 8656f744ddeSBryan Venteicher break; 8666f744ddeSBryan Venteicher 8676f744ddeSBryan Venteicher case VIRTIO_CONSOLE_CONSOLE_PORT: 8686f744ddeSBryan Venteicher vtcon_ctrl_port_console_event(sc, id); 8696f744ddeSBryan Venteicher break; 8706f744ddeSBryan Venteicher 8716f744ddeSBryan Venteicher case VIRTIO_CONSOLE_RESIZE: 8726f744ddeSBryan Venteicher break; 8736f744ddeSBryan Venteicher 8746f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_OPEN: 8756f744ddeSBryan Venteicher vtcon_ctrl_port_open_event(sc, id); 8766f744ddeSBryan Venteicher break; 8776f744ddeSBryan Venteicher 8786f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_NAME: 879a019e26cSBryan Venteicher vtcon_ctrl_port_name_event(sc, id, (const char *)data, data_len); 8806f744ddeSBryan Venteicher break; 8816f744ddeSBryan Venteicher } 8826f744ddeSBryan Venteicher } 8836f744ddeSBryan Venteicher 8846f744ddeSBryan Venteicher static void 8856f744ddeSBryan Venteicher vtcon_ctrl_task_cb(void *xsc, int pending) 8866f744ddeSBryan Venteicher { 8876f744ddeSBryan Venteicher struct vtcon_softc *sc; 8886f744ddeSBryan Venteicher struct virtqueue *vq; 8896f744ddeSBryan Venteicher struct virtio_console_control *control; 890a019e26cSBryan Venteicher void *data; 891a019e26cSBryan Venteicher size_t data_len; 89204434c94SBryan Venteicher int detached; 893cb981343SJakub Wojciech Klama uint32_t len; 8946f744ddeSBryan Venteicher 8956f744ddeSBryan Venteicher sc = xsc; 8966f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 8976f744ddeSBryan Venteicher 8986f744ddeSBryan Venteicher VTCON_LOCK(sc); 89904434c94SBryan Venteicher 90004434c94SBryan Venteicher while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) { 901cb981343SJakub Wojciech Klama control = virtqueue_dequeue(vq, &len); 9026f744ddeSBryan Venteicher if (control == NULL) 9036f744ddeSBryan Venteicher break; 9046f744ddeSBryan Venteicher 905a019e26cSBryan Venteicher if (len > sizeof(struct virtio_console_control)) { 906a019e26cSBryan Venteicher data = (void *) &control[1]; 907a019e26cSBryan Venteicher data_len = len - sizeof(struct virtio_console_control); 908a019e26cSBryan Venteicher } else { 909a019e26cSBryan Venteicher data = NULL; 910a019e26cSBryan Venteicher data_len = 0; 911cb981343SJakub Wojciech Klama } 912cb981343SJakub Wojciech Klama 9136f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 914a019e26cSBryan Venteicher vtcon_ctrl_process_event(sc, control, data, data_len); 9156f744ddeSBryan Venteicher VTCON_LOCK(sc); 91604434c94SBryan Venteicher vtcon_ctrl_event_requeue(sc, control); 9176f744ddeSBryan Venteicher } 9186f744ddeSBryan Venteicher 91904434c94SBryan Venteicher if (!detached) { 92004434c94SBryan Venteicher virtqueue_notify(vq); 9216f744ddeSBryan Venteicher if (virtqueue_enable_intr(vq) != 0) 92204434c94SBryan Venteicher taskqueue_enqueue(taskqueue_thread, 92304434c94SBryan Venteicher &sc->vtcon_ctrl_task); 92404434c94SBryan Venteicher } 92504434c94SBryan Venteicher 92604434c94SBryan Venteicher VTCON_UNLOCK(sc); 92704434c94SBryan Venteicher } 92804434c94SBryan Venteicher 92904434c94SBryan Venteicher static void 93004434c94SBryan Venteicher vtcon_ctrl_event_intr(void *xsc) 93104434c94SBryan Venteicher { 93204434c94SBryan Venteicher struct vtcon_softc *sc; 93304434c94SBryan Venteicher 93404434c94SBryan Venteicher sc = xsc; 93504434c94SBryan Venteicher 93604434c94SBryan Venteicher /* 93704434c94SBryan Venteicher * Only some events require us to potentially block, but it 93804434c94SBryan Venteicher * easier to just defer all event handling to the taskqueue. 93904434c94SBryan Venteicher */ 9406f744ddeSBryan Venteicher taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task); 9416f744ddeSBryan Venteicher } 9426f744ddeSBryan Venteicher 94304434c94SBryan Venteicher static void 94404434c94SBryan Venteicher vtcon_ctrl_poll(struct vtcon_softc *sc, 94504434c94SBryan Venteicher struct virtio_console_control *control) 9466f744ddeSBryan Venteicher { 94704434c94SBryan Venteicher struct sglist_seg segs[2]; 94804434c94SBryan Venteicher struct sglist sg; 94904434c94SBryan Venteicher struct virtqueue *vq; 95004434c94SBryan Venteicher int error; 95104434c94SBryan Venteicher 95204434c94SBryan Venteicher vq = sc->vtcon_ctrl_txvq; 95304434c94SBryan Venteicher 95404434c94SBryan Venteicher sglist_init(&sg, 2, segs); 95504434c94SBryan Venteicher error = sglist_append(&sg, control, 95604434c94SBryan Venteicher sizeof(struct virtio_console_control)); 95704434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding control to sglist", 95804434c94SBryan Venteicher __func__, error)); 95904434c94SBryan Venteicher 96004434c94SBryan Venteicher /* 96104434c94SBryan Venteicher * We cannot use the softc lock to serialize access to this 96204434c94SBryan Venteicher * virtqueue since this is called from the tty layer with the 96304434c94SBryan Venteicher * port lock held. Acquiring the softc would violate our lock 96404434c94SBryan Venteicher * ordering. 96504434c94SBryan Venteicher */ 96604434c94SBryan Venteicher VTCON_CTRL_TX_LOCK(sc); 96704434c94SBryan Venteicher KASSERT(virtqueue_empty(vq), 96804434c94SBryan Venteicher ("%s: virtqueue is not emtpy", __func__)); 96904434c94SBryan Venteicher error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0); 97004434c94SBryan Venteicher if (error == 0) { 97104434c94SBryan Venteicher virtqueue_notify(vq); 97204434c94SBryan Venteicher virtqueue_poll(vq, NULL); 97304434c94SBryan Venteicher } 97404434c94SBryan Venteicher VTCON_CTRL_TX_UNLOCK(sc); 97504434c94SBryan Venteicher } 97604434c94SBryan Venteicher 97704434c94SBryan Venteicher static void 97804434c94SBryan Venteicher vtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid, 97904434c94SBryan Venteicher uint16_t event, uint16_t value) 98004434c94SBryan Venteicher { 98104434c94SBryan Venteicher struct virtio_console_control control; 98204434c94SBryan Venteicher 98304434c94SBryan Venteicher if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) 98404434c94SBryan Venteicher return; 98504434c94SBryan Venteicher 98604434c94SBryan Venteicher control.id = portid; 98704434c94SBryan Venteicher control.event = event; 98804434c94SBryan Venteicher control.value = value; 98904434c94SBryan Venteicher 99004434c94SBryan Venteicher vtcon_ctrl_poll(sc, &control); 99104434c94SBryan Venteicher } 99204434c94SBryan Venteicher 99304434c94SBryan Venteicher static int 99404434c94SBryan Venteicher vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len) 99504434c94SBryan Venteicher { 99604434c94SBryan Venteicher struct sglist_seg segs[2]; 9976f744ddeSBryan Venteicher struct sglist sg; 9986f744ddeSBryan Venteicher struct virtqueue *vq; 9996f744ddeSBryan Venteicher int error; 10006f744ddeSBryan Venteicher 10016f744ddeSBryan Venteicher vq = port->vtcport_invq; 10026f744ddeSBryan Venteicher 100304434c94SBryan Venteicher sglist_init(&sg, 2, segs); 10046f744ddeSBryan Venteicher error = sglist_append(&sg, buf, len); 100504434c94SBryan Venteicher KASSERT(error == 0, 10066f744ddeSBryan Venteicher ("%s: error %d adding buffer to sglist", __func__, error)); 10076f744ddeSBryan Venteicher 100804434c94SBryan Venteicher error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg); 100904434c94SBryan Venteicher 101004434c94SBryan Venteicher return (error); 10116f744ddeSBryan Venteicher } 10126f744ddeSBryan Venteicher 10136f744ddeSBryan Venteicher static int 101404434c94SBryan Venteicher vtcon_port_create_buf(struct vtcon_port *port) 10156f744ddeSBryan Venteicher { 10166f744ddeSBryan Venteicher void *buf; 10176f744ddeSBryan Venteicher int error; 10186f744ddeSBryan Venteicher 10196f744ddeSBryan Venteicher buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT); 10206f744ddeSBryan Venteicher if (buf == NULL) 10216f744ddeSBryan Venteicher return (ENOMEM); 10226f744ddeSBryan Venteicher 102304434c94SBryan Venteicher error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 10246f744ddeSBryan Venteicher if (error) 10256f744ddeSBryan Venteicher free(buf, M_DEVBUF); 10266f744ddeSBryan Venteicher 10276f744ddeSBryan Venteicher return (error); 10286f744ddeSBryan Venteicher } 10296f744ddeSBryan Venteicher 10306f744ddeSBryan Venteicher static void 103104434c94SBryan Venteicher vtcon_port_requeue_buf(struct vtcon_port *port, void *buf) 10326f744ddeSBryan Venteicher { 103304434c94SBryan Venteicher int error; 10346f744ddeSBryan Venteicher 103504434c94SBryan Venteicher error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 10366f744ddeSBryan Venteicher KASSERT(error == 0, 10376f744ddeSBryan Venteicher ("%s: cannot requeue input buffer %d", __func__, error)); 10386f744ddeSBryan Venteicher } 10396f744ddeSBryan Venteicher 10406f744ddeSBryan Venteicher static int 10416f744ddeSBryan Venteicher vtcon_port_populate(struct vtcon_port *port) 10426f744ddeSBryan Venteicher { 10436f744ddeSBryan Venteicher struct virtqueue *vq; 10446f744ddeSBryan Venteicher int nbufs, error; 10456f744ddeSBryan Venteicher 10466f744ddeSBryan Venteicher vq = port->vtcport_invq; 10476f744ddeSBryan Venteicher error = ENOSPC; 10486f744ddeSBryan Venteicher 10496f744ddeSBryan Venteicher for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 105004434c94SBryan Venteicher error = vtcon_port_create_buf(port); 10516f744ddeSBryan Venteicher if (error) 10526f744ddeSBryan Venteicher break; 10536f744ddeSBryan Venteicher } 10546f744ddeSBryan Venteicher 10556f744ddeSBryan Venteicher if (nbufs > 0) { 10566f744ddeSBryan Venteicher virtqueue_notify(vq); 10576f744ddeSBryan Venteicher error = 0; 10586f744ddeSBryan Venteicher } 10596f744ddeSBryan Venteicher 10606f744ddeSBryan Venteicher return (error); 10616f744ddeSBryan Venteicher } 10626f744ddeSBryan Venteicher 10636f744ddeSBryan Venteicher static void 10646f744ddeSBryan Venteicher vtcon_port_destroy(struct vtcon_port *port) 10656f744ddeSBryan Venteicher { 10666f744ddeSBryan Venteicher 10676f744ddeSBryan Venteicher port->vtcport_sc = NULL; 106804434c94SBryan Venteicher port->vtcport_scport = NULL; 106904434c94SBryan Venteicher port->vtcport_invq = NULL; 107004434c94SBryan Venteicher port->vtcport_outvq = NULL; 10716f744ddeSBryan Venteicher port->vtcport_id = -1; 107204434c94SBryan Venteicher mtx_destroy(&port->vtcport_mtx); 10736f744ddeSBryan Venteicher free(port, M_DEVBUF); 10746f744ddeSBryan Venteicher } 10756f744ddeSBryan Venteicher 10766f744ddeSBryan Venteicher static int 107704434c94SBryan Venteicher vtcon_port_init_vqs(struct vtcon_port *port) 107804434c94SBryan Venteicher { 107904434c94SBryan Venteicher struct vtcon_softc_port *scport; 108004434c94SBryan Venteicher int error; 108104434c94SBryan Venteicher 108204434c94SBryan Venteicher scport = port->vtcport_scport; 108304434c94SBryan Venteicher 108404434c94SBryan Venteicher port->vtcport_invq = scport->vcsp_invq; 108504434c94SBryan Venteicher port->vtcport_outvq = scport->vcsp_outvq; 108604434c94SBryan Venteicher 108704434c94SBryan Venteicher /* 108804434c94SBryan Venteicher * Free any data left over from when this virtqueue was in use by a 108904434c94SBryan Venteicher * prior port. We have not yet notified the host that the port is 109004434c94SBryan Venteicher * ready, so assume nothing in the virtqueue can be for us. 109104434c94SBryan Venteicher */ 109204434c94SBryan Venteicher vtcon_port_drain(port); 109304434c94SBryan Venteicher 109404434c94SBryan Venteicher KASSERT(virtqueue_empty(port->vtcport_invq), 109504434c94SBryan Venteicher ("%s: in virtqueue is not empty", __func__)); 109604434c94SBryan Venteicher KASSERT(virtqueue_empty(port->vtcport_outvq), 109704434c94SBryan Venteicher ("%s: out virtqueue is not empty", __func__)); 109804434c94SBryan Venteicher 109904434c94SBryan Venteicher error = vtcon_port_populate(port); 110004434c94SBryan Venteicher if (error) 110104434c94SBryan Venteicher return (error); 110204434c94SBryan Venteicher 110304434c94SBryan Venteicher return (0); 110404434c94SBryan Venteicher } 110504434c94SBryan Venteicher 110604434c94SBryan Venteicher static int 110704434c94SBryan Venteicher vtcon_port_create(struct vtcon_softc *sc, int id) 11086f744ddeSBryan Venteicher { 11096f744ddeSBryan Venteicher device_t dev; 111004434c94SBryan Venteicher struct vtcon_softc_port *scport; 11116f744ddeSBryan Venteicher struct vtcon_port *port; 11126f744ddeSBryan Venteicher int error; 11136f744ddeSBryan Venteicher 11146f744ddeSBryan Venteicher dev = sc->vtcon_dev; 111504434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 11166f744ddeSBryan Venteicher 111704434c94SBryan Venteicher VTCON_ASSERT_VALID_PORTID(sc, id); 111804434c94SBryan Venteicher MPASS(scport->vcsp_port == NULL); 11196f744ddeSBryan Venteicher 11206f744ddeSBryan Venteicher port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO); 11216f744ddeSBryan Venteicher if (port == NULL) 11226f744ddeSBryan Venteicher return (ENOMEM); 11236f744ddeSBryan Venteicher 11246f744ddeSBryan Venteicher port->vtcport_sc = sc; 112504434c94SBryan Venteicher port->vtcport_scport = scport; 11266f744ddeSBryan Venteicher port->vtcport_id = id; 112704434c94SBryan Venteicher mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF); 11286f744ddeSBryan Venteicher port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port, 11296f744ddeSBryan Venteicher &port->vtcport_mtx); 11306f744ddeSBryan Venteicher 113104434c94SBryan Venteicher error = vtcon_port_init_vqs(port); 11326f744ddeSBryan Venteicher if (error) { 113304434c94SBryan Venteicher VTCON_PORT_LOCK(port); 113404434c94SBryan Venteicher vtcon_port_teardown(port); 11356f744ddeSBryan Venteicher return (error); 11366f744ddeSBryan Venteicher } 11376f744ddeSBryan Venteicher 11386f744ddeSBryan Venteicher VTCON_LOCK(sc); 113904434c94SBryan Venteicher VTCON_PORT_LOCK(port); 114004434c94SBryan Venteicher scport->vcsp_port = port; 114146822c48SBryan Venteicher vtcon_port_enable_intr(port); 114204434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1); 114304434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 11446f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 11456f744ddeSBryan Venteicher 114646822c48SBryan Venteicher tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX, 114746822c48SBryan Venteicher device_get_unit(dev), id); 114846822c48SBryan Venteicher 11496f744ddeSBryan Venteicher return (0); 11506f744ddeSBryan Venteicher } 11516f744ddeSBryan Venteicher 11526f744ddeSBryan Venteicher static void 1153a019e26cSBryan Venteicher vtcon_port_dev_alias(struct vtcon_port *port, const char *name, size_t len) 1154a019e26cSBryan Venteicher { 1155a019e26cSBryan Venteicher struct vtcon_softc *sc; 1156a019e26cSBryan Venteicher struct cdev *pdev; 1157a019e26cSBryan Venteicher struct tty *tp; 1158a019e26cSBryan Venteicher int i, error; 1159a019e26cSBryan Venteicher 1160a019e26cSBryan Venteicher sc = port->vtcport_sc; 1161a019e26cSBryan Venteicher tp = port->vtcport_tty; 1162a019e26cSBryan Venteicher 1163a019e26cSBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_ALIAS) 1164a019e26cSBryan Venteicher return; 1165a019e26cSBryan Venteicher 1166a019e26cSBryan Venteicher /* Port name is UTF-8, but we can only handle ASCII. */ 1167a019e26cSBryan Venteicher for (i = 0; i < len; i++) { 1168a019e26cSBryan Venteicher if (!isascii(name[i])) 1169a019e26cSBryan Venteicher return; 1170a019e26cSBryan Venteicher } 1171a019e26cSBryan Venteicher 1172a019e26cSBryan Venteicher /* 1173a019e26cSBryan Venteicher * Port name may not conform to the devfs requirements so we cannot use 1174a019e26cSBryan Venteicher * tty_makealias() because the MAKEDEV_CHECKNAME flag must be specified. 1175a019e26cSBryan Venteicher */ 1176a019e26cSBryan Venteicher error = make_dev_alias_p(MAKEDEV_NOWAIT | MAKEDEV_CHECKNAME, &pdev, 1177a019e26cSBryan Venteicher tp->t_dev, "%s/%*s", VTCON_TTY_ALIAS_PREFIX, (int)len, name); 1178a019e26cSBryan Venteicher if (error) { 1179a019e26cSBryan Venteicher device_printf(sc->vtcon_dev, 1180a019e26cSBryan Venteicher "%s: cannot make dev alias (%s/%*s) error %d\n", __func__, 1181a019e26cSBryan Venteicher VTCON_TTY_ALIAS_PREFIX, (int)len, name, error); 1182a019e26cSBryan Venteicher } else 1183a019e26cSBryan Venteicher port->vtcport_flags |= VTCON_PORT_FLAG_ALIAS; 1184a019e26cSBryan Venteicher } 1185a019e26cSBryan Venteicher 1186a019e26cSBryan Venteicher static void 118704434c94SBryan Venteicher vtcon_port_drain_bufs(struct virtqueue *vq) 11886f744ddeSBryan Venteicher { 11896f744ddeSBryan Venteicher void *buf; 11906f744ddeSBryan Venteicher int last; 11916f744ddeSBryan Venteicher 11926f744ddeSBryan Venteicher last = 0; 11936f744ddeSBryan Venteicher 11946f744ddeSBryan Venteicher while ((buf = virtqueue_drain(vq, &last)) != NULL) 11956f744ddeSBryan Venteicher free(buf, M_DEVBUF); 11966f744ddeSBryan Venteicher } 11976f744ddeSBryan Venteicher 11986f744ddeSBryan Venteicher static void 119904434c94SBryan Venteicher vtcon_port_drain(struct vtcon_port *port) 12006f744ddeSBryan Venteicher { 12016f744ddeSBryan Venteicher 120204434c94SBryan Venteicher vtcon_port_drain_bufs(port->vtcport_invq); 120304434c94SBryan Venteicher } 120404434c94SBryan Venteicher 120504434c94SBryan Venteicher static void 120604434c94SBryan Venteicher vtcon_port_teardown(struct vtcon_port *port) 120704434c94SBryan Venteicher { 120804434c94SBryan Venteicher struct tty *tp; 120904434c94SBryan Venteicher 12106f744ddeSBryan Venteicher tp = port->vtcport_tty; 12116f744ddeSBryan Venteicher 121204434c94SBryan Venteicher port->vtcport_flags |= VTCON_PORT_FLAG_GONE; 12136f744ddeSBryan Venteicher 12146f744ddeSBryan Venteicher if (tp != NULL) { 12156f744ddeSBryan Venteicher atomic_add_int(&vtcon_pending_free, 1); 12166f744ddeSBryan Venteicher tty_rel_gone(tp); 12176f744ddeSBryan Venteicher } else 12186f744ddeSBryan Venteicher vtcon_port_destroy(port); 12196f744ddeSBryan Venteicher } 12206f744ddeSBryan Venteicher 12216f744ddeSBryan Venteicher static void 12226f744ddeSBryan Venteicher vtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows) 12236f744ddeSBryan Venteicher { 12246f744ddeSBryan Venteicher struct tty *tp; 12256f744ddeSBryan Venteicher struct winsize sz; 12266f744ddeSBryan Venteicher 12276f744ddeSBryan Venteicher tp = port->vtcport_tty; 12286f744ddeSBryan Venteicher 12296f744ddeSBryan Venteicher if (tp == NULL) 12306f744ddeSBryan Venteicher return; 12316f744ddeSBryan Venteicher 12326f744ddeSBryan Venteicher bzero(&sz, sizeof(struct winsize)); 12336f744ddeSBryan Venteicher sz.ws_col = cols; 12346f744ddeSBryan Venteicher sz.ws_row = rows; 12356f744ddeSBryan Venteicher 12366f744ddeSBryan Venteicher tty_set_winsize(tp, &sz); 123704434c94SBryan Venteicher } 123804434c94SBryan Venteicher 123904434c94SBryan Venteicher static void 124004434c94SBryan Venteicher vtcon_port_update_console_size(struct vtcon_softc *sc) 124104434c94SBryan Venteicher { 124204434c94SBryan Venteicher struct vtcon_port *port; 124304434c94SBryan Venteicher struct vtcon_softc_port *scport; 124404434c94SBryan Venteicher uint16_t cols, rows; 124504434c94SBryan Venteicher 124604434c94SBryan Venteicher vtcon_get_console_size(sc, &cols, &rows); 124704434c94SBryan Venteicher 124804434c94SBryan Venteicher /* 124904434c94SBryan Venteicher * For now, assume the first (only) port is the console. Note 125004434c94SBryan Venteicher * QEMU does not implement this feature yet. 125104434c94SBryan Venteicher */ 125204434c94SBryan Venteicher scport = &sc->vtcon_ports[0]; 125304434c94SBryan Venteicher 125404434c94SBryan Venteicher VTCON_LOCK(sc); 125504434c94SBryan Venteicher port = scport->vcsp_port; 125604434c94SBryan Venteicher 125704434c94SBryan Venteicher if (port != NULL) { 125804434c94SBryan Venteicher VTCON_PORT_LOCK(port); 125904434c94SBryan Venteicher VTCON_UNLOCK(sc); 126004434c94SBryan Venteicher vtcon_port_change_size(port, cols, rows); 12616f744ddeSBryan Venteicher VTCON_PORT_UNLOCK(port); 126204434c94SBryan Venteicher } else 126304434c94SBryan Venteicher VTCON_UNLOCK(sc); 12646f744ddeSBryan Venteicher } 12656f744ddeSBryan Venteicher 12666f744ddeSBryan Venteicher static void 12676f744ddeSBryan Venteicher vtcon_port_enable_intr(struct vtcon_port *port) 12686f744ddeSBryan Venteicher { 12696f744ddeSBryan Venteicher 12706f744ddeSBryan Venteicher /* 1271453130d9SPedro F. Giffuni * NOTE: The out virtqueue is always polled, so its interrupt 127204434c94SBryan Venteicher * kept disabled. 12736f744ddeSBryan Venteicher */ 12746f744ddeSBryan Venteicher virtqueue_enable_intr(port->vtcport_invq); 12756f744ddeSBryan Venteicher } 12766f744ddeSBryan Venteicher 12776f744ddeSBryan Venteicher static void 12786f744ddeSBryan Venteicher vtcon_port_disable_intr(struct vtcon_port *port) 12796f744ddeSBryan Venteicher { 12806f744ddeSBryan Venteicher 12816f744ddeSBryan Venteicher if (port->vtcport_invq != NULL) 12826f744ddeSBryan Venteicher virtqueue_disable_intr(port->vtcport_invq); 12836f744ddeSBryan Venteicher if (port->vtcport_outvq != NULL) 12846f744ddeSBryan Venteicher virtqueue_disable_intr(port->vtcport_outvq); 12856f744ddeSBryan Venteicher } 12866f744ddeSBryan Venteicher 12876f744ddeSBryan Venteicher static void 128804434c94SBryan Venteicher vtcon_port_in(struct vtcon_port *port) 12896f744ddeSBryan Venteicher { 12906f744ddeSBryan Venteicher struct virtqueue *vq; 129104434c94SBryan Venteicher struct tty *tp; 12926f744ddeSBryan Venteicher char *buf; 12936f744ddeSBryan Venteicher uint32_t len; 12946f744ddeSBryan Venteicher int i, deq; 12956f744ddeSBryan Venteicher 12966f744ddeSBryan Venteicher tp = port->vtcport_tty; 12976f744ddeSBryan Venteicher vq = port->vtcport_invq; 12986f744ddeSBryan Venteicher 12996f744ddeSBryan Venteicher again: 13006f744ddeSBryan Venteicher deq = 0; 13016f744ddeSBryan Venteicher 13026f744ddeSBryan Venteicher while ((buf = virtqueue_dequeue(vq, &len)) != NULL) { 1303b84b3efdSBryan Venteicher for (i = 0; i < len; i++) { 1304b84b3efdSBryan Venteicher #if defined(KDB) 1305b84b3efdSBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE) 1306b84b3efdSBryan Venteicher kdb_alt_break(buf[i], 1307b84b3efdSBryan Venteicher &port->vtcport_alt_break_state); 1308b84b3efdSBryan Venteicher #endif 13096f744ddeSBryan Venteicher ttydisc_rint(tp, buf[i], 0); 1310b84b3efdSBryan Venteicher } 131104434c94SBryan Venteicher vtcon_port_requeue_buf(port, buf); 131204434c94SBryan Venteicher deq++; 13136f744ddeSBryan Venteicher } 13146f744ddeSBryan Venteicher ttydisc_rint_done(tp); 13156f744ddeSBryan Venteicher 13166f744ddeSBryan Venteicher if (deq > 0) 13176f744ddeSBryan Venteicher virtqueue_notify(vq); 13186f744ddeSBryan Venteicher 13196f744ddeSBryan Venteicher if (virtqueue_enable_intr(vq) != 0) 13206f744ddeSBryan Venteicher goto again; 13216f744ddeSBryan Venteicher } 13226f744ddeSBryan Venteicher 13236f744ddeSBryan Venteicher static void 132404434c94SBryan Venteicher vtcon_port_intr(void *scportx) 13256f744ddeSBryan Venteicher { 132604434c94SBryan Venteicher struct vtcon_softc_port *scport; 132704434c94SBryan Venteicher struct vtcon_softc *sc; 13286f744ddeSBryan Venteicher struct vtcon_port *port; 13296f744ddeSBryan Venteicher 133004434c94SBryan Venteicher scport = scportx; 133104434c94SBryan Venteicher sc = scport->vcsp_sc; 13326f744ddeSBryan Venteicher 133304434c94SBryan Venteicher VTCON_LOCK(sc); 133404434c94SBryan Venteicher port = scport->vcsp_port; 133504434c94SBryan Venteicher if (port == NULL) { 133604434c94SBryan Venteicher VTCON_UNLOCK(sc); 133704434c94SBryan Venteicher return; 133804434c94SBryan Venteicher } 133904434c94SBryan Venteicher VTCON_PORT_LOCK(port); 134004434c94SBryan Venteicher VTCON_UNLOCK(sc); 134104434c94SBryan Venteicher if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0) 134204434c94SBryan Venteicher vtcon_port_in(port); 134304434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 13446f744ddeSBryan Venteicher } 13456f744ddeSBryan Venteicher 13466f744ddeSBryan Venteicher static void 134704434c94SBryan Venteicher vtcon_port_out(struct vtcon_port *port, void *buf, int bufsize) 13486f744ddeSBryan Venteicher { 134904434c94SBryan Venteicher struct sglist_seg segs[2]; 13506f744ddeSBryan Venteicher struct sglist sg; 13516f744ddeSBryan Venteicher struct virtqueue *vq; 13526f744ddeSBryan Venteicher int error; 13536f744ddeSBryan Venteicher 13546f744ddeSBryan Venteicher vq = port->vtcport_outvq; 135504434c94SBryan Venteicher KASSERT(virtqueue_empty(vq), 135604434c94SBryan Venteicher ("%s: port %p out virtqueue not emtpy", __func__, port)); 13576f744ddeSBryan Venteicher 135804434c94SBryan Venteicher sglist_init(&sg, 2, segs); 13596f744ddeSBryan Venteicher error = sglist_append(&sg, buf, bufsize); 136004434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding buffer to sglist", 136104434c94SBryan Venteicher __func__, error)); 13626f744ddeSBryan Venteicher 136304434c94SBryan Venteicher error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0); 136404434c94SBryan Venteicher if (error == 0) { 13656f744ddeSBryan Venteicher virtqueue_notify(vq); 13666f744ddeSBryan Venteicher virtqueue_poll(vq, NULL); 13676f744ddeSBryan Venteicher } 13686f744ddeSBryan Venteicher } 13696f744ddeSBryan Venteicher 13706f744ddeSBryan Venteicher static void 137104434c94SBryan Venteicher vtcon_port_submit_event(struct vtcon_port *port, uint16_t event, 13726f744ddeSBryan Venteicher uint16_t value) 13736f744ddeSBryan Venteicher { 13746f744ddeSBryan Venteicher struct vtcon_softc *sc; 13756f744ddeSBryan Venteicher 13766f744ddeSBryan Venteicher sc = port->vtcport_sc; 13776f744ddeSBryan Venteicher 137804434c94SBryan Venteicher vtcon_ctrl_send_control(sc, port->vtcport_id, event, value); 13796f744ddeSBryan Venteicher } 13806f744ddeSBryan Venteicher 13816f744ddeSBryan Venteicher static int 13826f744ddeSBryan Venteicher vtcon_tty_open(struct tty *tp) 13836f744ddeSBryan Venteicher { 13846f744ddeSBryan Venteicher struct vtcon_port *port; 13856f744ddeSBryan Venteicher 13866f744ddeSBryan Venteicher port = tty_softc(tp); 13876f744ddeSBryan Venteicher 138804434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 138904434c94SBryan Venteicher return (ENXIO); 139004434c94SBryan Venteicher 139104434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 13926f744ddeSBryan Venteicher 13936f744ddeSBryan Venteicher return (0); 13946f744ddeSBryan Venteicher } 13956f744ddeSBryan Venteicher 13966f744ddeSBryan Venteicher static void 13976f744ddeSBryan Venteicher vtcon_tty_close(struct tty *tp) 13986f744ddeSBryan Venteicher { 13996f744ddeSBryan Venteicher struct vtcon_port *port; 14006f744ddeSBryan Venteicher 14016f744ddeSBryan Venteicher port = tty_softc(tp); 14026f744ddeSBryan Venteicher 140304434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 140404434c94SBryan Venteicher return; 140504434c94SBryan Venteicher 140604434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); 14076f744ddeSBryan Venteicher } 14086f744ddeSBryan Venteicher 14096f744ddeSBryan Venteicher static void 14106f744ddeSBryan Venteicher vtcon_tty_outwakeup(struct tty *tp) 14116f744ddeSBryan Venteicher { 14126f744ddeSBryan Venteicher struct vtcon_port *port; 14136f744ddeSBryan Venteicher char buf[VTCON_BULK_BUFSZ]; 14146f744ddeSBryan Venteicher int len; 14156f744ddeSBryan Venteicher 14166f744ddeSBryan Venteicher port = tty_softc(tp); 14176f744ddeSBryan Venteicher 141804434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 141904434c94SBryan Venteicher return; 142004434c94SBryan Venteicher 14216f744ddeSBryan Venteicher while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0) 142204434c94SBryan Venteicher vtcon_port_out(port, buf, len); 14236f744ddeSBryan Venteicher } 14246f744ddeSBryan Venteicher 14256f744ddeSBryan Venteicher static void 14266f744ddeSBryan Venteicher vtcon_tty_free(void *xport) 14276f744ddeSBryan Venteicher { 14286f744ddeSBryan Venteicher struct vtcon_port *port; 14296f744ddeSBryan Venteicher 14306f744ddeSBryan Venteicher port = xport; 14316f744ddeSBryan Venteicher 14326f744ddeSBryan Venteicher vtcon_port_destroy(port); 14336f744ddeSBryan Venteicher atomic_subtract_int(&vtcon_pending_free, 1); 14346f744ddeSBryan Venteicher } 14356f744ddeSBryan Venteicher 14366f744ddeSBryan Venteicher static void 14376f744ddeSBryan Venteicher vtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows) 14386f744ddeSBryan Venteicher { 14396f744ddeSBryan Venteicher struct virtio_console_config concfg; 14406f744ddeSBryan Venteicher 14416f744ddeSBryan Venteicher KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE, 14426f744ddeSBryan Venteicher ("%s: size feature not negotiated", __func__)); 14436f744ddeSBryan Venteicher 14446f744ddeSBryan Venteicher vtcon_read_config(sc, &concfg); 14456f744ddeSBryan Venteicher 14466f744ddeSBryan Venteicher *cols = concfg.cols; 14476f744ddeSBryan Venteicher *rows = concfg.rows; 14486f744ddeSBryan Venteicher } 14496f744ddeSBryan Venteicher 14506f744ddeSBryan Venteicher static void 14516f744ddeSBryan Venteicher vtcon_enable_interrupts(struct vtcon_softc *sc) 14526f744ddeSBryan Venteicher { 145304434c94SBryan Venteicher struct vtcon_softc_port *scport; 14546f744ddeSBryan Venteicher struct vtcon_port *port; 145504434c94SBryan Venteicher int i; 145604434c94SBryan Venteicher 145704434c94SBryan Venteicher VTCON_LOCK(sc); 14586f744ddeSBryan Venteicher 14596f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 14606f744ddeSBryan Venteicher virtqueue_enable_intr(sc->vtcon_ctrl_rxvq); 14616f744ddeSBryan Venteicher 146204434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 146304434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 146404434c94SBryan Venteicher 146504434c94SBryan Venteicher port = scport->vcsp_port; 146604434c94SBryan Venteicher if (port == NULL) 146704434c94SBryan Venteicher continue; 146804434c94SBryan Venteicher 146904434c94SBryan Venteicher VTCON_PORT_LOCK(port); 14706f744ddeSBryan Venteicher vtcon_port_enable_intr(port); 147104434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 147204434c94SBryan Venteicher } 147304434c94SBryan Venteicher 147404434c94SBryan Venteicher VTCON_UNLOCK(sc); 14756f744ddeSBryan Venteicher } 14766f744ddeSBryan Venteicher 14776f744ddeSBryan Venteicher static void 14786f744ddeSBryan Venteicher vtcon_disable_interrupts(struct vtcon_softc *sc) 14796f744ddeSBryan Venteicher { 148004434c94SBryan Venteicher struct vtcon_softc_port *scport; 14816f744ddeSBryan Venteicher struct vtcon_port *port; 148204434c94SBryan Venteicher int i; 148304434c94SBryan Venteicher 148404434c94SBryan Venteicher VTCON_LOCK_ASSERT(sc); 14856f744ddeSBryan Venteicher 14866f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 14876f744ddeSBryan Venteicher virtqueue_disable_intr(sc->vtcon_ctrl_rxvq); 14886f744ddeSBryan Venteicher 148904434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 149004434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 149104434c94SBryan Venteicher 149204434c94SBryan Venteicher port = scport->vcsp_port; 149304434c94SBryan Venteicher if (port == NULL) 149404434c94SBryan Venteicher continue; 149504434c94SBryan Venteicher 149604434c94SBryan Venteicher VTCON_PORT_LOCK(port); 14976f744ddeSBryan Venteicher vtcon_port_disable_intr(port); 149804434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 149904434c94SBryan Venteicher } 15006f744ddeSBryan Venteicher } 1501