diff options
-rw-r--r-- | drivers/char/virtio_console.c | 392 | ||||
-rw-r--r-- | include/linux/virtio_console.h | 21 |
2 files changed, 357 insertions, 56 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index d01051060be3..a70f2b3a9e64 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c | |||
@@ -1,5 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation | 2 | * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation |
3 | * Copyright (C) 2009, 2010 Red Hat, Inc. | ||
3 | * | 4 | * |
4 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by | 6 | * it under the terms of the GNU General Public License as published by |
@@ -21,6 +22,7 @@ | |||
21 | #include <linux/spinlock.h> | 22 | #include <linux/spinlock.h> |
22 | #include <linux/virtio.h> | 23 | #include <linux/virtio.h> |
23 | #include <linux/virtio_console.h> | 24 | #include <linux/virtio_console.h> |
25 | #include <linux/workqueue.h> | ||
24 | #include "hvc_console.h" | 26 | #include "hvc_console.h" |
25 | 27 | ||
26 | /* | 28 | /* |
@@ -69,17 +71,6 @@ struct console { | |||
69 | u32 vtermno; | 71 | u32 vtermno; |
70 | }; | 72 | }; |
71 | 73 | ||
72 | /* | ||
73 | * This is a per-device struct that stores data common to all the | ||
74 | * ports for that device (vdev->priv). | ||
75 | */ | ||
76 | struct ports_device { | ||
77 | /* Array of per-port IO virtqueues */ | ||
78 | struct virtqueue **in_vqs, **out_vqs; | ||
79 | |||
80 | struct virtio_device *vdev; | ||
81 | }; | ||
82 | |||
83 | struct port_buffer { | 74 | struct port_buffer { |
84 | char *buf; | 75 | char *buf; |
85 | 76 | ||
@@ -92,8 +83,46 @@ struct port_buffer { | |||
92 | size_t offset; | 83 | size_t offset; |
93 | }; | 84 | }; |
94 | 85 | ||
86 | /* | ||
87 | * This is a per-device struct that stores data common to all the | ||
88 | * ports for that device (vdev->priv). | ||
89 | */ | ||
90 | struct ports_device { | ||
91 | /* | ||
92 | * Workqueue handlers where we process deferred work after | ||
93 | * notification | ||
94 | */ | ||
95 | struct work_struct control_work; | ||
96 | |||
97 | struct list_head ports; | ||
98 | |||
99 | /* To protect the list of ports */ | ||
100 | spinlock_t ports_lock; | ||
101 | |||
102 | /* To protect the vq operations for the control channel */ | ||
103 | spinlock_t cvq_lock; | ||
104 | |||
105 | /* The current config space is stored here */ | ||
106 | struct virtio_console_config config; | ||
107 | |||
108 | /* The virtio device we're associated with */ | ||
109 | struct virtio_device *vdev; | ||
110 | |||
111 | /* | ||
112 | * A couple of virtqueues for the control channel: one for | ||
113 | * guest->host transfers, one for host->guest transfers | ||
114 | */ | ||
115 | struct virtqueue *c_ivq, *c_ovq; | ||
116 | |||
117 | /* Array of per-port IO virtqueues */ | ||
118 | struct virtqueue **in_vqs, **out_vqs; | ||
119 | }; | ||
120 | |||
95 | /* This struct holds the per-port data */ | 121 | /* This struct holds the per-port data */ |
96 | struct port { | 122 | struct port { |
123 | /* Next port in the list, head is in the ports_device */ | ||
124 | struct list_head list; | ||
125 | |||
97 | /* Pointer to the parent virtio_console device */ | 126 | /* Pointer to the parent virtio_console device */ |
98 | struct ports_device *portdev; | 127 | struct ports_device *portdev; |
99 | 128 | ||
@@ -115,6 +144,9 @@ struct port { | |||
115 | * hooked up to an hvc console | 144 | * hooked up to an hvc console |
116 | */ | 145 | */ |
117 | struct console cons; | 146 | struct console cons; |
147 | |||
148 | /* The 'id' to identify the port with the Host */ | ||
149 | u32 id; | ||
118 | }; | 150 | }; |
119 | 151 | ||
120 | /* This is the very early arch-specified put chars function. */ | 152 | /* This is the very early arch-specified put chars function. */ |
@@ -139,25 +171,56 @@ out: | |||
139 | return port; | 171 | return port; |
140 | } | 172 | } |
141 | 173 | ||
174 | static struct port *find_port_by_id(struct ports_device *portdev, u32 id) | ||
175 | { | ||
176 | struct port *port; | ||
177 | unsigned long flags; | ||
178 | |||
179 | spin_lock_irqsave(&portdev->ports_lock, flags); | ||
180 | list_for_each_entry(port, &portdev->ports, list) | ||
181 | if (port->id == id) | ||
182 | goto out; | ||
183 | port = NULL; | ||
184 | out: | ||
185 | spin_unlock_irqrestore(&portdev->ports_lock, flags); | ||
186 | |||
187 | return port; | ||
188 | } | ||
189 | |||
142 | static struct port *find_port_by_vq(struct ports_device *portdev, | 190 | static struct port *find_port_by_vq(struct ports_device *portdev, |
143 | struct virtqueue *vq) | 191 | struct virtqueue *vq) |
144 | { | 192 | { |
145 | struct port *port; | 193 | struct port *port; |
146 | struct console *cons; | ||
147 | unsigned long flags; | 194 | unsigned long flags; |
148 | 195 | ||
149 | spin_lock_irqsave(&pdrvdata_lock, flags); | 196 | spin_lock_irqsave(&portdev->ports_lock, flags); |
150 | list_for_each_entry(cons, &pdrvdata.consoles, list) { | 197 | list_for_each_entry(port, &portdev->ports, list) |
151 | port = container_of(cons, struct port, cons); | ||
152 | if (port->in_vq == vq || port->out_vq == vq) | 198 | if (port->in_vq == vq || port->out_vq == vq) |
153 | goto out; | 199 | goto out; |
154 | } | ||
155 | port = NULL; | 200 | port = NULL; |
156 | out: | 201 | out: |
157 | spin_unlock_irqrestore(&pdrvdata_lock, flags); | 202 | spin_unlock_irqrestore(&portdev->ports_lock, flags); |
158 | return port; | 203 | return port; |
159 | } | 204 | } |
160 | 205 | ||
206 | static bool is_console_port(struct port *port) | ||
207 | { | ||
208 | if (port->cons.hvc) | ||
209 | return true; | ||
210 | return false; | ||
211 | } | ||
212 | |||
213 | static inline bool use_multiport(struct ports_device *portdev) | ||
214 | { | ||
215 | /* | ||
216 | * This condition can be true when put_chars is called from | ||
217 | * early_init | ||
218 | */ | ||
219 | if (!portdev->vdev) | ||
220 | return 0; | ||
221 | return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT); | ||
222 | } | ||
223 | |||
161 | static void free_buf(struct port_buffer *buf) | 224 | static void free_buf(struct port_buffer *buf) |
162 | { | 225 | { |
163 | kfree(buf->buf); | 226 | kfree(buf->buf); |
@@ -233,6 +296,32 @@ static bool port_has_data(struct port *port) | |||
233 | return ret; | 296 | return ret; |
234 | } | 297 | } |
235 | 298 | ||
299 | static ssize_t send_control_msg(struct port *port, unsigned int event, | ||
300 | unsigned int value) | ||
301 | { | ||
302 | struct scatterlist sg[1]; | ||
303 | struct virtio_console_control cpkt; | ||
304 | struct virtqueue *vq; | ||
305 | int len; | ||
306 | |||
307 | if (!use_multiport(port->portdev)) | ||
308 | return 0; | ||
309 | |||
310 | cpkt.id = port->id; | ||
311 | cpkt.event = event; | ||
312 | cpkt.value = value; | ||
313 | |||
314 | vq = port->portdev->c_ovq; | ||
315 | |||
316 | sg_init_one(sg, &cpkt, sizeof(cpkt)); | ||
317 | if (vq->vq_ops->add_buf(vq, sg, 1, 0, &cpkt) >= 0) { | ||
318 | vq->vq_ops->kick(vq); | ||
319 | while (!vq->vq_ops->get_buf(vq, &len)) | ||
320 | cpu_relax(); | ||
321 | } | ||
322 | return 0; | ||
323 | } | ||
324 | |||
236 | static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) | 325 | static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) |
237 | { | 326 | { |
238 | struct scatterlist sg[1]; | 327 | struct scatterlist sg[1]; |
@@ -387,24 +476,7 @@ static void notifier_del_vio(struct hvc_struct *hp, int data) | |||
387 | hp->irq_requested = 0; | 476 | hp->irq_requested = 0; |
388 | } | 477 | } |
389 | 478 | ||
390 | static void hvc_handle_input(struct virtqueue *vq) | 479 | /* The operations for console ports. */ |
391 | { | ||
392 | struct port *port; | ||
393 | unsigned long flags; | ||
394 | |||
395 | port = find_port_by_vq(vq->vdev->priv, vq); | ||
396 | if (!port) | ||
397 | return; | ||
398 | |||
399 | spin_lock_irqsave(&port->inbuf_lock, flags); | ||
400 | port->inbuf = get_inbuf(port); | ||
401 | spin_unlock_irqrestore(&port->inbuf_lock, flags); | ||
402 | |||
403 | if (hvc_poll(port->cons.hvc)) | ||
404 | hvc_kick(); | ||
405 | } | ||
406 | |||
407 | /* The operations for the console. */ | ||
408 | static const struct hv_ops hv_ops = { | 480 | static const struct hv_ops hv_ops = { |
409 | .get_chars = get_chars, | 481 | .get_chars = get_chars, |
410 | .put_chars = put_chars, | 482 | .put_chars = put_chars, |
@@ -428,7 +500,7 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) | |||
428 | return hvc_instantiate(0, 0, &hv_ops); | 500 | return hvc_instantiate(0, 0, &hv_ops); |
429 | } | 501 | } |
430 | 502 | ||
431 | int __devinit init_port_console(struct port *port) | 503 | int init_port_console(struct port *port) |
432 | { | 504 | { |
433 | int ret; | 505 | int ret; |
434 | 506 | ||
@@ -465,7 +537,122 @@ int __devinit init_port_console(struct port *port) | |||
465 | return 0; | 537 | return 0; |
466 | } | 538 | } |
467 | 539 | ||
468 | static int __devinit add_port(struct ports_device *portdev) | 540 | /* Any private messages that the Host and Guest want to share */ |
541 | static void handle_control_message(struct ports_device *portdev, | ||
542 | struct port_buffer *buf) | ||
543 | { | ||
544 | struct virtio_console_control *cpkt; | ||
545 | struct port *port; | ||
546 | |||
547 | cpkt = (struct virtio_console_control *)(buf->buf + buf->offset); | ||
548 | |||
549 | port = find_port_by_id(portdev, cpkt->id); | ||
550 | if (!port) { | ||
551 | /* No valid header at start of buffer. Drop it. */ | ||
552 | dev_dbg(&portdev->vdev->dev, | ||
553 | "Invalid index %u in control packet\n", cpkt->id); | ||
554 | return; | ||
555 | } | ||
556 | |||
557 | switch (cpkt->event) { | ||
558 | case VIRTIO_CONSOLE_CONSOLE_PORT: | ||
559 | if (!cpkt->value) | ||
560 | break; | ||
561 | if (is_console_port(port)) | ||
562 | break; | ||
563 | |||
564 | init_port_console(port); | ||
565 | /* | ||
566 | * Could remove the port here in case init fails - but | ||
567 | * have to notify the host first. | ||
568 | */ | ||
569 | break; | ||
570 | case VIRTIO_CONSOLE_RESIZE: | ||
571 | if (!is_console_port(port)) | ||
572 | break; | ||
573 | port->cons.hvc->irq_requested = 1; | ||
574 | resize_console(port); | ||
575 | break; | ||
576 | } | ||
577 | } | ||
578 | |||
579 | static void control_work_handler(struct work_struct *work) | ||
580 | { | ||
581 | struct ports_device *portdev; | ||
582 | struct virtqueue *vq; | ||
583 | struct port_buffer *buf; | ||
584 | unsigned int len; | ||
585 | |||
586 | portdev = container_of(work, struct ports_device, control_work); | ||
587 | vq = portdev->c_ivq; | ||
588 | |||
589 | spin_lock(&portdev->cvq_lock); | ||
590 | while ((buf = vq->vq_ops->get_buf(vq, &len))) { | ||
591 | spin_unlock(&portdev->cvq_lock); | ||
592 | |||
593 | buf->len = len; | ||
594 | buf->offset = 0; | ||
595 | |||
596 | handle_control_message(portdev, buf); | ||
597 | |||
598 | spin_lock(&portdev->cvq_lock); | ||
599 | if (add_inbuf(portdev->c_ivq, buf) < 0) { | ||
600 | dev_warn(&portdev->vdev->dev, | ||
601 | "Error adding buffer to queue\n"); | ||
602 | free_buf(buf); | ||
603 | } | ||
604 | } | ||
605 | spin_unlock(&portdev->cvq_lock); | ||
606 | } | ||
607 | |||
608 | static void in_intr(struct virtqueue *vq) | ||
609 | { | ||
610 | struct port *port; | ||
611 | unsigned long flags; | ||
612 | |||
613 | port = find_port_by_vq(vq->vdev->priv, vq); | ||
614 | if (!port) | ||
615 | return; | ||
616 | |||
617 | spin_lock_irqsave(&port->inbuf_lock, flags); | ||
618 | port->inbuf = get_inbuf(port); | ||
619 | |||
620 | spin_unlock_irqrestore(&port->inbuf_lock, flags); | ||
621 | |||
622 | if (is_console_port(port) && hvc_poll(port->cons.hvc)) | ||
623 | hvc_kick(); | ||
624 | } | ||
625 | |||
626 | static void control_intr(struct virtqueue *vq) | ||
627 | { | ||
628 | struct ports_device *portdev; | ||
629 | |||
630 | portdev = vq->vdev->priv; | ||
631 | schedule_work(&portdev->control_work); | ||
632 | } | ||
633 | |||
634 | static void fill_queue(struct virtqueue *vq, spinlock_t *lock) | ||
635 | { | ||
636 | struct port_buffer *buf; | ||
637 | int ret; | ||
638 | |||
639 | do { | ||
640 | buf = alloc_buf(PAGE_SIZE); | ||
641 | if (!buf) | ||
642 | break; | ||
643 | |||
644 | spin_lock_irq(lock); | ||
645 | ret = add_inbuf(vq, buf); | ||
646 | if (ret < 0) { | ||
647 | spin_unlock_irq(lock); | ||
648 | free_buf(buf); | ||
649 | break; | ||
650 | } | ||
651 | spin_unlock_irq(lock); | ||
652 | } while (ret > 0); | ||
653 | } | ||
654 | |||
655 | static int add_port(struct ports_device *portdev, u32 id) | ||
469 | { | 656 | { |
470 | struct port *port; | 657 | struct port *port; |
471 | struct port_buffer *inbuf; | 658 | struct port_buffer *inbuf; |
@@ -478,11 +665,13 @@ static int __devinit add_port(struct ports_device *portdev) | |||
478 | } | 665 | } |
479 | 666 | ||
480 | port->portdev = portdev; | 667 | port->portdev = portdev; |
668 | port->id = id; | ||
481 | 669 | ||
482 | port->inbuf = NULL; | 670 | port->inbuf = NULL; |
671 | port->cons.hvc = NULL; | ||
483 | 672 | ||
484 | port->in_vq = portdev->in_vqs[0]; | 673 | port->in_vq = portdev->in_vqs[port->id]; |
485 | port->out_vq = portdev->out_vqs[0]; | 674 | port->out_vq = portdev->out_vqs[port->id]; |
486 | 675 | ||
487 | spin_lock_init(&port->inbuf_lock); | 676 | spin_lock_init(&port->inbuf_lock); |
488 | 677 | ||
@@ -495,9 +684,25 @@ static int __devinit add_port(struct ports_device *portdev) | |||
495 | /* Register the input buffer the first time. */ | 684 | /* Register the input buffer the first time. */ |
496 | add_inbuf(port->in_vq, inbuf); | 685 | add_inbuf(port->in_vq, inbuf); |
497 | 686 | ||
498 | err = init_port_console(port); | 687 | /* |
499 | if (err) | 688 | * If we're not using multiport support, this has to be a console port |
500 | goto free_inbuf; | 689 | */ |
690 | if (!use_multiport(port->portdev)) { | ||
691 | err = init_port_console(port); | ||
692 | if (err) | ||
693 | goto free_inbuf; | ||
694 | } | ||
695 | |||
696 | spin_lock_irq(&portdev->ports_lock); | ||
697 | list_add_tail(&port->list, &port->portdev->ports); | ||
698 | spin_unlock_irq(&portdev->ports_lock); | ||
699 | |||
700 | /* | ||
701 | * Tell the Host we're set so that it can send us various | ||
702 | * configuration parameters for this port (eg, port name, | ||
703 | * caching, whether this is a console port, etc.) | ||
704 | */ | ||
705 | send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); | ||
501 | 706 | ||
502 | return 0; | 707 | return 0; |
503 | 708 | ||
@@ -514,12 +719,11 @@ static int init_vqs(struct ports_device *portdev) | |||
514 | vq_callback_t **io_callbacks; | 719 | vq_callback_t **io_callbacks; |
515 | char **io_names; | 720 | char **io_names; |
516 | struct virtqueue **vqs; | 721 | struct virtqueue **vqs; |
517 | u32 nr_ports, nr_queues; | 722 | u32 i, j, nr_ports, nr_queues; |
518 | int err; | 723 | int err; |
519 | 724 | ||
520 | /* We currently only have one port and two queues for that port */ | 725 | nr_ports = portdev->config.max_nr_ports; |
521 | nr_ports = 1; | 726 | nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2; |
522 | nr_queues = 2; | ||
523 | 727 | ||
524 | vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL); | 728 | vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL); |
525 | if (!vqs) { | 729 | if (!vqs) { |
@@ -549,11 +753,32 @@ static int init_vqs(struct ports_device *portdev) | |||
549 | goto free_invqs; | 753 | goto free_invqs; |
550 | } | 754 | } |
551 | 755 | ||
552 | io_callbacks[0] = hvc_handle_input; | 756 | /* |
553 | io_callbacks[1] = NULL; | 757 | * For backward compat (newer host but older guest), the host |
554 | io_names[0] = "input"; | 758 | * spawns a console port first and also inits the vqs for port |
555 | io_names[1] = "output"; | 759 | * 0 before others. |
556 | 760 | */ | |
761 | j = 0; | ||
762 | io_callbacks[j] = in_intr; | ||
763 | io_callbacks[j + 1] = NULL; | ||
764 | io_names[j] = "input"; | ||
765 | io_names[j + 1] = "output"; | ||
766 | j += 2; | ||
767 | |||
768 | if (use_multiport(portdev)) { | ||
769 | io_callbacks[j] = control_intr; | ||
770 | io_callbacks[j + 1] = NULL; | ||
771 | io_names[j] = "control-i"; | ||
772 | io_names[j + 1] = "control-o"; | ||
773 | |||
774 | for (i = 1; i < nr_ports; i++) { | ||
775 | j += 2; | ||
776 | io_callbacks[j] = in_intr; | ||
777 | io_callbacks[j + 1] = NULL; | ||
778 | io_names[j] = "input"; | ||
779 | io_names[j + 1] = "output"; | ||
780 | } | ||
781 | } | ||
557 | /* Find the queues. */ | 782 | /* Find the queues. */ |
558 | err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs, | 783 | err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs, |
559 | io_callbacks, | 784 | io_callbacks, |
@@ -561,9 +786,20 @@ static int init_vqs(struct ports_device *portdev) | |||
561 | if (err) | 786 | if (err) |
562 | goto free_outvqs; | 787 | goto free_outvqs; |
563 | 788 | ||
789 | j = 0; | ||
564 | portdev->in_vqs[0] = vqs[0]; | 790 | portdev->in_vqs[0] = vqs[0]; |
565 | portdev->out_vqs[0] = vqs[1]; | 791 | portdev->out_vqs[0] = vqs[1]; |
566 | 792 | j += 2; | |
793 | if (use_multiport(portdev)) { | ||
794 | portdev->c_ivq = vqs[j]; | ||
795 | portdev->c_ovq = vqs[j + 1]; | ||
796 | |||
797 | for (i = 1; i < nr_ports; i++) { | ||
798 | j += 2; | ||
799 | portdev->in_vqs[i] = vqs[j]; | ||
800 | portdev->out_vqs[i] = vqs[j + 1]; | ||
801 | } | ||
802 | } | ||
567 | kfree(io_callbacks); | 803 | kfree(io_callbacks); |
568 | kfree(io_names); | 804 | kfree(io_names); |
569 | kfree(vqs); | 805 | kfree(vqs); |
@@ -587,11 +823,17 @@ fail: | |||
587 | /* | 823 | /* |
588 | * Once we're further in boot, we get probed like any other virtio | 824 | * Once we're further in boot, we get probed like any other virtio |
589 | * device. | 825 | * device. |
826 | * | ||
827 | * If the host also supports multiple console ports, we check the | ||
828 | * config space to see how many ports the host has spawned. We | ||
829 | * initialize each port found. | ||
590 | */ | 830 | */ |
591 | static int __devinit virtcons_probe(struct virtio_device *vdev) | 831 | static int __devinit virtcons_probe(struct virtio_device *vdev) |
592 | { | 832 | { |
593 | struct ports_device *portdev; | 833 | struct ports_device *portdev; |
834 | u32 i; | ||
594 | int err; | 835 | int err; |
836 | bool multiport; | ||
595 | 837 | ||
596 | portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); | 838 | portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); |
597 | if (!portdev) { | 839 | if (!portdev) { |
@@ -603,16 +845,53 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
603 | portdev->vdev = vdev; | 845 | portdev->vdev = vdev; |
604 | vdev->priv = portdev; | 846 | vdev->priv = portdev; |
605 | 847 | ||
848 | multiport = false; | ||
849 | portdev->config.nr_ports = 1; | ||
850 | portdev->config.max_nr_ports = 1; | ||
851 | if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { | ||
852 | multiport = true; | ||
853 | vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT; | ||
854 | |||
855 | vdev->config->get(vdev, offsetof(struct virtio_console_config, | ||
856 | nr_ports), | ||
857 | &portdev->config.nr_ports, | ||
858 | sizeof(portdev->config.nr_ports)); | ||
859 | vdev->config->get(vdev, offsetof(struct virtio_console_config, | ||
860 | max_nr_ports), | ||
861 | &portdev->config.max_nr_ports, | ||
862 | sizeof(portdev->config.max_nr_ports)); | ||
863 | if (portdev->config.nr_ports > portdev->config.max_nr_ports) { | ||
864 | dev_warn(&vdev->dev, | ||
865 | "More ports (%u) specified than allowed (%u). Will init %u ports.", | ||
866 | portdev->config.nr_ports, | ||
867 | portdev->config.max_nr_ports, | ||
868 | portdev->config.max_nr_ports); | ||
869 | |||
870 | portdev->config.nr_ports = portdev->config.max_nr_ports; | ||
871 | } | ||
872 | } | ||
873 | |||
874 | /* Let the Host know we support multiple ports.*/ | ||
875 | vdev->config->finalize_features(vdev); | ||
876 | |||
606 | err = init_vqs(portdev); | 877 | err = init_vqs(portdev); |
607 | if (err < 0) { | 878 | if (err < 0) { |
608 | dev_err(&vdev->dev, "Error %d initializing vqs\n", err); | 879 | dev_err(&vdev->dev, "Error %d initializing vqs\n", err); |
609 | goto free; | 880 | goto free; |
610 | } | 881 | } |
611 | 882 | ||
612 | /* We only have one port. */ | 883 | spin_lock_init(&portdev->ports_lock); |
613 | err = add_port(portdev); | 884 | INIT_LIST_HEAD(&portdev->ports); |
614 | if (err) | 885 | |
615 | goto free_vqs; | 886 | if (multiport) { |
887 | spin_lock_init(&portdev->cvq_lock); | ||
888 | INIT_WORK(&portdev->control_work, &control_work_handler); | ||
889 | |||
890 | fill_queue(portdev->c_ivq, &portdev->cvq_lock); | ||
891 | } | ||
892 | |||
893 | for (i = 0; i < portdev->config.nr_ports; i++) | ||
894 | add_port(portdev, i); | ||
616 | 895 | ||
617 | /* Start using the new console output. */ | 896 | /* Start using the new console output. */ |
618 | early_put_chars = NULL; | 897 | early_put_chars = NULL; |
@@ -635,6 +914,7 @@ static struct virtio_device_id id_table[] = { | |||
635 | 914 | ||
636 | static unsigned int features[] = { | 915 | static unsigned int features[] = { |
637 | VIRTIO_CONSOLE_F_SIZE, | 916 | VIRTIO_CONSOLE_F_SIZE, |
917 | VIRTIO_CONSOLE_F_MULTIPORT, | ||
638 | }; | 918 | }; |
639 | 919 | ||
640 | static struct virtio_driver virtio_console = { | 920 | static struct virtio_driver virtio_console = { |
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index 9e0da40beae0..f4d183b5b493 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h | |||
@@ -6,18 +6,39 @@ | |||
6 | /* | 6 | /* |
7 | * This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so | 7 | * This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so |
8 | * anyone can use the definitions to implement compatible drivers/servers. | 8 | * anyone can use the definitions to implement compatible drivers/servers. |
9 | * | ||
10 | * Copyright (C) Red Hat, Inc., 2009, 2010 | ||
9 | */ | 11 | */ |
10 | 12 | ||
11 | /* Feature bits */ | 13 | /* Feature bits */ |
12 | #define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ | 14 | #define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ |
15 | #define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ | ||
13 | 16 | ||
14 | struct virtio_console_config { | 17 | struct virtio_console_config { |
15 | /* colums of the screens */ | 18 | /* colums of the screens */ |
16 | __u16 cols; | 19 | __u16 cols; |
17 | /* rows of the screens */ | 20 | /* rows of the screens */ |
18 | __u16 rows; | 21 | __u16 rows; |
22 | /* max. number of ports this device can hold */ | ||
23 | __u32 max_nr_ports; | ||
24 | /* number of ports added so far */ | ||
25 | __u32 nr_ports; | ||
19 | } __attribute__((packed)); | 26 | } __attribute__((packed)); |
20 | 27 | ||
28 | /* | ||
29 | * A message that's passed between the Host and the Guest for a | ||
30 | * particular port. | ||
31 | */ | ||
32 | struct virtio_console_control { | ||
33 | __u32 id; /* Port number */ | ||
34 | __u16 event; /* The kind of control event (see below) */ | ||
35 | __u16 value; /* Extra information for the key */ | ||
36 | }; | ||
37 | |||
38 | /* Some events for control messages */ | ||
39 | #define VIRTIO_CONSOLE_PORT_READY 0 | ||
40 | #define VIRTIO_CONSOLE_CONSOLE_PORT 1 | ||
41 | #define VIRTIO_CONSOLE_RESIZE 2 | ||
21 | 42 | ||
22 | #ifdef __KERNEL__ | 43 | #ifdef __KERNEL__ |
23 | int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)); | 44 | int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)); |