Merge tag 'gfs2-4.8.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2...
[cascardo/linux.git] / drivers / usb / usbip / vhci_hcd.c
index 2e0450b..03eccf2 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015-2016 Nobuo Iwata
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -56,7 +57,9 @@ static int vhci_get_frame_number(struct usb_hcd *hcd);
 static const char driver_name[] = "vhci_hcd";
 static const char driver_desc[] = "USB/IP Virtual Host Controller";
 
-struct vhci_hcd *the_controller;
+int vhci_num_controllers = VHCI_NR_HCS;
+
+struct platform_device **vhci_pdevs;
 
 static const char * const bit_desc[] = {
        "CONNECTION",           /*0*/
@@ -119,47 +122,59 @@ static void dump_port_status_diff(u32 prev_status, u32 new_status)
        pr_debug("\n");
 }
 
-void rh_port_connect(int rhport, enum usb_device_speed speed)
+void rh_port_connect(struct vhci_device *vdev, enum usb_device_speed speed)
 {
+       struct vhci_hcd *vhci = vdev_to_vhci(vdev);
+       int             rhport = vdev->rhport;
+       u32             status;
        unsigned long   flags;
 
        usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport);
 
-       spin_lock_irqsave(&the_controller->lock, flags);
+       spin_lock_irqsave(&vhci->lock, flags);
+
+       status = vhci->port_status[rhport];
 
-       the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION
-               | (1 << USB_PORT_FEAT_C_CONNECTION);
+       status |= USB_PORT_STAT_CONNECTION | (1 << USB_PORT_FEAT_C_CONNECTION);
 
        switch (speed) {
        case USB_SPEED_HIGH:
-               the_controller->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED;
+               status |= USB_PORT_STAT_HIGH_SPEED;
                break;
        case USB_SPEED_LOW:
-               the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED;
+               status |= USB_PORT_STAT_LOW_SPEED;
                break;
        default:
                break;
        }
 
-       spin_unlock_irqrestore(&the_controller->lock, flags);
+       vhci->port_status[rhport] = status;
+
+       spin_unlock_irqrestore(&vhci->lock, flags);
 
-       usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
+       usb_hcd_poll_rh_status(vhci_to_hcd(vhci));
 }
 
-static void rh_port_disconnect(int rhport)
+static void rh_port_disconnect(struct vhci_device *vdev)
 {
+       struct vhci_hcd *vhci = vdev_to_vhci(vdev);
+       int             rhport = vdev->rhport;
+       u32             status;
        unsigned long   flags;
 
        usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport);
 
-       spin_lock_irqsave(&the_controller->lock, flags);
+       spin_lock_irqsave(&vhci->lock, flags);
+
+       status = vhci->port_status[rhport];
+
+       status &= ~USB_PORT_STAT_CONNECTION;
+       status |= (1 << USB_PORT_FEAT_C_CONNECTION);
 
-       the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION;
-       the_controller->port_status[rhport] |=
-                                       (1 << USB_PORT_FEAT_C_CONNECTION);
+       vhci->port_status[rhport] = status;
 
-       spin_unlock_irqrestore(&the_controller->lock, flags);
-       usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
+       spin_unlock_irqrestore(&vhci->lock, flags);
+       usb_hcd_poll_rh_status(vhci_to_hcd(vhci));
 }
 
 #define PORT_C_MASK                            \
@@ -188,7 +203,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
        int             changed = 0;
        unsigned long   flags;
 
-       retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8);
+       retval = DIV_ROUND_UP(VHCI_HC_PORTS + 1, 8);
        memset(buf, 0, retval);
 
        vhci = hcd_to_vhci(hcd);
@@ -200,7 +215,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
        }
 
        /* check pseudo status register for each port */
-       for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
+       for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
                if ((vhci->port_status[rhport] & PORT_C_MASK)) {
                        /* The status of a port has been changed, */
                        usbip_dbg_vhci_rh("port %d status changed\n", rhport);
@@ -225,7 +240,7 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc)
        desc->bDescLength = 9;
        desc->wHubCharacteristics = cpu_to_le16(
                HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
-       desc->bNbrPorts = VHCI_NPORTS;
+       desc->bNbrPorts = VHCI_HC_PORTS;
        desc->u.hs.DeviceRemovable[0] = 0xff;
        desc->u.hs.DeviceRemovable[1] = 0xff;
 }
@@ -238,7 +253,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
        int             rhport;
        unsigned long   flags;
 
-       u32 prev_port_status[VHCI_NPORTS];
+       u32 prev_port_status[VHCI_HC_PORTS];
 
        if (!HCD_HW_ACCESSIBLE(hcd))
                return -ETIMEDOUT;
@@ -249,7 +264,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
         */
        usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue,
                          wIndex);
-       if (wIndex > VHCI_NPORTS)
+       if (wIndex > VHCI_HC_PORTS)
                pr_err("invalid port number %d\n", wIndex);
        rhport = ((__u8)(wIndex & 0x00ff)) - 1;
 
@@ -315,7 +330,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                break;
        case GetPortStatus:
                usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex);
-               if (wIndex > VHCI_NPORTS || wIndex < 1) {
+               if (wIndex > VHCI_HC_PORTS || wIndex < 1) {
                        pr_err("invalid port number %d\n", wIndex);
                        retval = -EPIPE;
                }
@@ -416,14 +431,27 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
 static struct vhci_device *get_vdev(struct usb_device *udev)
 {
-       int i;
+       struct platform_device *pdev;
+       struct usb_hcd *hcd;
+       struct vhci_hcd *vhci;
+       int pdev_nr, rhport;
 
        if (!udev)
                return NULL;
 
-       for (i = 0; i < VHCI_NPORTS; i++)
-               if (the_controller->vdev[i].udev == udev)
-                       return port_to_vdev(i);
+       for (pdev_nr = 0; pdev_nr < vhci_num_controllers; pdev_nr++) {
+               pdev = *(vhci_pdevs + pdev_nr);
+               if (pdev == NULL)
+                       continue;
+               hcd = platform_get_drvdata(pdev);
+               if (hcd == NULL)
+                       continue;
+               vhci = hcd_to_vhci(hcd);
+               for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
+                       if (vhci->vdev[rhport].udev == udev)
+                               return &vhci->vdev[rhport];
+               }
+       }
 
        return NULL;
 }
@@ -432,6 +460,7 @@ static void vhci_tx_urb(struct urb *urb)
 {
        struct vhci_device *vdev = get_vdev(urb->dev);
        struct vhci_priv *priv;
+       struct vhci_hcd *vhci = vdev_to_vhci(vdev);
        unsigned long flags;
 
        if (!vdev) {
@@ -447,7 +476,7 @@ static void vhci_tx_urb(struct urb *urb)
 
        spin_lock_irqsave(&vdev->priv_lock, flags);
 
-       priv->seqnum = atomic_inc_return(&the_controller->seqnum);
+       priv->seqnum = atomic_inc_return(&vhci->seqnum);
        if (priv->seqnum == 0xffff)
                dev_info(&urb->dev->dev, "seqnum max\n");
 
@@ -465,7 +494,9 @@ static void vhci_tx_urb(struct urb *urb)
 static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
                            gfp_t mem_flags)
 {
+       struct vhci_hcd *vhci = hcd_to_vhci(hcd);
        struct device *dev = &urb->dev->dev;
+       u8 portnum = urb->dev->portnum;
        int ret = 0;
        struct vhci_device *vdev;
        unsigned long flags;
@@ -473,26 +504,30 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
        usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n",
                          hcd, urb, mem_flags);
 
+       if (portnum > VHCI_HC_PORTS) {
+               pr_err("invalid port number %d\n", portnum);
+               return -ENODEV;
+       }
+       vdev = &vhci->vdev[portnum-1];
+
        /* patch to usb_sg_init() is in 2.5.60 */
        BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length);
 
-       spin_lock_irqsave(&the_controller->lock, flags);
+       spin_lock_irqsave(&vhci->lock, flags);
 
        if (urb->status != -EINPROGRESS) {
                dev_err(dev, "URB already unlinked!, status %d\n", urb->status);
-               spin_unlock_irqrestore(&the_controller->lock, flags);
+               spin_unlock_irqrestore(&vhci->lock, flags);
                return urb->status;
        }
 
-       vdev = port_to_vdev(urb->dev->portnum-1);
-
        /* refuse enqueue for dead connection */
        spin_lock(&vdev->ud.lock);
        if (vdev->ud.status == VDEV_ST_NULL ||
            vdev->ud.status == VDEV_ST_ERROR) {
                dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport);
                spin_unlock(&vdev->ud.lock);
-               spin_unlock_irqrestore(&the_controller->lock, flags);
+               spin_unlock_irqrestore(&vhci->lock, flags);
                return -ENODEV;
        }
        spin_unlock(&vdev->ud.lock);
@@ -565,17 +600,16 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
 
 out:
        vhci_tx_urb(urb);
-       spin_unlock_irqrestore(&the_controller->lock, flags);
+       spin_unlock_irqrestore(&vhci->lock, flags);
 
        return 0;
 
 no_need_xmit:
        usb_hcd_unlink_urb_from_ep(hcd, urb);
 no_need_unlink:
-       spin_unlock_irqrestore(&the_controller->lock, flags);
+       spin_unlock_irqrestore(&vhci->lock, flags);
        if (!ret)
-               usb_hcd_giveback_urb(vhci_to_hcd(the_controller),
-                                    urb, urb->status);
+               usb_hcd_giveback_urb(hcd, urb, urb->status);
        return ret;
 }
 
@@ -627,19 +661,20 @@ no_need_unlink:
  */
 static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 {
+       struct vhci_hcd *vhci = hcd_to_vhci(hcd);
        struct vhci_priv *priv;
        struct vhci_device *vdev;
        unsigned long flags;
 
        pr_info("dequeue a urb %p\n", urb);
 
-       spin_lock_irqsave(&the_controller->lock, flags);
+       spin_lock_irqsave(&vhci->lock, flags);
 
        priv = urb->hcpriv;
        if (!priv) {
                /* URB was never linked! or will be soon given back by
                 * vhci_rx. */
-               spin_unlock_irqrestore(&the_controller->lock, flags);
+               spin_unlock_irqrestore(&vhci->lock, flags);
                return -EIDRM;
        }
 
@@ -648,7 +683,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 
                ret = usb_hcd_check_unlink_urb(hcd, urb, status);
                if (ret) {
-                       spin_unlock_irqrestore(&the_controller->lock, flags);
+                       spin_unlock_irqrestore(&vhci->lock, flags);
                        return ret;
                }
        }
@@ -676,10 +711,9 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 
                usb_hcd_unlink_urb_from_ep(hcd, urb);
 
-               spin_unlock_irqrestore(&the_controller->lock, flags);
-               usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
-                                    urb->status);
-               spin_lock_irqsave(&the_controller->lock, flags);
+               spin_unlock_irqrestore(&vhci->lock, flags);
+               usb_hcd_giveback_urb(vhci_to_hcd(vhci), urb, urb->status);
+               spin_lock_irqsave(&vhci->lock, flags);
 
        } else {
                /* tcp connection is alive */
@@ -691,12 +725,12 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
                unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC);
                if (!unlink) {
                        spin_unlock(&vdev->priv_lock);
-                       spin_unlock_irqrestore(&the_controller->lock, flags);
+                       spin_unlock_irqrestore(&vhci->lock, flags);
                        usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC);
                        return -ENOMEM;
                }
 
-               unlink->seqnum = atomic_inc_return(&the_controller->seqnum);
+               unlink->seqnum = atomic_inc_return(&vhci->seqnum);
                if (unlink->seqnum == 0xffff)
                        pr_info("seqnum max\n");
 
@@ -712,7 +746,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
                spin_unlock(&vdev->priv_lock);
        }
 
-       spin_unlock_irqrestore(&the_controller->lock, flags);
+       spin_unlock_irqrestore(&vhci->lock, flags);
 
        usbip_dbg_vhci_hc("leave\n");
        return 0;
@@ -720,10 +754,12 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 
 static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
 {
+       struct vhci_hcd *vhci = vdev_to_vhci(vdev);
+       struct usb_hcd *hcd = vhci_to_hcd(vhci);
        struct vhci_unlink *unlink, *tmp;
        unsigned long flags;
 
-       spin_lock_irqsave(&the_controller->lock, flags);
+       spin_lock_irqsave(&vhci->lock, flags);
        spin_lock(&vdev->priv_lock);
 
        list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
@@ -752,24 +788,23 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
 
                urb->status = -ENODEV;
 
-               usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
+               usb_hcd_unlink_urb_from_ep(hcd, urb);
 
                list_del(&unlink->list);
 
                spin_unlock(&vdev->priv_lock);
-               spin_unlock_irqrestore(&the_controller->lock, flags);
+               spin_unlock_irqrestore(&vhci->lock, flags);
 
-               usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
-                                    urb->status);
+               usb_hcd_giveback_urb(hcd, urb, urb->status);
 
-               spin_lock_irqsave(&the_controller->lock, flags);
+               spin_lock_irqsave(&vhci->lock, flags);
                spin_lock(&vdev->priv_lock);
 
                kfree(unlink);
        }
 
        spin_unlock(&vdev->priv_lock);
-       spin_unlock_irqrestore(&the_controller->lock, flags);
+       spin_unlock_irqrestore(&vhci->lock, flags);
 }
 
 /*
@@ -827,7 +862,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
         * is actually given back by vhci_rx after receiving its return pdu.
         *
         */
-       rh_port_disconnect(vdev->rhport);
+       rh_port_disconnect(vdev);
 
        pr_info("disconnect device\n");
 }
@@ -866,7 +901,7 @@ static void vhci_device_unusable(struct usbip_device *ud)
 
 static void vhci_device_init(struct vhci_device *vdev)
 {
-       memset(vdev, 0, sizeof(*vdev));
+       memset(vdev, 0, sizeof(struct vhci_device));
 
        vdev->ud.side   = USBIP_VHCI;
        vdev->ud.status = VDEV_ST_NULL;
@@ -887,17 +922,34 @@ static void vhci_device_init(struct vhci_device *vdev)
        usbip_start_eh(&vdev->ud);
 }
 
+static int hcd_name_to_id(const char *name)
+{
+       char *c;
+       long val;
+       int ret;
+
+       c = strchr(name, '.');
+       if (c == NULL)
+               return 0;
+
+       ret = kstrtol(c+1, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       return val;
+}
+
 static int vhci_start(struct usb_hcd *hcd)
 {
        struct vhci_hcd *vhci = hcd_to_vhci(hcd);
-       int rhport;
+       int id, rhport;
        int err = 0;
 
        usbip_dbg_vhci_hc("enter vhci_start\n");
 
        /* initialize private data of usb_hcd */
 
-       for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
+       for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
                struct vhci_device *vdev = &vhci->vdev[rhport];
 
                vhci_device_init(vdev);
@@ -910,11 +962,26 @@ static int vhci_start(struct usb_hcd *hcd)
        hcd->power_budget = 0; /* no limit */
        hcd->uses_new_polling = 1;
 
+       id = hcd_name_to_id(hcd_name(hcd));
+       if (id < 0) {
+               pr_err("invalid vhci name %s\n", hcd_name(hcd));
+               return -EINVAL;
+       }
+
        /* vhci_hcd is now ready to be controlled through sysfs */
-       err = sysfs_create_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
-       if (err) {
-               pr_err("create sysfs files\n");
-               return err;
+       if (id == 0) {
+               err = vhci_init_attr_group();
+               if (err) {
+                       pr_err("init attr group\n");
+                       return err;
+               }
+               err = sysfs_create_group(&hcd_dev(hcd)->kobj, &vhci_attr_group);
+               if (err) {
+                       pr_err("create sysfs files\n");
+                       vhci_finish_attr_group();
+                       return err;
+               }
+               pr_info("created sysfs %s\n", hcd_name(hcd));
        }
 
        return 0;
@@ -923,15 +990,19 @@ static int vhci_start(struct usb_hcd *hcd)
 static void vhci_stop(struct usb_hcd *hcd)
 {
        struct vhci_hcd *vhci = hcd_to_vhci(hcd);
-       int rhport = 0;
+       int id, rhport;
 
        usbip_dbg_vhci_hc("stop VHCI controller\n");
 
        /* 1. remove the userland interface of vhci_hcd */
-       sysfs_remove_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
+       id = hcd_name_to_id(hcd_name(hcd));
+       if (id == 0) {
+               sysfs_remove_group(&hcd_dev(hcd)->kobj, &vhci_attr_group);
+               vhci_finish_attr_group();
+       }
 
        /* 2. shutdown all the ports of vhci_hcd */
-       for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
+       for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
                struct vhci_device *vdev = &vhci->vdev[rhport];
 
                usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED);
@@ -1025,9 +1096,6 @@ static int vhci_hcd_probe(struct platform_device *pdev)
        }
        hcd->has_tt = 1;
 
-       /* this is private data for vhci_hcd */
-       the_controller = hcd_to_vhci(hcd);
-
        /*
         * Finish generic HCD structure initialization and register.
         * Call the driver's reset() and start() routines.
@@ -1036,7 +1104,6 @@ static int vhci_hcd_probe(struct platform_device *pdev)
        if (ret != 0) {
                pr_err("usb_add_hcd failed %d\n", ret);
                usb_put_hcd(hcd);
-               the_controller = NULL;
                return ret;
        }
 
@@ -1059,7 +1126,6 @@ static int vhci_hcd_remove(struct platform_device *pdev)
         */
        usb_remove_hcd(hcd);
        usb_put_hcd(hcd);
-       the_controller = NULL;
 
        return 0;
 }
@@ -1070,21 +1136,24 @@ static int vhci_hcd_remove(struct platform_device *pdev)
 static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state)
 {
        struct usb_hcd *hcd;
-       int rhport = 0;
+       struct vhci_hcd *vhci;
+       int rhport;
        int connected = 0;
        int ret = 0;
        unsigned long flags;
 
        hcd = platform_get_drvdata(pdev);
+       if (!hcd)
+               return 0;
+       vhci = hcd_to_vhci(hcd);
 
-       spin_lock_irqsave(&the_controller->lock, flags);
+       spin_lock_irqsave(&vhci->lock, flags);
 
-       for (rhport = 0; rhport < VHCI_NPORTS; rhport++)
-               if (the_controller->port_status[rhport] &
-                   USB_PORT_STAT_CONNECTION)
+       for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++)
+               if (vhci->port_status[rhport] & USB_PORT_STAT_CONNECTION)
                        connected += 1;
 
-       spin_unlock_irqrestore(&the_controller->lock, flags);
+       spin_unlock_irqrestore(&vhci->lock, flags);
 
        if (connected > 0) {
                dev_info(&pdev->dev,
@@ -1106,6 +1175,8 @@ static int vhci_hcd_resume(struct platform_device *pdev)
        dev_dbg(&pdev->dev, "%s\n", __func__);
 
        hcd = platform_get_drvdata(pdev);
+       if (!hcd)
+               return 0;
        set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
        usb_hcd_poll_rh_status(hcd);
 
@@ -1129,52 +1200,78 @@ static struct platform_driver vhci_driver = {
        },
 };
 
-/*
- * The VHCI 'device' is 'virtual'; not a real plug&play hardware.
- * We need to add this virtual device as a platform device arbitrarily:
- *     1. platform_device_register()
- */
-static void the_pdev_release(struct device *dev)
+static int add_platform_device(int id)
 {
+       struct platform_device *pdev;
+       int dev_nr;
+
+       if (id == 0)
+               dev_nr = -1;
+       else
+               dev_nr = id;
+
+       pdev = platform_device_register_simple(driver_name, dev_nr, NULL, 0);
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
+
+       *(vhci_pdevs + id) = pdev;
+       return 0;
 }
 
-static struct platform_device the_pdev = {
-       /* should be the same name as driver_name */
-       .name = driver_name,
-       .id = -1,
-       .dev = {
-               .release = the_pdev_release,
-       },
-};
+static void del_platform_devices(void)
+{
+       struct platform_device *pdev;
+       int i;
+
+       for (i = 0; i < vhci_num_controllers; i++) {
+               pdev = *(vhci_pdevs + i);
+               if (pdev != NULL)
+                       platform_device_unregister(pdev);
+               *(vhci_pdevs + i) = NULL;
+       }
+       sysfs_remove_link(&platform_bus.kobj, driver_name);
+}
 
 static int __init vhci_hcd_init(void)
 {
-       int ret;
+       int i, ret;
 
        if (usb_disabled())
                return -ENODEV;
 
+       if (vhci_num_controllers < 1)
+               vhci_num_controllers = 1;
+
+       vhci_pdevs = kcalloc(vhci_num_controllers, sizeof(void *), GFP_KERNEL);
+       if (vhci_pdevs == NULL)
+               return -ENOMEM;
+
        ret = platform_driver_register(&vhci_driver);
        if (ret)
                goto err_driver_register;
 
-       ret = platform_device_register(&the_pdev);
-       if (ret)
-               goto err_platform_device_register;
+       for (i = 0; i < vhci_num_controllers; i++) {
+               ret = add_platform_device(i);
+               if (ret)
+                       goto err_platform_device_register;
+       }
 
        pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
        return ret;
 
 err_platform_device_register:
+       del_platform_devices();
        platform_driver_unregister(&vhci_driver);
 err_driver_register:
+       kfree(vhci_pdevs);
        return ret;
 }
 
 static void __exit vhci_hcd_exit(void)
 {
-       platform_device_unregister(&the_pdev);
+       del_platform_devices();
        platform_driver_unregister(&vhci_driver);
+       kfree(vhci_pdevs);
 }
 
 module_init(vhci_hcd_init);