diff --git a/hw/usb-bus.c b/hw/usb-bus.c index 480956dfcf..2abce12de5 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -75,7 +75,7 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base) QLIST_INIT(&dev->strings); rc = dev->info->init(dev); if (rc == 0 && dev->auto_attach) - usb_device_attach(dev); + rc = usb_device_attach(dev); return rc; } @@ -121,7 +121,7 @@ USBDevice *usb_create(USBBus *bus, const char *name) bus = usb_bus_find(-1); if (!bus) return NULL; - fprintf(stderr, "%s: no bus specified, using \"%s\" for \"%s\"\n", + error_report("%s: no bus specified, using \"%s\" for \"%s\"\n", __FUNCTION__, bus->qbus.name, name); } #endif @@ -171,15 +171,20 @@ void usb_unregister_port(USBBus *bus, USBPort *port) bus->nfree--; } -static void do_attach(USBDevice *dev) +static int 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", + error_report("Error: tried to attach usb device %s twice\n", dev->product_desc); - return; + 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; } if (dev->port_path) { QTAILQ_FOREACH(port, &bus->free, next) { @@ -188,13 +193,18 @@ static void do_attach(USBDevice *dev) } } if (port == NULL) { - fprintf(stderr, "Warning: usb port %s (bus %s) not found\n", + error_report("Error: usb port %s (bus %s) not found\n", dev->port_path, bus->qbus.name); - return; + return -1; } } else { 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; + } dev->attached++; QTAILQ_REMOVE(&bus->free, port, next); @@ -204,6 +214,8 @@ static void do_attach(USBDevice *dev) QTAILQ_INSERT_TAIL(&bus->used, port, next); bus->nused++; + + return 0; } int usb_device_attach(USBDevice *dev) @@ -215,8 +227,7 @@ int usb_device_attach(USBDevice *dev) (unless a physical port location is specified). */ usb_create_simple(bus, "usb-hub"); } - do_attach(dev); - return 0; + return do_attach(dev); } int usb_device_detach(USBDevice *dev) @@ -225,7 +236,7 @@ int usb_device_detach(USBDevice *dev) USBPort *port; if (!dev->attached) { - fprintf(stderr, "Warning: tried to detach unattached usb device %s\n", + error_report("Error: tried to detach unattached usb device %s\n", dev->product_desc); return -1; } diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index 524b841da1..d3922998c5 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -1271,6 +1271,7 @@ static int ccid_initfn(USBDevice *dev) s->migration_target_ip = 0; s->migration_target_port = 0; s->dev.speed = USB_SPEED_FULL; + s->dev.speedmask = USB_SPEED_MASK_FULL; s->notify_slot_change = false; s->powered = true; s->pending_answers_num = 0; diff --git a/hw/usb-desc.c b/hw/usb-desc.c index e4a4680fee..bc6858f62f 100644 --- a/hw/usb-desc.c +++ b/hw/usb-desc.c @@ -242,7 +242,17 @@ static void usb_desc_setdefaults(USBDevice *dev) void usb_desc_init(USBDevice *dev) { + const USBDesc *desc = dev->info->usb_desc; + + assert(desc != NULL); dev->speed = USB_SPEED_FULL; + dev->speedmask = 0; + if (desc->full) { + dev->speedmask |= USB_SPEED_MASK_FULL; + } + if (desc->high) { + dev->speedmask |= USB_SPEED_MASK_HIGH; + } usb_desc_setdefaults(dev); } @@ -375,6 +385,10 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len trace_usb_desc_other_speed_config(dev->addr, index, len, ret); break; + case USB_DT_DEBUG: + /* ignore silently */ + break; + default: fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__, dev->addr, type, len); diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index e33e546b43..91fb7dea93 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -130,7 +130,7 @@ #define PORTSC_CONNECT (1 << 0) // Current Connect Status #define FRAME_TIMER_FREQ 1000 -#define FRAME_TIMER_USEC (1000000 / FRAME_TIMER_FREQ) +#define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ) #define NB_MAXINTRATE 8 // Max rate at which controller issues ints #define NB_PORTS 4 // Number of downstream ports @@ -348,7 +348,8 @@ struct EHCIQueue { EHCIState *ehci; QTAILQ_ENTRY(EHCIQueue) next; bool async_schedule; - uint32_t seen, ts; + uint32_t seen; + uint64_t ts; /* cached data from guest - needs to be flushed * when guest removes an entry (doorbell, handshake sequence) @@ -373,6 +374,11 @@ struct EHCIState { target_phys_addr_t mem_base; int mem; int num_ports; + + /* properties */ + uint32_t freq; + uint32_t maxframes; + /* * EHCI spec version 1.0 Section 2.3 * Host Controller Operational Registers @@ -413,12 +419,11 @@ struct EHCIState { uint8_t ibuffer[BUFF_SIZE]; int isoch_pause; - uint32_t last_run_usec; - uint32_t frame_end_usec; + uint64_t last_run_ns; }; #define SET_LAST_RUN_CLOCK(s) \ - (s)->last_run_usec = qemu_get_clock_ns(vm_clock) / 1000; + (s)->last_run_ns = qemu_get_clock_ns(vm_clock); /* nifty macros from Arnon's EHCI version */ #define get_field(data, field) \ @@ -685,10 +690,10 @@ static void ehci_queues_rip_unused(EHCIState *ehci) QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { if (q->seen) { q->seen = 0; - q->ts = ehci->last_run_usec; + q->ts = ehci->last_run_ns; continue; } - if (ehci->last_run_usec < q->ts + 250000) { + if (ehci->last_run_ns < q->ts + 250000000) { /* allow 0.25 sec idle */ continue; } @@ -2040,23 +2045,16 @@ static void ehci_frame_timer(void *opaque) { EHCIState *ehci = opaque; int64_t expire_time, t_now; - int usec_elapsed; + uint64_t ns_elapsed; int frames; - int usec_now; int i; int skipped_frames = 0; - t_now = qemu_get_clock_ns(vm_clock); - expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ); - if (expire_time == t_now) { - expire_time++; - } + expire_time = t_now + (get_ticks_per_sec() / ehci->freq); - usec_now = t_now / 1000; - usec_elapsed = usec_now - ehci->last_run_usec; - frames = usec_elapsed / FRAME_TIMER_USEC; - ehci->frame_end_usec = usec_now + FRAME_TIMER_USEC - 10; + ns_elapsed = t_now - ehci->last_run_ns; + frames = ns_elapsed / FRAME_TIMER_NS; for (i = 0; i < frames; i++) { if ( !(ehci->usbsts & USBSTS_HALT)) { @@ -2073,13 +2071,13 @@ static void ehci_frame_timer(void *opaque) ehci->sofv &= 0x000003ff; } - if (frames - i > 10) { + if (frames - i > ehci->maxframes) { skipped_frames++; } else { ehci_advance_periodic_state(ehci); } - ehci->last_run_usec += FRAME_TIMER_USEC; + ehci->last_run_ns += FRAME_TIMER_NS; } #if 0 @@ -2146,6 +2144,11 @@ static PCIDeviceInfo ehci_info = { .device_id = PCI_DEVICE_ID_INTEL_82801D, .revision = 0x10, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ), + DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128), + DEFINE_PROP_END_OF_LIST(), + }, }; static int usb_ehci_initfn(PCIDevice *dev) diff --git a/hw/usb-msd.c b/hw/usb-msd.c index c59797b27e..86582cc723 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -51,6 +51,7 @@ typedef struct { SCSIRequest *req; SCSIBus bus; BlockConf conf; + char *serial; SCSIDevice *scsi_dev; uint32_t removable; int result; @@ -497,8 +498,9 @@ static void usb_msd_password_cb(void *opaque, int err) MSDState *s = opaque; if (!err) - usb_device_attach(&s->dev); - else + err = usb_device_attach(&s->dev); + + if (err) qdev_unplug(&s->dev.qdev); } @@ -531,9 +533,15 @@ static int usb_msd_initfn(USBDevice *dev) bdrv_detach(bs, &s->dev.qdev); s->conf.bs = NULL; - dinfo = drive_get_by_blockdev(bs); - if (dinfo && dinfo->serial) { - usb_desc_set_string(dev, STR_SERIALNUMBER, dinfo->serial); + if (!s->serial) { + /* try to fall back to value set with legacy -drive serial=... */ + dinfo = drive_get_by_blockdev(bs); + if (*dinfo->serial) { + s->serial = strdup(dinfo->serial); + } + } + if (s->serial) { + usb_desc_set_string(dev, STR_SERIALNUMBER, s->serial); } usb_desc_init(dev); @@ -632,6 +640,7 @@ static struct USBDeviceInfo msd_info = { .usbdevice_init = usb_msd_init, .qdev.props = (Property[]) { DEFINE_BLOCK_PROPERTIES(MSDState, conf), + DEFINE_PROP_STRING("serial", MSDState, serial), DEFINE_PROP_BIT("removable", MSDState, removable, 0, false), DEFINE_PROP_END_OF_LIST(), }, diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 5d2ae01235..1c29b9fa6c 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -373,14 +373,25 @@ static void ohci_wakeup(USBDevice *dev) OHCIState *s = container_of(bus, OHCIState, bus); int portnum = dev->port->index; OHCIPort *port = &s->rhport[portnum]; + uint32_t intr = 0; if (port->ctrl & OHCI_PORT_PSS) { DPRINTF("usb-ohci: port %d: wakeup\n", portnum); port->ctrl |= OHCI_PORT_PSSC; port->ctrl &= ~OHCI_PORT_PSS; - if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { - ohci_set_interrupt(s, OHCI_INTR_RD); - } + intr = OHCI_INTR_RHSC; } + /* Note that the controller can be suspended even if this port is not */ + if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { + DPRINTF("usb-ohci: remote-wakeup: SUSPEND->RESUME\n"); + /* This is the one state transition the controller can do by itself */ + s->ctl &= ~OHCI_CTL_HCFS; + s->ctl |= OHCI_USB_RESUME; + /* In suspend mode only ResumeDetected is possible, not RHSC: + * see the OHCI spec 5.1.2.3. + */ + intr = OHCI_INTR_RD; + } + ohci_set_interrupt(s, intr); } /* Reset the controller */ diff --git a/hw/usb.h b/hw/usb.h index 06ce05826a..076e2ffce4 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -130,6 +130,7 @@ #define USB_DT_ENDPOINT 0x05 #define USB_DT_DEVICE_QUALIFIER 0x06 #define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_DEBUG 0x0A #define USB_DT_INTERFACE_ASSOC 0x0B #define USB_ENDPOINT_XFER_CONTROL 0 @@ -168,7 +169,10 @@ struct USBDevice { char *port_path; void *opaque; + /* Actual connected speed */ int speed; + /* Supported speeds, not in info because it may be variable (hostdevs) */ + int speedmask; uint8_t addr; char product_desc[32]; int auto_attach; diff --git a/usb-bsd.c b/usb-bsd.c index c1bcc4a1da..3b97eb491b 100644 --- a/usb-bsd.c +++ b/usb-bsd.c @@ -367,8 +367,10 @@ USBDevice *usb_host_device_open(const char *devname) if (dev_info.udi_speed == 1) { dev->dev.speed = USB_SPEED_LOW - 1; + dev->dev.speedmask = USB_SPEED_MASK_LOW; } else { dev->dev.speed = USB_SPEED_FULL - 1; + dev->dev.speedmask = USB_SPEED_MASK_FULL; } if (strncmp(dev_info.udi_product, "product", 7) != 0) { diff --git a/usb-linux.c b/usb-linux.c index 5d2ec5c5c7..1a2deb35c9 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -85,7 +85,6 @@ static int usb_fs_type; /* endpoint association data */ #define ISO_FRAME_DESC_PER_URB 32 -#define ISO_URB_COUNT 3 #define INVALID_EP_TYPE 255 /* devio.c limits single requests to 16k */ @@ -101,6 +100,7 @@ struct endp_data { int iso_urb_idx; int iso_buffer_used; int max_packet_size; + int inflight; }; struct USBAutoFilter { @@ -120,6 +120,7 @@ typedef struct USBHostDevice { int configuration; int ninterfaces; int closing; + uint32_t iso_urb_count; Notifier exit; struct endp_data endp_table[MAX_ENDPOINTS]; @@ -142,74 +143,91 @@ static void usb_host_auto_check(void *unused); static int usb_host_read_file(char *line, size_t line_size, const char *device_file, const char *device_name); +static struct endp_data *get_endp(USBHostDevice *s, int ep) +{ + return s->endp_table + ep - 1; +} + static int is_isoc(USBHostDevice *s, int ep) { - return s->endp_table[ep - 1].type == USBDEVFS_URB_TYPE_ISO; + return get_endp(s, ep)->type == USBDEVFS_URB_TYPE_ISO; } static int is_valid(USBHostDevice *s, int ep) { - return s->endp_table[ep - 1].type != INVALID_EP_TYPE; + return get_endp(s, ep)->type != INVALID_EP_TYPE; } static int is_halted(USBHostDevice *s, int ep) { - return s->endp_table[ep - 1].halted; + return get_endp(s, ep)->halted; } static void clear_halt(USBHostDevice *s, int ep) { - s->endp_table[ep - 1].halted = 0; + get_endp(s, ep)->halted = 0; } static void set_halt(USBHostDevice *s, int ep) { - s->endp_table[ep - 1].halted = 1; + get_endp(s, ep)->halted = 1; } static int is_iso_started(USBHostDevice *s, int ep) { - return s->endp_table[ep - 1].iso_started; + return get_endp(s, ep)->iso_started; } static void clear_iso_started(USBHostDevice *s, int ep) { - s->endp_table[ep - 1].iso_started = 0; + get_endp(s, ep)->iso_started = 0; } static void set_iso_started(USBHostDevice *s, int ep) { - s->endp_table[ep - 1].iso_started = 1; + struct endp_data *e = get_endp(s, ep); + if (!e->iso_started) { + e->iso_started = 1; + e->inflight = 0; + } +} + +static int change_iso_inflight(USBHostDevice *s, int ep, int value) +{ + struct endp_data *e = get_endp(s, ep); + + e->inflight += value; + return e->inflight; } static void set_iso_urb(USBHostDevice *s, int ep, AsyncURB *iso_urb) { - s->endp_table[ep - 1].iso_urb = iso_urb; + get_endp(s, ep)->iso_urb = iso_urb; } static AsyncURB *get_iso_urb(USBHostDevice *s, int ep) { - return s->endp_table[ep - 1].iso_urb; + return get_endp(s, ep)->iso_urb; } static void set_iso_urb_idx(USBHostDevice *s, int ep, int i) { - s->endp_table[ep - 1].iso_urb_idx = i; + get_endp(s, ep)->iso_urb_idx = i; } static int get_iso_urb_idx(USBHostDevice *s, int ep) { - return s->endp_table[ep - 1].iso_urb_idx; + return get_endp(s, ep)->iso_urb_idx; } static void set_iso_buffer_used(USBHostDevice *s, int ep, int i) { - s->endp_table[ep - 1].iso_buffer_used = i; + get_endp(s, ep)->iso_buffer_used = i; } static int get_iso_buffer_used(USBHostDevice *s, int ep) { - return s->endp_table[ep - 1].iso_buffer_used; + return get_endp(s, ep)->iso_buffer_used; } static void set_max_packet_size(USBHostDevice *s, int ep, uint8_t *descriptor) @@ -223,14 +241,12 @@ static void set_max_packet_size(USBHostDevice *s, int ep, uint8_t *descriptor) case 2: microframes = 3; break; default: microframes = 1; break; } - DPRINTF("husb: max packet size: 0x%x -> %d x %d\n", - raw, microframes, size); - s->endp_table[ep - 1].max_packet_size = size * microframes; + get_endp(s, ep)->max_packet_size = size * microframes; } static int get_max_packet_size(USBHostDevice *s, int ep) { - return s->endp_table[ep - 1].max_packet_size; + return get_endp(s, ep)->max_packet_size; } /* @@ -279,6 +295,7 @@ static void async_complete(void *opaque) { USBHostDevice *s = opaque; AsyncURB *aurb; + int urbs = 0; while (1) { USBPacket *p; @@ -286,6 +303,9 @@ static void async_complete(void *opaque) int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb); if (r < 0) { if (errno == EAGAIN) { + if (urbs > 2) { + fprintf(stderr, "husb: %d iso urbs finished at once\n", urbs); + } return; } if (errno == ENODEV && !s->closing) { @@ -303,10 +323,16 @@ static void async_complete(void *opaque) /* If this is a buffered iso urb mark it as complete and don't do anything else (it is handled further in usb_host_handle_iso_data) */ if (aurb->iso_frame_idx == -1) { + int inflight; if (aurb->urb.status == -EPIPE) { set_halt(s, aurb->urb.endpoint & 0xf); } aurb->iso_frame_idx = 0; + urbs++; + inflight = change_iso_inflight(s, aurb->urb.endpoint & 0xf, -1); + if (inflight == 0 && is_iso_started(s, aurb->urb.endpoint & 0xf)) { + fprintf(stderr, "husb: out of buffers for iso stream\n"); + } continue; } @@ -502,8 +528,8 @@ static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, uint8_t ep, int in) AsyncURB *aurb; int i, j, len = get_max_packet_size(s, ep); - aurb = qemu_mallocz(ISO_URB_COUNT * sizeof(*aurb)); - for (i = 0; i < ISO_URB_COUNT; i++) { + aurb = qemu_mallocz(s->iso_urb_count * sizeof(*aurb)); + for (i = 0; i < s->iso_urb_count; i++) { aurb[i].urb.endpoint = ep; aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len; aurb[i].urb.buffer = qemu_malloc(aurb[i].urb.buffer_length); @@ -533,7 +559,7 @@ static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep) return; } - for (i = 0; i < ISO_URB_COUNT; i++) { + for (i = 0; i < s->iso_urb_count; i++) { /* in flight? */ if (aurb[i].iso_frame_idx == -1) { ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]); @@ -551,7 +577,7 @@ static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep) async_complete(s); } - for (i = 0; i < ISO_URB_COUNT; i++) { + for (i = 0; i < s->iso_urb_count; i++) { qemu_free(aurb[i].urb.buffer); } @@ -636,7 +662,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) } aurb[i].iso_frame_idx++; if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { - i = (i + 1) % ISO_URB_COUNT; + i = (i + 1) % s->iso_urb_count; set_iso_urb_idx(s, p->devep, i); } } else { @@ -649,7 +675,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) if (is_iso_started(s, p->devep)) { /* (Re)-submit all fully consumed / filled urbs */ - for (i = 0; i < ISO_URB_COUNT; i++) { + for (i = 0; i < s->iso_urb_count; i++) { if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]); if (ret < 0) { @@ -667,6 +693,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) break; } aurb[i].iso_frame_idx = -1; + change_iso_inflight(s, p->devep, +1); } } } @@ -1061,6 +1088,42 @@ static int usb_linux_update_endp_table(USBHostDevice *s) return 0; } +/* + * Check if we can safely redirect a usb2 device to a usb1 virtual controller, + * this function assumes this is safe, if: + * 1) There are no isoc endpoints + * 2) There are no interrupt endpoints with a max_packet_size > 64 + * Note bulk endpoints with a max_packet_size > 64 in theory also are not + * usb1 compatible, but in practice this seems to work fine. + */ +static int usb_linux_full_speed_compat(USBHostDevice *dev) +{ + int i, packet_size; + + /* + * usb_linux_update_endp_table only registers info about ep in the current + * interface altsettings, so we need to parse the descriptors again. + */ + for (i = 0; (i + 5) < dev->descr_len; i += dev->descr[i]) { + if (dev->descr[i + 1] == USB_DT_ENDPOINT) { + switch (dev->descr[i + 3] & 0x3) { + case 0x00: /* CONTROL */ + break; + case 0x01: /* ISO */ + return 0; + case 0x02: /* BULK */ + break; + case 0x03: /* INTERRUPT */ + packet_size = dev->descr[i + 4] + (dev->descr[i + 5] << 8); + if (packet_size > 64) + return 0; + break; + } + } + } + return 1; +} + static int usb_host_open(USBHostDevice *dev, int bus_num, int addr, char *port, const char *prod_name, int speed) { @@ -1140,6 +1203,10 @@ static int usb_host_open(USBHostDevice *dev, int bus_num, } } dev->dev.speed = speed; + dev->dev.speedmask = (1 << speed); + if (dev->dev.speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) { + dev->dev.speedmask |= USB_SPEED_MASK_FULL; + } printf("husb: grabbed usb device %d.%d\n", bus_num, addr); @@ -1151,10 +1218,14 @@ static int usb_host_open(USBHostDevice *dev, int bus_num, prod_name); } + ret = usb_device_attach(&dev->dev); + if (ret) { + goto fail; + } + /* USB devio uses 'write' flag to check for async completions */ qemu_set_fd_handler(dev->fd, NULL, async_complete, dev); - usb_device_attach(&dev->dev); return 0; fail: @@ -1230,6 +1301,7 @@ static struct USBDeviceInfo usb_host_dev_info = { DEFINE_PROP_STRING("hostport", USBHostDevice, match.port), DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0), DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0), + DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4), DEFINE_PROP_END_OF_LIST(), }, };