[PATCH] RTAS console driver
Utz Bacher
utz.bacher at de.ibm.com
Wed Jul 27 05:20:08 EST 2005
RTAS console driver
This RTAS console driver can be used by all machines that abstract the
system console through the {get,put}-term-char interface. This code
should go into the hvc console driver some time, so it's just for
review and early usage on BPA platform systems; it's not intended for
inclusion at this time.
Signed-off-by: Utz Bacher <utz.bacher at de.ibm.com>
diff -ruNp linux-2.6.13-rc3-old/drivers/char/Kconfig linux-2.6.13-rc3-new/drivers/char/Kconfig
--- linux-2.6.13-rc3-old/drivers/char/Kconfig 2005-07-26 19:52:40.000000000 +0200
+++ linux-2.6.13-rc3-new/drivers/char/Kconfig 2005-07-26 20:00:58.000000000 +0200
@@ -560,6 +560,12 @@ config HVC_CONSOLE
console. This driver allows each pSeries partition to have a console
which is accessed via the HMC.
+config RTASCONS
+ bool "RTAS firmware console support"
+ depends on PPC_RTAS
+ help
+ RTAS console support.
+
config HVCS
tristate "IBM Hypervisor Virtual Console Server support"
depends on PPC_PSERIES
diff -ruNp linux-2.6.13-rc3-old/drivers/char/Makefile linux-2.6.13-rc3-new/drivers/char/Makefile
--- linux-2.6.13-rc3-old/drivers/char/Makefile 2005-07-26 19:52:40.000000000 +0200
+++ linux-2.6.13-rc3-new/drivers/char/Makefile 2005-07-26 20:06:16.000000000 +0200
@@ -40,6 +40,7 @@ obj-$(CONFIG_N_HDLC) += n_hdlc.o
obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
obj-$(CONFIG_SX) += sx.o generic_serial.o
obj-$(CONFIG_RIO) += rio/ generic_serial.o
+obj-$(CONFIG_RTASCONS) += rtascons.o
obj-$(CONFIG_HVC_CONSOLE) += hvc_console.o hvc_vio.o hvsi.o
obj-$(CONFIG_RAW_DRIVER) += raw.o
obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
diff -ruNp linux-2.6.13-rc3-old/drivers/char/rtascons.c linux-2.6.13-rc3-new/drivers/char/rtascons.c
--- linux-2.6.13-rc3-old/drivers/char/rtascons.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.13-rc3-new/drivers/char/rtascons.c 2005-07-26 20:07:06.000000000 +0200
@@ -0,0 +1,339 @@
+/*
+ * console driver using RTAS calls
+ *
+ * (C) Copyright IBM Corp. 2005
+ * RTAS console driver
+ *
+ * Author: Utz Bacher <utz.bacher at de.ibm.com>
+ *
+ * inspired by drivers/char/hvc_console.c
+ * written by Anton Blanchard and Paul Mackerras
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* The whole driver assumes we only have one RTAS console. This makes
+ * things pretty easy. */
+
+#include <linux/console.h>
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <asm/atomic.h>
+#include <asm/rtas.h>
+#include <asm/uaccess.h>
+
+
+#define RTASCONS_MAJOR 229
+#define RTASCONS_MINOR 0
+
+#define RTASCONS_SYSRQ_KEY '\x0f'
+
+#define RTASCONS_PUT_ATTEMPTS 16
+#define RTASCONS_PUT_DELAY 100
+#define RTASCONS_BUFFER_SIZE 4096
+
+#define RTASCONS_MAX_POLL 50
+#define RTASCONS_WRITE_ROOM 200
+
+#define RTASCONS_TIMEOUT ((HZ + 99) / 100)
+
+
+static struct tty_driver *rtascons_ttydriver;
+
+static atomic_t rtascons_usecount = ATOMIC_INIT(0);
+static struct tty_struct *rtascons_tty;
+
+static int rtascons_put_char_token;
+static int rtascons_get_char_token;
+
+static spinlock_t rtascons_buffer_lock = SPIN_LOCK_UNLOCKED;
+static char rtascons_buffer[RTASCONS_BUFFER_SIZE];
+static int rtascons_buffer_head = 0;
+static int rtascons_buffer_used = 0;
+
+static int
+rtascons_get_char(void)
+{
+ int result;
+
+ if (rtas_call(rtascons_get_char_token, 0, 2, &result))
+ result = -1;
+
+ return result;
+}
+
+/* assumes that rtascons_buffer_lock is held */
+static void
+rtascons_flush_chars(void)
+{
+ int result;
+ int attempts = RTASCONS_PUT_ATTEMPTS;
+
+ /* if there is more than one character to be displayed, wait a bit */
+ for (; rtascons_buffer_used && attempts; udelay(RTASCONS_PUT_DELAY)) {
+ attempts--;
+ result = rtas_call(rtascons_put_char_token, 1, 1, NULL,
+ rtascons_buffer[rtascons_buffer_head]);
+
+ if (!result) {
+ rtascons_buffer_head = (rtascons_buffer_head + 1) %
+ RTASCONS_BUFFER_SIZE;
+ rtascons_buffer_used--;
+ }
+ }
+}
+
+static void
+rtascons_put_char(char c)
+{
+ spin_lock(&rtascons_buffer_lock);
+
+ if (rtascons_buffer_used >= (RTASCONS_BUFFER_SIZE / 2))
+ udelay(RTASCONS_PUT_DELAY); /* slow down if buffer tends
+ to get full */
+
+ if (rtascons_buffer_used >= RTASCONS_BUFFER_SIZE)
+ goto out; /* we're loosing characters. */
+
+ /* enqueue character */
+ rtascons_buffer[(rtascons_buffer_head + rtascons_buffer_used) %
+ RTASCONS_BUFFER_SIZE] = c;
+ rtascons_buffer_used++;
+out:
+ rtascons_flush_chars();
+
+ spin_unlock(&rtascons_buffer_lock);
+}
+
+static void
+rtascons_print_str(const char *buf, int count)
+{
+ int i = 0;
+ while (i < count) {
+ rtascons_put_char(buf[i]);
+ if (buf[i] == '\n')
+ rtascons_put_char('\r');
+ i++;
+ }
+}
+
+static int
+rtascons_open(struct tty_struct *tty, struct file *filp)
+{
+ /* only one console */
+ if (tty->index) {
+ /* close will be called and that decrement */
+ atomic_inc(&rtascons_usecount);
+ return -ENODEV;
+ }
+
+ if (atomic_inc_return(&rtascons_usecount) == 1) {
+ rtascons_tty = tty;
+ }
+
+ tty->driver_data = &rtascons_ttydriver;
+
+ return 0;
+}
+
+static void
+rtascons_close(struct tty_struct *tty, struct file * filp)
+{
+ atomic_dec(&rtascons_usecount);
+}
+
+static void
+rtascons_hangup(struct tty_struct *tty)
+{
+}
+
+static int
+rtascons_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ if (!atomic_read(&rtascons_usecount))
+ return 0;
+
+ rtascons_print_str(buf, count);
+
+ return count;
+}
+
+static int
+rtascons_write_room(struct tty_struct *tty)
+{
+ return RTASCONS_WRITE_ROOM;
+}
+
+static int
+rtascons_chars_in_buffer(struct tty_struct *tty)
+{
+ return 0;
+}
+
+static void
+rtascons_poll(void)
+{
+ int i;
+ int do_poll = RTASCONS_MAX_POLL;
+#ifdef CONFIG_MAGIC_SYSRQ
+ static int sysrq_pressed = 0;
+#endif /* CONFIG_MAGIC_SYSRQ */
+
+ if (!atomic_read(&rtascons_usecount))
+ return;
+
+ while (do_poll--) {
+ i = rtascons_get_char();
+ if (i < 0)
+ break;
+
+#ifdef CONFIG_MAGIC_SYSRQ
+ if (i == RTASCONS_SYSRQ_KEY) {
+ sysrq_pressed = 1;
+ continue;
+ } else if (sysrq_pressed) {
+ handle_sysrq(i, NULL, rtascons_tty);
+ sysrq_pressed = 0;
+ continue;
+ }
+#endif /* CONFIG_MAGIC_SYSRQ */
+
+ tty_insert_flip_char(rtascons_tty, (unsigned char) i, 0);
+ }
+
+ tty_flip_buffer_push(rtascons_tty);
+}
+
+#if defined(CONFIG_XMON) && defined(CONFIG_SMP)
+extern cpumask_t cpus_in_xmon;
+#else
+static const cpumask_t cpus_in_xmon = CPU_MASK_NONE;
+#endif
+
+static int
+krtasconsd(void *unused)
+{
+ daemonize("krtasconsd");
+
+ for (;;) {
+ if (cpus_empty(cpus_in_xmon)) {
+ rtascons_poll();
+ /* no need for atomic access */
+ if (rtascons_buffer_used) {
+ spin_lock(&rtascons_buffer_lock);
+ rtascons_flush_chars();
+ spin_unlock(&rtascons_buffer_lock);
+ }
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(RTASCONS_TIMEOUT);
+ }
+}
+
+static struct tty_operations rtascons_ops = {
+ .open = rtascons_open,
+ .close = rtascons_close,
+ .write = rtascons_write,
+ .hangup = rtascons_hangup,
+ .write_room = rtascons_write_room,
+ .chars_in_buffer = rtascons_chars_in_buffer,
+};
+
+static int __init
+rtascons_init(void)
+{
+ rtascons_ttydriver = alloc_tty_driver(1);
+ if (!rtascons_ttydriver)
+ return -ENOMEM;
+
+ rtascons_ttydriver->owner = THIS_MODULE;
+ rtascons_ttydriver->devfs_name = "rtascons/";
+ rtascons_ttydriver->driver_name = "rtascons";
+ rtascons_ttydriver->name = "rtascons";
+ rtascons_ttydriver->major = RTASCONS_MAJOR;
+ rtascons_ttydriver->minor_start = RTASCONS_MINOR;
+ rtascons_ttydriver->type = TTY_DRIVER_TYPE_SYSTEM;
+ rtascons_ttydriver->subtype = SYSTEM_TYPE_CONSOLE;
+ rtascons_ttydriver->init_termios = tty_std_termios;
+ rtascons_ttydriver->flags = TTY_DRIVER_REAL_RAW;
+ tty_set_operations(rtascons_ttydriver, &rtascons_ops);
+
+ if (tty_register_driver(rtascons_ttydriver))
+ panic("Couldn't register RTAS console driver\n");
+
+ kernel_thread(krtasconsd, NULL, CLONE_KERNEL);
+
+ return 0;
+}
+
+static void __exit
+rtascons_exit(void)
+{
+}
+
+static void
+rtascons_print(struct console *con, const char *buf, unsigned count)
+{
+ rtascons_print_str(buf, count);
+}
+
+static struct tty_driver *rtascons_device(struct console *con, int *index)
+{
+ *index = con->index;
+ return rtascons_ttydriver;
+}
+
+static int __init
+rtascons_setup(struct console *con, char *options)
+{
+ return (con->index);
+}
+
+struct console rtascons_driver = {
+ .name = "rtas",
+ .write = rtascons_print,
+ .device = rtascons_device,
+ .setup = rtascons_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+static int __init
+rtascons_register(void)
+{
+ rtascons_put_char_token = rtas_token("put-term-char");
+ if (rtascons_put_char_token == -1)
+ return -EIO;
+ rtascons_get_char_token = rtas_token("get-term-char");
+ if (rtascons_get_char_token == -1)
+ return -EIO;
+
+ register_console(&rtascons_driver);
+ return 0;
+}
+
+console_initcall(rtascons_register);
+
+module_init(rtascons_init);
+module_exit(rtascons_exit);
More information about the Linuxppc64-dev
mailing list