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)); |
