diff options
author | Amit Shah <amit.shah@redhat.com> | 2010-01-18 08:45:05 -0500 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2010-02-23 22:52:41 -0500 |
commit | 38edf58d73c28b082ec808aecdeb0ef2b92af049 (patch) | |
tree | a0235a87189cc48efc4fb61a1a2d70ce386891e1 /drivers/char/virtio_console.c | |
parent | 73954488b1cc74cf46d6b94b8d3175f45496bd32 (diff) |
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: Amit Shah <amit.shah@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/char/virtio_console.c')
-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 e52ee1151f5f..6bbf707f9e33 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c | |||
@@ -17,10 +17,28 @@ | |||
17 | */ | 17 | */ |
18 | #include <linux/err.h> | 18 | #include <linux/err.h> |
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/list.h> | ||
21 | #include <linux/spinlock.h> | ||
20 | #include <linux/virtio.h> | 22 | #include <linux/virtio.h> |
21 | #include <linux/virtio_console.h> | 23 | #include <linux/virtio_console.h> |
22 | #include "hvc_console.h" | 24 | #include "hvc_console.h" |
23 | 25 | ||
26 | /* | ||
27 | * This is a global struct for storing common data for all the devices | ||
28 | * this driver handles. | ||
29 | * | ||
30 | * Mainly, it has a linked list for all the consoles in one place so | ||
31 | * that callbacks from hvc for get_chars(), put_chars() work properly | ||
32 | * across multiple devices and multiple ports per device. | ||
33 | */ | ||
34 | struct ports_driver_data { | ||
35 | /* All the console devices handled by this driver */ | ||
36 | struct list_head consoles; | ||
37 | }; | ||
38 | static struct ports_driver_data pdrvdata; | ||
39 | |||
40 | DEFINE_SPINLOCK(pdrvdata_lock); | ||
41 | |||
24 | struct port_buffer { | 42 | struct port_buffer { |
25 | char *buf; | 43 | char *buf; |
26 | 44 | ||
@@ -40,8 +58,15 @@ struct port { | |||
40 | /* The current buffer from which data has to be fed to readers */ | 58 | /* The current buffer from which data has to be fed to readers */ |
41 | struct port_buffer *inbuf; | 59 | struct port_buffer *inbuf; |
42 | 60 | ||
61 | /* For console ports, hvc != NULL and these are valid. */ | ||
43 | /* The hvc device */ | 62 | /* The hvc device */ |
44 | struct hvc_struct *hvc; | 63 | struct hvc_struct *hvc; |
64 | |||
65 | /* We'll place all consoles in a list in the pdrvdata struct */ | ||
66 | struct list_head list; | ||
67 | |||
68 | /* Our vterm number. */ | ||
69 | u32 vtermno; | ||
45 | }; | 70 | }; |
46 | 71 | ||
47 | /* We have one port ready to go immediately, for a console. */ | 72 | /* We have one port ready to go immediately, for a console. */ |
@@ -50,6 +75,22 @@ static struct port console; | |||
50 | /* This is the very early arch-specified put chars function. */ | 75 | /* This is the very early arch-specified put chars function. */ |
51 | static int (*early_put_chars)(u32, const char *, int); | 76 | static int (*early_put_chars)(u32, const char *, int); |
52 | 77 | ||
78 | static struct port *find_port_by_vtermno(u32 vtermno) | ||
79 | { | ||
80 | struct port *port; | ||
81 | unsigned long flags; | ||
82 | |||
83 | spin_lock_irqsave(&pdrvdata_lock, flags); | ||
84 | list_for_each_entry(port, &pdrvdata.consoles, list) { | ||
85 | if (port->vtermno == vtermno) | ||
86 | goto out; | ||
87 | } | ||
88 | port = NULL; | ||
89 | out: | ||
90 | spin_unlock_irqrestore(&pdrvdata_lock, flags); | ||
91 | return port; | ||
92 | } | ||
93 | |||
53 | static void free_buf(struct port_buffer *buf) | 94 | static void free_buf(struct port_buffer *buf) |
54 | { | 95 | { |
55 | kfree(buf->buf); | 96 | kfree(buf->buf); |
@@ -120,14 +161,16 @@ static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf) | |||
120 | static int put_chars(u32 vtermno, const char *buf, int count) | 161 | static int put_chars(u32 vtermno, const char *buf, int count) |
121 | { | 162 | { |
122 | struct scatterlist sg[1]; | 163 | struct scatterlist sg[1]; |
123 | unsigned int len; | ||
124 | struct port *port; | 164 | struct port *port; |
165 | unsigned int len; | ||
166 | |||
167 | port = find_port_by_vtermno(vtermno); | ||
168 | if (!port) | ||
169 | return 0; | ||
125 | 170 | ||
126 | if (unlikely(early_put_chars)) | 171 | if (unlikely(early_put_chars)) |
127 | return early_put_chars(vtermno, buf, count); | 172 | return early_put_chars(vtermno, buf, count); |
128 | 173 | ||
129 | port = &console; | ||
130 | |||
131 | /* This is a convenient routine to initialize a single-elem sg list */ | 174 | /* This is a convenient routine to initialize a single-elem sg list */ |
132 | sg_init_one(sg, buf, count); | 175 | sg_init_one(sg, buf, count); |
133 | 176 | ||
@@ -155,7 +198,10 @@ static int get_chars(u32 vtermno, char *buf, int count) | |||
155 | { | 198 | { |
156 | struct port *port; | 199 | struct port *port; |
157 | 200 | ||
158 | port = &console; | 201 | |
202 | port = find_port_by_vtermno(vtermno); | ||
203 | if (!port) | ||
204 | return 0; | ||
159 | 205 | ||
160 | /* If we don't have an input queue yet, we can't get input. */ | 206 | /* If we don't have an input queue yet, we can't get input. */ |
161 | BUG_ON(!port->in_vq); | 207 | BUG_ON(!port->in_vq); |
@@ -201,14 +247,17 @@ static void virtcons_apply_config(struct virtio_device *dev) | |||
201 | } | 247 | } |
202 | } | 248 | } |
203 | 249 | ||
204 | /* | 250 | /* We set the configuration at this point, since we now have a tty */ |
205 | * we support only one console, the hvc struct is a global var We set | ||
206 | * the configuration at this point, since we now have a tty | ||
207 | */ | ||
208 | static int notifier_add_vio(struct hvc_struct *hp, int data) | 251 | static int notifier_add_vio(struct hvc_struct *hp, int data) |
209 | { | 252 | { |
253 | struct port *port; | ||
254 | |||
255 | port = find_port_by_vtermno(hp->vtermno); | ||
256 | if (!port) | ||
257 | return -EINVAL; | ||
258 | |||
210 | hp->irq_requested = 1; | 259 | hp->irq_requested = 1; |
211 | virtcons_apply_config(console.vdev); | 260 | virtcons_apply_config(port->vdev); |
212 | 261 | ||
213 | return 0; | 262 | return 0; |
214 | } | 263 | } |
@@ -313,6 +362,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
313 | goto free_vqs; | 362 | goto free_vqs; |
314 | } | 363 | } |
315 | 364 | ||
365 | /* Add to vtermno list. */ | ||
366 | spin_lock_irq(&pdrvdata_lock); | ||
367 | list_add(&port->list, &pdrvdata.consoles); | ||
368 | spin_unlock_irq(&pdrvdata_lock); | ||
369 | |||
316 | /* Register the input buffer the first time. */ | 370 | /* Register the input buffer the first time. */ |
317 | add_inbuf(port->in_vq, port->inbuf); | 371 | add_inbuf(port->in_vq, port->inbuf); |
318 | 372 | ||
@@ -349,6 +403,8 @@ static struct virtio_driver virtio_console = { | |||
349 | 403 | ||
350 | static int __init init(void) | 404 | static int __init init(void) |
351 | { | 405 | { |
406 | INIT_LIST_HEAD(&pdrvdata.consoles); | ||
407 | |||
352 | return register_virtio_driver(&virtio_console); | 408 | return register_virtio_driver(&virtio_console); |
353 | } | 409 | } |
354 | module_init(init); | 410 | module_init(init); |