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> 336f744ddeSBryan Venteicher #include <sys/systm.h> 346f744ddeSBryan Venteicher #include <sys/kernel.h> 356f744ddeSBryan Venteicher #include <sys/malloc.h> 366f744ddeSBryan Venteicher #include <sys/module.h> 376f744ddeSBryan Venteicher #include <sys/lock.h> 386f744ddeSBryan Venteicher #include <sys/mutex.h> 396f744ddeSBryan Venteicher #include <sys/sglist.h> 406f744ddeSBryan Venteicher #include <sys/sysctl.h> 416f744ddeSBryan Venteicher #include <sys/taskqueue.h> 426f744ddeSBryan Venteicher #include <sys/queue.h> 436f744ddeSBryan Venteicher 446f744ddeSBryan Venteicher #include <sys/conf.h> 456f744ddeSBryan Venteicher #include <sys/cons.h> 466f744ddeSBryan Venteicher #include <sys/tty.h> 476f744ddeSBryan Venteicher 486f744ddeSBryan Venteicher #include <machine/bus.h> 496f744ddeSBryan Venteicher #include <machine/resource.h> 506f744ddeSBryan Venteicher #include <sys/bus.h> 516f744ddeSBryan Venteicher 526f744ddeSBryan Venteicher #include <dev/virtio/virtio.h> 536f744ddeSBryan Venteicher #include <dev/virtio/virtqueue.h> 546f744ddeSBryan Venteicher #include <dev/virtio/console/virtio_console.h> 556f744ddeSBryan Venteicher 566f744ddeSBryan Venteicher #include "virtio_if.h" 576f744ddeSBryan Venteicher 58*04434c94SBryan Venteicher #define VTCON_MAX_PORTS 32 596f744ddeSBryan Venteicher #define VTCON_TTY_PREFIX "V" 606f744ddeSBryan Venteicher #define VTCON_BULK_BUFSZ 128 616f744ddeSBryan Venteicher 62*04434c94SBryan Venteicher /* 63*04434c94SBryan Venteicher * The buffer cannot cross more than one page boundary due to the 64*04434c94SBryan Venteicher * size of the sglist segment array used. 65*04434c94SBryan Venteicher */ 66*04434c94SBryan Venteicher CTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE); 67*04434c94SBryan Venteicher 686f744ddeSBryan Venteicher struct vtcon_softc; 69*04434c94SBryan Venteicher struct vtcon_softc_port; 706f744ddeSBryan Venteicher 716f744ddeSBryan Venteicher struct vtcon_port { 726f744ddeSBryan Venteicher struct mtx vtcport_mtx; 73*04434c94SBryan Venteicher struct vtcon_softc *vtcport_sc; 74*04434c94SBryan Venteicher struct vtcon_softc_port *vtcport_scport; 756f744ddeSBryan Venteicher struct tty *vtcport_tty; 766f744ddeSBryan Venteicher struct virtqueue *vtcport_invq; 776f744ddeSBryan Venteicher struct virtqueue *vtcport_outvq; 78*04434c94SBryan Venteicher int vtcport_id; 79*04434c94SBryan Venteicher int vtcport_flags; 80*04434c94SBryan Venteicher #define VTCON_PORT_FLAG_GONE 0x01 816f744ddeSBryan Venteicher }; 826f744ddeSBryan Venteicher 83*04434c94SBryan Venteicher #define VTCON_PORT_LOCK(_port) mtx_lock(&(_port)->vtcport_mtx) 84*04434c94SBryan Venteicher #define VTCON_PORT_UNLOCK(_port) mtx_unlock(&(_port)->vtcport_mtx) 85*04434c94SBryan Venteicher 86*04434c94SBryan Venteicher struct vtcon_softc_port { 87*04434c94SBryan Venteicher struct vtcon_softc *vcsp_sc; 88*04434c94SBryan Venteicher struct vtcon_port *vcsp_port; 89*04434c94SBryan Venteicher struct virtqueue *vcsp_invq; 90*04434c94SBryan Venteicher struct virtqueue *vcsp_outvq; 91*04434c94SBryan Venteicher }; 926f744ddeSBryan Venteicher 936f744ddeSBryan Venteicher struct vtcon_softc { 946f744ddeSBryan Venteicher device_t vtcon_dev; 956f744ddeSBryan Venteicher struct mtx vtcon_mtx; 966f744ddeSBryan Venteicher uint64_t vtcon_features; 976f744ddeSBryan Venteicher uint32_t vtcon_flags; 986f744ddeSBryan Venteicher #define VTCON_FLAG_DETACHED 0x0001 996f744ddeSBryan Venteicher #define VTCON_FLAG_SIZE 0x0010 1006f744ddeSBryan Venteicher #define VTCON_FLAG_MULTIPORT 0x0020 1016f744ddeSBryan Venteicher 1026f744ddeSBryan Venteicher struct task vtcon_ctrl_task; 1036f744ddeSBryan Venteicher struct virtqueue *vtcon_ctrl_rxvq; 1046f744ddeSBryan Venteicher struct virtqueue *vtcon_ctrl_txvq; 105*04434c94SBryan Venteicher struct mtx vtcon_ctrl_tx_mtx; 1066f744ddeSBryan Venteicher 1076f744ddeSBryan Venteicher uint32_t vtcon_max_ports; 1086f744ddeSBryan Venteicher 1096f744ddeSBryan Venteicher /* 1106f744ddeSBryan Venteicher * Ports can be added and removed during runtime, but we have 1116f744ddeSBryan Venteicher * to allocate all the virtqueues during attach. This array is 1126f744ddeSBryan Venteicher * indexed by the port ID. 1136f744ddeSBryan Venteicher */ 114*04434c94SBryan Venteicher struct vtcon_softc_port *vtcon_ports; 1156f744ddeSBryan Venteicher }; 1166f744ddeSBryan Venteicher 117*04434c94SBryan Venteicher #define VTCON_LOCK(_sc) mtx_lock(&(_sc)->vtcon_mtx) 118*04434c94SBryan Venteicher #define VTCON_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_mtx) 119*04434c94SBryan Venteicher #define VTCON_LOCK_ASSERT(_sc) \ 120*04434c94SBryan Venteicher mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED) 1216f744ddeSBryan Venteicher #define VTCON_LOCK_ASSERT_NOTOWNED(_sc) \ 122*04434c94SBryan Venteicher mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED) 123*04434c94SBryan Venteicher 124*04434c94SBryan Venteicher #define VTCON_CTRL_TX_LOCK(_sc) mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx) 125*04434c94SBryan Venteicher #define VTCON_CTRL_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx) 1266f744ddeSBryan Venteicher 1276f744ddeSBryan Venteicher #define VTCON_ASSERT_VALID_PORTID(_sc, _id) \ 1286f744ddeSBryan Venteicher KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports, \ 1296f744ddeSBryan Venteicher ("%s: port ID %d out of range", __func__, _id)) 1306f744ddeSBryan Venteicher 131*04434c94SBryan Venteicher #define VTCON_FEATURES VIRTIO_CONSOLE_F_MULTIPORT 1326f744ddeSBryan Venteicher 1336f744ddeSBryan Venteicher static struct virtio_feature_desc vtcon_feature_desc[] = { 1346f744ddeSBryan Venteicher { VIRTIO_CONSOLE_F_SIZE, "ConsoleSize" }, 1356f744ddeSBryan Venteicher { VIRTIO_CONSOLE_F_MULTIPORT, "MultiplePorts" }, 1366f744ddeSBryan Venteicher 1376f744ddeSBryan Venteicher { 0, NULL } 1386f744ddeSBryan Venteicher }; 1396f744ddeSBryan Venteicher 1406f744ddeSBryan Venteicher static int vtcon_modevent(module_t, int, void *); 141*04434c94SBryan Venteicher static void vtcon_drain_all(void); 1426f744ddeSBryan Venteicher 1436f744ddeSBryan Venteicher static int vtcon_probe(device_t); 1446f744ddeSBryan Venteicher static int vtcon_attach(device_t); 1456f744ddeSBryan Venteicher static int vtcon_detach(device_t); 1466f744ddeSBryan Venteicher static int vtcon_config_change(device_t); 1476f744ddeSBryan Venteicher 148*04434c94SBryan Venteicher static void vtcon_setup_features(struct vtcon_softc *); 1496f744ddeSBryan Venteicher static void vtcon_negotiate_features(struct vtcon_softc *); 150*04434c94SBryan Venteicher static int vtcon_alloc_scports(struct vtcon_softc *); 1516f744ddeSBryan Venteicher static int vtcon_alloc_virtqueues(struct vtcon_softc *); 1526f744ddeSBryan Venteicher static void vtcon_read_config(struct vtcon_softc *, 1536f744ddeSBryan Venteicher struct virtio_console_config *); 1546f744ddeSBryan Venteicher 1556f744ddeSBryan Venteicher static void vtcon_determine_max_ports(struct vtcon_softc *, 1566f744ddeSBryan Venteicher struct virtio_console_config *); 157*04434c94SBryan Venteicher static void vtcon_destroy_ports(struct vtcon_softc *); 1586f744ddeSBryan Venteicher static void vtcon_stop(struct vtcon_softc *); 1596f744ddeSBryan Venteicher 160*04434c94SBryan Venteicher static int vtcon_ctrl_event_enqueue(struct vtcon_softc *, 1616f744ddeSBryan Venteicher struct virtio_console_control *); 162*04434c94SBryan Venteicher static int vtcon_ctrl_event_create(struct vtcon_softc *); 163*04434c94SBryan Venteicher static void vtcon_ctrl_event_requeue(struct vtcon_softc *, 1646f744ddeSBryan Venteicher struct virtio_console_control *); 165*04434c94SBryan Venteicher static int vtcon_ctrl_event_populate(struct vtcon_softc *); 166*04434c94SBryan Venteicher static void vtcon_ctrl_event_drain(struct vtcon_softc *); 1676f744ddeSBryan Venteicher static int vtcon_ctrl_init(struct vtcon_softc *); 1686f744ddeSBryan Venteicher static void vtcon_ctrl_deinit(struct vtcon_softc *); 1696f744ddeSBryan Venteicher static void vtcon_ctrl_port_add_event(struct vtcon_softc *, int); 1706f744ddeSBryan Venteicher static void vtcon_ctrl_port_remove_event(struct vtcon_softc *, int); 1716f744ddeSBryan Venteicher static void vtcon_ctrl_port_console_event(struct vtcon_softc *, int); 1726f744ddeSBryan Venteicher static void vtcon_ctrl_port_open_event(struct vtcon_softc *, int); 173*04434c94SBryan Venteicher static void vtcon_ctrl_process_event(struct vtcon_softc *, 1746f744ddeSBryan Venteicher struct virtio_console_control *); 1756f744ddeSBryan Venteicher static void vtcon_ctrl_task_cb(void *, int); 176*04434c94SBryan Venteicher static void vtcon_ctrl_event_intr(void *); 177*04434c94SBryan Venteicher static void vtcon_ctrl_poll(struct vtcon_softc *, 178*04434c94SBryan Venteicher struct virtio_console_control *control); 179*04434c94SBryan Venteicher static void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t, 180*04434c94SBryan Venteicher uint16_t, uint16_t); 1816f744ddeSBryan Venteicher 182*04434c94SBryan Venteicher static int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t); 183*04434c94SBryan Venteicher static int vtcon_port_create_buf(struct vtcon_port *); 184*04434c94SBryan Venteicher static void vtcon_port_requeue_buf(struct vtcon_port *, void *); 1856f744ddeSBryan Venteicher static int vtcon_port_populate(struct vtcon_port *); 1866f744ddeSBryan Venteicher static void vtcon_port_destroy(struct vtcon_port *); 187*04434c94SBryan Venteicher static int vtcon_port_create(struct vtcon_softc *, int); 188*04434c94SBryan Venteicher static void vtcon_port_drain_bufs(struct virtqueue *); 189*04434c94SBryan Venteicher static void vtcon_port_drain(struct vtcon_port *); 190*04434c94SBryan Venteicher static void vtcon_port_teardown(struct vtcon_port *); 1916f744ddeSBryan Venteicher static void vtcon_port_change_size(struct vtcon_port *, uint16_t, 1926f744ddeSBryan Venteicher uint16_t); 193*04434c94SBryan Venteicher static void vtcon_port_update_console_size(struct vtcon_softc *); 1946f744ddeSBryan Venteicher static void vtcon_port_enable_intr(struct vtcon_port *); 1956f744ddeSBryan Venteicher static void vtcon_port_disable_intr(struct vtcon_port *); 196*04434c94SBryan Venteicher static void vtcon_port_in(struct vtcon_port *); 197*04434c94SBryan Venteicher static void vtcon_port_intr(void *); 198*04434c94SBryan Venteicher static void vtcon_port_out(struct vtcon_port *, void *, int); 199*04434c94SBryan Venteicher static void vtcon_port_submit_event(struct vtcon_port *, uint16_t, 2006f744ddeSBryan Venteicher uint16_t); 2016f744ddeSBryan Venteicher 2026f744ddeSBryan Venteicher static int vtcon_tty_open(struct tty *); 2036f744ddeSBryan Venteicher static void vtcon_tty_close(struct tty *); 2046f744ddeSBryan Venteicher static void vtcon_tty_outwakeup(struct tty *); 2056f744ddeSBryan Venteicher static void vtcon_tty_free(void *); 2066f744ddeSBryan Venteicher 2076f744ddeSBryan Venteicher static void vtcon_get_console_size(struct vtcon_softc *, uint16_t *, 2086f744ddeSBryan Venteicher uint16_t *); 2096f744ddeSBryan Venteicher 2106f744ddeSBryan Venteicher static void vtcon_enable_interrupts(struct vtcon_softc *); 2116f744ddeSBryan Venteicher static void vtcon_disable_interrupts(struct vtcon_softc *); 2126f744ddeSBryan Venteicher 2136f744ddeSBryan Venteicher static int vtcon_pending_free; 2146f744ddeSBryan Venteicher 2156f744ddeSBryan Venteicher static struct ttydevsw vtcon_tty_class = { 2166f744ddeSBryan Venteicher .tsw_flags = 0, 2176f744ddeSBryan Venteicher .tsw_open = vtcon_tty_open, 2186f744ddeSBryan Venteicher .tsw_close = vtcon_tty_close, 2196f744ddeSBryan Venteicher .tsw_outwakeup = vtcon_tty_outwakeup, 2206f744ddeSBryan Venteicher .tsw_free = vtcon_tty_free, 2216f744ddeSBryan Venteicher }; 2226f744ddeSBryan Venteicher 2236f744ddeSBryan Venteicher static device_method_t vtcon_methods[] = { 2246f744ddeSBryan Venteicher /* Device methods. */ 2256f744ddeSBryan Venteicher DEVMETHOD(device_probe, vtcon_probe), 2266f744ddeSBryan Venteicher DEVMETHOD(device_attach, vtcon_attach), 2276f744ddeSBryan Venteicher DEVMETHOD(device_detach, vtcon_detach), 2286f744ddeSBryan Venteicher 2296f744ddeSBryan Venteicher /* VirtIO methods. */ 2306f744ddeSBryan Venteicher DEVMETHOD(virtio_config_change, vtcon_config_change), 2316f744ddeSBryan Venteicher 2326f744ddeSBryan Venteicher DEVMETHOD_END 2336f744ddeSBryan Venteicher }; 2346f744ddeSBryan Venteicher 2356f744ddeSBryan Venteicher static driver_t vtcon_driver = { 2366f744ddeSBryan Venteicher "vtcon", 2376f744ddeSBryan Venteicher vtcon_methods, 2386f744ddeSBryan Venteicher sizeof(struct vtcon_softc) 2396f744ddeSBryan Venteicher }; 2406f744ddeSBryan Venteicher static devclass_t vtcon_devclass; 2416f744ddeSBryan Venteicher 2426f744ddeSBryan Venteicher DRIVER_MODULE(virtio_console, virtio_pci, vtcon_driver, vtcon_devclass, 2436f744ddeSBryan Venteicher vtcon_modevent, 0); 2446f744ddeSBryan Venteicher MODULE_VERSION(virtio_console, 1); 2456f744ddeSBryan Venteicher MODULE_DEPEND(virtio_console, virtio, 1, 1, 1); 2466f744ddeSBryan Venteicher 2476f744ddeSBryan Venteicher static int 2486f744ddeSBryan Venteicher vtcon_modevent(module_t mod, int type, void *unused) 2496f744ddeSBryan Venteicher { 2506f744ddeSBryan Venteicher int error; 2516f744ddeSBryan Venteicher 2526f744ddeSBryan Venteicher switch (type) { 2536f744ddeSBryan Venteicher case MOD_LOAD: 2546f744ddeSBryan Venteicher error = 0; 2556f744ddeSBryan Venteicher break; 2566f744ddeSBryan Venteicher case MOD_QUIESCE: 257*04434c94SBryan Venteicher error = 0; 258*04434c94SBryan Venteicher break; 2596f744ddeSBryan Venteicher case MOD_UNLOAD: 260*04434c94SBryan Venteicher vtcon_drain_all(); 261*04434c94SBryan Venteicher error = 0; 2626f744ddeSBryan Venteicher break; 2636f744ddeSBryan Venteicher case MOD_SHUTDOWN: 2646f744ddeSBryan Venteicher error = 0; 2656f744ddeSBryan Venteicher break; 2666f744ddeSBryan Venteicher default: 2676f744ddeSBryan Venteicher error = EOPNOTSUPP; 2686f744ddeSBryan Venteicher break; 2696f744ddeSBryan Venteicher } 2706f744ddeSBryan Venteicher 2716f744ddeSBryan Venteicher return (error); 2726f744ddeSBryan Venteicher } 2736f744ddeSBryan Venteicher 274*04434c94SBryan Venteicher static void 275*04434c94SBryan Venteicher vtcon_drain_all(void) 276*04434c94SBryan Venteicher { 277*04434c94SBryan Venteicher int first; 278*04434c94SBryan Venteicher 279*04434c94SBryan Venteicher for (first = 1; vtcon_pending_free != 0; first = 0) { 280*04434c94SBryan Venteicher if (first != 0) { 281*04434c94SBryan Venteicher printf("virtio_console: Waiting for all detached TTY " 282*04434c94SBryan Venteicher "devices to have open fds closed.\n"); 283*04434c94SBryan Venteicher } 284*04434c94SBryan Venteicher pause("vtcondra", hz); 285*04434c94SBryan Venteicher } 286*04434c94SBryan Venteicher } 287*04434c94SBryan Venteicher 2886f744ddeSBryan Venteicher static int 2896f744ddeSBryan Venteicher vtcon_probe(device_t dev) 2906f744ddeSBryan Venteicher { 2916f744ddeSBryan Venteicher 2926f744ddeSBryan Venteicher if (virtio_get_device_type(dev) != VIRTIO_ID_CONSOLE) 2936f744ddeSBryan Venteicher return (ENXIO); 2946f744ddeSBryan Venteicher 2956f744ddeSBryan Venteicher device_set_desc(dev, "VirtIO Console Adapter"); 2966f744ddeSBryan Venteicher 2976f744ddeSBryan Venteicher return (BUS_PROBE_DEFAULT); 2986f744ddeSBryan Venteicher } 2996f744ddeSBryan Venteicher 3006f744ddeSBryan Venteicher static int 3016f744ddeSBryan Venteicher vtcon_attach(device_t dev) 3026f744ddeSBryan Venteicher { 3036f744ddeSBryan Venteicher struct vtcon_softc *sc; 3046f744ddeSBryan Venteicher struct virtio_console_config concfg; 3056f744ddeSBryan Venteicher int error; 3066f744ddeSBryan Venteicher 3076f744ddeSBryan Venteicher sc = device_get_softc(dev); 3086f744ddeSBryan Venteicher sc->vtcon_dev = dev; 3096f744ddeSBryan Venteicher 310*04434c94SBryan Venteicher mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF); 311*04434c94SBryan Venteicher mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF); 3126f744ddeSBryan Venteicher 3136f744ddeSBryan Venteicher virtio_set_feature_desc(dev, vtcon_feature_desc); 314*04434c94SBryan Venteicher vtcon_setup_features(sc); 3156f744ddeSBryan Venteicher 3166f744ddeSBryan Venteicher vtcon_read_config(sc, &concfg); 3176f744ddeSBryan Venteicher vtcon_determine_max_ports(sc, &concfg); 3186f744ddeSBryan Venteicher 319*04434c94SBryan Venteicher error = vtcon_alloc_scports(sc); 320*04434c94SBryan Venteicher if (error) { 321*04434c94SBryan Venteicher device_printf(dev, "cannot allocate softc port structures\n"); 322*04434c94SBryan Venteicher goto fail; 323*04434c94SBryan Venteicher } 324*04434c94SBryan Venteicher 3256f744ddeSBryan Venteicher error = vtcon_alloc_virtqueues(sc); 3266f744ddeSBryan Venteicher if (error) { 3276f744ddeSBryan Venteicher device_printf(dev, "cannot allocate virtqueues\n"); 3286f744ddeSBryan Venteicher goto fail; 3296f744ddeSBryan Venteicher } 3306f744ddeSBryan Venteicher 331*04434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 332*04434c94SBryan Venteicher TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc); 3336f744ddeSBryan Venteicher error = vtcon_ctrl_init(sc); 334*04434c94SBryan Venteicher } else 335*04434c94SBryan Venteicher error = vtcon_port_create(sc, 0); 3366f744ddeSBryan Venteicher if (error) 3376f744ddeSBryan Venteicher goto fail; 3386f744ddeSBryan Venteicher 3396f744ddeSBryan Venteicher error = virtio_setup_intr(dev, INTR_TYPE_TTY); 3406f744ddeSBryan Venteicher if (error) { 3416f744ddeSBryan Venteicher device_printf(dev, "cannot setup virtqueue interrupts\n"); 3426f744ddeSBryan Venteicher goto fail; 3436f744ddeSBryan Venteicher } 3446f744ddeSBryan Venteicher 3456f744ddeSBryan Venteicher vtcon_enable_interrupts(sc); 3466f744ddeSBryan Venteicher 347*04434c94SBryan Venteicher vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID, 3486f744ddeSBryan Venteicher VIRTIO_CONSOLE_DEVICE_READY, 1); 3496f744ddeSBryan Venteicher 3506f744ddeSBryan Venteicher fail: 3516f744ddeSBryan Venteicher if (error) 3526f744ddeSBryan Venteicher vtcon_detach(dev); 3536f744ddeSBryan Venteicher 3546f744ddeSBryan Venteicher return (error); 3556f744ddeSBryan Venteicher } 3566f744ddeSBryan Venteicher 3576f744ddeSBryan Venteicher static int 3586f744ddeSBryan Venteicher vtcon_detach(device_t dev) 3596f744ddeSBryan Venteicher { 3606f744ddeSBryan Venteicher struct vtcon_softc *sc; 3616f744ddeSBryan Venteicher 3626f744ddeSBryan Venteicher sc = device_get_softc(dev); 3636f744ddeSBryan Venteicher 3646f744ddeSBryan Venteicher VTCON_LOCK(sc); 3656f744ddeSBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_DETACHED; 3666f744ddeSBryan Venteicher if (device_is_attached(dev)) 3676f744ddeSBryan Venteicher vtcon_stop(sc); 3686f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 3696f744ddeSBryan Venteicher 370*04434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 3716f744ddeSBryan Venteicher taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task); 3726f744ddeSBryan Venteicher vtcon_ctrl_deinit(sc); 373*04434c94SBryan Venteicher } 3746f744ddeSBryan Venteicher 375*04434c94SBryan Venteicher vtcon_destroy_ports(sc); 376*04434c94SBryan Venteicher mtx_destroy(&sc->vtcon_mtx); 377*04434c94SBryan Venteicher mtx_destroy(&sc->vtcon_ctrl_tx_mtx); 3786f744ddeSBryan Venteicher 3796f744ddeSBryan Venteicher return (0); 3806f744ddeSBryan Venteicher } 3816f744ddeSBryan Venteicher 3826f744ddeSBryan Venteicher static int 3836f744ddeSBryan Venteicher vtcon_config_change(device_t dev) 3846f744ddeSBryan Venteicher { 3856f744ddeSBryan Venteicher struct vtcon_softc *sc; 3866f744ddeSBryan Venteicher 3876f744ddeSBryan Venteicher sc = device_get_softc(dev); 3886f744ddeSBryan Venteicher 3896f744ddeSBryan Venteicher /* 390*04434c94SBryan Venteicher * When the multiport feature is negotiated, all configuration 391*04434c94SBryan Venteicher * changes are done through control virtqueue events. 3926f744ddeSBryan Venteicher */ 393*04434c94SBryan Venteicher if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) { 394*04434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_SIZE) 395*04434c94SBryan Venteicher vtcon_port_update_console_size(sc); 3966f744ddeSBryan Venteicher } 3976f744ddeSBryan Venteicher 3986f744ddeSBryan Venteicher return (0); 3996f744ddeSBryan Venteicher } 4006f744ddeSBryan Venteicher 4016f744ddeSBryan Venteicher static void 4026f744ddeSBryan Venteicher vtcon_negotiate_features(struct vtcon_softc *sc) 4036f744ddeSBryan Venteicher { 4046f744ddeSBryan Venteicher device_t dev; 4056f744ddeSBryan Venteicher uint64_t features; 4066f744ddeSBryan Venteicher 4076f744ddeSBryan Venteicher dev = sc->vtcon_dev; 4086f744ddeSBryan Venteicher features = VTCON_FEATURES; 4096f744ddeSBryan Venteicher 4106f744ddeSBryan Venteicher sc->vtcon_features = virtio_negotiate_features(dev, features); 4116f744ddeSBryan Venteicher } 4126f744ddeSBryan Venteicher 413*04434c94SBryan Venteicher static void 414*04434c94SBryan Venteicher vtcon_setup_features(struct vtcon_softc *sc) 415*04434c94SBryan Venteicher { 416*04434c94SBryan Venteicher device_t dev; 417*04434c94SBryan Venteicher 418*04434c94SBryan Venteicher dev = sc->vtcon_dev; 419*04434c94SBryan Venteicher 420*04434c94SBryan Venteicher vtcon_negotiate_features(sc); 421*04434c94SBryan Venteicher 422*04434c94SBryan Venteicher if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE)) 423*04434c94SBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_SIZE; 424*04434c94SBryan Venteicher if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT)) 425*04434c94SBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_MULTIPORT; 426*04434c94SBryan Venteicher } 427*04434c94SBryan Venteicher 4286f744ddeSBryan Venteicher #define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg) \ 4296f744ddeSBryan Venteicher if (virtio_with_feature(_dev, _feature)) { \ 4306f744ddeSBryan Venteicher virtio_read_device_config(_dev, \ 4316f744ddeSBryan Venteicher offsetof(struct virtio_console_config, _field), \ 4326f744ddeSBryan Venteicher &(_cfg)->_field, sizeof((_cfg)->_field)); \ 4336f744ddeSBryan Venteicher } 4346f744ddeSBryan Venteicher 4356f744ddeSBryan Venteicher static void 4366f744ddeSBryan Venteicher vtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg) 4376f744ddeSBryan Venteicher { 4386f744ddeSBryan Venteicher device_t dev; 4396f744ddeSBryan Venteicher 4406f744ddeSBryan Venteicher dev = sc->vtcon_dev; 4416f744ddeSBryan Venteicher 4426f744ddeSBryan Venteicher bzero(concfg, sizeof(struct virtio_console_config)); 4436f744ddeSBryan Venteicher 4446f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg); 4456f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg); 4466f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg); 4476f744ddeSBryan Venteicher } 4486f744ddeSBryan Venteicher 4496f744ddeSBryan Venteicher #undef VTCON_GET_CONFIG 4506f744ddeSBryan Venteicher 4516f744ddeSBryan Venteicher static int 452*04434c94SBryan Venteicher vtcon_alloc_scports(struct vtcon_softc *sc) 453*04434c94SBryan Venteicher { 454*04434c94SBryan Venteicher struct vtcon_softc_port *scport; 455*04434c94SBryan Venteicher int max, i; 456*04434c94SBryan Venteicher 457*04434c94SBryan Venteicher max = sc->vtcon_max_ports; 458*04434c94SBryan Venteicher 459*04434c94SBryan Venteicher sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max, 460*04434c94SBryan Venteicher M_DEVBUF, M_NOWAIT | M_ZERO); 461*04434c94SBryan Venteicher if (sc->vtcon_ports == NULL) 462*04434c94SBryan Venteicher return (ENOMEM); 463*04434c94SBryan Venteicher 464*04434c94SBryan Venteicher for (i = 0; i < max; i++) { 465*04434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 466*04434c94SBryan Venteicher scport->vcsp_sc = sc; 467*04434c94SBryan Venteicher } 468*04434c94SBryan Venteicher 469*04434c94SBryan Venteicher return (0); 470*04434c94SBryan Venteicher } 471*04434c94SBryan Venteicher 472*04434c94SBryan Venteicher static int 4736f744ddeSBryan Venteicher vtcon_alloc_virtqueues(struct vtcon_softc *sc) 4746f744ddeSBryan Venteicher { 4756f744ddeSBryan Venteicher device_t dev; 4766f744ddeSBryan Venteicher struct vq_alloc_info *info; 477*04434c94SBryan Venteicher struct vtcon_softc_port *scport; 4786f744ddeSBryan Venteicher int i, idx, portidx, nvqs, error; 4796f744ddeSBryan Venteicher 4806f744ddeSBryan Venteicher dev = sc->vtcon_dev; 4816f744ddeSBryan Venteicher 4826f744ddeSBryan Venteicher nvqs = sc->vtcon_max_ports * 2; 4836f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 4846f744ddeSBryan Venteicher nvqs += 2; 4856f744ddeSBryan Venteicher 4866f744ddeSBryan Venteicher info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT); 4876f744ddeSBryan Venteicher if (info == NULL) 4886f744ddeSBryan Venteicher return (ENOMEM); 4896f744ddeSBryan Venteicher 4906f744ddeSBryan Venteicher for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) { 4916f744ddeSBryan Venteicher 4926f744ddeSBryan Venteicher if (i == 1) { 4936f744ddeSBryan Venteicher /* The control virtqueues are after the first port. */ 4946f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx], 0, 495*04434c94SBryan Venteicher vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq, 4966f744ddeSBryan Venteicher "%s-control rx", device_get_nameunit(dev)); 4976f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx+1], 0, 4986f744ddeSBryan Venteicher NULL, sc, &sc->vtcon_ctrl_txvq, 4996f744ddeSBryan Venteicher "%s-control tx", device_get_nameunit(dev)); 5006f744ddeSBryan Venteicher continue; 5016f744ddeSBryan Venteicher } 5026f744ddeSBryan Venteicher 503*04434c94SBryan Venteicher scport = &sc->vtcon_ports[portidx]; 5046f744ddeSBryan Venteicher 505*04434c94SBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr, 506*04434c94SBryan Venteicher scport, &scport->vcsp_invq, "%s-port%d in", 507*04434c94SBryan Venteicher device_get_nameunit(dev), i); 5086f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL, 509*04434c94SBryan Venteicher NULL, &scport->vcsp_outvq, "%s-port%d out", 510*04434c94SBryan Venteicher device_get_nameunit(dev), i); 5116f744ddeSBryan Venteicher 5126f744ddeSBryan Venteicher portidx++; 5136f744ddeSBryan Venteicher } 5146f744ddeSBryan Venteicher 5156f744ddeSBryan Venteicher error = virtio_alloc_virtqueues(dev, 0, nvqs, info); 5166f744ddeSBryan Venteicher free(info, M_TEMP); 5176f744ddeSBryan Venteicher 5186f744ddeSBryan Venteicher return (error); 5196f744ddeSBryan Venteicher } 5206f744ddeSBryan Venteicher 5216f744ddeSBryan Venteicher static void 5226f744ddeSBryan Venteicher vtcon_determine_max_ports(struct vtcon_softc *sc, 5236f744ddeSBryan Venteicher struct virtio_console_config *concfg) 5246f744ddeSBryan Venteicher { 5256f744ddeSBryan Venteicher 5266f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 5276f744ddeSBryan Venteicher sc->vtcon_max_ports = 5286f744ddeSBryan Venteicher min(concfg->max_nr_ports, VTCON_MAX_PORTS); 5296f744ddeSBryan Venteicher if (sc->vtcon_max_ports == 0) 5306f744ddeSBryan Venteicher sc->vtcon_max_ports = 1; 5316f744ddeSBryan Venteicher } else 5326f744ddeSBryan Venteicher sc->vtcon_max_ports = 1; 5336f744ddeSBryan Venteicher } 5346f744ddeSBryan Venteicher 5356f744ddeSBryan Venteicher static void 536*04434c94SBryan Venteicher vtcon_destroy_ports(struct vtcon_softc *sc) 5376f744ddeSBryan Venteicher { 538*04434c94SBryan Venteicher struct vtcon_softc_port *scport; 539*04434c94SBryan Venteicher struct vtcon_port *port; 540*04434c94SBryan Venteicher struct virtqueue *vq; 541*04434c94SBryan Venteicher int i; 5426f744ddeSBryan Venteicher 543*04434c94SBryan Venteicher if (sc->vtcon_ports == NULL) 544*04434c94SBryan Venteicher return; 545*04434c94SBryan Venteicher 546*04434c94SBryan Venteicher VTCON_LOCK(sc); 547*04434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 548*04434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 549*04434c94SBryan Venteicher 550*04434c94SBryan Venteicher port = scport->vcsp_port; 551*04434c94SBryan Venteicher if (port != NULL) { 552*04434c94SBryan Venteicher scport->vcsp_port = NULL; 553*04434c94SBryan Venteicher VTCON_PORT_LOCK(port); 554*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 555*04434c94SBryan Venteicher vtcon_port_teardown(port); 556*04434c94SBryan Venteicher VTCON_LOCK(sc); 5576f744ddeSBryan Venteicher } 5586f744ddeSBryan Venteicher 559*04434c94SBryan Venteicher vq = scport->vcsp_invq; 560*04434c94SBryan Venteicher if (vq != NULL) 561*04434c94SBryan Venteicher vtcon_port_drain_bufs(vq); 5626f744ddeSBryan Venteicher } 563*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 564*04434c94SBryan Venteicher 565*04434c94SBryan Venteicher free(sc->vtcon_ports, M_DEVBUF); 566*04434c94SBryan Venteicher sc->vtcon_ports = NULL; 5676f744ddeSBryan Venteicher } 5686f744ddeSBryan Venteicher 5696f744ddeSBryan Venteicher static void 5706f744ddeSBryan Venteicher vtcon_stop(struct vtcon_softc *sc) 5716f744ddeSBryan Venteicher { 5726f744ddeSBryan Venteicher 5736f744ddeSBryan Venteicher vtcon_disable_interrupts(sc); 5746f744ddeSBryan Venteicher virtio_stop(sc->vtcon_dev); 5756f744ddeSBryan Venteicher } 5766f744ddeSBryan Venteicher 5776f744ddeSBryan Venteicher static int 578*04434c94SBryan Venteicher vtcon_ctrl_event_enqueue(struct vtcon_softc *sc, 5796f744ddeSBryan Venteicher struct virtio_console_control *control) 5806f744ddeSBryan Venteicher { 581*04434c94SBryan Venteicher struct sglist_seg segs[2]; 5826f744ddeSBryan Venteicher struct sglist sg; 5836f744ddeSBryan Venteicher struct virtqueue *vq; 584*04434c94SBryan Venteicher int error; 5856f744ddeSBryan Venteicher 5866f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 5876f744ddeSBryan Venteicher 588*04434c94SBryan Venteicher sglist_init(&sg, 2, segs); 589*04434c94SBryan Venteicher error = sglist_append(&sg, control, 590*04434c94SBryan Venteicher sizeof(struct virtio_console_control)); 591*04434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding control to sglist", 592*04434c94SBryan Venteicher __func__, error)); 5936f744ddeSBryan Venteicher 594*04434c94SBryan Venteicher return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg)); 5956f744ddeSBryan Venteicher } 5966f744ddeSBryan Venteicher 5976f744ddeSBryan Venteicher static int 598*04434c94SBryan Venteicher vtcon_ctrl_event_create(struct vtcon_softc *sc) 5996f744ddeSBryan Venteicher { 6006f744ddeSBryan Venteicher struct virtio_console_control *control; 6016f744ddeSBryan Venteicher int error; 6026f744ddeSBryan Venteicher 603*04434c94SBryan Venteicher control = malloc(sizeof(struct virtio_console_control), M_DEVBUF, 604*04434c94SBryan Venteicher M_ZERO | M_NOWAIT); 6056f744ddeSBryan Venteicher if (control == NULL) 6066f744ddeSBryan Venteicher return (ENOMEM); 6076f744ddeSBryan Venteicher 608*04434c94SBryan Venteicher error = vtcon_ctrl_event_enqueue(sc, control); 6096f744ddeSBryan Venteicher if (error) 6106f744ddeSBryan Venteicher free(control, M_DEVBUF); 6116f744ddeSBryan Venteicher 6126f744ddeSBryan Venteicher return (error); 6136f744ddeSBryan Venteicher } 6146f744ddeSBryan Venteicher 6156f744ddeSBryan Venteicher static void 616*04434c94SBryan Venteicher vtcon_ctrl_event_requeue(struct vtcon_softc *sc, 6176f744ddeSBryan Venteicher struct virtio_console_control *control) 6186f744ddeSBryan Venteicher { 6196f744ddeSBryan Venteicher int error; 6206f744ddeSBryan Venteicher 621*04434c94SBryan Venteicher bzero(control, sizeof(struct virtio_console_control)); 6226f744ddeSBryan Venteicher 623*04434c94SBryan Venteicher error = vtcon_ctrl_event_enqueue(sc, control); 6246f744ddeSBryan Venteicher KASSERT(error == 0, 6256f744ddeSBryan Venteicher ("%s: cannot requeue control buffer %d", __func__, error)); 6266f744ddeSBryan Venteicher } 6276f744ddeSBryan Venteicher 6286f744ddeSBryan Venteicher static int 629*04434c94SBryan Venteicher vtcon_ctrl_event_populate(struct vtcon_softc *sc) 6306f744ddeSBryan Venteicher { 6316f744ddeSBryan Venteicher struct virtqueue *vq; 6326f744ddeSBryan Venteicher int nbufs, error; 6336f744ddeSBryan Venteicher 6346f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 6356f744ddeSBryan Venteicher error = ENOSPC; 6366f744ddeSBryan Venteicher 6376f744ddeSBryan Venteicher for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 638*04434c94SBryan Venteicher error = vtcon_ctrl_event_create(sc); 6396f744ddeSBryan Venteicher if (error) 6406f744ddeSBryan Venteicher break; 6416f744ddeSBryan Venteicher } 6426f744ddeSBryan Venteicher 6436f744ddeSBryan Venteicher if (nbufs > 0) { 6446f744ddeSBryan Venteicher virtqueue_notify(vq); 6456f744ddeSBryan Venteicher error = 0; 6466f744ddeSBryan Venteicher } 6476f744ddeSBryan Venteicher 6486f744ddeSBryan Venteicher return (error); 6496f744ddeSBryan Venteicher } 6506f744ddeSBryan Venteicher 6516f744ddeSBryan Venteicher static void 652*04434c94SBryan Venteicher vtcon_ctrl_event_drain(struct vtcon_softc *sc) 6536f744ddeSBryan Venteicher { 6546f744ddeSBryan Venteicher struct virtio_console_control *control; 6556f744ddeSBryan Venteicher struct virtqueue *vq; 6566f744ddeSBryan Venteicher int last; 6576f744ddeSBryan Venteicher 6586f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 6596f744ddeSBryan Venteicher last = 0; 6606f744ddeSBryan Venteicher 6616f744ddeSBryan Venteicher if (vq == NULL) 6626f744ddeSBryan Venteicher return; 6636f744ddeSBryan Venteicher 664*04434c94SBryan Venteicher VTCON_LOCK(sc); 6656f744ddeSBryan Venteicher while ((control = virtqueue_drain(vq, &last)) != NULL) 6666f744ddeSBryan Venteicher free(control, M_DEVBUF); 667*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 668*04434c94SBryan Venteicher } 669*04434c94SBryan Venteicher 670*04434c94SBryan Venteicher static int 671*04434c94SBryan Venteicher vtcon_ctrl_init(struct vtcon_softc *sc) 672*04434c94SBryan Venteicher { 673*04434c94SBryan Venteicher int error; 674*04434c94SBryan Venteicher 675*04434c94SBryan Venteicher error = vtcon_ctrl_event_populate(sc); 676*04434c94SBryan Venteicher 677*04434c94SBryan Venteicher return (error); 6786f744ddeSBryan Venteicher } 6796f744ddeSBryan Venteicher 6806f744ddeSBryan Venteicher static void 6816f744ddeSBryan Venteicher vtcon_ctrl_deinit(struct vtcon_softc *sc) 6826f744ddeSBryan Venteicher { 6836f744ddeSBryan Venteicher 684*04434c94SBryan Venteicher vtcon_ctrl_event_drain(sc); 6856f744ddeSBryan Venteicher } 6866f744ddeSBryan Venteicher 6876f744ddeSBryan Venteicher static void 6886f744ddeSBryan Venteicher vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id) 6896f744ddeSBryan Venteicher { 6906f744ddeSBryan Venteicher device_t dev; 6916f744ddeSBryan Venteicher int error; 6926f744ddeSBryan Venteicher 6936f744ddeSBryan Venteicher dev = sc->vtcon_dev; 6946f744ddeSBryan Venteicher 695*04434c94SBryan Venteicher /* This single thread only way for ports to be created. */ 696*04434c94SBryan Venteicher if (sc->vtcon_ports[id].vcsp_port != NULL) { 6976f744ddeSBryan Venteicher device_printf(dev, "%s: adding port %d, but already exists\n", 6986f744ddeSBryan Venteicher __func__, id); 6996f744ddeSBryan Venteicher return; 7006f744ddeSBryan Venteicher } 7016f744ddeSBryan Venteicher 702*04434c94SBryan Venteicher error = vtcon_port_create(sc, id); 7036f744ddeSBryan Venteicher if (error) { 7046f744ddeSBryan Venteicher device_printf(dev, "%s: cannot create port %d: %d\n", 7056f744ddeSBryan Venteicher __func__, id, error); 7066f744ddeSBryan Venteicher return; 7076f744ddeSBryan Venteicher } 7086f744ddeSBryan Venteicher } 7096f744ddeSBryan Venteicher 7106f744ddeSBryan Venteicher static void 7116f744ddeSBryan Venteicher vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id) 7126f744ddeSBryan Venteicher { 7136f744ddeSBryan Venteicher device_t dev; 714*04434c94SBryan Venteicher struct vtcon_softc_port *scport; 7156f744ddeSBryan Venteicher struct vtcon_port *port; 7166f744ddeSBryan Venteicher 7176f744ddeSBryan Venteicher dev = sc->vtcon_dev; 718*04434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 7196f744ddeSBryan Venteicher 720*04434c94SBryan Venteicher VTCON_LOCK(sc); 721*04434c94SBryan Venteicher port = scport->vcsp_port; 7226f744ddeSBryan Venteicher if (port == NULL) { 723*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 7246f744ddeSBryan Venteicher device_printf(dev, "%s: remove port %d, but does not exist\n", 7256f744ddeSBryan Venteicher __func__, id); 7266f744ddeSBryan Venteicher return; 7276f744ddeSBryan Venteicher } 7286f744ddeSBryan Venteicher 729*04434c94SBryan Venteicher scport->vcsp_port = NULL; 730*04434c94SBryan Venteicher VTCON_PORT_LOCK(port); 731*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 732*04434c94SBryan Venteicher vtcon_port_teardown(port); 7336f744ddeSBryan Venteicher } 7346f744ddeSBryan Venteicher 7356f744ddeSBryan Venteicher static void 7366f744ddeSBryan Venteicher vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id) 7376f744ddeSBryan Venteicher { 7386f744ddeSBryan Venteicher 739*04434c94SBryan Venteicher device_printf(sc->vtcon_dev, "%s: port %d console event\n", 740*04434c94SBryan Venteicher __func__, id); 7416f744ddeSBryan Venteicher } 7426f744ddeSBryan Venteicher 7436f744ddeSBryan Venteicher static void 7446f744ddeSBryan Venteicher vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id) 7456f744ddeSBryan Venteicher { 7466f744ddeSBryan Venteicher device_t dev; 747*04434c94SBryan Venteicher struct vtcon_softc_port *scport; 7486f744ddeSBryan Venteicher struct vtcon_port *port; 7496f744ddeSBryan Venteicher 7506f744ddeSBryan Venteicher dev = sc->vtcon_dev; 751*04434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 7526f744ddeSBryan Venteicher 753*04434c94SBryan Venteicher VTCON_LOCK(sc); 754*04434c94SBryan Venteicher port = scport->vcsp_port; 7556f744ddeSBryan Venteicher if (port == NULL) { 756*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 7576f744ddeSBryan Venteicher device_printf(dev, "%s: open port %d, but does not exist\n", 7586f744ddeSBryan Venteicher __func__, id); 7596f744ddeSBryan Venteicher return; 7606f744ddeSBryan Venteicher } 7616f744ddeSBryan Venteicher 762*04434c94SBryan Venteicher VTCON_PORT_LOCK(port); 763*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 7646f744ddeSBryan Venteicher vtcon_port_enable_intr(port); 765*04434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 7666f744ddeSBryan Venteicher } 7676f744ddeSBryan Venteicher 7686f744ddeSBryan Venteicher static void 769*04434c94SBryan Venteicher vtcon_ctrl_process_event(struct vtcon_softc *sc, 7706f744ddeSBryan Venteicher struct virtio_console_control *control) 7716f744ddeSBryan Venteicher { 7726f744ddeSBryan Venteicher device_t dev; 7736f744ddeSBryan Venteicher int id; 7746f744ddeSBryan Venteicher 7756f744ddeSBryan Venteicher dev = sc->vtcon_dev; 7766f744ddeSBryan Venteicher id = control->id; 7776f744ddeSBryan Venteicher 7786f744ddeSBryan Venteicher if (id < 0 || id >= sc->vtcon_max_ports) { 7796f744ddeSBryan Venteicher device_printf(dev, "%s: invalid port ID %d\n", __func__, id); 7806f744ddeSBryan Venteicher return; 7816f744ddeSBryan Venteicher } 7826f744ddeSBryan Venteicher 7836f744ddeSBryan Venteicher switch (control->event) { 7846f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_ADD: 7856f744ddeSBryan Venteicher vtcon_ctrl_port_add_event(sc, id); 7866f744ddeSBryan Venteicher break; 7876f744ddeSBryan Venteicher 7886f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_REMOVE: 7896f744ddeSBryan Venteicher vtcon_ctrl_port_remove_event(sc, id); 7906f744ddeSBryan Venteicher break; 7916f744ddeSBryan Venteicher 7926f744ddeSBryan Venteicher case VIRTIO_CONSOLE_CONSOLE_PORT: 7936f744ddeSBryan Venteicher vtcon_ctrl_port_console_event(sc, id); 7946f744ddeSBryan Venteicher break; 7956f744ddeSBryan Venteicher 7966f744ddeSBryan Venteicher case VIRTIO_CONSOLE_RESIZE: 7976f744ddeSBryan Venteicher break; 7986f744ddeSBryan Venteicher 7996f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_OPEN: 8006f744ddeSBryan Venteicher vtcon_ctrl_port_open_event(sc, id); 8016f744ddeSBryan Venteicher break; 8026f744ddeSBryan Venteicher 8036f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_NAME: 8046f744ddeSBryan Venteicher break; 8056f744ddeSBryan Venteicher } 8066f744ddeSBryan Venteicher } 8076f744ddeSBryan Venteicher 8086f744ddeSBryan Venteicher static void 8096f744ddeSBryan Venteicher vtcon_ctrl_task_cb(void *xsc, int pending) 8106f744ddeSBryan Venteicher { 8116f744ddeSBryan Venteicher struct vtcon_softc *sc; 8126f744ddeSBryan Venteicher struct virtqueue *vq; 8136f744ddeSBryan Venteicher struct virtio_console_control *control; 814*04434c94SBryan Venteicher int detached; 8156f744ddeSBryan Venteicher 8166f744ddeSBryan Venteicher sc = xsc; 8176f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 8186f744ddeSBryan Venteicher 8196f744ddeSBryan Venteicher VTCON_LOCK(sc); 820*04434c94SBryan Venteicher 821*04434c94SBryan Venteicher while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) { 8226f744ddeSBryan Venteicher control = virtqueue_dequeue(vq, NULL); 8236f744ddeSBryan Venteicher if (control == NULL) 8246f744ddeSBryan Venteicher break; 8256f744ddeSBryan Venteicher 8266f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 827*04434c94SBryan Venteicher vtcon_ctrl_process_event(sc, control); 8286f744ddeSBryan Venteicher VTCON_LOCK(sc); 829*04434c94SBryan Venteicher vtcon_ctrl_event_requeue(sc, control); 8306f744ddeSBryan Venteicher } 8316f744ddeSBryan Venteicher 832*04434c94SBryan Venteicher if (!detached) { 833*04434c94SBryan Venteicher virtqueue_notify(vq); 8346f744ddeSBryan Venteicher if (virtqueue_enable_intr(vq) != 0) 835*04434c94SBryan Venteicher taskqueue_enqueue(taskqueue_thread, 836*04434c94SBryan Venteicher &sc->vtcon_ctrl_task); 837*04434c94SBryan Venteicher } 838*04434c94SBryan Venteicher 839*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 840*04434c94SBryan Venteicher } 841*04434c94SBryan Venteicher 842*04434c94SBryan Venteicher static void 843*04434c94SBryan Venteicher vtcon_ctrl_event_intr(void *xsc) 844*04434c94SBryan Venteicher { 845*04434c94SBryan Venteicher struct vtcon_softc *sc; 846*04434c94SBryan Venteicher 847*04434c94SBryan Venteicher sc = xsc; 848*04434c94SBryan Venteicher 849*04434c94SBryan Venteicher /* 850*04434c94SBryan Venteicher * Only some events require us to potentially block, but it 851*04434c94SBryan Venteicher * easier to just defer all event handling to the taskqueue. 852*04434c94SBryan Venteicher */ 8536f744ddeSBryan Venteicher taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task); 8546f744ddeSBryan Venteicher } 8556f744ddeSBryan Venteicher 856*04434c94SBryan Venteicher static void 857*04434c94SBryan Venteicher vtcon_ctrl_poll(struct vtcon_softc *sc, 858*04434c94SBryan Venteicher struct virtio_console_control *control) 8596f744ddeSBryan Venteicher { 860*04434c94SBryan Venteicher struct sglist_seg segs[2]; 861*04434c94SBryan Venteicher struct sglist sg; 862*04434c94SBryan Venteicher struct virtqueue *vq; 863*04434c94SBryan Venteicher int error; 864*04434c94SBryan Venteicher 865*04434c94SBryan Venteicher vq = sc->vtcon_ctrl_txvq; 866*04434c94SBryan Venteicher 867*04434c94SBryan Venteicher sglist_init(&sg, 2, segs); 868*04434c94SBryan Venteicher error = sglist_append(&sg, control, 869*04434c94SBryan Venteicher sizeof(struct virtio_console_control)); 870*04434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding control to sglist", 871*04434c94SBryan Venteicher __func__, error)); 872*04434c94SBryan Venteicher 873*04434c94SBryan Venteicher /* 874*04434c94SBryan Venteicher * We cannot use the softc lock to serialize access to this 875*04434c94SBryan Venteicher * virtqueue since this is called from the tty layer with the 876*04434c94SBryan Venteicher * port lock held. Acquiring the softc would violate our lock 877*04434c94SBryan Venteicher * ordering. 878*04434c94SBryan Venteicher */ 879*04434c94SBryan Venteicher VTCON_CTRL_TX_LOCK(sc); 880*04434c94SBryan Venteicher KASSERT(virtqueue_empty(vq), 881*04434c94SBryan Venteicher ("%s: virtqueue is not emtpy", __func__)); 882*04434c94SBryan Venteicher error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0); 883*04434c94SBryan Venteicher if (error == 0) { 884*04434c94SBryan Venteicher virtqueue_notify(vq); 885*04434c94SBryan Venteicher virtqueue_poll(vq, NULL); 886*04434c94SBryan Venteicher } 887*04434c94SBryan Venteicher VTCON_CTRL_TX_UNLOCK(sc); 888*04434c94SBryan Venteicher } 889*04434c94SBryan Venteicher 890*04434c94SBryan Venteicher static void 891*04434c94SBryan Venteicher vtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid, 892*04434c94SBryan Venteicher uint16_t event, uint16_t value) 893*04434c94SBryan Venteicher { 894*04434c94SBryan Venteicher struct virtio_console_control control; 895*04434c94SBryan Venteicher 896*04434c94SBryan Venteicher if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) 897*04434c94SBryan Venteicher return; 898*04434c94SBryan Venteicher 899*04434c94SBryan Venteicher control.id = portid; 900*04434c94SBryan Venteicher control.event = event; 901*04434c94SBryan Venteicher control.value = value; 902*04434c94SBryan Venteicher 903*04434c94SBryan Venteicher vtcon_ctrl_poll(sc, &control); 904*04434c94SBryan Venteicher } 905*04434c94SBryan Venteicher 906*04434c94SBryan Venteicher static int 907*04434c94SBryan Venteicher vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len) 908*04434c94SBryan Venteicher { 909*04434c94SBryan Venteicher struct sglist_seg segs[2]; 9106f744ddeSBryan Venteicher struct sglist sg; 9116f744ddeSBryan Venteicher struct virtqueue *vq; 9126f744ddeSBryan Venteicher int error; 9136f744ddeSBryan Venteicher 9146f744ddeSBryan Venteicher vq = port->vtcport_invq; 9156f744ddeSBryan Venteicher 916*04434c94SBryan Venteicher sglist_init(&sg, 2, segs); 9176f744ddeSBryan Venteicher error = sglist_append(&sg, buf, len); 918*04434c94SBryan Venteicher KASSERT(error == 0, 9196f744ddeSBryan Venteicher ("%s: error %d adding buffer to sglist", __func__, error)); 9206f744ddeSBryan Venteicher 921*04434c94SBryan Venteicher error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg); 922*04434c94SBryan Venteicher 923*04434c94SBryan Venteicher return (error); 9246f744ddeSBryan Venteicher } 9256f744ddeSBryan Venteicher 9266f744ddeSBryan Venteicher static int 927*04434c94SBryan Venteicher vtcon_port_create_buf(struct vtcon_port *port) 9286f744ddeSBryan Venteicher { 9296f744ddeSBryan Venteicher void *buf; 9306f744ddeSBryan Venteicher int error; 9316f744ddeSBryan Venteicher 9326f744ddeSBryan Venteicher buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT); 9336f744ddeSBryan Venteicher if (buf == NULL) 9346f744ddeSBryan Venteicher return (ENOMEM); 9356f744ddeSBryan Venteicher 936*04434c94SBryan Venteicher error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 9376f744ddeSBryan Venteicher if (error) 9386f744ddeSBryan Venteicher free(buf, M_DEVBUF); 9396f744ddeSBryan Venteicher 9406f744ddeSBryan Venteicher return (error); 9416f744ddeSBryan Venteicher } 9426f744ddeSBryan Venteicher 9436f744ddeSBryan Venteicher static void 944*04434c94SBryan Venteicher vtcon_port_requeue_buf(struct vtcon_port *port, void *buf) 9456f744ddeSBryan Venteicher { 946*04434c94SBryan Venteicher int error; 9476f744ddeSBryan Venteicher 948*04434c94SBryan Venteicher error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 9496f744ddeSBryan Venteicher KASSERT(error == 0, 9506f744ddeSBryan Venteicher ("%s: cannot requeue input buffer %d", __func__, error)); 9516f744ddeSBryan Venteicher } 9526f744ddeSBryan Venteicher 9536f744ddeSBryan Venteicher static int 9546f744ddeSBryan Venteicher vtcon_port_populate(struct vtcon_port *port) 9556f744ddeSBryan Venteicher { 9566f744ddeSBryan Venteicher struct virtqueue *vq; 9576f744ddeSBryan Venteicher int nbufs, error; 9586f744ddeSBryan Venteicher 9596f744ddeSBryan Venteicher vq = port->vtcport_invq; 9606f744ddeSBryan Venteicher error = ENOSPC; 9616f744ddeSBryan Venteicher 9626f744ddeSBryan Venteicher for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 963*04434c94SBryan Venteicher error = vtcon_port_create_buf(port); 9646f744ddeSBryan Venteicher if (error) 9656f744ddeSBryan Venteicher break; 9666f744ddeSBryan Venteicher } 9676f744ddeSBryan Venteicher 9686f744ddeSBryan Venteicher if (nbufs > 0) { 9696f744ddeSBryan Venteicher virtqueue_notify(vq); 9706f744ddeSBryan Venteicher error = 0; 9716f744ddeSBryan Venteicher } 9726f744ddeSBryan Venteicher 9736f744ddeSBryan Venteicher return (error); 9746f744ddeSBryan Venteicher } 9756f744ddeSBryan Venteicher 9766f744ddeSBryan Venteicher static void 9776f744ddeSBryan Venteicher vtcon_port_destroy(struct vtcon_port *port) 9786f744ddeSBryan Venteicher { 9796f744ddeSBryan Venteicher 9806f744ddeSBryan Venteicher port->vtcport_sc = NULL; 981*04434c94SBryan Venteicher port->vtcport_scport = NULL; 982*04434c94SBryan Venteicher port->vtcport_invq = NULL; 983*04434c94SBryan Venteicher port->vtcport_outvq = NULL; 9846f744ddeSBryan Venteicher port->vtcport_id = -1; 985*04434c94SBryan Venteicher mtx_destroy(&port->vtcport_mtx); 9866f744ddeSBryan Venteicher free(port, M_DEVBUF); 9876f744ddeSBryan Venteicher } 9886f744ddeSBryan Venteicher 9896f744ddeSBryan Venteicher static int 990*04434c94SBryan Venteicher vtcon_port_init_vqs(struct vtcon_port *port) 991*04434c94SBryan Venteicher { 992*04434c94SBryan Venteicher struct vtcon_softc_port *scport; 993*04434c94SBryan Venteicher int error; 994*04434c94SBryan Venteicher 995*04434c94SBryan Venteicher scport = port->vtcport_scport; 996*04434c94SBryan Venteicher 997*04434c94SBryan Venteicher port->vtcport_invq = scport->vcsp_invq; 998*04434c94SBryan Venteicher port->vtcport_outvq = scport->vcsp_outvq; 999*04434c94SBryan Venteicher 1000*04434c94SBryan Venteicher /* 1001*04434c94SBryan Venteicher * Free any data left over from when this virtqueue was in use by a 1002*04434c94SBryan Venteicher * prior port. We have not yet notified the host that the port is 1003*04434c94SBryan Venteicher * ready, so assume nothing in the virtqueue can be for us. 1004*04434c94SBryan Venteicher */ 1005*04434c94SBryan Venteicher vtcon_port_drain(port); 1006*04434c94SBryan Venteicher 1007*04434c94SBryan Venteicher KASSERT(virtqueue_empty(port->vtcport_invq), 1008*04434c94SBryan Venteicher ("%s: in virtqueue is not empty", __func__)); 1009*04434c94SBryan Venteicher KASSERT(virtqueue_empty(port->vtcport_outvq), 1010*04434c94SBryan Venteicher ("%s: out virtqueue is not empty", __func__)); 1011*04434c94SBryan Venteicher 1012*04434c94SBryan Venteicher error = vtcon_port_populate(port); 1013*04434c94SBryan Venteicher if (error) 1014*04434c94SBryan Venteicher return (error); 1015*04434c94SBryan Venteicher 1016*04434c94SBryan Venteicher return (0); 1017*04434c94SBryan Venteicher } 1018*04434c94SBryan Venteicher 1019*04434c94SBryan Venteicher static int 1020*04434c94SBryan Venteicher vtcon_port_create(struct vtcon_softc *sc, int id) 10216f744ddeSBryan Venteicher { 10226f744ddeSBryan Venteicher device_t dev; 1023*04434c94SBryan Venteicher struct vtcon_softc_port *scport; 10246f744ddeSBryan Venteicher struct vtcon_port *port; 10256f744ddeSBryan Venteicher int error; 10266f744ddeSBryan Venteicher 10276f744ddeSBryan Venteicher dev = sc->vtcon_dev; 1028*04434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 10296f744ddeSBryan Venteicher 1030*04434c94SBryan Venteicher VTCON_ASSERT_VALID_PORTID(sc, id); 1031*04434c94SBryan Venteicher MPASS(scport->vcsp_port == NULL); 10326f744ddeSBryan Venteicher 10336f744ddeSBryan Venteicher port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO); 10346f744ddeSBryan Venteicher if (port == NULL) 10356f744ddeSBryan Venteicher return (ENOMEM); 10366f744ddeSBryan Venteicher 10376f744ddeSBryan Venteicher port->vtcport_sc = sc; 1038*04434c94SBryan Venteicher port->vtcport_scport = scport; 10396f744ddeSBryan Venteicher port->vtcport_id = id; 1040*04434c94SBryan Venteicher mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF); 10416f744ddeSBryan Venteicher port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port, 10426f744ddeSBryan Venteicher &port->vtcport_mtx); 10436f744ddeSBryan Venteicher 1044*04434c94SBryan Venteicher error = vtcon_port_init_vqs(port); 10456f744ddeSBryan Venteicher if (error) { 1046*04434c94SBryan Venteicher VTCON_PORT_LOCK(port); 1047*04434c94SBryan Venteicher vtcon_port_teardown(port); 10486f744ddeSBryan Venteicher return (error); 10496f744ddeSBryan Venteicher } 10506f744ddeSBryan Venteicher 10516f744ddeSBryan Venteicher tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX, 10526f744ddeSBryan Venteicher device_get_unit(dev), id); 10536f744ddeSBryan Venteicher 10546f744ddeSBryan Venteicher VTCON_LOCK(sc); 1055*04434c94SBryan Venteicher VTCON_PORT_LOCK(port); 1056*04434c94SBryan Venteicher vtcon_port_enable_intr(port); 1057*04434c94SBryan Venteicher scport->vcsp_port = port; 1058*04434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1); 1059*04434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 10606f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 10616f744ddeSBryan Venteicher 10626f744ddeSBryan Venteicher return (0); 10636f744ddeSBryan Venteicher } 10646f744ddeSBryan Venteicher 10656f744ddeSBryan Venteicher static void 1066*04434c94SBryan Venteicher vtcon_port_drain_bufs(struct virtqueue *vq) 10676f744ddeSBryan Venteicher { 10686f744ddeSBryan Venteicher void *buf; 10696f744ddeSBryan Venteicher int last; 10706f744ddeSBryan Venteicher 10716f744ddeSBryan Venteicher last = 0; 10726f744ddeSBryan Venteicher 10736f744ddeSBryan Venteicher while ((buf = virtqueue_drain(vq, &last)) != NULL) 10746f744ddeSBryan Venteicher free(buf, M_DEVBUF); 10756f744ddeSBryan Venteicher } 10766f744ddeSBryan Venteicher 10776f744ddeSBryan Venteicher static void 1078*04434c94SBryan Venteicher vtcon_port_drain(struct vtcon_port *port) 10796f744ddeSBryan Venteicher { 10806f744ddeSBryan Venteicher 1081*04434c94SBryan Venteicher vtcon_port_drain_bufs(port->vtcport_invq); 1082*04434c94SBryan Venteicher } 1083*04434c94SBryan Venteicher 1084*04434c94SBryan Venteicher static void 1085*04434c94SBryan Venteicher vtcon_port_teardown(struct vtcon_port *port) 1086*04434c94SBryan Venteicher { 1087*04434c94SBryan Venteicher struct tty *tp; 1088*04434c94SBryan Venteicher 10896f744ddeSBryan Venteicher tp = port->vtcport_tty; 10906f744ddeSBryan Venteicher 1091*04434c94SBryan Venteicher port->vtcport_flags |= VTCON_PORT_FLAG_GONE; 10926f744ddeSBryan Venteicher 10936f744ddeSBryan Venteicher if (tp != NULL) { 10946f744ddeSBryan Venteicher atomic_add_int(&vtcon_pending_free, 1); 10956f744ddeSBryan Venteicher tty_rel_gone(tp); 10966f744ddeSBryan Venteicher } else 10976f744ddeSBryan Venteicher vtcon_port_destroy(port); 10986f744ddeSBryan Venteicher } 10996f744ddeSBryan Venteicher 11006f744ddeSBryan Venteicher static void 11016f744ddeSBryan Venteicher vtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows) 11026f744ddeSBryan Venteicher { 11036f744ddeSBryan Venteicher struct tty *tp; 11046f744ddeSBryan Venteicher struct winsize sz; 11056f744ddeSBryan Venteicher 11066f744ddeSBryan Venteicher tp = port->vtcport_tty; 11076f744ddeSBryan Venteicher 11086f744ddeSBryan Venteicher if (tp == NULL) 11096f744ddeSBryan Venteicher return; 11106f744ddeSBryan Venteicher 11116f744ddeSBryan Venteicher bzero(&sz, sizeof(struct winsize)); 11126f744ddeSBryan Venteicher sz.ws_col = cols; 11136f744ddeSBryan Venteicher sz.ws_row = rows; 11146f744ddeSBryan Venteicher 11156f744ddeSBryan Venteicher tty_set_winsize(tp, &sz); 1116*04434c94SBryan Venteicher } 1117*04434c94SBryan Venteicher 1118*04434c94SBryan Venteicher static void 1119*04434c94SBryan Venteicher vtcon_port_update_console_size(struct vtcon_softc *sc) 1120*04434c94SBryan Venteicher { 1121*04434c94SBryan Venteicher struct vtcon_port *port; 1122*04434c94SBryan Venteicher struct vtcon_softc_port *scport; 1123*04434c94SBryan Venteicher uint16_t cols, rows; 1124*04434c94SBryan Venteicher 1125*04434c94SBryan Venteicher vtcon_get_console_size(sc, &cols, &rows); 1126*04434c94SBryan Venteicher 1127*04434c94SBryan Venteicher /* 1128*04434c94SBryan Venteicher * For now, assume the first (only) port is the console. Note 1129*04434c94SBryan Venteicher * QEMU does not implement this feature yet. 1130*04434c94SBryan Venteicher */ 1131*04434c94SBryan Venteicher scport = &sc->vtcon_ports[0]; 1132*04434c94SBryan Venteicher 1133*04434c94SBryan Venteicher VTCON_LOCK(sc); 1134*04434c94SBryan Venteicher port = scport->vcsp_port; 1135*04434c94SBryan Venteicher 1136*04434c94SBryan Venteicher if (port != NULL) { 1137*04434c94SBryan Venteicher VTCON_PORT_LOCK(port); 1138*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 1139*04434c94SBryan Venteicher vtcon_port_change_size(port, cols, rows); 11406f744ddeSBryan Venteicher VTCON_PORT_UNLOCK(port); 1141*04434c94SBryan Venteicher } else 1142*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 11436f744ddeSBryan Venteicher } 11446f744ddeSBryan Venteicher 11456f744ddeSBryan Venteicher static void 11466f744ddeSBryan Venteicher vtcon_port_enable_intr(struct vtcon_port *port) 11476f744ddeSBryan Venteicher { 11486f744ddeSBryan Venteicher 11496f744ddeSBryan Venteicher /* 1150*04434c94SBryan Venteicher * NOTE: The out virtqueue is always polled, so its interupt 1151*04434c94SBryan Venteicher * kept disabled. 11526f744ddeSBryan Venteicher */ 11536f744ddeSBryan Venteicher virtqueue_enable_intr(port->vtcport_invq); 11546f744ddeSBryan Venteicher } 11556f744ddeSBryan Venteicher 11566f744ddeSBryan Venteicher static void 11576f744ddeSBryan Venteicher vtcon_port_disable_intr(struct vtcon_port *port) 11586f744ddeSBryan Venteicher { 11596f744ddeSBryan Venteicher 11606f744ddeSBryan Venteicher if (port->vtcport_invq != NULL) 11616f744ddeSBryan Venteicher virtqueue_disable_intr(port->vtcport_invq); 11626f744ddeSBryan Venteicher if (port->vtcport_outvq != NULL) 11636f744ddeSBryan Venteicher virtqueue_disable_intr(port->vtcport_outvq); 11646f744ddeSBryan Venteicher } 11656f744ddeSBryan Venteicher 11666f744ddeSBryan Venteicher static void 1167*04434c94SBryan Venteicher vtcon_port_in(struct vtcon_port *port) 11686f744ddeSBryan Venteicher { 11696f744ddeSBryan Venteicher struct virtqueue *vq; 1170*04434c94SBryan Venteicher struct tty *tp; 11716f744ddeSBryan Venteicher char *buf; 11726f744ddeSBryan Venteicher uint32_t len; 11736f744ddeSBryan Venteicher int i, deq; 11746f744ddeSBryan Venteicher 11756f744ddeSBryan Venteicher tp = port->vtcport_tty; 11766f744ddeSBryan Venteicher vq = port->vtcport_invq; 11776f744ddeSBryan Venteicher 11786f744ddeSBryan Venteicher again: 11796f744ddeSBryan Venteicher deq = 0; 11806f744ddeSBryan Venteicher 11816f744ddeSBryan Venteicher while ((buf = virtqueue_dequeue(vq, &len)) != NULL) { 11826f744ddeSBryan Venteicher for (i = 0; i < len; i++) 11836f744ddeSBryan Venteicher ttydisc_rint(tp, buf[i], 0); 1184*04434c94SBryan Venteicher vtcon_port_requeue_buf(port, buf); 1185*04434c94SBryan Venteicher deq++; 11866f744ddeSBryan Venteicher } 11876f744ddeSBryan Venteicher ttydisc_rint_done(tp); 11886f744ddeSBryan Venteicher 11896f744ddeSBryan Venteicher if (deq > 0) 11906f744ddeSBryan Venteicher virtqueue_notify(vq); 11916f744ddeSBryan Venteicher 11926f744ddeSBryan Venteicher if (virtqueue_enable_intr(vq) != 0) 11936f744ddeSBryan Venteicher goto again; 11946f744ddeSBryan Venteicher } 11956f744ddeSBryan Venteicher 11966f744ddeSBryan Venteicher static void 1197*04434c94SBryan Venteicher vtcon_port_intr(void *scportx) 11986f744ddeSBryan Venteicher { 1199*04434c94SBryan Venteicher struct vtcon_softc_port *scport; 1200*04434c94SBryan Venteicher struct vtcon_softc *sc; 12016f744ddeSBryan Venteicher struct vtcon_port *port; 12026f744ddeSBryan Venteicher 1203*04434c94SBryan Venteicher scport = scportx; 1204*04434c94SBryan Venteicher sc = scport->vcsp_sc; 12056f744ddeSBryan Venteicher 1206*04434c94SBryan Venteicher VTCON_LOCK(sc); 1207*04434c94SBryan Venteicher port = scport->vcsp_port; 1208*04434c94SBryan Venteicher if (port == NULL) { 1209*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 1210*04434c94SBryan Venteicher return; 1211*04434c94SBryan Venteicher } 1212*04434c94SBryan Venteicher VTCON_PORT_LOCK(port); 1213*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 1214*04434c94SBryan Venteicher if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0) 1215*04434c94SBryan Venteicher vtcon_port_in(port); 1216*04434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 12176f744ddeSBryan Venteicher } 12186f744ddeSBryan Venteicher 12196f744ddeSBryan Venteicher static void 1220*04434c94SBryan Venteicher vtcon_port_out(struct vtcon_port *port, void *buf, int bufsize) 12216f744ddeSBryan Venteicher { 1222*04434c94SBryan Venteicher struct sglist_seg segs[2]; 12236f744ddeSBryan Venteicher struct sglist sg; 12246f744ddeSBryan Venteicher struct virtqueue *vq; 12256f744ddeSBryan Venteicher int error; 12266f744ddeSBryan Venteicher 12276f744ddeSBryan Venteicher vq = port->vtcport_outvq; 1228*04434c94SBryan Venteicher KASSERT(virtqueue_empty(vq), 1229*04434c94SBryan Venteicher ("%s: port %p out virtqueue not emtpy", __func__, port)); 12306f744ddeSBryan Venteicher 1231*04434c94SBryan Venteicher sglist_init(&sg, 2, segs); 12326f744ddeSBryan Venteicher error = sglist_append(&sg, buf, bufsize); 1233*04434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding buffer to sglist", 1234*04434c94SBryan Venteicher __func__, error)); 12356f744ddeSBryan Venteicher 1236*04434c94SBryan Venteicher error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0); 1237*04434c94SBryan Venteicher if (error == 0) { 12386f744ddeSBryan Venteicher virtqueue_notify(vq); 12396f744ddeSBryan Venteicher virtqueue_poll(vq, NULL); 12406f744ddeSBryan Venteicher } 12416f744ddeSBryan Venteicher } 12426f744ddeSBryan Venteicher 12436f744ddeSBryan Venteicher static void 1244*04434c94SBryan Venteicher vtcon_port_submit_event(struct vtcon_port *port, uint16_t event, 12456f744ddeSBryan Venteicher uint16_t value) 12466f744ddeSBryan Venteicher { 12476f744ddeSBryan Venteicher struct vtcon_softc *sc; 12486f744ddeSBryan Venteicher 12496f744ddeSBryan Venteicher sc = port->vtcport_sc; 12506f744ddeSBryan Venteicher 1251*04434c94SBryan Venteicher vtcon_ctrl_send_control(sc, port->vtcport_id, event, value); 12526f744ddeSBryan Venteicher } 12536f744ddeSBryan Venteicher 12546f744ddeSBryan Venteicher static int 12556f744ddeSBryan Venteicher vtcon_tty_open(struct tty *tp) 12566f744ddeSBryan Venteicher { 12576f744ddeSBryan Venteicher struct vtcon_port *port; 12586f744ddeSBryan Venteicher 12596f744ddeSBryan Venteicher port = tty_softc(tp); 12606f744ddeSBryan Venteicher 1261*04434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1262*04434c94SBryan Venteicher return (ENXIO); 1263*04434c94SBryan Venteicher 1264*04434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 12656f744ddeSBryan Venteicher 12666f744ddeSBryan Venteicher return (0); 12676f744ddeSBryan Venteicher } 12686f744ddeSBryan Venteicher 12696f744ddeSBryan Venteicher static void 12706f744ddeSBryan Venteicher vtcon_tty_close(struct tty *tp) 12716f744ddeSBryan Venteicher { 12726f744ddeSBryan Venteicher struct vtcon_port *port; 12736f744ddeSBryan Venteicher 12746f744ddeSBryan Venteicher port = tty_softc(tp); 12756f744ddeSBryan Venteicher 1276*04434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1277*04434c94SBryan Venteicher return; 1278*04434c94SBryan Venteicher 1279*04434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); 12806f744ddeSBryan Venteicher } 12816f744ddeSBryan Venteicher 12826f744ddeSBryan Venteicher static void 12836f744ddeSBryan Venteicher vtcon_tty_outwakeup(struct tty *tp) 12846f744ddeSBryan Venteicher { 12856f744ddeSBryan Venteicher struct vtcon_port *port; 12866f744ddeSBryan Venteicher char buf[VTCON_BULK_BUFSZ]; 12876f744ddeSBryan Venteicher int len; 12886f744ddeSBryan Venteicher 12896f744ddeSBryan Venteicher port = tty_softc(tp); 12906f744ddeSBryan Venteicher 1291*04434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1292*04434c94SBryan Venteicher return; 1293*04434c94SBryan Venteicher 12946f744ddeSBryan Venteicher while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0) 1295*04434c94SBryan Venteicher vtcon_port_out(port, buf, len); 12966f744ddeSBryan Venteicher } 12976f744ddeSBryan Venteicher 12986f744ddeSBryan Venteicher static void 12996f744ddeSBryan Venteicher vtcon_tty_free(void *xport) 13006f744ddeSBryan Venteicher { 13016f744ddeSBryan Venteicher struct vtcon_port *port; 13026f744ddeSBryan Venteicher 13036f744ddeSBryan Venteicher port = xport; 13046f744ddeSBryan Venteicher 13056f744ddeSBryan Venteicher vtcon_port_destroy(port); 13066f744ddeSBryan Venteicher atomic_subtract_int(&vtcon_pending_free, 1); 13076f744ddeSBryan Venteicher } 13086f744ddeSBryan Venteicher 13096f744ddeSBryan Venteicher static void 13106f744ddeSBryan Venteicher vtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows) 13116f744ddeSBryan Venteicher { 13126f744ddeSBryan Venteicher struct virtio_console_config concfg; 13136f744ddeSBryan Venteicher 13146f744ddeSBryan Venteicher KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE, 13156f744ddeSBryan Venteicher ("%s: size feature not negotiated", __func__)); 13166f744ddeSBryan Venteicher 13176f744ddeSBryan Venteicher vtcon_read_config(sc, &concfg); 13186f744ddeSBryan Venteicher 13196f744ddeSBryan Venteicher *cols = concfg.cols; 13206f744ddeSBryan Venteicher *rows = concfg.rows; 13216f744ddeSBryan Venteicher } 13226f744ddeSBryan Venteicher 13236f744ddeSBryan Venteicher static void 13246f744ddeSBryan Venteicher vtcon_enable_interrupts(struct vtcon_softc *sc) 13256f744ddeSBryan Venteicher { 1326*04434c94SBryan Venteicher struct vtcon_softc_port *scport; 13276f744ddeSBryan Venteicher struct vtcon_port *port; 1328*04434c94SBryan Venteicher int i; 1329*04434c94SBryan Venteicher 1330*04434c94SBryan Venteicher VTCON_LOCK(sc); 13316f744ddeSBryan Venteicher 13326f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 13336f744ddeSBryan Venteicher virtqueue_enable_intr(sc->vtcon_ctrl_rxvq); 13346f744ddeSBryan Venteicher 1335*04434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 1336*04434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 1337*04434c94SBryan Venteicher 1338*04434c94SBryan Venteicher port = scport->vcsp_port; 1339*04434c94SBryan Venteicher if (port == NULL) 1340*04434c94SBryan Venteicher continue; 1341*04434c94SBryan Venteicher 1342*04434c94SBryan Venteicher VTCON_PORT_LOCK(port); 13436f744ddeSBryan Venteicher vtcon_port_enable_intr(port); 1344*04434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 1345*04434c94SBryan Venteicher } 1346*04434c94SBryan Venteicher 1347*04434c94SBryan Venteicher VTCON_UNLOCK(sc); 13486f744ddeSBryan Venteicher } 13496f744ddeSBryan Venteicher 13506f744ddeSBryan Venteicher static void 13516f744ddeSBryan Venteicher vtcon_disable_interrupts(struct vtcon_softc *sc) 13526f744ddeSBryan Venteicher { 1353*04434c94SBryan Venteicher struct vtcon_softc_port *scport; 13546f744ddeSBryan Venteicher struct vtcon_port *port; 1355*04434c94SBryan Venteicher int i; 1356*04434c94SBryan Venteicher 1357*04434c94SBryan Venteicher VTCON_LOCK_ASSERT(sc); 13586f744ddeSBryan Venteicher 13596f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 13606f744ddeSBryan Venteicher virtqueue_disable_intr(sc->vtcon_ctrl_rxvq); 13616f744ddeSBryan Venteicher 1362*04434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 1363*04434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 1364*04434c94SBryan Venteicher 1365*04434c94SBryan Venteicher port = scport->vcsp_port; 1366*04434c94SBryan Venteicher if (port == NULL) 1367*04434c94SBryan Venteicher continue; 1368*04434c94SBryan Venteicher 1369*04434c94SBryan Venteicher VTCON_PORT_LOCK(port); 13706f744ddeSBryan Venteicher vtcon_port_disable_intr(port); 1371*04434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 1372*04434c94SBryan Venteicher } 13736f744ddeSBryan Venteicher } 1374