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