diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/hvc_console.c | 86 | ||||
-rw-r--r-- | drivers/char/hvc_console.h | 12 | ||||
-rw-r--r-- | drivers/char/hvc_irq.c | 5 | ||||
-rw-r--r-- | drivers/char/hvc_iseries.c | 1 | ||||
-rw-r--r-- | drivers/char/hvc_vio.c | 1 | ||||
-rw-r--r-- | drivers/char/hvc_xen.c | 1 | ||||
-rw-r--r-- | drivers/char/virtio_console.c | 1 |
7 files changed, 92 insertions, 15 deletions
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index bf70450a49cc..5b819b12675a 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -161,7 +161,7 @@ static void hvc_console_print(struct console *co, const char *b, } } else { r = cons_ops[index]->put_chars(vtermnos[index], c, i); - if (r < 0) { + if (r <= 0) { /* throw away chars on error */ i = 0; } else if (r > 0) { @@ -374,6 +374,9 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) if (hp->ops->notifier_del) hp->ops->notifier_del(hp, hp->data); + /* cancel pending tty resize work */ + cancel_work_sync(&hp->tty_resize); + /* * Chain calls chars_in_buffer() and returns immediately if * there is no buffered data otherwise sleeps on a wait queue @@ -399,6 +402,9 @@ static void hvc_hangup(struct tty_struct *tty) if (!hp) return; + /* cancel pending tty resize work */ + cancel_work_sync(&hp->tty_resize); + spin_lock_irqsave(&hp->lock, flags); /* @@ -418,8 +424,8 @@ static void hvc_hangup(struct tty_struct *tty) spin_unlock_irqrestore(&hp->lock, flags); - if (hp->ops->notifier_del) - hp->ops->notifier_del(hp, hp->data); + if (hp->ops->notifier_hangup) + hp->ops->notifier_hangup(hp, hp->data); while(temp_open_count) { --temp_open_count; @@ -431,7 +437,7 @@ static void hvc_hangup(struct tty_struct *tty) * Push buffered characters whether they were just recently buffered or waiting * on a blocked hypervisor. Call this function with hp->lock held. */ -static void hvc_push(struct hvc_struct *hp) +static int hvc_push(struct hvc_struct *hp) { int n; @@ -439,7 +445,7 @@ static void hvc_push(struct hvc_struct *hp) if (n <= 0) { if (n == 0) { hp->do_wakeup = 1; - return; + return 0; } /* throw away output on error; this happens when there is no session connected to the vterm. */ @@ -450,6 +456,8 @@ static void hvc_push(struct hvc_struct *hp) memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf); else hp->do_wakeup = 1; + + return n; } static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count) @@ -492,6 +500,39 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count return written; } +/** + * hvc_set_winsz() - Resize the hvc tty terminal window. + * @work: work structure. + * + * The routine shall not be called within an atomic context because it + * might sleep. + * + * Locking: hp->lock + */ +static void hvc_set_winsz(struct work_struct *work) +{ + struct hvc_struct *hp; + unsigned long hvc_flags; + struct tty_struct *tty; + struct winsize ws; + + hp = container_of(work, struct hvc_struct, tty_resize); + if (!hp) + return; + + spin_lock_irqsave(&hp->lock, hvc_flags); + if (!hp->tty) { + spin_unlock_irqrestore(&hp->lock, hvc_flags); + return; + } + ws = hp->ws; + tty = tty_kref_get(hp->tty); + spin_unlock_irqrestore(&hp->lock, hvc_flags); + + tty_do_resize(tty, tty, &ws); + tty_kref_put(tty); +} + /* * This is actually a contract between the driver and the tty layer outlining * how much write room the driver can guarantee will be sent OR BUFFERED. This @@ -538,16 +579,20 @@ int hvc_poll(struct hvc_struct *hp) char buf[N_INBUF] __ALIGNED__; unsigned long flags; int read_total = 0; + int written_total = 0; spin_lock_irqsave(&hp->lock, flags); /* Push pending writes */ if (hp->n_outbuf > 0) - hvc_push(hp); + written_total = hvc_push(hp); /* Reschedule us if still some write pending */ - if (hp->n_outbuf > 0) + if (hp->n_outbuf > 0) { poll_mask |= HVC_POLL_WRITE; + /* If hvc_push() was not able to write, sleep a few msecs */ + timeout = (written_total) ? 0 : MIN_TIMEOUT; + } /* No tty attached, just skip */ tty = hp->tty; @@ -632,6 +677,24 @@ int hvc_poll(struct hvc_struct *hp) } EXPORT_SYMBOL_GPL(hvc_poll); +/** + * hvc_resize() - Update terminal window size information. + * @hp: HVC console pointer + * @ws: Terminal window size structure + * + * Stores the specified window size information in the hvc structure of @hp. + * The function schedule the tty resize update. + * + * Locking: Locking free; the function MUST be called holding hp->lock + */ +void hvc_resize(struct hvc_struct *hp, struct winsize ws) +{ + if ((hp->ws.ws_row != ws.ws_row) || (hp->ws.ws_col != ws.ws_col)) { + hp->ws = ws; + schedule_work(&hp->tty_resize); + } +} + /* * This kthread is either polling or interrupt driven. This is determined by * calling hvc_poll() who determines whether a console adapter support @@ -659,10 +722,6 @@ static int khvcd(void *unused) poll_mask |= HVC_POLL_READ; if (hvc_kicked) continue; - if (poll_mask & HVC_POLL_WRITE) { - yield(); - continue; - } set_current_state(TASK_INTERRUPTIBLE); if (!hvc_kicked) { if (poll_mask == 0) @@ -718,6 +777,7 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data, kref_init(&hp->kref); + INIT_WORK(&hp->tty_resize, hvc_set_winsz); spin_lock_init(&hp->lock); spin_lock(&hvc_structs_lock); @@ -743,7 +803,7 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data, } EXPORT_SYMBOL_GPL(hvc_alloc); -int __devexit hvc_remove(struct hvc_struct *hp) +int hvc_remove(struct hvc_struct *hp) { unsigned long flags; struct tty_struct *tty; @@ -796,7 +856,7 @@ static int hvc_init(void) drv->minor_start = HVC_MINOR; drv->type = TTY_DRIVER_TYPE_SYSTEM; drv->init_termios = tty_std_termios; - drv->flags = TTY_DRIVER_REAL_RAW; + drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; tty_set_operations(drv, &hvc_ops); /* Always start the kthread because there can be hotplug vty adapters diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 9790201718ae..8297dbc2e6ec 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -27,6 +27,7 @@ #ifndef HVC_CONSOLE_H #define HVC_CONSOLE_H #include <linux/kref.h> +#include <linux/tty.h> /* * This is the max number of console adapters that can/will be found as @@ -56,6 +57,8 @@ struct hvc_struct { struct hv_ops *ops; int irq_requested; int data; + struct winsize ws; + struct work_struct tty_resize; struct list_head next; struct kref kref; /* ref count & hvc_struct lifetime */ }; @@ -65,9 +68,10 @@ struct hv_ops { int (*get_chars)(uint32_t vtermno, char *buf, int count); int (*put_chars)(uint32_t vtermno, const char *buf, int count); - /* Callbacks for notification. Called in open and close */ + /* Callbacks for notification. Called in open, close and hangup */ int (*notifier_add)(struct hvc_struct *hp, int irq); void (*notifier_del)(struct hvc_struct *hp, int irq); + void (*notifier_hangup)(struct hvc_struct *hp, int irq); }; /* Register a vterm and a slot index for use as a console (console_init) */ @@ -77,15 +81,19 @@ extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops); extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data, struct hv_ops *ops, int outbuf_size); /* remove a vterm from hvc tty operation (module_exit or hotplug remove) */ -extern int __devexit hvc_remove(struct hvc_struct *hp); +extern int hvc_remove(struct hvc_struct *hp); /* data available */ int hvc_poll(struct hvc_struct *hp); void hvc_kick(void); +/* Resize hvc tty terminal window */ +extern void hvc_resize(struct hvc_struct *hp, struct winsize ws); + /* default notifier for irq based notification */ extern int notifier_add_irq(struct hvc_struct *hp, int data); extern void notifier_del_irq(struct hvc_struct *hp, int data); +extern void notifier_hangup_irq(struct hvc_struct *hp, int data); #if defined(CONFIG_XMON) && defined(CONFIG_SMP) diff --git a/drivers/char/hvc_irq.c b/drivers/char/hvc_irq.c index 73a59cdb8947..d09e5688d449 100644 --- a/drivers/char/hvc_irq.c +++ b/drivers/char/hvc_irq.c @@ -42,3 +42,8 @@ void notifier_del_irq(struct hvc_struct *hp, int irq) free_irq(irq, hp); hp->irq_requested = 0; } + +void notifier_hangup_irq(struct hvc_struct *hp, int irq) +{ + notifier_del_irq(hp, irq); +} diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c index b71c610fe5ae..b74a2f8ab908 100644 --- a/drivers/char/hvc_iseries.c +++ b/drivers/char/hvc_iseries.c @@ -202,6 +202,7 @@ static struct hv_ops hvc_get_put_ops = { .put_chars = put_chars, .notifier_add = notifier_add_irq, .notifier_del = notifier_del_irq, + .notifier_hangup = notifier_hangup_irq, }; static int __devinit hvc_vio_probe(struct vio_dev *vdev, diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index 93f3840c1682..019e0b58593d 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -82,6 +82,7 @@ static struct hv_ops hvc_get_put_ops = { .put_chars = hvc_put_chars, .notifier_add = notifier_add_irq, .notifier_del = notifier_del_irq, + .notifier_hangup = notifier_hangup_irq, }; static int __devinit hvc_vio_probe(struct vio_dev *vdev, diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c index 538ceea5e7df..eba999f8598d 100644 --- a/drivers/char/hvc_xen.c +++ b/drivers/char/hvc_xen.c @@ -102,6 +102,7 @@ static struct hv_ops hvc_ops = { .put_chars = write_console, .notifier_add = notifier_add_irq, .notifier_del = notifier_del_irq, + .notifier_hangup = notifier_hangup_irq, }; static int __init xen_init(void) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index d0f4eb6fdb7f..3fb0d2c88ba5 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -198,6 +198,7 @@ static int __devinit virtcons_probe(struct virtio_device *dev) virtio_cons.put_chars = put_chars; virtio_cons.notifier_add = notifier_add_vio; virtio_cons.notifier_del = notifier_del_vio; + virtio_cons.notifier_hangup = notifier_del_vio; /* The first argument of hvc_alloc() is the virtual console number, so * we use zero. The second argument is the parameter for the |