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 #if __FreeBSD_version >= 502122 40 #include <sys/kdb.h> 41 #include <gdb/gdb.h> 42 #endif 43 #include <sys/kernel.h> 44 #include <sys/module.h> 45 #include <sys/systm.h> 46 #include <sys/types.h> 47 #include <sys/conf.h> 48 #include <sys/cons.h> 49 #include <sys/consio.h> 50 #include <sys/tty.h> 51 #include <sys/malloc.h> 52 #include <sys/proc.h> 53 #include <sys/ucred.h> 54 55 #include <machine/bus.h> 56 57 #include <dev/dcons/dcons.h> 58 59 #include <ddb/ddb.h> 60 #include <sys/reboot.h> 61 62 #include <sys/sysctl.h> 63 64 #include "opt_ddb.h" 65 #include "opt_comconsole.h" 66 #include "opt_dcons.h" 67 68 #ifndef DCONS_POLL_HZ 69 #define DCONS_POLL_HZ 100 70 #endif 71 72 #ifndef DCONS_BUF_SIZE 73 #define DCONS_BUF_SIZE (16*1024) 74 #endif 75 76 #ifndef DCONS_FORCE_CONSOLE 77 #define DCONS_FORCE_CONSOLE 0 /* mostly for FreeBSD-4 */ 78 #endif 79 80 #ifndef DCONS_FORCE_GDB 81 #define DCONS_FORCE_GDB 1 82 #endif 83 84 #if __FreeBSD_version >= 500101 85 #define CONS_NODEV 1 86 #if __FreeBSD_version < 502122 87 static struct consdev gdbconsdev; 88 #endif 89 #endif 90 91 92 static d_open_t dcons_open; 93 static d_close_t dcons_close; 94 95 static struct cdevsw dcons_cdevsw = { 96 #if __FreeBSD_version >= 500104 97 .d_version = D_VERSION, 98 .d_open = dcons_open, 99 .d_close = dcons_close, 100 .d_name = "dcons", 101 .d_flags = D_TTY | D_NEEDGIANT, 102 #else 103 /* open */ dcons_open, 104 /* close */ dcons_close, 105 /* read */ ttyread, 106 /* write */ ttywrite, 107 /* ioctl */ dcons_ioctl, 108 /* poll */ ttypoll, 109 /* mmap */ nommap, 110 /* strategy */ nostrategy, 111 /* name */ "dcons", 112 /* major */ CDEV_MAJOR, 113 /* dump */ nodump, 114 /* psize */ nopsize, 115 /* flags */ 0, 116 #endif 117 }; 118 119 #ifndef KLD_MODULE 120 static char bssbuf[DCONS_BUF_SIZE]; /* buf in bss */ 121 #endif 122 123 /* global data */ 124 static struct dcons_global dg; 125 struct dcons_global *dcons_conf; 126 static int poll_hz = DCONS_POLL_HZ; 127 128 SYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console"); 129 SYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0, 130 "dcons polling rate"); 131 132 static int drv_init = 0; 133 static struct callout dcons_callout; 134 struct dcons_buf *dcons_buf; /* for local dconschat */ 135 136 /* per device data */ 137 static struct dcons_softc { 138 struct cdev *dev; 139 struct dcons_ch o, i; 140 int brk_state; 141 #define DC_GDB 1 142 int flags; 143 } sc[DCONS_NPORT]; 144 static void dcons_tty_start(struct tty *); 145 static int dcons_tty_param(struct tty *, struct termios *); 146 static void dcons_timeout(void *); 147 static int dcons_drv_init(int); 148 static int dcons_getc(struct dcons_softc *); 149 static int dcons_checkc(struct dcons_softc *); 150 static void dcons_putc(struct dcons_softc *, int); 151 152 static cn_probe_t dcons_cnprobe; 153 static cn_init_t dcons_cninit; 154 static cn_getc_t dcons_cngetc; 155 static cn_checkc_t dcons_cncheckc; 156 static cn_putc_t dcons_cnputc; 157 158 CONS_DRIVER(dcons, dcons_cnprobe, dcons_cninit, NULL, dcons_cngetc, 159 dcons_cncheckc, dcons_cnputc, NULL); 160 161 #if __FreeBSD_version >= 502122 162 static gdb_probe_f dcons_dbg_probe; 163 static gdb_init_f dcons_dbg_init; 164 static gdb_term_f dcons_dbg_term; 165 static gdb_getc_f dcons_dbg_getc; 166 static gdb_checkc_f dcons_dbg_checkc; 167 static gdb_putc_f dcons_dbg_putc; 168 169 GDB_DBGPORT(dcons, dcons_dbg_probe, dcons_dbg_init, dcons_dbg_term, 170 dcons_dbg_checkc, dcons_dbg_getc, dcons_dbg_putc); 171 172 extern struct gdb_dbgport *gdb_cur; 173 #endif 174 175 #if __FreeBSD_version < 500000 176 #define THREAD proc 177 #else 178 #define THREAD thread 179 #endif 180 181 static int 182 dcons_open(struct cdev *dev, int flag, int mode, struct THREAD *td) 183 { 184 struct tty *tp; 185 int unit, error, s; 186 187 unit = minor(dev); 188 if (unit != 0) 189 return (ENXIO); 190 191 tp = dev->si_tty = ttymalloc(dev->si_tty); 192 tp->t_oproc = dcons_tty_start; 193 tp->t_param = dcons_tty_param; 194 tp->t_stop = nottystop; 195 tp->t_dev = dev; 196 197 error = 0; 198 199 s = spltty(); 200 if ((tp->t_state & TS_ISOPEN) == 0) { 201 tp->t_state |= TS_CARR_ON; 202 ttychars(tp); 203 tp->t_iflag = TTYDEF_IFLAG; 204 tp->t_oflag = TTYDEF_OFLAG; 205 tp->t_cflag = TTYDEF_CFLAG|CLOCAL; 206 tp->t_lflag = TTYDEF_LFLAG; 207 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 208 ttsetwater(tp); 209 } else if ((tp->t_state & TS_XCLUDE) && suser(td)) { 210 splx(s); 211 return (EBUSY); 212 } 213 splx(s); 214 215 error = ttyld_open(tp, dev); 216 217 return (error); 218 } 219 220 static int 221 dcons_close(struct cdev *dev, int flag, int mode, struct THREAD *td) 222 { 223 int unit; 224 struct tty *tp; 225 226 unit = minor(dev); 227 if (unit != 0) 228 return (ENXIO); 229 230 tp = dev->si_tty; 231 if (tp->t_state & TS_ISOPEN) { 232 ttyld_close(tp, flag); 233 tty_close(tp); 234 } 235 236 return (0); 237 } 238 239 static int 240 dcons_tty_param(struct tty *tp, struct termios *t) 241 { 242 tp->t_ispeed = t->c_ispeed; 243 tp->t_ospeed = t->c_ospeed; 244 tp->t_cflag = t->c_cflag; 245 return 0; 246 } 247 248 static void 249 dcons_tty_start(struct tty *tp) 250 { 251 struct dcons_softc *dc; 252 int s; 253 254 dc = (struct dcons_softc *)tp->t_dev->si_drv1; 255 s = spltty(); 256 if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { 257 ttwwakeup(tp); 258 return; 259 } 260 261 tp->t_state |= TS_BUSY; 262 while (tp->t_outq.c_cc != 0) 263 dcons_putc(dc, getc(&tp->t_outq)); 264 tp->t_state &= ~TS_BUSY; 265 266 ttwwakeup(tp); 267 splx(s); 268 } 269 270 static void 271 dcons_timeout(void *v) 272 { 273 struct tty *tp; 274 struct dcons_softc *dc; 275 int i, c, polltime; 276 277 for (i = 0; i < DCONS_NPORT; i ++) { 278 dc = &sc[i]; 279 tp = dc->dev->si_tty; 280 while ((c = dcons_checkc(dc)) != -1) 281 if (tp->t_state & TS_ISOPEN) 282 ttyld_rint(tp, c); 283 } 284 polltime = hz / poll_hz; 285 if (polltime < 1) 286 polltime = 1; 287 callout_reset(&dcons_callout, polltime, dcons_timeout, tp); 288 } 289 290 static void 291 dcons_cnprobe(struct consdev *cp) 292 { 293 #if __FreeBSD_version >= 501109 294 sprintf(cp->cn_name, "dcons"); 295 #else 296 cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON); 297 #endif 298 #if DCONS_FORCE_CONSOLE 299 cp->cn_pri = CN_REMOTE; 300 #else 301 cp->cn_pri = CN_NORMAL; 302 #endif 303 } 304 305 static void 306 dcons_cninit(struct consdev *cp) 307 { 308 dcons_drv_init(0); 309 #if CONS_NODEV 310 cp->cn_arg 311 #else 312 cp->cn_dev->si_drv1 313 #endif 314 = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */ 315 } 316 317 #if CONS_NODEV 318 static int 319 dcons_cngetc(struct consdev *cp) 320 { 321 return(dcons_getc((struct dcons_softc *)cp->cn_arg)); 322 } 323 static int 324 dcons_cncheckc(struct consdev *cp) 325 { 326 return(dcons_checkc((struct dcons_softc *)cp->cn_arg)); 327 } 328 static void 329 dcons_cnputc(struct consdev *cp, int c) 330 { 331 dcons_putc((struct dcons_softc *)cp->cn_arg, c); 332 } 333 #else 334 static int 335 dcons_cngetc(struct cdev *dev) 336 { 337 return(dcons_getc((struct dcons_softc *)dev->si_drv1)); 338 } 339 static int 340 dcons_cncheckc(struct cdev *dev) 341 { 342 return(dcons_checkc((struct dcons_softc *)dev->si_drv1)); 343 } 344 static void 345 dcons_cnputc(struct cdev *dev, int c) 346 { 347 dcons_putc((struct dcons_softc *)dev->si_drv1, c); 348 } 349 #endif 350 351 static int 352 dcons_getc(struct dcons_softc *dc) 353 { 354 int c; 355 356 while ((c = dcons_checkc(dc)) == -1); 357 358 return (c & 0xff); 359 } 360 361 static int 362 dcons_checkc(struct dcons_softc *dc) 363 { 364 unsigned char c; 365 u_int32_t ptr, pos, gen, next_gen; 366 struct dcons_ch *ch; 367 368 ch = &dc->i; 369 370 if (dg.dma_tag != NULL) 371 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD); 372 ptr = ntohl(*ch->ptr); 373 gen = ptr >> DCONS_GEN_SHIFT; 374 pos = ptr & DCONS_POS_MASK; 375 if (gen == ch->gen && pos == ch->pos) 376 return (-1); 377 378 next_gen = DCONS_NEXT_GEN(ch->gen); 379 /* XXX sanity check */ 380 if ((gen != ch->gen && gen != next_gen) 381 || (gen == ch->gen && pos < ch->pos)) { 382 /* generation skipped !! */ 383 /* XXX discard */ 384 ch->gen = gen; 385 ch->pos = pos; 386 return (-1); 387 } 388 389 c = ch->buf[ch->pos]; 390 ch->pos ++; 391 if (ch->pos >= ch->size) { 392 ch->gen = next_gen; 393 ch->pos = 0; 394 } 395 396 #if __FreeBSD_version >= 502122 397 #if KDB && ALT_BREAK_TO_DEBUGGER 398 if (kdb_alt_break(c, &dc->brk_state)) { 399 if ((dc->flags & DC_GDB) != 0) { 400 if (gdb_cur == &dcons_gdb_dbgport) { 401 kdb_dbbe_select("gdb"); 402 breakpoint(); 403 } 404 } else 405 breakpoint(); 406 } 407 #endif 408 #else 409 #if DDB && ALT_BREAK_TO_DEBUGGER 410 switch (dc->brk_state) { 411 case STATE1: 412 if (c == KEY_TILDE) 413 dc->brk_state = STATE2; 414 else 415 dc->brk_state = STATE0; 416 break; 417 case STATE2: 418 dc->brk_state = STATE0; 419 if (c == KEY_CTRLB) { 420 #if DCONS_FORCE_GDB 421 if (dc->flags & DC_GDB) 422 boothowto |= RB_GDB; 423 #endif 424 breakpoint(); 425 } 426 } 427 if (c == KEY_CR) 428 dc->brk_state = STATE1; 429 #endif 430 #endif 431 return (c); 432 } 433 434 static void 435 dcons_putc(struct dcons_softc *dc, int c) 436 { 437 struct dcons_ch *ch; 438 439 ch = &dc->o; 440 441 ch->buf[ch->pos] = c; 442 ch->pos ++; 443 if (ch->pos >= ch->size) { 444 ch->gen = DCONS_NEXT_GEN(ch->gen); 445 ch->pos = 0; 446 } 447 *ch->ptr = DCONS_MAKE_PTR(ch); 448 if (dg.dma_tag != NULL) 449 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE); 450 } 451 452 static int 453 dcons_init_port(int port, int offset, int size) 454 { 455 int osize; 456 struct dcons_softc *dc; 457 458 dc = &sc[port]; 459 460 osize = size * 3 / 4; 461 462 dc->o.size = osize; 463 dc->i.size = size - osize; 464 dc->o.buf = (char *)dg.buf + offset; 465 dc->i.buf = dc->o.buf + osize; 466 dc->o.gen = dc->i.gen = 0; 467 dc->o.pos = dc->i.pos = 0; 468 dc->o.ptr = &dg.buf->optr[port]; 469 dc->i.ptr = &dg.buf->iptr[port]; 470 dc->brk_state = STATE0; 471 dg.buf->osize[port] = htonl(osize); 472 dg.buf->isize[port] = htonl(size - osize); 473 dg.buf->ooffset[port] = htonl(offset); 474 dg.buf->ioffset[port] = htonl(offset + osize); 475 dg.buf->optr[port] = DCONS_MAKE_PTR(&dc->o); 476 dg.buf->iptr[port] = DCONS_MAKE_PTR(&dc->i); 477 478 return(0); 479 } 480 481 static int 482 dcons_drv_init(int stage) 483 { 484 int size, size0, offset; 485 486 if (drv_init) 487 return(drv_init); 488 489 drv_init = -1; 490 491 bzero(&dg, sizeof(dg)); 492 dcons_conf = &dg; 493 dg.cdev = &dcons_consdev; 494 dg.size = DCONS_BUF_SIZE; 495 496 #ifndef KLD_MODULE 497 if (stage == 0) /* XXX or cold */ 498 /* 499 * DCONS_FORCE_CONSOLE == 1 and statically linked. 500 * called from cninit(). can't use contigmalloc yet . 501 */ 502 dg.buf = (struct dcons_buf *) bssbuf; 503 else 504 #endif 505 /* 506 * DCONS_FORCE_CONSOLE == 0 or kernel module case. 507 * if the module is loaded after boot, 508 * bssbuf could be non-continuous. 509 */ 510 dg.buf = (struct dcons_buf *) contigmalloc(dg.size, 511 M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul); 512 513 dcons_buf = dg.buf; 514 offset = DCONS_HEADER_SIZE; 515 size = (dg.size - offset); 516 size0 = size * 3 / 4; 517 518 dcons_init_port(0, offset, size0); 519 offset += size0; 520 dcons_init_port(1, offset, size - size0); 521 dg.buf->version = htonl(DCONS_VERSION); 522 dg.buf->magic = ntohl(DCONS_MAGIC); 523 524 #if __FreeBSD_version < 502122 525 #if DDB && DCONS_FORCE_GDB 526 #if CONS_NODEV 527 gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB]; 528 #if __FreeBSD_version >= 501109 529 sprintf(gdbconsdev.cn_name, "dgdb"); 530 #endif 531 gdb_arg = &gdbconsdev; 532 #else 533 gdbdev = makedev(CDEV_MAJOR, DCONS_GDB); 534 #endif 535 gdb_getc = dcons_cngetc; 536 gdb_putc = dcons_cnputc; 537 #endif 538 #endif 539 drv_init = 1; 540 541 return 0; 542 } 543 544 545 static int 546 dcons_attach_port(int port, char *name, int flags) 547 { 548 struct dcons_softc *dc; 549 struct tty *tp; 550 551 dc = &sc[port]; 552 dc->flags = flags; 553 dc->dev = make_dev(&dcons_cdevsw, port, 554 UID_ROOT, GID_WHEEL, 0600, name); 555 tp = ttymalloc(NULL); 556 557 dc->dev->si_drv1 = (void *)dc; 558 dc->dev->si_tty = tp; 559 560 tp->t_oproc = dcons_tty_start; 561 tp->t_param = dcons_tty_param; 562 tp->t_stop = nottystop; 563 tp->t_dev = dc->dev; 564 565 return(0); 566 } 567 568 static int 569 dcons_attach(void) 570 { 571 int polltime; 572 573 dcons_attach_port(DCONS_CON, "dcons", 0); 574 dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB); 575 #if __FreeBSD_version < 500000 576 callout_init(&dcons_callout); 577 #else 578 callout_init(&dcons_callout, 0); 579 #endif 580 polltime = hz / poll_hz; 581 if (polltime < 1) 582 polltime = 1; 583 callout_reset(&dcons_callout, polltime, dcons_timeout, NULL); 584 return(0); 585 } 586 587 static int 588 dcons_detach(int port) 589 { 590 struct tty *tp; 591 struct dcons_softc *dc; 592 593 dc = &sc[port]; 594 595 tp = dc->dev->si_tty; 596 597 if (tp->t_state & TS_ISOPEN) { 598 printf("dcons: still opened\n"); 599 ttyld_close(tp, 0); 600 tty_close(tp); 601 } 602 /* XXX 603 * must wait until all device are closed. 604 */ 605 tsleep((void *)dc, PWAIT, "dcodtc", hz/4); 606 destroy_dev(dc->dev); 607 608 return(0); 609 } 610 611 612 /* cnXXX works only for FreeBSD-5 */ 613 static int 614 dcons_modevent(module_t mode, int type, void *data) 615 { 616 int err = 0, ret; 617 618 switch (type) { 619 case MOD_LOAD: 620 ret = dcons_drv_init(1); 621 dcons_attach(); 622 #if __FreeBSD_version >= 500000 623 if (ret == 0) { 624 dcons_cnprobe(&dcons_consdev); 625 dcons_cninit(&dcons_consdev); 626 cnadd(&dcons_consdev); 627 } 628 #endif 629 break; 630 case MOD_UNLOAD: 631 printf("dcons: unload\n"); 632 callout_stop(&dcons_callout); 633 #if __FreeBSD_version < 502122 634 #if DDB && DCONS_FORCE_GDB 635 #if CONS_NODEV 636 gdb_arg = NULL; 637 #else 638 gdbdev = NULL; 639 #endif 640 #endif 641 #endif 642 #if __FreeBSD_version >= 500000 643 cnremove(&dcons_consdev); 644 #endif 645 dcons_detach(DCONS_CON); 646 dcons_detach(DCONS_GDB); 647 dg.buf->magic = 0; 648 649 contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF); 650 651 break; 652 case MOD_SHUTDOWN: 653 break; 654 default: 655 err = EOPNOTSUPP; 656 break; 657 } 658 return(err); 659 } 660 661 #if __FreeBSD_version >= 502122 662 /* Debugger interface */ 663 664 static int 665 dcons_dbg_probe(void) 666 { 667 return(DCONS_FORCE_GDB); 668 } 669 670 static void 671 dcons_dbg_init(void) 672 { 673 } 674 675 static void 676 dcons_dbg_term(void) 677 { 678 } 679 680 static void 681 dcons_dbg_putc(int c) 682 { 683 dcons_putc(&sc[DCONS_GDB], c); 684 } 685 686 static int 687 dcons_dbg_checkc(void) 688 { 689 return (dcons_checkc(&sc[DCONS_GDB])); 690 } 691 692 static int 693 dcons_dbg_getc(void) 694 { 695 return (dcons_getc(&sc[DCONS_GDB])); 696 } 697 #endif 698 699 DEV_MODULE(dcons, dcons_modevent, NULL); 700 MODULE_VERSION(dcons, DCONS_VERSION); 701