121308Sache/*- 221308Sache * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org> 321308Sache * All rights reserved. 421308Sache * 521308Sache * Redistribution and use in source and binary forms, with or without 621308Sache * modification, are permitted provided that the following conditions 721308Sache * are met: 821308Sache * 1. Redistributions of source code must retain the above copyright 921308Sache * notice unmodified, this list of conditions, and the following 1021308Sache * disclaimer. 1121308Sache * 2. Redistributions in binary form must reproduce the above copyright 1221308Sache * notice, this list of conditions and the following disclaimer in the 1321308Sache * documentation and/or other materials provided with the distribution. 1421308Sache * 1521308Sache * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1621308Sache * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1721308Sache * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1821308Sache * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1921308Sache * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2021308Sache * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2121308Sache * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2221308Sache * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2321308Sache * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2421308Sache * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2521308Sache */ 2621308Sache 2721308Sache/* Driver for VirtIO console devices. */ 2821308Sache 2921308Sache#include <sys/cdefs.h> 3021308Sache__FBSDID("$FreeBSD: releng/10.3/sys/dev/virtio/console/virtio_console.c 275273 2014-11-29 22:48:40Z bryanv $"); 3121308Sache 3221308Sache#include <sys/param.h> 3321308Sache#include <sys/systm.h> 3421308Sache#include <sys/kernel.h> 3521308Sache#include <sys/malloc.h> 3621308Sache#include <sys/module.h> 3721308Sache#include <sys/kdb.h> 3821308Sache#include <sys/lock.h> 3921308Sache#include <sys/mutex.h> 4021308Sache#include <sys/sglist.h> 4121308Sache#include <sys/sysctl.h> 4221308Sache#include <sys/taskqueue.h> 4321308Sache#include <sys/queue.h> 4421308Sache 4521308Sache#include <sys/conf.h> 4621308Sache#include <sys/cons.h> 4721308Sache#include <sys/tty.h> 4821308Sache 4921308Sache#include <machine/bus.h> 5021308Sache#include <machine/resource.h> 5121308Sache#include <sys/bus.h> 5221308Sache 5321308Sache#include <dev/virtio/virtio.h> 5421308Sache#include <dev/virtio/virtqueue.h> 5521308Sache#include <dev/virtio/console/virtio_console.h> 5626497Sache 5721308Sache#include "virtio_if.h" 5821308Sache 5921308Sache#define VTCON_MAX_PORTS 32 6021308Sache#define VTCON_TTY_PREFIX "V" 6121308Sache#define VTCON_BULK_BUFSZ 128 6221308Sache 6321308Sache/* 6421308Sache * The buffer cannot cross more than one page boundary due to the 6521308Sache * size of the sglist segment array used. 6621308Sache */ 6721308SacheCTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE); 6821308Sache 6921308Sachestruct vtcon_softc; 7021308Sachestruct vtcon_softc_port; 7121308Sache 7221308Sachestruct vtcon_port { 7321308Sache struct mtx vtcport_mtx; 7421308Sache struct vtcon_softc *vtcport_sc; 7521308Sache struct vtcon_softc_port *vtcport_scport; 7621308Sache struct tty *vtcport_tty; 7721308Sache struct virtqueue *vtcport_invq; 7821308Sache struct virtqueue *vtcport_outvq; 7921308Sache int vtcport_id; 8021308Sache int vtcport_flags; 8121308Sache#define VTCON_PORT_FLAG_GONE 0x01 8221308Sache#define VTCON_PORT_FLAG_CONSOLE 0x02 8321308Sache 8421308Sache#if defined(KDB) 8521308Sache int vtcport_alt_break_state; 8621308Sache#endif 8721308Sache}; 8821308Sache 8921308Sache#define VTCON_PORT_LOCK(_port) mtx_lock(&(_port)->vtcport_mtx) 9021308Sache#define VTCON_PORT_UNLOCK(_port) mtx_unlock(&(_port)->vtcport_mtx) 9121308Sache 9221308Sachestruct vtcon_softc_port { 9321308Sache struct vtcon_softc *vcsp_sc; 9421308Sache struct vtcon_port *vcsp_port; 9521308Sache struct virtqueue *vcsp_invq; 9621308Sache struct virtqueue *vcsp_outvq; 9721308Sache}; 9821308Sache 9921308Sachestruct vtcon_softc { 10021308Sache device_t vtcon_dev; 10121308Sache struct mtx vtcon_mtx; 10221308Sache uint64_t vtcon_features; 10321308Sache uint32_t vtcon_max_ports; 10421308Sache uint32_t vtcon_flags; 10521308Sache#define VTCON_FLAG_DETACHED 0x01 10621308Sache#define VTCON_FLAG_SIZE 0x02 10721308Sache#define VTCON_FLAG_MULTIPORT 0x04 10821308Sache 10921308Sache /* 11021308Sache * Ports can be added and removed during runtime, but we have 11121308Sache * to allocate all the virtqueues during attach. This array is 11221308Sache * indexed by the port ID. 11321308Sache */ 11421308Sache struct vtcon_softc_port *vtcon_ports; 11521308Sache 11621308Sache struct task vtcon_ctrl_task; 11721308Sache struct virtqueue *vtcon_ctrl_rxvq; 11821308Sache struct virtqueue *vtcon_ctrl_txvq; 11921308Sache struct mtx vtcon_ctrl_tx_mtx; 12021308Sache}; 12121308Sache 12221308Sache#define VTCON_LOCK(_sc) mtx_lock(&(_sc)->vtcon_mtx) 12321308Sache#define VTCON_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_mtx) 12421308Sache#define VTCON_LOCK_ASSERT(_sc) \ 12521308Sache mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED) 12621308Sache#define VTCON_LOCK_ASSERT_NOTOWNED(_sc) \ 12721308Sache mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED) 12821308Sache 12921308Sache#define VTCON_CTRL_TX_LOCK(_sc) mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx) 13021308Sache#define VTCON_CTRL_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx) 13121308Sache 13221308Sache#define VTCON_ASSERT_VALID_PORTID(_sc, _id) \ 13321308Sache KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports, \ 13421308Sache ("%s: port ID %d out of range", __func__, _id)) 13521308Sache 13621308Sache#define VTCON_FEATURES VIRTIO_CONSOLE_F_MULTIPORT 13721308Sache 13821308Sachestatic struct virtio_feature_desc vtcon_feature_desc[] = { 13921308Sache { VIRTIO_CONSOLE_F_SIZE, "ConsoleSize" }, 14021308Sache { VIRTIO_CONSOLE_F_MULTIPORT, "MultiplePorts" }, 14121308Sache { VIRTIO_CONSOLE_F_EMERG_WRITE, "EmergencyWrite" }, 14221308Sache 14321308Sache { 0, NULL } 14421308Sache}; 14521308Sache 14621308Sachestatic int vtcon_modevent(module_t, int, void *); 14721308Sachestatic void vtcon_drain_all(void); 14821308Sache 14921308Sachestatic int vtcon_probe(device_t); 15021308Sachestatic int vtcon_attach(device_t); 15121308Sachestatic int vtcon_detach(device_t); 15221308Sachestatic int vtcon_config_change(device_t); 15321308Sache 15421308Sachestatic void vtcon_setup_features(struct vtcon_softc *); 15521308Sachestatic void vtcon_negotiate_features(struct vtcon_softc *); 15621308Sachestatic int vtcon_alloc_scports(struct vtcon_softc *); 15721308Sachestatic int vtcon_alloc_virtqueues(struct vtcon_softc *); 15821308Sachestatic void vtcon_read_config(struct vtcon_softc *, 15921308Sache struct virtio_console_config *); 16021308Sache 16121308Sachestatic void vtcon_determine_max_ports(struct vtcon_softc *, 16221308Sache struct virtio_console_config *); 16321308Sachestatic void vtcon_destroy_ports(struct vtcon_softc *); 16421308Sachestatic void vtcon_stop(struct vtcon_softc *); 16521308Sache 16621308Sachestatic int vtcon_ctrl_event_enqueue(struct vtcon_softc *, 16721308Sache struct virtio_console_control *); 16821308Sachestatic int vtcon_ctrl_event_create(struct vtcon_softc *); 16921308Sachestatic void vtcon_ctrl_event_requeue(struct vtcon_softc *, 17021308Sache struct virtio_console_control *); 17121308Sachestatic int vtcon_ctrl_event_populate(struct vtcon_softc *); 17221308Sachestatic void vtcon_ctrl_event_drain(struct vtcon_softc *); 17321308Sachestatic int vtcon_ctrl_init(struct vtcon_softc *); 17421308Sachestatic void vtcon_ctrl_deinit(struct vtcon_softc *); 17521308Sachestatic void vtcon_ctrl_port_add_event(struct vtcon_softc *, int); 17621308Sachestatic void vtcon_ctrl_port_remove_event(struct vtcon_softc *, int); 17721308Sachestatic void vtcon_ctrl_port_console_event(struct vtcon_softc *, int); 17821308Sachestatic void vtcon_ctrl_port_open_event(struct vtcon_softc *, int); 17921308Sachestatic void vtcon_ctrl_process_event(struct vtcon_softc *, 18021308Sache struct virtio_console_control *); 18121308Sachestatic void vtcon_ctrl_task_cb(void *, int); 18221308Sachestatic void vtcon_ctrl_event_intr(void *); 18321308Sachestatic void vtcon_ctrl_poll(struct vtcon_softc *, 18421308Sache struct virtio_console_control *control); 18521308Sachestatic void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t, 18621308Sache uint16_t, uint16_t); 18721308Sache 18821308Sachestatic int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t); 18921308Sachestatic int vtcon_port_create_buf(struct vtcon_port *); 19021308Sachestatic void vtcon_port_requeue_buf(struct vtcon_port *, void *); 19121308Sachestatic int vtcon_port_populate(struct vtcon_port *); 19221308Sachestatic void vtcon_port_destroy(struct vtcon_port *); 19321308Sachestatic int vtcon_port_create(struct vtcon_softc *, int); 19421308Sachestatic void vtcon_port_drain_bufs(struct virtqueue *); 19521308Sachestatic void vtcon_port_drain(struct vtcon_port *); 19621308Sachestatic void vtcon_port_teardown(struct vtcon_port *); 19721308Sachestatic void vtcon_port_change_size(struct vtcon_port *, uint16_t, 19821308Sache uint16_t); 19921308Sachestatic void vtcon_port_update_console_size(struct vtcon_softc *); 20021308Sachestatic void vtcon_port_enable_intr(struct vtcon_port *); 20121308Sachestatic void vtcon_port_disable_intr(struct vtcon_port *); 20221308Sachestatic void vtcon_port_in(struct vtcon_port *); 20321308Sachestatic void vtcon_port_intr(void *); 20421308Sachestatic void vtcon_port_out(struct vtcon_port *, void *, int); 20521308Sachestatic void vtcon_port_submit_event(struct vtcon_port *, uint16_t, 20621308Sache uint16_t); 20721308Sache 20821308Sachestatic int vtcon_tty_open(struct tty *); 20921308Sachestatic void vtcon_tty_close(struct tty *); 21021308Sachestatic void vtcon_tty_outwakeup(struct tty *); 21121308Sachestatic void vtcon_tty_free(void *); 21221308Sache 21321308Sachestatic void vtcon_get_console_size(struct vtcon_softc *, uint16_t *, 21421308Sache uint16_t *); 21521308Sache 21621308Sachestatic void vtcon_enable_interrupts(struct vtcon_softc *); 21721308Sachestatic void vtcon_disable_interrupts(struct vtcon_softc *); 21821308Sache 21921308Sachestatic int vtcon_pending_free; 22021308Sache 22121308Sachestatic struct ttydevsw vtcon_tty_class = { 22221308Sache .tsw_flags = 0, 22321308Sache .tsw_open = vtcon_tty_open, 22421308Sache .tsw_close = vtcon_tty_close, 22521308Sache .tsw_outwakeup = vtcon_tty_outwakeup, 22621308Sache .tsw_free = vtcon_tty_free, 22721308Sache}; 22821308Sache 22921308Sachestatic device_method_t vtcon_methods[] = { 23021308Sache /* Device methods. */ 23121308Sache DEVMETHOD(device_probe, vtcon_probe), 23221308Sache DEVMETHOD(device_attach, vtcon_attach), 23321308Sache DEVMETHOD(device_detach, vtcon_detach), 23421308Sache 23521308Sache /* VirtIO methods. */ 23621308Sache DEVMETHOD(virtio_config_change, vtcon_config_change), 23721308Sache 23821308Sache DEVMETHOD_END 23921308Sache}; 24021308Sache 24121308Sachestatic driver_t vtcon_driver = { 24221308Sache "vtcon", 24321308Sache vtcon_methods, 24421308Sache sizeof(struct vtcon_softc) 24521308Sache}; 24621308Sachestatic devclass_t vtcon_devclass; 24721308Sache 24821308SacheDRIVER_MODULE(virtio_console, virtio_pci, vtcon_driver, vtcon_devclass, 24921308Sache vtcon_modevent, 0); 25021308SacheMODULE_VERSION(virtio_console, 1); 25121308SacheMODULE_DEPEND(virtio_console, virtio, 1, 1, 1); 25221308Sache 25321308Sachestatic int 25421308Sachevtcon_modevent(module_t mod, int type, void *unused) 25521308Sache{ 25621308Sache int error; 25721308Sache 25821308Sache switch (type) { 25921308Sache case MOD_LOAD: 26021308Sache error = 0; 26121308Sache break; 26221308Sache case MOD_QUIESCE: 26321308Sache error = 0; 26421308Sache break; 26521308Sache case MOD_UNLOAD: 26621308Sache vtcon_drain_all(); 26721308Sache error = 0; 26821308Sache break; 26921308Sache case MOD_SHUTDOWN: 27021308Sache error = 0; 27121308Sache break; 27221308Sache default: 27321308Sache error = EOPNOTSUPP; 27421308Sache break; 27521308Sache } 27621308Sache 27721308Sache return (error); 27821308Sache} 27921308Sache 28021308Sachestatic void 28121308Sachevtcon_drain_all(void) 28221308Sache{ 28321308Sache int first; 28421308Sache 28521308Sache for (first = 1; vtcon_pending_free != 0; first = 0) { 28621308Sache if (first != 0) { 28721308Sache printf("virtio_console: Waiting for all detached TTY " 28821308Sache "devices to have open fds closed.\n"); 28921308Sache } 29021308Sache pause("vtcondra", hz); 29121308Sache } 29221308Sache} 29321308Sache 29421308Sachestatic int 29521308Sachevtcon_probe(device_t dev) 29621308Sache{ 29721308Sache 29821308Sache if (virtio_get_device_type(dev) != VIRTIO_ID_CONSOLE) 29921308Sache return (ENXIO); 30021308Sache 30121308Sache device_set_desc(dev, "VirtIO Console Adapter"); 30221308Sache 30321308Sache return (BUS_PROBE_DEFAULT); 30421308Sache} 30521308Sache 30621308Sachestatic int 30721308Sachevtcon_attach(device_t dev) 30821308Sache{ 30921308Sache struct vtcon_softc *sc; 31021308Sache struct virtio_console_config concfg; 31121308Sache int error; 31221308Sache 31321308Sache sc = device_get_softc(dev); 31421308Sache sc->vtcon_dev = dev; 31521308Sache 31621308Sache mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF); 31721308Sache mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF); 31821308Sache 31921308Sache virtio_set_feature_desc(dev, vtcon_feature_desc); 32021308Sache vtcon_setup_features(sc); 32121308Sache 32221308Sache vtcon_read_config(sc, &concfg); 32321308Sache vtcon_determine_max_ports(sc, &concfg); 32421308Sache 32521308Sache error = vtcon_alloc_scports(sc); 32621308Sache if (error) { 32721308Sache device_printf(dev, "cannot allocate softc port structures\n"); 32821308Sache goto fail; 32921308Sache } 33021308Sache 33121308Sache error = vtcon_alloc_virtqueues(sc); 33221308Sache if (error) { 33321308Sache device_printf(dev, "cannot allocate virtqueues\n"); 33421308Sache goto fail; 33521308Sache } 33621308Sache 33721308Sache if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 33821308Sache TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc); 33921308Sache error = vtcon_ctrl_init(sc); 34021308Sache if (error) 34121308Sache goto fail; 34221308Sache } else { 34321308Sache error = vtcon_port_create(sc, 0); 34421308Sache if (error) 34521308Sache goto fail; 34621308Sache if (sc->vtcon_flags & VTCON_FLAG_SIZE) 34721308Sache vtcon_port_update_console_size(sc); 34821308Sache } 34921308Sache 35021308Sache error = virtio_setup_intr(dev, INTR_TYPE_TTY); 35121308Sache if (error) { 35221308Sache device_printf(dev, "cannot setup virtqueue interrupts\n"); 35321308Sache goto fail; 35421308Sache } 35521308Sache 35621308Sache vtcon_enable_interrupts(sc); 35721308Sache 35821308Sache vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID, 35921308Sache VIRTIO_CONSOLE_DEVICE_READY, 1); 36021308Sache 36121308Sachefail: 36221308Sache if (error) 36321308Sache vtcon_detach(dev); 36421308Sache 36521308Sache return (error); 36621308Sache} 36721308Sache 36821308Sachestatic int 36921308Sachevtcon_detach(device_t dev) 37021308Sache{ 37121308Sache struct vtcon_softc *sc; 37221308Sache 37321308Sache sc = device_get_softc(dev); 37421308Sache 37521308Sache VTCON_LOCK(sc); 37621308Sache sc->vtcon_flags |= VTCON_FLAG_DETACHED; 37721308Sache if (device_is_attached(dev)) 37821308Sache vtcon_stop(sc); 37921308Sache VTCON_UNLOCK(sc); 38021308Sache 38121308Sache if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 38221308Sache taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task); 38321308Sache vtcon_ctrl_deinit(sc); 38421308Sache } 38521308Sache 38621308Sache vtcon_destroy_ports(sc); 38721308Sache mtx_destroy(&sc->vtcon_mtx); 38826497Sache mtx_destroy(&sc->vtcon_ctrl_tx_mtx); 38926497Sache 39026497Sache return (0); 39126497Sache} 39226497Sache 39321308Sachestatic int 39421308Sachevtcon_config_change(device_t dev) 39521308Sache{ 39621308Sache struct vtcon_softc *sc; 39721308Sache 39821308Sache sc = device_get_softc(dev); 39921308Sache 40021308Sache /* 40121308Sache * When the multiport feature is negotiated, all configuration 40221308Sache * changes are done through control virtqueue events. 40321308Sache */ 40421308Sache if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) { 40521308Sache if (sc->vtcon_flags & VTCON_FLAG_SIZE) 40621308Sache vtcon_port_update_console_size(sc); 40721308Sache } 40821308Sache 40921308Sache return (0); 41021308Sache} 41121308Sache 41221308Sachestatic void 41321308Sachevtcon_negotiate_features(struct vtcon_softc *sc) 41421308Sache{ 41521308Sache device_t dev; 41621308Sache uint64_t features; 41721308Sache 41821308Sache dev = sc->vtcon_dev; 41921308Sache features = VTCON_FEATURES; 42021308Sache 42121308Sache sc->vtcon_features = virtio_negotiate_features(dev, features); 42221308Sache} 42321308Sache 42421308Sachestatic void 42521308Sachevtcon_setup_features(struct vtcon_softc *sc) 42621308Sache{ 42721308Sache device_t dev; 42821308Sache 42921308Sache dev = sc->vtcon_dev; 43021308Sache 43121308Sache vtcon_negotiate_features(sc); 43221308Sache 43321308Sache if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE)) 43421308Sache sc->vtcon_flags |= VTCON_FLAG_SIZE; 43521308Sache if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT)) 43621308Sache sc->vtcon_flags |= VTCON_FLAG_MULTIPORT; 43721308Sache} 43821308Sache 43921308Sache#define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg) \ 44021308Sache if (virtio_with_feature(_dev, _feature)) { \ 44121308Sache virtio_read_device_config(_dev, \ 44221308Sache offsetof(struct virtio_console_config, _field), \ 44321308Sache &(_cfg)->_field, sizeof((_cfg)->_field)); \ 44421308Sache } 44521308Sache 44621308Sachestatic void 44721308Sachevtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg) 44821308Sache{ 44921308Sache device_t dev; 45021308Sache 45121308Sache dev = sc->vtcon_dev; 45221308Sache 45321308Sache bzero(concfg, sizeof(struct virtio_console_config)); 45421308Sache 45521308Sache VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg); 45621308Sache VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg); 45721308Sache VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg); 45821308Sache} 45921308Sache 46021308Sache#undef VTCON_GET_CONFIG 46121308Sache 46221308Sachestatic int 46321308Sachevtcon_alloc_scports(struct vtcon_softc *sc) 46421308Sache{ 46521308Sache struct vtcon_softc_port *scport; 46621308Sache int max, i; 46721308Sache 46821308Sache max = sc->vtcon_max_ports; 46921308Sache 47021308Sache sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max, 47121308Sache M_DEVBUF, M_NOWAIT | M_ZERO); 47221308Sache if (sc->vtcon_ports == NULL) 47321308Sache return (ENOMEM); 47421308Sache 47521308Sache for (i = 0; i < max; i++) { 47621308Sache scport = &sc->vtcon_ports[i]; 47721308Sache scport->vcsp_sc = sc; 47821308Sache } 47921308Sache 48021308Sache return (0); 48121308Sache} 48221308Sache 48321308Sachestatic int 48421308Sachevtcon_alloc_virtqueues(struct vtcon_softc *sc) 48521308Sache{ 48621308Sache device_t dev; 48721308Sache struct vq_alloc_info *info; 48821308Sache struct vtcon_softc_port *scport; 48921308Sache int i, idx, portidx, nvqs, error; 49021308Sache 49121308Sache dev = sc->vtcon_dev; 49221308Sache 49321308Sache nvqs = sc->vtcon_max_ports * 2; 49421308Sache if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 49521308Sache nvqs += 2; 49621308Sache 49721308Sache info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT); 49821308Sache if (info == NULL) 49921308Sache return (ENOMEM); 50021308Sache 50121308Sache for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) { 50221308Sache 50321308Sache if (i == 1) { 50421308Sache /* The control virtqueues are after the first port. */ 50521308Sache VQ_ALLOC_INFO_INIT(&info[idx], 0, 50621308Sache vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq, 50721308Sache "%s-control rx", device_get_nameunit(dev)); 50821308Sache VQ_ALLOC_INFO_INIT(&info[idx+1], 0, 50921308Sache NULL, sc, &sc->vtcon_ctrl_txvq, 51021308Sache "%s-control tx", device_get_nameunit(dev)); 51121308Sache continue; 51221308Sache } 51321308Sache 51421308Sache scport = &sc->vtcon_ports[portidx]; 51521308Sache 51621308Sache VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr, 51721308Sache scport, &scport->vcsp_invq, "%s-port%d in", 51821308Sache device_get_nameunit(dev), i); 51921308Sache VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL, 52021308Sache NULL, &scport->vcsp_outvq, "%s-port%d out", 52121308Sache device_get_nameunit(dev), i); 52221308Sache 52321308Sache portidx++; 52421308Sache } 52521308Sache 52621308Sache error = virtio_alloc_virtqueues(dev, 0, nvqs, info); 52721308Sache free(info, M_TEMP); 52821308Sache 52926497Sache return (error); 53021308Sache} 53121308Sache 53221308Sachestatic void 53321308Sachevtcon_determine_max_ports(struct vtcon_softc *sc, 53421308Sache struct virtio_console_config *concfg) 53521308Sache{ 53621308Sache 53721308Sache if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { 53821308Sache sc->vtcon_max_ports = 53921308Sache min(concfg->max_nr_ports, VTCON_MAX_PORTS); 54021308Sache if (sc->vtcon_max_ports == 0) 54121308Sache sc->vtcon_max_ports = 1; 54221308Sache } else 54321308Sache sc->vtcon_max_ports = 1; 54421308Sache} 54521308Sache 54621308Sachestatic void 54721308Sachevtcon_destroy_ports(struct vtcon_softc *sc) 54821308Sache{ 54921308Sache struct vtcon_softc_port *scport; 55021308Sache struct vtcon_port *port; 55121308Sache struct virtqueue *vq; 55221308Sache int i; 553 554 if (sc->vtcon_ports == NULL) 555 return; 556 557 VTCON_LOCK(sc); 558 for (i = 0; i < sc->vtcon_max_ports; i++) { 559 scport = &sc->vtcon_ports[i]; 560 561 port = scport->vcsp_port; 562 if (port != NULL) { 563 scport->vcsp_port = NULL; 564 VTCON_PORT_LOCK(port); 565 VTCON_UNLOCK(sc); 566 vtcon_port_teardown(port); 567 VTCON_LOCK(sc); 568 } 569 570 vq = scport->vcsp_invq; 571 if (vq != NULL) 572 vtcon_port_drain_bufs(vq); 573 } 574 VTCON_UNLOCK(sc); 575 576 free(sc->vtcon_ports, M_DEVBUF); 577 sc->vtcon_ports = NULL; 578} 579 580static void 581vtcon_stop(struct vtcon_softc *sc) 582{ 583 584 vtcon_disable_interrupts(sc); 585 virtio_stop(sc->vtcon_dev); 586} 587 588static int 589vtcon_ctrl_event_enqueue(struct vtcon_softc *sc, 590 struct virtio_console_control *control) 591{ 592 struct sglist_seg segs[2]; 593 struct sglist sg; 594 struct virtqueue *vq; 595 int error; 596 597 vq = sc->vtcon_ctrl_rxvq; 598 599 sglist_init(&sg, 2, segs); 600 error = sglist_append(&sg, control, 601 sizeof(struct virtio_console_control)); 602 KASSERT(error == 0, ("%s: error %d adding control to sglist", 603 __func__, error)); 604 605 return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg)); 606} 607 608static int 609vtcon_ctrl_event_create(struct vtcon_softc *sc) 610{ 611 struct virtio_console_control *control; 612 int error; 613 614 control = malloc(sizeof(struct virtio_console_control), M_DEVBUF, 615 M_ZERO | M_NOWAIT); 616 if (control == NULL) 617 return (ENOMEM); 618 619 error = vtcon_ctrl_event_enqueue(sc, control); 620 if (error) 621 free(control, M_DEVBUF); 622 623 return (error); 624} 625 626static void 627vtcon_ctrl_event_requeue(struct vtcon_softc *sc, 628 struct virtio_console_control *control) 629{ 630 int error; 631 632 bzero(control, sizeof(struct virtio_console_control)); 633 634 error = vtcon_ctrl_event_enqueue(sc, control); 635 KASSERT(error == 0, 636 ("%s: cannot requeue control buffer %d", __func__, error)); 637} 638 639static int 640vtcon_ctrl_event_populate(struct vtcon_softc *sc) 641{ 642 struct virtqueue *vq; 643 int nbufs, error; 644 645 vq = sc->vtcon_ctrl_rxvq; 646 error = ENOSPC; 647 648 for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 649 error = vtcon_ctrl_event_create(sc); 650 if (error) 651 break; 652 } 653 654 if (nbufs > 0) { 655 virtqueue_notify(vq); 656 error = 0; 657 } 658 659 return (error); 660} 661 662static void 663vtcon_ctrl_event_drain(struct vtcon_softc *sc) 664{ 665 struct virtio_console_control *control; 666 struct virtqueue *vq; 667 int last; 668 669 vq = sc->vtcon_ctrl_rxvq; 670 last = 0; 671 672 if (vq == NULL) 673 return; 674 675 VTCON_LOCK(sc); 676 while ((control = virtqueue_drain(vq, &last)) != NULL) 677 free(control, M_DEVBUF); 678 VTCON_UNLOCK(sc); 679} 680 681static int 682vtcon_ctrl_init(struct vtcon_softc *sc) 683{ 684 int error; 685 686 error = vtcon_ctrl_event_populate(sc); 687 688 return (error); 689} 690 691static void 692vtcon_ctrl_deinit(struct vtcon_softc *sc) 693{ 694 695 vtcon_ctrl_event_drain(sc); 696} 697 698static void 699vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id) 700{ 701 device_t dev; 702 int error; 703 704 dev = sc->vtcon_dev; 705 706 /* This single thread only way for ports to be created. */ 707 if (sc->vtcon_ports[id].vcsp_port != NULL) { 708 device_printf(dev, "%s: adding port %d, but already exists\n", 709 __func__, id); 710 return; 711 } 712 713 error = vtcon_port_create(sc, id); 714 if (error) { 715 device_printf(dev, "%s: cannot create port %d: %d\n", 716 __func__, id, error); 717 vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0); 718 return; 719 } 720} 721 722static void 723vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id) 724{ 725 device_t dev; 726 struct vtcon_softc_port *scport; 727 struct vtcon_port *port; 728 729 dev = sc->vtcon_dev; 730 scport = &sc->vtcon_ports[id]; 731 732 VTCON_LOCK(sc); 733 port = scport->vcsp_port; 734 if (port == NULL) { 735 VTCON_UNLOCK(sc); 736 device_printf(dev, "%s: remove port %d, but does not exist\n", 737 __func__, id); 738 return; 739 } 740 741 scport->vcsp_port = NULL; 742 VTCON_PORT_LOCK(port); 743 VTCON_UNLOCK(sc); 744 vtcon_port_teardown(port); 745} 746 747static void 748vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id) 749{ 750 device_t dev; 751 struct vtcon_softc_port *scport; 752 struct vtcon_port *port; 753 754 dev = sc->vtcon_dev; 755 scport = &sc->vtcon_ports[id]; 756 757 VTCON_LOCK(sc); 758 port = scport->vcsp_port; 759 if (port == NULL) { 760 VTCON_UNLOCK(sc); 761 device_printf(dev, "%s: console port %d, but does not exist\n", 762 __func__, id); 763 return; 764 } 765 766 VTCON_PORT_LOCK(port); 767 VTCON_UNLOCK(sc); 768 port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE; 769 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 770 VTCON_PORT_UNLOCK(port); 771} 772 773static void 774vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id) 775{ 776 device_t dev; 777 struct vtcon_softc_port *scport; 778 struct vtcon_port *port; 779 780 dev = sc->vtcon_dev; 781 scport = &sc->vtcon_ports[id]; 782 783 VTCON_LOCK(sc); 784 port = scport->vcsp_port; 785 if (port == NULL) { 786 VTCON_UNLOCK(sc); 787 device_printf(dev, "%s: open port %d, but does not exist\n", 788 __func__, id); 789 return; 790 } 791 792 VTCON_PORT_LOCK(port); 793 VTCON_UNLOCK(sc); 794 vtcon_port_enable_intr(port); 795 VTCON_PORT_UNLOCK(port); 796} 797 798static void 799vtcon_ctrl_process_event(struct vtcon_softc *sc, 800 struct virtio_console_control *control) 801{ 802 device_t dev; 803 int id; 804 805 dev = sc->vtcon_dev; 806 id = control->id; 807 808 if (id < 0 || id >= sc->vtcon_max_ports) { 809 device_printf(dev, "%s: invalid port ID %d\n", __func__, id); 810 return; 811 } 812 813 switch (control->event) { 814 case VIRTIO_CONSOLE_PORT_ADD: 815 vtcon_ctrl_port_add_event(sc, id); 816 break; 817 818 case VIRTIO_CONSOLE_PORT_REMOVE: 819 vtcon_ctrl_port_remove_event(sc, id); 820 break; 821 822 case VIRTIO_CONSOLE_CONSOLE_PORT: 823 vtcon_ctrl_port_console_event(sc, id); 824 break; 825 826 case VIRTIO_CONSOLE_RESIZE: 827 break; 828 829 case VIRTIO_CONSOLE_PORT_OPEN: 830 vtcon_ctrl_port_open_event(sc, id); 831 break; 832 833 case VIRTIO_CONSOLE_PORT_NAME: 834 break; 835 } 836} 837 838static void 839vtcon_ctrl_task_cb(void *xsc, int pending) 840{ 841 struct vtcon_softc *sc; 842 struct virtqueue *vq; 843 struct virtio_console_control *control; 844 int detached; 845 846 sc = xsc; 847 vq = sc->vtcon_ctrl_rxvq; 848 849 VTCON_LOCK(sc); 850 851 while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) { 852 control = virtqueue_dequeue(vq, NULL); 853 if (control == NULL) 854 break; 855 856 VTCON_UNLOCK(sc); 857 vtcon_ctrl_process_event(sc, control); 858 VTCON_LOCK(sc); 859 vtcon_ctrl_event_requeue(sc, control); 860 } 861 862 if (!detached) { 863 virtqueue_notify(vq); 864 if (virtqueue_enable_intr(vq) != 0) 865 taskqueue_enqueue(taskqueue_thread, 866 &sc->vtcon_ctrl_task); 867 } 868 869 VTCON_UNLOCK(sc); 870} 871 872static void 873vtcon_ctrl_event_intr(void *xsc) 874{ 875 struct vtcon_softc *sc; 876 877 sc = xsc; 878 879 /* 880 * Only some events require us to potentially block, but it 881 * easier to just defer all event handling to the taskqueue. 882 */ 883 taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task); 884} 885 886static void 887vtcon_ctrl_poll(struct vtcon_softc *sc, 888 struct virtio_console_control *control) 889{ 890 struct sglist_seg segs[2]; 891 struct sglist sg; 892 struct virtqueue *vq; 893 int error; 894 895 vq = sc->vtcon_ctrl_txvq; 896 897 sglist_init(&sg, 2, segs); 898 error = sglist_append(&sg, control, 899 sizeof(struct virtio_console_control)); 900 KASSERT(error == 0, ("%s: error %d adding control to sglist", 901 __func__, error)); 902 903 /* 904 * We cannot use the softc lock to serialize access to this 905 * virtqueue since this is called from the tty layer with the 906 * port lock held. Acquiring the softc would violate our lock 907 * ordering. 908 */ 909 VTCON_CTRL_TX_LOCK(sc); 910 KASSERT(virtqueue_empty(vq), 911 ("%s: virtqueue is not emtpy", __func__)); 912 error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0); 913 if (error == 0) { 914 virtqueue_notify(vq); 915 virtqueue_poll(vq, NULL); 916 } 917 VTCON_CTRL_TX_UNLOCK(sc); 918} 919 920static void 921vtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid, 922 uint16_t event, uint16_t value) 923{ 924 struct virtio_console_control control; 925 926 if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) 927 return; 928 929 control.id = portid; 930 control.event = event; 931 control.value = value; 932 933 vtcon_ctrl_poll(sc, &control); 934} 935 936static int 937vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len) 938{ 939 struct sglist_seg segs[2]; 940 struct sglist sg; 941 struct virtqueue *vq; 942 int error; 943 944 vq = port->vtcport_invq; 945 946 sglist_init(&sg, 2, segs); 947 error = sglist_append(&sg, buf, len); 948 KASSERT(error == 0, 949 ("%s: error %d adding buffer to sglist", __func__, error)); 950 951 error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg); 952 953 return (error); 954} 955 956static int 957vtcon_port_create_buf(struct vtcon_port *port) 958{ 959 void *buf; 960 int error; 961 962 buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT); 963 if (buf == NULL) 964 return (ENOMEM); 965 966 error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 967 if (error) 968 free(buf, M_DEVBUF); 969 970 return (error); 971} 972 973static void 974vtcon_port_requeue_buf(struct vtcon_port *port, void *buf) 975{ 976 int error; 977 978 error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ); 979 KASSERT(error == 0, 980 ("%s: cannot requeue input buffer %d", __func__, error)); 981} 982 983static int 984vtcon_port_populate(struct vtcon_port *port) 985{ 986 struct virtqueue *vq; 987 int nbufs, error; 988 989 vq = port->vtcport_invq; 990 error = ENOSPC; 991 992 for (nbufs = 0; !virtqueue_full(vq); nbufs++) { 993 error = vtcon_port_create_buf(port); 994 if (error) 995 break; 996 } 997 998 if (nbufs > 0) { 999 virtqueue_notify(vq); 1000 error = 0; 1001 } 1002 1003 return (error); 1004} 1005 1006static void 1007vtcon_port_destroy(struct vtcon_port *port) 1008{ 1009 1010 port->vtcport_sc = NULL; 1011 port->vtcport_scport = NULL; 1012 port->vtcport_invq = NULL; 1013 port->vtcport_outvq = NULL; 1014 port->vtcport_id = -1; 1015 mtx_destroy(&port->vtcport_mtx); 1016 free(port, M_DEVBUF); 1017} 1018 1019static int 1020vtcon_port_init_vqs(struct vtcon_port *port) 1021{ 1022 struct vtcon_softc_port *scport; 1023 int error; 1024 1025 scport = port->vtcport_scport; 1026 1027 port->vtcport_invq = scport->vcsp_invq; 1028 port->vtcport_outvq = scport->vcsp_outvq; 1029 1030 /* 1031 * Free any data left over from when this virtqueue was in use by a 1032 * prior port. We have not yet notified the host that the port is 1033 * ready, so assume nothing in the virtqueue can be for us. 1034 */ 1035 vtcon_port_drain(port); 1036 1037 KASSERT(virtqueue_empty(port->vtcport_invq), 1038 ("%s: in virtqueue is not empty", __func__)); 1039 KASSERT(virtqueue_empty(port->vtcport_outvq), 1040 ("%s: out virtqueue is not empty", __func__)); 1041 1042 error = vtcon_port_populate(port); 1043 if (error) 1044 return (error); 1045 1046 return (0); 1047} 1048 1049static int 1050vtcon_port_create(struct vtcon_softc *sc, int id) 1051{ 1052 device_t dev; 1053 struct vtcon_softc_port *scport; 1054 struct vtcon_port *port; 1055 int error; 1056 1057 dev = sc->vtcon_dev; 1058 scport = &sc->vtcon_ports[id]; 1059 1060 VTCON_ASSERT_VALID_PORTID(sc, id); 1061 MPASS(scport->vcsp_port == NULL); 1062 1063 port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO); 1064 if (port == NULL) 1065 return (ENOMEM); 1066 1067 port->vtcport_sc = sc; 1068 port->vtcport_scport = scport; 1069 port->vtcport_id = id; 1070 mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF); 1071 port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port, 1072 &port->vtcport_mtx); 1073 1074 error = vtcon_port_init_vqs(port); 1075 if (error) { 1076 VTCON_PORT_LOCK(port); 1077 vtcon_port_teardown(port); 1078 return (error); 1079 } 1080 1081 VTCON_LOCK(sc); 1082 VTCON_PORT_LOCK(port); 1083 scport->vcsp_port = port; 1084 vtcon_port_enable_intr(port); 1085 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1); 1086 VTCON_PORT_UNLOCK(port); 1087 VTCON_UNLOCK(sc); 1088 1089 tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX, 1090 device_get_unit(dev), id); 1091 1092 return (0); 1093} 1094 1095static void 1096vtcon_port_drain_bufs(struct virtqueue *vq) 1097{ 1098 void *buf; 1099 int last; 1100 1101 last = 0; 1102 1103 while ((buf = virtqueue_drain(vq, &last)) != NULL) 1104 free(buf, M_DEVBUF); 1105} 1106 1107static void 1108vtcon_port_drain(struct vtcon_port *port) 1109{ 1110 1111 vtcon_port_drain_bufs(port->vtcport_invq); 1112} 1113 1114static void 1115vtcon_port_teardown(struct vtcon_port *port) 1116{ 1117 struct tty *tp; 1118 1119 tp = port->vtcport_tty; 1120 1121 port->vtcport_flags |= VTCON_PORT_FLAG_GONE; 1122 1123 if (tp != NULL) { 1124 atomic_add_int(&vtcon_pending_free, 1); 1125 tty_rel_gone(tp); 1126 } else 1127 vtcon_port_destroy(port); 1128} 1129 1130static void 1131vtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows) 1132{ 1133 struct tty *tp; 1134 struct winsize sz; 1135 1136 tp = port->vtcport_tty; 1137 1138 if (tp == NULL) 1139 return; 1140 1141 bzero(&sz, sizeof(struct winsize)); 1142 sz.ws_col = cols; 1143 sz.ws_row = rows; 1144 1145 tty_set_winsize(tp, &sz); 1146} 1147 1148static void 1149vtcon_port_update_console_size(struct vtcon_softc *sc) 1150{ 1151 struct vtcon_port *port; 1152 struct vtcon_softc_port *scport; 1153 uint16_t cols, rows; 1154 1155 vtcon_get_console_size(sc, &cols, &rows); 1156 1157 /* 1158 * For now, assume the first (only) port is the console. Note 1159 * QEMU does not implement this feature yet. 1160 */ 1161 scport = &sc->vtcon_ports[0]; 1162 1163 VTCON_LOCK(sc); 1164 port = scport->vcsp_port; 1165 1166 if (port != NULL) { 1167 VTCON_PORT_LOCK(port); 1168 VTCON_UNLOCK(sc); 1169 vtcon_port_change_size(port, cols, rows); 1170 VTCON_PORT_UNLOCK(port); 1171 } else 1172 VTCON_UNLOCK(sc); 1173} 1174 1175static void 1176vtcon_port_enable_intr(struct vtcon_port *port) 1177{ 1178 1179 /* 1180 * NOTE: The out virtqueue is always polled, so its interupt 1181 * kept disabled. 1182 */ 1183 virtqueue_enable_intr(port->vtcport_invq); 1184} 1185 1186static void 1187vtcon_port_disable_intr(struct vtcon_port *port) 1188{ 1189 1190 if (port->vtcport_invq != NULL) 1191 virtqueue_disable_intr(port->vtcport_invq); 1192 if (port->vtcport_outvq != NULL) 1193 virtqueue_disable_intr(port->vtcport_outvq); 1194} 1195 1196static void 1197vtcon_port_in(struct vtcon_port *port) 1198{ 1199 struct virtqueue *vq; 1200 struct tty *tp; 1201 char *buf; 1202 uint32_t len; 1203 int i, deq; 1204 1205 tp = port->vtcport_tty; 1206 vq = port->vtcport_invq; 1207 1208again: 1209 deq = 0; 1210 1211 while ((buf = virtqueue_dequeue(vq, &len)) != NULL) { 1212 for (i = 0; i < len; i++) { 1213#if defined(KDB) 1214 if (port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE) 1215 kdb_alt_break(buf[i], 1216 &port->vtcport_alt_break_state); 1217#endif 1218 ttydisc_rint(tp, buf[i], 0); 1219 } 1220 vtcon_port_requeue_buf(port, buf); 1221 deq++; 1222 } 1223 ttydisc_rint_done(tp); 1224 1225 if (deq > 0) 1226 virtqueue_notify(vq); 1227 1228 if (virtqueue_enable_intr(vq) != 0) 1229 goto again; 1230} 1231 1232static void 1233vtcon_port_intr(void *scportx) 1234{ 1235 struct vtcon_softc_port *scport; 1236 struct vtcon_softc *sc; 1237 struct vtcon_port *port; 1238 1239 scport = scportx; 1240 sc = scport->vcsp_sc; 1241 1242 VTCON_LOCK(sc); 1243 port = scport->vcsp_port; 1244 if (port == NULL) { 1245 VTCON_UNLOCK(sc); 1246 return; 1247 } 1248 VTCON_PORT_LOCK(port); 1249 VTCON_UNLOCK(sc); 1250 if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0) 1251 vtcon_port_in(port); 1252 VTCON_PORT_UNLOCK(port); 1253} 1254 1255static void 1256vtcon_port_out(struct vtcon_port *port, void *buf, int bufsize) 1257{ 1258 struct sglist_seg segs[2]; 1259 struct sglist sg; 1260 struct virtqueue *vq; 1261 int error; 1262 1263 vq = port->vtcport_outvq; 1264 KASSERT(virtqueue_empty(vq), 1265 ("%s: port %p out virtqueue not emtpy", __func__, port)); 1266 1267 sglist_init(&sg, 2, segs); 1268 error = sglist_append(&sg, buf, bufsize); 1269 KASSERT(error == 0, ("%s: error %d adding buffer to sglist", 1270 __func__, error)); 1271 1272 error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0); 1273 if (error == 0) { 1274 virtqueue_notify(vq); 1275 virtqueue_poll(vq, NULL); 1276 } 1277} 1278 1279static void 1280vtcon_port_submit_event(struct vtcon_port *port, uint16_t event, 1281 uint16_t value) 1282{ 1283 struct vtcon_softc *sc; 1284 1285 sc = port->vtcport_sc; 1286 1287 vtcon_ctrl_send_control(sc, port->vtcport_id, event, value); 1288} 1289 1290static int 1291vtcon_tty_open(struct tty *tp) 1292{ 1293 struct vtcon_port *port; 1294 1295 port = tty_softc(tp); 1296 1297 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1298 return (ENXIO); 1299 1300 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); 1301 1302 return (0); 1303} 1304 1305static void 1306vtcon_tty_close(struct tty *tp) 1307{ 1308 struct vtcon_port *port; 1309 1310 port = tty_softc(tp); 1311 1312 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1313 return; 1314 1315 vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); 1316} 1317 1318static void 1319vtcon_tty_outwakeup(struct tty *tp) 1320{ 1321 struct vtcon_port *port; 1322 char buf[VTCON_BULK_BUFSZ]; 1323 int len; 1324 1325 port = tty_softc(tp); 1326 1327 if (port->vtcport_flags & VTCON_PORT_FLAG_GONE) 1328 return; 1329 1330 while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0) 1331 vtcon_port_out(port, buf, len); 1332} 1333 1334static void 1335vtcon_tty_free(void *xport) 1336{ 1337 struct vtcon_port *port; 1338 1339 port = xport; 1340 1341 vtcon_port_destroy(port); 1342 atomic_subtract_int(&vtcon_pending_free, 1); 1343} 1344 1345static void 1346vtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows) 1347{ 1348 struct virtio_console_config concfg; 1349 1350 KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE, 1351 ("%s: size feature not negotiated", __func__)); 1352 1353 vtcon_read_config(sc, &concfg); 1354 1355 *cols = concfg.cols; 1356 *rows = concfg.rows; 1357} 1358 1359static void 1360vtcon_enable_interrupts(struct vtcon_softc *sc) 1361{ 1362 struct vtcon_softc_port *scport; 1363 struct vtcon_port *port; 1364 int i; 1365 1366 VTCON_LOCK(sc); 1367 1368 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 1369 virtqueue_enable_intr(sc->vtcon_ctrl_rxvq); 1370 1371 for (i = 0; i < sc->vtcon_max_ports; i++) { 1372 scport = &sc->vtcon_ports[i]; 1373 1374 port = scport->vcsp_port; 1375 if (port == NULL) 1376 continue; 1377 1378 VTCON_PORT_LOCK(port); 1379 vtcon_port_enable_intr(port); 1380 VTCON_PORT_UNLOCK(port); 1381 } 1382 1383 VTCON_UNLOCK(sc); 1384} 1385 1386static void 1387vtcon_disable_interrupts(struct vtcon_softc *sc) 1388{ 1389 struct vtcon_softc_port *scport; 1390 struct vtcon_port *port; 1391 int i; 1392 1393 VTCON_LOCK_ASSERT(sc); 1394 1395 if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) 1396 virtqueue_disable_intr(sc->vtcon_ctrl_rxvq); 1397 1398 for (i = 0; i < sc->vtcon_max_ports; i++) { 1399 scport = &sc->vtcon_ports[i]; 1400 1401 port = scport->vcsp_port; 1402 if (port == NULL) 1403 continue; 1404 1405 VTCON_PORT_LOCK(port); 1406 vtcon_port_disable_intr(port); 1407 VTCON_PORT_UNLOCK(port); 1408 } 1409} 1410