11447c7c4f
The srm console is always built in. It will never be modular, so using module_init as an alias for __initcall is rather misleading. Fix this up now, so that we can relocate module_init from init.h into module.h in the future. If we don't do this, we'd have to add module.h to obviously non-modular code, and that would be a worse thing. Direct use of __initcall is discouraged, vs prioritized ones. Use of device_initcall is consistent with what __initcall maps onto, and hence does not change the init order, making the impact of this change zero. Should someone with real hardware for boot testing want to change it later to arch_initcall or console_initcall, they can do that at a later date. Reviewed-by: Richard Henderson <rth@twiddle.net> Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru> Acked-by: Matt Turner <mattst88@gmail.com> Cc: linux-alpha@vger.kernel.org Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> Signed-off-by: Matt Turner <mattst88@gmail.com>
296 lines
6 KiB
C
296 lines
6 KiB
C
/*
|
||
* linux/arch/alpha/kernel/srmcons.c
|
||
*
|
||
* Callback based driver for SRM Console console device.
|
||
* (TTY driver and console driver)
|
||
*/
|
||
|
||
#include <linux/kernel.h>
|
||
#include <linux/init.h>
|
||
#include <linux/console.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/mm.h>
|
||
#include <linux/slab.h>
|
||
#include <linux/spinlock.h>
|
||
#include <linux/timer.h>
|
||
#include <linux/tty.h>
|
||
#include <linux/tty_driver.h>
|
||
#include <linux/tty_flip.h>
|
||
|
||
#include <asm/console.h>
|
||
#include <asm/uaccess.h>
|
||
|
||
|
||
static DEFINE_SPINLOCK(srmcons_callback_lock);
|
||
static int srm_is_registered_console = 0;
|
||
|
||
/*
|
||
* The TTY driver
|
||
*/
|
||
#define MAX_SRM_CONSOLE_DEVICES 1 /* only support 1 console device */
|
||
|
||
struct srmcons_private {
|
||
struct tty_port port;
|
||
struct timer_list timer;
|
||
} srmcons_singleton;
|
||
|
||
typedef union _srmcons_result {
|
||
struct {
|
||
unsigned long c :61;
|
||
unsigned long status :3;
|
||
} bits;
|
||
long as_long;
|
||
} srmcons_result;
|
||
|
||
/* called with callback_lock held */
|
||
static int
|
||
srmcons_do_receive_chars(struct tty_port *port)
|
||
{
|
||
srmcons_result result;
|
||
int count = 0, loops = 0;
|
||
|
||
do {
|
||
result.as_long = callback_getc(0);
|
||
if (result.bits.status < 2) {
|
||
tty_insert_flip_char(port, (char)result.bits.c, 0);
|
||
count++;
|
||
}
|
||
} while((result.bits.status & 1) && (++loops < 10));
|
||
|
||
if (count)
|
||
tty_schedule_flip(port);
|
||
|
||
return count;
|
||
}
|
||
|
||
static void
|
||
srmcons_receive_chars(unsigned long data)
|
||
{
|
||
struct srmcons_private *srmconsp = (struct srmcons_private *)data;
|
||
struct tty_port *port = &srmconsp->port;
|
||
unsigned long flags;
|
||
int incr = 10;
|
||
|
||
local_irq_save(flags);
|
||
if (spin_trylock(&srmcons_callback_lock)) {
|
||
if (!srmcons_do_receive_chars(port))
|
||
incr = 100;
|
||
spin_unlock(&srmcons_callback_lock);
|
||
}
|
||
|
||
spin_lock(&port->lock);
|
||
if (port->tty)
|
||
mod_timer(&srmconsp->timer, jiffies + incr);
|
||
spin_unlock(&port->lock);
|
||
|
||
local_irq_restore(flags);
|
||
}
|
||
|
||
/* called with callback_lock held */
|
||
static int
|
||
srmcons_do_write(struct tty_port *port, const char *buf, int count)
|
||
{
|
||
static char str_cr[1] = "\r";
|
||
long c, remaining = count;
|
||
srmcons_result result;
|
||
char *cur;
|
||
int need_cr;
|
||
|
||
for (cur = (char *)buf; remaining > 0; ) {
|
||
need_cr = 0;
|
||
/*
|
||
* Break it up into reasonable size chunks to allow a chance
|
||
* for input to get in
|
||
*/
|
||
for (c = 0; c < min_t(long, 128L, remaining) && !need_cr; c++)
|
||
if (cur[c] == '\n')
|
||
need_cr = 1;
|
||
|
||
while (c > 0) {
|
||
result.as_long = callback_puts(0, cur, c);
|
||
c -= result.bits.c;
|
||
remaining -= result.bits.c;
|
||
cur += result.bits.c;
|
||
|
||
/*
|
||
* Check for pending input iff a tty port was provided
|
||
*/
|
||
if (port)
|
||
srmcons_do_receive_chars(port);
|
||
}
|
||
|
||
while (need_cr) {
|
||
result.as_long = callback_puts(0, str_cr, 1);
|
||
if (result.bits.c > 0)
|
||
need_cr = 0;
|
||
}
|
||
}
|
||
return count;
|
||
}
|
||
|
||
static int
|
||
srmcons_write(struct tty_struct *tty,
|
||
const unsigned char *buf, int count)
|
||
{
|
||
unsigned long flags;
|
||
|
||
spin_lock_irqsave(&srmcons_callback_lock, flags);
|
||
srmcons_do_write(tty->port, (const char *) buf, count);
|
||
spin_unlock_irqrestore(&srmcons_callback_lock, flags);
|
||
|
||
return count;
|
||
}
|
||
|
||
static int
|
||
srmcons_write_room(struct tty_struct *tty)
|
||
{
|
||
return 512;
|
||
}
|
||
|
||
static int
|
||
srmcons_chars_in_buffer(struct tty_struct *tty)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
srmcons_open(struct tty_struct *tty, struct file *filp)
|
||
{
|
||
struct srmcons_private *srmconsp = &srmcons_singleton;
|
||
struct tty_port *port = &srmconsp->port;
|
||
unsigned long flags;
|
||
|
||
spin_lock_irqsave(&port->lock, flags);
|
||
|
||
if (!port->tty) {
|
||
tty->driver_data = srmconsp;
|
||
tty->port = port;
|
||
port->tty = tty; /* XXX proper refcounting */
|
||
mod_timer(&srmconsp->timer, jiffies + 10);
|
||
}
|
||
|
||
spin_unlock_irqrestore(&port->lock, flags);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static void
|
||
srmcons_close(struct tty_struct *tty, struct file *filp)
|
||
{
|
||
struct srmcons_private *srmconsp = tty->driver_data;
|
||
struct tty_port *port = &srmconsp->port;
|
||
unsigned long flags;
|
||
|
||
spin_lock_irqsave(&port->lock, flags);
|
||
|
||
if (tty->count == 1) {
|
||
port->tty = NULL;
|
||
del_timer(&srmconsp->timer);
|
||
}
|
||
|
||
spin_unlock_irqrestore(&port->lock, flags);
|
||
}
|
||
|
||
|
||
static struct tty_driver *srmcons_driver;
|
||
|
||
static const struct tty_operations srmcons_ops = {
|
||
.open = srmcons_open,
|
||
.close = srmcons_close,
|
||
.write = srmcons_write,
|
||
.write_room = srmcons_write_room,
|
||
.chars_in_buffer= srmcons_chars_in_buffer,
|
||
};
|
||
|
||
static int __init
|
||
srmcons_init(void)
|
||
{
|
||
setup_timer(&srmcons_singleton.timer, srmcons_receive_chars,
|
||
(unsigned long)&srmcons_singleton);
|
||
if (srm_is_registered_console) {
|
||
struct tty_driver *driver;
|
||
int err;
|
||
|
||
driver = alloc_tty_driver(MAX_SRM_CONSOLE_DEVICES);
|
||
if (!driver)
|
||
return -ENOMEM;
|
||
|
||
tty_port_init(&srmcons_singleton.port);
|
||
|
||
driver->driver_name = "srm";
|
||
driver->name = "srm";
|
||
driver->major = 0; /* dynamic */
|
||
driver->minor_start = 0;
|
||
driver->type = TTY_DRIVER_TYPE_SYSTEM;
|
||
driver->subtype = SYSTEM_TYPE_SYSCONS;
|
||
driver->init_termios = tty_std_termios;
|
||
tty_set_operations(driver, &srmcons_ops);
|
||
tty_port_link_device(&srmcons_singleton.port, driver, 0);
|
||
err = tty_register_driver(driver);
|
||
if (err) {
|
||
put_tty_driver(driver);
|
||
tty_port_destroy(&srmcons_singleton.port);
|
||
return err;
|
||
}
|
||
srmcons_driver = driver;
|
||
}
|
||
|
||
return -ENODEV;
|
||
}
|
||
device_initcall(srmcons_init);
|
||
|
||
|
||
/*
|
||
* The console driver
|
||
*/
|
||
static void
|
||
srm_console_write(struct console *co, const char *s, unsigned count)
|
||
{
|
||
unsigned long flags;
|
||
|
||
spin_lock_irqsave(&srmcons_callback_lock, flags);
|
||
srmcons_do_write(NULL, s, count);
|
||
spin_unlock_irqrestore(&srmcons_callback_lock, flags);
|
||
}
|
||
|
||
static struct tty_driver *
|
||
srm_console_device(struct console *co, int *index)
|
||
{
|
||
*index = co->index;
|
||
return srmcons_driver;
|
||
}
|
||
|
||
static int
|
||
srm_console_setup(struct console *co, char *options)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static struct console srmcons = {
|
||
.name = "srm",
|
||
.write = srm_console_write,
|
||
.device = srm_console_device,
|
||
.setup = srm_console_setup,
|
||
.flags = CON_PRINTBUFFER | CON_BOOT,
|
||
.index = -1,
|
||
};
|
||
|
||
void __init
|
||
register_srm_console(void)
|
||
{
|
||
if (!srm_is_registered_console) {
|
||
callback_open_console();
|
||
register_console(&srmcons);
|
||
srm_is_registered_console = 1;
|
||
}
|
||
}
|
||
|
||
void __init
|
||
unregister_srm_console(void)
|
||
{
|
||
if (srm_is_registered_console) {
|
||
callback_close_console();
|
||
unregister_console(&srmcons);
|
||
srm_is_registered_console = 0;
|
||
}
|
||
}
|