linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / char / hvc_console.c
index 613d67f..d763a97 100644 (file)
@@ -22,6 +22,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
+#include <linux/config.h>
 #include <linux/console.h>
 #include <linux/cpumask.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <linux/delay.h>
-
 #include <asm/uaccess.h>
-
-#include "hvc_console.h"
+#include <asm/hvconsole.h>
 
 #define HVC_MAJOR      229
 #define HVC_MINOR      0
 #define HVC_CLOSE_WAIT (HZ/100) /* 1/10 of a second */
 
 /*
- * These sizes are most efficient for vio, because they are the
- * native transfer size. We could make them selectable in the
- * future to better deal with backends that want other buffer sizes.
+ * The Linux TTY code does not support dynamic addition of tty derived devices
+ * so we need to know how many tty devices we might need when space is allocated
+ * for the tty device.  Since this driver supports hotplug of vty adapters we
+ * need to make sure we have enough allocated.
  */
+#define HVC_ALLOC_TTY_ADAPTERS 8
+
 #define N_OUTBUF       16
 #define N_INBUF                16
 
-#define __ALIGNED__ __attribute__((__aligned__(sizeof(long))))
+#define __ALIGNED__    __attribute__((__aligned__(8)))
 
 static struct tty_driver *hvc_driver;
 static struct task_struct *hvc_task;
@@ -152,7 +154,7 @@ static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] =
 
 void hvc_console_print(struct console *co, const char *b, unsigned count)
 {
-       char c[N_OUTBUF] __ALIGNED__;
+       char c[16] __ALIGNED__;
        unsigned i = 0, n = 0;
        int r, donecr = 0, index = co->index;
 
@@ -346,7 +348,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
        spin_unlock_irqrestore(&hp->lock, flags);
        /* check error, fallback to non-irq */
        if (irq != NO_IRQ)
-               rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, "hvc_console", hp);
+               rc = request_irq(irq, hvc_handle_interrupt, SA_INTERRUPT, "hvc_console", hp);
 
        /*
         * If the request_irq() fails and we return an error.  The tty layer
@@ -471,10 +473,8 @@ static void hvc_push(struct hvc_struct *hp)
 
        n = hp->ops->put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf);
        if (n <= 0) {
-               if (n == 0) {
-                       hp->do_wakeup = 1;
+               if (n == 0)
                        return;
-               }
                /* throw away output on error; this happens when
                   there is no session connected to the vterm. */
                hp->n_outbuf = 0;
@@ -486,19 +486,12 @@ static void hvc_push(struct hvc_struct *hp)
                hp->do_wakeup = 1;
 }
 
-static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count)
+static inline int __hvc_write_kernel(struct hvc_struct *hp,
+                                  const unsigned char *buf, int count)
 {
-       struct hvc_struct *hp = tty->driver_data;
        unsigned long flags;
        int rsize, written = 0;
 
-       /* This write was probably executed during a tty close. */
-       if (!hp)
-               return -EPIPE;
-
-       if (hp->count <= 0)
-               return -EIO;
-
        spin_lock_irqsave(&hp->lock, flags);
 
        /* Push pending writes */
@@ -517,8 +510,26 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count
        }
        spin_unlock_irqrestore(&hp->lock, flags);
 
+       return written;
+}
+static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+       struct hvc_struct *hp = tty->driver_data;
+       int written;
+
+       /* This write was probably executed during a tty close. */
+       if (!hp)
+               return -EPIPE;
+
+       if (hp->count <= 0)
+               return -EIO;
+
+       written = __hvc_write_kernel(hp, buf, count);
+
        /*
         * Racy, but harmless, kick thread if there is still pending data.
+        * There really is nothing wrong with kicking the thread, even if there
+        * is no buffered data.
         */
        if (hp->n_outbuf)
                hvc_kick();
@@ -552,6 +563,7 @@ static int hvc_chars_in_buffer(struct tty_struct *tty)
 
 #define HVC_POLL_READ  0x00000001
 #define HVC_POLL_WRITE 0x00000002
+#define HVC_POLL_QUICK 0x00000004
 
 static int hvc_poll(struct hvc_struct *hp)
 {
@@ -566,7 +578,6 @@ static int hvc_poll(struct hvc_struct *hp)
        /* Push pending writes */
        if (hp->n_outbuf > 0)
                hvc_push(hp);
-
        /* Reschedule us if still some write pending */
        if (hp->n_outbuf > 0)
                poll_mask |= HVC_POLL_WRITE;
@@ -603,13 +614,6 @@ static int hvc_poll(struct hvc_struct *hp)
                                spin_unlock_irqrestore(&hp->lock, flags);
                                tty_hangup(tty);
                                spin_lock_irqsave(&hp->lock, flags);
-                       } else if ( n == -EAGAIN ) {
-                               /*
-                                * Some back-ends can only ensure a certain min
-                                * num of bytes read, which may be > 'count'.
-                                * Let the tty clear the flip buff to make room.
-                                */
-                               poll_mask |= HVC_POLL_READ;
                        }
                        break;
                }
@@ -631,7 +635,16 @@ static int hvc_poll(struct hvc_struct *hp)
                        tty_insert_flip_char(tty, buf[i], 0);
                }
 
+               /*
+                * Account for the total amount read in one loop, and if above
+                * 64 bytes, we do a quick schedule loop to let the tty grok
+                * the data and eventually throttle us.
+                */
                read_total += n;
+               if (read_total >= 64) {
+                       poll_mask |= HVC_POLL_QUICK;
+                       break;
+               }
        }
  throttled:
        /* Wakeup write queue if necessary */
@@ -680,7 +693,7 @@ int khvcd(void *unused)
                        poll_mask |= HVC_POLL_READ;
                if (hvc_kicked)
                        continue;
-               if (poll_mask & HVC_POLL_WRITE) {
+               if (poll_mask & HVC_POLL_QUICK) {
                        yield();
                        continue;
                }
@@ -755,8 +768,7 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
         * see if this vterm id matches one registered for console.
         */
        for (i=0; i < MAX_NR_HVC_CONSOLES; i++)
-               if (vtermnos[i] == hp->vtermno &&
-                   cons_ops[i] == hp->ops)
+               if (vtermnos[i] == hp->vtermno)
                        break;
 
        /* no matching slot, just use a counter */
@@ -812,37 +824,34 @@ EXPORT_SYMBOL(hvc_remove);
  * interfaces start to become available. */
 int __init hvc_init(void)
 {
-       struct tty_driver *drv;
-
        /* We need more than hvc_count adapters due to hotplug additions. */
-       drv = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS);
-       if (!drv)
+       hvc_driver = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS);
+       if (!hvc_driver)
                return -ENOMEM;
 
-       drv->owner = THIS_MODULE;
-       drv->driver_name = "hvc";
-       drv->name = "hvc";
-       drv->major = HVC_MAJOR;
-       drv->minor_start = HVC_MINOR;
-       drv->type = TTY_DRIVER_TYPE_SYSTEM;
-       drv->init_termios = tty_std_termios;
-       drv->flags = TTY_DRIVER_REAL_RAW;
-       tty_set_operations(drv, &hvc_ops);
+       hvc_driver->owner = THIS_MODULE;
+       hvc_driver->devfs_name = "hvc/";
+       hvc_driver->driver_name = "hvc";
+       hvc_driver->name = "hvc";
+       hvc_driver->major = HVC_MAJOR;
+       hvc_driver->minor_start = HVC_MINOR;
+       hvc_driver->type = TTY_DRIVER_TYPE_SYSTEM;
+       hvc_driver->init_termios = tty_std_termios;
+       hvc_driver->flags = TTY_DRIVER_REAL_RAW;
+       tty_set_operations(hvc_driver, &hvc_ops);
 
        /* Always start the kthread because there can be hotplug vty adapters
         * added later. */
        hvc_task = kthread_run(khvcd, NULL, "khvcd");
        if (IS_ERR(hvc_task)) {
                panic("Couldn't create kthread for console.\n");
-               put_tty_driver(drv);
+               put_tty_driver(hvc_driver);
                return -EIO;
        }
 
-       if (tty_register_driver(drv))
+       if (tty_register_driver(hvc_driver))
                panic("Couldn't register hvc console driver\n");
 
-       mb();
-       hvc_driver = drv;
        return 0;
 }
 module_init(hvc_init);