virtio: console: struct ports for multiple ports per device.

Rather than assume a single port, add a 'struct ports' with an array
of ports.  Currently, there's always only one, but that will change.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 drivers/char/virtio_console.c |   71 +++++++++++++++++++++++++++++-------------
 1 file changed, 50 insertions(+), 21 deletions(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -39,6 +39,13 @@ struct port {
 	u32 vtermno;
 };
 
+/* This is vdev->priv. */
+struct ports {
+	/* We use a simple array. */
+	unsigned int num_ports;
+	struct port port[0];
+};
+
 /* The list of ports. */
 static LIST_HEAD(ports_by_vtermno);
 
@@ -148,7 +155,7 @@ static int get_chars(u32 vtermno, char *
  */
 static void virtcons_apply_config(struct virtio_device *dev)
 {
-	struct port *port = dev->priv;
+	struct ports *ports = dev->priv;
 	struct winsize ws;
 
 	if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) {
@@ -158,7 +165,9 @@ static void virtcons_apply_config(struct
 		dev->config->get(dev,
 				 offsetof(struct virtio_console_config, rows),
 				 &ws.ws_row, sizeof(u16));
-		hvc_resize(port->hvc, ws);
+		/* This is the pre-multiport style: we use control messages
+		 * these days which specify the port.  So this means port 0. */
+		hvc_resize(ports->port[0].hvc, ws);
 	}
 }
 
@@ -181,9 +190,14 @@ static void notifier_del_vio(struct hvc_
 
 static void hvc_handle_input(struct virtqueue *vq)
 {
-	struct port *port = vq->vdev->priv;
+	struct ports *ports = vq->vdev->priv;
+	unsigned int i;
+	bool activity = false;
 
-	if (hvc_poll(port->hvc))
+	for (i = 0; i < ports->num_ports; i++)
+		activity |= hvc_poll(ports->port[i].hvc);
+
+	if (activity)
 		hvc_kick();
 }
 
@@ -208,27 +222,37 @@ int __init virtio_cons_early_init(int (*
 	return hvc_instantiate(0, 0, &hv_ops);
 }
 
-static struct port *__devinit alloc_port(u32 vtermno)
+static struct ports *__devinit alloc_ports(unsigned int num)
 {
-	struct port *port = kmalloc(sizeof *port, GFP_KERNEL);
+	struct ports *ports;
+	int i;
 
-	if (!port)
+	ports = kmalloc(sizeof *ports + sizeof(ports->port[0]) * num,
+			GFP_KERNEL);
+	if (!ports)
 		return NULL;
 
-	port->used_len = 0;
-	port->inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!port->inbuf) {
-		kfree(port);
-		return NULL;
+	ports->num_ports = num;
+	for (i = 0; i < ports->num_ports; i++) {
+		ports->port[i].used_len = 0;
+		ports->port[i].inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+		if (unlikely(!ports->port[i].inbuf)) {
+			while (--i >= 0)
+				kfree(ports->port[i].inbuf);
+			kfree(ports);
+			return NULL;
+		}
 	}
-	port->vtermno = vtermno;
-	return port;
+	return ports;
 }
 
-static void free_port(struct port *port)
+static void free_ports(struct ports *ports)
 {
-	kfree(port->inbuf);
-	kfree(port);
+	unsigned int i;
+
+	for (i = 0; i < ports->num_ports; i++)
+		kfree(ports->port[i].inbuf);
+	kfree(ports);
 }
 
 /* Once we're further in boot, we get probed like any other virtio device.
@@ -244,17 +268,21 @@ static int __devinit virtcons_probe(stru
 	const char *names[] = { "input", "output" };
 	struct virtqueue *vqs[2];
 	int err;
+	struct ports *ports;
 	struct port *port;
 
-	port = alloc_port(0);
-	if (!port) {
+	ports = alloc_ports(1);
+	if (!ports) {
 		err = -ENOMEM;
 		goto fail;
 	}
 
+	/* Convenience variable since we only have one port. */
+	port = &ports->port[0];
+
 	/* Attach this port to this virtio_device, and vice-versa. */
 	port->vdev = vdev;
-	vdev->priv = port;
+	vdev->priv = ports;
 
 	/* Find the queues. */
 	err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
@@ -274,6 +303,7 @@ static int __devinit virtcons_probe(stru
 	 * The final argument is the output buffer size: we can do any size,
 	 * so we put PAGE_SIZE here.
 	 */
+	port->vtermno = 0;
 	port->hvc = hvc_alloc(port->vtermno, 0, &hv_ops, PAGE_SIZE);
 	if (IS_ERR(port->hvc)) {
 		err = PTR_ERR(port->hvc);
@@ -295,7 +325,7 @@ static int __devinit virtcons_probe(stru
 free_vqs:
 	vdev->config->del_vqs(vdev);
 free:
-	free_port(port);
+	free_ports(ports);
 fail:
 	return err;
 }
