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 { 0, NULL } 1506f744ddeSBryan Venteicher }; 1516f744ddeSBryan Venteicher 1526f744ddeSBryan Venteicher static int vtcon_modevent(module_t, int, void *); 15304434c94SBryan Venteicher static void vtcon_drain_all(void); 1546f744ddeSBryan Venteicher 1556f744ddeSBryan Venteicher static int vtcon_probe(device_t); 1566f744ddeSBryan Venteicher static int vtcon_attach(device_t); 1576f744ddeSBryan Venteicher static int vtcon_detach(device_t); 1586f744ddeSBryan Venteicher static int vtcon_config_change(device_t); 1596f744ddeSBryan Venteicher 160e6cc42f1SBryan Venteicher static int vtcon_setup_features(struct vtcon_softc *); 161e6cc42f1SBryan Venteicher static int vtcon_negotiate_features(struct vtcon_softc *); 16204434c94SBryan Venteicher static int vtcon_alloc_scports(struct vtcon_softc *); 1636f744ddeSBryan Venteicher static int vtcon_alloc_virtqueues(struct vtcon_softc *); 1646f744ddeSBryan Venteicher static void vtcon_read_config(struct vtcon_softc *, 1656f744ddeSBryan Venteicher struct virtio_console_config *); 1666f744ddeSBryan Venteicher 1676f744ddeSBryan Venteicher static void vtcon_determine_max_ports(struct vtcon_softc *, 1686f744ddeSBryan Venteicher struct virtio_console_config *); 16904434c94SBryan Venteicher static void vtcon_destroy_ports(struct vtcon_softc *); 1706f744ddeSBryan Venteicher static void vtcon_stop(struct vtcon_softc *); 1716f744ddeSBryan Venteicher 17204434c94SBryan Venteicher static int vtcon_ctrl_event_enqueue(struct vtcon_softc *, 1736f744ddeSBryan Venteicher struct virtio_console_control *); 17404434c94SBryan Venteicher static int vtcon_ctrl_event_create(struct vtcon_softc *); 17504434c94SBryan Venteicher static void vtcon_ctrl_event_requeue(struct vtcon_softc *, 1766f744ddeSBryan Venteicher struct virtio_console_control *); 17704434c94SBryan Venteicher static int vtcon_ctrl_event_populate(struct vtcon_softc *); 17804434c94SBryan Venteicher static void vtcon_ctrl_event_drain(struct vtcon_softc *); 1796f744ddeSBryan Venteicher static int vtcon_ctrl_init(struct vtcon_softc *); 1806f744ddeSBryan Venteicher static void vtcon_ctrl_deinit(struct vtcon_softc *); 1816f744ddeSBryan Venteicher static void vtcon_ctrl_port_add_event(struct vtcon_softc *, int); 1826f744ddeSBryan Venteicher static void vtcon_ctrl_port_remove_event(struct vtcon_softc *, int); 1836f744ddeSBryan Venteicher static void vtcon_ctrl_port_console_event(struct vtcon_softc *, int); 1846f744ddeSBryan Venteicher static void vtcon_ctrl_port_open_event(struct vtcon_softc *, int); 185cb981343SJakub Wojciech Klama static void vtcon_ctrl_port_name_event(struct vtcon_softc *, int, 186cb981343SJakub Wojciech Klama const char *, size_t); 18704434c94SBryan Venteicher static void vtcon_ctrl_process_event(struct vtcon_softc *, 188cb981343SJakub Wojciech Klama struct virtio_console_control *, void *, size_t); 1896f744ddeSBryan Venteicher static void vtcon_ctrl_task_cb(void *, int); 19004434c94SBryan Venteicher static void vtcon_ctrl_event_intr(void *); 19104434c94SBryan Venteicher static void vtcon_ctrl_poll(struct vtcon_softc *, 19204434c94SBryan Venteicher struct virtio_console_control *control); 19304434c94SBryan Venteicher static void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t, 19404434c94SBryan Venteicher uint16_t, uint16_t); 1956f744ddeSBryan Venteicher 19604434c94SBryan Venteicher static int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t); 19704434c94SBryan Venteicher static int vtcon_port_create_buf(struct vtcon_port *); 19804434c94SBryan Venteicher static void vtcon_port_requeue_buf(struct vtcon_port *, void *); 1996f744ddeSBryan Venteicher static int vtcon_port_populate(struct vtcon_port *); 2006f744ddeSBryan Venteicher static void vtcon_port_destroy(struct vtcon_port *); 20104434c94SBryan Venteicher static int vtcon_port_create(struct vtcon_softc *, int); 202a019e26cSBryan Venteicher static void vtcon_port_dev_alias(struct vtcon_port *, const char *, 203a019e26cSBryan Venteicher size_t); 20404434c94SBryan Venteicher static void vtcon_port_drain_bufs(struct virtqueue *); 20504434c94SBryan Venteicher static void vtcon_port_drain(struct vtcon_port *); 20604434c94SBryan Venteicher static void vtcon_port_teardown(struct vtcon_port *); 2076f744ddeSBryan Venteicher static void vtcon_port_change_size(struct vtcon_port *, uint16_t, 2086f744ddeSBryan Venteicher uint16_t); 20904434c94SBryan Venteicher static void vtcon_port_update_console_size(struct vtcon_softc *); 2106f744ddeSBryan Venteicher static void vtcon_port_enable_intr(struct vtcon_port *); 2116f744ddeSBryan Venteicher static void vtcon_port_disable_intr(struct vtcon_port *); 21204434c94SBryan Venteicher static void vtcon_port_in(struct vtcon_port *); 21304434c94SBryan Venteicher static void vtcon_port_intr(void *); 21404434c94SBryan Venteicher static void vtcon_port_out(struct vtcon_port *, void *, int); 21504434c94SBryan Venteicher static void vtcon_port_submit_event(struct vtcon_port *, uint16_t, 2166f744ddeSBryan Venteicher uint16_t); 2176f744ddeSBryan Venteicher 2186f744ddeSBryan Venteicher static int vtcon_tty_open(struct tty *); 2196f744ddeSBryan Venteicher static void vtcon_tty_close(struct tty *); 2206f744ddeSBryan Venteicher static void vtcon_tty_outwakeup(struct tty *); 2216f744ddeSBryan Venteicher static void vtcon_tty_free(void *); 2226f744ddeSBryan Venteicher 2236f744ddeSBryan Venteicher static void vtcon_get_console_size(struct vtcon_softc *, uint16_t *, 2246f744ddeSBryan Venteicher uint16_t *); 2256f744ddeSBryan Venteicher 2266f744ddeSBryan Venteicher static void vtcon_enable_interrupts(struct vtcon_softc *); 2276f744ddeSBryan Venteicher static void vtcon_disable_interrupts(struct vtcon_softc *); 2286f744ddeSBryan Venteicher 229edf7c8ddSBryan Venteicher #define vtcon_modern(_sc) (((_sc)->vtcon_features & VIRTIO_F_VERSION_1) != 0) 230edf7c8ddSBryan Venteicher #define vtcon_htog16(_sc, _val) virtio_htog16(vtcon_modern(_sc), _val) 231edf7c8ddSBryan Venteicher #define vtcon_htog32(_sc, _val) virtio_htog32(vtcon_modern(_sc), _val) 232edf7c8ddSBryan Venteicher #define vtcon_htog64(_sc, _val) virtio_htog64(vtcon_modern(_sc), _val) 233edf7c8ddSBryan Venteicher #define vtcon_gtoh16(_sc, _val) virtio_gtoh16(vtcon_modern(_sc), _val) 234edf7c8ddSBryan Venteicher #define vtcon_gtoh32(_sc, _val) virtio_gtoh32(vtcon_modern(_sc), _val) 235edf7c8ddSBryan Venteicher #define vtcon_gtoh64(_sc, _val) virtio_gtoh64(vtcon_modern(_sc), _val) 236edf7c8ddSBryan Venteicher 2376f744ddeSBryan Venteicher static int vtcon_pending_free; 2386f744ddeSBryan Venteicher 2396f744ddeSBryan Venteicher static struct ttydevsw vtcon_tty_class = { 2406f744ddeSBryan Venteicher .tsw_flags = 0, 2416f744ddeSBryan Venteicher .tsw_open = vtcon_tty_open, 2426f744ddeSBryan Venteicher .tsw_close = vtcon_tty_close, 2436f744ddeSBryan Venteicher .tsw_outwakeup = vtcon_tty_outwakeup, 2446f744ddeSBryan Venteicher .tsw_free = vtcon_tty_free, 2456f744ddeSBryan Venteicher }; 2466f744ddeSBryan Venteicher 2476f744ddeSBryan Venteicher static device_method_t vtcon_methods[] = { 2486f744ddeSBryan Venteicher /* Device methods. */ 2496f744ddeSBryan Venteicher DEVMETHOD(device_probe, vtcon_probe), 2506f744ddeSBryan Venteicher DEVMETHOD(device_attach, vtcon_attach), 2516f744ddeSBryan Venteicher DEVMETHOD(device_detach, vtcon_detach), 2526f744ddeSBryan Venteicher 2536f744ddeSBryan Venteicher /* VirtIO methods. */ 2546f744ddeSBryan Venteicher DEVMETHOD(virtio_config_change, vtcon_config_change), 2556f744ddeSBryan Venteicher 2566f744ddeSBryan Venteicher DEVMETHOD_END 2576f744ddeSBryan Venteicher }; 2586f744ddeSBryan Venteicher 2596f744ddeSBryan Venteicher static driver_t vtcon_driver = { 2606f744ddeSBryan Venteicher "vtcon", 2616f744ddeSBryan Venteicher vtcon_methods, 2626f744ddeSBryan Venteicher sizeof(struct vtcon_softc) 2636f744ddeSBryan Venteicher }; 2646f744ddeSBryan Venteicher 265*5c4c96d3SJohn Baldwin VIRTIO_DRIVER_MODULE(virtio_console, vtcon_driver, vtcon_modevent, NULL); 2666f744ddeSBryan Venteicher MODULE_VERSION(virtio_console, 1); 2676f744ddeSBryan Venteicher MODULE_DEPEND(virtio_console, virtio, 1, 1, 1); 2686f744ddeSBryan Venteicher 269633218eeSJessica Clarke VIRTIO_SIMPLE_PNPINFO(virtio_console, VIRTIO_ID_CONSOLE, 2700f6040f0SConrad Meyer "VirtIO Console Adapter"); 2710f6040f0SConrad Meyer 2726f744ddeSBryan Venteicher static int 2736f744ddeSBryan Venteicher vtcon_modevent(module_t mod, int type, void *unused) 2746f744ddeSBryan Venteicher { 2756f744ddeSBryan Venteicher int error; 2766f744ddeSBryan Venteicher 2776f744ddeSBryan Venteicher switch (type) { 2786f744ddeSBryan Venteicher case MOD_LOAD: 2796f744ddeSBryan Venteicher error = 0; 2806f744ddeSBryan Venteicher break; 2816f744ddeSBryan Venteicher case MOD_QUIESCE: 28204434c94SBryan Venteicher error = 0; 28304434c94SBryan Venteicher break; 2846f744ddeSBryan Venteicher case MOD_UNLOAD: 28504434c94SBryan Venteicher vtcon_drain_all(); 28604434c94SBryan Venteicher error = 0; 2876f744ddeSBryan Venteicher break; 2886f744ddeSBryan Venteicher case MOD_SHUTDOWN: 2896f744ddeSBryan Venteicher error = 0; 2906f744ddeSBryan Venteicher break; 2916f744ddeSBryan Venteicher default: 2926f744ddeSBryan Venteicher error = EOPNOTSUPP; 2936f744ddeSBryan Venteicher break; 2946f744ddeSBryan Venteicher } 2956f744ddeSBryan Venteicher 2966f744ddeSBryan Venteicher return (error); 2976f744ddeSBryan Venteicher } 2986f744ddeSBryan Venteicher 29904434c94SBryan Venteicher static void 30004434c94SBryan Venteicher vtcon_drain_all(void) 30104434c94SBryan Venteicher { 30204434c94SBryan Venteicher int first; 30304434c94SBryan Venteicher 30404434c94SBryan Venteicher for (first = 1; vtcon_pending_free != 0; first = 0) { 30504434c94SBryan Venteicher if (first != 0) { 30604434c94SBryan Venteicher printf("virtio_console: Waiting for all detached TTY " 30704434c94SBryan Venteicher "devices to have open fds closed.\n"); 30804434c94SBryan Venteicher } 30904434c94SBryan Venteicher pause("vtcondra", hz); 31004434c94SBryan Venteicher } 31104434c94SBryan Venteicher } 31204434c94SBryan Venteicher 3136f744ddeSBryan Venteicher static int 3146f744ddeSBryan Venteicher vtcon_probe(device_t dev) 3156f744ddeSBryan Venteicher { 3160f6040f0SConrad Meyer return (VIRTIO_SIMPLE_PROBE(dev, virtio_console)); 3176f744ddeSBryan Venteicher } 3186f744ddeSBryan Venteicher 3196f744ddeSBryan Venteicher static int 3206f744ddeSBryan Venteicher vtcon_attach(device_t dev) 3216f744ddeSBryan Venteicher { 3226f744ddeSBryan Venteicher struct vtcon_softc *sc; 3236f744ddeSBryan Venteicher struct virtio_console_config concfg; 3246f744ddeSBryan Venteicher int error; 3256f744ddeSBryan Venteicher 3266f744ddeSBryan Venteicher sc = device_get_softc(dev); 3276f744ddeSBryan Venteicher sc->vtcon_dev = dev; 328e6cc42f1SBryan Venteicher virtio_set_feature_desc(dev, vtcon_feature_desc); 3296f744ddeSBryan Venteicher 33004434c94SBryan Venteicher mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF); 33104434c94SBryan Venteicher mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF); 3326f744ddeSBryan Venteicher 333e6cc42f1SBryan Venteicher error = vtcon_setup_features(sc); 334e6cc42f1SBryan Venteicher if (error) { 335e6cc42f1SBryan Venteicher device_printf(dev, "cannot setup features\n"); 336e6cc42f1SBryan Venteicher goto fail; 337e6cc42f1SBryan Venteicher } 3386f744ddeSBryan Venteicher 3396f744ddeSBryan Venteicher vtcon_read_config(sc, &concfg); 3406f744ddeSBryan Venteicher vtcon_determine_max_ports(sc, &concfg); 3416f744ddeSBryan Venteicher 34204434c94SBryan Venteicher error = vtcon_alloc_scports(sc); 34304434c94SBryan Venteicher if (error) { 34404434c94SBryan Venteicher device_printf(dev, "cannot allocate softc port structures\n"); 34504434c94SBryan Venteicher goto fail; 34604434c94SBryan Venteicher } 34704434c94SBryan Venteicher 3486f744ddeSBryan Venteicher error = vtcon_alloc_virtqueues(sc); 3496f744ddeSBryan Venteicher if (error) { 3506f744ddeSBryan Venteicher device_printf(dev, "cannot allocate virtqueues\n"); 3516f744ddeSBryan Venteicher goto fail; 3526f744ddeSBryan Venteicher } 3536f744ddeSBryan Venteicher 35404434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 35504434c94SBryan Venteicher TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc); 3566f744ddeSBryan Venteicher error = vtcon_ctrl_init(sc); 357b84b3efdSBryan Venteicher if (error) 358b84b3efdSBryan Venteicher goto fail; 359b84b3efdSBryan Venteicher } else { 36004434c94SBryan Venteicher error = vtcon_port_create(sc, 0); 3616f744ddeSBryan Venteicher if (error) 3626f744ddeSBryan Venteicher goto fail; 363b84b3efdSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_SIZE) 364b84b3efdSBryan Venteicher vtcon_port_update_console_size(sc); 365b84b3efdSBryan Venteicher } 3666f744ddeSBryan Venteicher 3676f744ddeSBryan Venteicher error = virtio_setup_intr(dev, INTR_TYPE_TTY); 3686f744ddeSBryan Venteicher if (error) { 3696f744ddeSBryan Venteicher device_printf(dev, "cannot setup virtqueue interrupts\n"); 3706f744ddeSBryan Venteicher goto fail; 3716f744ddeSBryan Venteicher } 3726f744ddeSBryan Venteicher 3736f744ddeSBryan Venteicher vtcon_enable_interrupts(sc); 3746f744ddeSBryan Venteicher 37504434c94SBryan Venteicher vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID, 3766f744ddeSBryan Venteicher VIRTIO_CONSOLE_DEVICE_READY, 1); 3776f744ddeSBryan Venteicher 3786f744ddeSBryan Venteicher fail: 3796f744ddeSBryan Venteicher if (error) 3806f744ddeSBryan Venteicher vtcon_detach(dev); 3816f744ddeSBryan Venteicher 3826f744ddeSBryan Venteicher return (error); 3836f744ddeSBryan Venteicher } 3846f744ddeSBryan Venteicher 3856f744ddeSBryan Venteicher static int 3866f744ddeSBryan Venteicher vtcon_detach(device_t dev) 3876f744ddeSBryan Venteicher { 3886f744ddeSBryan Venteicher struct vtcon_softc *sc; 3896f744ddeSBryan Venteicher 3906f744ddeSBryan Venteicher sc = device_get_softc(dev); 3916f744ddeSBryan Venteicher 3926f744ddeSBryan Venteicher VTCON_LOCK(sc); 3936f744ddeSBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_DETACHED; 3946f744ddeSBryan Venteicher if (device_is_attached(dev)) 3956f744ddeSBryan Venteicher vtcon_stop(sc); 3966f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 3976f744ddeSBryan Venteicher 39804434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 3996f744ddeSBryan Venteicher taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task); 4006f744ddeSBryan Venteicher vtcon_ctrl_deinit(sc); 40104434c94SBryan Venteicher } 4026f744ddeSBryan Venteicher 40304434c94SBryan Venteicher vtcon_destroy_ports(sc); 40404434c94SBryan Venteicher mtx_destroy(&sc->vtcon_mtx); 40504434c94SBryan Venteicher mtx_destroy(&sc->vtcon_ctrl_tx_mtx); 4066f744ddeSBryan Venteicher 4076f744ddeSBryan Venteicher return (0); 4086f744ddeSBryan Venteicher } 4096f744ddeSBryan Venteicher 4106f744ddeSBryan Venteicher static int 4116f744ddeSBryan Venteicher vtcon_config_change(device_t dev) 4126f744ddeSBryan Venteicher { 4136f744ddeSBryan Venteicher struct vtcon_softc *sc; 4146f744ddeSBryan Venteicher 4156f744ddeSBryan Venteicher sc = device_get_softc(dev); 4166f744ddeSBryan Venteicher 4176f744ddeSBryan Venteicher /* 41804434c94SBryan Venteicher * When the multiport feature is negotiated, all configuration 41904434c94SBryan Venteicher * changes are done through control virtqueue events. 4206f744ddeSBryan Venteicher */ 42104434c94SBryan Venteicher if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) { 42204434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_SIZE) 42304434c94SBryan Venteicher vtcon_port_update_console_size(sc); 4246f744ddeSBryan Venteicher } 4256f744ddeSBryan Venteicher 4266f744ddeSBryan Venteicher return (0); 4276f744ddeSBryan Venteicher } 4286f744ddeSBryan Venteicher 429e6cc42f1SBryan Venteicher static int 4306f744ddeSBryan Venteicher vtcon_negotiate_features(struct vtcon_softc *sc) 4316f744ddeSBryan Venteicher { 4326f744ddeSBryan Venteicher device_t dev; 4336f744ddeSBryan Venteicher uint64_t features; 4346f744ddeSBryan Venteicher 4356f744ddeSBryan Venteicher dev = sc->vtcon_dev; 4366f744ddeSBryan Venteicher features = VTCON_FEATURES; 4376f744ddeSBryan Venteicher 4386f744ddeSBryan Venteicher sc->vtcon_features = virtio_negotiate_features(dev, features); 439e6cc42f1SBryan Venteicher return (virtio_finalize_features(dev)); 4406f744ddeSBryan Venteicher } 4416f744ddeSBryan Venteicher 442e6cc42f1SBryan Venteicher static int 44304434c94SBryan Venteicher vtcon_setup_features(struct vtcon_softc *sc) 44404434c94SBryan Venteicher { 44504434c94SBryan Venteicher device_t dev; 446e6cc42f1SBryan Venteicher int error; 44704434c94SBryan Venteicher 44804434c94SBryan Venteicher dev = sc->vtcon_dev; 44904434c94SBryan Venteicher 450e6cc42f1SBryan Venteicher error = vtcon_negotiate_features(sc); 451e6cc42f1SBryan Venteicher if (error) 452e6cc42f1SBryan Venteicher return (error); 45304434c94SBryan Venteicher 45404434c94SBryan Venteicher if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE)) 45504434c94SBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_SIZE; 45604434c94SBryan Venteicher if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT)) 45704434c94SBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_MULTIPORT; 458e6cc42f1SBryan Venteicher 459e6cc42f1SBryan Venteicher return (0); 46004434c94SBryan Venteicher } 46104434c94SBryan Venteicher 4626f744ddeSBryan Venteicher #define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg) \ 4636f744ddeSBryan Venteicher if (virtio_with_feature(_dev, _feature)) { \ 4646f744ddeSBryan Venteicher virtio_read_device_config(_dev, \ 4656f744ddeSBryan Venteicher offsetof(struct virtio_console_config, _field), \ 4666f744ddeSBryan Venteicher &(_cfg)->_field, sizeof((_cfg)->_field)); \ 4676f744ddeSBryan Venteicher } 4686f744ddeSBryan Venteicher 4696f744ddeSBryan Venteicher static void 4706f744ddeSBryan Venteicher vtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg) 4716f744ddeSBryan Venteicher { 4726f744ddeSBryan Venteicher device_t dev; 4736f744ddeSBryan Venteicher 4746f744ddeSBryan Venteicher dev = sc->vtcon_dev; 4756f744ddeSBryan Venteicher 4766f744ddeSBryan Venteicher bzero(concfg, sizeof(struct virtio_console_config)); 4776f744ddeSBryan Venteicher 4786f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg); 4796f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg); 4806f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg); 4816f744ddeSBryan Venteicher } 4826f744ddeSBryan Venteicher 4836f744ddeSBryan Venteicher #undef VTCON_GET_CONFIG 4846f744ddeSBryan Venteicher 4856f744ddeSBryan Venteicher static int 48604434c94SBryan Venteicher vtcon_alloc_scports(struct vtcon_softc *sc) 48704434c94SBryan Venteicher { 48804434c94SBryan Venteicher struct vtcon_softc_port *scport; 489ac2fffa4SPedro F. Giffuni int max, i; 49004434c94SBryan Venteicher 49104434c94SBryan Venteicher max = sc->vtcon_max_ports; 49204434c94SBryan Venteicher 493ac2fffa4SPedro F. Giffuni sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max, 49404434c94SBryan Venteicher M_DEVBUF, M_NOWAIT | M_ZERO); 49504434c94SBryan Venteicher if (sc->vtcon_ports == NULL) 49604434c94SBryan Venteicher return (ENOMEM); 49704434c94SBryan Venteicher 49804434c94SBryan Venteicher for (i = 0; i < max; i++) { 49904434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 50004434c94SBryan Venteicher scport->vcsp_sc = sc; 50104434c94SBryan Venteicher } 50204434c94SBryan Venteicher 50304434c94SBryan Venteicher return (0); 50404434c94SBryan Venteicher } 50504434c94SBryan Venteicher 50604434c94SBryan Venteicher static int 5076f744ddeSBryan Venteicher vtcon_alloc_virtqueues(struct vtcon_softc *sc) 5086f744ddeSBryan Venteicher { 5096f744ddeSBryan Venteicher device_t dev; 5106f744ddeSBryan Venteicher struct vq_alloc_info *info; 51104434c94SBryan Venteicher struct vtcon_softc_port *scport; 512ac2fffa4SPedro F. Giffuni int i, idx, portidx, nvqs, error; 5136f744ddeSBryan Venteicher 5146f744ddeSBryan Venteicher dev = sc->vtcon_dev; 5156f744ddeSBryan Venteicher 5166f744ddeSBryan Venteicher nvqs = sc->vtcon_max_ports * 2; 5176f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 5186f744ddeSBryan Venteicher nvqs += 2; 5196f744ddeSBryan Venteicher 520ac2fffa4SPedro F. Giffuni info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT); 5216f744ddeSBryan Venteicher if (info == NULL) 5226f744ddeSBryan Venteicher return (ENOMEM); 5236f744ddeSBryan Venteicher 5246f744ddeSBryan Venteicher for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) { 5256f744ddeSBryan Venteicher if (i == 1) { 5266f744ddeSBryan Venteicher /* The control virtqueues are after the first port. */ 5276f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx], 0, 52804434c94SBryan Venteicher vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq, 5296f744ddeSBryan Venteicher "%s-control rx", device_get_nameunit(dev)); 5306f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx+1], 0, 5316f744ddeSBryan Venteicher NULL, sc, &sc->vtcon_ctrl_txvq, 5326f744ddeSBryan Venteicher "%s-control tx", device_get_nameunit(dev)); 5336f744ddeSBryan Venteicher continue; 5346f744ddeSBryan Venteicher } 5356f744ddeSBryan Venteicher 53604434c94SBryan Venteicher scport = &sc->vtcon_ports[portidx]; 5376f744ddeSBryan Venteicher 53804434c94SBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr, 53904434c94SBryan Venteicher scport, &scport->vcsp_invq, "%s-port%d in", 54004434c94SBryan Venteicher device_get_nameunit(dev), i); 5416f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL, 54204434c94SBryan Venteicher NULL, &scport->vcsp_outvq, "%s-port%d out", 54304434c94SBryan Venteicher device_get_nameunit(dev), i); 5446f744ddeSBryan Venteicher 5456f744ddeSBryan Venteicher portidx++; 5466f744ddeSBryan Venteicher } 5476f744ddeSBryan Venteicher 5486f744ddeSBryan Venteicher error = virtio_alloc_virtqueues(dev, 0, nvqs, info); 5496f744ddeSBryan Venteicher free(info, M_TEMP); 5506f744ddeSBryan Venteicher 5516f744ddeSBryan Venteicher return (error); 5526f744ddeSBryan Venteicher } 5536f744ddeSBryan Venteicher 5546f744ddeSBryan Venteicher static void 5556f744ddeSBryan Venteicher vtcon_determine_max_ports(struct vtcon_softc *sc, 5566f744ddeSBryan Venteicher struct virtio_console_config *concfg) 5576f744ddeSBryan Venteicher { 5586f744ddeSBryan Venteicher 5596f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 5606f744ddeSBryan Venteicher sc->vtcon_max_ports = 5616f744ddeSBryan Venteicher min(concfg->max_nr_ports, VTCON_MAX_PORTS); 5626f744ddeSBryan Venteicher if (sc->vtcon_max_ports == 0) 5636f744ddeSBryan Venteicher sc->vtcon_max_ports = 1; 5646f744ddeSBryan Venteicher } else 5656f744ddeSBryan Venteicher sc->vtcon_max_ports = 1; 5666f744ddeSBryan Venteicher } 5676f744ddeSBryan Venteicher 5686f744ddeSBryan Venteicher static void 56904434c94SBryan Venteicher vtcon_destroy_ports(struct vtcon_softc *sc) 5706f744ddeSBryan Venteicher { 57104434c94SBryan Venteicher struct vtcon_softc_port *scport; 57204434c94SBryan Venteicher struct vtcon_port *port; 57304434c94SBryan Venteicher struct virtqueue *vq; 57404434c94SBryan Venteicher int i; 5756f744ddeSBryan Venteicher 57604434c94SBryan Venteicher if (sc->vtcon_ports == NULL) 57704434c94SBryan Venteicher return; 57804434c94SBryan Venteicher 57904434c94SBryan Venteicher VTCON_LOCK(sc); 58004434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 58104434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 58204434c94SBryan Venteicher 58304434c94SBryan Venteicher port = scport->vcsp_port; 58404434c94SBryan Venteicher if (port != NULL) { 58504434c94SBryan Venteicher scport->vcsp_port = NULL; 58604434c94SBryan Venteicher VTCON_PORT_LOCK(port); 58704434c94SBryan Venteicher VTCON_UNLOCK(sc); 58804434c94SBryan Venteicher vtcon_port_teardown(port); 58904434c94SBryan Venteicher VTCON_LOCK(sc); 5906f744ddeSBryan Venteicher } 5916f744ddeSBryan Venteicher 59204434c94SBryan Venteicher vq = scport->vcsp_invq; 59304434c94SBryan Venteicher if (vq != NULL) 59404434c94SBryan Venteicher vtcon_port_drain_bufs(vq); 5956f744ddeSBryan Venteicher } 59604434c94SBryan Venteicher VTCON_UNLOCK(sc); 59704434c94SBryan Venteicher 59804434c94SBryan Venteicher free(sc->vtcon_ports, M_DEVBUF); 59904434c94SBryan Venteicher sc->vtcon_ports = NULL; 6006f744ddeSBryan Venteicher } 6016f744ddeSBryan Venteicher 6026f744ddeSBryan Venteicher static void 6036f744ddeSBryan Venteicher vtcon_stop(struct vtcon_softc *sc) 6046f744ddeSBryan Venteicher { 6056f744ddeSBryan Venteicher 6066f744ddeSBryan Venteicher vtcon_disable_interrupts(sc); 6076f744ddeSBryan Venteicher virtio_stop(sc->vtcon_dev); 6086f744ddeSBryan Venteicher } 6096f744ddeSBryan Venteicher 6106f744ddeSBryan Venteicher static int 61104434c94SBryan Venteicher vtcon_ctrl_event_enqueue(struct vtcon_softc *sc, 6126f744ddeSBryan Venteicher struct virtio_console_control *control) 6136f744ddeSBryan Venteicher { 61404434c94SBryan Venteicher struct sglist_seg segs[2]; 6156f744ddeSBryan Venteicher struct sglist sg; 6166f744ddeSBryan Venteicher struct virtqueue *vq; 6174d78ef3eSMateusz Guzik int error __diagused; 6186f744ddeSBryan Venteicher 6196f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 6206f744ddeSBryan Venteicher 62104434c94SBryan Venteicher sglist_init(&sg, 2, segs); 622a019e26cSBryan Venteicher error = sglist_append(&sg, control, VTCON_CTRL_BUFSZ); 62304434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding control to sglist", 62404434c94SBryan Venteicher __func__, error)); 6256f744ddeSBryan Venteicher 62604434c94SBryan Venteicher return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg)); 6276f744ddeSBryan Venteicher } 6286f744ddeSBryan Venteicher 6296f744ddeSBryan Venteicher static int 63004434c94SBryan Venteicher vtcon_ctrl_event_create(struct vtcon_softc *sc) 6316f744ddeSBryan Venteicher { 6326f744ddeSBryan Venteicher struct virtio_console_control *control; 6336f744ddeSBryan Venteicher int error; 6346f744ddeSBryan Venteicher 635a019e26cSBryan Venteicher control = malloc(VTCON_CTRL_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT); 6366f744ddeSBryan Venteicher if (control == NULL) 6376f744ddeSBryan Venteicher return (ENOMEM); 6386f744ddeSBryan Venteicher 63904434c94SBryan Venteicher error = vtcon_ctrl_event_enqueue(sc, control); 6406f744ddeSBryan Venteicher if (error) 6416f744ddeSBryan Venteicher free(control, M_DEVBUF); 6426f744ddeSBryan Venteicher 6436f744ddeSBryan Venteicher return (error); 6446f744ddeSBryan Venteicher } 6456f744ddeSBryan Venteicher 6466f744ddeSBryan Venteicher static void 64704434c94SBryan Venteicher vtcon_ctrl_event_requeue(struct vtcon_softc *sc, 6486f744ddeSBryan Venteicher struct virtio_console_control *control) 6496f744ddeSBryan Venteicher { 6504d78ef3eSMateusz Guzik int error __diagused; 6516f744ddeSBryan Venteicher 652a019e26cSBryan Venteicher bzero(control, VTCON_CTRL_BUFSZ); 6536f744ddeSBryan Venteicher 65404434c94SBryan Venteicher error = vtcon_ctrl_event_enqueue(sc, control); 6556f744ddeSBryan Venteicher KASSERT(error == 0, 6566f744ddeSBryan Venteicher ("%s: cannot requeue control buffer %d", __func__, error)); 6576f744ddeSBryan Venteicher } 6586f744ddeSBryan Venteicher 6596f744ddeSBryan Venteicher static int 66004434c94SBryan Venteicher vtcon_ctrl_event_populate(struct vtcon_softc *sc) 6616f744ddeSBryan Venteicher { 6626f744ddeSBryan Venteicher struct virtqueue *vq; 6636f744ddeSBryan Venteicher int nbufs, error; 6646f744ddeSBryan Venteicher 6656f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 6666f744ddeSBryan Venteicher error = ENOSPC; 6676f744ddeSBryan Venteicher 6686f744ddeSBryan Venteicher for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 66904434c94SBryan Venteicher error = vtcon_ctrl_event_create(sc); 6706f744ddeSBryan Venteicher if (error) 6716f744ddeSBryan Venteicher break; 6726f744ddeSBryan Venteicher } 6736f744ddeSBryan Venteicher 6746f744ddeSBryan Venteicher if (nbufs > 0) { 6756f744ddeSBryan Venteicher virtqueue_notify(vq); 6766f744ddeSBryan Venteicher error = 0; 6776f744ddeSBryan Venteicher } 6786f744ddeSBryan Venteicher 6796f744ddeSBryan Venteicher return (error); 6806f744ddeSBryan Venteicher } 6816f744ddeSBryan Venteicher 6826f744ddeSBryan Venteicher static void 68304434c94SBryan Venteicher vtcon_ctrl_event_drain(struct vtcon_softc *sc) 6846f744ddeSBryan Venteicher { 6856f744ddeSBryan Venteicher struct virtio_console_control *control; 6866f744ddeSBryan Venteicher struct virtqueue *vq; 6876f744ddeSBryan Venteicher int last; 6886f744ddeSBryan Venteicher 6896f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 6906f744ddeSBryan Venteicher last = 0; 6916f744ddeSBryan Venteicher 6926f744ddeSBryan Venteicher if (vq == NULL) 6936f744ddeSBryan Venteicher return; 6946f744ddeSBryan Venteicher 69504434c94SBryan Venteicher VTCON_LOCK(sc); 6966f744ddeSBryan Venteicher while ((control = virtqueue_drain(vq, &last)) != NULL) 6976f744ddeSBryan Venteicher free(control, M_DEVBUF); 69804434c94SBryan Venteicher VTCON_UNLOCK(sc); 69904434c94SBryan Venteicher } 70004434c94SBryan Venteicher 70104434c94SBryan Venteicher static int 70204434c94SBryan Venteicher vtcon_ctrl_init(struct vtcon_softc *sc) 70304434c94SBryan Venteicher { 70404434c94SBryan Venteicher int error; 70504434c94SBryan Venteicher 70604434c94SBryan Venteicher error = vtcon_ctrl_event_populate(sc); 70704434c94SBryan Venteicher 70804434c94SBryan Venteicher return (error); 7096f744ddeSBryan Venteicher } 7106f744ddeSBryan Venteicher 7116f744ddeSBryan Venteicher static void 7126f744ddeSBryan Venteicher vtcon_ctrl_deinit(struct vtcon_softc *sc) 7136f744ddeSBryan Venteicher { 7146f744ddeSBryan Venteicher 71504434c94SBryan Venteicher vtcon_ctrl_event_drain(sc); 7166f744ddeSBryan Venteicher } 7176f744ddeSBryan Venteicher 7186f744ddeSBryan Venteicher static void 7196f744ddeSBryan Venteicher vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id) 7206f744ddeSBryan Venteicher { 7216f744ddeSBryan Venteicher device_t dev; 7226f744ddeSBryan Venteicher int error; 7236f744ddeSBryan Venteicher 7246f744ddeSBryan Venteicher dev = sc->vtcon_dev; 7256f744ddeSBryan Venteicher 72604434c94SBryan Venteicher /* This single thread only way for ports to be created. */ 72704434c94SBryan Venteicher if (sc->vtcon_ports[id].vcsp_port != NULL) { 7286f744ddeSBryan Venteicher device_printf(dev, "%s: adding port %d, but already exists\n", 7296f744ddeSBryan Venteicher __func__, id); 7306f744ddeSBryan Venteicher return; 7316f744ddeSBryan Venteicher } 7326f744ddeSBryan Venteicher 73304434c94SBryan Venteicher error = vtcon_port_create(sc, id); 7346f744ddeSBryan Venteicher if (error) { 7356f744ddeSBryan Venteicher device_printf(dev, "%s: cannot create port %d: %d\n", 7366f744ddeSBryan Venteicher __func__, id, error); 737b84b3efdSBryan Venteicher vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0); 7386f744ddeSBryan Venteicher return; 7396f744ddeSBryan Venteicher } 7406f744ddeSBryan Venteicher } 7416f744ddeSBryan Venteicher 7426f744ddeSBryan Venteicher static void 7436f744ddeSBryan Venteicher vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id) 7446f744ddeSBryan Venteicher { 7456f744ddeSBryan Venteicher device_t dev; 74604434c94SBryan Venteicher struct vtcon_softc_port *scport; 7476f744ddeSBryan Venteicher struct vtcon_port *port; 7486f744ddeSBryan Venteicher 7496f744ddeSBryan Venteicher dev = sc->vtcon_dev; 75004434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 7516f744ddeSBryan Venteicher 75204434c94SBryan Venteicher VTCON_LOCK(sc); 75304434c94SBryan Venteicher port = scport->vcsp_port; 7546f744ddeSBryan Venteicher if (port == NULL) { 75504434c94SBryan Venteicher VTCON_UNLOCK(sc); 7566f744ddeSBryan Venteicher device_printf(dev, "%s: remove port %d, but does not exist\n", 7576f744ddeSBryan Venteicher __func__, id); 7586f744ddeSBryan Venteicher return; 7596f744ddeSBryan Venteicher } 7606f744ddeSBryan Venteicher 76104434c94SBryan Venteicher scport->vcsp_port = NULL; 76204434c94SBryan Venteicher VTCON_PORT_LOCK(port); 76304434c94SBryan Venteicher VTCON_UNLOCK(sc); 76404434c94SBryan Venteicher vtcon_port_teardown(port); 7656f744ddeSBryan Venteicher } 7666f744ddeSBryan Venteicher 7676f744ddeSBryan Venteicher static void 7686f744ddeSBryan Venteicher vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id) 7696f744ddeSBryan Venteicher { 770b84b3efdSBryan Venteicher device_t dev; 771b84b3efdSBryan Venteicher struct vtcon_softc_port *scport; 772b84b3efdSBryan Venteicher struct vtcon_port *port; 7736f744ddeSBryan Venteicher 774b84b3efdSBryan Venteicher dev = sc->vtcon_dev; 775b84b3efdSBryan Venteicher scport = &sc->vtcon_ports[id]; 776b84b3efdSBryan Venteicher 777b84b3efdSBryan Venteicher VTCON_LOCK(sc); 778b84b3efdSBryan Venteicher port = scport->vcsp_port; 779b84b3efdSBryan Venteicher if (port == NULL) { 780b84b3efdSBryan Venteicher VTCON_UNLOCK(sc); 781b84b3efdSBryan Venteicher device_printf(dev, "%s: console port %d, but does not exist\n", 78204434c94SBryan Venteicher __func__, id); 783b84b3efdSBryan Venteicher return; 784b84b3efdSBryan Venteicher } 785b84b3efdSBryan Venteicher 786b84b3efdSBryan Venteicher VTCON_PORT_LOCK(port); 787b84b3efdSBryan Venteicher VTCON_UNLOCK(sc); 788b84b3efdSBryan Venteicher port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE; 789b84b3efdSBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 790b84b3efdSBryan Venteicher VTCON_PORT_UNLOCK(port); 7916f744ddeSBryan Venteicher } 7926f744ddeSBryan Venteicher 7936f744ddeSBryan Venteicher static void 7946f744ddeSBryan Venteicher vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id) 7956f744ddeSBryan Venteicher { 7966f744ddeSBryan Venteicher device_t dev; 79704434c94SBryan Venteicher struct vtcon_softc_port *scport; 7986f744ddeSBryan Venteicher struct vtcon_port *port; 7996f744ddeSBryan Venteicher 8006f744ddeSBryan Venteicher dev = sc->vtcon_dev; 80104434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 8026f744ddeSBryan Venteicher 80304434c94SBryan Venteicher VTCON_LOCK(sc); 80404434c94SBryan Venteicher port = scport->vcsp_port; 8056f744ddeSBryan Venteicher if (port == NULL) { 80604434c94SBryan Venteicher VTCON_UNLOCK(sc); 8076f744ddeSBryan Venteicher device_printf(dev, "%s: open port %d, but does not exist\n", 8086f744ddeSBryan Venteicher __func__, id); 8096f744ddeSBryan Venteicher return; 8106f744ddeSBryan Venteicher } 8116f744ddeSBryan Venteicher 81204434c94SBryan Venteicher VTCON_PORT_LOCK(port); 81304434c94SBryan Venteicher VTCON_UNLOCK(sc); 8146f744ddeSBryan Venteicher vtcon_port_enable_intr(port); 81504434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 8166f744ddeSBryan Venteicher } 8176f744ddeSBryan Venteicher 8186f744ddeSBryan Venteicher static void 819cb981343SJakub Wojciech Klama vtcon_ctrl_port_name_event(struct vtcon_softc *sc, int id, const char *name, 820cb981343SJakub Wojciech Klama size_t len) 821cb981343SJakub Wojciech Klama { 822cb981343SJakub Wojciech Klama device_t dev; 823cb981343SJakub Wojciech Klama struct vtcon_softc_port *scport; 824cb981343SJakub Wojciech Klama struct vtcon_port *port; 825cb981343SJakub Wojciech Klama 826cb981343SJakub Wojciech Klama dev = sc->vtcon_dev; 827cb981343SJakub Wojciech Klama scport = &sc->vtcon_ports[id]; 828cb981343SJakub Wojciech Klama 829a019e26cSBryan Venteicher /* 830a019e26cSBryan Venteicher * The VirtIO specification says the NUL terminator is not included in 831a019e26cSBryan Venteicher * the length, but QEMU includes it. Adjust the length if needed. 832a019e26cSBryan Venteicher */ 833a019e26cSBryan Venteicher if (name == NULL || len == 0) 834a019e26cSBryan Venteicher return; 835a019e26cSBryan Venteicher if (name[len - 1] == '\0') { 836a019e26cSBryan Venteicher len--; 837a019e26cSBryan Venteicher if (len == 0) 838a019e26cSBryan Venteicher return; 839a019e26cSBryan Venteicher } 840a019e26cSBryan Venteicher 841a019e26cSBryan Venteicher VTCON_LOCK(sc); 842cb981343SJakub Wojciech Klama port = scport->vcsp_port; 843cb981343SJakub Wojciech Klama if (port == NULL) { 844a019e26cSBryan Venteicher VTCON_UNLOCK(sc); 845cb981343SJakub Wojciech Klama device_printf(dev, "%s: name port %d, but does not exist\n", 846cb981343SJakub Wojciech Klama __func__, id); 847cb981343SJakub Wojciech Klama return; 848cb981343SJakub Wojciech Klama } 849cb981343SJakub Wojciech Klama 850a019e26cSBryan Venteicher VTCON_PORT_LOCK(port); 851a019e26cSBryan Venteicher VTCON_UNLOCK(sc); 852a019e26cSBryan Venteicher vtcon_port_dev_alias(port, name, len); 853a019e26cSBryan Venteicher VTCON_PORT_UNLOCK(port); 854cb981343SJakub Wojciech Klama } 855cb981343SJakub Wojciech Klama 856cb981343SJakub Wojciech Klama static void 85704434c94SBryan Venteicher vtcon_ctrl_process_event(struct vtcon_softc *sc, 858a019e26cSBryan Venteicher struct virtio_console_control *control, void *data, size_t data_len) 8596f744ddeSBryan Venteicher { 8606f744ddeSBryan Venteicher device_t dev; 861edf7c8ddSBryan Venteicher uint32_t id; 862edf7c8ddSBryan Venteicher uint16_t event; 8636f744ddeSBryan Venteicher 8646f744ddeSBryan Venteicher dev = sc->vtcon_dev; 865edf7c8ddSBryan Venteicher id = vtcon_htog32(sc, control->id); 866edf7c8ddSBryan Venteicher event = vtcon_htog16(sc, control->event); 8676f744ddeSBryan Venteicher 868edf7c8ddSBryan Venteicher if (id >= sc->vtcon_max_ports) { 869edf7c8ddSBryan Venteicher device_printf(dev, "%s: event %d invalid port ID %d\n", 870edf7c8ddSBryan Venteicher __func__, event, id); 8716f744ddeSBryan Venteicher return; 8726f744ddeSBryan Venteicher } 8736f744ddeSBryan Venteicher 874edf7c8ddSBryan Venteicher switch (event) { 8756f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_ADD: 8766f744ddeSBryan Venteicher vtcon_ctrl_port_add_event(sc, id); 8776f744ddeSBryan Venteicher break; 8786f744ddeSBryan Venteicher 8796f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_REMOVE: 8806f744ddeSBryan Venteicher vtcon_ctrl_port_remove_event(sc, id); 8816f744ddeSBryan Venteicher break; 8826f744ddeSBryan Venteicher 8836f744ddeSBryan Venteicher case VIRTIO_CONSOLE_CONSOLE_PORT: 8846f744ddeSBryan Venteicher vtcon_ctrl_port_console_event(sc, id); 8856f744ddeSBryan Venteicher break; 8866f744ddeSBryan Venteicher 8876f744ddeSBryan Venteicher case VIRTIO_CONSOLE_RESIZE: 8886f744ddeSBryan Venteicher break; 8896f744ddeSBryan Venteicher 8906f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_OPEN: 8916f744ddeSBryan Venteicher vtcon_ctrl_port_open_event(sc, id); 8926f744ddeSBryan Venteicher break; 8936f744ddeSBryan Venteicher 8946f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_NAME: 895a019e26cSBryan Venteicher vtcon_ctrl_port_name_event(sc, id, (const char *)data, data_len); 8966f744ddeSBryan Venteicher break; 8976f744ddeSBryan Venteicher } 8986f744ddeSBryan Venteicher } 8996f744ddeSBryan Venteicher 9006f744ddeSBryan Venteicher static void 9016f744ddeSBryan Venteicher vtcon_ctrl_task_cb(void *xsc, int pending) 9026f744ddeSBryan Venteicher { 9036f744ddeSBryan Venteicher struct vtcon_softc *sc; 9046f744ddeSBryan Venteicher struct virtqueue *vq; 9056f744ddeSBryan Venteicher struct virtio_console_control *control; 906a019e26cSBryan Venteicher void *data; 907a019e26cSBryan Venteicher size_t data_len; 90804434c94SBryan Venteicher int detached; 909cb981343SJakub Wojciech Klama uint32_t len; 9106f744ddeSBryan Venteicher 9116f744ddeSBryan Venteicher sc = xsc; 9126f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 9136f744ddeSBryan Venteicher 9146f744ddeSBryan Venteicher VTCON_LOCK(sc); 91504434c94SBryan Venteicher 91604434c94SBryan Venteicher while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) { 917cb981343SJakub Wojciech Klama control = virtqueue_dequeue(vq, &len); 9186f744ddeSBryan Venteicher if (control == NULL) 9196f744ddeSBryan Venteicher break; 9206f744ddeSBryan Venteicher 921a019e26cSBryan Venteicher if (len > sizeof(struct virtio_console_control)) { 922a019e26cSBryan Venteicher data = (void *) &control[1]; 923a019e26cSBryan Venteicher data_len = len - sizeof(struct virtio_console_control); 924a019e26cSBryan Venteicher } else { 925a019e26cSBryan Venteicher data = NULL; 926a019e26cSBryan Venteicher data_len = 0; 927cb981343SJakub Wojciech Klama } 928cb981343SJakub Wojciech Klama 9296f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 930a019e26cSBryan Venteicher vtcon_ctrl_process_event(sc, control, data, data_len); 9316f744ddeSBryan Venteicher VTCON_LOCK(sc); 93204434c94SBryan Venteicher vtcon_ctrl_event_requeue(sc, control); 9336f744ddeSBryan Venteicher } 9346f744ddeSBryan Venteicher 93504434c94SBryan Venteicher if (!detached) { 93604434c94SBryan Venteicher virtqueue_notify(vq); 9376f744ddeSBryan Venteicher if (virtqueue_enable_intr(vq) != 0) 93804434c94SBryan Venteicher taskqueue_enqueue(taskqueue_thread, 93904434c94SBryan Venteicher &sc->vtcon_ctrl_task); 94004434c94SBryan Venteicher } 94104434c94SBryan Venteicher 94204434c94SBryan Venteicher VTCON_UNLOCK(sc); 94304434c94SBryan Venteicher } 94404434c94SBryan Venteicher 94504434c94SBryan Venteicher static void 94604434c94SBryan Venteicher vtcon_ctrl_event_intr(void *xsc) 94704434c94SBryan Venteicher { 94804434c94SBryan Venteicher struct vtcon_softc *sc; 94904434c94SBryan Venteicher 95004434c94SBryan Venteicher sc = xsc; 95104434c94SBryan Venteicher 95204434c94SBryan Venteicher /* 95304434c94SBryan Venteicher * Only some events require us to potentially block, but it 95404434c94SBryan Venteicher * easier to just defer all event handling to the taskqueue. 95504434c94SBryan Venteicher */ 9566f744ddeSBryan Venteicher taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task); 9576f744ddeSBryan Venteicher } 9586f744ddeSBryan Venteicher 95904434c94SBryan Venteicher static void 96004434c94SBryan Venteicher vtcon_ctrl_poll(struct vtcon_softc *sc, 96104434c94SBryan Venteicher struct virtio_console_control *control) 9626f744ddeSBryan Venteicher { 96304434c94SBryan Venteicher struct sglist_seg segs[2]; 96404434c94SBryan Venteicher struct sglist sg; 96504434c94SBryan Venteicher struct virtqueue *vq; 96604434c94SBryan Venteicher int error; 96704434c94SBryan Venteicher 96804434c94SBryan Venteicher vq = sc->vtcon_ctrl_txvq; 96904434c94SBryan Venteicher 97004434c94SBryan Venteicher sglist_init(&sg, 2, segs); 97104434c94SBryan Venteicher error = sglist_append(&sg, control, 97204434c94SBryan Venteicher sizeof(struct virtio_console_control)); 97304434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding control to sglist", 97404434c94SBryan Venteicher __func__, error)); 97504434c94SBryan Venteicher 97604434c94SBryan Venteicher /* 97704434c94SBryan Venteicher * We cannot use the softc lock to serialize access to this 97804434c94SBryan Venteicher * virtqueue since this is called from the tty layer with the 97904434c94SBryan Venteicher * port lock held. Acquiring the softc would violate our lock 98004434c94SBryan Venteicher * ordering. 98104434c94SBryan Venteicher */ 98204434c94SBryan Venteicher VTCON_CTRL_TX_LOCK(sc); 98304434c94SBryan Venteicher KASSERT(virtqueue_empty(vq), 98404434c94SBryan Venteicher ("%s: virtqueue is not emtpy", __func__)); 98504434c94SBryan Venteicher error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0); 98604434c94SBryan Venteicher if (error == 0) { 98704434c94SBryan Venteicher virtqueue_notify(vq); 98804434c94SBryan Venteicher virtqueue_poll(vq, NULL); 98904434c94SBryan Venteicher } 99004434c94SBryan Venteicher VTCON_CTRL_TX_UNLOCK(sc); 99104434c94SBryan Venteicher } 99204434c94SBryan Venteicher 99304434c94SBryan Venteicher static void 99404434c94SBryan Venteicher vtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid, 99504434c94SBryan Venteicher uint16_t event, uint16_t value) 99604434c94SBryan Venteicher { 99704434c94SBryan Venteicher struct virtio_console_control control; 99804434c94SBryan Venteicher 99904434c94SBryan Venteicher if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) 100004434c94SBryan Venteicher return; 100104434c94SBryan Venteicher 1002edf7c8ddSBryan Venteicher control.id = vtcon_gtoh32(sc, portid); 1003edf7c8ddSBryan Venteicher control.event = vtcon_gtoh16(sc, event); 1004edf7c8ddSBryan Venteicher control.value = vtcon_gtoh16(sc, value); 100504434c94SBryan Venteicher 100604434c94SBryan Venteicher vtcon_ctrl_poll(sc, &control); 100704434c94SBryan Venteicher } 100804434c94SBryan Venteicher 100904434c94SBryan Venteicher static int 101004434c94SBryan Venteicher vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len) 101104434c94SBryan Venteicher { 101204434c94SBryan Venteicher struct sglist_seg segs[2]; 10136f744ddeSBryan Venteicher struct sglist sg; 10146f744ddeSBryan Venteicher struct virtqueue *vq; 10156f744ddeSBryan Venteicher int error; 10166f744ddeSBryan Venteicher 10176f744ddeSBryan Venteicher vq = port->vtcport_invq; 10186f744ddeSBryan Venteicher 101904434c94SBryan Venteicher sglist_init(&sg, 2, segs); 10206f744ddeSBryan Venteicher error = sglist_append(&sg, buf, len); 102104434c94SBryan Venteicher KASSERT(error == 0, 10226f744ddeSBryan Venteicher ("%s: error %d adding buffer to sglist", __func__, error)); 10236f744ddeSBryan Venteicher 102404434c94SBryan Venteicher error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg); 102504434c94SBryan Venteicher 102604434c94SBryan Venteicher return (error); 10276f744ddeSBryan Venteicher } 10286f744ddeSBryan Venteicher 10296f744ddeSBryan Venteicher static int 103004434c94SBryan Venteicher vtcon_port_create_buf(struct vtcon_port *port) 10316f744ddeSBryan Venteicher { 10326f744ddeSBryan Venteicher void *buf; 10336f744ddeSBryan Venteicher int error; 10346f744ddeSBryan Venteicher 10356f744ddeSBryan Venteicher buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT); 10366f744ddeSBryan Venteicher if (buf == NULL) 10376f744ddeSBryan Venteicher return (ENOMEM); 10386f744ddeSBryan Venteicher 103904434c94SBryan Venteicher error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 10406f744ddeSBryan Venteicher if (error) 10416f744ddeSBryan Venteicher free(buf, M_DEVBUF); 10426f744ddeSBryan Venteicher 10436f744ddeSBryan Venteicher return (error); 10446f744ddeSBryan Venteicher } 10456f744ddeSBryan Venteicher 10466f744ddeSBryan Venteicher static void 104704434c94SBryan Venteicher vtcon_port_requeue_buf(struct vtcon_port *port, void *buf) 10486f744ddeSBryan Venteicher { 10494d78ef3eSMateusz Guzik int error __diagused; 10506f744ddeSBryan Venteicher 105104434c94SBryan Venteicher error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 10526f744ddeSBryan Venteicher KASSERT(error == 0, 10536f744ddeSBryan Venteicher ("%s: cannot requeue input buffer %d", __func__, error)); 10546f744ddeSBryan Venteicher } 10556f744ddeSBryan Venteicher 10566f744ddeSBryan Venteicher static int 10576f744ddeSBryan Venteicher vtcon_port_populate(struct vtcon_port *port) 10586f744ddeSBryan Venteicher { 10596f744ddeSBryan Venteicher struct virtqueue *vq; 10606f744ddeSBryan Venteicher int nbufs, error; 10616f744ddeSBryan Venteicher 10626f744ddeSBryan Venteicher vq = port->vtcport_invq; 10636f744ddeSBryan Venteicher error = ENOSPC; 10646f744ddeSBryan Venteicher 10656f744ddeSBryan Venteicher for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 106604434c94SBryan Venteicher error = vtcon_port_create_buf(port); 10676f744ddeSBryan Venteicher if (error) 10686f744ddeSBryan Venteicher break; 10696f744ddeSBryan Venteicher } 10706f744ddeSBryan Venteicher 10716f744ddeSBryan Venteicher if (nbufs > 0) { 10726f744ddeSBryan Venteicher virtqueue_notify(vq); 10736f744ddeSBryan Venteicher error = 0; 10746f744ddeSBryan Venteicher } 10756f744ddeSBryan Venteicher 10766f744ddeSBryan Venteicher return (error); 10776f744ddeSBryan Venteicher } 10786f744ddeSBryan Venteicher 10796f744ddeSBryan Venteicher static void 10806f744ddeSBryan Venteicher vtcon_port_destroy(struct vtcon_port *port) 10816f744ddeSBryan Venteicher { 10826f744ddeSBryan Venteicher 10836f744ddeSBryan Venteicher port->vtcport_sc = NULL; 108404434c94SBryan Venteicher port->vtcport_scport = NULL; 108504434c94SBryan Venteicher port->vtcport_invq = NULL; 108604434c94SBryan Venteicher port->vtcport_outvq = NULL; 10876f744ddeSBryan Venteicher port->vtcport_id = -1; 108804434c94SBryan Venteicher mtx_destroy(&port->vtcport_mtx); 10896f744ddeSBryan Venteicher free(port, M_DEVBUF); 10906f744ddeSBryan Venteicher } 10916f744ddeSBryan Venteicher 10926f744ddeSBryan Venteicher static int 109304434c94SBryan Venteicher vtcon_port_init_vqs(struct vtcon_port *port) 109404434c94SBryan Venteicher { 109504434c94SBryan Venteicher struct vtcon_softc_port *scport; 109604434c94SBryan Venteicher int error; 109704434c94SBryan Venteicher 109804434c94SBryan Venteicher scport = port->vtcport_scport; 109904434c94SBryan Venteicher 110004434c94SBryan Venteicher port->vtcport_invq = scport->vcsp_invq; 110104434c94SBryan Venteicher port->vtcport_outvq = scport->vcsp_outvq; 110204434c94SBryan Venteicher 110304434c94SBryan Venteicher /* 110404434c94SBryan Venteicher * Free any data left over from when this virtqueue was in use by a 110504434c94SBryan Venteicher * prior port. We have not yet notified the host that the port is 110604434c94SBryan Venteicher * ready, so assume nothing in the virtqueue can be for us. 110704434c94SBryan Venteicher */ 110804434c94SBryan Venteicher vtcon_port_drain(port); 110904434c94SBryan Venteicher 111004434c94SBryan Venteicher KASSERT(virtqueue_empty(port->vtcport_invq), 111104434c94SBryan Venteicher ("%s: in virtqueue is not empty", __func__)); 111204434c94SBryan Venteicher KASSERT(virtqueue_empty(port->vtcport_outvq), 111304434c94SBryan Venteicher ("%s: out virtqueue is not empty", __func__)); 111404434c94SBryan Venteicher 111504434c94SBryan Venteicher error = vtcon_port_populate(port); 111604434c94SBryan Venteicher if (error) 111704434c94SBryan Venteicher return (error); 111804434c94SBryan Venteicher 111904434c94SBryan Venteicher return (0); 112004434c94SBryan Venteicher } 112104434c94SBryan Venteicher 112204434c94SBryan Venteicher static int 112304434c94SBryan Venteicher vtcon_port_create(struct vtcon_softc *sc, int id) 11246f744ddeSBryan Venteicher { 11256f744ddeSBryan Venteicher device_t dev; 112604434c94SBryan Venteicher struct vtcon_softc_port *scport; 11276f744ddeSBryan Venteicher struct vtcon_port *port; 11286f744ddeSBryan Venteicher int error; 11296f744ddeSBryan Venteicher 11306f744ddeSBryan Venteicher dev = sc->vtcon_dev; 113104434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 11326f744ddeSBryan Venteicher 113304434c94SBryan Venteicher VTCON_ASSERT_VALID_PORTID(sc, id); 113404434c94SBryan Venteicher MPASS(scport->vcsp_port == NULL); 11356f744ddeSBryan Venteicher 11366f744ddeSBryan Venteicher port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO); 11376f744ddeSBryan Venteicher if (port == NULL) 11386f744ddeSBryan Venteicher return (ENOMEM); 11396f744ddeSBryan Venteicher 11406f744ddeSBryan Venteicher port->vtcport_sc = sc; 114104434c94SBryan Venteicher port->vtcport_scport = scport; 11426f744ddeSBryan Venteicher port->vtcport_id = id; 114304434c94SBryan Venteicher mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF); 11446f744ddeSBryan Venteicher port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port, 11456f744ddeSBryan Venteicher &port->vtcport_mtx); 11466f744ddeSBryan Venteicher 114704434c94SBryan Venteicher error = vtcon_port_init_vqs(port); 11486f744ddeSBryan Venteicher if (error) { 114904434c94SBryan Venteicher VTCON_PORT_LOCK(port); 115004434c94SBryan Venteicher vtcon_port_teardown(port); 11516f744ddeSBryan Venteicher return (error); 11526f744ddeSBryan Venteicher } 11536f744ddeSBryan Venteicher 11546f744ddeSBryan Venteicher VTCON_LOCK(sc); 115504434c94SBryan Venteicher VTCON_PORT_LOCK(port); 115604434c94SBryan Venteicher scport->vcsp_port = port; 115746822c48SBryan Venteicher vtcon_port_enable_intr(port); 115804434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1); 115904434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 11606f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 11616f744ddeSBryan Venteicher 116246822c48SBryan Venteicher tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX, 116346822c48SBryan Venteicher device_get_unit(dev), id); 116446822c48SBryan Venteicher 11656f744ddeSBryan Venteicher return (0); 11666f744ddeSBryan Venteicher } 11676f744ddeSBryan Venteicher 11686f744ddeSBryan Venteicher static void 1169a019e26cSBryan Venteicher vtcon_port_dev_alias(struct vtcon_port *port, const char *name, size_t len) 1170a019e26cSBryan Venteicher { 1171a019e26cSBryan Venteicher struct vtcon_softc *sc; 1172a019e26cSBryan Venteicher struct cdev *pdev; 1173a019e26cSBryan Venteicher struct tty *tp; 1174a019e26cSBryan Venteicher int i, error; 1175a019e26cSBryan Venteicher 1176a019e26cSBryan Venteicher sc = port->vtcport_sc; 1177a019e26cSBryan Venteicher tp = port->vtcport_tty; 1178a019e26cSBryan Venteicher 1179a019e26cSBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_ALIAS) 1180a019e26cSBryan Venteicher return; 1181a019e26cSBryan Venteicher 1182a019e26cSBryan Venteicher /* Port name is UTF-8, but we can only handle ASCII. */ 1183a019e26cSBryan Venteicher for (i = 0; i < len; i++) { 1184a019e26cSBryan Venteicher if (!isascii(name[i])) 1185a019e26cSBryan Venteicher return; 1186a019e26cSBryan Venteicher } 1187a019e26cSBryan Venteicher 1188a019e26cSBryan Venteicher /* 1189a019e26cSBryan Venteicher * Port name may not conform to the devfs requirements so we cannot use 1190a019e26cSBryan Venteicher * tty_makealias() because the MAKEDEV_CHECKNAME flag must be specified. 1191a019e26cSBryan Venteicher */ 1192a019e26cSBryan Venteicher error = make_dev_alias_p(MAKEDEV_NOWAIT | MAKEDEV_CHECKNAME, &pdev, 1193a019e26cSBryan Venteicher tp->t_dev, "%s/%*s", VTCON_TTY_ALIAS_PREFIX, (int)len, name); 1194a019e26cSBryan Venteicher if (error) { 1195a019e26cSBryan Venteicher device_printf(sc->vtcon_dev, 1196a019e26cSBryan Venteicher "%s: cannot make dev alias (%s/%*s) error %d\n", __func__, 1197a019e26cSBryan Venteicher VTCON_TTY_ALIAS_PREFIX, (int)len, name, error); 1198a019e26cSBryan Venteicher } else 1199a019e26cSBryan Venteicher port->vtcport_flags |= VTCON_PORT_FLAG_ALIAS; 1200a019e26cSBryan Venteicher } 1201a019e26cSBryan Venteicher 1202a019e26cSBryan Venteicher static void 120304434c94SBryan Venteicher vtcon_port_drain_bufs(struct virtqueue *vq) 12046f744ddeSBryan Venteicher { 12056f744ddeSBryan Venteicher void *buf; 12066f744ddeSBryan Venteicher int last; 12076f744ddeSBryan Venteicher 12086f744ddeSBryan Venteicher last = 0; 12096f744ddeSBryan Venteicher 12106f744ddeSBryan Venteicher while ((buf = virtqueue_drain(vq, &last)) != NULL) 12116f744ddeSBryan Venteicher free(buf, M_DEVBUF); 12126f744ddeSBryan Venteicher } 12136f744ddeSBryan Venteicher 12146f744ddeSBryan Venteicher static void 121504434c94SBryan Venteicher vtcon_port_drain(struct vtcon_port *port) 12166f744ddeSBryan Venteicher { 12176f744ddeSBryan Venteicher 121804434c94SBryan Venteicher vtcon_port_drain_bufs(port->vtcport_invq); 121904434c94SBryan Venteicher } 122004434c94SBryan Venteicher 122104434c94SBryan Venteicher static void 122204434c94SBryan Venteicher vtcon_port_teardown(struct vtcon_port *port) 122304434c94SBryan Venteicher { 122404434c94SBryan Venteicher struct tty *tp; 122504434c94SBryan Venteicher 12266f744ddeSBryan Venteicher tp = port->vtcport_tty; 12276f744ddeSBryan Venteicher 122804434c94SBryan Venteicher port->vtcport_flags |= VTCON_PORT_FLAG_GONE; 12296f744ddeSBryan Venteicher 12306f744ddeSBryan Venteicher if (tp != NULL) { 12316f744ddeSBryan Venteicher atomic_add_int(&vtcon_pending_free, 1); 12326f744ddeSBryan Venteicher tty_rel_gone(tp); 12336f744ddeSBryan Venteicher } else 12346f744ddeSBryan Venteicher vtcon_port_destroy(port); 12356f744ddeSBryan Venteicher } 12366f744ddeSBryan Venteicher 12376f744ddeSBryan Venteicher static void 12386f744ddeSBryan Venteicher vtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows) 12396f744ddeSBryan Venteicher { 12406f744ddeSBryan Venteicher struct tty *tp; 12416f744ddeSBryan Venteicher struct winsize sz; 12426f744ddeSBryan Venteicher 12436f744ddeSBryan Venteicher tp = port->vtcport_tty; 12446f744ddeSBryan Venteicher 12456f744ddeSBryan Venteicher if (tp == NULL) 12466f744ddeSBryan Venteicher return; 12476f744ddeSBryan Venteicher 12486f744ddeSBryan Venteicher bzero(&sz, sizeof(struct winsize)); 12496f744ddeSBryan Venteicher sz.ws_col = cols; 12506f744ddeSBryan Venteicher sz.ws_row = rows; 12516f744ddeSBryan Venteicher 12526f744ddeSBryan Venteicher tty_set_winsize(tp, &sz); 125304434c94SBryan Venteicher } 125404434c94SBryan Venteicher 125504434c94SBryan Venteicher static void 125604434c94SBryan Venteicher vtcon_port_update_console_size(struct vtcon_softc *sc) 125704434c94SBryan Venteicher { 125804434c94SBryan Venteicher struct vtcon_port *port; 125904434c94SBryan Venteicher struct vtcon_softc_port *scport; 126004434c94SBryan Venteicher uint16_t cols, rows; 126104434c94SBryan Venteicher 126204434c94SBryan Venteicher vtcon_get_console_size(sc, &cols, &rows); 126304434c94SBryan Venteicher 126404434c94SBryan Venteicher /* 126504434c94SBryan Venteicher * For now, assume the first (only) port is the console. Note 126604434c94SBryan Venteicher * QEMU does not implement this feature yet. 126704434c94SBryan Venteicher */ 126804434c94SBryan Venteicher scport = &sc->vtcon_ports[0]; 126904434c94SBryan Venteicher 127004434c94SBryan Venteicher VTCON_LOCK(sc); 127104434c94SBryan Venteicher port = scport->vcsp_port; 127204434c94SBryan Venteicher 127304434c94SBryan Venteicher if (port != NULL) { 127404434c94SBryan Venteicher VTCON_PORT_LOCK(port); 127504434c94SBryan Venteicher VTCON_UNLOCK(sc); 127604434c94SBryan Venteicher vtcon_port_change_size(port, cols, rows); 12776f744ddeSBryan Venteicher VTCON_PORT_UNLOCK(port); 127804434c94SBryan Venteicher } else 127904434c94SBryan Venteicher VTCON_UNLOCK(sc); 12806f744ddeSBryan Venteicher } 12816f744ddeSBryan Venteicher 12826f744ddeSBryan Venteicher static void 12836f744ddeSBryan Venteicher vtcon_port_enable_intr(struct vtcon_port *port) 12846f744ddeSBryan Venteicher { 12856f744ddeSBryan Venteicher 12866f744ddeSBryan Venteicher /* 1287453130d9SPedro F. Giffuni * NOTE: The out virtqueue is always polled, so its interrupt 128804434c94SBryan Venteicher * kept disabled. 12896f744ddeSBryan Venteicher */ 12906f744ddeSBryan Venteicher virtqueue_enable_intr(port->vtcport_invq); 12916f744ddeSBryan Venteicher } 12926f744ddeSBryan Venteicher 12936f744ddeSBryan Venteicher static void 12946f744ddeSBryan Venteicher vtcon_port_disable_intr(struct vtcon_port *port) 12956f744ddeSBryan Venteicher { 12966f744ddeSBryan Venteicher 12976f744ddeSBryan Venteicher if (port->vtcport_invq != NULL) 12986f744ddeSBryan Venteicher virtqueue_disable_intr(port->vtcport_invq); 12996f744ddeSBryan Venteicher if (port->vtcport_outvq != NULL) 13006f744ddeSBryan Venteicher virtqueue_disable_intr(port->vtcport_outvq); 13016f744ddeSBryan Venteicher } 13026f744ddeSBryan Venteicher 13036f744ddeSBryan Venteicher static void 130404434c94SBryan Venteicher vtcon_port_in(struct vtcon_port *port) 13056f744ddeSBryan Venteicher { 13066f744ddeSBryan Venteicher struct virtqueue *vq; 130704434c94SBryan Venteicher struct tty *tp; 13086f744ddeSBryan Venteicher char *buf; 13096f744ddeSBryan Venteicher uint32_t len; 13106f744ddeSBryan Venteicher int i, deq; 13116f744ddeSBryan Venteicher 13126f744ddeSBryan Venteicher tp = port->vtcport_tty; 13136f744ddeSBryan Venteicher vq = port->vtcport_invq; 13146f744ddeSBryan Venteicher 13156f744ddeSBryan Venteicher again: 13166f744ddeSBryan Venteicher deq = 0; 13176f744ddeSBryan Venteicher 13186f744ddeSBryan Venteicher while ((buf = virtqueue_dequeue(vq, &len)) != NULL) { 1319b84b3efdSBryan Venteicher for (i = 0; i < len; i++) { 1320b84b3efdSBryan Venteicher #if defined(KDB) 1321b84b3efdSBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE) 1322b84b3efdSBryan Venteicher kdb_alt_break(buf[i], 1323b84b3efdSBryan Venteicher &port->vtcport_alt_break_state); 1324b84b3efdSBryan Venteicher #endif 13256f744ddeSBryan Venteicher ttydisc_rint(tp, buf[i], 0); 1326b84b3efdSBryan Venteicher } 132704434c94SBryan Venteicher vtcon_port_requeue_buf(port, buf); 132804434c94SBryan Venteicher deq++; 13296f744ddeSBryan Venteicher } 13306f744ddeSBryan Venteicher ttydisc_rint_done(tp); 13316f744ddeSBryan Venteicher 13326f744ddeSBryan Venteicher if (deq > 0) 13336f744ddeSBryan Venteicher virtqueue_notify(vq); 13346f744ddeSBryan Venteicher 13356f744ddeSBryan Venteicher if (virtqueue_enable_intr(vq) != 0) 13366f744ddeSBryan Venteicher goto again; 13376f744ddeSBryan Venteicher } 13386f744ddeSBryan Venteicher 13396f744ddeSBryan Venteicher static void 134004434c94SBryan Venteicher vtcon_port_intr(void *scportx) 13416f744ddeSBryan Venteicher { 134204434c94SBryan Venteicher struct vtcon_softc_port *scport; 134304434c94SBryan Venteicher struct vtcon_softc *sc; 13446f744ddeSBryan Venteicher struct vtcon_port *port; 13456f744ddeSBryan Venteicher 134604434c94SBryan Venteicher scport = scportx; 134704434c94SBryan Venteicher sc = scport->vcsp_sc; 13486f744ddeSBryan Venteicher 134904434c94SBryan Venteicher VTCON_LOCK(sc); 135004434c94SBryan Venteicher port = scport->vcsp_port; 135104434c94SBryan Venteicher if (port == NULL) { 135204434c94SBryan Venteicher VTCON_UNLOCK(sc); 135304434c94SBryan Venteicher return; 135404434c94SBryan Venteicher } 135504434c94SBryan Venteicher VTCON_PORT_LOCK(port); 135604434c94SBryan Venteicher VTCON_UNLOCK(sc); 135704434c94SBryan Venteicher if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0) 135804434c94SBryan Venteicher vtcon_port_in(port); 135904434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 13606f744ddeSBryan Venteicher } 13616f744ddeSBryan Venteicher 13626f744ddeSBryan Venteicher static void 136304434c94SBryan Venteicher vtcon_port_out(struct vtcon_port *port, void *buf, int bufsize) 13646f744ddeSBryan Venteicher { 136504434c94SBryan Venteicher struct sglist_seg segs[2]; 13666f744ddeSBryan Venteicher struct sglist sg; 13676f744ddeSBryan Venteicher struct virtqueue *vq; 13686f744ddeSBryan Venteicher int error; 13696f744ddeSBryan Venteicher 13706f744ddeSBryan Venteicher vq = port->vtcport_outvq; 137104434c94SBryan Venteicher KASSERT(virtqueue_empty(vq), 137204434c94SBryan Venteicher ("%s: port %p out virtqueue not emtpy", __func__, port)); 13736f744ddeSBryan Venteicher 137404434c94SBryan Venteicher sglist_init(&sg, 2, segs); 13756f744ddeSBryan Venteicher error = sglist_append(&sg, buf, bufsize); 137604434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding buffer to sglist", 137704434c94SBryan Venteicher __func__, error)); 13786f744ddeSBryan Venteicher 137904434c94SBryan Venteicher error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0); 138004434c94SBryan Venteicher if (error == 0) { 13816f744ddeSBryan Venteicher virtqueue_notify(vq); 13826f744ddeSBryan Venteicher virtqueue_poll(vq, NULL); 13836f744ddeSBryan Venteicher } 13846f744ddeSBryan Venteicher } 13856f744ddeSBryan Venteicher 13866f744ddeSBryan Venteicher static void 138704434c94SBryan Venteicher vtcon_port_submit_event(struct vtcon_port *port, uint16_t event, 13886f744ddeSBryan Venteicher uint16_t value) 13896f744ddeSBryan Venteicher { 13906f744ddeSBryan Venteicher struct vtcon_softc *sc; 13916f744ddeSBryan Venteicher 13926f744ddeSBryan Venteicher sc = port->vtcport_sc; 13936f744ddeSBryan Venteicher 139404434c94SBryan Venteicher vtcon_ctrl_send_control(sc, port->vtcport_id, event, value); 13956f744ddeSBryan Venteicher } 13966f744ddeSBryan Venteicher 13976f744ddeSBryan Venteicher static int 13986f744ddeSBryan Venteicher vtcon_tty_open(struct tty *tp) 13996f744ddeSBryan Venteicher { 14006f744ddeSBryan Venteicher struct vtcon_port *port; 14016f744ddeSBryan Venteicher 14026f744ddeSBryan Venteicher port = tty_softc(tp); 14036f744ddeSBryan Venteicher 140404434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 140504434c94SBryan Venteicher return (ENXIO); 140604434c94SBryan Venteicher 140704434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 14086f744ddeSBryan Venteicher 14096f744ddeSBryan Venteicher return (0); 14106f744ddeSBryan Venteicher } 14116f744ddeSBryan Venteicher 14126f744ddeSBryan Venteicher static void 14136f744ddeSBryan Venteicher vtcon_tty_close(struct tty *tp) 14146f744ddeSBryan Venteicher { 14156f744ddeSBryan Venteicher struct vtcon_port *port; 14166f744ddeSBryan Venteicher 14176f744ddeSBryan Venteicher port = tty_softc(tp); 14186f744ddeSBryan Venteicher 141904434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 142004434c94SBryan Venteicher return; 142104434c94SBryan Venteicher 142204434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); 14236f744ddeSBryan Venteicher } 14246f744ddeSBryan Venteicher 14256f744ddeSBryan Venteicher static void 14266f744ddeSBryan Venteicher vtcon_tty_outwakeup(struct tty *tp) 14276f744ddeSBryan Venteicher { 14286f744ddeSBryan Venteicher struct vtcon_port *port; 14296f744ddeSBryan Venteicher char buf[VTCON_BULK_BUFSZ]; 14306f744ddeSBryan Venteicher int len; 14316f744ddeSBryan Venteicher 14326f744ddeSBryan Venteicher port = tty_softc(tp); 14336f744ddeSBryan Venteicher 143404434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 143504434c94SBryan Venteicher return; 143604434c94SBryan Venteicher 14376f744ddeSBryan Venteicher while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0) 143804434c94SBryan Venteicher vtcon_port_out(port, buf, len); 14396f744ddeSBryan Venteicher } 14406f744ddeSBryan Venteicher 14416f744ddeSBryan Venteicher static void 14426f744ddeSBryan Venteicher vtcon_tty_free(void *xport) 14436f744ddeSBryan Venteicher { 14446f744ddeSBryan Venteicher struct vtcon_port *port; 14456f744ddeSBryan Venteicher 14466f744ddeSBryan Venteicher port = xport; 14476f744ddeSBryan Venteicher 14486f744ddeSBryan Venteicher vtcon_port_destroy(port); 14496f744ddeSBryan Venteicher atomic_subtract_int(&vtcon_pending_free, 1); 14506f744ddeSBryan Venteicher } 14516f744ddeSBryan Venteicher 14526f744ddeSBryan Venteicher static void 14536f744ddeSBryan Venteicher vtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows) 14546f744ddeSBryan Venteicher { 14556f744ddeSBryan Venteicher struct virtio_console_config concfg; 14566f744ddeSBryan Venteicher 14576f744ddeSBryan Venteicher KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE, 14586f744ddeSBryan Venteicher ("%s: size feature not negotiated", __func__)); 14596f744ddeSBryan Venteicher 14606f744ddeSBryan Venteicher vtcon_read_config(sc, &concfg); 14616f744ddeSBryan Venteicher 14626f744ddeSBryan Venteicher *cols = concfg.cols; 14636f744ddeSBryan Venteicher *rows = concfg.rows; 14646f744ddeSBryan Venteicher } 14656f744ddeSBryan Venteicher 14666f744ddeSBryan Venteicher static void 14676f744ddeSBryan Venteicher vtcon_enable_interrupts(struct vtcon_softc *sc) 14686f744ddeSBryan Venteicher { 146904434c94SBryan Venteicher struct vtcon_softc_port *scport; 14706f744ddeSBryan Venteicher struct vtcon_port *port; 147104434c94SBryan Venteicher int i; 147204434c94SBryan Venteicher 147304434c94SBryan Venteicher VTCON_LOCK(sc); 14746f744ddeSBryan Venteicher 14756f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 14766f744ddeSBryan Venteicher virtqueue_enable_intr(sc->vtcon_ctrl_rxvq); 14776f744ddeSBryan Venteicher 147804434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 147904434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 148004434c94SBryan Venteicher 148104434c94SBryan Venteicher port = scport->vcsp_port; 148204434c94SBryan Venteicher if (port == NULL) 148304434c94SBryan Venteicher continue; 148404434c94SBryan Venteicher 148504434c94SBryan Venteicher VTCON_PORT_LOCK(port); 14866f744ddeSBryan Venteicher vtcon_port_enable_intr(port); 148704434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 148804434c94SBryan Venteicher } 148904434c94SBryan Venteicher 149004434c94SBryan Venteicher VTCON_UNLOCK(sc); 14916f744ddeSBryan Venteicher } 14926f744ddeSBryan Venteicher 14936f744ddeSBryan Venteicher static void 14946f744ddeSBryan Venteicher vtcon_disable_interrupts(struct vtcon_softc *sc) 14956f744ddeSBryan Venteicher { 149604434c94SBryan Venteicher struct vtcon_softc_port *scport; 14976f744ddeSBryan Venteicher struct vtcon_port *port; 149804434c94SBryan Venteicher int i; 149904434c94SBryan Venteicher 150004434c94SBryan Venteicher VTCON_LOCK_ASSERT(sc); 15016f744ddeSBryan Venteicher 15026f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 15036f744ddeSBryan Venteicher virtqueue_disable_intr(sc->vtcon_ctrl_rxvq); 15046f744ddeSBryan Venteicher 150504434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 150604434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 150704434c94SBryan Venteicher 150804434c94SBryan Venteicher port = scport->vcsp_port; 150904434c94SBryan Venteicher if (port == NULL) 151004434c94SBryan Venteicher continue; 151104434c94SBryan Venteicher 151204434c94SBryan Venteicher VTCON_PORT_LOCK(port); 15136f744ddeSBryan Venteicher vtcon_port_disable_intr(port); 151404434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 151504434c94SBryan Venteicher } 15166f744ddeSBryan Venteicher } 1517