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