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