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