* 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;
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;
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
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;
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 */
}
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();
#define HVC_POLL_READ 0x00000001
#define HVC_POLL_WRITE 0x00000002
+#define HVC_POLL_QUICK 0x00000004
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;
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;
}
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 */
poll_mask |= HVC_POLL_READ;
if (hvc_kicked)
continue;
- if (poll_mask & HVC_POLL_WRITE) {
+ if (poll_mask & HVC_POLL_QUICK) {
yield();
continue;
}
* 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 */
* 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);