diff --git a/hw/bt-hid.c b/hw/bt-hid.c index 6f37705494..020176eacc 100644 --- a/hw/bt-hid.c +++ b/hw/bt-hid.c @@ -566,5 +566,6 @@ static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net) { - return bt_hid_init(net, usb_keyboard_init(), class_keyboard); + USBDevice *dev = usb_create_simple(NULL /* FIXME */, "QEMU USB Keyboard"); + return bt_hid_init(net, dev, class_keyboard); } diff --git a/hw/usb-bus.c b/hw/usb-bus.c index c695a379cd..169fb2f58d 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -1,10 +1,15 @@ #include "hw.h" #include "usb.h" #include "qdev.h" +#include "sysemu.h" +#include "monitor.h" + +static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); static struct BusInfo usb_bus_info = { - .name = "USB", - .size = sizeof(USBBus), + .name = "USB", + .size = sizeof(USBBus), + .print_dev = usb_bus_dev_print, }; static int next_usb_bus = 0; static TAILQ_HEAD(, USBBus) busses = TAILQ_HEAD_INITIALIZER(busses); @@ -43,6 +48,8 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base) pstrcpy(dev->devname, sizeof(dev->devname), qdev->info->name); dev->info = info; rc = dev->info->init(dev); + if (rc == 0) + usb_device_attach(dev); return rc; } @@ -61,7 +68,7 @@ void usb_qdev_register_many(USBDeviceInfo *info) } } -USBDevice *usb_create_simple(USBBus *bus, const char *name) +USBDevice *usb_create(USBBus *bus, const char *name) { DeviceState *dev; @@ -77,6 +84,131 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name) #endif dev = qdev_create(&bus->qbus, name); - qdev_init(dev); return DO_UPCAST(USBDevice, qdev, dev); } + +USBDevice *usb_create_simple(USBBus *bus, const char *name) +{ + USBDevice *dev = usb_create(bus, name); + qdev_init(&dev->qdev); + return dev; +} + +void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, + usb_attachfn attach) +{ + port->opaque = opaque; + port->index = index; + port->attach = attach; + TAILQ_INSERT_TAIL(&bus->free, port, next); + bus->nfree++; +} + +static void do_attach(USBDevice *dev) +{ + USBBus *bus = usb_bus_from_device(dev); + USBPort *port; + + if (dev->attached) { + fprintf(stderr, "Warning: tried to attach usb device %s twice\n", + dev->devname); + return; + } + dev->attached++; + + port = TAILQ_FIRST(&bus->free); + TAILQ_REMOVE(&bus->free, port, next); + bus->nfree--; + + usb_attach(port, dev); + + TAILQ_INSERT_TAIL(&bus->used, port, next); + bus->nused++; +} + +int usb_device_attach(USBDevice *dev) +{ + USBBus *bus = usb_bus_from_device(dev); + USBDevice *hub; + + if (bus->nfree == 1) { + /* Create a new hub and chain it on. */ + hub = usb_create_simple(bus, "QEMU USB Hub"); + } + do_attach(dev); + return 0; +} + +int usb_device_delete_addr(int busnr, int addr) +{ + USBBus *bus; + USBPort *port; + USBDevice *dev; + + bus = usb_bus_find(busnr); + if (!bus) + return -1; + + TAILQ_FOREACH(port, &bus->used, next) { + if (port->dev->addr == addr) + break; + } + if (!port) + return -1; + + dev = port->dev; + TAILQ_REMOVE(&bus->used, port, next); + bus->nused--; + + usb_attach(port, NULL); + dev->info->handle_destroy(dev); + + TAILQ_INSERT_TAIL(&bus->free, port, next); + bus->nfree++; + return 0; +} + +static const char *usb_speed(unsigned int speed) +{ + static const char *txt[] = { + [ USB_SPEED_LOW ] = "1.5", + [ USB_SPEED_FULL ] = "12", + [ USB_SPEED_HIGH ] = "480", + }; + if (speed >= ARRAY_SIZE(txt)) + return "?"; + return txt[speed]; +} + +static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) +{ + USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev); + USBBus *bus = usb_bus_from_device(dev); + + monitor_printf(mon, "%*saddr %d.%d, speed %s, name %s\n", indent, "", + bus->busnr, dev->addr, + usb_speed(dev->speed), dev->devname); +} + +void usb_info(Monitor *mon) +{ + USBBus *bus; + USBDevice *dev; + USBPort *port; + + if (TAILQ_EMPTY(&busses)) { + monitor_printf(mon, "USB support not enabled\n"); + return; + } + + TAILQ_FOREACH(bus, &busses, next) { + TAILQ_FOREACH(port, &bus->used, next) { + dev = port->dev; + if (!dev) + continue; + monitor_printf(mon, " Device %d.%d, Speed %s Mb/s, Product %s\n", + bus->busnr, dev->addr, usb_speed(dev->speed), dev->devname); + } + } +} + diff --git a/hw/usb-hid.c b/hw/usb-hid.c index c8b6ee7cd1..0837ec1587 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -871,21 +871,6 @@ static int usb_keyboard_initfn(USBDevice *dev) return usb_hid_initfn(dev, USB_KEYBOARD); } -USBDevice *usb_tablet_init(void) -{ - return usb_create_simple(NULL /* FIXME */, "QEMU USB Tablet"); -} - -USBDevice *usb_mouse_init(void) -{ - return usb_create_simple(NULL /* FIXME */, "QEMU USB Mouse"); -} - -USBDevice *usb_keyboard_init(void) -{ - return usb_create_simple(NULL /* FIXME */, "QEMU USB Keyboard"); -} - void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *)) { USBHIDState *s = (USBHIDState *)dev; diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 116b1d2107..0a39986468 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -531,18 +531,14 @@ static int usb_hub_initfn(USBDevice *dev) s->nb_ports = MAX_PORTS; /* FIXME: make configurable */ for (i = 0; i < s->nb_ports; i++) { port = &s->ports[i]; - qemu_register_usb_port(&port->port, s, i, usb_hub_attach); + usb_register_port(usb_bus_from_device(dev), + &port->port, s, i, usb_hub_attach); port->wPortStatus = PORT_STAT_POWER; port->wPortChange = 0; } return 0; } -USBDevice *usb_hub_init(int nb_ports) -{ - return usb_create_simple(NULL /* FIXME */, "QEMU USB Hub"); -} - static struct USBDeviceInfo hub_info = { .qdev.name = "QEMU USB Hub", .qdev.size = sizeof(USBHubState), diff --git a/hw/usb-musb.c b/hw/usb-musb.c index b23ed3f313..8fba84dadf 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -281,6 +281,7 @@ typedef struct { struct MUSBState { qemu_irq *irqs; + USBBus *bus; USBPort port; int idx; @@ -330,7 +331,8 @@ struct MUSBState { s->ep[i].epnum = i; } - qemu_register_usb_port(&s->port, s, 0, musb_attach); + s->bus = usb_bus_new(NULL /* FIXME */); + usb_register_port(s->bus, &s->port, s, 0, musb_attach); return s; } diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index e725e974b2..7f620c7d21 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -65,6 +65,7 @@ enum ohci_type { }; typedef struct { + USBBus *bus; qemu_irq irq; enum ohci_type type; int mem; @@ -1688,9 +1689,10 @@ static void usb_ohci_init(OHCIState *ohci, int num_ports, int devfn, ohci->irq = irq; ohci->type = type; + ohci->bus = usb_bus_new(NULL /* FIXME */); ohci->num_ports = num_ports; for (i = 0; i < num_ports; i++) { - qemu_register_usb_port(&ohci->rhport[i].port, ohci, i, ohci_attach); + usb_register_port(ohci->bus, &ohci->rhport[i].port, ohci, i, ohci_attach); } ohci->async_td = 0; diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index a112a6960e..2ff287b30f 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -122,6 +122,7 @@ typedef struct UHCIPort { typedef struct UHCIState { PCIDevice dev; + USBBus *bus; uint16_t cmd; /* cmd register */ uint16_t status; uint16_t intr; /* interrupt enable register */ @@ -1089,8 +1090,9 @@ void usb_uhci_piix3_init(PCIBus *bus, int devfn) pci_conf[0x3d] = 4; // interrupt pin 3 pci_conf[0x60] = 0x10; // release number + s->bus = usb_bus_new(NULL /* FIXME */); for(i = 0; i < NB_PORTS; i++) { - qemu_register_usb_port(&s->ports[i].port, s, i, uhci_attach); + usb_register_port(s->bus, &s->ports[i].port, s, i, uhci_attach); } s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); @@ -1124,8 +1126,9 @@ void usb_uhci_piix4_init(PCIBus *bus, int devfn) pci_conf[0x3d] = 4; // interrupt pin 3 pci_conf[0x60] = 0x10; // release number + s->bus = usb_bus_new(NULL /* FIXME */); for(i = 0; i < NB_PORTS; i++) { - qemu_register_usb_port(&s->ports[i].port, s, i, uhci_attach); + usb_register_port(s->bus, &s->ports[i].port, s, i, uhci_attach); } s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c index a5abb98f5c..4007e0fe5a 100644 --- a/hw/usb-wacom.c +++ b/hw/usb-wacom.c @@ -399,11 +399,6 @@ static int usb_wacom_initfn(USBDevice *dev) return 0; } -USBDevice *usb_wacom_init(void) -{ - return usb_create_simple(NULL /* FIXME */, "QEMU PenPartner Tablet"); -} - static struct USBDeviceInfo wacom_info = { .qdev.name = "QEMU PenPartner Tablet", .qdev.size = sizeof(USBWacomState), diff --git a/hw/usb.h b/hw/usb.h index dea57184c1..36326399cd 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -24,6 +24,7 @@ #include "block.h" #include "qdev.h" +#include "sys-queue.h" #define USB_TOKEN_SETUP 0x2d #define USB_TOKEN_IN 0x69 /* device -> host */ @@ -132,6 +133,7 @@ struct USBDevice { int speed; uint8_t addr; char devname[32]; + int attached; int state; uint8_t setup_buf[8]; @@ -191,7 +193,7 @@ struct USBPort { usb_attachfn attach; void *opaque; int index; /* internal port index, may be used with the opaque */ - struct USBPort *next; /* Used internally by qemu. */ + TAILQ_ENTRY(USBPort) next; }; typedef void USBCallback(USBPacket * packet, void *opaque); @@ -236,25 +238,17 @@ static inline void usb_cancel_packet(USBPacket * p) p->cancel_cb(p, p->cancel_opaque); } -int usb_device_add_dev(USBDevice *dev); -int usb_device_del_addr(int bus_num, int addr); void usb_attach(USBPort *port, USBDevice *dev); int usb_generic_handle_packet(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); void usb_send_msg(USBDevice *dev, int msg); -/* usb hub */ -USBDevice *usb_hub_init(int nb_ports); - /* usb-linux.c */ USBDevice *usb_host_device_open(const char *devname); int usb_host_device_close(const char *devname); void usb_host_info(Monitor *mon); /* usb-hid.c */ -USBDevice *usb_mouse_init(void); -USBDevice *usb_tablet_init(void); -USBDevice *usb_keyboard_init(void); void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *)); /* usb-msd.c */ @@ -267,17 +261,11 @@ USBDevice *usb_net_init(NICInfo *nd); /* usb-bt.c */ USBDevice *usb_bt_init(HCIInfo *hci); -/* usb-wacom.c */ -USBDevice *usb_wacom_init(void); - /* usb-serial.c */ USBDevice *usb_serial_init(const char *filename); /* usb ports of the VM */ -void qemu_register_usb_port(USBPort *port, void *opaque, int index, - usb_attachfn attach); - #define VM_USB_HUB_SIZE 8 /* usb-musb.c */ @@ -319,4 +307,14 @@ USBBus *usb_bus_new(DeviceState *host); USBBus *usb_bus_find(int busnr); void usb_qdev_register(USBDeviceInfo *info); void usb_qdev_register_many(USBDeviceInfo *info); +USBDevice *usb_create(USBBus *bus, const char *name); USBDevice *usb_create_simple(USBBus *bus, const char *name); +void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, + usb_attachfn attach); +int usb_device_attach(USBDevice *dev); +int usb_device_delete_addr(int busnr, int addr); + +static inline USBBus *usb_bus_from_device(USBDevice *d) +{ + return DO_UPCAST(USBBus, qbus, d->qdev.parent_bus); +} diff --git a/usb-linux.c b/usb-linux.c index 7f6ca90d25..01d145d173 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -252,7 +252,7 @@ static void async_complete(void *opaque) if (errno == ENODEV && !s->closing) { printf("husb: device %d.%d disconnected\n", s->bus_num, s->addr); - usb_device_del_addr(0, s->dev.addr); + usb_device_delete_addr(s->bus_num, s->dev.addr); return; } @@ -909,7 +909,7 @@ static USBDevice *usb_host_device_open_addr(int bus_num, int addr, const char *p } dprintf("husb: opened %s\n", buf); - d = usb_create_simple(NULL /* FIXME */, "USB Host Device"); + d = usb_create(NULL /* FIXME */, "USB Host Device"); dev = DO_UPCAST(USBHostDevice, dev, d); dev->bus_num = bus_num; @@ -1039,16 +1039,16 @@ int usb_host_device_close(const char *devname) if (usb_host_find_device(&bus_num, &addr, product_name, sizeof(product_name), devname) < 0) return -1; - + s = hostdev_find(bus_num, addr); if (s) { - usb_device_del_addr(0, s->dev.addr); + usb_device_delete_addr(s->bus_num, s->dev.addr); return 0; } return -1; } - + static int get_tag_value(char *buf, int buf_size, const char *str, const char *tag, const char *stopchars) @@ -1387,7 +1387,7 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, dev = usb_host_device_open_addr(bus_num, addr, product_name); if (dev) - usb_device_add_dev(dev); + qdev_init(&dev->qdev); } return 0; diff --git a/vl.c b/vl.c index 062ecbde5d..215132a552 100644 --- a/vl.c +++ b/vl.c @@ -2434,72 +2434,56 @@ static void smp_parse(const char *optarg) /***********************************************************/ /* USB devices */ -static USBPort *used_usb_ports; -static USBPort *free_usb_ports; - -/* ??? Maybe change this to register a hub to keep track of the topology. */ -void qemu_register_usb_port(USBPort *port, void *opaque, int index, - usb_attachfn attach) -{ - port->opaque = opaque; - port->index = index; - port->attach = attach; - port->next = free_usb_ports; - free_usb_ports = port; -} - -int usb_device_add_dev(USBDevice *dev) -{ - USBPort *port; - - /* Find a USB port to add the device to. */ - port = free_usb_ports; - if (!port->next) { - USBDevice *hub; - - /* Create a new hub and chain it on. */ - free_usb_ports = NULL; - port->next = used_usb_ports; - used_usb_ports = port; - - hub = usb_hub_init(VM_USB_HUB_SIZE); - usb_attach(port, hub); - port = free_usb_ports; - } - - free_usb_ports = port->next; - port->next = used_usb_ports; - used_usb_ports = port; - usb_attach(port, dev); - return 0; -} - static void usb_msd_password_cb(void *opaque, int err) { USBDevice *dev = opaque; if (!err) - usb_device_add_dev(dev); + usb_device_attach(dev); else dev->info->handle_destroy(dev); } +static struct { + const char *name; + const char *qdev; +} usbdevs[] = { + { + .name = "mouse", + .qdev = "QEMU USB Mouse", + },{ + .name = "tablet", + .qdev = "QEMU USB Tablet", + },{ + .name = "keyboard", + .qdev = "QEMU USB Keyboard", + },{ + .name = "wacom-tablet", + .qdev = "QEMU PenPartner Tablet", + } +}; + static int usb_device_add(const char *devname, int is_hotplug) { const char *p; - USBDevice *dev; + USBBus *bus = usb_bus_find(-1 /* any */); + USBDevice *dev = NULL; + int i; - if (!free_usb_ports) + if (!usb_enabled) return -1; + /* simple devices which don't need extra care */ + for (i = 0; i < ARRAY_SIZE(usbdevs); i++) { + if (strcmp(devname, usbdevs[i].name) != 0) + continue; + dev = usb_create_simple(bus, usbdevs[i].qdev); + goto done; + } + + /* the other ones */ if (strstart(devname, "host:", &p)) { dev = usb_host_device_open(p); - } else if (!strcmp(devname, "mouse")) { - dev = usb_mouse_init(); - } else if (!strcmp(devname, "tablet")) { - dev = usb_tablet_init(); - } else if (!strcmp(devname, "keyboard")) { - dev = usb_keyboard_init(); } else if (strstart(devname, "disk:", &p)) { BlockDriverState *bs; @@ -2515,8 +2499,6 @@ static int usb_device_add(const char *devname, int is_hotplug) return 0; } } - } else if (!strcmp(devname, "wacom-tablet")) { - dev = usb_wacom_init(); } else if (strstart(devname, "serial:", &p)) { dev = usb_serial_init(p); #ifdef CONFIG_BRLAPI @@ -2539,37 +2521,7 @@ static int usb_device_add(const char *devname, int is_hotplug) if (!dev) return -1; - return usb_device_add_dev(dev); -} - -int usb_device_del_addr(int bus_num, int addr) -{ - USBPort *port; - USBPort **lastp; - USBDevice *dev; - - if (!used_usb_ports) - return -1; - - if (bus_num != 0) - return -1; - - lastp = &used_usb_ports; - port = used_usb_ports; - while (port && port->dev->addr != addr) { - lastp = &port->next; - port = port->next; - } - - if (!port) - return -1; - - dev = port->dev; - *lastp = port->next; - usb_attach(port, NULL); - dev->info->handle_destroy(dev); - port->next = free_usb_ports; - free_usb_ports = port; +done: return 0; } @@ -2581,7 +2533,7 @@ static int usb_device_del(const char *devname) if (strstart(devname, "host:", &p)) return usb_host_device_close(p); - if (!used_usb_ports) + if (!usb_enabled) return -1; p = strchr(devname, '.'); @@ -2590,7 +2542,7 @@ static int usb_device_del(const char *devname) bus_num = strtoul(devname, NULL, 0); addr = strtoul(p + 1, NULL, 0); - return usb_device_del_addr(bus_num, addr); + return usb_device_delete_addr(bus_num, addr); } static int usb_parse(const char *cmdline) @@ -2608,40 +2560,6 @@ void do_usb_del(Monitor *mon, const QDict *qdict) usb_device_del(qdict_get_str(qdict, "devname")); } -void usb_info(Monitor *mon) -{ - USBDevice *dev; - USBPort *port; - const char *speed_str; - - if (!usb_enabled) { - monitor_printf(mon, "USB support not enabled\n"); - return; - } - - for (port = used_usb_ports; port; port = port->next) { - dev = port->dev; - if (!dev) - continue; - switch(dev->speed) { - case USB_SPEED_LOW: - speed_str = "1.5"; - break; - case USB_SPEED_FULL: - speed_str = "12"; - break; - case USB_SPEED_HIGH: - speed_str = "480"; - break; - default: - speed_str = "?"; - break; - } - monitor_printf(mon, " Device %d.%d, Speed %s Mb/s, Product %s\n", - 0, dev->addr, speed_str, dev->devname); - } -} - /***********************************************************/ /* PCMCIA/Cardbus */