diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/char/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/char/virtio_console.c | 81 |
2 files changed, 82 insertions, 7 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index e023682be2c4..3141dd3b6e53 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig | |||
| @@ -666,6 +666,14 @@ config VIRTIO_CONSOLE | |||
| 666 | help | 666 | help |
| 667 | Virtio console for use with lguest and other hypervisors. | 667 | Virtio console for use with lguest and other hypervisors. |
| 668 | 668 | ||
| 669 | Also serves as a general-purpose serial device for data | ||
| 670 | transfer between the guest and host. Character devices at | ||
| 671 | /dev/vportNpn will be created when corresponding ports are | ||
| 672 | found, where N is the device number and n is the port number | ||
| 673 | within that device. If specified by the host, a sysfs | ||
| 674 | attribute called 'name' will be populated with a name for | ||
| 675 | the port which can be used by udev scripts to create a | ||
| 676 | symlink to the device. | ||
| 669 | 677 | ||
| 670 | config HVCS | 678 | config HVCS |
| 671 | tristate "IBM Hypervisor Virtual Console Server support" | 679 | tristate "IBM Hypervisor Virtual Console Server support" |
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 8e447e1e12bc..64ef476d6557 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c | |||
| @@ -16,6 +16,8 @@ | |||
| 16 | * along with this program; if not, write to the Free Software | 16 | * along with this program; if not, write to the Free Software |
| 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 18 | */ | 18 | */ |
| 19 | #include <linux/cdev.h> | ||
| 20 | #include <linux/device.h> | ||
| 19 | #include <linux/err.h> | 21 | #include <linux/err.h> |
| 20 | #include <linux/init.h> | 22 | #include <linux/init.h> |
| 21 | #include <linux/list.h> | 23 | #include <linux/list.h> |
| @@ -34,6 +36,12 @@ | |||
| 34 | * across multiple devices and multiple ports per device. | 36 | * across multiple devices and multiple ports per device. |
| 35 | */ | 37 | */ |
| 36 | struct ports_driver_data { | 38 | struct ports_driver_data { |
| 39 | /* Used for registering chardevs */ | ||
| 40 | struct class *class; | ||
| 41 | |||
| 42 | /* Number of devices this driver is handling */ | ||
| 43 | unsigned int index; | ||
| 44 | |||
| 37 | /* | 45 | /* |
| 38 | * This is used to keep track of the number of hvc consoles | 46 | * This is used to keep track of the number of hvc consoles |
| 39 | * spawned by this driver. This number is given as the first | 47 | * spawned by this driver. This number is given as the first |
| @@ -116,6 +124,12 @@ struct ports_device { | |||
| 116 | 124 | ||
| 117 | /* Array of per-port IO virtqueues */ | 125 | /* Array of per-port IO virtqueues */ |
| 118 | struct virtqueue **in_vqs, **out_vqs; | 126 | struct virtqueue **in_vqs, **out_vqs; |
| 127 | |||
| 128 | /* Used for numbering devices for sysfs and debugfs */ | ||
| 129 | unsigned int drv_index; | ||
| 130 | |||
| 131 | /* Major number for this device. Ports will be created as minors. */ | ||
| 132 | int chr_major; | ||
| 119 | }; | 133 | }; |
| 120 | 134 | ||
| 121 | /* This struct holds the per-port data */ | 135 | /* This struct holds the per-port data */ |
| @@ -145,6 +159,10 @@ struct port { | |||
| 145 | */ | 159 | */ |
| 146 | struct console cons; | 160 | struct console cons; |
| 147 | 161 | ||
| 162 | /* Each port associates with a separate char device */ | ||
| 163 | struct cdev cdev; | ||
| 164 | struct device *dev; | ||
| 165 | |||
| 148 | /* The 'id' to identify the port with the Host */ | 166 | /* The 'id' to identify the port with the Host */ |
| 149 | u32 id; | 167 | u32 id; |
| 150 | }; | 168 | }; |
| @@ -391,7 +409,7 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, | |||
| 391 | port->inbuf = NULL; | 409 | port->inbuf = NULL; |
| 392 | 410 | ||
| 393 | if (add_inbuf(port->in_vq, buf) < 0) | 411 | if (add_inbuf(port->in_vq, buf) < 0) |
| 394 | dev_warn(&port->portdev->vdev->dev, "failed add_buf\n"); | 412 | dev_warn(port->dev, "failed add_buf\n"); |
| 395 | 413 | ||
| 396 | spin_unlock_irqrestore(&port->inbuf_lock, flags); | 414 | spin_unlock_irqrestore(&port->inbuf_lock, flags); |
| 397 | } | 415 | } |
| @@ -664,6 +682,7 @@ static int add_port(struct ports_device *portdev, u32 id) | |||
| 664 | { | 682 | { |
| 665 | struct port *port; | 683 | struct port *port; |
| 666 | struct port_buffer *inbuf; | 684 | struct port_buffer *inbuf; |
| 685 | dev_t devt; | ||
| 667 | int err; | 686 | int err; |
| 668 | 687 | ||
| 669 | port = kmalloc(sizeof(*port), GFP_KERNEL); | 688 | port = kmalloc(sizeof(*port), GFP_KERNEL); |
| @@ -681,12 +700,32 @@ static int add_port(struct ports_device *portdev, u32 id) | |||
| 681 | port->in_vq = portdev->in_vqs[port->id]; | 700 | port->in_vq = portdev->in_vqs[port->id]; |
| 682 | port->out_vq = portdev->out_vqs[port->id]; | 701 | port->out_vq = portdev->out_vqs[port->id]; |
| 683 | 702 | ||
| 703 | cdev_init(&port->cdev, NULL); | ||
| 704 | |||
| 705 | devt = MKDEV(portdev->chr_major, id); | ||
| 706 | err = cdev_add(&port->cdev, devt, 1); | ||
| 707 | if (err < 0) { | ||
| 708 | dev_err(&port->portdev->vdev->dev, | ||
| 709 | "Error %d adding cdev for port %u\n", err, id); | ||
| 710 | goto free_port; | ||
| 711 | } | ||
| 712 | port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, | ||
| 713 | devt, port, "vport%up%u", | ||
| 714 | port->portdev->drv_index, id); | ||
| 715 | if (IS_ERR(port->dev)) { | ||
| 716 | err = PTR_ERR(port->dev); | ||
| 717 | dev_err(&port->portdev->vdev->dev, | ||
| 718 | "Error %d creating device for port %u\n", | ||
| 719 | err, id); | ||
| 720 | goto free_cdev; | ||
| 721 | } | ||
| 722 | |||
| 684 | spin_lock_init(&port->inbuf_lock); | 723 | spin_lock_init(&port->inbuf_lock); |
| 685 | 724 | ||
| 686 | inbuf = alloc_buf(PAGE_SIZE); | 725 | inbuf = alloc_buf(PAGE_SIZE); |
| 687 | if (!inbuf) { | 726 | if (!inbuf) { |
| 688 | err = -ENOMEM; | 727 | err = -ENOMEM; |
| 689 | goto free_port; | 728 | goto free_device; |
| 690 | } | 729 | } |
| 691 | 730 | ||
| 692 | /* Register the input buffer the first time. */ | 731 | /* Register the input buffer the first time. */ |
| @@ -716,6 +755,10 @@ static int add_port(struct ports_device *portdev, u32 id) | |||
| 716 | 755 | ||
| 717 | free_inbuf: | 756 | free_inbuf: |
| 718 | free_buf(inbuf); | 757 | free_buf(inbuf); |
| 758 | free_device: | ||
| 759 | device_destroy(pdrvdata.class, port->dev->devt); | ||
| 760 | free_cdev: | ||
| 761 | cdev_del(&port->cdev); | ||
| 719 | free_port: | 762 | free_port: |
| 720 | kfree(port); | 763 | kfree(port); |
| 721 | fail: | 764 | fail: |
| @@ -828,6 +871,10 @@ fail: | |||
| 828 | return err; | 871 | return err; |
| 829 | } | 872 | } |
| 830 | 873 | ||
| 874 | static const struct file_operations portdev_fops = { | ||
| 875 | .owner = THIS_MODULE, | ||
| 876 | }; | ||
| 877 | |||
| 831 | /* | 878 | /* |
| 832 | * Once we're further in boot, we get probed like any other virtio | 879 | * Once we're further in boot, we get probed like any other virtio |
| 833 | * device. | 880 | * device. |
| @@ -853,6 +900,20 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
| 853 | portdev->vdev = vdev; | 900 | portdev->vdev = vdev; |
| 854 | vdev->priv = portdev; | 901 | vdev->priv = portdev; |
| 855 | 902 | ||
| 903 | spin_lock_irq(&pdrvdata_lock); | ||
| 904 | portdev->drv_index = pdrvdata.index++; | ||
| 905 | spin_unlock_irq(&pdrvdata_lock); | ||
| 906 | |||
| 907 | portdev->chr_major = register_chrdev(0, "virtio-portsdev", | ||
| 908 | &portdev_fops); | ||
| 909 | if (portdev->chr_major < 0) { | ||
| 910 | dev_err(&vdev->dev, | ||
| 911 | "Error %d registering chrdev for device %u\n", | ||
| 912 | portdev->chr_major, portdev->drv_index); | ||
| 913 | err = portdev->chr_major; | ||
| 914 | goto free; | ||
| 915 | } | ||
| 916 | |||
| 856 | multiport = false; | 917 | multiport = false; |
| 857 | portdev->config.nr_ports = 1; | 918 | portdev->config.nr_ports = 1; |
| 858 | portdev->config.max_nr_ports = 1; | 919 | portdev->config.max_nr_ports = 1; |
| @@ -885,7 +946,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
| 885 | err = init_vqs(portdev); | 946 | err = init_vqs(portdev); |
| 886 | if (err < 0) { | 947 | if (err < 0) { |
| 887 | dev_err(&vdev->dev, "Error %d initializing vqs\n", err); | 948 | dev_err(&vdev->dev, "Error %d initializing vqs\n", err); |
| 888 | goto free; | 949 | goto free_chrdev; |
| 889 | } | 950 | } |
| 890 | 951 | ||
| 891 | spin_lock_init(&portdev->ports_lock); | 952 | spin_lock_init(&portdev->ports_lock); |
| @@ -905,10 +966,8 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
| 905 | early_put_chars = NULL; | 966 | early_put_chars = NULL; |
| 906 | return 0; | 967 | return 0; |
| 907 | 968 | ||
| 908 | free_vqs: | 969 | free_chrdev: |
| 909 | vdev->config->del_vqs(vdev); | 970 | unregister_chrdev(portdev->chr_major, "virtio-portsdev"); |
| 910 | kfree(portdev->in_vqs); | ||
| 911 | kfree(portdev->out_vqs); | ||
| 912 | free: | 971 | free: |
| 913 | kfree(portdev); | 972 | kfree(portdev); |
| 914 | fail: | 973 | fail: |
| @@ -937,6 +996,14 @@ static struct virtio_driver virtio_console = { | |||
| 937 | 996 | ||
| 938 | static int __init init(void) | 997 | static int __init init(void) |
| 939 | { | 998 | { |
| 999 | int err; | ||
| 1000 | |||
| 1001 | pdrvdata.class = class_create(THIS_MODULE, "virtio-ports"); | ||
| 1002 | if (IS_ERR(pdrvdata.class)) { | ||
| 1003 | err = PTR_ERR(pdrvdata.class); | ||
| 1004 | pr_err("Error %d creating virtio-ports class\n", err); | ||
| 1005 | return err; | ||
| 1006 | } | ||
| 940 | INIT_LIST_HEAD(&pdrvdata.consoles); | 1007 | INIT_LIST_HEAD(&pdrvdata.consoles); |
| 941 | 1008 | ||
| 942 | return register_virtio_driver(&virtio_console); | 1009 | return register_virtio_driver(&virtio_console); |
