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> 37b84b3efdSBryan Venteicher #include <sys/kdb.h> 386f744ddeSBryan Venteicher #include <sys/lock.h> 396f744ddeSBryan Venteicher #include <sys/mutex.h> 406f744ddeSBryan Venteicher #include <sys/sglist.h> 416f744ddeSBryan Venteicher #include <sys/sysctl.h> 426f744ddeSBryan Venteicher #include <sys/taskqueue.h> 436f744ddeSBryan Venteicher #include <sys/queue.h> 446f744ddeSBryan Venteicher 456f744ddeSBryan Venteicher #include <sys/conf.h> 466f744ddeSBryan Venteicher #include <sys/cons.h> 476f744ddeSBryan Venteicher #include <sys/tty.h> 486f744ddeSBryan Venteicher 496f744ddeSBryan Venteicher #include <machine/bus.h> 506f744ddeSBryan Venteicher #include <machine/resource.h> 516f744ddeSBryan Venteicher #include <sys/bus.h> 526f744ddeSBryan Venteicher 536f744ddeSBryan Venteicher #include <dev/virtio/virtio.h> 546f744ddeSBryan Venteicher #include <dev/virtio/virtqueue.h> 556f744ddeSBryan Venteicher #include <dev/virtio/console/virtio_console.h> 566f744ddeSBryan Venteicher 576f744ddeSBryan Venteicher #include "virtio_if.h" 586f744ddeSBryan Venteicher 5904434c94SBryan Venteicher #define VTCON_MAX_PORTS 32 606f744ddeSBryan Venteicher #define VTCON_TTY_PREFIX "V" 616f744ddeSBryan Venteicher #define VTCON_BULK_BUFSZ 128 626f744ddeSBryan Venteicher 6304434c94SBryan Venteicher /* 6404434c94SBryan Venteicher * The buffer cannot cross more than one page boundary due to the 6504434c94SBryan Venteicher * size of the sglist segment array used. 6604434c94SBryan Venteicher */ 6704434c94SBryan Venteicher CTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE); 6804434c94SBryan Venteicher 696f744ddeSBryan Venteicher struct vtcon_softc; 7004434c94SBryan Venteicher struct vtcon_softc_port; 716f744ddeSBryan Venteicher 726f744ddeSBryan Venteicher struct vtcon_port { 736f744ddeSBryan Venteicher struct mtx vtcport_mtx; 7404434c94SBryan Venteicher struct vtcon_softc *vtcport_sc; 7504434c94SBryan Venteicher struct vtcon_softc_port *vtcport_scport; 766f744ddeSBryan Venteicher struct tty *vtcport_tty; 776f744ddeSBryan Venteicher struct virtqueue *vtcport_invq; 786f744ddeSBryan Venteicher struct virtqueue *vtcport_outvq; 7904434c94SBryan Venteicher int vtcport_id; 8004434c94SBryan Venteicher int vtcport_flags; 8104434c94SBryan Venteicher #define VTCON_PORT_FLAG_GONE 0x01 82b84b3efdSBryan Venteicher #define VTCON_PORT_FLAG_CONSOLE 0x02 83b84b3efdSBryan Venteicher 84b84b3efdSBryan Venteicher #if defined(KDB) 85b84b3efdSBryan Venteicher int vtcport_alt_break_state; 86b84b3efdSBryan Venteicher #endif 876f744ddeSBryan Venteicher }; 886f744ddeSBryan Venteicher 8904434c94SBryan Venteicher #define VTCON_PORT_LOCK(_port) mtx_lock(&(_port)->vtcport_mtx) 9004434c94SBryan Venteicher #define VTCON_PORT_UNLOCK(_port) mtx_unlock(&(_port)->vtcport_mtx) 9104434c94SBryan Venteicher 9204434c94SBryan Venteicher struct vtcon_softc_port { 9304434c94SBryan Venteicher struct vtcon_softc *vcsp_sc; 9404434c94SBryan Venteicher struct vtcon_port *vcsp_port; 9504434c94SBryan Venteicher struct virtqueue *vcsp_invq; 9604434c94SBryan Venteicher struct virtqueue *vcsp_outvq; 9704434c94SBryan Venteicher }; 986f744ddeSBryan Venteicher 996f744ddeSBryan Venteicher struct vtcon_softc { 1006f744ddeSBryan Venteicher device_t vtcon_dev; 1016f744ddeSBryan Venteicher struct mtx vtcon_mtx; 1026f744ddeSBryan Venteicher uint64_t vtcon_features; 1036f744ddeSBryan Venteicher uint32_t vtcon_max_ports; 104b84b3efdSBryan Venteicher uint32_t vtcon_flags; 105b84b3efdSBryan Venteicher #define VTCON_FLAG_DETACHED 0x01 106b84b3efdSBryan Venteicher #define VTCON_FLAG_SIZE 0x02 107b84b3efdSBryan Venteicher #define VTCON_FLAG_MULTIPORT 0x04 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 */ 11404434c94SBryan Venteicher struct vtcon_softc_port *vtcon_ports; 115b84b3efdSBryan Venteicher 116b84b3efdSBryan Venteicher struct task vtcon_ctrl_task; 117b84b3efdSBryan Venteicher struct virtqueue *vtcon_ctrl_rxvq; 118b84b3efdSBryan Venteicher struct virtqueue *vtcon_ctrl_txvq; 119b84b3efdSBryan Venteicher struct mtx vtcon_ctrl_tx_mtx; 1206f744ddeSBryan Venteicher }; 1216f744ddeSBryan Venteicher 12204434c94SBryan Venteicher #define VTCON_LOCK(_sc) mtx_lock(&(_sc)->vtcon_mtx) 12304434c94SBryan Venteicher #define VTCON_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_mtx) 12404434c94SBryan Venteicher #define VTCON_LOCK_ASSERT(_sc) \ 12504434c94SBryan Venteicher mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED) 1266f744ddeSBryan Venteicher #define VTCON_LOCK_ASSERT_NOTOWNED(_sc) \ 12704434c94SBryan Venteicher mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED) 12804434c94SBryan Venteicher 12904434c94SBryan Venteicher #define VTCON_CTRL_TX_LOCK(_sc) mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx) 13004434c94SBryan Venteicher #define VTCON_CTRL_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx) 1316f744ddeSBryan Venteicher 1326f744ddeSBryan Venteicher #define VTCON_ASSERT_VALID_PORTID(_sc, _id) \ 1336f744ddeSBryan Venteicher KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports, \ 1346f744ddeSBryan Venteicher ("%s: port ID %d out of range", __func__, _id)) 1356f744ddeSBryan Venteicher 13604434c94SBryan Venteicher #define VTCON_FEATURES VIRTIO_CONSOLE_F_MULTIPORT 1376f744ddeSBryan Venteicher 1386f744ddeSBryan Venteicher static struct virtio_feature_desc vtcon_feature_desc[] = { 1396f744ddeSBryan Venteicher { VIRTIO_CONSOLE_F_SIZE, "ConsoleSize" }, 1406f744ddeSBryan Venteicher { VIRTIO_CONSOLE_F_MULTIPORT, "MultiplePorts" }, 141b84b3efdSBryan Venteicher { VIRTIO_CONSOLE_F_EMERG_WRITE, "EmergencyWrite" }, 1426f744ddeSBryan Venteicher 1436f744ddeSBryan Venteicher { 0, NULL } 1446f744ddeSBryan Venteicher }; 1456f744ddeSBryan Venteicher 1466f744ddeSBryan Venteicher static int vtcon_modevent(module_t, int, void *); 14704434c94SBryan Venteicher static void vtcon_drain_all(void); 1486f744ddeSBryan Venteicher 1496f744ddeSBryan Venteicher static int vtcon_probe(device_t); 1506f744ddeSBryan Venteicher static int vtcon_attach(device_t); 1516f744ddeSBryan Venteicher static int vtcon_detach(device_t); 1526f744ddeSBryan Venteicher static int vtcon_config_change(device_t); 1536f744ddeSBryan Venteicher 15404434c94SBryan Venteicher static void vtcon_setup_features(struct vtcon_softc *); 1556f744ddeSBryan Venteicher static void vtcon_negotiate_features(struct vtcon_softc *); 15604434c94SBryan Venteicher static int vtcon_alloc_scports(struct vtcon_softc *); 1576f744ddeSBryan Venteicher static int vtcon_alloc_virtqueues(struct vtcon_softc *); 1586f744ddeSBryan Venteicher static void vtcon_read_config(struct vtcon_softc *, 1596f744ddeSBryan Venteicher struct virtio_console_config *); 1606f744ddeSBryan Venteicher 1616f744ddeSBryan Venteicher static void vtcon_determine_max_ports(struct vtcon_softc *, 1626f744ddeSBryan Venteicher struct virtio_console_config *); 16304434c94SBryan Venteicher static void vtcon_destroy_ports(struct vtcon_softc *); 1646f744ddeSBryan Venteicher static void vtcon_stop(struct vtcon_softc *); 1656f744ddeSBryan Venteicher 16604434c94SBryan Venteicher static int vtcon_ctrl_event_enqueue(struct vtcon_softc *, 1676f744ddeSBryan Venteicher struct virtio_console_control *); 16804434c94SBryan Venteicher static int vtcon_ctrl_event_create(struct vtcon_softc *); 16904434c94SBryan Venteicher static void vtcon_ctrl_event_requeue(struct vtcon_softc *, 1706f744ddeSBryan Venteicher struct virtio_console_control *); 17104434c94SBryan Venteicher static int vtcon_ctrl_event_populate(struct vtcon_softc *); 17204434c94SBryan Venteicher static void vtcon_ctrl_event_drain(struct vtcon_softc *); 1736f744ddeSBryan Venteicher static int vtcon_ctrl_init(struct vtcon_softc *); 1746f744ddeSBryan Venteicher static void vtcon_ctrl_deinit(struct vtcon_softc *); 1756f744ddeSBryan Venteicher static void vtcon_ctrl_port_add_event(struct vtcon_softc *, int); 1766f744ddeSBryan Venteicher static void vtcon_ctrl_port_remove_event(struct vtcon_softc *, int); 1776f744ddeSBryan Venteicher static void vtcon_ctrl_port_console_event(struct vtcon_softc *, int); 1786f744ddeSBryan Venteicher static void vtcon_ctrl_port_open_event(struct vtcon_softc *, int); 179*cb981343SJakub Wojciech Klama static void vtcon_ctrl_port_name_event(struct vtcon_softc *, int, 180*cb981343SJakub Wojciech Klama const char *, size_t); 18104434c94SBryan Venteicher static void vtcon_ctrl_process_event(struct vtcon_softc *, 182*cb981343SJakub Wojciech Klama struct virtio_console_control *, void *, size_t); 1836f744ddeSBryan Venteicher static void vtcon_ctrl_task_cb(void *, int); 18404434c94SBryan Venteicher static void vtcon_ctrl_event_intr(void *); 18504434c94SBryan Venteicher static void vtcon_ctrl_poll(struct vtcon_softc *, 18604434c94SBryan Venteicher struct virtio_console_control *control); 18704434c94SBryan Venteicher static void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t, 18804434c94SBryan Venteicher uint16_t, uint16_t); 1896f744ddeSBryan Venteicher 19004434c94SBryan Venteicher static int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t); 19104434c94SBryan Venteicher static int vtcon_port_create_buf(struct vtcon_port *); 19204434c94SBryan Venteicher static void vtcon_port_requeue_buf(struct vtcon_port *, void *); 1936f744ddeSBryan Venteicher static int vtcon_port_populate(struct vtcon_port *); 1946f744ddeSBryan Venteicher static void vtcon_port_destroy(struct vtcon_port *); 19504434c94SBryan Venteicher static int vtcon_port_create(struct vtcon_softc *, int); 19604434c94SBryan Venteicher static void vtcon_port_drain_bufs(struct virtqueue *); 19704434c94SBryan Venteicher static void vtcon_port_drain(struct vtcon_port *); 19804434c94SBryan Venteicher static void vtcon_port_teardown(struct vtcon_port *); 1996f744ddeSBryan Venteicher static void vtcon_port_change_size(struct vtcon_port *, uint16_t, 2006f744ddeSBryan Venteicher uint16_t); 20104434c94SBryan Venteicher static void vtcon_port_update_console_size(struct vtcon_softc *); 2026f744ddeSBryan Venteicher static void vtcon_port_enable_intr(struct vtcon_port *); 2036f744ddeSBryan Venteicher static void vtcon_port_disable_intr(struct vtcon_port *); 20404434c94SBryan Venteicher static void vtcon_port_in(struct vtcon_port *); 20504434c94SBryan Venteicher static void vtcon_port_intr(void *); 20604434c94SBryan Venteicher static void vtcon_port_out(struct vtcon_port *, void *, int); 20704434c94SBryan Venteicher static void vtcon_port_submit_event(struct vtcon_port *, uint16_t, 2086f744ddeSBryan Venteicher uint16_t); 2096f744ddeSBryan Venteicher 2106f744ddeSBryan Venteicher static int vtcon_tty_open(struct tty *); 2116f744ddeSBryan Venteicher static void vtcon_tty_close(struct tty *); 2126f744ddeSBryan Venteicher static void vtcon_tty_outwakeup(struct tty *); 2136f744ddeSBryan Venteicher static void vtcon_tty_free(void *); 2146f744ddeSBryan Venteicher 2156f744ddeSBryan Venteicher static void vtcon_get_console_size(struct vtcon_softc *, uint16_t *, 2166f744ddeSBryan Venteicher uint16_t *); 2176f744ddeSBryan Venteicher 2186f744ddeSBryan Venteicher static void vtcon_enable_interrupts(struct vtcon_softc *); 2196f744ddeSBryan Venteicher static void vtcon_disable_interrupts(struct vtcon_softc *); 2206f744ddeSBryan Venteicher 2216f744ddeSBryan Venteicher static int vtcon_pending_free; 2226f744ddeSBryan Venteicher 2236f744ddeSBryan Venteicher static struct ttydevsw vtcon_tty_class = { 2246f744ddeSBryan Venteicher .tsw_flags = 0, 2256f744ddeSBryan Venteicher .tsw_open = vtcon_tty_open, 2266f744ddeSBryan Venteicher .tsw_close = vtcon_tty_close, 2276f744ddeSBryan Venteicher .tsw_outwakeup = vtcon_tty_outwakeup, 2286f744ddeSBryan Venteicher .tsw_free = vtcon_tty_free, 2296f744ddeSBryan Venteicher }; 2306f744ddeSBryan Venteicher 2316f744ddeSBryan Venteicher static device_method_t vtcon_methods[] = { 2326f744ddeSBryan Venteicher /* Device methods. */ 2336f744ddeSBryan Venteicher DEVMETHOD(device_probe, vtcon_probe), 2346f744ddeSBryan Venteicher DEVMETHOD(device_attach, vtcon_attach), 2356f744ddeSBryan Venteicher DEVMETHOD(device_detach, vtcon_detach), 2366f744ddeSBryan Venteicher 2376f744ddeSBryan Venteicher /* VirtIO methods. */ 2386f744ddeSBryan Venteicher DEVMETHOD(virtio_config_change, vtcon_config_change), 2396f744ddeSBryan Venteicher 2406f744ddeSBryan Venteicher DEVMETHOD_END 2416f744ddeSBryan Venteicher }; 2426f744ddeSBryan Venteicher 2436f744ddeSBryan Venteicher static driver_t vtcon_driver = { 2446f744ddeSBryan Venteicher "vtcon", 2456f744ddeSBryan Venteicher vtcon_methods, 2466f744ddeSBryan Venteicher sizeof(struct vtcon_softc) 2476f744ddeSBryan Venteicher }; 2486f744ddeSBryan Venteicher static devclass_t vtcon_devclass; 2496f744ddeSBryan Venteicher 2506f744ddeSBryan Venteicher DRIVER_MODULE(virtio_console, virtio_pci, vtcon_driver, vtcon_devclass, 2516f744ddeSBryan Venteicher vtcon_modevent, 0); 2526f744ddeSBryan Venteicher MODULE_VERSION(virtio_console, 1); 2536f744ddeSBryan Venteicher MODULE_DEPEND(virtio_console, virtio, 1, 1, 1); 2546f744ddeSBryan Venteicher 2556f744ddeSBryan Venteicher static int 2566f744ddeSBryan Venteicher vtcon_modevent(module_t mod, int type, void *unused) 2576f744ddeSBryan Venteicher { 2586f744ddeSBryan Venteicher int error; 2596f744ddeSBryan Venteicher 2606f744ddeSBryan Venteicher switch (type) { 2616f744ddeSBryan Venteicher case MOD_LOAD: 2626f744ddeSBryan Venteicher error = 0; 2636f744ddeSBryan Venteicher break; 2646f744ddeSBryan Venteicher case MOD_QUIESCE: 26504434c94SBryan Venteicher error = 0; 26604434c94SBryan Venteicher break; 2676f744ddeSBryan Venteicher case MOD_UNLOAD: 26804434c94SBryan Venteicher vtcon_drain_all(); 26904434c94SBryan Venteicher error = 0; 2706f744ddeSBryan Venteicher break; 2716f744ddeSBryan Venteicher case MOD_SHUTDOWN: 2726f744ddeSBryan Venteicher error = 0; 2736f744ddeSBryan Venteicher break; 2746f744ddeSBryan Venteicher default: 2756f744ddeSBryan Venteicher error = EOPNOTSUPP; 2766f744ddeSBryan Venteicher break; 2776f744ddeSBryan Venteicher } 2786f744ddeSBryan Venteicher 2796f744ddeSBryan Venteicher return (error); 2806f744ddeSBryan Venteicher } 2816f744ddeSBryan Venteicher 28204434c94SBryan Venteicher static void 28304434c94SBryan Venteicher vtcon_drain_all(void) 28404434c94SBryan Venteicher { 28504434c94SBryan Venteicher int first; 28604434c94SBryan Venteicher 28704434c94SBryan Venteicher for (first = 1; vtcon_pending_free != 0; first = 0) { 28804434c94SBryan Venteicher if (first != 0) { 28904434c94SBryan Venteicher printf("virtio_console: Waiting for all detached TTY " 29004434c94SBryan Venteicher "devices to have open fds closed.\n"); 29104434c94SBryan Venteicher } 29204434c94SBryan Venteicher pause("vtcondra", hz); 29304434c94SBryan Venteicher } 29404434c94SBryan Venteicher } 29504434c94SBryan Venteicher 2966f744ddeSBryan Venteicher static int 2976f744ddeSBryan Venteicher vtcon_probe(device_t dev) 2986f744ddeSBryan Venteicher { 2996f744ddeSBryan Venteicher 3006f744ddeSBryan Venteicher if (virtio_get_device_type(dev) != VIRTIO_ID_CONSOLE) 3016f744ddeSBryan Venteicher return (ENXIO); 3026f744ddeSBryan Venteicher 3036f744ddeSBryan Venteicher device_set_desc(dev, "VirtIO Console Adapter"); 3046f744ddeSBryan Venteicher 3056f744ddeSBryan Venteicher return (BUS_PROBE_DEFAULT); 3066f744ddeSBryan Venteicher } 3076f744ddeSBryan Venteicher 3086f744ddeSBryan Venteicher static int 3096f744ddeSBryan Venteicher vtcon_attach(device_t dev) 3106f744ddeSBryan Venteicher { 3116f744ddeSBryan Venteicher struct vtcon_softc *sc; 3126f744ddeSBryan Venteicher struct virtio_console_config concfg; 3136f744ddeSBryan Venteicher int error; 3146f744ddeSBryan Venteicher 3156f744ddeSBryan Venteicher sc = device_get_softc(dev); 3166f744ddeSBryan Venteicher sc->vtcon_dev = dev; 3176f744ddeSBryan Venteicher 31804434c94SBryan Venteicher mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF); 31904434c94SBryan Venteicher mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF); 3206f744ddeSBryan Venteicher 3216f744ddeSBryan Venteicher virtio_set_feature_desc(dev, vtcon_feature_desc); 32204434c94SBryan Venteicher vtcon_setup_features(sc); 3236f744ddeSBryan Venteicher 3246f744ddeSBryan Venteicher vtcon_read_config(sc, &concfg); 3256f744ddeSBryan Venteicher vtcon_determine_max_ports(sc, &concfg); 3266f744ddeSBryan Venteicher 32704434c94SBryan Venteicher error = vtcon_alloc_scports(sc); 32804434c94SBryan Venteicher if (error) { 32904434c94SBryan Venteicher device_printf(dev, "cannot allocate softc port structures\n"); 33004434c94SBryan Venteicher goto fail; 33104434c94SBryan Venteicher } 33204434c94SBryan Venteicher 3336f744ddeSBryan Venteicher error = vtcon_alloc_virtqueues(sc); 3346f744ddeSBryan Venteicher if (error) { 3356f744ddeSBryan Venteicher device_printf(dev, "cannot allocate virtqueues\n"); 3366f744ddeSBryan Venteicher goto fail; 3376f744ddeSBryan Venteicher } 3386f744ddeSBryan Venteicher 33904434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 34004434c94SBryan Venteicher TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc); 3416f744ddeSBryan Venteicher error = vtcon_ctrl_init(sc); 342b84b3efdSBryan Venteicher if (error) 343b84b3efdSBryan Venteicher goto fail; 344b84b3efdSBryan Venteicher } else { 34504434c94SBryan Venteicher error = vtcon_port_create(sc, 0); 3466f744ddeSBryan Venteicher if (error) 3476f744ddeSBryan Venteicher goto fail; 348b84b3efdSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_SIZE) 349b84b3efdSBryan Venteicher vtcon_port_update_console_size(sc); 350b84b3efdSBryan Venteicher } 3516f744ddeSBryan Venteicher 3526f744ddeSBryan Venteicher error = virtio_setup_intr(dev, INTR_TYPE_TTY); 3536f744ddeSBryan Venteicher if (error) { 3546f744ddeSBryan Venteicher device_printf(dev, "cannot setup virtqueue interrupts\n"); 3556f744ddeSBryan Venteicher goto fail; 3566f744ddeSBryan Venteicher } 3576f744ddeSBryan Venteicher 3586f744ddeSBryan Venteicher vtcon_enable_interrupts(sc); 3596f744ddeSBryan Venteicher 36004434c94SBryan Venteicher vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID, 3616f744ddeSBryan Venteicher VIRTIO_CONSOLE_DEVICE_READY, 1); 3626f744ddeSBryan Venteicher 3636f744ddeSBryan Venteicher fail: 3646f744ddeSBryan Venteicher if (error) 3656f744ddeSBryan Venteicher vtcon_detach(dev); 3666f744ddeSBryan Venteicher 3676f744ddeSBryan Venteicher return (error); 3686f744ddeSBryan Venteicher } 3696f744ddeSBryan Venteicher 3706f744ddeSBryan Venteicher static int 3716f744ddeSBryan Venteicher vtcon_detach(device_t dev) 3726f744ddeSBryan Venteicher { 3736f744ddeSBryan Venteicher struct vtcon_softc *sc; 3746f744ddeSBryan Venteicher 3756f744ddeSBryan Venteicher sc = device_get_softc(dev); 3766f744ddeSBryan Venteicher 3776f744ddeSBryan Venteicher VTCON_LOCK(sc); 3786f744ddeSBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_DETACHED; 3796f744ddeSBryan Venteicher if (device_is_attached(dev)) 3806f744ddeSBryan Venteicher vtcon_stop(sc); 3816f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 3826f744ddeSBryan Venteicher 38304434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 3846f744ddeSBryan Venteicher taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task); 3856f744ddeSBryan Venteicher vtcon_ctrl_deinit(sc); 38604434c94SBryan Venteicher } 3876f744ddeSBryan Venteicher 38804434c94SBryan Venteicher vtcon_destroy_ports(sc); 38904434c94SBryan Venteicher mtx_destroy(&sc->vtcon_mtx); 39004434c94SBryan Venteicher mtx_destroy(&sc->vtcon_ctrl_tx_mtx); 3916f744ddeSBryan Venteicher 3926f744ddeSBryan Venteicher return (0); 3936f744ddeSBryan Venteicher } 3946f744ddeSBryan Venteicher 3956f744ddeSBryan Venteicher static int 3966f744ddeSBryan Venteicher vtcon_config_change(device_t dev) 3976f744ddeSBryan Venteicher { 3986f744ddeSBryan Venteicher struct vtcon_softc *sc; 3996f744ddeSBryan Venteicher 4006f744ddeSBryan Venteicher sc = device_get_softc(dev); 4016f744ddeSBryan Venteicher 4026f744ddeSBryan Venteicher /* 40304434c94SBryan Venteicher * When the multiport feature is negotiated, all configuration 40404434c94SBryan Venteicher * changes are done through control virtqueue events. 4056f744ddeSBryan Venteicher */ 40604434c94SBryan Venteicher if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) { 40704434c94SBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_SIZE) 40804434c94SBryan Venteicher vtcon_port_update_console_size(sc); 4096f744ddeSBryan Venteicher } 4106f744ddeSBryan Venteicher 4116f744ddeSBryan Venteicher return (0); 4126f744ddeSBryan Venteicher } 4136f744ddeSBryan Venteicher 4146f744ddeSBryan Venteicher static void 4156f744ddeSBryan Venteicher vtcon_negotiate_features(struct vtcon_softc *sc) 4166f744ddeSBryan Venteicher { 4176f744ddeSBryan Venteicher device_t dev; 4186f744ddeSBryan Venteicher uint64_t features; 4196f744ddeSBryan Venteicher 4206f744ddeSBryan Venteicher dev = sc->vtcon_dev; 4216f744ddeSBryan Venteicher features = VTCON_FEATURES; 4226f744ddeSBryan Venteicher 4236f744ddeSBryan Venteicher sc->vtcon_features = virtio_negotiate_features(dev, features); 4246f744ddeSBryan Venteicher } 4256f744ddeSBryan Venteicher 42604434c94SBryan Venteicher static void 42704434c94SBryan Venteicher vtcon_setup_features(struct vtcon_softc *sc) 42804434c94SBryan Venteicher { 42904434c94SBryan Venteicher device_t dev; 43004434c94SBryan Venteicher 43104434c94SBryan Venteicher dev = sc->vtcon_dev; 43204434c94SBryan Venteicher 43304434c94SBryan Venteicher vtcon_negotiate_features(sc); 43404434c94SBryan Venteicher 43504434c94SBryan Venteicher if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE)) 43604434c94SBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_SIZE; 43704434c94SBryan Venteicher if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT)) 43804434c94SBryan Venteicher sc->vtcon_flags |= VTCON_FLAG_MULTIPORT; 43904434c94SBryan Venteicher } 44004434c94SBryan Venteicher 4416f744ddeSBryan Venteicher #define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg) \ 4426f744ddeSBryan Venteicher if (virtio_with_feature(_dev, _feature)) { \ 4436f744ddeSBryan Venteicher virtio_read_device_config(_dev, \ 4446f744ddeSBryan Venteicher offsetof(struct virtio_console_config, _field), \ 4456f744ddeSBryan Venteicher &(_cfg)->_field, sizeof((_cfg)->_field)); \ 4466f744ddeSBryan Venteicher } 4476f744ddeSBryan Venteicher 4486f744ddeSBryan Venteicher static void 4496f744ddeSBryan Venteicher vtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg) 4506f744ddeSBryan Venteicher { 4516f744ddeSBryan Venteicher device_t dev; 4526f744ddeSBryan Venteicher 4536f744ddeSBryan Venteicher dev = sc->vtcon_dev; 4546f744ddeSBryan Venteicher 4556f744ddeSBryan Venteicher bzero(concfg, sizeof(struct virtio_console_config)); 4566f744ddeSBryan Venteicher 4576f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg); 4586f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg); 4596f744ddeSBryan Venteicher VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg); 4606f744ddeSBryan Venteicher } 4616f744ddeSBryan Venteicher 4626f744ddeSBryan Venteicher #undef VTCON_GET_CONFIG 4636f744ddeSBryan Venteicher 4646f744ddeSBryan Venteicher static int 46504434c94SBryan Venteicher vtcon_alloc_scports(struct vtcon_softc *sc) 46604434c94SBryan Venteicher { 46704434c94SBryan Venteicher struct vtcon_softc_port *scport; 46804434c94SBryan Venteicher int max, i; 46904434c94SBryan Venteicher 47004434c94SBryan Venteicher max = sc->vtcon_max_ports; 47104434c94SBryan Venteicher 47204434c94SBryan Venteicher sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max, 47304434c94SBryan Venteicher M_DEVBUF, M_NOWAIT | M_ZERO); 47404434c94SBryan Venteicher if (sc->vtcon_ports == NULL) 47504434c94SBryan Venteicher return (ENOMEM); 47604434c94SBryan Venteicher 47704434c94SBryan Venteicher for (i = 0; i < max; i++) { 47804434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 47904434c94SBryan Venteicher scport->vcsp_sc = sc; 48004434c94SBryan Venteicher } 48104434c94SBryan Venteicher 48204434c94SBryan Venteicher return (0); 48304434c94SBryan Venteicher } 48404434c94SBryan Venteicher 48504434c94SBryan Venteicher static int 4866f744ddeSBryan Venteicher vtcon_alloc_virtqueues(struct vtcon_softc *sc) 4876f744ddeSBryan Venteicher { 4886f744ddeSBryan Venteicher device_t dev; 4896f744ddeSBryan Venteicher struct vq_alloc_info *info; 49004434c94SBryan Venteicher struct vtcon_softc_port *scport; 4916f744ddeSBryan Venteicher int i, idx, portidx, nvqs, error; 4926f744ddeSBryan Venteicher 4936f744ddeSBryan Venteicher dev = sc->vtcon_dev; 4946f744ddeSBryan Venteicher 4956f744ddeSBryan Venteicher nvqs = sc->vtcon_max_ports * 2; 4966f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 4976f744ddeSBryan Venteicher nvqs += 2; 4986f744ddeSBryan Venteicher 4996f744ddeSBryan Venteicher info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT); 5006f744ddeSBryan Venteicher if (info == NULL) 5016f744ddeSBryan Venteicher return (ENOMEM); 5026f744ddeSBryan Venteicher 5036f744ddeSBryan Venteicher for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) { 5046f744ddeSBryan Venteicher 5056f744ddeSBryan Venteicher if (i == 1) { 5066f744ddeSBryan Venteicher /* The control virtqueues are after the first port. */ 5076f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx], 0, 50804434c94SBryan Venteicher vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq, 5096f744ddeSBryan Venteicher "%s-control rx", device_get_nameunit(dev)); 5106f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx+1], 0, 5116f744ddeSBryan Venteicher NULL, sc, &sc->vtcon_ctrl_txvq, 5126f744ddeSBryan Venteicher "%s-control tx", device_get_nameunit(dev)); 5136f744ddeSBryan Venteicher continue; 5146f744ddeSBryan Venteicher } 5156f744ddeSBryan Venteicher 51604434c94SBryan Venteicher scport = &sc->vtcon_ports[portidx]; 5176f744ddeSBryan Venteicher 51804434c94SBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr, 51904434c94SBryan Venteicher scport, &scport->vcsp_invq, "%s-port%d in", 52004434c94SBryan Venteicher device_get_nameunit(dev), i); 5216f744ddeSBryan Venteicher VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL, 52204434c94SBryan Venteicher NULL, &scport->vcsp_outvq, "%s-port%d out", 52304434c94SBryan Venteicher device_get_nameunit(dev), i); 5246f744ddeSBryan Venteicher 5256f744ddeSBryan Venteicher portidx++; 5266f744ddeSBryan Venteicher } 5276f744ddeSBryan Venteicher 5286f744ddeSBryan Venteicher error = virtio_alloc_virtqueues(dev, 0, nvqs, info); 5296f744ddeSBryan Venteicher free(info, M_TEMP); 5306f744ddeSBryan Venteicher 5316f744ddeSBryan Venteicher return (error); 5326f744ddeSBryan Venteicher } 5336f744ddeSBryan Venteicher 5346f744ddeSBryan Venteicher static void 5356f744ddeSBryan Venteicher vtcon_determine_max_ports(struct vtcon_softc *sc, 5366f744ddeSBryan Venteicher struct virtio_console_config *concfg) 5376f744ddeSBryan Venteicher { 5386f744ddeSBryan Venteicher 5396f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 5406f744ddeSBryan Venteicher sc->vtcon_max_ports = 5416f744ddeSBryan Venteicher min(concfg->max_nr_ports, VTCON_MAX_PORTS); 5426f744ddeSBryan Venteicher if (sc->vtcon_max_ports == 0) 5436f744ddeSBryan Venteicher sc->vtcon_max_ports = 1; 5446f744ddeSBryan Venteicher } else 5456f744ddeSBryan Venteicher sc->vtcon_max_ports = 1; 5466f744ddeSBryan Venteicher } 5476f744ddeSBryan Venteicher 5486f744ddeSBryan Venteicher static void 54904434c94SBryan Venteicher vtcon_destroy_ports(struct vtcon_softc *sc) 5506f744ddeSBryan Venteicher { 55104434c94SBryan Venteicher struct vtcon_softc_port *scport; 55204434c94SBryan Venteicher struct vtcon_port *port; 55304434c94SBryan Venteicher struct virtqueue *vq; 55404434c94SBryan Venteicher int i; 5556f744ddeSBryan Venteicher 55604434c94SBryan Venteicher if (sc->vtcon_ports == NULL) 55704434c94SBryan Venteicher return; 55804434c94SBryan Venteicher 55904434c94SBryan Venteicher VTCON_LOCK(sc); 56004434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 56104434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 56204434c94SBryan Venteicher 56304434c94SBryan Venteicher port = scport->vcsp_port; 56404434c94SBryan Venteicher if (port != NULL) { 56504434c94SBryan Venteicher scport->vcsp_port = NULL; 56604434c94SBryan Venteicher VTCON_PORT_LOCK(port); 56704434c94SBryan Venteicher VTCON_UNLOCK(sc); 56804434c94SBryan Venteicher vtcon_port_teardown(port); 56904434c94SBryan Venteicher VTCON_LOCK(sc); 5706f744ddeSBryan Venteicher } 5716f744ddeSBryan Venteicher 57204434c94SBryan Venteicher vq = scport->vcsp_invq; 57304434c94SBryan Venteicher if (vq != NULL) 57404434c94SBryan Venteicher vtcon_port_drain_bufs(vq); 5756f744ddeSBryan Venteicher } 57604434c94SBryan Venteicher VTCON_UNLOCK(sc); 57704434c94SBryan Venteicher 57804434c94SBryan Venteicher free(sc->vtcon_ports, M_DEVBUF); 57904434c94SBryan Venteicher sc->vtcon_ports = NULL; 5806f744ddeSBryan Venteicher } 5816f744ddeSBryan Venteicher 5826f744ddeSBryan Venteicher static void 5836f744ddeSBryan Venteicher vtcon_stop(struct vtcon_softc *sc) 5846f744ddeSBryan Venteicher { 5856f744ddeSBryan Venteicher 5866f744ddeSBryan Venteicher vtcon_disable_interrupts(sc); 5876f744ddeSBryan Venteicher virtio_stop(sc->vtcon_dev); 5886f744ddeSBryan Venteicher } 5896f744ddeSBryan Venteicher 5906f744ddeSBryan Venteicher static int 59104434c94SBryan Venteicher vtcon_ctrl_event_enqueue(struct vtcon_softc *sc, 5926f744ddeSBryan Venteicher struct virtio_console_control *control) 5936f744ddeSBryan Venteicher { 59404434c94SBryan Venteicher struct sglist_seg segs[2]; 5956f744ddeSBryan Venteicher struct sglist sg; 5966f744ddeSBryan Venteicher struct virtqueue *vq; 59704434c94SBryan Venteicher int error; 5986f744ddeSBryan Venteicher 5996f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 6006f744ddeSBryan Venteicher 60104434c94SBryan Venteicher sglist_init(&sg, 2, segs); 60204434c94SBryan Venteicher error = sglist_append(&sg, control, 60304434c94SBryan Venteicher sizeof(struct virtio_console_control)); 60404434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding control to sglist", 60504434c94SBryan Venteicher __func__, error)); 6066f744ddeSBryan Venteicher 60704434c94SBryan Venteicher return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg)); 6086f744ddeSBryan Venteicher } 6096f744ddeSBryan Venteicher 6106f744ddeSBryan Venteicher static int 61104434c94SBryan Venteicher vtcon_ctrl_event_create(struct vtcon_softc *sc) 6126f744ddeSBryan Venteicher { 6136f744ddeSBryan Venteicher struct virtio_console_control *control; 6146f744ddeSBryan Venteicher int error; 6156f744ddeSBryan Venteicher 616*cb981343SJakub Wojciech Klama control = malloc( 617*cb981343SJakub Wojciech Klama sizeof(struct virtio_console_control) + VTCON_BULK_BUFSZ, 618*cb981343SJakub Wojciech Klama M_DEVBUF, M_ZERO | M_NOWAIT); 619*cb981343SJakub Wojciech Klama 6206f744ddeSBryan Venteicher if (control == NULL) 6216f744ddeSBryan Venteicher return (ENOMEM); 6226f744ddeSBryan Venteicher 62304434c94SBryan Venteicher error = vtcon_ctrl_event_enqueue(sc, control); 6246f744ddeSBryan Venteicher if (error) 6256f744ddeSBryan Venteicher free(control, M_DEVBUF); 6266f744ddeSBryan Venteicher 6276f744ddeSBryan Venteicher return (error); 6286f744ddeSBryan Venteicher } 6296f744ddeSBryan Venteicher 6306f744ddeSBryan Venteicher static void 63104434c94SBryan Venteicher vtcon_ctrl_event_requeue(struct vtcon_softc *sc, 6326f744ddeSBryan Venteicher struct virtio_console_control *control) 6336f744ddeSBryan Venteicher { 6346f744ddeSBryan Venteicher int error; 6356f744ddeSBryan Venteicher 63604434c94SBryan Venteicher bzero(control, sizeof(struct virtio_console_control)); 6376f744ddeSBryan Venteicher 63804434c94SBryan Venteicher error = vtcon_ctrl_event_enqueue(sc, control); 6396f744ddeSBryan Venteicher KASSERT(error == 0, 6406f744ddeSBryan Venteicher ("%s: cannot requeue control buffer %d", __func__, error)); 6416f744ddeSBryan Venteicher } 6426f744ddeSBryan Venteicher 6436f744ddeSBryan Venteicher static int 64404434c94SBryan Venteicher vtcon_ctrl_event_populate(struct vtcon_softc *sc) 6456f744ddeSBryan Venteicher { 6466f744ddeSBryan Venteicher struct virtqueue *vq; 6476f744ddeSBryan Venteicher int nbufs, error; 6486f744ddeSBryan Venteicher 6496f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 6506f744ddeSBryan Venteicher error = ENOSPC; 6516f744ddeSBryan Venteicher 6526f744ddeSBryan Venteicher for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 65304434c94SBryan Venteicher error = vtcon_ctrl_event_create(sc); 6546f744ddeSBryan Venteicher if (error) 6556f744ddeSBryan Venteicher break; 6566f744ddeSBryan Venteicher } 6576f744ddeSBryan Venteicher 6586f744ddeSBryan Venteicher if (nbufs > 0) { 6596f744ddeSBryan Venteicher virtqueue_notify(vq); 6606f744ddeSBryan Venteicher error = 0; 6616f744ddeSBryan Venteicher } 6626f744ddeSBryan Venteicher 6636f744ddeSBryan Venteicher return (error); 6646f744ddeSBryan Venteicher } 6656f744ddeSBryan Venteicher 6666f744ddeSBryan Venteicher static void 66704434c94SBryan Venteicher vtcon_ctrl_event_drain(struct vtcon_softc *sc) 6686f744ddeSBryan Venteicher { 6696f744ddeSBryan Venteicher struct virtio_console_control *control; 6706f744ddeSBryan Venteicher struct virtqueue *vq; 6716f744ddeSBryan Venteicher int last; 6726f744ddeSBryan Venteicher 6736f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 6746f744ddeSBryan Venteicher last = 0; 6756f744ddeSBryan Venteicher 6766f744ddeSBryan Venteicher if (vq == NULL) 6776f744ddeSBryan Venteicher return; 6786f744ddeSBryan Venteicher 67904434c94SBryan Venteicher VTCON_LOCK(sc); 6806f744ddeSBryan Venteicher while ((control = virtqueue_drain(vq, &last)) != NULL) 6816f744ddeSBryan Venteicher free(control, M_DEVBUF); 68204434c94SBryan Venteicher VTCON_UNLOCK(sc); 68304434c94SBryan Venteicher } 68404434c94SBryan Venteicher 68504434c94SBryan Venteicher static int 68604434c94SBryan Venteicher vtcon_ctrl_init(struct vtcon_softc *sc) 68704434c94SBryan Venteicher { 68804434c94SBryan Venteicher int error; 68904434c94SBryan Venteicher 69004434c94SBryan Venteicher error = vtcon_ctrl_event_populate(sc); 69104434c94SBryan Venteicher 69204434c94SBryan Venteicher return (error); 6936f744ddeSBryan Venteicher } 6946f744ddeSBryan Venteicher 6956f744ddeSBryan Venteicher static void 6966f744ddeSBryan Venteicher vtcon_ctrl_deinit(struct vtcon_softc *sc) 6976f744ddeSBryan Venteicher { 6986f744ddeSBryan Venteicher 69904434c94SBryan Venteicher vtcon_ctrl_event_drain(sc); 7006f744ddeSBryan Venteicher } 7016f744ddeSBryan Venteicher 7026f744ddeSBryan Venteicher static void 7036f744ddeSBryan Venteicher vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id) 7046f744ddeSBryan Venteicher { 7056f744ddeSBryan Venteicher device_t dev; 7066f744ddeSBryan Venteicher int error; 7076f744ddeSBryan Venteicher 7086f744ddeSBryan Venteicher dev = sc->vtcon_dev; 7096f744ddeSBryan Venteicher 71004434c94SBryan Venteicher /* This single thread only way for ports to be created. */ 71104434c94SBryan Venteicher if (sc->vtcon_ports[id].vcsp_port != NULL) { 7126f744ddeSBryan Venteicher device_printf(dev, "%s: adding port %d, but already exists\n", 7136f744ddeSBryan Venteicher __func__, id); 7146f744ddeSBryan Venteicher return; 7156f744ddeSBryan Venteicher } 7166f744ddeSBryan Venteicher 71704434c94SBryan Venteicher error = vtcon_port_create(sc, id); 7186f744ddeSBryan Venteicher if (error) { 7196f744ddeSBryan Venteicher device_printf(dev, "%s: cannot create port %d: %d\n", 7206f744ddeSBryan Venteicher __func__, id, error); 721b84b3efdSBryan Venteicher vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0); 7226f744ddeSBryan Venteicher return; 7236f744ddeSBryan Venteicher } 7246f744ddeSBryan Venteicher } 7256f744ddeSBryan Venteicher 7266f744ddeSBryan Venteicher static void 7276f744ddeSBryan Venteicher vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id) 7286f744ddeSBryan Venteicher { 7296f744ddeSBryan Venteicher device_t dev; 73004434c94SBryan Venteicher struct vtcon_softc_port *scport; 7316f744ddeSBryan Venteicher struct vtcon_port *port; 7326f744ddeSBryan Venteicher 7336f744ddeSBryan Venteicher dev = sc->vtcon_dev; 73404434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 7356f744ddeSBryan Venteicher 73604434c94SBryan Venteicher VTCON_LOCK(sc); 73704434c94SBryan Venteicher port = scport->vcsp_port; 7386f744ddeSBryan Venteicher if (port == NULL) { 73904434c94SBryan Venteicher VTCON_UNLOCK(sc); 7406f744ddeSBryan Venteicher device_printf(dev, "%s: remove port %d, but does not exist\n", 7416f744ddeSBryan Venteicher __func__, id); 7426f744ddeSBryan Venteicher return; 7436f744ddeSBryan Venteicher } 7446f744ddeSBryan Venteicher 74504434c94SBryan Venteicher scport->vcsp_port = NULL; 74604434c94SBryan Venteicher VTCON_PORT_LOCK(port); 74704434c94SBryan Venteicher VTCON_UNLOCK(sc); 74804434c94SBryan Venteicher vtcon_port_teardown(port); 7496f744ddeSBryan Venteicher } 7506f744ddeSBryan Venteicher 7516f744ddeSBryan Venteicher static void 7526f744ddeSBryan Venteicher vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id) 7536f744ddeSBryan Venteicher { 754b84b3efdSBryan Venteicher device_t dev; 755b84b3efdSBryan Venteicher struct vtcon_softc_port *scport; 756b84b3efdSBryan Venteicher struct vtcon_port *port; 7576f744ddeSBryan Venteicher 758b84b3efdSBryan Venteicher dev = sc->vtcon_dev; 759b84b3efdSBryan Venteicher scport = &sc->vtcon_ports[id]; 760b84b3efdSBryan Venteicher 761b84b3efdSBryan Venteicher VTCON_LOCK(sc); 762b84b3efdSBryan Venteicher port = scport->vcsp_port; 763b84b3efdSBryan Venteicher if (port == NULL) { 764b84b3efdSBryan Venteicher VTCON_UNLOCK(sc); 765b84b3efdSBryan Venteicher device_printf(dev, "%s: console port %d, but does not exist\n", 76604434c94SBryan Venteicher __func__, id); 767b84b3efdSBryan Venteicher return; 768b84b3efdSBryan Venteicher } 769b84b3efdSBryan Venteicher 770b84b3efdSBryan Venteicher VTCON_PORT_LOCK(port); 771b84b3efdSBryan Venteicher VTCON_UNLOCK(sc); 772b84b3efdSBryan Venteicher port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE; 773b84b3efdSBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 774b84b3efdSBryan Venteicher VTCON_PORT_UNLOCK(port); 7756f744ddeSBryan Venteicher } 7766f744ddeSBryan Venteicher 7776f744ddeSBryan Venteicher static void 7786f744ddeSBryan Venteicher vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id) 7796f744ddeSBryan Venteicher { 7806f744ddeSBryan Venteicher device_t dev; 78104434c94SBryan Venteicher struct vtcon_softc_port *scport; 7826f744ddeSBryan Venteicher struct vtcon_port *port; 7836f744ddeSBryan Venteicher 7846f744ddeSBryan Venteicher dev = sc->vtcon_dev; 78504434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 7866f744ddeSBryan Venteicher 78704434c94SBryan Venteicher VTCON_LOCK(sc); 78804434c94SBryan Venteicher port = scport->vcsp_port; 7896f744ddeSBryan Venteicher if (port == NULL) { 79004434c94SBryan Venteicher VTCON_UNLOCK(sc); 7916f744ddeSBryan Venteicher device_printf(dev, "%s: open port %d, but does not exist\n", 7926f744ddeSBryan Venteicher __func__, id); 7936f744ddeSBryan Venteicher return; 7946f744ddeSBryan Venteicher } 7956f744ddeSBryan Venteicher 79604434c94SBryan Venteicher VTCON_PORT_LOCK(port); 79704434c94SBryan Venteicher VTCON_UNLOCK(sc); 7986f744ddeSBryan Venteicher vtcon_port_enable_intr(port); 79904434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 8006f744ddeSBryan Venteicher } 8016f744ddeSBryan Venteicher 8026f744ddeSBryan Venteicher static void 803*cb981343SJakub Wojciech Klama vtcon_ctrl_port_name_event(struct vtcon_softc *sc, int id, const char *name, 804*cb981343SJakub Wojciech Klama size_t len) 805*cb981343SJakub Wojciech Klama { 806*cb981343SJakub Wojciech Klama device_t dev; 807*cb981343SJakub Wojciech Klama struct vtcon_softc_port *scport; 808*cb981343SJakub Wojciech Klama struct vtcon_port *port; 809*cb981343SJakub Wojciech Klama 810*cb981343SJakub Wojciech Klama dev = sc->vtcon_dev; 811*cb981343SJakub Wojciech Klama scport = &sc->vtcon_ports[id]; 812*cb981343SJakub Wojciech Klama 813*cb981343SJakub Wojciech Klama port = scport->vcsp_port; 814*cb981343SJakub Wojciech Klama if (port == NULL) { 815*cb981343SJakub Wojciech Klama device_printf(dev, "%s: name port %d, but does not exist\n", 816*cb981343SJakub Wojciech Klama __func__, id); 817*cb981343SJakub Wojciech Klama return; 818*cb981343SJakub Wojciech Klama } 819*cb981343SJakub Wojciech Klama 820*cb981343SJakub Wojciech Klama tty_makealias(port->vtcport_tty, "vtcon/%*s", (int)len, name); 821*cb981343SJakub Wojciech Klama } 822*cb981343SJakub Wojciech Klama 823*cb981343SJakub Wojciech Klama static void 82404434c94SBryan Venteicher vtcon_ctrl_process_event(struct vtcon_softc *sc, 825*cb981343SJakub Wojciech Klama struct virtio_console_control *control, void *payload, size_t plen) 8266f744ddeSBryan Venteicher { 8276f744ddeSBryan Venteicher device_t dev; 8286f744ddeSBryan Venteicher int id; 8296f744ddeSBryan Venteicher 8306f744ddeSBryan Venteicher dev = sc->vtcon_dev; 8316f744ddeSBryan Venteicher id = control->id; 8326f744ddeSBryan Venteicher 8336f744ddeSBryan Venteicher if (id < 0 || id >= sc->vtcon_max_ports) { 8346f744ddeSBryan Venteicher device_printf(dev, "%s: invalid port ID %d\n", __func__, id); 8356f744ddeSBryan Venteicher return; 8366f744ddeSBryan Venteicher } 8376f744ddeSBryan Venteicher 8386f744ddeSBryan Venteicher switch (control->event) { 8396f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_ADD: 8406f744ddeSBryan Venteicher vtcon_ctrl_port_add_event(sc, id); 8416f744ddeSBryan Venteicher break; 8426f744ddeSBryan Venteicher 8436f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_REMOVE: 8446f744ddeSBryan Venteicher vtcon_ctrl_port_remove_event(sc, id); 8456f744ddeSBryan Venteicher break; 8466f744ddeSBryan Venteicher 8476f744ddeSBryan Venteicher case VIRTIO_CONSOLE_CONSOLE_PORT: 8486f744ddeSBryan Venteicher vtcon_ctrl_port_console_event(sc, id); 8496f744ddeSBryan Venteicher break; 8506f744ddeSBryan Venteicher 8516f744ddeSBryan Venteicher case VIRTIO_CONSOLE_RESIZE: 8526f744ddeSBryan Venteicher break; 8536f744ddeSBryan Venteicher 8546f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_OPEN: 8556f744ddeSBryan Venteicher vtcon_ctrl_port_open_event(sc, id); 8566f744ddeSBryan Venteicher break; 8576f744ddeSBryan Venteicher 8586f744ddeSBryan Venteicher case VIRTIO_CONSOLE_PORT_NAME: 859*cb981343SJakub Wojciech Klama if (payload != NULL && plen > 0) 860*cb981343SJakub Wojciech Klama vtcon_ctrl_port_name_event(sc, id, 861*cb981343SJakub Wojciech Klama (const char *)payload, plen); 8626f744ddeSBryan Venteicher break; 8636f744ddeSBryan Venteicher } 8646f744ddeSBryan Venteicher } 8656f744ddeSBryan Venteicher 8666f744ddeSBryan Venteicher static void 8676f744ddeSBryan Venteicher vtcon_ctrl_task_cb(void *xsc, int pending) 8686f744ddeSBryan Venteicher { 8696f744ddeSBryan Venteicher struct vtcon_softc *sc; 8706f744ddeSBryan Venteicher struct virtqueue *vq; 8716f744ddeSBryan Venteicher struct virtio_console_control *control; 87204434c94SBryan Venteicher int detached; 873*cb981343SJakub Wojciech Klama uint32_t len; 874*cb981343SJakub Wojciech Klama size_t plen; 875*cb981343SJakub Wojciech Klama void *payload; 8766f744ddeSBryan Venteicher 8776f744ddeSBryan Venteicher sc = xsc; 8786f744ddeSBryan Venteicher vq = sc->vtcon_ctrl_rxvq; 8796f744ddeSBryan Venteicher 8806f744ddeSBryan Venteicher VTCON_LOCK(sc); 88104434c94SBryan Venteicher 88204434c94SBryan Venteicher while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) { 883*cb981343SJakub Wojciech Klama control = virtqueue_dequeue(vq, &len); 884*cb981343SJakub Wojciech Klama payload = NULL; 885*cb981343SJakub Wojciech Klama plen = 0; 886*cb981343SJakub Wojciech Klama 8876f744ddeSBryan Venteicher if (control == NULL) 8886f744ddeSBryan Venteicher break; 8896f744ddeSBryan Venteicher 890*cb981343SJakub Wojciech Klama if (len > sizeof(control)) { 891*cb981343SJakub Wojciech Klama payload = (void *)(control + 1); 892*cb981343SJakub Wojciech Klama plen = len - sizeof(control); 893*cb981343SJakub Wojciech Klama } 894*cb981343SJakub Wojciech Klama 8956f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 896*cb981343SJakub Wojciech Klama vtcon_ctrl_process_event(sc, control, payload, plen); 8976f744ddeSBryan Venteicher VTCON_LOCK(sc); 89804434c94SBryan Venteicher vtcon_ctrl_event_requeue(sc, control); 8996f744ddeSBryan Venteicher } 9006f744ddeSBryan Venteicher 90104434c94SBryan Venteicher if (!detached) { 90204434c94SBryan Venteicher virtqueue_notify(vq); 9036f744ddeSBryan Venteicher if (virtqueue_enable_intr(vq) != 0) 90404434c94SBryan Venteicher taskqueue_enqueue(taskqueue_thread, 90504434c94SBryan Venteicher &sc->vtcon_ctrl_task); 90604434c94SBryan Venteicher } 90704434c94SBryan Venteicher 90804434c94SBryan Venteicher VTCON_UNLOCK(sc); 90904434c94SBryan Venteicher } 91004434c94SBryan Venteicher 91104434c94SBryan Venteicher static void 91204434c94SBryan Venteicher vtcon_ctrl_event_intr(void *xsc) 91304434c94SBryan Venteicher { 91404434c94SBryan Venteicher struct vtcon_softc *sc; 91504434c94SBryan Venteicher 91604434c94SBryan Venteicher sc = xsc; 91704434c94SBryan Venteicher 91804434c94SBryan Venteicher /* 91904434c94SBryan Venteicher * Only some events require us to potentially block, but it 92004434c94SBryan Venteicher * easier to just defer all event handling to the taskqueue. 92104434c94SBryan Venteicher */ 9226f744ddeSBryan Venteicher taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task); 9236f744ddeSBryan Venteicher } 9246f744ddeSBryan Venteicher 92504434c94SBryan Venteicher static void 92604434c94SBryan Venteicher vtcon_ctrl_poll(struct vtcon_softc *sc, 92704434c94SBryan Venteicher struct virtio_console_control *control) 9286f744ddeSBryan Venteicher { 92904434c94SBryan Venteicher struct sglist_seg segs[2]; 93004434c94SBryan Venteicher struct sglist sg; 93104434c94SBryan Venteicher struct virtqueue *vq; 93204434c94SBryan Venteicher int error; 93304434c94SBryan Venteicher 93404434c94SBryan Venteicher vq = sc->vtcon_ctrl_txvq; 93504434c94SBryan Venteicher 93604434c94SBryan Venteicher sglist_init(&sg, 2, segs); 93704434c94SBryan Venteicher error = sglist_append(&sg, control, 93804434c94SBryan Venteicher sizeof(struct virtio_console_control)); 93904434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding control to sglist", 94004434c94SBryan Venteicher __func__, error)); 94104434c94SBryan Venteicher 94204434c94SBryan Venteicher /* 94304434c94SBryan Venteicher * We cannot use the softc lock to serialize access to this 94404434c94SBryan Venteicher * virtqueue since this is called from the tty layer with the 94504434c94SBryan Venteicher * port lock held. Acquiring the softc would violate our lock 94604434c94SBryan Venteicher * ordering. 94704434c94SBryan Venteicher */ 94804434c94SBryan Venteicher VTCON_CTRL_TX_LOCK(sc); 94904434c94SBryan Venteicher KASSERT(virtqueue_empty(vq), 95004434c94SBryan Venteicher ("%s: virtqueue is not emtpy", __func__)); 95104434c94SBryan Venteicher error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0); 95204434c94SBryan Venteicher if (error == 0) { 95304434c94SBryan Venteicher virtqueue_notify(vq); 95404434c94SBryan Venteicher virtqueue_poll(vq, NULL); 95504434c94SBryan Venteicher } 95604434c94SBryan Venteicher VTCON_CTRL_TX_UNLOCK(sc); 95704434c94SBryan Venteicher } 95804434c94SBryan Venteicher 95904434c94SBryan Venteicher static void 96004434c94SBryan Venteicher vtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid, 96104434c94SBryan Venteicher uint16_t event, uint16_t value) 96204434c94SBryan Venteicher { 96304434c94SBryan Venteicher struct virtio_console_control control; 96404434c94SBryan Venteicher 96504434c94SBryan Venteicher if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) 96604434c94SBryan Venteicher return; 96704434c94SBryan Venteicher 96804434c94SBryan Venteicher control.id = portid; 96904434c94SBryan Venteicher control.event = event; 97004434c94SBryan Venteicher control.value = value; 97104434c94SBryan Venteicher 97204434c94SBryan Venteicher vtcon_ctrl_poll(sc, &control); 97304434c94SBryan Venteicher } 97404434c94SBryan Venteicher 97504434c94SBryan Venteicher static int 97604434c94SBryan Venteicher vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len) 97704434c94SBryan Venteicher { 97804434c94SBryan Venteicher struct sglist_seg segs[2]; 9796f744ddeSBryan Venteicher struct sglist sg; 9806f744ddeSBryan Venteicher struct virtqueue *vq; 9816f744ddeSBryan Venteicher int error; 9826f744ddeSBryan Venteicher 9836f744ddeSBryan Venteicher vq = port->vtcport_invq; 9846f744ddeSBryan Venteicher 98504434c94SBryan Venteicher sglist_init(&sg, 2, segs); 9866f744ddeSBryan Venteicher error = sglist_append(&sg, buf, len); 98704434c94SBryan Venteicher KASSERT(error == 0, 9886f744ddeSBryan Venteicher ("%s: error %d adding buffer to sglist", __func__, error)); 9896f744ddeSBryan Venteicher 99004434c94SBryan Venteicher error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg); 99104434c94SBryan Venteicher 99204434c94SBryan Venteicher return (error); 9936f744ddeSBryan Venteicher } 9946f744ddeSBryan Venteicher 9956f744ddeSBryan Venteicher static int 99604434c94SBryan Venteicher vtcon_port_create_buf(struct vtcon_port *port) 9976f744ddeSBryan Venteicher { 9986f744ddeSBryan Venteicher void *buf; 9996f744ddeSBryan Venteicher int error; 10006f744ddeSBryan Venteicher 10016f744ddeSBryan Venteicher buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT); 10026f744ddeSBryan Venteicher if (buf == NULL) 10036f744ddeSBryan Venteicher return (ENOMEM); 10046f744ddeSBryan Venteicher 100504434c94SBryan Venteicher error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 10066f744ddeSBryan Venteicher if (error) 10076f744ddeSBryan Venteicher free(buf, M_DEVBUF); 10086f744ddeSBryan Venteicher 10096f744ddeSBryan Venteicher return (error); 10106f744ddeSBryan Venteicher } 10116f744ddeSBryan Venteicher 10126f744ddeSBryan Venteicher static void 101304434c94SBryan Venteicher vtcon_port_requeue_buf(struct vtcon_port *port, void *buf) 10146f744ddeSBryan Venteicher { 101504434c94SBryan Venteicher int error; 10166f744ddeSBryan Venteicher 101704434c94SBryan Venteicher error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 10186f744ddeSBryan Venteicher KASSERT(error == 0, 10196f744ddeSBryan Venteicher ("%s: cannot requeue input buffer %d", __func__, error)); 10206f744ddeSBryan Venteicher } 10216f744ddeSBryan Venteicher 10226f744ddeSBryan Venteicher static int 10236f744ddeSBryan Venteicher vtcon_port_populate(struct vtcon_port *port) 10246f744ddeSBryan Venteicher { 10256f744ddeSBryan Venteicher struct virtqueue *vq; 10266f744ddeSBryan Venteicher int nbufs, error; 10276f744ddeSBryan Venteicher 10286f744ddeSBryan Venteicher vq = port->vtcport_invq; 10296f744ddeSBryan Venteicher error = ENOSPC; 10306f744ddeSBryan Venteicher 10316f744ddeSBryan Venteicher for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 103204434c94SBryan Venteicher error = vtcon_port_create_buf(port); 10336f744ddeSBryan Venteicher if (error) 10346f744ddeSBryan Venteicher break; 10356f744ddeSBryan Venteicher } 10366f744ddeSBryan Venteicher 10376f744ddeSBryan Venteicher if (nbufs > 0) { 10386f744ddeSBryan Venteicher virtqueue_notify(vq); 10396f744ddeSBryan Venteicher error = 0; 10406f744ddeSBryan Venteicher } 10416f744ddeSBryan Venteicher 10426f744ddeSBryan Venteicher return (error); 10436f744ddeSBryan Venteicher } 10446f744ddeSBryan Venteicher 10456f744ddeSBryan Venteicher static void 10466f744ddeSBryan Venteicher vtcon_port_destroy(struct vtcon_port *port) 10476f744ddeSBryan Venteicher { 10486f744ddeSBryan Venteicher 10496f744ddeSBryan Venteicher port->vtcport_sc = NULL; 105004434c94SBryan Venteicher port->vtcport_scport = NULL; 105104434c94SBryan Venteicher port->vtcport_invq = NULL; 105204434c94SBryan Venteicher port->vtcport_outvq = NULL; 10536f744ddeSBryan Venteicher port->vtcport_id = -1; 105404434c94SBryan Venteicher mtx_destroy(&port->vtcport_mtx); 10556f744ddeSBryan Venteicher free(port, M_DEVBUF); 10566f744ddeSBryan Venteicher } 10576f744ddeSBryan Venteicher 10586f744ddeSBryan Venteicher static int 105904434c94SBryan Venteicher vtcon_port_init_vqs(struct vtcon_port *port) 106004434c94SBryan Venteicher { 106104434c94SBryan Venteicher struct vtcon_softc_port *scport; 106204434c94SBryan Venteicher int error; 106304434c94SBryan Venteicher 106404434c94SBryan Venteicher scport = port->vtcport_scport; 106504434c94SBryan Venteicher 106604434c94SBryan Venteicher port->vtcport_invq = scport->vcsp_invq; 106704434c94SBryan Venteicher port->vtcport_outvq = scport->vcsp_outvq; 106804434c94SBryan Venteicher 106904434c94SBryan Venteicher /* 107004434c94SBryan Venteicher * Free any data left over from when this virtqueue was in use by a 107104434c94SBryan Venteicher * prior port. We have not yet notified the host that the port is 107204434c94SBryan Venteicher * ready, so assume nothing in the virtqueue can be for us. 107304434c94SBryan Venteicher */ 107404434c94SBryan Venteicher vtcon_port_drain(port); 107504434c94SBryan Venteicher 107604434c94SBryan Venteicher KASSERT(virtqueue_empty(port->vtcport_invq), 107704434c94SBryan Venteicher ("%s: in virtqueue is not empty", __func__)); 107804434c94SBryan Venteicher KASSERT(virtqueue_empty(port->vtcport_outvq), 107904434c94SBryan Venteicher ("%s: out virtqueue is not empty", __func__)); 108004434c94SBryan Venteicher 108104434c94SBryan Venteicher error = vtcon_port_populate(port); 108204434c94SBryan Venteicher if (error) 108304434c94SBryan Venteicher return (error); 108404434c94SBryan Venteicher 108504434c94SBryan Venteicher return (0); 108604434c94SBryan Venteicher } 108704434c94SBryan Venteicher 108804434c94SBryan Venteicher static int 108904434c94SBryan Venteicher vtcon_port_create(struct vtcon_softc *sc, int id) 10906f744ddeSBryan Venteicher { 10916f744ddeSBryan Venteicher device_t dev; 109204434c94SBryan Venteicher struct vtcon_softc_port *scport; 10936f744ddeSBryan Venteicher struct vtcon_port *port; 10946f744ddeSBryan Venteicher int error; 10956f744ddeSBryan Venteicher 10966f744ddeSBryan Venteicher dev = sc->vtcon_dev; 109704434c94SBryan Venteicher scport = &sc->vtcon_ports[id]; 10986f744ddeSBryan Venteicher 109904434c94SBryan Venteicher VTCON_ASSERT_VALID_PORTID(sc, id); 110004434c94SBryan Venteicher MPASS(scport->vcsp_port == NULL); 11016f744ddeSBryan Venteicher 11026f744ddeSBryan Venteicher port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO); 11036f744ddeSBryan Venteicher if (port == NULL) 11046f744ddeSBryan Venteicher return (ENOMEM); 11056f744ddeSBryan Venteicher 11066f744ddeSBryan Venteicher port->vtcport_sc = sc; 110704434c94SBryan Venteicher port->vtcport_scport = scport; 11086f744ddeSBryan Venteicher port->vtcport_id = id; 110904434c94SBryan Venteicher mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF); 11106f744ddeSBryan Venteicher port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port, 11116f744ddeSBryan Venteicher &port->vtcport_mtx); 11126f744ddeSBryan Venteicher 111304434c94SBryan Venteicher error = vtcon_port_init_vqs(port); 11146f744ddeSBryan Venteicher if (error) { 111504434c94SBryan Venteicher VTCON_PORT_LOCK(port); 111604434c94SBryan Venteicher vtcon_port_teardown(port); 11176f744ddeSBryan Venteicher return (error); 11186f744ddeSBryan Venteicher } 11196f744ddeSBryan Venteicher 11206f744ddeSBryan Venteicher VTCON_LOCK(sc); 112104434c94SBryan Venteicher VTCON_PORT_LOCK(port); 112204434c94SBryan Venteicher scport->vcsp_port = port; 112346822c48SBryan Venteicher vtcon_port_enable_intr(port); 112404434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1); 112504434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 11266f744ddeSBryan Venteicher VTCON_UNLOCK(sc); 11276f744ddeSBryan Venteicher 112846822c48SBryan Venteicher tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX, 112946822c48SBryan Venteicher device_get_unit(dev), id); 113046822c48SBryan Venteicher 11316f744ddeSBryan Venteicher return (0); 11326f744ddeSBryan Venteicher } 11336f744ddeSBryan Venteicher 11346f744ddeSBryan Venteicher static void 113504434c94SBryan Venteicher vtcon_port_drain_bufs(struct virtqueue *vq) 11366f744ddeSBryan Venteicher { 11376f744ddeSBryan Venteicher void *buf; 11386f744ddeSBryan Venteicher int last; 11396f744ddeSBryan Venteicher 11406f744ddeSBryan Venteicher last = 0; 11416f744ddeSBryan Venteicher 11426f744ddeSBryan Venteicher while ((buf = virtqueue_drain(vq, &last)) != NULL) 11436f744ddeSBryan Venteicher free(buf, M_DEVBUF); 11446f744ddeSBryan Venteicher } 11456f744ddeSBryan Venteicher 11466f744ddeSBryan Venteicher static void 114704434c94SBryan Venteicher vtcon_port_drain(struct vtcon_port *port) 11486f744ddeSBryan Venteicher { 11496f744ddeSBryan Venteicher 115004434c94SBryan Venteicher vtcon_port_drain_bufs(port->vtcport_invq); 115104434c94SBryan Venteicher } 115204434c94SBryan Venteicher 115304434c94SBryan Venteicher static void 115404434c94SBryan Venteicher vtcon_port_teardown(struct vtcon_port *port) 115504434c94SBryan Venteicher { 115604434c94SBryan Venteicher struct tty *tp; 115704434c94SBryan Venteicher 11586f744ddeSBryan Venteicher tp = port->vtcport_tty; 11596f744ddeSBryan Venteicher 116004434c94SBryan Venteicher port->vtcport_flags |= VTCON_PORT_FLAG_GONE; 11616f744ddeSBryan Venteicher 11626f744ddeSBryan Venteicher if (tp != NULL) { 11636f744ddeSBryan Venteicher atomic_add_int(&vtcon_pending_free, 1); 11646f744ddeSBryan Venteicher tty_rel_gone(tp); 11656f744ddeSBryan Venteicher } else 11666f744ddeSBryan Venteicher vtcon_port_destroy(port); 11676f744ddeSBryan Venteicher } 11686f744ddeSBryan Venteicher 11696f744ddeSBryan Venteicher static void 11706f744ddeSBryan Venteicher vtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows) 11716f744ddeSBryan Venteicher { 11726f744ddeSBryan Venteicher struct tty *tp; 11736f744ddeSBryan Venteicher struct winsize sz; 11746f744ddeSBryan Venteicher 11756f744ddeSBryan Venteicher tp = port->vtcport_tty; 11766f744ddeSBryan Venteicher 11776f744ddeSBryan Venteicher if (tp == NULL) 11786f744ddeSBryan Venteicher return; 11796f744ddeSBryan Venteicher 11806f744ddeSBryan Venteicher bzero(&sz, sizeof(struct winsize)); 11816f744ddeSBryan Venteicher sz.ws_col = cols; 11826f744ddeSBryan Venteicher sz.ws_row = rows; 11836f744ddeSBryan Venteicher 11846f744ddeSBryan Venteicher tty_set_winsize(tp, &sz); 118504434c94SBryan Venteicher } 118604434c94SBryan Venteicher 118704434c94SBryan Venteicher static void 118804434c94SBryan Venteicher vtcon_port_update_console_size(struct vtcon_softc *sc) 118904434c94SBryan Venteicher { 119004434c94SBryan Venteicher struct vtcon_port *port; 119104434c94SBryan Venteicher struct vtcon_softc_port *scport; 119204434c94SBryan Venteicher uint16_t cols, rows; 119304434c94SBryan Venteicher 119404434c94SBryan Venteicher vtcon_get_console_size(sc, &cols, &rows); 119504434c94SBryan Venteicher 119604434c94SBryan Venteicher /* 119704434c94SBryan Venteicher * For now, assume the first (only) port is the console. Note 119804434c94SBryan Venteicher * QEMU does not implement this feature yet. 119904434c94SBryan Venteicher */ 120004434c94SBryan Venteicher scport = &sc->vtcon_ports[0]; 120104434c94SBryan Venteicher 120204434c94SBryan Venteicher VTCON_LOCK(sc); 120304434c94SBryan Venteicher port = scport->vcsp_port; 120404434c94SBryan Venteicher 120504434c94SBryan Venteicher if (port != NULL) { 120604434c94SBryan Venteicher VTCON_PORT_LOCK(port); 120704434c94SBryan Venteicher VTCON_UNLOCK(sc); 120804434c94SBryan Venteicher vtcon_port_change_size(port, cols, rows); 12096f744ddeSBryan Venteicher VTCON_PORT_UNLOCK(port); 121004434c94SBryan Venteicher } else 121104434c94SBryan Venteicher VTCON_UNLOCK(sc); 12126f744ddeSBryan Venteicher } 12136f744ddeSBryan Venteicher 12146f744ddeSBryan Venteicher static void 12156f744ddeSBryan Venteicher vtcon_port_enable_intr(struct vtcon_port *port) 12166f744ddeSBryan Venteicher { 12176f744ddeSBryan Venteicher 12186f744ddeSBryan Venteicher /* 1219453130d9SPedro F. Giffuni * NOTE: The out virtqueue is always polled, so its interrupt 122004434c94SBryan Venteicher * kept disabled. 12216f744ddeSBryan Venteicher */ 12226f744ddeSBryan Venteicher virtqueue_enable_intr(port->vtcport_invq); 12236f744ddeSBryan Venteicher } 12246f744ddeSBryan Venteicher 12256f744ddeSBryan Venteicher static void 12266f744ddeSBryan Venteicher vtcon_port_disable_intr(struct vtcon_port *port) 12276f744ddeSBryan Venteicher { 12286f744ddeSBryan Venteicher 12296f744ddeSBryan Venteicher if (port->vtcport_invq != NULL) 12306f744ddeSBryan Venteicher virtqueue_disable_intr(port->vtcport_invq); 12316f744ddeSBryan Venteicher if (port->vtcport_outvq != NULL) 12326f744ddeSBryan Venteicher virtqueue_disable_intr(port->vtcport_outvq); 12336f744ddeSBryan Venteicher } 12346f744ddeSBryan Venteicher 12356f744ddeSBryan Venteicher static void 123604434c94SBryan Venteicher vtcon_port_in(struct vtcon_port *port) 12376f744ddeSBryan Venteicher { 12386f744ddeSBryan Venteicher struct virtqueue *vq; 123904434c94SBryan Venteicher struct tty *tp; 12406f744ddeSBryan Venteicher char *buf; 12416f744ddeSBryan Venteicher uint32_t len; 12426f744ddeSBryan Venteicher int i, deq; 12436f744ddeSBryan Venteicher 12446f744ddeSBryan Venteicher tp = port->vtcport_tty; 12456f744ddeSBryan Venteicher vq = port->vtcport_invq; 12466f744ddeSBryan Venteicher 12476f744ddeSBryan Venteicher again: 12486f744ddeSBryan Venteicher deq = 0; 12496f744ddeSBryan Venteicher 12506f744ddeSBryan Venteicher while ((buf = virtqueue_dequeue(vq, &len)) != NULL) { 1251b84b3efdSBryan Venteicher for (i = 0; i < len; i++) { 1252b84b3efdSBryan Venteicher #if defined(KDB) 1253b84b3efdSBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE) 1254b84b3efdSBryan Venteicher kdb_alt_break(buf[i], 1255b84b3efdSBryan Venteicher &port->vtcport_alt_break_state); 1256b84b3efdSBryan Venteicher #endif 12576f744ddeSBryan Venteicher ttydisc_rint(tp, buf[i], 0); 1258b84b3efdSBryan Venteicher } 125904434c94SBryan Venteicher vtcon_port_requeue_buf(port, buf); 126004434c94SBryan Venteicher deq++; 12616f744ddeSBryan Venteicher } 12626f744ddeSBryan Venteicher ttydisc_rint_done(tp); 12636f744ddeSBryan Venteicher 12646f744ddeSBryan Venteicher if (deq > 0) 12656f744ddeSBryan Venteicher virtqueue_notify(vq); 12666f744ddeSBryan Venteicher 12676f744ddeSBryan Venteicher if (virtqueue_enable_intr(vq) != 0) 12686f744ddeSBryan Venteicher goto again; 12696f744ddeSBryan Venteicher } 12706f744ddeSBryan Venteicher 12716f744ddeSBryan Venteicher static void 127204434c94SBryan Venteicher vtcon_port_intr(void *scportx) 12736f744ddeSBryan Venteicher { 127404434c94SBryan Venteicher struct vtcon_softc_port *scport; 127504434c94SBryan Venteicher struct vtcon_softc *sc; 12766f744ddeSBryan Venteicher struct vtcon_port *port; 12776f744ddeSBryan Venteicher 127804434c94SBryan Venteicher scport = scportx; 127904434c94SBryan Venteicher sc = scport->vcsp_sc; 12806f744ddeSBryan Venteicher 128104434c94SBryan Venteicher VTCON_LOCK(sc); 128204434c94SBryan Venteicher port = scport->vcsp_port; 128304434c94SBryan Venteicher if (port == NULL) { 128404434c94SBryan Venteicher VTCON_UNLOCK(sc); 128504434c94SBryan Venteicher return; 128604434c94SBryan Venteicher } 128704434c94SBryan Venteicher VTCON_PORT_LOCK(port); 128804434c94SBryan Venteicher VTCON_UNLOCK(sc); 128904434c94SBryan Venteicher if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0) 129004434c94SBryan Venteicher vtcon_port_in(port); 129104434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 12926f744ddeSBryan Venteicher } 12936f744ddeSBryan Venteicher 12946f744ddeSBryan Venteicher static void 129504434c94SBryan Venteicher vtcon_port_out(struct vtcon_port *port, void *buf, int bufsize) 12966f744ddeSBryan Venteicher { 129704434c94SBryan Venteicher struct sglist_seg segs[2]; 12986f744ddeSBryan Venteicher struct sglist sg; 12996f744ddeSBryan Venteicher struct virtqueue *vq; 13006f744ddeSBryan Venteicher int error; 13016f744ddeSBryan Venteicher 13026f744ddeSBryan Venteicher vq = port->vtcport_outvq; 130304434c94SBryan Venteicher KASSERT(virtqueue_empty(vq), 130404434c94SBryan Venteicher ("%s: port %p out virtqueue not emtpy", __func__, port)); 13056f744ddeSBryan Venteicher 130604434c94SBryan Venteicher sglist_init(&sg, 2, segs); 13076f744ddeSBryan Venteicher error = sglist_append(&sg, buf, bufsize); 130804434c94SBryan Venteicher KASSERT(error == 0, ("%s: error %d adding buffer to sglist", 130904434c94SBryan Venteicher __func__, error)); 13106f744ddeSBryan Venteicher 131104434c94SBryan Venteicher error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0); 131204434c94SBryan Venteicher if (error == 0) { 13136f744ddeSBryan Venteicher virtqueue_notify(vq); 13146f744ddeSBryan Venteicher virtqueue_poll(vq, NULL); 13156f744ddeSBryan Venteicher } 13166f744ddeSBryan Venteicher } 13176f744ddeSBryan Venteicher 13186f744ddeSBryan Venteicher static void 131904434c94SBryan Venteicher vtcon_port_submit_event(struct vtcon_port *port, uint16_t event, 13206f744ddeSBryan Venteicher uint16_t value) 13216f744ddeSBryan Venteicher { 13226f744ddeSBryan Venteicher struct vtcon_softc *sc; 13236f744ddeSBryan Venteicher 13246f744ddeSBryan Venteicher sc = port->vtcport_sc; 13256f744ddeSBryan Venteicher 132604434c94SBryan Venteicher vtcon_ctrl_send_control(sc, port->vtcport_id, event, value); 13276f744ddeSBryan Venteicher } 13286f744ddeSBryan Venteicher 13296f744ddeSBryan Venteicher static int 13306f744ddeSBryan Venteicher vtcon_tty_open(struct tty *tp) 13316f744ddeSBryan Venteicher { 13326f744ddeSBryan Venteicher struct vtcon_port *port; 13336f744ddeSBryan Venteicher 13346f744ddeSBryan Venteicher port = tty_softc(tp); 13356f744ddeSBryan Venteicher 133604434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 133704434c94SBryan Venteicher return (ENXIO); 133804434c94SBryan Venteicher 133904434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 13406f744ddeSBryan Venteicher 13416f744ddeSBryan Venteicher return (0); 13426f744ddeSBryan Venteicher } 13436f744ddeSBryan Venteicher 13446f744ddeSBryan Venteicher static void 13456f744ddeSBryan Venteicher vtcon_tty_close(struct tty *tp) 13466f744ddeSBryan Venteicher { 13476f744ddeSBryan Venteicher struct vtcon_port *port; 13486f744ddeSBryan Venteicher 13496f744ddeSBryan Venteicher port = tty_softc(tp); 13506f744ddeSBryan Venteicher 135104434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 135204434c94SBryan Venteicher return; 135304434c94SBryan Venteicher 135404434c94SBryan Venteicher vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); 13556f744ddeSBryan Venteicher } 13566f744ddeSBryan Venteicher 13576f744ddeSBryan Venteicher static void 13586f744ddeSBryan Venteicher vtcon_tty_outwakeup(struct tty *tp) 13596f744ddeSBryan Venteicher { 13606f744ddeSBryan Venteicher struct vtcon_port *port; 13616f744ddeSBryan Venteicher char buf[VTCON_BULK_BUFSZ]; 13626f744ddeSBryan Venteicher int len; 13636f744ddeSBryan Venteicher 13646f744ddeSBryan Venteicher port = tty_softc(tp); 13656f744ddeSBryan Venteicher 136604434c94SBryan Venteicher if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 136704434c94SBryan Venteicher return; 136804434c94SBryan Venteicher 13696f744ddeSBryan Venteicher while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0) 137004434c94SBryan Venteicher vtcon_port_out(port, buf, len); 13716f744ddeSBryan Venteicher } 13726f744ddeSBryan Venteicher 13736f744ddeSBryan Venteicher static void 13746f744ddeSBryan Venteicher vtcon_tty_free(void *xport) 13756f744ddeSBryan Venteicher { 13766f744ddeSBryan Venteicher struct vtcon_port *port; 13776f744ddeSBryan Venteicher 13786f744ddeSBryan Venteicher port = xport; 13796f744ddeSBryan Venteicher 13806f744ddeSBryan Venteicher vtcon_port_destroy(port); 13816f744ddeSBryan Venteicher atomic_subtract_int(&vtcon_pending_free, 1); 13826f744ddeSBryan Venteicher } 13836f744ddeSBryan Venteicher 13846f744ddeSBryan Venteicher static void 13856f744ddeSBryan Venteicher vtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows) 13866f744ddeSBryan Venteicher { 13876f744ddeSBryan Venteicher struct virtio_console_config concfg; 13886f744ddeSBryan Venteicher 13896f744ddeSBryan Venteicher KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE, 13906f744ddeSBryan Venteicher ("%s: size feature not negotiated", __func__)); 13916f744ddeSBryan Venteicher 13926f744ddeSBryan Venteicher vtcon_read_config(sc, &concfg); 13936f744ddeSBryan Venteicher 13946f744ddeSBryan Venteicher *cols = concfg.cols; 13956f744ddeSBryan Venteicher *rows = concfg.rows; 13966f744ddeSBryan Venteicher } 13976f744ddeSBryan Venteicher 13986f744ddeSBryan Venteicher static void 13996f744ddeSBryan Venteicher vtcon_enable_interrupts(struct vtcon_softc *sc) 14006f744ddeSBryan Venteicher { 140104434c94SBryan Venteicher struct vtcon_softc_port *scport; 14026f744ddeSBryan Venteicher struct vtcon_port *port; 140304434c94SBryan Venteicher int i; 140404434c94SBryan Venteicher 140504434c94SBryan Venteicher VTCON_LOCK(sc); 14066f744ddeSBryan Venteicher 14076f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 14086f744ddeSBryan Venteicher virtqueue_enable_intr(sc->vtcon_ctrl_rxvq); 14096f744ddeSBryan Venteicher 141004434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 141104434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 141204434c94SBryan Venteicher 141304434c94SBryan Venteicher port = scport->vcsp_port; 141404434c94SBryan Venteicher if (port == NULL) 141504434c94SBryan Venteicher continue; 141604434c94SBryan Venteicher 141704434c94SBryan Venteicher VTCON_PORT_LOCK(port); 14186f744ddeSBryan Venteicher vtcon_port_enable_intr(port); 141904434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 142004434c94SBryan Venteicher } 142104434c94SBryan Venteicher 142204434c94SBryan Venteicher VTCON_UNLOCK(sc); 14236f744ddeSBryan Venteicher } 14246f744ddeSBryan Venteicher 14256f744ddeSBryan Venteicher static void 14266f744ddeSBryan Venteicher vtcon_disable_interrupts(struct vtcon_softc *sc) 14276f744ddeSBryan Venteicher { 142804434c94SBryan Venteicher struct vtcon_softc_port *scport; 14296f744ddeSBryan Venteicher struct vtcon_port *port; 143004434c94SBryan Venteicher int i; 143104434c94SBryan Venteicher 143204434c94SBryan Venteicher VTCON_LOCK_ASSERT(sc); 14336f744ddeSBryan Venteicher 14346f744ddeSBryan Venteicher if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 14356f744ddeSBryan Venteicher virtqueue_disable_intr(sc->vtcon_ctrl_rxvq); 14366f744ddeSBryan Venteicher 143704434c94SBryan Venteicher for (i = 0; i < sc->vtcon_max_ports; i++) { 143804434c94SBryan Venteicher scport = &sc->vtcon_ports[i]; 143904434c94SBryan Venteicher 144004434c94SBryan Venteicher port = scport->vcsp_port; 144104434c94SBryan Venteicher if (port == NULL) 144204434c94SBryan Venteicher continue; 144304434c94SBryan Venteicher 144404434c94SBryan Venteicher VTCON_PORT_LOCK(port); 14456f744ddeSBryan Venteicher vtcon_port_disable_intr(port); 144604434c94SBryan Venteicher VTCON_PORT_UNLOCK(port); 144704434c94SBryan Venteicher } 14486f744ddeSBryan Venteicher } 1449