mirror of https://github.com/proxmox/mirror_qemu
Merge remote-tracking branch 'kraxel/usb.17' into staging
commit
656acddb39
31
hw/usb-bus.c
31
hw/usb-bus.c
|
@ -75,7 +75,7 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
|
||||||
QLIST_INIT(&dev->strings);
|
QLIST_INIT(&dev->strings);
|
||||||
rc = dev->info->init(dev);
|
rc = dev->info->init(dev);
|
||||||
if (rc == 0 && dev->auto_attach)
|
if (rc == 0 && dev->auto_attach)
|
||||||
usb_device_attach(dev);
|
rc = usb_device_attach(dev);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ USBDevice *usb_create(USBBus *bus, const char *name)
|
||||||
bus = usb_bus_find(-1);
|
bus = usb_bus_find(-1);
|
||||||
if (!bus)
|
if (!bus)
|
||||||
return NULL;
|
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);
|
__FUNCTION__, bus->qbus.name, name);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -171,15 +171,20 @@ void usb_unregister_port(USBBus *bus, USBPort *port)
|
||||||
bus->nfree--;
|
bus->nfree--;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_attach(USBDevice *dev)
|
static int do_attach(USBDevice *dev)
|
||||||
{
|
{
|
||||||
USBBus *bus = usb_bus_from_device(dev);
|
USBBus *bus = usb_bus_from_device(dev);
|
||||||
USBPort *port;
|
USBPort *port;
|
||||||
|
|
||||||
if (dev->attached) {
|
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);
|
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) {
|
if (dev->port_path) {
|
||||||
QTAILQ_FOREACH(port, &bus->free, next) {
|
QTAILQ_FOREACH(port, &bus->free, next) {
|
||||||
|
@ -188,13 +193,18 @@ static void do_attach(USBDevice *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (port == NULL) {
|
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);
|
dev->port_path, bus->qbus.name);
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
port = QTAILQ_FIRST(&bus->free);
|
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++;
|
dev->attached++;
|
||||||
QTAILQ_REMOVE(&bus->free, port, next);
|
QTAILQ_REMOVE(&bus->free, port, next);
|
||||||
|
@ -204,6 +214,8 @@ static void do_attach(USBDevice *dev)
|
||||||
|
|
||||||
QTAILQ_INSERT_TAIL(&bus->used, port, next);
|
QTAILQ_INSERT_TAIL(&bus->used, port, next);
|
||||||
bus->nused++;
|
bus->nused++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int usb_device_attach(USBDevice *dev)
|
int usb_device_attach(USBDevice *dev)
|
||||||
|
@ -215,8 +227,7 @@ int usb_device_attach(USBDevice *dev)
|
||||||
(unless a physical port location is specified). */
|
(unless a physical port location is specified). */
|
||||||
usb_create_simple(bus, "usb-hub");
|
usb_create_simple(bus, "usb-hub");
|
||||||
}
|
}
|
||||||
do_attach(dev);
|
return do_attach(dev);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int usb_device_detach(USBDevice *dev)
|
int usb_device_detach(USBDevice *dev)
|
||||||
|
@ -225,7 +236,7 @@ int usb_device_detach(USBDevice *dev)
|
||||||
USBPort *port;
|
USBPort *port;
|
||||||
|
|
||||||
if (!dev->attached) {
|
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);
|
dev->product_desc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1271,6 +1271,7 @@ static int ccid_initfn(USBDevice *dev)
|
||||||
s->migration_target_ip = 0;
|
s->migration_target_ip = 0;
|
||||||
s->migration_target_port = 0;
|
s->migration_target_port = 0;
|
||||||
s->dev.speed = USB_SPEED_FULL;
|
s->dev.speed = USB_SPEED_FULL;
|
||||||
|
s->dev.speedmask = USB_SPEED_MASK_FULL;
|
||||||
s->notify_slot_change = false;
|
s->notify_slot_change = false;
|
||||||
s->powered = true;
|
s->powered = true;
|
||||||
s->pending_answers_num = 0;
|
s->pending_answers_num = 0;
|
||||||
|
|
|
@ -242,7 +242,17 @@ static void usb_desc_setdefaults(USBDevice *dev)
|
||||||
|
|
||||||
void usb_desc_init(USBDevice *dev)
|
void usb_desc_init(USBDevice *dev)
|
||||||
{
|
{
|
||||||
|
const USBDesc *desc = dev->info->usb_desc;
|
||||||
|
|
||||||
|
assert(desc != NULL);
|
||||||
dev->speed = USB_SPEED_FULL;
|
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);
|
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);
|
trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case USB_DT_DEBUG:
|
||||||
|
/* ignore silently */
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__,
|
fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__,
|
||||||
dev->addr, type, len);
|
dev->addr, type, len);
|
||||||
|
|
|
@ -130,7 +130,7 @@
|
||||||
#define PORTSC_CONNECT (1 << 0) // Current Connect Status
|
#define PORTSC_CONNECT (1 << 0) // Current Connect Status
|
||||||
|
|
||||||
#define FRAME_TIMER_FREQ 1000
|
#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_MAXINTRATE 8 // Max rate at which controller issues ints
|
||||||
#define NB_PORTS 4 // Number of downstream ports
|
#define NB_PORTS 4 // Number of downstream ports
|
||||||
|
@ -348,7 +348,8 @@ struct EHCIQueue {
|
||||||
EHCIState *ehci;
|
EHCIState *ehci;
|
||||||
QTAILQ_ENTRY(EHCIQueue) next;
|
QTAILQ_ENTRY(EHCIQueue) next;
|
||||||
bool async_schedule;
|
bool async_schedule;
|
||||||
uint32_t seen, ts;
|
uint32_t seen;
|
||||||
|
uint64_t ts;
|
||||||
|
|
||||||
/* cached data from guest - needs to be flushed
|
/* cached data from guest - needs to be flushed
|
||||||
* when guest removes an entry (doorbell, handshake sequence)
|
* when guest removes an entry (doorbell, handshake sequence)
|
||||||
|
@ -373,6 +374,11 @@ struct EHCIState {
|
||||||
target_phys_addr_t mem_base;
|
target_phys_addr_t mem_base;
|
||||||
int mem;
|
int mem;
|
||||||
int num_ports;
|
int num_ports;
|
||||||
|
|
||||||
|
/* properties */
|
||||||
|
uint32_t freq;
|
||||||
|
uint32_t maxframes;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* EHCI spec version 1.0 Section 2.3
|
* EHCI spec version 1.0 Section 2.3
|
||||||
* Host Controller Operational Registers
|
* Host Controller Operational Registers
|
||||||
|
@ -413,12 +419,11 @@ struct EHCIState {
|
||||||
uint8_t ibuffer[BUFF_SIZE];
|
uint8_t ibuffer[BUFF_SIZE];
|
||||||
int isoch_pause;
|
int isoch_pause;
|
||||||
|
|
||||||
uint32_t last_run_usec;
|
uint64_t last_run_ns;
|
||||||
uint32_t frame_end_usec;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SET_LAST_RUN_CLOCK(s) \
|
#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 */
|
/* nifty macros from Arnon's EHCI version */
|
||||||
#define get_field(data, field) \
|
#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) {
|
QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) {
|
||||||
if (q->seen) {
|
if (q->seen) {
|
||||||
q->seen = 0;
|
q->seen = 0;
|
||||||
q->ts = ehci->last_run_usec;
|
q->ts = ehci->last_run_ns;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (ehci->last_run_usec < q->ts + 250000) {
|
if (ehci->last_run_ns < q->ts + 250000000) {
|
||||||
/* allow 0.25 sec idle */
|
/* allow 0.25 sec idle */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2040,23 +2045,16 @@ static void ehci_frame_timer(void *opaque)
|
||||||
{
|
{
|
||||||
EHCIState *ehci = opaque;
|
EHCIState *ehci = opaque;
|
||||||
int64_t expire_time, t_now;
|
int64_t expire_time, t_now;
|
||||||
int usec_elapsed;
|
uint64_t ns_elapsed;
|
||||||
int frames;
|
int frames;
|
||||||
int usec_now;
|
|
||||||
int i;
|
int i;
|
||||||
int skipped_frames = 0;
|
int skipped_frames = 0;
|
||||||
|
|
||||||
|
|
||||||
t_now = qemu_get_clock_ns(vm_clock);
|
t_now = qemu_get_clock_ns(vm_clock);
|
||||||
expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ);
|
expire_time = t_now + (get_ticks_per_sec() / ehci->freq);
|
||||||
if (expire_time == t_now) {
|
|
||||||
expire_time++;
|
|
||||||
}
|
|
||||||
|
|
||||||
usec_now = t_now / 1000;
|
ns_elapsed = t_now - ehci->last_run_ns;
|
||||||
usec_elapsed = usec_now - ehci->last_run_usec;
|
frames = ns_elapsed / FRAME_TIMER_NS;
|
||||||
frames = usec_elapsed / FRAME_TIMER_USEC;
|
|
||||||
ehci->frame_end_usec = usec_now + FRAME_TIMER_USEC - 10;
|
|
||||||
|
|
||||||
for (i = 0; i < frames; i++) {
|
for (i = 0; i < frames; i++) {
|
||||||
if ( !(ehci->usbsts & USBSTS_HALT)) {
|
if ( !(ehci->usbsts & USBSTS_HALT)) {
|
||||||
|
@ -2073,13 +2071,13 @@ static void ehci_frame_timer(void *opaque)
|
||||||
ehci->sofv &= 0x000003ff;
|
ehci->sofv &= 0x000003ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frames - i > 10) {
|
if (frames - i > ehci->maxframes) {
|
||||||
skipped_frames++;
|
skipped_frames++;
|
||||||
} else {
|
} else {
|
||||||
ehci_advance_periodic_state(ehci);
|
ehci_advance_periodic_state(ehci);
|
||||||
}
|
}
|
||||||
|
|
||||||
ehci->last_run_usec += FRAME_TIMER_USEC;
|
ehci->last_run_ns += FRAME_TIMER_NS;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -2146,6 +2144,11 @@ static PCIDeviceInfo ehci_info = {
|
||||||
.device_id = PCI_DEVICE_ID_INTEL_82801D,
|
.device_id = PCI_DEVICE_ID_INTEL_82801D,
|
||||||
.revision = 0x10,
|
.revision = 0x10,
|
||||||
.class_id = PCI_CLASS_SERIAL_USB,
|
.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)
|
static int usb_ehci_initfn(PCIDevice *dev)
|
||||||
|
|
19
hw/usb-msd.c
19
hw/usb-msd.c
|
@ -51,6 +51,7 @@ typedef struct {
|
||||||
SCSIRequest *req;
|
SCSIRequest *req;
|
||||||
SCSIBus bus;
|
SCSIBus bus;
|
||||||
BlockConf conf;
|
BlockConf conf;
|
||||||
|
char *serial;
|
||||||
SCSIDevice *scsi_dev;
|
SCSIDevice *scsi_dev;
|
||||||
uint32_t removable;
|
uint32_t removable;
|
||||||
int result;
|
int result;
|
||||||
|
@ -497,8 +498,9 @@ static void usb_msd_password_cb(void *opaque, int err)
|
||||||
MSDState *s = opaque;
|
MSDState *s = opaque;
|
||||||
|
|
||||||
if (!err)
|
if (!err)
|
||||||
usb_device_attach(&s->dev);
|
err = usb_device_attach(&s->dev);
|
||||||
else
|
|
||||||
|
if (err)
|
||||||
qdev_unplug(&s->dev.qdev);
|
qdev_unplug(&s->dev.qdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,9 +533,15 @@ static int usb_msd_initfn(USBDevice *dev)
|
||||||
bdrv_detach(bs, &s->dev.qdev);
|
bdrv_detach(bs, &s->dev.qdev);
|
||||||
s->conf.bs = NULL;
|
s->conf.bs = NULL;
|
||||||
|
|
||||||
dinfo = drive_get_by_blockdev(bs);
|
if (!s->serial) {
|
||||||
if (dinfo && dinfo->serial) {
|
/* try to fall back to value set with legacy -drive serial=... */
|
||||||
usb_desc_set_string(dev, STR_SERIALNUMBER, dinfo->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);
|
usb_desc_init(dev);
|
||||||
|
@ -632,6 +640,7 @@ static struct USBDeviceInfo msd_info = {
|
||||||
.usbdevice_init = usb_msd_init,
|
.usbdevice_init = usb_msd_init,
|
||||||
.qdev.props = (Property[]) {
|
.qdev.props = (Property[]) {
|
||||||
DEFINE_BLOCK_PROPERTIES(MSDState, conf),
|
DEFINE_BLOCK_PROPERTIES(MSDState, conf),
|
||||||
|
DEFINE_PROP_STRING("serial", MSDState, serial),
|
||||||
DEFINE_PROP_BIT("removable", MSDState, removable, 0, false),
|
DEFINE_PROP_BIT("removable", MSDState, removable, 0, false),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -373,14 +373,25 @@ static void ohci_wakeup(USBDevice *dev)
|
||||||
OHCIState *s = container_of(bus, OHCIState, bus);
|
OHCIState *s = container_of(bus, OHCIState, bus);
|
||||||
int portnum = dev->port->index;
|
int portnum = dev->port->index;
|
||||||
OHCIPort *port = &s->rhport[portnum];
|
OHCIPort *port = &s->rhport[portnum];
|
||||||
|
uint32_t intr = 0;
|
||||||
if (port->ctrl & OHCI_PORT_PSS) {
|
if (port->ctrl & OHCI_PORT_PSS) {
|
||||||
DPRINTF("usb-ohci: port %d: wakeup\n", portnum);
|
DPRINTF("usb-ohci: port %d: wakeup\n", portnum);
|
||||||
port->ctrl |= OHCI_PORT_PSSC;
|
port->ctrl |= OHCI_PORT_PSSC;
|
||||||
port->ctrl &= ~OHCI_PORT_PSS;
|
port->ctrl &= ~OHCI_PORT_PSS;
|
||||||
if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
|
intr = OHCI_INTR_RHSC;
|
||||||
ohci_set_interrupt(s, OHCI_INTR_RD);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
/* 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 */
|
/* Reset the controller */
|
||||||
|
|
4
hw/usb.h
4
hw/usb.h
|
@ -130,6 +130,7 @@
|
||||||
#define USB_DT_ENDPOINT 0x05
|
#define USB_DT_ENDPOINT 0x05
|
||||||
#define USB_DT_DEVICE_QUALIFIER 0x06
|
#define USB_DT_DEVICE_QUALIFIER 0x06
|
||||||
#define USB_DT_OTHER_SPEED_CONFIG 0x07
|
#define USB_DT_OTHER_SPEED_CONFIG 0x07
|
||||||
|
#define USB_DT_DEBUG 0x0A
|
||||||
#define USB_DT_INTERFACE_ASSOC 0x0B
|
#define USB_DT_INTERFACE_ASSOC 0x0B
|
||||||
|
|
||||||
#define USB_ENDPOINT_XFER_CONTROL 0
|
#define USB_ENDPOINT_XFER_CONTROL 0
|
||||||
|
@ -168,7 +169,10 @@ struct USBDevice {
|
||||||
char *port_path;
|
char *port_path;
|
||||||
void *opaque;
|
void *opaque;
|
||||||
|
|
||||||
|
/* Actual connected speed */
|
||||||
int speed;
|
int speed;
|
||||||
|
/* Supported speeds, not in info because it may be variable (hostdevs) */
|
||||||
|
int speedmask;
|
||||||
uint8_t addr;
|
uint8_t addr;
|
||||||
char product_desc[32];
|
char product_desc[32];
|
||||||
int auto_attach;
|
int auto_attach;
|
||||||
|
|
|
@ -367,8 +367,10 @@ USBDevice *usb_host_device_open(const char *devname)
|
||||||
|
|
||||||
if (dev_info.udi_speed == 1) {
|
if (dev_info.udi_speed == 1) {
|
||||||
dev->dev.speed = USB_SPEED_LOW - 1;
|
dev->dev.speed = USB_SPEED_LOW - 1;
|
||||||
|
dev->dev.speedmask = USB_SPEED_MASK_LOW;
|
||||||
} else {
|
} else {
|
||||||
dev->dev.speed = USB_SPEED_FULL - 1;
|
dev->dev.speed = USB_SPEED_FULL - 1;
|
||||||
|
dev->dev.speedmask = USB_SPEED_MASK_FULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strncmp(dev_info.udi_product, "product", 7) != 0) {
|
if (strncmp(dev_info.udi_product, "product", 7) != 0) {
|
||||||
|
|
124
usb-linux.c
124
usb-linux.c
|
@ -85,7 +85,6 @@ static int usb_fs_type;
|
||||||
|
|
||||||
/* endpoint association data */
|
/* endpoint association data */
|
||||||
#define ISO_FRAME_DESC_PER_URB 32
|
#define ISO_FRAME_DESC_PER_URB 32
|
||||||
#define ISO_URB_COUNT 3
|
|
||||||
#define INVALID_EP_TYPE 255
|
#define INVALID_EP_TYPE 255
|
||||||
|
|
||||||
/* devio.c limits single requests to 16k */
|
/* devio.c limits single requests to 16k */
|
||||||
|
@ -101,6 +100,7 @@ struct endp_data {
|
||||||
int iso_urb_idx;
|
int iso_urb_idx;
|
||||||
int iso_buffer_used;
|
int iso_buffer_used;
|
||||||
int max_packet_size;
|
int max_packet_size;
|
||||||
|
int inflight;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct USBAutoFilter {
|
struct USBAutoFilter {
|
||||||
|
@ -120,6 +120,7 @@ typedef struct USBHostDevice {
|
||||||
int configuration;
|
int configuration;
|
||||||
int ninterfaces;
|
int ninterfaces;
|
||||||
int closing;
|
int closing;
|
||||||
|
uint32_t iso_urb_count;
|
||||||
Notifier exit;
|
Notifier exit;
|
||||||
|
|
||||||
struct endp_data endp_table[MAX_ENDPOINTS];
|
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,
|
static int usb_host_read_file(char *line, size_t line_size,
|
||||||
const char *device_file, const char *device_name);
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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;
|
case 2: microframes = 3; break;
|
||||||
default: microframes = 1; break;
|
default: microframes = 1; break;
|
||||||
}
|
}
|
||||||
DPRINTF("husb: max packet size: 0x%x -> %d x %d\n",
|
get_endp(s, ep)->max_packet_size = size * microframes;
|
||||||
raw, microframes, size);
|
|
||||||
s->endp_table[ep - 1].max_packet_size = size * microframes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_max_packet_size(USBHostDevice *s, int ep)
|
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;
|
USBHostDevice *s = opaque;
|
||||||
AsyncURB *aurb;
|
AsyncURB *aurb;
|
||||||
|
int urbs = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
USBPacket *p;
|
USBPacket *p;
|
||||||
|
@ -286,6 +303,9 @@ static void async_complete(void *opaque)
|
||||||
int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
|
int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (errno == EAGAIN) {
|
if (errno == EAGAIN) {
|
||||||
|
if (urbs > 2) {
|
||||||
|
fprintf(stderr, "husb: %d iso urbs finished at once\n", urbs);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (errno == ENODEV && !s->closing) {
|
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
|
/* 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) */
|
anything else (it is handled further in usb_host_handle_iso_data) */
|
||||||
if (aurb->iso_frame_idx == -1) {
|
if (aurb->iso_frame_idx == -1) {
|
||||||
|
int inflight;
|
||||||
if (aurb->urb.status == -EPIPE) {
|
if (aurb->urb.status == -EPIPE) {
|
||||||
set_halt(s, aurb->urb.endpoint & 0xf);
|
set_halt(s, aurb->urb.endpoint & 0xf);
|
||||||
}
|
}
|
||||||
aurb->iso_frame_idx = 0;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,8 +528,8 @@ static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, uint8_t ep, int in)
|
||||||
AsyncURB *aurb;
|
AsyncURB *aurb;
|
||||||
int i, j, len = get_max_packet_size(s, ep);
|
int i, j, len = get_max_packet_size(s, ep);
|
||||||
|
|
||||||
aurb = qemu_mallocz(ISO_URB_COUNT * sizeof(*aurb));
|
aurb = qemu_mallocz(s->iso_urb_count * sizeof(*aurb));
|
||||||
for (i = 0; i < ISO_URB_COUNT; i++) {
|
for (i = 0; i < s->iso_urb_count; i++) {
|
||||||
aurb[i].urb.endpoint = ep;
|
aurb[i].urb.endpoint = ep;
|
||||||
aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len;
|
aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len;
|
||||||
aurb[i].urb.buffer = qemu_malloc(aurb[i].urb.buffer_length);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ISO_URB_COUNT; i++) {
|
for (i = 0; i < s->iso_urb_count; i++) {
|
||||||
/* in flight? */
|
/* in flight? */
|
||||||
if (aurb[i].iso_frame_idx == -1) {
|
if (aurb[i].iso_frame_idx == -1) {
|
||||||
ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]);
|
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);
|
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);
|
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++;
|
aurb[i].iso_frame_idx++;
|
||||||
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
|
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);
|
set_iso_urb_idx(s, p->devep, i);
|
||||||
}
|
}
|
||||||
} else {
|
} 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)) {
|
if (is_iso_started(s, p->devep)) {
|
||||||
/* (Re)-submit all fully consumed / filled urbs */
|
/* (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) {
|
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
|
||||||
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
|
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -667,6 +693,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
aurb[i].iso_frame_idx = -1;
|
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;
|
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,
|
static int usb_host_open(USBHostDevice *dev, int bus_num,
|
||||||
int addr, char *port, const char *prod_name, int speed)
|
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.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);
|
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);
|
prod_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = usb_device_attach(&dev->dev);
|
||||||
|
if (ret) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
/* USB devio uses 'write' flag to check for async completions */
|
/* USB devio uses 'write' flag to check for async completions */
|
||||||
qemu_set_fd_handler(dev->fd, NULL, async_complete, dev);
|
qemu_set_fd_handler(dev->fd, NULL, async_complete, dev);
|
||||||
|
|
||||||
usb_device_attach(&dev->dev);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -1230,6 +1301,7 @@ static struct USBDeviceInfo usb_host_dev_info = {
|
||||||
DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
|
DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
|
||||||
DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0),
|
DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0),
|
||||||
DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_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(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue