diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2011-09-01 13:56:37 +0200 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2011-09-07 09:58:26 +0200 |
commit | 891fb2cd4592b6fe76106a69e0ca40efbf82726a (patch) | |
tree | 9f7c75abb2c3de0e1f856cdd4179518f1603b04b /hw/usb-bus.c | |
parent | 7755260f015dcbb8fa47bcc6f1e2ca317b2205aa (diff) |
usb: claim port at device initialization time.
This patch makes qemu assign a port when creating the device, not when
attaching it. For most usb devices this isn't a noticable difference
because they are in attached state all the time.
The change affects usb-host devices which live in detached state while
the real device is unplugged from the host. They have a fixed port
assigned all the time now instead of getting grabbing one on attach and
releasing it at detach, i.e. they stop floating around at the usb bus.
The change also allows to simplify usb-hub. It doesn't need the
handle_attach() callback any more to configure the downstream ports.
This can be done at device initialitation time now. The changed
initialization order (first grab upstream port, then register downstream
ports) also fixes some icky corner cases. For example it is not possible
any more to plug the hub into one of its own downstream ports.
The usb host adapters must care too. USBPort->dev being non-NULL
doesn't imply any more the device is in attached state. The host
adapters must additionally check the USBPort->dev->attached flag.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw/usb-bus.c')
-rw-r--r-- | hw/usb-bus.c | 110 |
1 files changed, 64 insertions, 46 deletions
diff --git a/hw/usb-bus.c b/hw/usb-bus.c index c0bbc7c6b0..93f640d370 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -3,6 +3,7 @@ #include "qdev.h" #include "sysemu.h" #include "monitor.h" +#include "trace.h" static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); @@ -73,9 +74,13 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base) dev->info = info; dev->auto_attach = 1; QLIST_INIT(&dev->strings); - rc = dev->info->init(dev); - if (rc == 0 && dev->auto_attach) + rc = usb_claim_port(dev); + if (rc == 0) { + rc = dev->info->init(dev); + } + if (rc == 0 && dev->auto_attach) { rc = usb_device_attach(dev); + } return rc; } @@ -89,6 +94,9 @@ static int usb_qdev_exit(DeviceState *qdev) if (dev->info->handle_destroy) { dev->info->handle_destroy(dev); } + if (dev->port) { + usb_release_port(dev); + } return 0; } @@ -205,21 +213,13 @@ void usb_unregister_port(USBBus *bus, USBPort *port) bus->nfree--; } -static int do_attach(USBDevice *dev) +int usb_claim_port(USBDevice *dev) { USBBus *bus = usb_bus_from_device(dev); USBPort *port; - if (dev->attached) { - error_report("Error: tried to attach usb device %s twice\n", - dev->product_desc); - return -1; - } - if (bus->nfree == 0) { - error_report("Error: tried to attach usb device %s to a bus with no free ports\n", - dev->product_desc); - return -1; - } + assert(dev->port == NULL); + if (dev->port_path) { QTAILQ_FOREACH(port, &bus->free, next) { if (strcmp(port->path, dev->port_path) == 0) { @@ -227,68 +227,86 @@ static int do_attach(USBDevice *dev) } } if (port == NULL) { - error_report("Error: usb port %s (bus %s) not found\n", - dev->port_path, bus->qbus.name); + error_report("Error: usb port %s (bus %s) not found (in use?)\n", + dev->port_path, bus->qbus.name); return -1; } } else { + if (bus->nfree == 1 && strcmp(dev->qdev.info->name, "usb-hub") != 0) { + /* Create a new hub and chain it on */ + usb_create_simple(bus, "usb-hub"); + } + if (bus->nfree == 0) { + error_report("Error: tried to attach usb device %s to a bus " + "with no free ports\n", dev->product_desc); + return -1; + } port = QTAILQ_FIRST(&bus->free); } - if (!(port->speedmask & dev->speedmask)) { - error_report("Warning: speed mismatch trying to attach usb device %s to bus %s\n", - dev->product_desc, bus->qbus.name); - return -1; - } + trace_usb_port_claim(bus->busnr, port->path); - dev->attached++; QTAILQ_REMOVE(&bus->free, port, next); bus->nfree--; - usb_attach(port, dev); + dev->port = port; + port->dev = dev; QTAILQ_INSERT_TAIL(&bus->used, port, next); bus->nused++; - return 0; } -int usb_device_attach(USBDevice *dev) +void usb_release_port(USBDevice *dev) { USBBus *bus = usb_bus_from_device(dev); + USBPort *port = dev->port; - if (bus->nfree == 1 && dev->port_path == NULL) { - /* Create a new hub and chain it on - (unless a physical port location is specified). */ - usb_create_simple(bus, "usb-hub"); - } - return do_attach(dev); + assert(port != NULL); + trace_usb_port_release(bus->busnr, port->path); + + QTAILQ_REMOVE(&bus->used, port, next); + bus->nused--; + + dev->port = NULL; + port->dev = NULL; + + QTAILQ_INSERT_TAIL(&bus->free, port, next); + bus->nfree++; } -int usb_device_detach(USBDevice *dev) +int usb_device_attach(USBDevice *dev) { USBBus *bus = usb_bus_from_device(dev); - USBPort *port; + USBPort *port = dev->port; - if (!dev->attached) { - error_report("Error: tried to detach unattached usb device %s\n", - dev->product_desc); + assert(port != NULL); + assert(!dev->attached); + trace_usb_port_attach(bus->busnr, port->path); + + if (!(port->speedmask & dev->speedmask)) { + error_report("Warning: speed mismatch trying to attach " + "usb device %s to bus %s\n", + dev->product_desc, bus->qbus.name); return -1; } - dev->attached--; - QTAILQ_FOREACH(port, &bus->used, next) { - if (port->dev == dev) - break; - } - assert(port != NULL); + dev->attached++; + usb_attach(port); - QTAILQ_REMOVE(&bus->used, port, next); - bus->nused--; + return 0; +} + +int usb_device_detach(USBDevice *dev) +{ + USBBus *bus = usb_bus_from_device(dev); + USBPort *port = dev->port; - usb_attach(port, NULL); + assert(port != NULL); + assert(dev->attached); + trace_usb_port_detach(bus->busnr, port->path); - QTAILQ_INSERT_TAIL(&bus->free, port, next); - bus->nfree++; + usb_detach(port); + dev->attached--; return 0; } |