Change the kernel dev_t, representing a pointer to a specinfo structure,
[dragonfly.git] / sys / dev / misc / dcons / dcons_os.c
... / ...
CommitLineData
1/*
2 * Copyright (C) 2003,2004
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 * $FreeBSD: src/sys/dev/dcons/dcons_os.c,v 1.4 2004/10/24 12:41:04 simokawa Exp $
35 * $DragonFly: src/sys/dev/misc/dcons/dcons_os.c,v 1.6 2006/09/10 01:26:34 dillon Exp $
36 */
37
38#include <sys/param.h>
39#if __FreeBSD_version >= 502122
40#include <sys/kdb.h>
41#include <gdb/gdb.h>
42#endif
43#include <sys/kernel.h>
44#include <sys/module.h>
45#include <sys/systm.h>
46#include <sys/types.h>
47#include <sys/conf.h>
48#include <sys/cons.h>
49#include <sys/consio.h>
50#include <sys/tty.h>
51#include <sys/malloc.h>
52#include <sys/proc.h>
53#include <sys/thread2.h>
54#include <sys/ucred.h>
55
56#include <machine/bus.h>
57
58#ifdef __DragonFly__
59#include "dcons.h"
60#include "dcons_os.h"
61#else
62#include <dev/dcons/dcons.h>
63#include <dev/dcons/dcons_os.h>
64#endif
65
66#include <ddb/ddb.h>
67#include <sys/reboot.h>
68
69#include <sys/sysctl.h>
70
71#include <vm/vm.h>
72#include <vm/vm_param.h>
73#include <vm/pmap.h>
74
75#include "opt_ddb.h"
76#include "opt_comconsole.h"
77#include "opt_dcons.h"
78
79#ifndef DCONS_POLL_HZ
80#define DCONS_POLL_HZ 100
81#endif
82
83#ifndef DCONS_BUF_SIZE
84#define DCONS_BUF_SIZE (16*1024)
85#endif
86
87#ifndef DCONS_FORCE_CONSOLE
88#define DCONS_FORCE_CONSOLE 0 /* Mostly for FreeBSD-4/DragonFly */
89#endif
90
91#ifndef DCONS_FORCE_GDB
92#define DCONS_FORCE_GDB 1
93#endif
94
95#if __FreeBSD_version >= 500101
96#define CONS_NODEV 1
97#if __FreeBSD_version < 502122
98static struct consdev gdbconsdev;
99#endif
100#endif
101
102#define CDEV_MAJOR 184
103
104static d_open_t dcons_open;
105static d_close_t dcons_close;
106static d_ioctl_t dcons_ioctl;
107
108static struct dev_ops dcons_ops = {
109 { "dcons", CDEV_MAJOR, D_TTY },
110 .d_open = dcons_open,
111 .d_close = dcons_close,
112 .d_read = ttyread,
113 .d_write = ttywrite,
114 .d_ioctl = dcons_ioctl,
115 .d_poll = ttypoll,
116};
117
118#ifndef KLD_MODULE
119static char bssbuf[DCONS_BUF_SIZE]; /* buf in bss */
120#endif
121
122/* global data */
123static struct dcons_global dg;
124struct dcons_global *dcons_conf;
125static int poll_hz = DCONS_POLL_HZ;
126
127static struct dcons_softc sc[DCONS_NPORT];
128
129SYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console");
130SYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0,
131 "dcons polling rate");
132
133static int drv_init = 0;
134static struct callout dcons_callout;
135struct dcons_buf *dcons_buf; /* for local dconschat */
136
137#ifdef __DragonFly__
138#define DEV cdev_t
139#define THREAD d_thread_t
140#elif __FreeBSD_version < 500000
141#define DEV cdev_t
142#define THREAD struct proc
143#else
144#define DEV struct cdev *
145#define THREAD struct thread
146#endif
147
148
149static void dcons_tty_start(struct tty *);
150static int dcons_tty_param(struct tty *, struct termios *);
151static void dcons_timeout(void *);
152static int dcons_drv_init(int);
153
154static cn_probe_t dcons_cnprobe;
155static cn_init_t dcons_cninit;
156static cn_getc_t dcons_cngetc;
157static cn_checkc_t dcons_cncheckc;
158static cn_putc_t dcons_cnputc;
159
160CONS_DRIVER(dcons, dcons_cnprobe, dcons_cninit, NULL, dcons_cngetc,
161 dcons_cncheckc, dcons_cnputc, NULL);
162
163#if __FreeBSD_version >= 502122
164static gdb_probe_f dcons_dbg_probe;
165static gdb_init_f dcons_dbg_init;
166static gdb_term_f dcons_dbg_term;
167static gdb_getc_f dcons_dbg_getc;
168static gdb_checkc_f dcons_dbg_checkc;
169static gdb_putc_f dcons_dbg_putc;
170
171GDB_DBGPORT(dcons, dcons_dbg_probe, dcons_dbg_init, dcons_dbg_term,
172 dcons_dbg_checkc, dcons_dbg_getc, dcons_dbg_putc);
173
174extern struct gdb_dbgport *gdb_cur;
175#endif
176
177#if (KDB || DDB) && ALT_BREAK_TO_DEBUGGER
178static int
179dcons_check_break(struct dcons_softc *dc, int c)
180{
181 if (c < 0)
182 return (c);
183
184#if __FreeBSD_version >= 502122
185 if (kdb_alt_break(c, &dc->brk_state)) {
186 if ((dc->flags & DC_GDB) != 0) {
187 if (gdb_cur == &dcons_gdb_dbgport) {
188 kdb_dbbe_select("gdb");
189 breakpoint();
190 }
191 } else
192 breakpoint();
193 }
194#else
195 switch (dc->brk_state) {
196 case STATE1:
197 if (c == KEY_TILDE)
198 dc->brk_state = STATE2;
199 else
200 dc->brk_state = STATE0;
201 break;
202 case STATE2:
203 dc->brk_state = STATE0;
204 if (c == KEY_CTRLB) {
205#if DCONS_FORCE_GDB
206 if (dc->flags & DC_GDB)
207 boothowto |= RB_GDB;
208#endif
209 breakpoint();
210 }
211 }
212 if (c == KEY_CR)
213 dc->brk_state = STATE1;
214#endif
215 return (c);
216}
217#else
218#define dcons_check_break(dc, c) (c)
219#endif
220
221static int
222dcons_os_checkc(struct dcons_softc *dc)
223{
224 int c;
225
226 if (dg.dma_tag != NULL)
227 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD);
228
229 c = dcons_check_break(dc, dcons_checkc(dc));
230
231 if (dg.dma_tag != NULL)
232 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREREAD);
233
234 return (c);
235}
236
237static int
238dcons_os_getc(struct dcons_softc *dc)
239{
240 int c;
241
242 while ((c = dcons_os_checkc(dc)) == -1);
243
244 return (c & 0xff);
245}
246
247static void
248dcons_os_putc(struct dcons_softc *dc, int c)
249{
250 if (dg.dma_tag != NULL)
251 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTWRITE);
252
253 dcons_putc(dc, c);
254
255 if (dg.dma_tag != NULL)
256 bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE);
257}
258static int
259dcons_open(struct dev_open_args *ap)
260{
261 cdev_t dev = ap->a_head.a_dev;
262 struct tty *tp;
263 int unit, error;
264
265 unit = minor(dev);
266 if (unit != 0)
267 return (ENXIO);
268
269 tp = dev->si_tty = ttymalloc(dev->si_tty);
270 tp->t_oproc = dcons_tty_start;
271 tp->t_param = dcons_tty_param;
272 tp->t_stop = nottystop;
273 tp->t_dev = dev;
274
275 error = 0;
276
277 crit_enter();
278 if ((tp->t_state & TS_ISOPEN) == 0) {
279 tp->t_state |= TS_CARR_ON;
280 ttychars(tp);
281 tp->t_iflag = TTYDEF_IFLAG;
282 tp->t_oflag = TTYDEF_OFLAG;
283 tp->t_cflag = TTYDEF_CFLAG|CLOCAL;
284 tp->t_lflag = TTYDEF_LFLAG;
285 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
286 ttsetwater(tp);
287 } else if ((tp->t_state & TS_XCLUDE) && suser_cred(ap->a_cred, 0)) {
288 crit_exit();
289 return (EBUSY);
290 }
291 crit_exit();
292
293#if __FreeBSD_version < 502113
294 error = (*linesw[tp->t_line].l_open)(dev, tp);
295#else
296 error = ttyld_open(tp, dev);
297#endif
298
299 return (error);
300}
301
302static int
303dcons_close(struct dev_close_args *ap)
304{
305 cdev_t dev = ap->a_head.a_dev;
306 int unit;
307 struct tty *tp;
308
309 unit = minor(dev);
310 if (unit != 0)
311 return (ENXIO);
312
313 tp = dev->si_tty;
314 if (tp->t_state & TS_ISOPEN) {
315 (*linesw[tp->t_line].l_close)(tp, ap->a_fflag);
316 ttyclose(tp);
317 }
318
319 return (0);
320}
321
322static int
323dcons_ioctl(struct dev_ioctl_args *ap)
324{
325 cdev_t dev = ap->a_head.a_dev;
326 int unit;
327 struct tty *tp;
328 int error;
329
330 unit = minor(dev);
331 if (unit != 0)
332 return (ENXIO);
333
334 tp = dev->si_tty;
335 error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data, ap->a_fflag, ap->a_cred);
336 if (error != ENOIOCTL)
337 return (error);
338
339 error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
340 if (error != ENOIOCTL)
341 return (error);
342
343 return (ENOTTY);
344}
345
346static int
347dcons_tty_param(struct tty *tp, struct termios *t)
348{
349 tp->t_ispeed = t->c_ispeed;
350 tp->t_ospeed = t->c_ospeed;
351 tp->t_cflag = t->c_cflag;
352 return 0;
353}
354
355static void
356dcons_tty_start(struct tty *tp)
357{
358 struct dcons_softc *dc;
359
360 dc = (struct dcons_softc *)tp->t_dev->si_drv1;
361 crit_enter();
362 if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
363 ttwwakeup(tp);
364 return;
365 }
366
367 tp->t_state |= TS_BUSY;
368 while (tp->t_outq.c_cc != 0)
369 dcons_os_putc(dc, clist_getc(&tp->t_outq));
370 tp->t_state &= ~TS_BUSY;
371
372 ttwwakeup(tp);
373 crit_exit();
374}
375
376static void
377dcons_timeout(void *v)
378{
379 struct tty *tp;
380 struct dcons_softc *dc;
381 int i, c, polltime;
382
383 for (i = 0; i < DCONS_NPORT; i ++) {
384 dc = &sc[i];
385 tp = ((DEV)dc->dev)->si_tty;
386 while ((c = dcons_os_checkc(dc)) != -1)
387 if (tp->t_state & TS_ISOPEN)
388#if __FreeBSD_version < 502113
389 (*linesw[tp->t_line].l_rint)(c, tp);
390#else
391 ttyld_rint(tp, c);
392#endif
393 }
394 polltime = hz / poll_hz;
395 if (polltime < 1)
396 polltime = 1;
397 callout_reset(&dcons_callout, polltime, dcons_timeout, tp);
398}
399
400static void
401dcons_cnprobe(struct consdev *cp)
402{
403#ifdef __DragonFly__
404 cp->cn_dev = make_dev(&dcons_ops, DCONS_CON,
405 UID_ROOT, GID_WHEEL, 0600, "dcons");
406#elif __FreeBSD_version >= 501109
407 sprintf(cp->cn_name, "dcons");
408#else
409 cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON);
410#endif
411#if DCONS_FORCE_CONSOLE
412 cp->cn_pri = CN_REMOTE;
413#else
414 cp->cn_pri = CN_NORMAL;
415#endif
416}
417
418static void
419dcons_cninit(struct consdev *cp)
420{
421 dcons_drv_init(0);
422#if CONS_NODEV
423 cp->cn_arg
424#else
425 cp->cn_dev->si_drv1
426#endif
427 = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */
428}
429
430#if CONS_NODEV
431static int
432dcons_cngetc(struct consdev *cp)
433{
434 struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg;
435 return (dcons_os_getc(dc));
436}
437static int
438dcons_cncheckc(struct consdev *cp)
439{
440 struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg;
441 return (dcons_os_checkc(dc));
442}
443static void
444dcons_cnputc(struct consdev *cp, int c)
445{
446 struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg;
447 dcons_os_putc(dc, c);
448}
449#else
450static int
451dcons_cngetc(DEV dev)
452{
453 struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1;
454 return (dcons_os_getc(dc));
455}
456static int
457dcons_cncheckc(DEV dev)
458{
459 struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1;
460 return (dcons_os_checkc(dc));
461}
462static void
463dcons_cnputc(DEV dev, int c)
464{
465 struct dcons_softc *dc = (struct dcons_softc *)dev->si_drv1;
466 dcons_os_putc(dc, c);
467}
468#endif
469
470static int
471dcons_drv_init(int stage)
472{
473#ifdef __i386__
474 quad_t addr, size;
475#endif
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.buf = NULL;
486 dg.size = DCONS_BUF_SIZE;
487
488#ifdef __i386__
489 if (kgetenv_quad("dcons.addr", &addr) > 0 &&
490 kgetenv_quad("dcons.size", &size) > 0) {
491 vm_paddr_t pa;
492 /*
493 * Allow read/write access to dcons buffer.
494 */
495 for (pa = trunc_page(addr); pa < addr + size; pa += PAGE_SIZE)
496 *vtopte(KERNBASE + pa) |= PG_RW;
497 cpu_invltlb();
498 /* XXX P to V */
499 dg.buf = (struct dcons_buf *)(vm_offset_t)(KERNBASE + addr);
500 dg.size = size;
501 if (dcons_load_buffer(dg.buf, dg.size, sc) < 0)
502 dg.buf = NULL;
503 }
504#endif
505 if (dg.buf != NULL)
506 goto ok;
507
508#ifndef KLD_MODULE
509 if (stage == 0) { /* XXX or cold */
510 /*
511 * DCONS_FORCE_CONSOLE == 1 and statically linked.
512 * called from cninit(). can't use contigmalloc yet .
513 */
514 dg.buf = (struct dcons_buf *) bssbuf;
515 dcons_init(dg.buf, dg.size, sc);
516 } else
517#endif
518 {
519 /*
520 * DCONS_FORCE_CONSOLE == 0 or kernel module case.
521 * if the module is loaded after boot,
522 * bssbuf could be non-continuous.
523 */
524 dg.buf = (struct dcons_buf *) contigmalloc(dg.size,
525 M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul);
526 dcons_init(dg.buf, dg.size, sc);
527 }
528
529ok:
530 dcons_buf = dg.buf;
531
532#if __FreeBSD_version < 502122
533#if DDB && DCONS_FORCE_GDB
534#if CONS_NODEV
535 gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB];
536#if __FreeBSD_version >= 501109
537 sprintf(gdbconsdev.cn_name, "dgdb");
538#endif
539 gdb_arg = &gdbconsdev;
540#elif defined(__DragonFly__)
541 gdbdev = make_dev(&dcons_ops, DCONS_GDB,
542 UID_ROOT, GID_WHEEL, 0600, "dgdb");
543#else
544 gdbdev = makedev(CDEV_MAJOR, DCONS_GDB);
545#endif
546 gdb_getc = dcons_cngetc;
547 gdb_putc = dcons_cnputc;
548#endif
549#endif
550 drv_init = 1;
551
552 return 0;
553}
554
555
556static int
557dcons_attach_port(int port, char *name, int flags)
558{
559 struct dcons_softc *dc;
560 struct tty *tp;
561 DEV dev;
562
563 dc = &sc[port];
564 dc->flags = flags;
565 dev = make_dev(&dcons_ops, port,
566 UID_ROOT, GID_WHEEL, 0600, name);
567 dc->dev = (void *)dev;
568 tp = ttymalloc(NULL);
569
570 dev->si_drv1 = (void *)dc;
571 dev->si_tty = tp;
572
573 tp->t_oproc = dcons_tty_start;
574 tp->t_param = dcons_tty_param;
575 tp->t_stop = nottystop;
576 tp->t_dev = dc->dev;
577
578 return(0);
579}
580
581static int
582dcons_attach(void)
583{
584 int polltime;
585
586#ifdef __DragonFly__
587 dev_ops_add(&dcons_ops, -1, 0);
588#endif
589 dcons_attach_port(DCONS_CON, "dcons", 0);
590 dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB);
591#if __FreeBSD_version < 500000
592 callout_init(&dcons_callout);
593#else
594 callout_init(&dcons_callout, 0);
595#endif
596 polltime = hz / poll_hz;
597 if (polltime < 1)
598 polltime = 1;
599 callout_reset(&dcons_callout, polltime, dcons_timeout, NULL);
600 return(0);
601}
602
603static int
604dcons_detach(int port)
605{
606 struct tty *tp;
607 struct dcons_softc *dc;
608
609 dc = &sc[port];
610
611 tp = ((DEV)dc->dev)->si_tty;
612
613 if (tp->t_state & TS_ISOPEN) {
614 printf("dcons: still opened\n");
615#if __FreeBSD_version < 502113
616 (*linesw[tp->t_line].l_close)(tp, 0);
617 tp->t_gen++;
618 ttyclose(tp);
619 ttwakeup(tp);
620 ttwwakeup(tp);
621#else
622 ttyld_close(tp, 0);
623 tty_close(tp);
624#endif
625 }
626 /* XXX
627 * must wait until all device are closed.
628 */
629#ifdef __DragonFly__
630 tsleep((void *)dc, 0, "dcodtc", hz/4);
631#else
632 tsleep((void *)dc, PWAIT, "dcodtc", hz/4);
633#endif
634 destroy_dev(dc->dev);
635
636 return(0);
637}
638
639
640/* cnXXX works only for FreeBSD-5 */
641static int
642dcons_modevent(module_t mode, int type, void *data)
643{
644 int err = 0, ret;
645
646 switch (type) {
647 case MOD_LOAD:
648 ret = dcons_drv_init(1);
649 dcons_attach();
650#if __FreeBSD_version >= 500000
651 if (ret == 0) {
652 dcons_cnprobe(&dcons_consdev);
653 dcons_cninit(&dcons_consdev);
654 cnadd(&dcons_consdev);
655 }
656#endif
657 break;
658 case MOD_UNLOAD:
659 printf("dcons: unload\n");
660 callout_stop(&dcons_callout);
661#if __FreeBSD_version < 502122
662#if DDB && DCONS_FORCE_GDB
663#if CONS_NODEV
664 gdb_arg = NULL;
665#else
666 gdbdev = NULL;
667#endif
668#endif
669#endif
670#if __FreeBSD_version >= 500000
671 cnremove(&dcons_consdev);
672#endif
673 dcons_detach(DCONS_CON);
674 dcons_detach(DCONS_GDB);
675 dg.buf->magic = 0;
676
677 contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF);
678
679 break;
680 case MOD_SHUTDOWN:
681 dg.buf->magic = 0;
682 break;
683 default:
684 err = EOPNOTSUPP;
685 break;
686 }
687 return(err);
688}
689
690#if __FreeBSD_version >= 502122
691/* Debugger interface */
692
693static int
694dcons_dbg_probe(void)
695{
696 return(DCONS_FORCE_GDB);
697}
698
699static void
700dcons_dbg_init(void)
701{
702}
703
704static void
705dcons_dbg_term(void)
706{
707}
708
709static void
710dcons_dbg_putc(int c)
711{
712 struct dcons_softc *dc = &sc[DCONS_GDB];
713 dcons_os_putc(dc, c);
714}
715
716static int
717dcons_dbg_checkc(void)
718{
719 struct dcons_softc *dc = &sc[DCONS_GDB];
720 return (dcons_os_checkc(dc));
721}
722
723static int
724dcons_dbg_getc(void)
725{
726 struct dcons_softc *dc = &sc[DCONS_GDB];
727 return (dcons_os_getc(dc));
728}
729#endif
730
731DEV_MODULE(dcons, dcons_modevent, NULL);
732MODULE_VERSION(dcons, DCONS_VERSION);