1 /* $OpenBSD: vcctty.c,v 1.11 2014/05/10 11:49:31 kettenis 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 void *sc_tx_ih; 62 void *sc_rx_ih; 63 uint64_t sc_tx_sysino; 64 uint64_t sc_rx_sysino; 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 109 if (cbus_intr_map(ca->ca_node, ca->ca_tx_ino, &sc->sc_tx_sysino) || 110 cbus_intr_map(ca->ca_node, ca->ca_rx_ino, &sc->sc_rx_sysino)) { 111 printf(": can't map interrupt\n"); 112 return; 113 } 114 printf(": ivec 0x%llx, 0x%llx", sc->sc_tx_sysino, sc->sc_rx_sysino); 115 116 /* 117 * Un-configure queues before registering interrupt handlers, 118 * such that we dont get any stale LDC packets or events. 119 */ 120 hv_ldc_tx_qconf(ca->ca_id, 0, 0); 121 hv_ldc_rx_qconf(ca->ca_id, 0, 0); 122 123 sc->sc_tx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_tx_sysino, 124 IPL_TTY, 0, vcctty_tx_intr, sc, sc->sc_dv.dv_xname); 125 sc->sc_rx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_rx_sysino, 126 IPL_TTY, 0, vcctty_rx_intr, sc, sc->sc_dv.dv_xname); 127 if (sc->sc_tx_ih == NULL || sc->sc_rx_ih == NULL) { 128 printf(", can't establish interrupt\n"); 129 return; 130 } 131 132 lc = &sc->sc_lc; 133 lc->lc_id = ca->ca_id; 134 lc->lc_sc = sc; 135 136 lc->lc_txq = ldc_queue_alloc(sc->sc_dmatag, VCCTTY_TX_ENTRIES); 137 if (lc->lc_txq == NULL) { 138 printf(", can't allocate tx queue\n"); 139 return; 140 } 141 142 lc->lc_rxq = ldc_queue_alloc(sc->sc_dmatag, VCCTTY_RX_ENTRIES); 143 if (lc->lc_rxq == NULL) { 144 printf(", can't allocate rx queue\n"); 145 goto free_txqueue; 146 } 147 148 err = hv_ldc_tx_qconf(lc->lc_id, 149 lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries); 150 if (err != H_EOK) 151 printf("%s: hv_ldc_tx_qconf %d\n", __func__, err); 152 153 err = hv_ldc_rx_qconf(lc->lc_id, 154 lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries); 155 if (err != H_EOK) 156 printf("%s: hv_ldc_rx_qconf %d\n", __func__, err); 157 158 cbus_intr_setenabled(sc->sc_tx_sysino, INTR_ENABLED); 159 cbus_intr_setenabled(sc->sc_rx_sysino, INTR_ENABLED); 160 161 printf(" domain \"%s\"\n", ca->ca_name); 162 return; 163 164 free_txqueue: 165 ldc_queue_free(sc->sc_dmatag, lc->lc_txq); 166 } 167 168 int 169 vcctty_tx_intr(void *arg) 170 { 171 struct vcctty_softc *sc = arg; 172 struct ldc_conn *lc = &sc->sc_lc; 173 uint64_t tx_head, tx_tail, tx_state; 174 int err; 175 176 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 177 if (err != H_EOK) { 178 printf("%s: hv_ldc_tx_get_state %d\n", __func__, err); 179 return (0); 180 } 181 182 if (tx_state != lc->lc_tx_state) { 183 switch (tx_state) { 184 case LDC_CHANNEL_DOWN: 185 DPRINTF(("Tx link down\n")); 186 break; 187 case LDC_CHANNEL_UP: 188 DPRINTF(("Tx link up\n")); 189 break; 190 case LDC_CHANNEL_RESET: 191 DPRINTF(("Tx link reset\n")); 192 break; 193 } 194 lc->lc_tx_state = tx_state; 195 } 196 197 return (1); 198 } 199 200 int 201 vcctty_rx_intr(void *arg) 202 { 203 struct vcctty_softc *sc = arg; 204 struct tty *tp = sc->sc_tty; 205 struct ldc_conn *lc = &sc->sc_lc; 206 uint64_t rx_head, rx_tail, rx_state; 207 struct vcctty_msg *msg; 208 int err; 209 int i; 210 211 err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state); 212 if (err != H_EOK) { 213 printf("%s: hv_ldc_rx_get_state %d\n", __func__, err); 214 return (0); 215 } 216 217 if (rx_state != lc->lc_rx_state) { 218 switch (rx_state) { 219 case LDC_CHANNEL_DOWN: 220 DPRINTF(("Rx link down\n")); 221 break; 222 case LDC_CHANNEL_UP: 223 DPRINTF(("Rx link up\n")); 224 break; 225 case LDC_CHANNEL_RESET: 226 DPRINTF(("Rx link reset\n")); 227 break; 228 } 229 lc->lc_rx_state = rx_state; 230 return (1); 231 } 232 233 if (rx_head == rx_tail) 234 return (0); 235 236 msg = (struct vcctty_msg *)(lc->lc_rxq->lq_va + rx_head); 237 if (tp && msg->type == LDC_CONSOLE_DATA) { 238 for (i = 0; i < msg->size; i++) { 239 if (tp->t_state & TS_ISOPEN) 240 (*linesw[tp->t_line].l_rint)(msg->data[i], tp); 241 } 242 } 243 244 rx_head += sizeof(*msg); 245 rx_head &= ((lc->lc_rxq->lq_nentries * sizeof(*msg)) - 1); 246 err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head); 247 if (err != H_EOK) 248 printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err); 249 250 return (1); 251 } 252 253 void 254 vcctty_send_data(struct vcctty_softc *sc, struct tty *tp) 255 { 256 struct ldc_conn *lc = &sc->sc_lc; 257 uint64_t tx_head, tx_tail, tx_state; 258 uint64_t next_tx_tail; 259 struct vcctty_msg *msg; 260 int err; 261 262 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 263 if (err != H_EOK || tx_state != LDC_CHANNEL_UP) 264 return; 265 266 while (tp->t_outq.c_cc > 0) { 267 next_tx_tail = tx_tail + sizeof(*msg); 268 next_tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*msg)) - 1); 269 270 if (next_tx_tail == tx_head) 271 return; 272 273 msg = (struct vcctty_msg *)(lc->lc_txq->lq_va + tx_tail); 274 bzero(msg, sizeof(*msg)); 275 msg->type = LDC_CONSOLE_DATA; 276 msg->size = q_to_b(&tp->t_outq, msg->data, sizeof(msg->data)); 277 278 err = hv_ldc_tx_set_qtail(lc->lc_id, next_tx_tail); 279 if (err != H_EOK) 280 printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err); 281 tx_tail = next_tx_tail; 282 } 283 } 284 285 void 286 vcctty_send_break(struct vcctty_softc *sc) 287 { 288 struct ldc_conn *lc = &sc->sc_lc; 289 uint64_t tx_head, tx_tail, tx_state; 290 struct vcctty_msg *msg; 291 int err; 292 293 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); 294 if (err != H_EOK || tx_state != LDC_CHANNEL_UP) 295 return; 296 297 msg = (struct vcctty_msg *)(lc->lc_txq->lq_va + tx_tail); 298 bzero(msg, sizeof(*msg)); 299 msg->type = LDC_CONSOLE_CTRL; 300 msg->ctrl_msg = CONS_BREAK; 301 302 tx_tail += sizeof(*msg); 303 tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*msg)) - 1); 304 err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail); 305 if (err != H_EOK) 306 printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err); 307 } 308 309 int 310 vccttyopen(dev_t dev, int flag, int mode, struct proc *p) 311 { 312 struct vcctty_softc *sc; 313 struct tty *tp; 314 int unit = minor(dev); 315 316 if (unit >= vcctty_cd.cd_ndevs) 317 return (ENXIO); 318 sc = vcctty_cd.cd_devs[unit]; 319 if (sc == NULL) 320 return (ENXIO); 321 322 if (sc->sc_tty) 323 tp = sc->sc_tty; 324 else 325 tp = sc->sc_tty = ttymalloc(0); 326 327 tp->t_oproc = vccttystart; 328 tp->t_param = vccttyparam; 329 tp->t_hwiflow = vccttyhwiflow; 330 tp->t_dev = dev; 331 if ((tp->t_state & TS_ISOPEN) == 0) { 332 ttychars(tp); 333 tp->t_iflag = TTYDEF_IFLAG; 334 tp->t_oflag = TTYDEF_OFLAG; 335 tp->t_cflag = TTYDEF_CFLAG | CRTSCTS; 336 tp->t_lflag = TTYDEF_LFLAG; 337 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 338 ttsetwater(tp); 339 } else if ((tp->t_state & TS_XCLUDE) && suser(p, 0)) 340 return (EBUSY); 341 tp->t_state |= TS_CARR_ON; 342 343 return ((*linesw[tp->t_line].l_open)(dev, tp, p)); 344 } 345 346 int 347 vccttyclose(dev_t dev, int flag, int mode, struct proc *p) 348 { 349 struct vcctty_softc *sc; 350 struct tty *tp; 351 int unit = minor(dev); 352 353 if (unit >= vcctty_cd.cd_ndevs) 354 return (ENXIO); 355 sc = vcctty_cd.cd_devs[unit]; 356 if (sc == NULL) 357 return (ENXIO); 358 359 tp = sc->sc_tty; 360 (*linesw[tp->t_line].l_close)(tp, flag, p); 361 ttyclose(tp); 362 return (0); 363 } 364 365 int 366 vccttyread(dev_t dev, struct uio *uio, int flag) 367 { 368 struct vcctty_softc *sc; 369 struct tty *tp; 370 int unit = minor(dev); 371 372 if (unit >= vcctty_cd.cd_ndevs) 373 return (ENXIO); 374 sc = vcctty_cd.cd_devs[unit]; 375 if (sc == NULL) 376 return (ENXIO); 377 378 tp = sc->sc_tty; 379 return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 380 } 381 382 int 383 vccttywrite(dev_t dev, struct uio *uio, int flag) 384 { 385 struct vcctty_softc *sc; 386 struct tty *tp; 387 int unit = minor(dev); 388 389 if (unit >= vcctty_cd.cd_ndevs) 390 return (ENXIO); 391 sc = vcctty_cd.cd_devs[unit]; 392 if (sc == NULL) 393 return (ENXIO); 394 395 tp = sc->sc_tty; 396 return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 397 } 398 399 int 400 vccttyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 401 { 402 struct vcctty_softc *sc; 403 struct tty *tp; 404 int unit = minor(dev); 405 int error; 406 407 if (unit >= vcctty_cd.cd_ndevs) 408 return (ENXIO); 409 sc = vcctty_cd.cd_devs[unit]; 410 if (sc == NULL) 411 return (ENXIO); 412 413 tp = sc->sc_tty; 414 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 415 if (error >= 0) 416 return error; 417 error = ttioctl(tp, cmd, data, flag, p); 418 if (error >= 0) 419 return (error); 420 421 error = 0; 422 423 switch (cmd) { 424 case TIOCSBRK: 425 vcctty_send_break(sc); 426 break; 427 case TIOCCBRK: 428 /* BREAK gets cleared automatically. */ 429 break; 430 default: 431 error = ENOTTY; 432 break; 433 } 434 435 return (error); 436 } 437 438 void 439 vccttystart(struct tty *tp) 440 { 441 struct vcctty_softc *sc = vcctty_cd.cd_devs[minor(tp->t_dev)]; 442 int s; 443 444 s = spltty(); 445 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { 446 splx(s); 447 return; 448 } 449 ttwakeupwr(tp); 450 tp->t_state |= TS_BUSY; 451 if (tp->t_outq.c_cc > 0) 452 vcctty_send_data(sc, tp); 453 tp->t_state &= ~TS_BUSY; 454 if (tp->t_outq.c_cc > 0) { 455 tp->t_state |= TS_TIMEOUT; 456 timeout_add(&tp->t_rstrt_to, 1); 457 } 458 splx(s); 459 } 460 461 int 462 vccttystop(struct tty *tp, int flag) 463 { 464 int s; 465 466 s = spltty(); 467 if (tp->t_state & TS_BUSY) 468 if ((tp->t_state & TS_TTSTOP) == 0) 469 tp->t_state |= TS_FLUSH; 470 splx(s); 471 return (0); 472 } 473 474 struct tty * 475 vccttytty(dev_t dev) 476 { 477 struct vcctty_softc *sc; 478 int unit = minor(dev); 479 480 if (unit >= vcctty_cd.cd_ndevs) 481 return (NULL); 482 sc = vcctty_cd.cd_devs[unit]; 483 if (sc == NULL) 484 return (NULL); 485 486 return sc->sc_tty; 487 } 488 489 int 490 vccttyparam(struct tty *tp, struct termios *t) 491 { 492 tp->t_ispeed = t->c_ispeed; 493 tp->t_ospeed = t->c_ospeed; 494 tp->t_cflag = t->c_cflag; 495 return (0); 496 } 497 498 int 499 vccttyhwiflow(struct tty *tp, int stop) 500 { 501 struct vcctty_softc *sc = vcctty_cd.cd_devs[minor(tp->t_dev)]; 502 503 if (stop) { 504 cbus_intr_setenabled(sc->sc_tx_sysino, INTR_DISABLED); 505 cbus_intr_setenabled(sc->sc_rx_sysino, INTR_DISABLED); 506 } else { 507 cbus_intr_setenabled(sc->sc_tx_sysino, INTR_ENABLED); 508 cbus_intr_setenabled(sc->sc_rx_sysino, INTR_ENABLED); 509 } 510 511 return (1); 512 } 513