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