mirror of https://github.com/xemu-project/xemu.git
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:
commit
b174de51ae
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue