| /* | |
| * arch/v850/kernel/memcons.c -- Console I/O to a memory buffer | |
| * | |
| * Copyright (C) 2001,02 NEC Corporation | |
| * Copyright (C) 2001,02 Miles Bader <miles@gnu.org> | |
| * | |
| * This file is subject to the terms and conditions of the GNU General | |
| * Public License. See the file COPYING in the main directory of this | |
| * archive for more details. | |
| * | |
| * Written by Miles Bader <miles@gnu.org> | |
| */ | |
| #include <linux/kernel.h> | |
| #include <linux/console.h> | |
| #include <linux/tty.h> | |
| #include <linux/tty_driver.h> | |
| #include <linux/init.h> | |
| /* If this device is enabled, the linker map should define start and | |
| end points for its buffer. */ | |
| extern char memcons_output[], memcons_output_end; | |
| /* Current offset into the buffer. */ | |
| static unsigned long memcons_offs = 0; | |
| /* Spinlock protecting memcons_offs. */ | |
| static DEFINE_SPINLOCK(memcons_lock); | |
| static size_t write (const char *buf, size_t len) | |
| { | |
| unsigned long flags; | |
| char *point; | |
| spin_lock_irqsave (memcons_lock, flags); | |
| point = memcons_output + memcons_offs; | |
| if (point + len >= &memcons_output_end) { | |
| len = &memcons_output_end - point; | |
| memcons_offs = 0; | |
| } else | |
| memcons_offs += len; | |
| spin_unlock_irqrestore (memcons_lock, flags); | |
| memcpy (point, buf, len); | |
| return len; | |
| } | |
| /* Low-level console. */ | |
| static void memcons_write (struct console *co, const char *buf, unsigned len) | |
| { | |
| while (len > 0) | |
| len -= write (buf, len); | |
| } | |
| static struct tty_driver *tty_driver; | |
| static struct tty_driver *memcons_device (struct console *co, int *index) | |
| { | |
| *index = co->index; | |
| return tty_driver; | |
| } | |
| static struct console memcons = | |
| { | |
| .name = "memcons", | |
| .write = memcons_write, | |
| .device = memcons_device, | |
| .flags = CON_PRINTBUFFER, | |
| .index = -1, | |
| }; | |
| void memcons_setup (void) | |
| { | |
| register_console (&memcons); | |
| printk (KERN_INFO "Console: static memory buffer (memcons)\n"); | |
| } | |
| /* Higher level TTY interface. */ | |
| int memcons_tty_open (struct tty_struct *tty, struct file *filp) | |
| { | |
| return 0; | |
| } | |
| int memcons_tty_write (struct tty_struct *tty, const unsigned char *buf, int len) | |
| { | |
| return write (buf, len); | |
| } | |
| int memcons_tty_write_room (struct tty_struct *tty) | |
| { | |
| return &memcons_output_end - (memcons_output + memcons_offs); | |
| } | |
| int memcons_tty_chars_in_buffer (struct tty_struct *tty) | |
| { | |
| /* We have no buffer. */ | |
| return 0; | |
| } | |
| static const struct tty_operations ops = { | |
| .open = memcons_tty_open, | |
| .write = memcons_tty_write, | |
| .write_room = memcons_tty_write_room, | |
| .chars_in_buffer = memcons_tty_chars_in_buffer, | |
| }; | |
| int __init memcons_tty_init (void) | |
| { | |
| int err; | |
| struct tty_driver *driver = alloc_tty_driver(1); | |
| if (!driver) | |
| return -ENOMEM; | |
| driver->name = "memcons"; | |
| driver->major = TTY_MAJOR; | |
| driver->minor_start = 64; | |
| driver->type = TTY_DRIVER_TYPE_SYSCONS; | |
| driver->init_termios = tty_std_termios; | |
| tty_set_operations(driver, &ops); | |
| err = tty_register_driver(driver); | |
| if (err) { | |
| put_tty_driver(driver); | |
| return err; | |
| } | |
| tty_driver = driver; | |
| return 0; | |
| } | |
| __initcall (memcons_tty_init); |