aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/virtio_console.c
diff options
context:
space:
mode:
authorAmit Shah <amit.shah@redhat.com>2010-01-18 08:45:07 -0500
committerRusty Russell <rusty@rustcorp.com.au>2010-02-23 22:52:43 -0500
commit1c85bf35449196e74deb487961d2f90c98f7b7ff (patch)
tree6bc3a6f9407271ff9d8cd4e6d8b5fd99b33e3689 /drivers/char/virtio_console.c
parentd8a02bd58ab6da4495a2d1af74d980c217e9abcf (diff)
virtio: console: struct ports for multiple ports per device.
Rather than assume a single port, add a 'struct ports_device' which stores data related to all the ports for that device. Currently, there's only one port and is hooked up with hvc, but that will change. 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.c152
1 files changed, 89 insertions, 63 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 82f5180d4a37..8631d431fe7f 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -51,6 +51,15 @@ static struct ports_driver_data pdrvdata;
51 51
52DEFINE_SPINLOCK(pdrvdata_lock); 52DEFINE_SPINLOCK(pdrvdata_lock);
53 53
54/*
55 * This is a per-device struct that stores data common to all the
56 * ports for that device (vdev->priv).
57 */
58struct ports_device {
59 struct virtqueue *in_vq, *out_vq;
60 struct virtio_device *vdev;
61};
62
54struct port_buffer { 63struct port_buffer {
55 char *buf; 64 char *buf;
56 65
@@ -63,13 +72,17 @@ struct port_buffer {
63 size_t offset; 72 size_t offset;
64}; 73};
65 74
75/* This struct holds the per-port data */
66struct port { 76struct port {
67 struct virtqueue *in_vq, *out_vq; 77 /* Pointer to the parent virtio_console device */
68 struct virtio_device *vdev; 78 struct ports_device *portdev;
69 79
70 /* The current buffer from which data has to be fed to readers */ 80 /* The current buffer from which data has to be fed to readers */
71 struct port_buffer *inbuf; 81 struct port_buffer *inbuf;
72 82
83 /* The IO vqs for this port */
84 struct virtqueue *in_vq, *out_vq;
85
73 /* For console ports, hvc != NULL and these are valid. */ 86 /* For console ports, hvc != NULL and these are valid. */
74 /* The hvc device */ 87 /* The hvc device */
75 struct hvc_struct *hvc; 88 struct hvc_struct *hvc;
@@ -152,6 +165,7 @@ static void *get_inbuf(struct port *port)
152static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf) 165static void add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
153{ 166{
154 struct scatterlist sg[1]; 167 struct scatterlist sg[1];
168
155 sg_init_one(sg, buf->buf, buf->size); 169 sg_init_one(sg, buf->buf, buf->size);
156 170
157 if (vq->vq_ops->add_buf(vq, sg, 0, 1, buf) < 0) 171 if (vq->vq_ops->add_buf(vq, sg, 0, 1, buf) < 0)
@@ -171,6 +185,7 @@ static int put_chars(u32 vtermno, const char *buf, int count)
171{ 185{
172 struct scatterlist sg[1]; 186 struct scatterlist sg[1];
173 struct port *port; 187 struct port *port;
188 struct virtqueue *out_vq;
174 unsigned int len; 189 unsigned int len;
175 190
176 port = find_port_by_vtermno(vtermno); 191 port = find_port_by_vtermno(vtermno);
@@ -180,14 +195,15 @@ static int put_chars(u32 vtermno, const char *buf, int count)
180 if (unlikely(early_put_chars)) 195 if (unlikely(early_put_chars))
181 return early_put_chars(vtermno, buf, count); 196 return early_put_chars(vtermno, buf, count);
182 197
198 out_vq = port->out_vq;
183 /* This is a convenient routine to initialize a single-elem sg list */ 199 /* This is a convenient routine to initialize a single-elem sg list */
184 sg_init_one(sg, buf, count); 200 sg_init_one(sg, buf, count);
185 201
186 /* This shouldn't fail: if it does, we lose chars. */ 202 /* This shouldn't fail: if it does, we lose chars. */
187 if (port->out_vq->vq_ops->add_buf(port->out_vq, sg, 1, 0, port) >= 0) { 203 if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, port) >= 0) {
188 /* Tell Host to go! */ 204 /* Tell Host to go! */
189 port->out_vq->vq_ops->kick(port->out_vq); 205 out_vq->vq_ops->kick(out_vq);
190 while (!port->out_vq->vq_ops->get_buf(port->out_vq, &len)) 206 while (!out_vq->vq_ops->get_buf(out_vq, &len))
191 cpu_relax(); 207 cpu_relax();
192 } 208 }
193 209
@@ -207,7 +223,6 @@ static int get_chars(u32 vtermno, char *buf, int count)
207{ 223{
208 struct port *port; 224 struct port *port;
209 225
210
211 port = find_port_by_vtermno(vtermno); 226 port = find_port_by_vtermno(vtermno);
212 if (!port) 227 if (!port)
213 return 0; 228 return 0;
@@ -242,7 +257,6 @@ static int get_chars(u32 vtermno, char *buf, int count)
242 */ 257 */
243static void virtcons_apply_config(struct virtio_device *dev) 258static void virtcons_apply_config(struct virtio_device *dev)
244{ 259{
245 struct port *port = dev->priv;
246 struct winsize ws; 260 struct winsize ws;
247 261
248 if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) { 262 if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) {
@@ -252,7 +266,9 @@ static void virtcons_apply_config(struct virtio_device *dev)
252 dev->config->get(dev, 266 dev->config->get(dev,
253 offsetof(struct virtio_console_config, rows), 267 offsetof(struct virtio_console_config, rows),
254 &ws.ws_row, sizeof(u16)); 268 &ws.ws_row, sizeof(u16));
255 hvc_resize(port->hvc, ws); 269 /* This is the pre-multiport style: we use control messages
270 * these days which specify the port. So this means port 0. */
271 hvc_resize(find_port_by_vtermno(0)->hvc, ws);
256 } 272 }
257} 273}
258 274
@@ -266,7 +282,7 @@ static int notifier_add_vio(struct hvc_struct *hp, int data)
266 return -EINVAL; 282 return -EINVAL;
267 283
268 hp->irq_requested = 1; 284 hp->irq_requested = 1;
269 virtcons_apply_config(port->vdev); 285 virtcons_apply_config(port->portdev->vdev);
270 286
271 return 0; 287 return 0;
272} 288}
@@ -278,9 +294,13 @@ static void notifier_del_vio(struct hvc_struct *hp, int data)
278 294
279static void hvc_handle_input(struct virtqueue *vq) 295static void hvc_handle_input(struct virtqueue *vq)
280{ 296{
281 struct port *port = vq->vdev->priv; 297 struct port *port;
298 bool activity = false;
299
300 list_for_each_entry(port, &pdrvdata.consoles, list)
301 activity |= hvc_poll(port->hvc);
282 302
283 if (hvc_poll(port->hvc)) 303 if (activity)
284 hvc_kick(); 304 hvc_kick();
285} 305}
286 306
@@ -308,66 +328,26 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
308 return hvc_instantiate(0, 0, &hv_ops); 328 return hvc_instantiate(0, 0, &hv_ops);
309} 329}
310 330
311static struct port *__devinit add_port(u32 vtermno) 331static int __devinit add_port(struct ports_device *portdev)
312{
313 struct port *port;
314
315 port = kmalloc(sizeof(*port), GFP_KERNEL);
316 if (!port)
317 return NULL;
318
319 port->inbuf = alloc_buf(PAGE_SIZE);
320 if (!port->inbuf) {
321 kfree(port);
322 return NULL;
323 }
324 port->hvc = NULL;
325 port->vtermno = vtermno;
326 return port;
327}
328
329static void free_port(struct port *port)
330{ 332{
331 free_buf(port->inbuf);
332 kfree(port);
333}
334
335/*
336 * Once we're further in boot, we get probed like any other virtio
337 * device. At this stage we set up the output virtqueue.
338 *
339 * To set up and manage our virtual console, we call hvc_alloc().
340 * Since we never remove the console device we never need this pointer
341 * again.
342 *
343 * Finally we put our input buffer in the input queue, ready to
344 * receive.
345 */
346static int __devinit virtcons_probe(struct virtio_device *vdev)
347{
348 vq_callback_t *callbacks[] = { hvc_handle_input, NULL};
349 const char *names[] = { "input", "output" };
350 struct virtqueue *vqs[2];
351 struct port *port; 333 struct port *port;
352 int err; 334 int err;
353 335
354 port = add_port(pdrvdata.next_vtermno); 336 port = kmalloc(sizeof(*port), GFP_KERNEL);
355 if (!port) { 337 if (!port) {
356 err = -ENOMEM; 338 err = -ENOMEM;
357 goto fail; 339 goto fail;
358 } 340 }
359 341
360 /* Attach this port to this virtio_device, and vice-versa. */ 342 port->portdev = portdev;
361 port->vdev = vdev; 343 port->in_vq = portdev->in_vq;
362 vdev->priv = port; 344 port->out_vq = portdev->out_vq;
363 345
364 /* Find the queues. */ 346 port->inbuf = alloc_buf(PAGE_SIZE);
365 err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names); 347 if (!port->inbuf) {
366 if (err) 348 err = -ENOMEM;
367 goto free; 349 goto free_port;
368 350 }
369 port->in_vq = vqs[0];
370 port->out_vq = vqs[1];
371 351
372 /* 352 /*
373 * The first argument of hvc_alloc() is the virtual console 353 * The first argument of hvc_alloc() is the virtual console
@@ -380,10 +360,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
380 * pointers. The final argument is the output buffer size: we 360 * pointers. The final argument is the output buffer size: we
381 * can do any size, so we put PAGE_SIZE here. 361 * can do any size, so we put PAGE_SIZE here.
382 */ 362 */
363 port->vtermno = pdrvdata.next_vtermno;
383 port->hvc = hvc_alloc(port->vtermno, 0, &hv_ops, PAGE_SIZE); 364 port->hvc = hvc_alloc(port->vtermno, 0, &hv_ops, PAGE_SIZE);
384 if (IS_ERR(port->hvc)) { 365 if (IS_ERR(port->hvc)) {
385 err = PTR_ERR(port->hvc); 366 err = PTR_ERR(port->hvc);
386 goto free_vqs; 367 goto free_inbuf;
387 } 368 }
388 369
389 /* Add to vtermno list. */ 370 /* Add to vtermno list. */
@@ -395,6 +376,51 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
395 /* Register the input buffer the first time. */ 376 /* Register the input buffer the first time. */
396 add_inbuf(port->in_vq, port->inbuf); 377 add_inbuf(port->in_vq, port->inbuf);
397 378
379 return 0;
380
381free_inbuf:
382 free_buf(port->inbuf);
383free_port:
384 kfree(port);
385fail:
386 return err;
387}
388
389/*
390 * Once we're further in boot, we get probed like any other virtio
391 * device.
392 */
393static int __devinit virtcons_probe(struct virtio_device *vdev)
394{
395 vq_callback_t *callbacks[] = { hvc_handle_input, NULL};
396 const char *names[] = { "input", "output" };
397 struct virtqueue *vqs[2];
398 struct ports_device *portdev;
399 int err;
400
401 portdev = kmalloc(sizeof(*portdev), GFP_KERNEL);
402 if (!portdev) {
403 err = -ENOMEM;
404 goto fail;
405 }
406
407 /* Attach this portdev to this virtio_device, and vice-versa. */
408 portdev->vdev = vdev;
409 vdev->priv = portdev;
410
411 /* Find the queues. */
412 err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
413 if (err)
414 goto free;
415
416 portdev->in_vq = vqs[0];
417 portdev->out_vq = vqs[1];
418
419 /* We only have one port. */
420 err = add_port(portdev);
421 if (err)
422 goto free_vqs;
423
398 /* Start using the new console output. */ 424 /* Start using the new console output. */
399 early_put_chars = NULL; 425 early_put_chars = NULL;
400 return 0; 426 return 0;
@@ -402,7 +428,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
402free_vqs: 428free_vqs:
403 vdev->config->del_vqs(vdev); 429 vdev->config->del_vqs(vdev);
404free: 430free:
405 free_port(port); 431 kfree(portdev);
406fail: 432fail:
407 return err; 433 return err;
408} 434}