|
@@ -855,6 +855,139 @@ static const struct file_operations port_debugfs_ops = {
|
|
|
.read = debugfs_read,
|
|
|
};
|
|
|
|
|
|
+static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)
|
|
|
+{
|
|
|
+ struct port_buffer *buf;
|
|
|
+ unsigned int nr_added_bufs;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ nr_added_bufs = 0;
|
|
|
+ do {
|
|
|
+ buf = alloc_buf(PAGE_SIZE);
|
|
|
+ if (!buf)
|
|
|
+ break;
|
|
|
+
|
|
|
+ spin_lock_irq(lock);
|
|
|
+ ret = add_inbuf(vq, buf);
|
|
|
+ if (ret < 0) {
|
|
|
+ spin_unlock_irq(lock);
|
|
|
+ free_buf(buf);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ nr_added_bufs++;
|
|
|
+ spin_unlock_irq(lock);
|
|
|
+ } while (ret > 0);
|
|
|
+
|
|
|
+ return nr_added_bufs;
|
|
|
+}
|
|
|
+
|
|
|
+static int add_port(struct ports_device *portdev, u32 id)
|
|
|
+{
|
|
|
+ char debugfs_name[16];
|
|
|
+ struct port *port;
|
|
|
+ struct port_buffer *buf;
|
|
|
+ dev_t devt;
|
|
|
+ unsigned int nr_added_bufs;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ port = kmalloc(sizeof(*port), GFP_KERNEL);
|
|
|
+ if (!port) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+
|
|
|
+ port->portdev = portdev;
|
|
|
+ port->id = id;
|
|
|
+
|
|
|
+ port->name = NULL;
|
|
|
+ port->inbuf = NULL;
|
|
|
+ port->cons.hvc = NULL;
|
|
|
+
|
|
|
+ port->host_connected = port->guest_connected = false;
|
|
|
+
|
|
|
+ port->in_vq = portdev->in_vqs[port->id];
|
|
|
+ port->out_vq = portdev->out_vqs[port->id];
|
|
|
+
|
|
|
+ cdev_init(&port->cdev, &port_fops);
|
|
|
+
|
|
|
+ devt = MKDEV(portdev->chr_major, id);
|
|
|
+ err = cdev_add(&port->cdev, devt, 1);
|
|
|
+ if (err < 0) {
|
|
|
+ dev_err(&port->portdev->vdev->dev,
|
|
|
+ "Error %d adding cdev for port %u\n", err, id);
|
|
|
+ goto free_port;
|
|
|
+ }
|
|
|
+ port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev,
|
|
|
+ devt, port, "vport%up%u",
|
|
|
+ port->portdev->drv_index, id);
|
|
|
+ if (IS_ERR(port->dev)) {
|
|
|
+ err = PTR_ERR(port->dev);
|
|
|
+ dev_err(&port->portdev->vdev->dev,
|
|
|
+ "Error %d creating device for port %u\n",
|
|
|
+ err, id);
|
|
|
+ goto free_cdev;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock_init(&port->inbuf_lock);
|
|
|
+ init_waitqueue_head(&port->waitqueue);
|
|
|
+
|
|
|
+ /* Fill the in_vq with buffers so the host can send us data. */
|
|
|
+ nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock);
|
|
|
+ if (!nr_added_bufs) {
|
|
|
+ dev_err(port->dev, "Error allocating inbufs\n");
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto free_device;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we're not using multiport support, this has to be a console port
|
|
|
+ */
|
|
|
+ if (!use_multiport(port->portdev)) {
|
|
|
+ err = init_port_console(port);
|
|
|
+ if (err)
|
|
|
+ goto free_inbufs;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock_irq(&portdev->ports_lock);
|
|
|
+ list_add_tail(&port->list, &port->portdev->ports);
|
|
|
+ spin_unlock_irq(&portdev->ports_lock);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Tell the Host we're set so that it can send us various
|
|
|
+ * configuration parameters for this port (eg, port name,
|
|
|
+ * caching, whether this is a console port, etc.)
|
|
|
+ */
|
|
|
+ send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
|
|
|
+
|
|
|
+ if (pdrvdata.debugfs_dir) {
|
|
|
+ /*
|
|
|
+ * Finally, create the debugfs file that we can use to
|
|
|
+ * inspect a port's state at any time
|
|
|
+ */
|
|
|
+ sprintf(debugfs_name, "vport%up%u",
|
|
|
+ port->portdev->drv_index, id);
|
|
|
+ port->debugfs_file = debugfs_create_file(debugfs_name, 0444,
|
|
|
+ pdrvdata.debugfs_dir,
|
|
|
+ port,
|
|
|
+ &port_debugfs_ops);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+
|
|
|
+free_inbufs:
|
|
|
+ while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
|
|
|
+ free_buf(buf);
|
|
|
+free_device:
|
|
|
+ device_destroy(pdrvdata.class, port->dev->devt);
|
|
|
+free_cdev:
|
|
|
+ cdev_del(&port->cdev);
|
|
|
+free_port:
|
|
|
+ kfree(port);
|
|
|
+fail:
|
|
|
+ /* The host might want to notify management sw about port add failure */
|
|
|
+ send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 0);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
/* Remove all port-specific data. */
|
|
|
static int remove_port(struct port *port)
|
|
|
{
|
|
@@ -1093,139 +1226,6 @@ static void config_intr(struct virtio_device *vdev)
|
|
|
resize_console(find_port_by_id(portdev, 0));
|
|
|
}
|
|
|
|
|
|
-static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)
|
|
|
-{
|
|
|
- struct port_buffer *buf;
|
|
|
- unsigned int nr_added_bufs;
|
|
|
- int ret;
|
|
|
-
|
|
|
- nr_added_bufs = 0;
|
|
|
- do {
|
|
|
- buf = alloc_buf(PAGE_SIZE);
|
|
|
- if (!buf)
|
|
|
- break;
|
|
|
-
|
|
|
- spin_lock_irq(lock);
|
|
|
- ret = add_inbuf(vq, buf);
|
|
|
- if (ret < 0) {
|
|
|
- spin_unlock_irq(lock);
|
|
|
- free_buf(buf);
|
|
|
- break;
|
|
|
- }
|
|
|
- nr_added_bufs++;
|
|
|
- spin_unlock_irq(lock);
|
|
|
- } while (ret > 0);
|
|
|
-
|
|
|
- return nr_added_bufs;
|
|
|
-}
|
|
|
-
|
|
|
-static int add_port(struct ports_device *portdev, u32 id)
|
|
|
-{
|
|
|
- char debugfs_name[16];
|
|
|
- struct port *port;
|
|
|
- struct port_buffer *buf;
|
|
|
- dev_t devt;
|
|
|
- unsigned int nr_added_bufs;
|
|
|
- int err;
|
|
|
-
|
|
|
- port = kmalloc(sizeof(*port), GFP_KERNEL);
|
|
|
- if (!port) {
|
|
|
- err = -ENOMEM;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
-
|
|
|
- port->portdev = portdev;
|
|
|
- port->id = id;
|
|
|
-
|
|
|
- port->name = NULL;
|
|
|
- port->inbuf = NULL;
|
|
|
- port->cons.hvc = NULL;
|
|
|
-
|
|
|
- port->host_connected = port->guest_connected = false;
|
|
|
-
|
|
|
- port->in_vq = portdev->in_vqs[port->id];
|
|
|
- port->out_vq = portdev->out_vqs[port->id];
|
|
|
-
|
|
|
- cdev_init(&port->cdev, &port_fops);
|
|
|
-
|
|
|
- devt = MKDEV(portdev->chr_major, id);
|
|
|
- err = cdev_add(&port->cdev, devt, 1);
|
|
|
- if (err < 0) {
|
|
|
- dev_err(&port->portdev->vdev->dev,
|
|
|
- "Error %d adding cdev for port %u\n", err, id);
|
|
|
- goto free_port;
|
|
|
- }
|
|
|
- port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev,
|
|
|
- devt, port, "vport%up%u",
|
|
|
- port->portdev->drv_index, id);
|
|
|
- if (IS_ERR(port->dev)) {
|
|
|
- err = PTR_ERR(port->dev);
|
|
|
- dev_err(&port->portdev->vdev->dev,
|
|
|
- "Error %d creating device for port %u\n",
|
|
|
- err, id);
|
|
|
- goto free_cdev;
|
|
|
- }
|
|
|
-
|
|
|
- spin_lock_init(&port->inbuf_lock);
|
|
|
- init_waitqueue_head(&port->waitqueue);
|
|
|
-
|
|
|
- /* Fill the in_vq with buffers so the host can send us data. */
|
|
|
- nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock);
|
|
|
- if (!nr_added_bufs) {
|
|
|
- dev_err(port->dev, "Error allocating inbufs\n");
|
|
|
- err = -ENOMEM;
|
|
|
- goto free_device;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * If we're not using multiport support, this has to be a console port
|
|
|
- */
|
|
|
- if (!use_multiport(port->portdev)) {
|
|
|
- err = init_port_console(port);
|
|
|
- if (err)
|
|
|
- goto free_inbufs;
|
|
|
- }
|
|
|
-
|
|
|
- spin_lock_irq(&portdev->ports_lock);
|
|
|
- list_add_tail(&port->list, &port->portdev->ports);
|
|
|
- spin_unlock_irq(&portdev->ports_lock);
|
|
|
-
|
|
|
- /*
|
|
|
- * Tell the Host we're set so that it can send us various
|
|
|
- * configuration parameters for this port (eg, port name,
|
|
|
- * caching, whether this is a console port, etc.)
|
|
|
- */
|
|
|
- send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
|
|
|
-
|
|
|
- if (pdrvdata.debugfs_dir) {
|
|
|
- /*
|
|
|
- * Finally, create the debugfs file that we can use to
|
|
|
- * inspect a port's state at any time
|
|
|
- */
|
|
|
- sprintf(debugfs_name, "vport%up%u",
|
|
|
- port->portdev->drv_index, id);
|
|
|
- port->debugfs_file = debugfs_create_file(debugfs_name, 0444,
|
|
|
- pdrvdata.debugfs_dir,
|
|
|
- port,
|
|
|
- &port_debugfs_ops);
|
|
|
- }
|
|
|
- return 0;
|
|
|
-
|
|
|
-free_inbufs:
|
|
|
- while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
|
|
|
- free_buf(buf);
|
|
|
-free_device:
|
|
|
- device_destroy(pdrvdata.class, port->dev->devt);
|
|
|
-free_cdev:
|
|
|
- cdev_del(&port->cdev);
|
|
|
-free_port:
|
|
|
- kfree(port);
|
|
|
-fail:
|
|
|
- /* The host might want to notify management sw about port add failure */
|
|
|
- send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 0);
|
|
|
- return err;
|
|
|
-}
|
|
|
-
|
|
|
static int init_vqs(struct ports_device *portdev)
|
|
|
{
|
|
|
vq_callback_t **io_callbacks;
|