aboutsummaryrefslogtreecommitdiff
path: root/drivers/tty/hvc/hvc_console.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/hvc/hvc_console.c')
-rw-r--r--drivers/tty/hvc/hvc_console.c202
1 files changed, 124 insertions, 78 deletions
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index b6b2d18fa38..4fcec1d793a 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -31,6 +31,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/major.h>
+#include <linux/atomic.h>
#include <linux/sysrq.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
@@ -70,6 +71,9 @@ static struct task_struct *hvc_task;
/* Picks up late kicks after list walk but before schedule() */
static int hvc_kicked;
+/* hvc_init is triggered from hvc_alloc, i.e. only when actually used */
+static atomic_t hvc_needs_init __read_mostly = ATOMIC_INIT(-1);
+
static int hvc_init(void);
#ifdef CONFIG_MAGIC_SYSRQ
@@ -107,7 +111,7 @@ static struct hvc_struct *hvc_get_by_index(int index)
list_for_each_entry(hp, &hvc_structs, next) {
spin_lock_irqsave(&hp->lock, flags);
if (hp->index == index) {
- kref_get(&hp->kref);
+ tty_port_get(&hp->port);
spin_unlock_irqrestore(&hp->lock, flags);
spin_unlock(&hvc_structs_lock);
return hp;
@@ -186,7 +190,7 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index)
return hvc_driver;
}
-static int __init hvc_console_setup(struct console *co, char *options)
+static int hvc_console_setup(struct console *co, char *options)
{
if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES)
return -ENODEV;
@@ -229,9 +233,9 @@ static int __init hvc_console_init(void)
console_initcall(hvc_console_init);
/* callback when the kboject ref count reaches zero. */
-static void destroy_hvc_struct(struct kref *kref)
+static void hvc_port_destruct(struct tty_port *port)
{
- struct hvc_struct *hp = container_of(kref, struct hvc_struct, kref);
+ struct hvc_struct *hp = container_of(port, struct hvc_struct, port);
unsigned long flags;
spin_lock(&hvc_structs_lock);
@@ -245,6 +249,20 @@ static void destroy_hvc_struct(struct kref *kref)
kfree(hp);
}
+static void hvc_check_console(int index)
+{
+ /* Already enabled, bail out */
+ if (hvc_console.flags & CON_ENABLED)
+ return;
+
+ /* If this index is what the user requested, then register
+ * now (setup won't fail at this point). It's ok to just
+ * call register again if previously .setup failed.
+ */
+ if (index == hvc_console.index)
+ register_console(&hvc_console);
+}
+
/*
* hvc_instantiate() is an early console discovery method which locates
* consoles * prior to the vio subsystem discovering them. Hotplugged
@@ -264,7 +282,7 @@ int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops)
/* make sure no no tty has been registered in this index */
hp = hvc_get_by_index(index);
if (hp) {
- kref_put(&hp->kref, destroy_hvc_struct);
+ tty_port_put(&hp->port);
return -1;
}
@@ -275,12 +293,8 @@ int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops)
if (last_hvc < index)
last_hvc = index;
- /* if this index is what the user requested, then register
- * now (setup won't fail at this point). It's ok to just
- * call register again if previously .setup failed.
- */
- if (index == hvc_console.index)
- register_console(&hvc_console);
+ /* check if we need to re-register the kernel console */
+ hvc_check_console(index);
return 0;
}
@@ -299,34 +313,43 @@ static void hvc_unthrottle(struct tty_struct *tty)
hvc_kick();
}
+static int hvc_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ struct hvc_struct *hp;
+ int rc;
+
+ /* Auto increments kref reference if found. */
+ if (!(hp = hvc_get_by_index(tty->index)))
+ return -ENODEV;
+
+ tty->driver_data = hp;
+
+ rc = tty_port_install(&hp->port, driver, tty);
+ if (rc)
+ tty_port_put(&hp->port);
+ return rc;
+}
+
/*
* The TTY interface won't be used until after the vio layer has exposed the vty
* adapter to the kernel.
*/
static int hvc_open(struct tty_struct *tty, struct file * filp)
{
- struct hvc_struct *hp;
+ struct hvc_struct *hp = tty->driver_data;
unsigned long flags;
int rc = 0;
- /* Auto increments kref reference if found. */
- if (!(hp = hvc_get_by_index(tty->index)))
- return -ENODEV;
-
- spin_lock_irqsave(&hp->lock, flags);
+ spin_lock_irqsave(&hp->port.lock, flags);
/* Check and then increment for fast path open. */
- if (hp->count++ > 0) {
- tty_kref_get(tty);
- spin_unlock_irqrestore(&hp->lock, flags);
+ if (hp->port.count++ > 0) {
+ spin_unlock_irqrestore(&hp->port.lock, flags);
hvc_kick();
return 0;
} /* else count == 0 */
+ spin_unlock_irqrestore(&hp->port.lock, flags);
- tty->driver_data = hp;
-
- hp->tty = tty_kref_get(tty);
-
- spin_unlock_irqrestore(&hp->lock, flags);
+ tty_port_tty_set(&hp->port, tty);
if (hp->ops->notifier_add)
rc = hp->ops->notifier_add(hp, hp->data);
@@ -338,14 +361,16 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
* tty fields and return the kref reference.
*/
if (rc) {
- spin_lock_irqsave(&hp->lock, flags);
- hp->tty = NULL;
- spin_unlock_irqrestore(&hp->lock, flags);
- tty_kref_put(tty);
+ tty_port_tty_set(&hp->port, NULL);
tty->driver_data = NULL;
- kref_put(&hp->kref, destroy_hvc_struct);
+ tty_port_put(&hp->port);
printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc);
- }
+ } else
+ /* We are ready... raise DTR/RTS */
+ if (C_BAUD(tty))
+ if (hp->ops->dtr_rts)
+ hp->ops->dtr_rts(hp, 1);
+
/* Force wakeup of the polling thread */
hvc_kick();
@@ -370,12 +395,16 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
hp = tty->driver_data;
- spin_lock_irqsave(&hp->lock, flags);
+ spin_lock_irqsave(&hp->port.lock, flags);
- if (--hp->count == 0) {
+ if (--hp->port.count == 0) {
+ spin_unlock_irqrestore(&hp->port.lock, flags);
/* We are done with the tty pointer now. */
- hp->tty = NULL;
- spin_unlock_irqrestore(&hp->lock, flags);
+ tty_port_tty_set(&hp->port, NULL);
+
+ if (C_HUPCL(tty))
+ if (hp->ops->dtr_rts)
+ hp->ops->dtr_rts(hp, 0);
if (hp->ops->notifier_del)
hp->ops->notifier_del(hp, hp->data);
@@ -390,21 +419,24 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
*/
tty_wait_until_sent_from_close(tty, HVC_CLOSE_WAIT);
} else {
- if (hp->count < 0)
+ if (hp->port.count < 0)
printk(KERN_ERR "hvc_close %X: oops, count is %d\n",
- hp->vtermno, hp->count);
- spin_unlock_irqrestore(&hp->lock, flags);
+ hp->vtermno, hp->port.count);
+ spin_unlock_irqrestore(&hp->port.lock, flags);
}
+}
- tty_kref_put(tty);
- kref_put(&hp->kref, destroy_hvc_struct);
+static void hvc_cleanup(struct tty_struct *tty)
+{
+ struct hvc_struct *hp = tty->driver_data;
+
+ tty_port_put(&hp->port);
}
static void hvc_hangup(struct tty_struct *tty)
{
struct hvc_struct *hp = tty->driver_data;
unsigned long flags;
- int temp_open_count;
if (!hp)
return;
@@ -412,33 +444,26 @@ static void hvc_hangup(struct tty_struct *tty)
/* cancel pending tty resize work */
cancel_work_sync(&hp->tty_resize);
- spin_lock_irqsave(&hp->lock, flags);
+ spin_lock_irqsave(&hp->port.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);
+ if (hp->port.count <= 0) {
+ spin_unlock_irqrestore(&hp->port.lock, flags);
return;
}
- temp_open_count = hp->count;
- hp->count = 0;
- hp->n_outbuf = 0;
- hp->tty = NULL;
+ hp->port.count = 0;
+ spin_unlock_irqrestore(&hp->port.lock, flags);
+ tty_port_tty_set(&hp->port, NULL);
- spin_unlock_irqrestore(&hp->lock, flags);
+ hp->n_outbuf = 0;
if (hp->ops->notifier_hangup)
hp->ops->notifier_hangup(hp, hp->data);
-
- while(temp_open_count) {
- --temp_open_count;
- tty_kref_put(tty);
- kref_put(&hp->kref, destroy_hvc_struct);
- }
}
/*
@@ -478,7 +503,8 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count
if (!hp)
return -EPIPE;
- if (hp->count <= 0)
+ /* FIXME what's this (unprotected) check for? */
+ if (hp->port.count <= 0)
return -EIO;
spin_lock_irqsave(&hp->lock, flags);
@@ -526,13 +552,12 @@ static void hvc_set_winsz(struct work_struct *work)
hp = container_of(work, struct hvc_struct, tty_resize);
- spin_lock_irqsave(&hp->lock, hvc_flags);
- if (!hp->tty) {
- spin_unlock_irqrestore(&hp->lock, hvc_flags);
+ tty = tty_port_tty_get(&hp->port);
+ if (!tty)
return;
- }
- ws = hp->ws;
- tty = tty_kref_get(hp->tty);
+
+ spin_lock_irqsave(&hp->lock, hvc_flags);
+ ws = hp->ws;
spin_unlock_irqrestore(&hp->lock, hvc_flags);
tty_do_resize(tty, &ws);
@@ -549,7 +574,7 @@ static int hvc_write_room(struct tty_struct *tty)
struct hvc_struct *hp = tty->driver_data;
if (!hp)
- return -1;
+ return 0;
return hp->outbuf_size - hp->n_outbuf;
}
@@ -601,7 +626,7 @@ int hvc_poll(struct hvc_struct *hp)
}
/* No tty attached, just skip */
- tty = tty_kref_get(hp->tty);
+ tty = tty_port_tty_get(&hp->port);
if (tty == NULL)
goto bail;
@@ -617,7 +642,7 @@ int hvc_poll(struct hvc_struct *hp)
/* Read data if any */
for (;;) {
- int count = tty_buffer_request_room(tty, N_INBUF);
+ int count = tty_buffer_request_room(&hp->port, N_INBUF);
/* If flip is full, just reschedule a later read */
if (count == 0) {
@@ -660,7 +685,7 @@ int hvc_poll(struct hvc_struct *hp)
}
}
#endif /* CONFIG_MAGIC_SYSRQ */
- tty_insert_flip_char(tty, buf[i], 0);
+ tty_insert_flip_char(&hp->port, buf[i], 0);
}
read_total += n;
@@ -679,10 +704,9 @@ int hvc_poll(struct hvc_struct *hp)
a minimum for performance. */
timeout = MIN_TIMEOUT;
- tty_flip_buffer_push(tty);
+ tty_flip_buffer_push(&hp->port);
}
- if (tty)
- tty_kref_put(tty);
+ tty_kref_put(tty);
return poll_mask;
}
@@ -736,10 +760,17 @@ static int khvcd(void *unused)
if (poll_mask == 0)
schedule();
else {
+ unsigned long j_timeout;
+
if (timeout < MAX_TIMEOUT)
timeout += (timeout >> 6) + 1;
- msleep_interruptible(timeout);
+ /*
+ * We don't use msleep_interruptible otherwise
+ * "kick" will fail to wake us up
+ */
+ j_timeout = msecs_to_jiffies(timeout) + 1;
+ schedule_timeout_interruptible(j_timeout);
}
}
__set_current_state(TASK_RUNNING);
@@ -768,7 +799,7 @@ static int hvc_tiocmset(struct tty_struct *tty,
}
#ifdef CONFIG_CONSOLE_POLL
-int hvc_poll_init(struct tty_driver *driver, int line, char *options)
+static int hvc_poll_init(struct tty_driver *driver, int line, char *options)
{
return 0;
}
@@ -801,8 +832,10 @@ static void hvc_poll_put_char(struct tty_driver *driver, int line, char ch)
#endif
static const struct tty_operations hvc_ops = {
+ .install = hvc_install,
.open = hvc_open,
.close = hvc_close,
+ .cleanup = hvc_cleanup,
.write = hvc_write,
.hangup = hvc_hangup,
.unthrottle = hvc_unthrottle,
@@ -817,6 +850,10 @@ static const struct tty_operations hvc_ops = {
#endif
};
+static const struct tty_port_operations hvc_port_ops = {
+ .destruct = hvc_port_destruct,
+};
+
struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
const struct hv_ops *ops,
int outbuf_size)
@@ -825,7 +862,7 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
int i;
/* We wait until a driver actually comes along */
- if (!hvc_driver) {
+ if (atomic_inc_not_zero(&hvc_needs_init)) {
int err = hvc_init();
if (err)
return ERR_PTR(err);
@@ -842,7 +879,8 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
hp->outbuf_size = outbuf_size;
hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))];
- kref_init(&hp->kref);
+ tty_port_init(&hp->port);
+ hp->port.ops = &hvc_port_ops;
INIT_WORK(&hp->tty_resize, hvc_set_winsz);
spin_lock_init(&hp->lock);
@@ -862,10 +900,15 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
i = ++last_hvc;
hp->index = i;
+ cons_ops[i] = ops;
+ vtermnos[i] = vtermno;
list_add_tail(&(hp->next), &hvc_structs);
spin_unlock(&hvc_structs_lock);
+ /* check if we need to re-register the kernel console */
+ hvc_check_console(i);
+
return hp;
}
EXPORT_SYMBOL_GPL(hvc_alloc);
@@ -875,11 +918,15 @@ int hvc_remove(struct hvc_struct *hp)
unsigned long flags;
struct tty_struct *tty;
- spin_lock_irqsave(&hp->lock, flags);
- tty = tty_kref_get(hp->tty);
+ tty = tty_port_tty_get(&hp->port);
- if (hp->index < MAX_NR_HVC_CONSOLES)
+ spin_lock_irqsave(&hp->lock, flags);
+ if (hp->index < MAX_NR_HVC_CONSOLES) {
+ console_lock();
vtermnos[hp->index] = -1;
+ cons_ops[hp->index] = NULL;
+ console_unlock();
+ }
/* Don't whack hp->irq because tty_hangup() will need to free the irq. */
@@ -891,7 +938,7 @@ int hvc_remove(struct hvc_struct *hp)
* kref cause it to be removed, which will probably be the tty_vhangup
* below.
*/
- kref_put(&hp->kref, destroy_hvc_struct);
+ tty_port_put(&hp->port);
/*
* This function call will auto chain call hvc_hangup.
@@ -917,7 +964,6 @@ static int hvc_init(void)
goto out;
}
- drv->owner = THIS_MODULE;
drv->driver_name = "hvc";
drv->name = "hvc";
drv->major = HVC_MAJOR;