summaryrefslogtreecommitdiffstats
path: root/drivers/tty/hvc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/hvc')
-rw-r--r--drivers/tty/hvc/Kconfig2
-rw-r--r--drivers/tty/hvc/hvc_console.c66
-rw-r--r--drivers/tty/hvc/hvc_vio.c123
-rw-r--r--drivers/tty/hvc/hvcs.c82
-rw-r--r--drivers/tty/hvc/hvsi.c4
-rw-r--r--drivers/tty/hvc/hvsi_lib.c2
6 files changed, 176 insertions, 103 deletions
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index 0282a83f51fb..f47b734c6a7a 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -76,7 +76,7 @@ config HVC_XEN_FRONTEND
config HVC_UDBG
bool "udbg based fake hypervisor console"
- depends on PPC && EXPERIMENTAL
+ depends on PPC
select HVC_DRIVER
default n
help
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index 2d691eb7c40a..a5dec1ca1b82 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -245,6 +245,20 @@ static void hvc_port_destruct(struct tty_port *port)
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
@@ -275,12 +289,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,20 +309,33 @@ 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->port.lock, flags);
/* Check and then increment for fast path open. */
if (hp->port.count++ > 0) {
@@ -322,7 +345,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
} /* else count == 0 */
spin_unlock_irqrestore(&hp->port.lock, flags);
- tty->driver_data = hp;
tty_port_tty_set(&hp->port, tty);
if (hp->ops->notifier_add)
@@ -389,6 +411,11 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
hp->vtermno, hp->port.count);
spin_unlock_irqrestore(&hp->port.lock, flags);
}
+}
+
+static void hvc_cleanup(struct tty_struct *tty)
+{
+ struct hvc_struct *hp = tty->driver_data;
tty_port_put(&hp->port);
}
@@ -541,7 +568,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;
}
@@ -792,8 +819,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,
@@ -858,10 +887,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);
@@ -874,8 +908,12 @@ int hvc_remove(struct hvc_struct *hp)
tty = tty_port_tty_get(&hp->port);
spin_lock_irqsave(&hp->lock, flags);
- if (hp->index < MAX_NR_HVC_CONSOLES)
+ 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. */
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index ee307799271a..070c0ee68642 100644
--- a/drivers/tty/hvc/hvc_vio.c
+++ b/drivers/tty/hvc/hvc_vio.c
@@ -230,6 +230,69 @@ static const struct hv_ops hvterm_hvsi_ops = {
.tiocmset = hvterm_hvsi_tiocmset,
};
+static void udbg_hvc_putc(char c)
+{
+ int count = -1;
+
+ if (!hvterm_privs[0])
+ return;
+
+ if (c == '\n')
+ udbg_hvc_putc('\r');
+
+ do {
+ switch(hvterm_privs[0]->proto) {
+ case HV_PROTOCOL_RAW:
+ count = hvterm_raw_put_chars(0, &c, 1);
+ break;
+ case HV_PROTOCOL_HVSI:
+ count = hvterm_hvsi_put_chars(0, &c, 1);
+ break;
+ }
+ } while(count == 0);
+}
+
+static int udbg_hvc_getc_poll(void)
+{
+ int rc = 0;
+ char c;
+
+ if (!hvterm_privs[0])
+ return -1;
+
+ switch(hvterm_privs[0]->proto) {
+ case HV_PROTOCOL_RAW:
+ rc = hvterm_raw_get_chars(0, &c, 1);
+ break;
+ case HV_PROTOCOL_HVSI:
+ rc = hvterm_hvsi_get_chars(0, &c, 1);
+ break;
+ }
+ if (!rc)
+ return -1;
+ return c;
+}
+
+static int udbg_hvc_getc(void)
+{
+ int ch;
+
+ if (!hvterm_privs[0])
+ return -1;
+
+ for (;;) {
+ ch = udbg_hvc_getc_poll();
+ if (ch == -1) {
+ /* This shouldn't be needed...but... */
+ volatile unsigned long delay;
+ for (delay=0; delay < 2000000; delay++)
+ ;
+ } else {
+ return ch;
+ }
+ }
+}
+
static int __devinit hvc_vio_probe(struct vio_dev *vdev,
const struct vio_device_id *id)
{
@@ -289,6 +352,13 @@ static int __devinit hvc_vio_probe(struct vio_dev *vdev,
return PTR_ERR(hp);
dev_set_drvdata(&vdev->dev, hp);
+ /* register udbg if it's not there already for console 0 */
+ if (hp->index == 0 && !udbg_putc) {
+ udbg_putc = udbg_hvc_putc;
+ udbg_getc = udbg_hvc_getc;
+ udbg_getc_poll = udbg_hvc_getc_poll;
+ }
+
return 0;
}
@@ -331,59 +401,6 @@ static void __exit hvc_vio_exit(void)
}
module_exit(hvc_vio_exit);
-static void udbg_hvc_putc(char c)
-{
- int count = -1;
-
- if (c == '\n')
- udbg_hvc_putc('\r');
-
- do {
- switch(hvterm_priv0.proto) {
- case HV_PROTOCOL_RAW:
- count = hvterm_raw_put_chars(0, &c, 1);
- break;
- case HV_PROTOCOL_HVSI:
- count = hvterm_hvsi_put_chars(0, &c, 1);
- break;
- }
- } while(count == 0);
-}
-
-static int udbg_hvc_getc_poll(void)
-{
- int rc = 0;
- char c;
-
- switch(hvterm_priv0.proto) {
- case HV_PROTOCOL_RAW:
- rc = hvterm_raw_get_chars(0, &c, 1);
- break;
- case HV_PROTOCOL_HVSI:
- rc = hvterm_hvsi_get_chars(0, &c, 1);
- break;
- }
- if (!rc)
- return -1;
- return c;
-}
-
-static int udbg_hvc_getc(void)
-{
- int ch;
- for (;;) {
- ch = udbg_hvc_getc_poll();
- if (ch == -1) {
- /* This shouldn't be needed...but... */
- volatile unsigned long delay;
- for (delay=0; delay < 2000000; delay++)
- ;
- } else {
- return ch;
- }
- }
-}
-
void __init hvc_vio_init_early(void)
{
struct device_node *stdout_node;
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index d56788c83974..cab5c7adf8e8 100644
--- a/drivers/tty/hvc/hvcs.c
+++ b/drivers/tty/hvc/hvcs.c
@@ -1102,27 +1102,20 @@ static struct hvcs_struct *hvcs_get_by_index(int index)
return NULL;
}
-/*
- * This is invoked via the tty_open interface when a user app connects to the
- * /dev node.
- */
-static int hvcs_open(struct tty_struct *tty, struct file *filp)
+static int hvcs_install(struct tty_driver *driver, struct tty_struct *tty)
{
struct hvcs_struct *hvcsd;
- int rc, retval = 0;
- unsigned long flags;
- unsigned int irq;
struct vio_dev *vdev;
- unsigned long unit_address;
-
- if (tty->driver_data)
- goto fast_open;
+ unsigned long unit_address, flags;
+ unsigned int irq;
+ int retval;
/*
* Is there a vty-server that shares the same index?
* This function increments the kref index.
*/
- if (!(hvcsd = hvcs_get_by_index(tty->index))) {
+ hvcsd = hvcs_get_by_index(tty->index);
+ if (!hvcsd) {
printk(KERN_WARNING "HVCS: open failed, no device associated"
" with tty->index %d.\n", tty->index);
return -ENODEV;
@@ -1130,11 +1123,16 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp)
spin_lock_irqsave(&hvcsd->lock, flags);
- if (hvcsd->connected == 0)
- if ((retval = hvcs_partner_connect(hvcsd)))
- goto error_release;
+ if (hvcsd->connected == 0) {
+ retval = hvcs_partner_connect(hvcsd);
+ if (retval) {
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ printk(KERN_WARNING "HVCS: partner connect failed.\n");
+ goto err_put;
+ }
+ }
- hvcsd->port.count = 1;
+ hvcsd->port.count = 0;
hvcsd->port.tty = tty;
tty->driver_data = hvcsd;
@@ -1155,37 +1153,48 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp)
* This must be done outside of the spinlock because it requests irqs
* and will grab the spinlock and free the connection if it fails.
*/
- if (((rc = hvcs_enable_device(hvcsd, unit_address, irq, vdev)))) {
- tty_port_put(&hvcsd->port);
+ retval = hvcs_enable_device(hvcsd, unit_address, irq, vdev);
+ if (retval) {
printk(KERN_WARNING "HVCS: enable device failed.\n");
- return rc;
+ goto err_put;
}
- goto open_success;
+ retval = tty_port_install(&hvcsd->port, driver, tty);
+ if (retval)
+ goto err_irq;
-fast_open:
- hvcsd = tty->driver_data;
+ return 0;
+err_irq:
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ vio_disable_interrupts(hvcsd->vdev);
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ free_irq(irq, hvcsd);
+err_put:
+ tty_port_put(&hvcsd->port);
+
+ return retval;
+}
+
+/*
+ * This is invoked via the tty_open interface when a user app connects to the
+ * /dev node.
+ */
+static int hvcs_open(struct tty_struct *tty, struct file *filp)
+{
+ struct hvcs_struct *hvcsd = tty->driver_data;
+ unsigned long flags;
spin_lock_irqsave(&hvcsd->lock, flags);
- tty_port_get(&hvcsd->port);
hvcsd->port.count++;
hvcsd->todo_mask |= HVCS_SCHED_READ;
spin_unlock_irqrestore(&hvcsd->lock, flags);
-open_success:
hvcs_kick();
printk(KERN_INFO "HVCS: vty-server@%X connection opened.\n",
hvcsd->vdev->unit_address );
return 0;
-
-error_release:
- spin_unlock_irqrestore(&hvcsd->lock, flags);
- tty_port_put(&hvcsd->port);
-
- printk(KERN_WARNING "HVCS: partner connect failed.\n");
- return retval;
}
static void hvcs_close(struct tty_struct *tty, struct file *filp)
@@ -1236,7 +1245,6 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
tty->driver_data = NULL;
free_irq(irq, hvcsd);
- tty_port_put(&hvcsd->port);
return;
} else if (hvcsd->port.count < 0) {
printk(KERN_ERR "HVCS: vty-server@%X open_count: %d"
@@ -1245,6 +1253,12 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
}
spin_unlock_irqrestore(&hvcsd->lock, flags);
+}
+
+static void hvcs_cleanup(struct tty_struct * tty)
+{
+ struct hvcs_struct *hvcsd = tty->driver_data;
+
tty_port_put(&hvcsd->port);
}
@@ -1431,8 +1445,10 @@ static int hvcs_chars_in_buffer(struct tty_struct *tty)
}
static const struct tty_operations hvcs_ops = {
+ .install = hvcs_install,
.open = hvcs_open,
.close = hvcs_close,
+ .cleanup = hvcs_cleanup,
.hangup = hvcs_hangup,
.write = hvcs_write,
.write_room = hvcs_write_room,
diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c
index 6f5bc49c441f..5b95b4f28cf3 100644
--- a/drivers/tty/hvc/hvsi.c
+++ b/drivers/tty/hvc/hvsi.c
@@ -765,7 +765,7 @@ static void hvsi_flush_output(struct hvsi_struct *hp)
/* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */
cancel_delayed_work_sync(&hp->writer);
- flush_work_sync(&hp->handshaker);
+ flush_work(&hp->handshaker);
/*
* it's also possible that our timeout expired and hvsi_write_worker
@@ -1080,6 +1080,8 @@ static int __init hvsi_init(void)
struct hvsi_struct *hp = &hvsi_ports[i];
int ret = 1;
+ tty_port_link_device(&hp->port, hvsi_driver, i);
+
ret = request_irq(hp->virq, hvsi_interrupt, 0, "hvsi", hp);
if (ret)
printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n",
diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c
index 59c135dd5d20..3396eb9d57a3 100644
--- a/drivers/tty/hvc/hvsi_lib.c
+++ b/drivers/tty/hvc/hvsi_lib.c
@@ -400,7 +400,7 @@ void hvsilib_close(struct hvsi_priv *pv, struct hvc_struct *hp)
spin_unlock_irqrestore(&hp->lock, flags);
/* Clear our own DTR */
- if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL))
+ if (!pv->tty || (pv->tty->termios.c_cflag & HUPCL))
hvsilib_write_mctrl(pv, 0);
/* Tear down the connection */
OpenPOWER on IntegriCloud