X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fchar%2Fhvc_console.c;h=3c0af2cad6ee3c8851f0a5e0a96ebe75edd2c5d9;hb=9e1bf581d67d87a1d7fc0ea500729e3a03643a26;hp=9d7682c8250963a9a5800070f3d8bff6174e6bce;hpb=8d40237c730b8be87c1b80a5d96b9c603fefa829;p=linux-2.6.git diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 9d7682c82..3c0af2cad 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -45,7 +44,7 @@ #define HVC_MAJOR 229 #define HVC_MINOR 0 -#define TIMEOUT (10) +#define TIMEOUT ((HZ + 99) / 100) /* * Wait this long per iteration while trying to push buffered data to the @@ -221,7 +220,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) spin_unlock_irqrestore(&hp->lock, flags); tty->driver_data = NULL; kobject_put(kobjp); - printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc); } /* Force wakeup of the polling thread */ hvc_kick(); @@ -241,7 +239,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) /* * No driver_data means that this close was issued after a failed - * hvc_open by the tty layer's release_dev() function and we can just + * hvcs_open by the tty layer's release_dev() function and we can just * exit cleanly because the kobject reference wasn't made. */ if (!tty->driver_data) @@ -267,6 +265,13 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) */ tty_wait_until_sent(tty, HVC_CLOSE_WAIT); + /* + * Since the line disc doesn't block writes during tty close + * operations we'll set driver_data to NULL and then make sure + * to check tty->driver_data for NULL in hvc_write(). + */ + tty->driver_data = NULL; + if (irq != NO_IRQ) free_irq(irq, hp); @@ -288,21 +293,7 @@ static void hvc_hangup(struct tty_struct *tty) int temp_open_count; struct kobject *kobjp; - if (!hp) - return; - spin_lock_irqsave(&hp->lock, flags); - - /* - * The N_TTY line discipline has problems such that in a close vs - * open->hangup case this can be called after the final close so prevent - * that from happening for now. - */ - if (hp->count <= 0) { - spin_unlock_irqrestore(&hp->lock, flags); - return; - } - kobjp = &hp->kobj; temp_open_count = hp->count; hp->count = 0; @@ -344,6 +335,62 @@ static void hvc_push(struct hvc_struct *hp) hp->do_wakeup = 1; } +static inline int __hvc_write_user(struct hvc_struct *hp, + const unsigned char *buf, int count) +{ + char *tbuf, *p; + int tbsize, rsize, written = 0; + unsigned long flags; + + tbsize = min(count, (int)PAGE_SIZE); + if (!(tbuf = kmalloc(tbsize, GFP_KERNEL))) + return -ENOMEM; + + while ((rsize = count - written) > 0) { + int wsize; + if (rsize > tbsize) + rsize = tbsize; + + p = tbuf; + rsize -= copy_from_user(p, buf, rsize); + if (!rsize) { + if (written == 0) + written = -EFAULT; + break; + } + buf += rsize; + + spin_lock_irqsave(&hp->lock, flags); + + /* Push pending writes: make some room in buffer */ + if (hp->n_outbuf > 0) + hvc_push(hp); + + for (wsize = N_OUTBUF - hp->n_outbuf; rsize && wsize; + wsize = N_OUTBUF - hp->n_outbuf) { + if (wsize > rsize) + wsize = rsize; + memcpy(hp->outbuf + hp->n_outbuf, p, wsize); + hp->n_outbuf += wsize; + hvc_push(hp); + rsize -= wsize; + p += wsize; + written += wsize; + } + spin_unlock_irqrestore(&hp->lock, flags); + + if (rsize) + break; + + if (count < tbsize) + tbsize = count; + } + + kfree(tbuf); + + return written; +} + static inline int __hvc_write_kernel(struct hvc_struct *hp, const unsigned char *buf, int count) { @@ -370,7 +417,8 @@ static inline int __hvc_write_kernel(struct hvc_struct *hp, return written; } -static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count) +static int hvc_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) { struct hvc_struct *hp = tty->driver_data; int written; @@ -379,10 +427,10 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count if (!hp) return -EPIPE; - if (hp->count <= 0) - return -EIO; - - written = __hvc_write_kernel(hp, buf, count); + if (from_user) + written = __hvc_write_user(hp, buf, count); + else + written = __hvc_write_kernel(hp, buf, count); /* * Racy, but harmless, kick thread if there is still pending data. @@ -559,7 +607,7 @@ int khvcd(void *unused) if (poll_mask == 0) schedule(); else - msleep_interruptible(TIMEOUT); + schedule_timeout(TIMEOUT); } __set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); @@ -581,7 +629,7 @@ char hvc_driver_name[] = "hvc_console"; static struct vio_device_id hvc_driver_table[] __devinitdata= { {"serial", "hvterm1"}, - { NULL, } + { 0, } }; MODULE_DEVICE_TABLE(vio, hvc_driver_table);