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