patch-2.4.20 linux-2.4.20/drivers/char/hvc_console.c

Next file: linux-2.4.20/drivers/char/i810-tco.c
Previous file: linux-2.4.20/drivers/char/hp_psaux.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.19/drivers/char/hvc_console.c linux-2.4.20/drivers/char/hvc_console.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
+ * Copyright (C) 2001 Paul Mackerras <paulus@au.ibm.com>, IBM
+ *
+ * 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/major.h>
+#include <linux/kernel.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/sched.h>
+#include <linux/kbd_kern.h>
+#include <asm/uaccess.h>
+#include <linux/spinlock.h>
+
+extern int hvc_count(int *);
+extern int hvc_get_chars(int index, char *buf, int count);
+extern int hvc_put_chars(int index, const char *buf, int count);
+
+#define HVC_MAJOR	229
+#define HVC_MINOR	0
+
+#define MAX_NR_HVC_CONSOLES	4
+
+#define TIMEOUT		((HZ + 99) / 100)
+
+struct tty_driver hvc_driver;
+static int hvc_refcount;
+static struct tty_struct *hvc_table[MAX_NR_HVC_CONSOLES];
+static struct termios *hvc_termios[MAX_NR_HVC_CONSOLES];
+static struct termios *hvc_termios_locked[MAX_NR_HVC_CONSOLES];
+static int hvc_offset;
+#ifdef CONFIG_MAGIC_SYSRQ
+static int sysrq_pressed;
+#endif
+
+#define N_OUTBUF	16
+
+#define __ALIGNED__	__attribute__((__aligned__(8)))
+
+struct hvc_struct {
+	spinlock_t lock;
+	int index;
+	struct tty_struct *tty;
+	unsigned int count;
+	int do_wakeup;
+	char outbuf[N_OUTBUF] __ALIGNED__;
+	int n_outbuf;
+};
+
+struct hvc_struct hvc_struct[MAX_NR_HVC_CONSOLES];
+
+static int hvc_open(struct tty_struct *tty, struct file * filp)
+{
+	int line = MINOR(tty->device) - tty->driver.minor_start;
+	struct hvc_struct *hp;
+	unsigned long flags;
+
+	if (line < 0 || line >= MAX_NR_HVC_CONSOLES)
+		return -ENODEV;
+	hp = &hvc_struct[line];
+
+	tty->driver_data = hp;
+	spin_lock_irqsave(&hp->lock, flags);
+	hp->tty = tty;
+	hp->count++;
+	spin_unlock_irqrestore(&hp->lock, flags);
+
+	return 0;
+}
+
+static void hvc_close(struct tty_struct *tty, struct file * filp)
+{
+	struct hvc_struct *hp = tty->driver_data;
+	unsigned long flags;
+
+	if (tty_hung_up_p(filp))
+		return;
+	spin_lock_irqsave(&hp->lock, flags);
+	if (--hp->count == 0)
+		hp->tty = NULL;
+	else if (hp->count < 0)
+		printk(KERN_ERR "hvc_close %lu: oops, count is %d\n",
+		       hp - hvc_struct, hp->count);
+	spin_unlock_irqrestore(&hp->lock, flags);
+}
+
+static void hvc_hangup(struct tty_struct *tty)
+{
+	struct hvc_struct *hp = tty->driver_data;
+
+	hp->count = 0;
+	hp->tty = NULL;
+}
+
+/* called with hp->lock held */
+static void hvc_push(struct hvc_struct *hp)
+{
+	int n;
+
+	n = hvc_put_chars(hp->index + hvc_offset, hp->outbuf, hp->n_outbuf);
+	if (n <= 0) {
+		if (n == 0)
+			return;
+		/* throw away output on error; this happens when
+		   there is no session connected to the vterm. */
+		hp->n_outbuf = 0;
+	} else
+		hp->n_outbuf -= n;
+	if (hp->n_outbuf > 0)
+		memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf);
+	else
+		hp->do_wakeup = 1;
+}
+
+static int hvc_write(struct tty_struct *tty, int from_user,
+		     const unsigned char *buf, int count)
+{
+	struct hvc_struct *hp = tty->driver_data;
+	char *p;
+	int todo, written = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hp->lock, flags);
+	while (count > 0 && (todo = N_OUTBUF - hp->n_outbuf) > 0) {
+		if (todo > count)
+			todo = count;
+		p = hp->outbuf + hp->n_outbuf;
+		if (from_user) {
+			todo -= copy_from_user(p, buf, todo);
+			if (todo == 0) {
+				if (written == 0)
+					written = -EFAULT;
+				break;
+			}
+		} else
+			memcpy(p, buf, todo);
+		count -= todo;
+		buf += todo;
+		hp->n_outbuf += todo;
+		written += todo;
+		hvc_push(hp);
+	}
+	spin_unlock_irqrestore(&hp->lock, flags);
+
+	return written;
+}
+
+static int hvc_write_room(struct tty_struct *tty)
+{
+	struct hvc_struct *hp = tty->driver_data;
+
+	return N_OUTBUF - hp->n_outbuf;
+}
+
+static int hvc_chars_in_buffer(struct tty_struct *tty)
+{
+	struct hvc_struct *hp = tty->driver_data;
+
+	return hp->n_outbuf;
+}
+
+static void hvc_poll(int index)
+{
+	struct hvc_struct *hp = &hvc_struct[index];
+	struct tty_struct *tty;
+	int i, n;
+	char buf[16] __ALIGNED__;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hp->lock, flags);
+
+	if (hp->n_outbuf > 0)
+		hvc_push(hp);
+
+	tty = hp->tty;
+	if (tty) {
+		for (;;) {
+			if (TTY_FLIPBUF_SIZE - tty->flip.count < sizeof(buf))
+				break;
+			n = hvc_get_chars(index + hvc_offset, buf, sizeof(buf));
+			if (n <= 0)
+				break;
+			for (i = 0; i < n; ++i) {
+#ifdef CONFIG_MAGIC_SYSRQ		/* Handle the SysRq Hack */
+				if (buf[i] == '\x0f') {	/* ^O -- should support a sequence */
+					sysrq_pressed = 1;
+					continue;
+				} else if (sysrq_pressed) {
+					handle_sysrq(buf[i], NULL, NULL, tty);
+					sysrq_pressed = 0;
+					continue;
+				}
+#endif
+				tty_insert_flip_char(tty, buf[i], 0);
+			}
+		}
+		if (tty->flip.count)
+			tty_schedule_flip(tty);
+
+		if (hp->do_wakeup) {
+			hp->do_wakeup = 0;
+			if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+			    && tty->ldisc.write_wakeup)
+				(tty->ldisc.write_wakeup)(tty);
+			wake_up_interruptible(&tty->write_wait);
+		}
+	}
+
+	spin_unlock_irqrestore(&hp->lock, flags);
+}
+
+int khvcd(void *unused)
+{
+	int i;
+
+	daemonize();
+	reparent_to_init();
+	strcpy(current->comm, "khvcd");
+	sigfillset(&current->blocked);
+
+	for (;;) {
+		for (i = 0; i < MAX_NR_HVC_CONSOLES; ++i)
+			hvc_poll(i);
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(TIMEOUT);
+	}
+}
+
+int __init hvc_init(void)
+{
+	int i;
+
+	memset(&hvc_driver, 0, sizeof(struct tty_driver));
+
+	hvc_driver.magic = TTY_DRIVER_MAGIC;
+	hvc_driver.driver_name = "hvc";
+	hvc_driver.name = "hvc/%d";
+	hvc_driver.major = HVC_MAJOR;
+	hvc_driver.minor_start = HVC_MINOR;
+	hvc_driver.num = hvc_count(&hvc_offset);
+	if (hvc_driver.num > MAX_NR_HVC_CONSOLES)
+		hvc_driver.num = MAX_NR_HVC_CONSOLES;
+	hvc_driver.type = TTY_DRIVER_TYPE_SYSTEM;
+	hvc_driver.init_termios = tty_std_termios;
+	hvc_driver.flags = TTY_DRIVER_REAL_RAW;
+	hvc_driver.refcount = &hvc_refcount;
+	hvc_driver.table = hvc_table;
+	hvc_driver.termios = hvc_termios;
+	hvc_driver.termios_locked = hvc_termios_locked;
+
+	hvc_driver.open = hvc_open;
+	hvc_driver.close = hvc_close;
+	hvc_driver.write = hvc_write;
+	hvc_driver.hangup = hvc_hangup;
+	hvc_driver.write_room = hvc_write_room;
+	hvc_driver.chars_in_buffer = hvc_chars_in_buffer;
+
+	for (i = 0; i < hvc_driver.num; i++) {
+		hvc_struct[i].lock = SPIN_LOCK_UNLOCKED;
+		hvc_struct[i].index = i;
+		tty_register_devfs(&hvc_driver, 0, hvc_driver.minor_start + i);
+	}
+
+	if (tty_register_driver(&hvc_driver))
+		panic("Couldn't register hvc console driver\n");
+
+	if (hvc_driver.num > 0)
+		kernel_thread(khvcd, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
+
+	return 0;
+}
+
+static void __exit hvc_exit(void)
+{
+}
+
+void hvc_console_print(struct console *co, const char *b, unsigned count)
+{
+	char c[16] __ALIGNED__;
+	unsigned i, n;
+	int r, donecr = 0;
+
+	i = n = 0;
+	while (count > 0 || i > 0) {
+		if (count > 0 && i < sizeof(c)) {
+			if (b[n] == '\n' && !donecr) {
+				c[i++] = '\r';
+				donecr = 1;
+			} else {
+				c[i++] = b[n++];
+				donecr = 0;
+				--count;
+			}
+		} else {
+			r = hvc_put_chars(co->index + hvc_offset, c, i);
+			if (r < 0) {
+				/* throw away chars on error */
+				i = 0;
+			} else if (r > 0) {
+				i -= r;
+				if (i > 0)
+					memmove(c, c+r, i);
+			}
+		}
+	}
+}
+
+static kdev_t hvc_console_device(struct console *c)
+{
+	return MKDEV(HVC_MAJOR, HVC_MINOR + c->index);
+}
+
+int hvc_wait_for_keypress(struct console *co)
+{
+	char c[16] __ALIGNED__;
+
+	while (hvc_get_chars(co->index, &c[0], 1) < 1)
+		;
+	return 0;
+}
+
+static int __init hvc_console_setup(struct console *co, char *options)
+{
+	if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES
+	    || co->index >= hvc_count(&hvc_offset))
+		return -1;
+	return 0;
+}
+
+struct console hvc_con_driver = {
+	name:		"hvc",
+	write:		hvc_console_print,
+	device:		hvc_console_device,
+	setup:		hvc_console_setup,
+	flags:		CON_PRINTBUFFER,
+	index:		-1,
+};
+
+int __init hvc_console_init(void)
+{
+	register_console(&hvc_con_driver);
+	return 0;
+}
+
+module_init(hvc_init);
+module_exit(hvc_exit);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)