Deleted Added
full compact
dcons.c (130585) dcons.c (130640)
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 $
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 130585 2004-06-16 09:47:26Z phk $
35 * $FreeBSD: head/sys/dev/dcons/dcons.c 130640 2004-06-17 17:16:53Z phk $
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
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
601 gdbdev = NODEV;
601 gdbdev = NULL;
602#endif
603#endif
604#if __FreeBSD_version >= 500000
605 cnremove(&dcons_consdev);
606#endif
607 dcons_detach(DCONS_CON);
608 dcons_detach(DCONS_GDB);
609 dg.buf->magic = 0;
610
611 contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF);
612
613 break;
614 case MOD_SHUTDOWN:
615 break;
616 }
617 return(err);
618}
619
620DEV_MODULE(dcons, dcons_modevent, NULL);
621MODULE_VERSION(dcons, DCONS_VERSION);
602#endif
603#endif
604#if __FreeBSD_version >= 500000
605 cnremove(&dcons_consdev);
606#endif
607 dcons_detach(DCONS_CON);
608 dcons_detach(DCONS_GDB);
609 dg.buf->magic = 0;
610
611 contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF);
612
613 break;
614 case MOD_SHUTDOWN:
615 break;
616 }
617 return(err);
618}
619
620DEV_MODULE(dcons, dcons_modevent, NULL);
621MODULE_VERSION(dcons, DCONS_VERSION);