usb: hook unplug into qdev, cleanups + fixes.

Hook into DeviceInfo->exit().

handle_destroy() must not free the state struct, this is handled
by the new usb_qdev_exit() function now.

qdev_free(usb_device) works now.

Fix usb hub to qdev_free() all connected devices on unplug.
Unplugging a usb hub works now.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/hw/usb-bus.c b/hw/usb-bus.c
index 2cac1e8..d0b59dd 100644
--- a/hw/usb-bus.c
+++ b/hw/usb-bus.c
@@ -50,10 +50,22 @@
     return rc;
 }
 
+static int usb_qdev_exit(DeviceState *qdev)
+{
+    USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
+
+    usb_device_detach(dev);
+    if (dev->info->handle_destroy) {
+        dev->info->handle_destroy(dev);
+    }
+    return 0;
+}
+
 void usb_qdev_register(USBDeviceInfo *info)
 {
     info->qdev.bus_info = &usb_bus_info;
     info->qdev.init     = usb_qdev_init;
+    info->qdev.exit     = usb_qdev_exit;
     qdev_register(&info->qdev);
 }
 
@@ -101,6 +113,14 @@
     bus->nfree++;
 }
 
+void usb_unregister_port(USBBus *bus, USBPort *port)
+{
+    if (port->dev)
+        qdev_free(&port->dev->qdev);
+    QTAILQ_REMOVE(&bus->free, port, next);
+    bus->nfree--;
+}
+
 static void do_attach(USBDevice *dev)
 {
     USBBus *bus = usb_bus_from_device(dev);
@@ -136,6 +156,34 @@
     return 0;
 }
 
+int usb_device_detach(USBDevice *dev)
+{
+    USBBus *bus = usb_bus_from_device(dev);
+    USBPort *port;
+
+    if (!dev->attached) {
+        fprintf(stderr, "Warning: tried to detach unattached usb device %s\n",
+                dev->devname);
+        return -1;
+    }
+    dev->attached--;
+
+    QTAILQ_FOREACH(port, &bus->used, next) {
+        if (port->dev == dev)
+            break;
+    }
+    assert(port != NULL);
+
+    QTAILQ_REMOVE(&bus->used, port, next);
+    bus->nused--;
+
+    usb_attach(port, NULL);
+
+    QTAILQ_INSERT_TAIL(&bus->free, port, next);
+    bus->nfree++;
+    return 0;
+}
+
 int usb_device_delete_addr(int busnr, int addr)
 {
     USBBus *bus;
@@ -152,16 +200,9 @@
     }
     if (!port)
         return -1;
-
     dev = port->dev;
-    QTAILQ_REMOVE(&bus->used, port, next);
-    bus->nused--;
 
-    usb_attach(port, NULL);
-    dev->info->handle_destroy(dev);
-
-    QTAILQ_INSERT_TAIL(&bus->free, port, next);
-    bus->nfree++;
+    qdev_free(&dev->qdev);
     return 0;
 }