[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