Commit 38edf58d authored by Amit Shah's avatar Amit Shah Committed by Rusty Russell

virtio: console: don't assume a single console port.

Keep a list of all ports being used as a console, and provide a lock
and a lookup function.  The hvc callbacks only give us a vterm number,
so we need to map this.
Signed-off-by: default avatarAmit Shah <amit.shah@redhat.com>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 73954488
...@@ -17,10 +17,28 @@ ...@@ -17,10 +17,28 @@
*/ */
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/virtio.h> #include <linux/virtio.h>
#include <linux/virtio_console.h> #include <linux/virtio_console.h>
#include "hvc_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 { struct port_buffer {
char *buf; char *buf;
...@@ -40,8 +58,15 @@ struct port { ...@@ -40,8 +58,15 @@ struct port {
/* The current buffer from which data has to be fed to readers */ /* The current buffer from which data has to be fed to readers */
struct port_buffer *inbuf; struct port_buffer *inbuf;
/* For console ports, hvc != NULL and these are valid. */
/* The hvc device */ /* The hvc device */
struct hvc_struct *hvc; 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. */ /* We have one port ready to go immediately, for a console. */
...@@ -50,6 +75,22 @@ static struct port console; ...@@ -50,6 +75,22 @@ static struct port console;
/* This is the very early arch-specified put chars function. */ /* This is the very early arch-specified put chars function. */
static int (*early_put_chars)(u32, const char *, int); 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) static void free_buf(struct port_buffer *buf)
{ {
kfree(buf->buf); kfree(buf->buf);
...@@ -120,14 +161,16 @@ static void add_inbuf(struct virtqueue *vq, struct port_buffer *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) static int put_chars(u32 vtermno, const char *buf, int count)
{ {
struct scatterlist sg[1]; struct scatterlist sg[1];
unsigned int len;
struct port *port; struct port *port;
unsigned int len;
port = find_port_by_vtermno(vtermno);
if (!port)
return 0;
if (unlikely(early_put_chars)) if (unlikely(early_put_chars))
return early_put_chars(vtermno, buf, count); return early_put_chars(vtermno, buf, count);
port = &console;
/* This is a convenient routine to initialize a single-elem sg list */ /* This is a convenient routine to initialize a single-elem sg list */
sg_init_one(sg, buf, count); sg_init_one(sg, buf, count);
...@@ -155,7 +198,10 @@ static int get_chars(u32 vtermno, char *buf, int count) ...@@ -155,7 +198,10 @@ static int get_chars(u32 vtermno, char *buf, int count)
{ {
struct port *port; 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. */ /* If we don't have an input queue yet, we can't get input. */
BUG_ON(!port->in_vq); BUG_ON(!port->in_vq);
...@@ -201,14 +247,17 @@ static void virtcons_apply_config(struct virtio_device *dev) ...@@ -201,14 +247,17 @@ static void virtcons_apply_config(struct virtio_device *dev)
} }
} }
/* /* We set the configuration at this point, since we now have a tty */
* 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
*/
static int notifier_add_vio(struct hvc_struct *hp, int data) 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; hp->irq_requested = 1;
virtcons_apply_config(console.vdev); virtcons_apply_config(port->vdev);
return 0; return 0;
} }
...@@ -313,6 +362,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) ...@@ -313,6 +362,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
goto free_vqs; 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. */ /* Register the input buffer the first time. */
add_inbuf(port->in_vq, port->inbuf); add_inbuf(port->in_vq, port->inbuf);
...@@ -349,6 +403,8 @@ static struct virtio_driver virtio_console = { ...@@ -349,6 +403,8 @@ static struct virtio_driver virtio_console = {
static int __init init(void) static int __init init(void)
{ {
INIT_LIST_HEAD(&pdrvdata.consoles);
return register_virtio_driver(&virtio_console); return register_virtio_driver(&virtio_console);
} }
module_init(init); module_init(init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment