1*18b0d9ddSriastradh /* $NetBSD: viocon.c,v 1.10 2024/08/05 19:13:34 riastradh Exp $ */ 2cc725e35Sriastradh /* $OpenBSD: viocon.c,v 1.8 2021/11/05 11:38:29 mpi Exp $ */ 3cc725e35Sriastradh 4cc725e35Sriastradh /* 5cc725e35Sriastradh * Copyright (c) 2013-2015 Stefan Fritsch <sf@sfritsch.de> 6cc725e35Sriastradh * 7cc725e35Sriastradh * Permission to use, copy, modify, and distribute this software for any 8cc725e35Sriastradh * purpose with or without fee is hereby granted, provided that the above 9cc725e35Sriastradh * copyright notice and this permission notice appear in all copies. 10cc725e35Sriastradh * 11cc725e35Sriastradh * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12cc725e35Sriastradh * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13cc725e35Sriastradh * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14cc725e35Sriastradh * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15cc725e35Sriastradh * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16cc725e35Sriastradh * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17cc725e35Sriastradh * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18cc725e35Sriastradh */ 19cc725e35Sriastradh 20cc725e35Sriastradh #include <sys/cdefs.h> 21*18b0d9ddSriastradh __KERNEL_RCSID(0, "$NetBSD: viocon.c,v 1.10 2024/08/05 19:13:34 riastradh Exp $"); 22cc725e35Sriastradh 23cc725e35Sriastradh #include <sys/param.h> 24cc725e35Sriastradh #include <sys/types.h> 25cc725e35Sriastradh 26cc725e35Sriastradh #include <sys/bus.h> 27cc725e35Sriastradh #include <sys/conf.h> 28cc725e35Sriastradh #include <sys/device.h> 29cc725e35Sriastradh #include <sys/kauth.h> 30cc725e35Sriastradh #include <sys/kernel.h> 31cc725e35Sriastradh #include <sys/kmem.h> 32cc725e35Sriastradh #include <sys/lwp.h> 33cc725e35Sriastradh #include <sys/systm.h> 34cc725e35Sriastradh #include <sys/tty.h> 35cc725e35Sriastradh 36cc725e35Sriastradh #include <dev/pci/virtioreg.h> 37cc725e35Sriastradh #include <dev/pci/virtiovar.h> 38cc725e35Sriastradh 39cc725e35Sriastradh #include "ioconf.h" 40cc725e35Sriastradh 41cc725e35Sriastradh /* OpenBSD compat shims */ 42cc725e35Sriastradh #define ttymalloc(speed) tty_alloc() 43cc725e35Sriastradh #define splassert(ipl) __nothing 44cc725e35Sriastradh #define virtio_notify(vsc, vq) virtio_enqueue_commit(vsc, vq, -1, true) 45cc725e35Sriastradh #define ttwakeupwr(tp) __nothing 46cc725e35Sriastradh 47cc725e35Sriastradh /* features */ 48cc725e35Sriastradh #define VIRTIO_CONSOLE_F_SIZE (1ULL<<0) 49cc725e35Sriastradh #define VIRTIO_CONSOLE_F_MULTIPORT (1ULL<<1) 50cc725e35Sriastradh #define VIRTIO_CONSOLE_F_EMERG_WRITE (1ULL<<2) 51cc725e35Sriastradh 52cc725e35Sriastradh /* config space */ 53cc725e35Sriastradh #define VIRTIO_CONSOLE_COLS 0 /* 16 bits */ 54cc725e35Sriastradh #define VIRTIO_CONSOLE_ROWS 2 /* 16 bits */ 55cc725e35Sriastradh #define VIRTIO_CONSOLE_MAX_NR_PORTS 4 /* 32 bits */ 56cc725e35Sriastradh #define VIRTIO_CONSOLE_EMERG_WR 8 /* 32 bits */ 57cc725e35Sriastradh 58cc725e35Sriastradh #define VIOCON_DEBUG 0 59cc725e35Sriastradh 60cc725e35Sriastradh #if VIOCON_DEBUG 61cc725e35Sriastradh #define DPRINTF(x...) printf(x) 62cc725e35Sriastradh #else 63cc725e35Sriastradh #define DPRINTF(x...) 64cc725e35Sriastradh #endif 65cc725e35Sriastradh 66cc725e35Sriastradh #define VIRTIO_CONSOLE_FLAG_BITS \ 67cc725e35Sriastradh VIRTIO_COMMON_FLAG_BITS \ 68cc725e35Sriastradh "b\x00" "SIZE\0" \ 69cc725e35Sriastradh "b\x01" "MULTIPORT\0" \ 70cc725e35Sriastradh "b\x02" "EMERG_WRITE\0" 71cc725e35Sriastradh 72cc725e35Sriastradh struct virtio_console_control { 73cc725e35Sriastradh uint32_t id; /* Port number */ 74cc725e35Sriastradh 75cc725e35Sriastradh #define VIRTIO_CONSOLE_DEVICE_READY 0 76cc725e35Sriastradh #define VIRTIO_CONSOLE_PORT_ADD 1 77cc725e35Sriastradh #define VIRTIO_CONSOLE_PORT_REMOVE 2 78cc725e35Sriastradh #define VIRTIO_CONSOLE_PORT_READY 3 79cc725e35Sriastradh #define VIRTIO_CONSOLE_CONSOLE_PORT 4 80cc725e35Sriastradh #define VIRTIO_CONSOLE_RESIZE 5 81cc725e35Sriastradh #define VIRTIO_CONSOLE_PORT_OPEN 6 82cc725e35Sriastradh #define VIRTIO_CONSOLE_PORT_NAME 7 83cc725e35Sriastradh uint16_t event; 84cc725e35Sriastradh 85cc725e35Sriastradh uint16_t value; 86cc725e35Sriastradh }; 87cc725e35Sriastradh 88cc725e35Sriastradh struct virtio_console_control_resize { 89cc725e35Sriastradh /* yes, the order is different than in config space */ 90cc725e35Sriastradh uint16_t rows; 91cc725e35Sriastradh uint16_t cols; 92cc725e35Sriastradh }; 93cc725e35Sriastradh 94cc725e35Sriastradh #define BUFSIZE 128 95cc725e35Sriastradh 96a8a1e860Sriastradh #define VIOCONDEV(u,p) makedev(cdevsw_lookup_major(&viocon_cdevsw), \ 97a8a1e860Sriastradh ((u) << 4) | (p)) 98cc725e35Sriastradh #define VIOCONUNIT(x) (minor(x) >> 4) 99cc725e35Sriastradh #define VIOCONPORT(x) (minor(x) & 0x0f) 100cc725e35Sriastradh 101cc725e35Sriastradh struct viocon_port { 102cc725e35Sriastradh struct viocon_softc *vp_sc; 103cc725e35Sriastradh struct virtqueue *vp_rx; 104cc725e35Sriastradh struct virtqueue *vp_tx; 105cc725e35Sriastradh void *vp_si; 106cc725e35Sriastradh struct tty *vp_tty; 107cc725e35Sriastradh const char *vp_name; 108cc725e35Sriastradh bus_dma_segment_t vp_dmaseg; 109cc725e35Sriastradh bus_dmamap_t vp_dmamap; 110cc725e35Sriastradh #ifdef NOTYET 111cc725e35Sriastradh unsigned int vp_host_open:1; /* XXX needs F_MULTIPORT */ 112cc725e35Sriastradh unsigned int vp_guest_open:1; /* XXX needs F_MULTIPORT */ 113cc725e35Sriastradh unsigned int vp_is_console:1; /* XXX needs F_MULTIPORT */ 114cc725e35Sriastradh #endif 115cc725e35Sriastradh unsigned int vp_iflow:1; /* rx flow control */ 116cc725e35Sriastradh uint16_t vp_rows; 117cc725e35Sriastradh uint16_t vp_cols; 118cc725e35Sriastradh u_char *vp_rx_buf; 119cc725e35Sriastradh u_char *vp_tx_buf; 120cc725e35Sriastradh }; 121cc725e35Sriastradh 122cc725e35Sriastradh struct viocon_softc { 123cc725e35Sriastradh struct device *sc_dev; 124cc725e35Sriastradh struct virtio_softc *sc_virtio; 125cc725e35Sriastradh struct virtqueue *sc_vqs; 126702ae628Syamaguchi #define VIOCON_PORT_RX 0 127702ae628Syamaguchi #define VIOCON_PORT_TX 1 128702ae628Syamaguchi #define VIOCON_PORT_NQS 2 129cc725e35Sriastradh 130cc725e35Sriastradh struct virtqueue *sc_c_vq_rx; 131cc725e35Sriastradh struct virtqueue *sc_c_vq_tx; 132cc725e35Sriastradh 133cc725e35Sriastradh unsigned int sc_max_ports; 134cc725e35Sriastradh struct viocon_port **sc_ports; 135cc725e35Sriastradh }; 136cc725e35Sriastradh 137cc725e35Sriastradh int viocon_match(struct device *, struct cfdata *, void *); 138cc725e35Sriastradh void viocon_attach(struct device *, struct device *, void *); 139cc725e35Sriastradh int viocon_tx_intr(struct virtqueue *); 140cc725e35Sriastradh int viocon_tx_drain(struct viocon_port *, struct virtqueue *vq); 141cc725e35Sriastradh int viocon_rx_intr(struct virtqueue *); 142cc725e35Sriastradh void viocon_rx_soft(void *); 143cc725e35Sriastradh void viocon_rx_fill(struct viocon_port *); 144cc725e35Sriastradh int viocon_port_create(struct viocon_softc *, int); 145cc725e35Sriastradh void vioconstart(struct tty *); 146cc725e35Sriastradh int vioconhwiflow(struct tty *, int); 147cc725e35Sriastradh int vioconparam(struct tty *, struct termios *); 148cc725e35Sriastradh int vioconopen(dev_t, int, int, struct lwp *); 149cc725e35Sriastradh int vioconclose(dev_t, int, int, struct lwp *); 150cc725e35Sriastradh int vioconread(dev_t, struct uio *, int); 151cc725e35Sriastradh int vioconwrite(dev_t, struct uio *, int); 152cc725e35Sriastradh void vioconstop(struct tty *, int); 153cc725e35Sriastradh int vioconioctl(dev_t, u_long, void *, int, struct lwp *); 154cc725e35Sriastradh struct tty *viocontty(dev_t dev); 155cc725e35Sriastradh 156cc725e35Sriastradh CFATTACH_DECL_NEW(viocon, sizeof(struct viocon_softc), 157cc725e35Sriastradh viocon_match, viocon_attach, /*detach*/NULL, /*activate*/NULL); 158cc725e35Sriastradh 159cc725e35Sriastradh const struct cdevsw viocon_cdevsw = { 160cc725e35Sriastradh .d_open = vioconopen, 161cc725e35Sriastradh .d_close = vioconclose, 162cc725e35Sriastradh .d_read = vioconread, 163cc725e35Sriastradh .d_write = vioconwrite, 164cc725e35Sriastradh .d_ioctl = vioconioctl, 165cc725e35Sriastradh .d_stop = vioconstop, 166cc725e35Sriastradh .d_tty = viocontty, 167cc725e35Sriastradh .d_poll = nopoll, /* XXX */ 168cc725e35Sriastradh .d_mmap = nommap, 169cc725e35Sriastradh .d_kqfilter = ttykqfilter, 170cc725e35Sriastradh .d_discard = nodiscard, 171cc725e35Sriastradh .d_flag = D_TTY, 172cc725e35Sriastradh }; 173cc725e35Sriastradh 174cc725e35Sriastradh static inline struct viocon_softc * 175cc725e35Sriastradh dev2sc(dev_t dev) 176cc725e35Sriastradh { 177cc725e35Sriastradh return device_lookup_private(&viocon_cd, VIOCONUNIT(dev)); 178cc725e35Sriastradh } 179cc725e35Sriastradh 180cc725e35Sriastradh static inline struct viocon_port * 181cc725e35Sriastradh dev2port(dev_t dev) 182cc725e35Sriastradh { 183cc725e35Sriastradh return dev2sc(dev)->sc_ports[VIOCONPORT(dev)]; 184cc725e35Sriastradh } 185cc725e35Sriastradh 186cc725e35Sriastradh int viocon_match(struct device *parent, struct cfdata *match, void *aux) 187cc725e35Sriastradh { 188cc725e35Sriastradh struct virtio_attach_args *va = aux; 189cc725e35Sriastradh if (va->sc_childdevid == VIRTIO_DEVICE_ID_CONSOLE) 190cc725e35Sriastradh return 1; 191cc725e35Sriastradh return 0; 192cc725e35Sriastradh } 193cc725e35Sriastradh 194cc725e35Sriastradh void 195cc725e35Sriastradh viocon_attach(struct device *parent, struct device *self, void *aux) 196cc725e35Sriastradh { 197cc725e35Sriastradh struct viocon_softc *sc = device_private(self); 198cc725e35Sriastradh struct virtio_softc *vsc = device_private(parent); 199cc725e35Sriastradh int maxports = 1; 200702ae628Syamaguchi size_t nvqs; 201cc725e35Sriastradh 202cc725e35Sriastradh sc->sc_dev = self; 203cc725e35Sriastradh if (virtio_child(vsc) != NULL) { 204cc725e35Sriastradh aprint_error(": parent %s already has a child\n", 205cc725e35Sriastradh device_xname(parent)); 206cc725e35Sriastradh return; 207cc725e35Sriastradh } 208cc725e35Sriastradh sc->sc_virtio = vsc; 209cc725e35Sriastradh sc->sc_max_ports = maxports; 210702ae628Syamaguchi nvqs = VIOCON_PORT_NQS * maxports; 211cc725e35Sriastradh 212702ae628Syamaguchi sc->sc_vqs = kmem_zalloc(nvqs * sizeof(sc->sc_vqs[0]), 213cc725e35Sriastradh KM_SLEEP); 214cc725e35Sriastradh sc->sc_ports = kmem_zalloc(maxports * sizeof(sc->sc_ports[0]), 215cc725e35Sriastradh KM_SLEEP); 216cc725e35Sriastradh 21786598168Syamaguchi virtio_child_attach_start(vsc, self, IPL_TTY, 21886598168Syamaguchi /*req_features*/VIRTIO_CONSOLE_F_SIZE, VIRTIO_CONSOLE_FLAG_BITS); 219cc725e35Sriastradh 220cc725e35Sriastradh DPRINTF("%s: softc: %p\n", __func__, sc); 221cc725e35Sriastradh if (viocon_port_create(sc, 0) != 0) { 222cc725e35Sriastradh printf("\n%s: viocon_port_create failed\n", __func__); 223cc725e35Sriastradh goto err; 224cc725e35Sriastradh } 225cc725e35Sriastradh 226702ae628Syamaguchi if (virtio_child_attach_finish(vsc, sc->sc_vqs, nvqs, 2273cf221ffSyamaguchi /*config_change*/NULL, /*req_flags*/0) != 0) 228cc725e35Sriastradh goto err; 229cc725e35Sriastradh 230*18b0d9ddSriastradh viocon_rx_fill(sc->sc_ports[0]); 231*18b0d9ddSriastradh 232cc725e35Sriastradh return; 233cc725e35Sriastradh err: 234702ae628Syamaguchi kmem_free(sc->sc_vqs, nvqs * sizeof(sc->sc_vqs[0])); 235cc725e35Sriastradh kmem_free(sc->sc_ports, maxports * sizeof(sc->sc_ports[0])); 236cc725e35Sriastradh virtio_child_attach_failed(vsc); 237cc725e35Sriastradh } 238cc725e35Sriastradh 239cc725e35Sriastradh int 240cc725e35Sriastradh viocon_port_create(struct viocon_softc *sc, int portidx) 241cc725e35Sriastradh { 242cc725e35Sriastradh struct virtio_softc *vsc = sc->sc_virtio; 243cc725e35Sriastradh int rxidx, txidx, allocsize, nsegs; 244cc725e35Sriastradh char name[6]; 245cc725e35Sriastradh struct viocon_port *vp; 246cc725e35Sriastradh void *kva; 247cc725e35Sriastradh struct tty *tp; 248cc725e35Sriastradh 249cc725e35Sriastradh vp = kmem_zalloc(sizeof(*vp), KM_SLEEP); 250cc725e35Sriastradh if (vp == NULL) 251cc725e35Sriastradh return ENOMEM; 252cc725e35Sriastradh sc->sc_ports[portidx] = vp; 253cc725e35Sriastradh vp->vp_sc = sc; 254cc725e35Sriastradh DPRINTF("%s: vp: %p\n", __func__, vp); 255cc725e35Sriastradh 256702ae628Syamaguchi rxidx = (portidx * VIOCON_PORT_NQS) + VIOCON_PORT_RX; 257702ae628Syamaguchi txidx = (portidx * VIOCON_PORT_NQS) + VIOCON_PORT_TX; 258cc725e35Sriastradh 259cc725e35Sriastradh snprintf(name, sizeof(name), "p%drx", portidx); 2603cf221ffSyamaguchi virtio_init_vq_vqdone(vsc, &sc->sc_vqs[rxidx], rxidx, 2613cf221ffSyamaguchi viocon_rx_intr); 2623cf221ffSyamaguchi if (virtio_alloc_vq(vsc, &sc->sc_vqs[rxidx], BUFSIZE, 1, 263cc725e35Sriastradh name) != 0) { 264cc725e35Sriastradh printf("\nCan't alloc %s virtqueue\n", name); 265cc725e35Sriastradh goto err; 266cc725e35Sriastradh } 267cc725e35Sriastradh vp->vp_rx = &sc->sc_vqs[rxidx]; 268cc725e35Sriastradh vp->vp_si = softint_establish(SOFTINT_SERIAL, viocon_rx_soft, vp); 269cc725e35Sriastradh DPRINTF("%s: rx: %p\n", __func__, vp->vp_rx); 270cc725e35Sriastradh 271cc725e35Sriastradh snprintf(name, sizeof(name), "p%dtx", portidx); 2723cf221ffSyamaguchi virtio_init_vq_vqdone(vsc, &sc->sc_vqs[txidx], txidx, 2733cf221ffSyamaguchi viocon_tx_intr); 2743cf221ffSyamaguchi if (virtio_alloc_vq(vsc, &sc->sc_vqs[txidx], BUFSIZE, 1, 275cc725e35Sriastradh name) != 0) { 276cc725e35Sriastradh printf("\nCan't alloc %s virtqueue\n", name); 277cc725e35Sriastradh goto err; 278cc725e35Sriastradh } 279cc725e35Sriastradh vp->vp_tx = &sc->sc_vqs[txidx]; 280cc725e35Sriastradh DPRINTF("%s: tx: %p\n", __func__, vp->vp_tx); 281cc725e35Sriastradh 282cc725e35Sriastradh allocsize = (vp->vp_rx->vq_num + vp->vp_tx->vq_num) * BUFSIZE; 283cc725e35Sriastradh 284cc725e35Sriastradh if (bus_dmamap_create(virtio_dmat(vsc), allocsize, 1, allocsize, 0, 285cc725e35Sriastradh BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &vp->vp_dmamap) != 0) 286cc725e35Sriastradh goto err; 287cc725e35Sriastradh if (bus_dmamem_alloc(virtio_dmat(vsc), allocsize, 8, 0, &vp->vp_dmaseg, 288cc725e35Sriastradh 1, &nsegs, BUS_DMA_NOWAIT) != 0) 289cc725e35Sriastradh goto err; 290cc725e35Sriastradh if (bus_dmamem_map(virtio_dmat(vsc), &vp->vp_dmaseg, nsegs, 291cc725e35Sriastradh allocsize, &kva, BUS_DMA_NOWAIT) != 0) 292cc725e35Sriastradh goto err; 293cc725e35Sriastradh memset(kva, 0, allocsize); 294cc725e35Sriastradh if (bus_dmamap_load(virtio_dmat(vsc), vp->vp_dmamap, kva, 295cc725e35Sriastradh allocsize, NULL, BUS_DMA_NOWAIT) != 0) 296cc725e35Sriastradh goto err; 297cc725e35Sriastradh vp->vp_rx_buf = (unsigned char *)kva; 298cc725e35Sriastradh /* 299cc725e35Sriastradh * XXX use only a small circular tx buffer instead of many BUFSIZE buffers? 300cc725e35Sriastradh */ 301cc725e35Sriastradh vp->vp_tx_buf = vp->vp_rx_buf + vp->vp_rx->vq_num * BUFSIZE; 302cc725e35Sriastradh 303cc725e35Sriastradh if (virtio_features(vsc) & VIRTIO_CONSOLE_F_SIZE) { 304cc725e35Sriastradh vp->vp_cols = virtio_read_device_config_2(vsc, 305cc725e35Sriastradh VIRTIO_CONSOLE_COLS); 306cc725e35Sriastradh vp->vp_rows = virtio_read_device_config_2(vsc, 307cc725e35Sriastradh VIRTIO_CONSOLE_ROWS); 308cc725e35Sriastradh } 309cc725e35Sriastradh 310cc725e35Sriastradh tp = ttymalloc(1000000); 311cc725e35Sriastradh tp->t_oproc = vioconstart; 312cc725e35Sriastradh tp->t_param = vioconparam; 313cc725e35Sriastradh tp->t_hwiflow = vioconhwiflow; 314a8a1e860Sriastradh tp->t_dev = VIOCONDEV(device_unit(sc->sc_dev), portidx); 315cc725e35Sriastradh vp->vp_tty = tp; 316cc725e35Sriastradh DPRINTF("%s: tty: %p\n", __func__, tp); 317cc725e35Sriastradh 318cc725e35Sriastradh virtio_start_vq_intr(vsc, vp->vp_rx); 319cc725e35Sriastradh virtio_start_vq_intr(vsc, vp->vp_tx); 320cc725e35Sriastradh 321cc725e35Sriastradh return 0; 322cc725e35Sriastradh err: 323cc725e35Sriastradh panic("%s failed", __func__); 324cc725e35Sriastradh return -1; 325cc725e35Sriastradh } 326cc725e35Sriastradh 327cc725e35Sriastradh int 328cc725e35Sriastradh viocon_tx_drain(struct viocon_port *vp, struct virtqueue *vq) 329cc725e35Sriastradh { 330cc725e35Sriastradh struct virtio_softc *vsc = vq->vq_owner; 331cc725e35Sriastradh int ndone = 0, len, slot; 332cc725e35Sriastradh 333cc725e35Sriastradh splassert(IPL_TTY); 334cc725e35Sriastradh while (virtio_dequeue(vsc, vq, &slot, &len) == 0) { 335cc725e35Sriastradh bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap, 336cc725e35Sriastradh vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, BUFSIZE, 337e35330b9Sriastradh BUS_DMASYNC_POSTWRITE); 338cc725e35Sriastradh virtio_dequeue_commit(vsc, vq, slot); 339cc725e35Sriastradh ndone++; 340cc725e35Sriastradh } 341cc725e35Sriastradh return ndone; 342cc725e35Sriastradh } 343cc725e35Sriastradh 344cc725e35Sriastradh int 345cc725e35Sriastradh viocon_tx_intr(struct virtqueue *vq) 346cc725e35Sriastradh { 347cc725e35Sriastradh struct virtio_softc *vsc = vq->vq_owner; 348cc725e35Sriastradh struct viocon_softc *sc = device_private(virtio_child(vsc)); 349cc725e35Sriastradh int ndone = 0; 350cc725e35Sriastradh int portidx = (vq->vq_index - 1) / 2; 351cc725e35Sriastradh struct viocon_port *vp = sc->sc_ports[portidx]; 352cc725e35Sriastradh struct tty *tp = vp->vp_tty; 353cc725e35Sriastradh 354cc725e35Sriastradh splassert(IPL_TTY); 355cc725e35Sriastradh ndone = viocon_tx_drain(vp, vq); 356cc725e35Sriastradh if (ndone && ISSET(tp->t_state, TS_BUSY)) { 357cc725e35Sriastradh CLR(tp->t_state, TS_BUSY); 358cc725e35Sriastradh (*tp->t_linesw->l_start)(tp); 359cc725e35Sriastradh } 360cc725e35Sriastradh 361cc725e35Sriastradh return 1; 362cc725e35Sriastradh } 363cc725e35Sriastradh 364cc725e35Sriastradh void 365cc725e35Sriastradh viocon_rx_fill(struct viocon_port *vp) 366cc725e35Sriastradh { 367cc725e35Sriastradh struct virtqueue *vq = vp->vp_rx; 368cc725e35Sriastradh struct virtio_softc *vsc = vp->vp_sc->sc_virtio; 369cc725e35Sriastradh int r, slot, ndone = 0; 370cc725e35Sriastradh 371cc725e35Sriastradh while ((r = virtio_enqueue_prep(vsc, vq, &slot)) == 0) { 372cc725e35Sriastradh if (virtio_enqueue_reserve(vsc, vq, slot, 1) != 0) 373cc725e35Sriastradh break; 374cc725e35Sriastradh bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap, slot * BUFSIZE, 375cc725e35Sriastradh BUFSIZE, BUS_DMASYNC_PREREAD); 376cc725e35Sriastradh virtio_enqueue_p(vsc, vq, slot, vp->vp_dmamap, slot * BUFSIZE, 377cc725e35Sriastradh BUFSIZE, 0); 378cc725e35Sriastradh virtio_enqueue_commit(vsc, vq, slot, 0); 379cc725e35Sriastradh ndone++; 380cc725e35Sriastradh } 3817790e19fSriastradh KASSERTMSG(r == 0 || r == EAGAIN, "r=%d", r); 382cc725e35Sriastradh if (ndone > 0) 383cc725e35Sriastradh virtio_notify(vsc, vq); 384cc725e35Sriastradh } 385cc725e35Sriastradh 386cc725e35Sriastradh int 387cc725e35Sriastradh viocon_rx_intr(struct virtqueue *vq) 388cc725e35Sriastradh { 389cc725e35Sriastradh struct virtio_softc *vsc = vq->vq_owner; 390cc725e35Sriastradh struct viocon_softc *sc = device_private(virtio_child(vsc)); 391cc725e35Sriastradh int portidx = (vq->vq_index - 1) / 2; 392cc725e35Sriastradh struct viocon_port *vp = sc->sc_ports[portidx]; 393cc725e35Sriastradh 394cc725e35Sriastradh softint_schedule(vp->vp_si); 395cc725e35Sriastradh return 1; 396cc725e35Sriastradh } 397cc725e35Sriastradh 398cc725e35Sriastradh void 399cc725e35Sriastradh viocon_rx_soft(void *arg) 400cc725e35Sriastradh { 401cc725e35Sriastradh struct viocon_port *vp = arg; 402cc725e35Sriastradh struct virtqueue *vq = vp->vp_rx; 403cc725e35Sriastradh struct virtio_softc *vsc = vq->vq_owner; 404cc725e35Sriastradh struct tty *tp = vp->vp_tty; 405cc725e35Sriastradh int slot, len, i; 406cc725e35Sriastradh u_char *p; 407cc725e35Sriastradh 408cc725e35Sriastradh while (!vp->vp_iflow && virtio_dequeue(vsc, vq, &slot, &len) == 0) { 409cc725e35Sriastradh bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap, 410cc725e35Sriastradh slot * BUFSIZE, BUFSIZE, BUS_DMASYNC_POSTREAD); 411cc725e35Sriastradh p = vp->vp_rx_buf + slot * BUFSIZE; 412cc725e35Sriastradh for (i = 0; i < len; i++) 413cc725e35Sriastradh (*tp->t_linesw->l_rint)(*p++, tp); 414cc725e35Sriastradh virtio_dequeue_commit(vsc, vq, slot); 415cc725e35Sriastradh } 416cc725e35Sriastradh 417cc725e35Sriastradh viocon_rx_fill(vp); 418cc725e35Sriastradh 419cc725e35Sriastradh return; 420cc725e35Sriastradh } 421cc725e35Sriastradh 422cc725e35Sriastradh void 423cc725e35Sriastradh vioconstart(struct tty *tp) 424cc725e35Sriastradh { 425cc725e35Sriastradh struct viocon_softc *sc = dev2sc(tp->t_dev); 426cc725e35Sriastradh struct virtio_softc *vsc; 427cc725e35Sriastradh struct viocon_port *vp = dev2port(tp->t_dev); 428cc725e35Sriastradh struct virtqueue *vq; 429cc725e35Sriastradh u_char *buf; 430cc725e35Sriastradh int s, cnt, slot, ret, ndone; 431cc725e35Sriastradh 432cc725e35Sriastradh vsc = sc->sc_virtio; 433cc725e35Sriastradh vq = vp->vp_tx; 434cc725e35Sriastradh 435cc725e35Sriastradh s = spltty(); 436cc725e35Sriastradh 437cc725e35Sriastradh ndone = viocon_tx_drain(vp, vq); 438cc725e35Sriastradh if (ISSET(tp->t_state, TS_BUSY)) { 439cc725e35Sriastradh if (ndone > 0) 440cc725e35Sriastradh CLR(tp->t_state, TS_BUSY); 441cc725e35Sriastradh else 442cc725e35Sriastradh goto out; 443cc725e35Sriastradh } 444cc725e35Sriastradh if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP)) 445cc725e35Sriastradh goto out; 446cc725e35Sriastradh 447cc725e35Sriastradh if (tp->t_outq.c_cc == 0) 448cc725e35Sriastradh goto out; 449cc725e35Sriastradh ndone = 0; 450cc725e35Sriastradh 451cc725e35Sriastradh while (tp->t_outq.c_cc > 0) { 452cc725e35Sriastradh ret = virtio_enqueue_prep(vsc, vq, &slot); 453cc725e35Sriastradh if (ret == EAGAIN) { 454cc725e35Sriastradh SET(tp->t_state, TS_BUSY); 455cc725e35Sriastradh break; 456cc725e35Sriastradh } 4577790e19fSriastradh KASSERTMSG(ret == 0, "ret=%d", ret); 458cc725e35Sriastradh ret = virtio_enqueue_reserve(vsc, vq, slot, 1); 4597790e19fSriastradh KASSERTMSG(ret == 0, "ret=%d", ret); 460cc725e35Sriastradh buf = vp->vp_tx_buf + slot * BUFSIZE; 461cc725e35Sriastradh cnt = q_to_b(&tp->t_outq, buf, BUFSIZE); 462cc725e35Sriastradh bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap, 463cc725e35Sriastradh vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt, 464cc725e35Sriastradh BUS_DMASYNC_PREWRITE); 465cc725e35Sriastradh virtio_enqueue_p(vsc, vq, slot, vp->vp_dmamap, 466cc725e35Sriastradh vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt, 1); 467cc725e35Sriastradh virtio_enqueue_commit(vsc, vq, slot, 0); 468cc725e35Sriastradh ndone++; 469cc725e35Sriastradh } 470cc725e35Sriastradh if (ndone > 0) 471cc725e35Sriastradh virtio_notify(vsc, vq); 472cc725e35Sriastradh ttwakeupwr(tp); 473cc725e35Sriastradh out: 474cc725e35Sriastradh splx(s); 475cc725e35Sriastradh } 476cc725e35Sriastradh 477cc725e35Sriastradh int 478cc725e35Sriastradh vioconhwiflow(struct tty *tp, int stop) 479cc725e35Sriastradh { 480cc725e35Sriastradh struct viocon_port *vp = dev2port(tp->t_dev); 481cc725e35Sriastradh int s; 482cc725e35Sriastradh 483cc725e35Sriastradh s = spltty(); 484cc725e35Sriastradh vp->vp_iflow = stop; 485cc725e35Sriastradh if (stop) { 486cc725e35Sriastradh virtio_stop_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx); 487cc725e35Sriastradh } else { 488cc725e35Sriastradh virtio_start_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx); 489cc725e35Sriastradh softint_schedule(vp->vp_si); 490cc725e35Sriastradh } 491cc725e35Sriastradh splx(s); 492cc725e35Sriastradh return 1; 493cc725e35Sriastradh } 494cc725e35Sriastradh 495cc725e35Sriastradh int 496cc725e35Sriastradh vioconparam(struct tty *tp, struct termios *t) 497cc725e35Sriastradh { 498cc725e35Sriastradh tp->t_ispeed = t->c_ispeed; 499cc725e35Sriastradh tp->t_ospeed = t->c_ospeed; 500cc725e35Sriastradh tp->t_cflag = t->c_cflag; 501cc725e35Sriastradh 502cc725e35Sriastradh vioconstart(tp); 503cc725e35Sriastradh return 0; 504cc725e35Sriastradh } 505cc725e35Sriastradh 506cc725e35Sriastradh int 507cc725e35Sriastradh vioconopen(dev_t dev, int flag, int mode, struct lwp *l) 508cc725e35Sriastradh { 509cc725e35Sriastradh int unit = VIOCONUNIT(dev); 510cc725e35Sriastradh int port = VIOCONPORT(dev); 511cc725e35Sriastradh struct viocon_softc *sc; 512cc725e35Sriastradh struct viocon_port *vp; 513cc725e35Sriastradh struct tty *tp; 514cc725e35Sriastradh int s, error; 515cc725e35Sriastradh 516cc725e35Sriastradh sc = device_lookup_private(&viocon_cd, unit); 517cc725e35Sriastradh if (sc == NULL) 518cc725e35Sriastradh return (ENXIO); 519cc725e35Sriastradh if (!device_is_active(sc->sc_dev)) 520cc725e35Sriastradh return (ENXIO); 521cc725e35Sriastradh 522cc725e35Sriastradh s = spltty(); 523cc725e35Sriastradh if (port >= sc->sc_max_ports) { 524cc725e35Sriastradh splx(s); 525cc725e35Sriastradh return (ENXIO); 526cc725e35Sriastradh } 527cc725e35Sriastradh vp = sc->sc_ports[port]; 528cc725e35Sriastradh tp = vp->vp_tty; 529cc725e35Sriastradh #ifdef NOTYET 530cc725e35Sriastradh vp->vp_guest_open = 1; 531cc725e35Sriastradh #endif 532cc725e35Sriastradh splx(s); 533cc725e35Sriastradh 534cc725e35Sriastradh if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) 535cc725e35Sriastradh return (EBUSY); 536cc725e35Sriastradh 537cc725e35Sriastradh s = spltty(); 538cc725e35Sriastradh if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { 539cc725e35Sriastradh ttychars(tp); 540cc725e35Sriastradh tp->t_ispeed = 1000000; 541cc725e35Sriastradh tp->t_ospeed = 1000000; 542cc725e35Sriastradh tp->t_cflag = TTYDEF_CFLAG|CLOCAL|CRTSCTS; 543cc725e35Sriastradh tp->t_iflag = TTYDEF_IFLAG; 544cc725e35Sriastradh tp->t_oflag = TTYDEF_OFLAG; 545cc725e35Sriastradh tp->t_lflag = TTYDEF_LFLAG; 546cc725e35Sriastradh if (vp->vp_cols != 0) { 547cc725e35Sriastradh tp->t_winsize.ws_col = vp->vp_cols; 548cc725e35Sriastradh tp->t_winsize.ws_row = vp->vp_rows; 549cc725e35Sriastradh } 550cc725e35Sriastradh 551cc725e35Sriastradh vioconparam(tp, &tp->t_termios); 552cc725e35Sriastradh ttsetwater(tp); 553cc725e35Sriastradh } 554cc725e35Sriastradh splx(s); 555cc725e35Sriastradh 556cc725e35Sriastradh error = (*tp->t_linesw->l_open)(dev, tp); 557cc725e35Sriastradh return error; 558cc725e35Sriastradh } 559cc725e35Sriastradh 560cc725e35Sriastradh int 561cc725e35Sriastradh vioconclose(dev_t dev, int flag, int mode, struct lwp *l) 562cc725e35Sriastradh { 563cc725e35Sriastradh struct viocon_port *vp = dev2port(dev); 564cc725e35Sriastradh struct tty *tp = vp->vp_tty; 565cc725e35Sriastradh int s; 566cc725e35Sriastradh 567cc725e35Sriastradh if (!ISSET(tp->t_state, TS_ISOPEN)) 568cc725e35Sriastradh return 0; 569cc725e35Sriastradh 570cc725e35Sriastradh (*tp->t_linesw->l_close)(tp, flag); 571cc725e35Sriastradh s = spltty(); 572cc725e35Sriastradh #ifdef NOTYET 573cc725e35Sriastradh vp->vp_guest_open = 0; 574cc725e35Sriastradh #endif 575cc725e35Sriastradh CLR(tp->t_state, TS_BUSY | TS_FLUSH); 576cc725e35Sriastradh ttyclose(tp); 577cc725e35Sriastradh splx(s); 578cc725e35Sriastradh 579cc725e35Sriastradh return 0; 580cc725e35Sriastradh } 581cc725e35Sriastradh 582cc725e35Sriastradh int 583cc725e35Sriastradh vioconread(dev_t dev, struct uio *uio, int flag) 584cc725e35Sriastradh { 585cc725e35Sriastradh struct viocon_port *vp = dev2port(dev); 586cc725e35Sriastradh struct tty *tp = vp->vp_tty; 587cc725e35Sriastradh 588cc725e35Sriastradh return (*tp->t_linesw->l_read)(tp, uio, flag); 589cc725e35Sriastradh } 590cc725e35Sriastradh 591cc725e35Sriastradh int 592cc725e35Sriastradh vioconwrite(dev_t dev, struct uio *uio, int flag) 593cc725e35Sriastradh { 594cc725e35Sriastradh struct viocon_port *vp = dev2port(dev); 595cc725e35Sriastradh struct tty *tp = vp->vp_tty; 596cc725e35Sriastradh 597cc725e35Sriastradh return (*tp->t_linesw->l_write)(tp, uio, flag); 598cc725e35Sriastradh } 599cc725e35Sriastradh 600cc725e35Sriastradh struct tty * 601cc725e35Sriastradh viocontty(dev_t dev) 602cc725e35Sriastradh { 603cc725e35Sriastradh struct viocon_port *vp = dev2port(dev); 604cc725e35Sriastradh 605cc725e35Sriastradh return vp->vp_tty; 606cc725e35Sriastradh } 607cc725e35Sriastradh 608cc725e35Sriastradh void 609cc725e35Sriastradh vioconstop(struct tty *tp, int flag) 610cc725e35Sriastradh { 611cc725e35Sriastradh int s; 612cc725e35Sriastradh 613cc725e35Sriastradh s = spltty(); 614cc725e35Sriastradh if (ISSET(tp->t_state, TS_BUSY)) 615cc725e35Sriastradh if (!ISSET(tp->t_state, TS_TTSTOP)) 616cc725e35Sriastradh SET(tp->t_state, TS_FLUSH); 617cc725e35Sriastradh splx(s); 618cc725e35Sriastradh } 619cc725e35Sriastradh 620cc725e35Sriastradh int 621cc725e35Sriastradh vioconioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 622cc725e35Sriastradh { 623cc725e35Sriastradh struct viocon_port *vp = dev2port(dev); 624cc725e35Sriastradh struct tty *tp; 625cc725e35Sriastradh int error1, error2; 626cc725e35Sriastradh 627cc725e35Sriastradh tp = vp->vp_tty; 628cc725e35Sriastradh 629cc725e35Sriastradh error1 = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l); 630cc725e35Sriastradh if (error1 >= 0) 631cc725e35Sriastradh return error1; 632cc725e35Sriastradh error2 = ttioctl(tp, cmd, data, flag, l); 633cc725e35Sriastradh if (error2 >= 0) 634cc725e35Sriastradh return error2; 635cc725e35Sriastradh return ENOTTY; 636cc725e35Sriastradh } 637