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