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
- Lines: 365
- Date:
Thu Nov 28 15:53:12 2002
- Orig file:
linux-2.4.19/drivers/char/hvc_console.c
- Orig date:
Wed Dec 31 16:00:00 1969
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(¤t->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)