1 /* 2 * Copyright (C) 2003 3 * Hidetoshi Shimokawa. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * 16 * This product includes software developed by Hidetoshi Shimokawa. 17 * 18 * 4. Neither the name of the author nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $Id: dcons.c,v 1.65 2003/10/24 03:24:55 simokawa Exp $ 35 * $FreeBSD$ 36 */ 37 38 #include <sys/param.h> 39 #include <sys/kernel.h> 40 #include <sys/systm.h> 41 #include <sys/types.h> 42 #include <sys/conf.h> 43 #include <sys/cons.h> 44 #include <sys/consio.h> 45 #include <sys/tty.h> 46 #include <sys/malloc.h> 47 #include <sys/proc.h> 48 #include <sys/ucred.h> 49 50 #include <machine/bus.h> 51 52 #include <dev/dcons/dcons.h> 53 54 #include <ddb/ddb.h> 55 #include <sys/reboot.h> 56 57 #include <sys/sysctl.h> 58 59 #include "opt_ddb.h" 60 #include "opt_comconsole.h" 61 #include "opt_dcons.h" 62 63 #ifndef DCONS_POLL_HZ 64 #define DCONS_POLL_HZ 100 65 #endif 66 67 #ifndef DCONS_BUF_SIZE 68 #define DCONS_BUF_SIZE (16*1024) 69 #endif 70 71 #ifndef DCONS_FORCE_CONSOLE 72 #define DCONS_FORCE_CONSOLE 0 /* mostly for FreeBSD-4 */ 73 #endif 74 75 #ifndef DCONS_FORCE_GDB 76 #define DCONS_FORCE_GDB 1 77 #endif 78 79 #if __FreeBSD_version >= 500101 80 #define CONS_NODEV 1 /* for latest current */ 81 static struct consdev gdbconsdev; 82 #endif 83 84 85 static d_open_t dcons_open; 86 static d_close_t dcons_close; 87 static d_ioctl_t dcons_ioctl; 88 89 static struct cdevsw dcons_cdevsw = { 90 #if __FreeBSD_version >= 500104 91 .d_version = D_VERSION, 92 .d_open = dcons_open, 93 .d_close = dcons_close, 94 .d_ioctl = dcons_ioctl, 95 .d_name = "dcons", 96 .d_flags = D_TTY | D_NEEDGIANT, 97 #else 98 /* open */ dcons_open, 99 /* close */ dcons_close, 100 /* read */ ttyread, 101 /* write */ ttywrite, 102 /* ioctl */ dcons_ioctl, 103 /* poll */ ttypoll, 104 /* mmap */ nommap, 105 /* strategy */ nostrategy, 106 /* name */ "dcons", 107 /* major */ CDEV_MAJOR, 108 /* dump */ nodump, 109 /* psize */ nopsize, 110 /* flags */ 0, 111 #endif 112 }; 113 114 #ifndef KLD_MODULE 115 static char bssbuf[DCONS_BUF_SIZE]; /* buf in bss */ 116 #endif 117 118 /* global data */ 119 static struct dcons_global dg; 120 struct dcons_global *dcons_conf; 121 static int poll_hz = DCONS_POLL_HZ; 122 123 SYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console"); 124 SYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0, 125 "dcons polling rate"); 126 127 static int drv_init = 0; 128 static struct callout dcons_callout; 129 struct dcons_buf *dcons_buf; /* for local dconschat */ 130 131 /* per device data */ 132 static struct dcons_softc { 133 dev_t dev; 134 struct dcons_ch o, i; 135 int brk_state; 136 #define DC_GDB 1 137 int flags; 138 } sc[DCONS_NPORT]; 139 static void dcons_tty_start(struct tty *); 140 static int dcons_tty_param(struct tty *, struct termios *); 141 static void dcons_timeout(void *); 142 static int dcons_drv_init(int); 143 static int dcons_getc(struct dcons_softc *); 144 static int dcons_checkc(struct dcons_softc *); 145 static void dcons_putc(struct dcons_softc *, int); 146 147 static cn_probe_t dcons_cnprobe; 148 static cn_init_t dcons_cninit; 149 static cn_getc_t dcons_cngetc; 150 static cn_checkc_t dcons_cncheckc; 151 static cn_putc_t dcons_cnputc; 152 153 CONS_DRIVER(dcons, dcons_cnprobe, dcons_cninit, NULL, dcons_cngetc, 154 dcons_cncheckc, dcons_cnputc, NULL); 155 156 #if __FreeBSD_version < 500000 157 #define THREAD proc 158 #else 159 #define THREAD thread 160 #endif 161 162 static int 163 dcons_open(dev_t dev, int flag, int mode, struct THREAD *td) 164 { 165 struct tty *tp; 166 int unit, error, s; 167 168 unit = minor(dev); 169 if (unit != 0) 170 return (ENXIO); 171 172 tp = dev->si_tty = ttymalloc(dev->si_tty); 173 tp->t_oproc = dcons_tty_start; 174 tp->t_param = dcons_tty_param; 175 tp->t_stop = nottystop; 176 tp->t_dev = dev; 177 178 error = 0; 179 180 s = spltty(); 181 if ((tp->t_state & TS_ISOPEN) == 0) { 182 tp->t_state |= TS_CARR_ON; 183 ttychars(tp); 184 tp->t_iflag = TTYDEF_IFLAG; 185 tp->t_oflag = TTYDEF_OFLAG; 186 tp->t_cflag = TTYDEF_CFLAG|CLOCAL; 187 tp->t_lflag = TTYDEF_LFLAG; 188 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 189 ttsetwater(tp); 190 } else if ((tp->t_state & TS_XCLUDE) && suser(td)) { 191 splx(s); 192 return (EBUSY); 193 } 194 splx(s); 195 196 error = (*linesw[tp->t_line].l_open)(dev, tp); 197 198 return (error); 199 } 200 201 static int 202 dcons_close(dev_t dev, int flag, int mode, struct THREAD *td) 203 { 204 int unit; 205 struct tty *tp; 206 207 unit = minor(dev); 208 if (unit != 0) 209 return (ENXIO); 210 211 tp = dev->si_tty; 212 if (tp->t_state & TS_ISOPEN) { 213 (*linesw[tp->t_line].l_close)(tp, flag); 214 ttyclose(tp); 215 } 216 217 return (0); 218 } 219 220 static int 221 dcons_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct THREAD *td) 222 { 223 int unit; 224 struct tty *tp; 225 int error; 226 227 unit = minor(dev); 228 if (unit != 0) 229 return (ENXIO); 230 231 tp = dev->si_tty; 232 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td); 233 if (error != ENOIOCTL) 234 return (error); 235 236 error = ttioctl(tp, cmd, data, flag); 237 if (error != ENOIOCTL) 238 return (error); 239 240 return (ENOTTY); 241 } 242 243 static int 244 dcons_tty_param(struct tty *tp, struct termios *t) 245 { 246 tp->t_ispeed = t->c_ispeed; 247 tp->t_ospeed = t->c_ospeed; 248 tp->t_cflag = t->c_cflag; 249 return 0; 250 } 251 252 static void 253 dcons_tty_start(struct tty *tp) 254 { 255 struct dcons_softc *dc; 256 int s; 257 258 dc = (struct dcons_softc *)tp->t_dev->si_drv1; 259 s = spltty(); 260 if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 261 ttwwakeup(tp); 262 return; 263 } 264 265 tp->t_state |= TS_BUSY; 266 while (tp->t_outq.c_cc != 0) 267 dcons_putc(dc, getc(&tp->t_outq)); 268 tp->t_state &= ~TS_BUSY; 269 270 ttwwakeup(tp); 271 splx(s); 272 } 273 274 static void 275 dcons_timeout(void *v) 276 { 277 struct tty *tp; 278 struct dcons_softc *dc; 279 int i, c, polltime; 280 281 for (i = 0; i < DCONS_NPORT; i ++) { 282 dc = &sc[i]; 283 tp = dc->dev->si_tty; 284 while ((c = dcons_checkc(dc)) != -1) 285 if (tp->t_state & TS_ISOPEN) 286 (*linesw[tp->t_line].l_rint)(c, tp); 287 } 288 polltime = hz / poll_hz; 289 if (polltime < 1) 290 polltime = 1; 291 callout_reset(&dcons_callout, polltime, dcons_timeout, tp); 292 } 293 294 static void 295 dcons_cnprobe(struct consdev *cp) 296 { 297 #if __FreeBSD_version >= 501109 298 sprintf(cp->cn_name, "dcons"); 299 #else 300 cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON); 301 #endif 302 #if DCONS_FORCE_CONSOLE 303 cp->cn_pri = CN_REMOTE; 304 #else 305 cp->cn_pri = CN_NORMAL; 306 #endif 307 } 308 309 static void 310 dcons_cninit(struct consdev *cp) 311 { 312 dcons_drv_init(0); 313 #if CONS_NODEV 314 cp->cn_arg 315 #else 316 cp->cn_dev->si_drv1 317 #endif 318 = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */ 319 } 320 321 #if CONS_NODEV 322 static int 323 dcons_cngetc(struct consdev *cp) 324 { 325 return(dcons_getc((struct dcons_softc *)cp->cn_arg)); 326 } 327 static int 328 dcons_cncheckc(struct consdev *cp) 329 { 330 return(dcons_checkc((struct dcons_softc *)cp->cn_arg)); 331 } 332 static void 333 dcons_cnputc(struct consdev *cp, int c) 334 { 335 dcons_putc((struct dcons_softc *)cp->cn_arg, c); 336 } 337 #else 338 static int 339 dcons_cngetc(dev_t dev) 340 { 341 return(dcons_getc((struct dcons_softc *)dev->si_drv1)); 342 } 343 static int 344 dcons_cncheckc(dev_t dev) 345 { 346 return(dcons_checkc((struct dcons_softc *)dev->si_drv1)); 347 } 348 static void 349 dcons_cnputc(dev_t dev, int c) 350 { 351 dcons_putc((struct dcons_softc *)dev->si_drv1, c); 352 } 353 #endif 354 355 static int 356 dcons_getc(struct dcons_softc *dc) 357 { 358 int c; 359 360 while ((c = dcons_checkc(dc)) == -1); 361 362 return (c & 0xff); 363 } 364 365 static int 366 dcons_checkc(struct dcons_softc *dc) 367 { 368 unsigned char c; 369 u_int32_t ptr, pos, gen, next_gen; 370 struct dcons_ch *ch; 371 372 ch = &dc->i; 373 374 if (dg.dma_tag != NULL) 375 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD); 376 ptr = ntohl(*ch->ptr); 377 gen = ptr >> DCONS_GEN_SHIFT; 378 pos = ptr & DCONS_POS_MASK; 379 if (gen == ch->gen && pos == ch->pos) 380 return (-1); 381 382 next_gen = DCONS_NEXT_GEN(ch->gen); 383 /* XXX sanity check */ 384 if ((gen != ch->gen && gen != next_gen) 385 || (gen == ch->gen && pos < ch->pos)) { 386 /* generation skipped !! */ 387 /* XXX discard */ 388 ch->gen = gen; 389 ch->pos = pos; 390 return (-1); 391 } 392 393 c = ch->buf[ch->pos]; 394 ch->pos ++; 395 if (ch->pos >= ch->size) { 396 ch->gen = next_gen; 397 ch->pos = 0; 398 } 399 400 #if DDB && ALT_BREAK_TO_DEBUGGER 401 switch (dc->brk_state) { 402 case STATE1: 403 if (c == KEY_TILDE) 404 dc->brk_state = STATE2; 405 else 406 dc->brk_state = STATE0; 407 break; 408 case STATE2: 409 dc->brk_state = STATE0; 410 if (c == KEY_CTRLB) { 411 #if DCONS_FORCE_GDB 412 if (dc->flags & DC_GDB) 413 boothowto |= RB_GDB; 414 #endif 415 breakpoint(); 416 } 417 } 418 if (c == KEY_CR) 419 dc->brk_state = STATE1; 420 #endif 421 return (c); 422 } 423 424 static void 425 dcons_putc(struct dcons_softc *dc, int c) 426 { 427 struct dcons_ch *ch; 428 429 ch = &dc->o; 430 431 ch->buf[ch->pos] = c; 432 ch->pos ++; 433 if (ch->pos >= ch->size) { 434 ch->gen = DCONS_NEXT_GEN(ch->gen); 435 ch->pos = 0; 436 } 437 *ch->ptr = DCONS_MAKE_PTR(ch); 438 if (dg.dma_tag != NULL) 439 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE); 440 } 441 442 static int 443 dcons_init_port(int port, int offset, int size) 444 { 445 int osize; 446 struct dcons_softc *dc; 447 448 dc = &sc[port]; 449 450 osize = size * 3 / 4; 451 452 dc->o.size = osize; 453 dc->i.size = size - osize; 454 dc->o.buf = (char *)dg.buf + offset; 455 dc->i.buf = dc->o.buf + osize; 456 dc->o.gen = dc->i.gen = 0; 457 dc->o.pos = dc->i.pos = 0; 458 dc->o.ptr = &dg.buf->optr[port]; 459 dc->i.ptr = &dg.buf->iptr[port]; 460 dc->brk_state = STATE0; 461 dg.buf->osize[port] = htonl(osize); 462 dg.buf->isize[port] = htonl(size - osize); 463 dg.buf->ooffset[port] = htonl(offset); 464 dg.buf->ioffset[port] = htonl(offset + osize); 465 dg.buf->optr[port] = DCONS_MAKE_PTR(&dc->o); 466 dg.buf->iptr[port] = DCONS_MAKE_PTR(&dc->i); 467 468 return(0); 469 } 470 471 static int 472 dcons_drv_init(int stage) 473 { 474 int size, size0, offset; 475 476 if (drv_init) 477 return(drv_init); 478 479 drv_init = -1; 480 481 bzero(&dg, sizeof(dg)); 482 dcons_conf = &dg; 483 dg.cdev = &dcons_consdev; 484 dg.size = DCONS_BUF_SIZE; 485 486 #ifndef KLD_MODULE 487 if (stage == 0) /* XXX or cold */ 488 /* 489 * DCONS_FORCE_CONSOLE == 1 and statically linked. 490 * called from cninit(). can't use contigmalloc yet . 491 */ 492 dg.buf = (struct dcons_buf *) bssbuf; 493 else 494 #endif 495 /* 496 * DCONS_FORCE_CONSOLE == 0 or kernel module case. 497 * if the module is loaded after boot, 498 * bssbuf could be non-continuous. 499 */ 500 dg.buf = (struct dcons_buf *) contigmalloc(dg.size, 501 M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul); 502 503 dcons_buf = dg.buf; 504 offset = DCONS_HEADER_SIZE; 505 size = (dg.size - offset); 506 size0 = size * 3 / 4; 507 508 dcons_init_port(0, offset, size0); 509 offset += size0; 510 dcons_init_port(1, offset, size - size0); 511 dg.buf->version = htonl(DCONS_VERSION); 512 dg.buf->magic = ntohl(DCONS_MAGIC); 513 514 #if DDB && DCONS_FORCE_GDB 515 #if CONS_NODEV 516 gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB]; 517 #if __FreeBSD_version >= 501109 518 sprintf(gdbconsdev.cn_name, "dgdb"); 519 #endif 520 gdb_arg = &gdbconsdev; 521 #else 522 gdbdev = makedev(CDEV_MAJOR, DCONS_GDB); 523 #endif 524 gdb_getc = dcons_cngetc; 525 gdb_putc = dcons_cnputc; 526 #endif 527 drv_init = 1; 528 529 return 0; 530 } 531 532 533 static int 534 dcons_attach_port(int port, char *name, int flags) 535 { 536 struct dcons_softc *dc; 537 struct tty *tp; 538 539 dc = &sc[port]; 540 dc->flags = flags; 541 dc->dev = make_dev(&dcons_cdevsw, port, 542 UID_ROOT, GID_WHEEL, 0600, name); 543 tp = ttymalloc(NULL); 544 545 dc->dev->si_drv1 = (void *)dc; 546 dc->dev->si_tty = tp; 547 548 tp->t_oproc = dcons_tty_start; 549 tp->t_param = dcons_tty_param; 550 tp->t_stop = nottystop; 551 tp->t_dev = dc->dev; 552 553 return(0); 554 } 555 556 static int 557 dcons_attach(void) 558 { 559 int polltime; 560 561 dcons_attach_port(DCONS_CON, "dcons", 0); 562 dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB); 563 #if __FreeBSD_version < 500000 564 callout_init(&dcons_callout); 565 #else 566 callout_init(&dcons_callout, 0); 567 #endif 568 polltime = hz / poll_hz; 569 if (polltime < 1) 570 polltime = 1; 571 callout_reset(&dcons_callout, polltime, dcons_timeout, NULL); 572 return(0); 573 } 574 575 static int 576 dcons_detach(int port) 577 { 578 struct tty *tp; 579 struct dcons_softc *dc; 580 581 dc = &sc[port]; 582 583 tp = dc->dev->si_tty; 584 585 if (tp->t_state & TS_ISOPEN) { 586 printf("dcons: still opened\n"); 587 (*linesw[tp->t_line].l_close)(tp, 0); 588 tp->t_gen++; 589 ttyclose(tp); 590 ttwakeup(tp); 591 ttwwakeup(tp); 592 } 593 /* XXX 594 * must wait until all device are closed. 595 */ 596 tsleep((void *)dc, PWAIT, "dcodtc", hz/4); 597 destroy_dev(dc->dev); 598 599 return(0); 600 } 601 602 603 /* cnXXX works only for FreeBSD-5 */ 604 static int 605 dcons_modevent(module_t mode, int type, void *data) 606 { 607 int err = 0, ret; 608 609 switch (type) { 610 case MOD_LOAD: 611 ret = dcons_drv_init(1); 612 dcons_attach(); 613 #if __FreeBSD_version >= 500000 614 if (ret == 0) { 615 dcons_cnprobe(&dcons_consdev); 616 dcons_cninit(&dcons_consdev); 617 cnadd(&dcons_consdev); 618 } 619 #endif 620 break; 621 case MOD_UNLOAD: 622 printf("dcons: unload\n"); 623 callout_stop(&dcons_callout); 624 #if DDB && DCONS_FORCE_GDB 625 #if CONS_NODEV 626 gdb_arg = NULL; 627 #else 628 gdbdev = NODEV; 629 #endif 630 #endif 631 #if __FreeBSD_version >= 500000 632 cnremove(&dcons_consdev); 633 #endif 634 dcons_detach(DCONS_CON); 635 dcons_detach(DCONS_GDB); 636 dg.buf->magic = 0; 637 638 contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF); 639 640 break; 641 case MOD_SHUTDOWN: 642 break; 643 } 644 return(err); 645 } 646 647 DEV_MODULE(dcons, dcons_modevent, NULL); 648 MODULE_VERSION(dcons, DCONS_VERSION); 649