Merge remote-tracking branch 'kraxel/usb.82' into staging

# By Gerd Hoffmann (3) and Hans de Goede (1)
# Via Gerd Hoffmann
* kraxel/usb.82:
  xhci: handle USB_RET_BABBLE
  uhci: Use an intermediate buffer for usb packet data
  usb-host: add usb_host_full_speed_compat
  usb-host: live migration support for the libusb version

Message-id: 1367920207-1404-1-git-send-email-kraxel@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
Anthony Liguori 2013-05-07 06:48:31 -05:00
commit b174de51ae
3 changed files with 104 additions and 16 deletions

View file

@ -119,7 +119,8 @@ struct UHCIPCIDeviceClass {
struct UHCIAsync {
USBPacket packet;
QEMUSGList sgl;
uint8_t static_buf[64]; /* 64 bytes is enough, except for isoc packets */
uint8_t *buf;
UHCIQueue *queue;
QTAILQ_ENTRY(UHCIAsync) next;
uint32_t td_addr;
@ -264,7 +265,6 @@ static UHCIAsync *uhci_async_alloc(UHCIQueue *queue, uint32_t td_addr)
async->queue = queue;
async->td_addr = td_addr;
usb_packet_init(&async->packet);
pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
trace_usb_uhci_packet_add(async->queue->token, async->td_addr);
return async;
@ -274,7 +274,9 @@ static void uhci_async_free(UHCIAsync *async)
{
trace_usb_uhci_packet_del(async->queue->token, async->td_addr);
usb_packet_cleanup(&async->packet);
qemu_sglist_destroy(&async->sgl);
if (async->buf != async->static_buf) {
g_free(async->buf);
}
g_free(async);
}
@ -299,7 +301,6 @@ static void uhci_async_cancel(UHCIAsync *async)
async->done);
if (!async->done)
usb_cancel_packet(&async->packet);
usb_packet_unmap(&async->packet, &async->sgl);
uhci_async_free(async);
}
@ -774,6 +775,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
*int_mask |= 0x01;
if (pid == USB_TOKEN_IN) {
pci_dma_write(&s->dev, td->buffer, async->buf, len);
if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
*int_mask |= 0x02;
/* short packet: do not update QH */
@ -881,12 +883,17 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0);
usb_packet_setup(&async->packet, pid, q->ep, 0, td_addr, spd,
(td->ctrl & TD_CTRL_IOC) != 0);
qemu_sglist_add(&async->sgl, td->buffer, max_len);
usb_packet_map(&async->packet, &async->sgl);
if (max_len <= sizeof(async->static_buf)) {
async->buf = async->static_buf;
} else {
async->buf = g_malloc(max_len);
}
usb_packet_addbuf(&async->packet, async->buf, max_len);
switch(pid) {
case USB_TOKEN_OUT:
case USB_TOKEN_SETUP:
pci_dma_read(&s->dev, td->buffer, async->buf, max_len);
usb_handle_packet(q->ep->dev, &async->packet);
if (async->packet.status == USB_RET_SUCCESS) {
async->packet.actual_length = max_len;
@ -899,7 +906,6 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
default:
/* invalid pid : frame interrupted */
usb_packet_unmap(&async->packet, &async->sgl);
uhci_async_free(async);
s->status |= UHCI_STS_HCPERR;
uhci_update_irq(s);
@ -916,7 +922,6 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
done:
ret = uhci_complete_td(s, td, async, int_mask);
usb_packet_unmap(&async->packet, &async->sgl);
uhci_async_free(async);
return ret;
}

View file

@ -1716,6 +1716,11 @@ static int xhci_complete_packet(XHCITransfer *xfer)
xhci_xfer_report(xfer);
xhci_stall_ep(xfer);
break;
case USB_RET_BABBLE:
xfer->status = CC_BABBLE_DETECTED;
xhci_xfer_report(xfer);
xhci_stall_ep(xfer);
break;
default:
fprintf(stderr, "%s: FIXME: status = %d\n", __func__,
xfer->packet.status);

View file

@ -94,7 +94,8 @@ struct USBHostDevice {
} ifs[USB_MAX_INTERFACES];
/* callbacks & friends */
QEMUBH *bh;
QEMUBH *bh_nodev;
QEMUBH *bh_postld;
Notifier exit;
/* request queues */
@ -666,6 +667,42 @@ static void usb_host_iso_data_out(USBHostDevice *s, USBPacket *p)
/* ------------------------------------------------------------------------ */
static bool usb_host_full_speed_compat(USBHostDevice *s)
{
struct libusb_config_descriptor *conf;
const struct libusb_interface_descriptor *intf;
const struct libusb_endpoint_descriptor *endp;
uint8_t type;
int rc, c, i, a, e;
for (c = 0;; c++) {
rc = libusb_get_config_descriptor(s->dev, c, &conf);
if (rc != 0) {
break;
}
for (i = 0; i < conf->bNumInterfaces; i++) {
for (a = 0; a < conf->interface[i].num_altsetting; a++) {
intf = &conf->interface[i].altsetting[a];
for (e = 0; e < intf->bNumEndpoints; e++) {
endp = &intf->endpoint[e];
type = endp->bmAttributes & 0x3;
switch (type) {
case 0x01: /* ISO */
return false;
case 0x03: /* INTERRUPT */
if (endp->wMaxPacketSize > 64) {
return false;
}
break;
}
}
}
}
libusb_free_config_descriptor(conf);
}
return true;
}
static void usb_host_ep_update(USBHostDevice *s)
{
static const char *tname[] = {
@ -757,11 +794,9 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev)
udev->speed = speed_map[libusb_get_device_speed(dev)];
udev->speedmask = (1 << udev->speed);
#if 0
if (udev->speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) {
if (udev->speed == USB_SPEED_HIGH && usb_host_full_speed_compat(s)) {
udev->speedmask |= USB_SPEED_MASK_FULL;
}
#endif
if (s->ddesc.iProduct) {
libusb_get_string_descriptor_ascii(s->dh, s->ddesc.iProduct,
@ -835,10 +870,10 @@ static void usb_host_nodev_bh(void *opaque)
static void usb_host_nodev(USBHostDevice *s)
{
if (!s->bh) {
s->bh = qemu_bh_new(usb_host_nodev_bh, s);
if (!s->bh_nodev) {
s->bh_nodev = qemu_bh_new(usb_host_nodev_bh, s);
}
qemu_bh_schedule(s->bh);
qemu_bh_schedule(s->bh_nodev);
}
static void usb_host_exit_notifier(struct Notifier *n, void *data)
@ -1228,9 +1263,52 @@ static void usb_host_handle_reset(USBDevice *udev)
usb_host_ep_update(s);
}
/*
* This is *NOT* about restoring state. We have absolutely no idea
* what state the host device is in at the moment and whenever it is
* still present in the first place. Attemping to contine where we
* left off is impossible.
*
* What we are going to to to here is emulate a surprise removal of
* the usb device passed through, then kick host scan so the device
* will get re-attached (and re-initialized by the guest) in case it
* is still present.
*
* As the device removal will change the state of other devices (usb
* host controller, most likely interrupt controller too) we have to
* wait with it until *all* vmstate is loaded. Thus post_load just
* kicks a bottom half which then does the actual work.
*/
static void usb_host_post_load_bh(void *opaque)
{
USBHostDevice *dev = opaque;
USBDevice *udev = USB_DEVICE(dev);
if (dev->dh != NULL) {
usb_host_close(dev);
}
if (udev->attached) {
usb_device_detach(udev);
}
usb_host_auto_check(NULL);
}
static int usb_host_post_load(void *opaque, int version_id)
{
USBHostDevice *dev = opaque;
if (!dev->bh_postld) {
dev->bh_postld = qemu_bh_new(usb_host_post_load_bh, dev);
}
qemu_bh_schedule(dev->bh_postld);
return 0;
}
static const VMStateDescription vmstate_usb_host = {
.name = "usb-host",
.unmigratable = 1,
.version_id = 1,
.minimum_version_id = 1,
.post_load = usb_host_post_load,
.fields = (VMStateField[]) {
VMSTATE_USB_DEVICE(parent_obj, USBHostDevice),
VMSTATE_END_OF_LIST()