diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/virtio_console.c | 74 |
1 files changed, 65 insertions, 9 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index e52ee1151f5..6bbf707f9e3 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -17,10 +17,28 @@ */ #include <linux/err.h> #include <linux/init.h> +#include <linux/list.h> +#include <linux/spinlock.h> #include <linux/virtio.h> #include <linux/virtio_console.h> #include "hvc_console.h" +/* + * This is a global struct for storing common data for all the devices + * this driver handles. + * + * Mainly, it has a linked list for all the consoles in one place so + * that callbacks from hvc for get_chars(), put_chars() work properly + * across multiple devices and multiple ports per device. + */ +struct ports_driver_data { + /* All the console devices handled by this driver */ + struct list_head consoles; +}; +static struct ports_driver_data pdrvdata; + +DEFINE_SPINLOCK(pdrvdata_lock); + struct port_buffer { char *buf; @@ -40,8 +58,15 @@ struct port { /* The current buffer from which data has to be fed to readers */ struct port_buffer *inbuf; + /* For console ports, hvc != NULL and these are valid. */ /* The hvc device */ struct hvc_struct *hvc; + + /* We'll place all consoles in a list in the pdrvdata struct */ + struct list_head list; + + /* Our vterm number. */ + u32 vtermno; }; /* We have one port ready to go immediately, for a console. */ @@ -50,6 +75,22 @@ static struct port console; /* This is the very early arch-specified put chars function. */ static int (*early_put_chars)(u32, const char *, int); +static struct port *find_port_by_vtermno(u32 vtermno) +{ + struct port *port; + unsigned long flags; + + spin_lock_irqsave(&pdrvdata_lock, flags); + list_for_each_entry(port, &pdrvdata.consoles, list) { + if (port->vtermno == vtermno) + goto out; + } + port = NULL; +out: + spin_unlock_irqrestore(&pdrvdata_lock, flags); + return port; +} + static void free_buf(struct port_buffer *buf) { kfree(buf->buf); @@ -120,14 +161,16 @@ static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf) static int put_chars(u32 vtermno, const char *buf, int count) { struct scatterlist sg[1]; - unsigned int len; struct port *port; + unsigned int len; + + port = find_port_by_vtermno(vtermno); + if (!port) + return 0; if (unlikely(early_put_chars)) return early_put_chars(vtermno, buf, count); - port = &console; - /* This is a convenient routine to initialize a single-elem sg list */ sg_init_one(sg, buf, count); @@ -155,7 +198,10 @@ static int get_chars(u32 vtermno, char *buf, int count) { struct port *port; - port = &console; + + port = find_port_by_vtermno(vtermno); + if (!port) + return 0; /* If we don't have an input queue yet, we can't get input. */ BUG_ON(!port->in_vq); @@ -201,14 +247,17 @@ static void virtcons_apply_config(struct virtio_device *dev) } } -/* - * we support only one console, the hvc struct is a global var We set - * the configuration at this point, since we now have a tty - */ +/* We set the configuration at this point, since we now have a tty */ static int notifier_add_vio(struct hvc_struct *hp, int data) { + struct port *port; + + port = find_port_by_vtermno(hp->vtermno); + if (!port) + return -EINVAL; + hp->irq_requested = 1; - virtcons_apply_config(console.vdev); + virtcons_apply_config(port->vdev); return 0; } @@ -313,6 +362,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) goto free_vqs; } + /* Add to vtermno list. */ + spin_lock_irq(&pdrvdata_lock); + list_add(&port->list, &pdrvdata.consoles); + spin_unlock_irq(&pdrvdata_lock); + /* Register the input buffer the first time. */ add_inbuf(port->in_vq, port->inbuf); @@ -349,6 +403,8 @@ static struct virtio_driver virtio_console = { static int __init init(void) { + INIT_LIST_HEAD(&pdrvdata.consoles); + return register_virtio_driver(&virtio_console); } module_init(init); |