1 /* $OpenBSD: vcctty.c,v 1.14 2018/02/19 08:59:52 mpi Exp $ */ 2 /* 3 * Copyright (c) 2009 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/conf.h> 20 #include <sys/device.h> 21 #include <sys/malloc.h> 22 #include <sys/proc.h> 23 #include <sys/systm.h> 24 #include <sys/tty.h> 25 26 #include <machine/autoconf.h> 27 #include <machine/conf.h> 28 #include <machine/hypervisor.h> 29 #include <machine/mdesc.h> 30 31 #include <dev/cons.h> 32 #include <sparc64/dev/cbusvar.h> 33 #include <sparc64/dev/ldcvar.h> 34 35 #ifdef VCCTTY_DEBUG 36 #define DPRINTF(x) printf x 37 #else 38 #define DPRINTF(x) 39 #endif 40 41 #define VCCTTY_TX_ENTRIES 128 42 #define VCCTTY_RX_ENTRIES 128 43 44 struct vcctty_msg { 45 uint8_t type; 46 uint8_t size; 47 uint16_t rsvd; 48 uint32_t ctrl_msg; 49 uint8_t data[56]; 50 }; 51 52 /* Packet types. */ 53 #define LDC_CONSOLE_CTRL 0x01 54 #define LDC_CONSOLE_DATA 0x02 55 56 struct vcctty_softc { 57 struct device sc_dv; 58 bus_space_tag_t sc_bustag; 59 bus_dma_tag_t sc_dmatag; 60 61 uint64_t sc_tx_ino; 62 uint64_t sc_rx_ino; 63 void *sc_tx_ih; 64 void *sc_rx_ih; 65 66 struct ldc_conn sc_lc; 67 68 struct tty *sc_tty; 69 }; 70 71 int vcctty_match(struct device *, void *, void *); 72 void vcctty_attach(struct device *, struct device *, void *); 73 74 struct cfattach vcctty_ca = { 75 sizeof(struct vcctty_softc), vcctty_match, vcctty_attach 76 }; 77 78 struct cfdriver vcctty_cd = { 79 NULL, "vcctty", DV_DULL 80 }; 81 82 int vcctty_tx_intr(void *); 83 int vcctty_rx_intr(void *); 84 85 void vcctty_send_data(struct vcctty_softc *, struct tty *); 86 void vcctty_send_break(struct vcctty_softc *); 87 88 void vccttystart(struct tty *); 89 int vccttyparam(struct tty *, struct termios *); 90 int vccttyhwiflow(struct tty *, int); 91 92 int 93 vcctty_match(struct device *parent, void *match, void *aux) 94 { 95 return (1); 96 } 97 98 void 99 vcctty_attach(struct device *parent, struct device *self, void *aux) 100 { 101 struct vcctty_softc *sc = (struct vcctty_softc *)self; 102 struct cbus_attach_args *ca = aux; 103 struct ldc_conn *lc; 104 int err; 105 106 sc->sc_bustag = ca->ca_bustag; 107 sc->sc_dmatag = ca->ca_dmatag; 108 sc->sc_tx_ino = ca->ca_tx_ino; 109 sc->sc_rx_ino = ca->ca_rx_ino; 110 111 printf(": ivec 0x%llx, 0x%llx", sc->sc_tx_ino, sc->sc_rx_ino); 112 113 /* 114 * Un-configure queues before registering interrupt handlers, 115 * such that we dont get any stale LDC packets or events. 116 */ 117 hv_ldc_tx_qconf(ca->ca_id, 0, 0); 118 hv_ldc_rx_qconf(ca->ca_id, 0, 0); 119 120 sc->sc_tx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_tx_ino, 121 IPL_TTY, 0, vcctty_tx_intr, sc, sc->sc_dv.dv_xname); 122 sc->sc_rx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_rx_ino, 123 IPL_TTY, 0, vcctty_rx_intr, sc, sc->sc_dv.dv_xname); 124 if (sc->sc_tx_ih == NULL || sc->sc_rx_ih == NULL) { 125 printf(", can't establish interrupt\n"); 126 return; 127 } 128 129 lc = &sc->sc_lc; 130 lc->lc_id = ca->ca_id; 131 lc->lc_sc = sc; 132 133 lc->lc_txq = ldc_queue_alloc(sc->sc_dmatag, VCCTTY_TX_ENTRIES); 134 if (lc->lc_txq == NULL) { 135 printf(", can't allocate tx queue\n"); 136 return; 137 } 138 139 lc->lc_rxq = ldc_queue_alloc(sc->sc_dmatag, VCCTTY_RX_ENTRIES); 140 if (lc->lc_rxq == NULL) { 141 printf(", can't allocate rx queue\n"); 142 goto free_txqueue; 143 } 144 145 err = hv_ldc_tx_qconf(lc->lc_id, 146 lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries); 147 if (err != H_EOK) 148 printf("%s: hv_ldc_tx_qconf %d\n", __func__, err); 149 150 err = hv_ldc_rx_qconf(lc->lc_id, 151 lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries); 152 if (err != H_EOK) 153 printf("%s: hv_ldc_rx_qconf %d\n", __func__, err); 154 155 cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, INTR_ENABLED); 156 cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, INTR_ENABLED); 157 158 printf(" domain \"%s\"\n", ca->ca_name); 159 return; 160 161 free_txqueue: 162 ldc_queue_free(sc->sc_dmatag, lc->lc_txq); 163 } 164 165 int 166 vcctty_tx_intr(void *arg) 167 { 168 struct vcctty_softc *sc = arg; 169 struct ldc_conn *lc = &sc->sc_lc; 170 uint64_t tx_head, tx_tail, tx_state; 171 int err; 172 173 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 174 if (err != H_EOK) { 175 printf("%s: hv_ldc_tx_get_state %d\n", __func__, err); 176 return (0); 177 } 178 179 if (tx_state != lc->lc_tx_state) { 180 switch (tx_state) { 181 case LDC_CHANNEL_DOWN: 182 DPRINTF(("%s: Tx link down\n", __func__)); 183 break; 184 case LDC_CHANNEL_UP: 185 DPRINTF(("%s: Tx link up\n", __func__)); 186 break; 187 case LDC_CHANNEL_RESET: 188 DPRINTF(("%s: Tx link reset\n", __func__)); 189 break; 190 } 191 lc->lc_tx_state = tx_state; 192 } 193 194 return (1); 195 } 196 197 int 198 vcctty_rx_intr(void *arg) 199 { 200 struct vcctty_softc *sc = arg; 201 struct tty *tp = sc->sc_tty; 202 struct ldc_conn *lc = &sc->sc_lc; 203 uint64_t rx_head, rx_tail, rx_state; 204 struct vcctty_msg *msg; 205 int err; 206 int i; 207 208 err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state); 209 if (err != H_EOK) { 210 printf("%s: hv_ldc_rx_get_state %d\n", __func__, err); 211 return (0); 212 } 213 214 if (rx_state != lc->lc_rx_state) { 215 switch (rx_state) { 216 case LDC_CHANNEL_DOWN: 217 DPRINTF(("%s: Rx link down\n", __func__)); 218 break; 219 case LDC_CHANNEL_UP: 220 DPRINTF(("%s: Rx link up\n", __func__)); 221 break; 222 case LDC_CHANNEL_RESET: 223 DPRINTF(("%s: Rx link reset\n", __func__)); 224 break; 225 } 226 lc->lc_rx_state = rx_state; 227 return (1); 228 } 229 230 if (rx_head == rx_tail) 231 return (0); 232 233 msg = (struct vcctty_msg *)(lc->lc_rxq->lq_va + rx_head); 234 if (tp && msg->type == LDC_CONSOLE_DATA) { 235 for (i = 0; i < msg->size; i++) { 236 if (tp->t_state & TS_ISOPEN) 237 (*linesw[tp->t_line].l_rint)(msg->data[i], tp); 238 } 239 } 240 241 rx_head += sizeof(*msg); 242 rx_head &= ((lc->lc_rxq->lq_nentries * sizeof(*msg)) - 1); 243 err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head); 244 if (err != H_EOK) 245 printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err); 246 247 return (1); 248 } 249 250 void 251 vcctty_send_data(struct vcctty_softc *sc, struct tty *tp) 252 { 253 struct ldc_conn *lc = &sc->sc_lc; 254 uint64_t tx_head, tx_tail, tx_state; 255 uint64_t next_tx_tail; 256 struct vcctty_msg *msg; 257 int err; 258 259 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 260 if (err != H_EOK || tx_state != LDC_CHANNEL_UP) 261 return; 262 263 while (tp->t_outq.c_cc > 0) { 264 next_tx_tail = tx_tail + sizeof(*msg); 265 next_tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*msg)) - 1); 266 267 if (next_tx_tail == tx_head) 268 return; 269 270 msg = (struct vcctty_msg *)(lc->lc_txq->lq_va + tx_tail); 271 bzero(msg, sizeof(*msg)); 272 msg->type = LDC_CONSOLE_DATA; 273 msg->size = q_to_b(&tp->t_outq, msg->data, sizeof(msg->data)); 274 275 err = hv_ldc_tx_set_qtail(lc->lc_id, next_tx_tail); 276 if (err != H_EOK) 277 printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err); 278 tx_tail = next_tx_tail; 279 } 280 } 281 282 void 283 vcctty_send_break(struct vcctty_softc *sc) 284 { 285 struct ldc_conn *lc = &sc->sc_lc; 286 uint64_t tx_head, tx_tail, tx_state; 287 struct vcctty_msg *msg; 288 int err; 289 290 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 291 if (err != H_EOK || tx_state != LDC_CHANNEL_UP) 292 return; 293 294 msg = (struct vcctty_msg *)(lc->lc_txq->lq_va + tx_tail); 295 bzero(msg, sizeof(*msg)); 296 msg->type = LDC_CONSOLE_CTRL; 297 msg->ctrl_msg = CONS_BREAK; 298 299 tx_tail += sizeof(*msg); 300 tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*msg)) - 1); 301 err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail); 302 if (err != H_EOK) 303 printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err); 304 } 305 306 int 307 vccttyopen(dev_t dev, int flag, int mode, struct proc *p) 308 { 309 struct vcctty_softc *sc; 310 struct tty *tp; 311 int unit = minor(dev); 312 313 if (unit >= vcctty_cd.cd_ndevs) 314 return (ENXIO); 315 sc = vcctty_cd.cd_devs[unit]; 316 if (sc == NULL) 317 return (ENXIO); 318 319 if (sc->sc_tty) 320 tp = sc->sc_tty; 321 else 322 tp = sc->sc_tty = ttymalloc(0); 323 324 tp->t_oproc = vccttystart; 325 tp->t_param = vccttyparam; 326 tp->t_hwiflow = vccttyhwiflow; 327 tp->t_dev = dev; 328 if ((tp->t_state & TS_ISOPEN) == 0) { 329 ttychars(tp); 330 tp->t_iflag = TTYDEF_IFLAG; 331 tp->t_oflag = TTYDEF_OFLAG; 332 tp->t_cflag = TTYDEF_CFLAG | CRTSCTS; 333 tp->t_lflag = TTYDEF_LFLAG; 334 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 335 ttsetwater(tp); 336 } else if ((tp->t_state & TS_XCLUDE) && suser(p)) 337 return (EBUSY); 338 tp->t_state |= TS_CARR_ON; 339 340 return ((*linesw[tp->t_line].l_open)(dev, tp, p)); 341 } 342 343 int 344 vccttyclose(dev_t dev, int flag, int mode, struct proc *p) 345 { 346 struct vcctty_softc *sc; 347 struct tty *tp; 348 int unit = minor(dev); 349 350 if (unit >= vcctty_cd.cd_ndevs) 351 return (ENXIO); 352 sc = vcctty_cd.cd_devs[unit]; 353 if (sc == NULL) 354 return (ENXIO); 355 356 tp = sc->sc_tty; 357 (*linesw[tp->t_line].l_close)(tp, flag, p); 358 ttyclose(tp); 359 return (0); 360 } 361 362 int 363 vccttyread(dev_t dev, struct uio *uio, int flag) 364 { 365 struct vcctty_softc *sc; 366 struct tty *tp; 367 int unit = minor(dev); 368 369 if (unit >= vcctty_cd.cd_ndevs) 370 return (ENXIO); 371 sc = vcctty_cd.cd_devs[unit]; 372 if (sc == NULL) 373 return (ENXIO); 374 375 tp = sc->sc_tty; 376 return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 377 } 378 379 int 380 vccttywrite(dev_t dev, struct uio *uio, int flag) 381 { 382 struct vcctty_softc *sc; 383 struct tty *tp; 384 int unit = minor(dev); 385 386 if (unit >= vcctty_cd.cd_ndevs) 387 return (ENXIO); 388 sc = vcctty_cd.cd_devs[unit]; 389 if (sc == NULL) 390 return (ENXIO); 391 392 tp = sc->sc_tty; 393 return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 394 } 395 396 int 397 vccttyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 398 { 399 struct vcctty_softc *sc; 400 struct tty *tp; 401 int unit = minor(dev); 402 int error; 403 404 if (unit >= vcctty_cd.cd_ndevs) 405 return (ENXIO); 406 sc = vcctty_cd.cd_devs[unit]; 407 if (sc == NULL) 408 return (ENXIO); 409 410 tp = sc->sc_tty; 411 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 412 if (error >= 0) 413 return error; 414 error = ttioctl(tp, cmd, data, flag, p); 415 if (error >= 0) 416 return (error); 417 418 error = 0; 419 420 switch (cmd) { 421 case TIOCSBRK: 422 vcctty_send_break(sc); 423 break; 424 case TIOCCBRK: 425 /* BREAK gets cleared automatically. */ 426 break; 427 default: 428 error = ENOTTY; 429 break; 430 } 431 432 return (error); 433 } 434 435 void 436 vccttystart(struct tty *tp) 437 { 438 struct vcctty_softc *sc = vcctty_cd.cd_devs[minor(tp->t_dev)]; 439 int s; 440 441 s = spltty(); 442 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { 443 splx(s); 444 return; 445 } 446 ttwakeupwr(tp); 447 tp->t_state |= TS_BUSY; 448 if (tp->t_outq.c_cc > 0) 449 vcctty_send_data(sc, tp); 450 tp->t_state &= ~TS_BUSY; 451 if (tp->t_outq.c_cc > 0) { 452 tp->t_state |= TS_TIMEOUT; 453 timeout_add(&tp->t_rstrt_to, 1); 454 } 455 splx(s); 456 } 457 458 int 459 vccttystop(struct tty *tp, int flag) 460 { 461 int s; 462 463 s = spltty(); 464 if (tp->t_state & TS_BUSY) 465 if ((tp->t_state & TS_TTSTOP) == 0) 466 tp->t_state |= TS_FLUSH; 467 splx(s); 468 return (0); 469 } 470 471 struct tty * 472 vccttytty(dev_t dev) 473 { 474 struct vcctty_softc *sc; 475 int unit = minor(dev); 476 477 if (unit >= vcctty_cd.cd_ndevs) 478 return (NULL); 479 sc = vcctty_cd.cd_devs[unit]; 480 if (sc == NULL) 481 return (NULL); 482 483 return sc->sc_tty; 484 } 485 486 int 487 vccttyparam(struct tty *tp, struct termios *t) 488 { 489 tp->t_ispeed = t->c_ispeed; 490 tp->t_ospeed = t->c_ospeed; 491 tp->t_cflag = t->c_cflag; 492 return (0); 493 } 494 495 int 496 vccttyhwiflow(struct tty *tp, int stop) 497 { 498 struct vcctty_softc *sc = vcctty_cd.cd_devs[minor(tp->t_dev)]; 499 uint64_t state = stop ? INTR_DISABLED : INTR_ENABLED; 500 501 cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, state); 502 cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, state); 503 504 return (1); 505 } 506