Merge remote-tracking branch 'kraxel/usb.55' into staging
* kraxel/usb.55: usb-host: add trace events for iso xfers usb: fix interface initialization usb: split endpoint init and reset usb-redir: Correctly handle the usb_redir_babble usbredir status ehci: Kick async schedule on wakeup in the non companion case usb-ehci: Fix an assert whenever isoc transfers are used ehci: don't flush cache on doorbell rings. ehci: fix td writeback ehci: fix ehci_qh_do_overlay
This commit is contained in:
		
						commit
						fe0cb8ef84
					
				
							
								
								
									
										3
									
								
								hw/usb.h
								
								
								
								
							
							
						
						
									
										3
									
								
								hw/usb.h
								
								
								
								
							| 
						 | 
				
			
			@ -145,6 +145,8 @@
 | 
			
		|||
#define USB_ENDPOINT_XFER_INT		3
 | 
			
		||||
#define USB_ENDPOINT_XFER_INVALID     255
 | 
			
		||||
 | 
			
		||||
#define USB_INTERFACE_INVALID         255
 | 
			
		||||
 | 
			
		||||
typedef struct USBBus USBBus;
 | 
			
		||||
typedef struct USBBusOps USBBusOps;
 | 
			
		||||
typedef struct USBPort USBPort;
 | 
			
		||||
| 
						 | 
				
			
			@ -363,6 +365,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p);
 | 
			
		|||
void usb_cancel_packet(USBPacket * p);
 | 
			
		||||
 | 
			
		||||
void usb_ep_init(USBDevice *dev);
 | 
			
		||||
void usb_ep_reset(USBDevice *dev);
 | 
			
		||||
void usb_ep_dump(USBDevice *dev);
 | 
			
		||||
struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep);
 | 
			
		||||
uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -550,7 +550,7 @@ void usb_packet_cleanup(USBPacket *p)
 | 
			
		|||
    qemu_iovec_destroy(&p->iov);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usb_ep_init(USBDevice *dev)
 | 
			
		||||
void usb_ep_reset(USBDevice *dev)
 | 
			
		||||
{
 | 
			
		||||
    int ep;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -559,7 +559,6 @@ void usb_ep_init(USBDevice *dev)
 | 
			
		|||
    dev->ep_ctl.ifnum = 0;
 | 
			
		||||
    dev->ep_ctl.dev = dev;
 | 
			
		||||
    dev->ep_ctl.pipeline = false;
 | 
			
		||||
    QTAILQ_INIT(&dev->ep_ctl.queue);
 | 
			
		||||
    for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
 | 
			
		||||
        dev->ep_in[ep].nr = ep + 1;
 | 
			
		||||
        dev->ep_out[ep].nr = ep + 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -567,12 +566,22 @@ void usb_ep_init(USBDevice *dev)
 | 
			
		|||
        dev->ep_out[ep].pid = USB_TOKEN_OUT;
 | 
			
		||||
        dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID;
 | 
			
		||||
        dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID;
 | 
			
		||||
        dev->ep_in[ep].ifnum = 0;
 | 
			
		||||
        dev->ep_out[ep].ifnum = 0;
 | 
			
		||||
        dev->ep_in[ep].ifnum = USB_INTERFACE_INVALID;
 | 
			
		||||
        dev->ep_out[ep].ifnum = USB_INTERFACE_INVALID;
 | 
			
		||||
        dev->ep_in[ep].dev = dev;
 | 
			
		||||
        dev->ep_out[ep].dev = dev;
 | 
			
		||||
        dev->ep_in[ep].pipeline = false;
 | 
			
		||||
        dev->ep_out[ep].pipeline = false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usb_ep_init(USBDevice *dev)
 | 
			
		||||
{
 | 
			
		||||
    int ep;
 | 
			
		||||
 | 
			
		||||
    usb_ep_reset(dev);
 | 
			
		||||
    QTAILQ_INIT(&dev->ep_ctl.queue);
 | 
			
		||||
    for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
 | 
			
		||||
        QTAILQ_INIT(&dev->ep_in[ep].queue);
 | 
			
		||||
        QTAILQ_INIT(&dev->ep_out[ep].queue);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -365,6 +365,7 @@ struct EHCIQueue {
 | 
			
		|||
    uint32_t seen;
 | 
			
		||||
    uint64_t ts;
 | 
			
		||||
    int async;
 | 
			
		||||
    int revalidate;
 | 
			
		||||
 | 
			
		||||
    /* cached data from guest - needs to be flushed
 | 
			
		||||
     * when guest removes an entry (doorbell, handshake sequence)
 | 
			
		||||
| 
						 | 
				
			
			@ -775,7 +776,18 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
 | 
			
		|||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
 | 
			
		||||
static void ehci_queues_tag_unused_async(EHCIState *ehci)
 | 
			
		||||
{
 | 
			
		||||
    EHCIQueue *q;
 | 
			
		||||
 | 
			
		||||
    QTAILQ_FOREACH(q, &ehci->aqueues, next) {
 | 
			
		||||
        if (!q->seen) {
 | 
			
		||||
            q->revalidate = 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ehci_queues_rip_unused(EHCIState *ehci, int async)
 | 
			
		||||
{
 | 
			
		||||
    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
 | 
			
		||||
    uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4;
 | 
			
		||||
| 
						 | 
				
			
			@ -787,7 +799,7 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
 | 
			
		|||
            q->ts = ehci->last_run_ns;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (!flush && ehci->last_run_ns < q->ts + maxage) {
 | 
			
		||||
        if (ehci->last_run_ns < q->ts + maxage) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        ehci_free_queue(q);
 | 
			
		||||
| 
						 | 
				
			
			@ -893,10 +905,11 @@ static void ehci_wakeup(USBPort *port)
 | 
			
		|||
        USBPort *companion = s->companion_ports[port->index];
 | 
			
		||||
        if (companion->ops->wakeup) {
 | 
			
		||||
            companion->ops->wakeup(companion);
 | 
			
		||||
        } else {
 | 
			
		||||
            qemu_bh_schedule(s->async_bh);
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_bh_schedule(s->async_bh);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ehci_register_companion(USBBus *bus, USBPort *ports[],
 | 
			
		||||
| 
						 | 
				
			
			@ -1246,6 +1259,23 @@ static inline int put_dwords(EHCIState *ehci, uint32_t addr,
 | 
			
		|||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  Write the qh back to guest physical memory.  This step isn't
 | 
			
		||||
 *  in the EHCI spec but we need to do it since we don't share
 | 
			
		||||
 *  physical memory with our guest VM.
 | 
			
		||||
 *
 | 
			
		||||
 *  The first three dwords are read-only for the EHCI, so skip them
 | 
			
		||||
 *  when writing back the qh.
 | 
			
		||||
 */
 | 
			
		||||
static void ehci_flush_qh(EHCIQueue *q)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t *qh = (uint32_t *) &q->qh;
 | 
			
		||||
    uint32_t dwords = sizeof(EHCIqh) >> 2;
 | 
			
		||||
    uint32_t addr = NLPTR_GET(q->qhaddr);
 | 
			
		||||
 | 
			
		||||
    put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 4.10.2
 | 
			
		||||
 | 
			
		||||
static int ehci_qh_do_overlay(EHCIQueue *q)
 | 
			
		||||
| 
						 | 
				
			
			@ -1293,8 +1323,7 @@ static int ehci_qh_do_overlay(EHCIQueue *q)
 | 
			
		|||
    q->qh.bufptr[1] &= ~BUFPTR_CPROGMASK_MASK;
 | 
			
		||||
    q->qh.bufptr[2] &= ~BUFPTR_FRAMETAG_MASK;
 | 
			
		||||
 | 
			
		||||
    put_dwords(q->ehci, NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh,
 | 
			
		||||
               sizeof(EHCIqh) >> 2);
 | 
			
		||||
    ehci_flush_qh(q);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1600,23 +1629,6 @@ static int ehci_process_itd(EHCIState *ehci,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  Write the qh back to guest physical memory.  This step isn't
 | 
			
		||||
 *  in the EHCI spec but we need to do it since we don't share
 | 
			
		||||
 *  physical memory with our guest VM.
 | 
			
		||||
 *
 | 
			
		||||
 *  The first three dwords are read-only for the EHCI, so skip them
 | 
			
		||||
 *  when writing back the qh.
 | 
			
		||||
 */
 | 
			
		||||
static void ehci_flush_qh(EHCIQueue *q)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t *qh = (uint32_t *) &q->qh;
 | 
			
		||||
    uint32_t dwords = sizeof(EHCIqh) >> 2;
 | 
			
		||||
    uint32_t addr = NLPTR_GET(q->qhaddr);
 | 
			
		||||
 | 
			
		||||
    put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*  This state is the entry point for asynchronous schedule
 | 
			
		||||
 *  processing.  Entry here consitutes a EHCI start event state (4.8.5)
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -1632,7 +1644,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci,  int async)
 | 
			
		|||
        ehci_set_usbsts(ehci, USBSTS_REC);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ehci_queues_rip_unused(ehci, async, 0);
 | 
			
		||||
    ehci_queues_rip_unused(ehci, async);
 | 
			
		||||
 | 
			
		||||
    /*  Find the head of the list (4.9.1.1) */
 | 
			
		||||
    for(i = 0; i < MAX_QH; i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1717,6 +1729,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
 | 
			
		|||
    EHCIPacket *p;
 | 
			
		||||
    uint32_t entry, devaddr;
 | 
			
		||||
    EHCIQueue *q;
 | 
			
		||||
    EHCIqh qh;
 | 
			
		||||
 | 
			
		||||
    entry = ehci_get_fetch_addr(ehci, async);
 | 
			
		||||
    q = ehci_find_queue_by_qh(ehci, entry, async);
 | 
			
		||||
| 
						 | 
				
			
			@ -1734,7 +1747,17 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    get_dwords(ehci, NLPTR_GET(q->qhaddr),
 | 
			
		||||
               (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2);
 | 
			
		||||
               (uint32_t *) &qh, sizeof(EHCIqh) >> 2);
 | 
			
		||||
    if (q->revalidate && (q->qh.epchar      != qh.epchar ||
 | 
			
		||||
                          q->qh.epcap       != qh.epcap  ||
 | 
			
		||||
                          q->qh.current_qtd != qh.current_qtd)) {
 | 
			
		||||
        ehci_free_queue(q);
 | 
			
		||||
        q = ehci_alloc_queue(ehci, entry, async);
 | 
			
		||||
        q->seen++;
 | 
			
		||||
        p = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    q->qh = qh;
 | 
			
		||||
    q->revalidate = 0;
 | 
			
		||||
    ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh);
 | 
			
		||||
 | 
			
		||||
    devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
 | 
			
		||||
| 
						 | 
				
			
			@ -2071,6 +2094,7 @@ out:
 | 
			
		|||
static int ehci_state_writeback(EHCIQueue *q)
 | 
			
		||||
{
 | 
			
		||||
    EHCIPacket *p = QTAILQ_FIRST(&q->packets);
 | 
			
		||||
    uint32_t *qtd, addr;
 | 
			
		||||
    int again = 0;
 | 
			
		||||
 | 
			
		||||
    /*  Write back the QTD from the QH area */
 | 
			
		||||
| 
						 | 
				
			
			@ -2078,8 +2102,9 @@ static int ehci_state_writeback(EHCIQueue *q)
 | 
			
		|||
    assert(p->qtdaddr == q->qtdaddr);
 | 
			
		||||
 | 
			
		||||
    ehci_trace_qtd(q, NLPTR_GET(p->qtdaddr), (EHCIqtd *) &q->qh.next_qtd);
 | 
			
		||||
    put_dwords(q->ehci, NLPTR_GET(p->qtdaddr), (uint32_t *) &q->qh.next_qtd,
 | 
			
		||||
               sizeof(EHCIqtd) >> 2);
 | 
			
		||||
    qtd = (uint32_t *) &q->qh.next_qtd;
 | 
			
		||||
    addr = NLPTR_GET(p->qtdaddr);
 | 
			
		||||
    put_dwords(q->ehci, addr + 2 * sizeof(uint32_t), qtd + 2, 2);
 | 
			
		||||
    ehci_free_packet(p);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
| 
						 | 
				
			
			@ -2227,7 +2252,7 @@ static void ehci_advance_async_state(EHCIState *ehci)
 | 
			
		|||
         */
 | 
			
		||||
        if (ehci->usbcmd & USBCMD_IAAD) {
 | 
			
		||||
            /* Remove all unseen qhs from the async qhs queue */
 | 
			
		||||
            ehci_queues_rip_unused(ehci, async, 1);
 | 
			
		||||
            ehci_queues_tag_unused_async(ehci);
 | 
			
		||||
            DPRINTF("ASYNC: doorbell request acknowledged\n");
 | 
			
		||||
            ehci->usbcmd &= ~USBCMD_IAAD;
 | 
			
		||||
            ehci_set_interrupt(ehci, USBSTS_IAA);
 | 
			
		||||
| 
						 | 
				
			
			@ -2280,7 +2305,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
 | 
			
		|||
        ehci_set_fetch_addr(ehci, async,entry);
 | 
			
		||||
        ehci_set_state(ehci, async, EST_FETCHENTRY);
 | 
			
		||||
        ehci_advance_state(ehci, async);
 | 
			
		||||
        ehci_queues_rip_unused(ehci, async, 0);
 | 
			
		||||
        ehci_queues_rip_unused(ehci, async);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
| 
						 | 
				
			
			@ -2557,6 +2582,7 @@ static int usb_ehci_initfn(PCIDevice *dev)
 | 
			
		|||
    s->async_bh = qemu_bh_new(ehci_async_bh, s);
 | 
			
		||||
    QTAILQ_INIT(&s->aqueues);
 | 
			
		||||
    QTAILQ_INIT(&s->pqueues);
 | 
			
		||||
    usb_packet_init(&s->ipacket);
 | 
			
		||||
 | 
			
		||||
    qemu_register_reset(ehci_reset, s);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -213,7 +213,7 @@ static int is_iso_started(USBHostDevice *s, int pid, int ep)
 | 
			
		|||
 | 
			
		||||
static void clear_iso_started(USBHostDevice *s, int pid, int ep)
 | 
			
		||||
{
 | 
			
		||||
    trace_usb_host_ep_stop_iso(s->bus_num, s->addr, ep);
 | 
			
		||||
    trace_usb_host_iso_stop(s->bus_num, s->addr, ep);
 | 
			
		||||
    get_endp(s, pid, ep)->iso_started = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -221,7 +221,7 @@ static void set_iso_started(USBHostDevice *s, int pid, int ep)
 | 
			
		|||
{
 | 
			
		||||
    struct endp_data *e = get_endp(s, pid, ep);
 | 
			
		||||
 | 
			
		||||
    trace_usb_host_ep_start_iso(s->bus_num, s->addr, ep);
 | 
			
		||||
    trace_usb_host_iso_start(s->bus_num, s->addr, ep);
 | 
			
		||||
    if (!e->iso_started) {
 | 
			
		||||
        e->iso_started = 1;
 | 
			
		||||
        e->inflight = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -319,7 +319,8 @@ static void async_complete(void *opaque)
 | 
			
		|||
        if (r < 0) {
 | 
			
		||||
            if (errno == EAGAIN) {
 | 
			
		||||
                if (urbs > 2) {
 | 
			
		||||
                    fprintf(stderr, "husb: %d iso urbs finished at once\n", urbs);
 | 
			
		||||
                    /* indicates possible latency issues */
 | 
			
		||||
                    trace_usb_host_iso_many_urbs(s->bus_num, s->addr, urbs);
 | 
			
		||||
                }
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -352,7 +353,8 @@ static void async_complete(void *opaque)
 | 
			
		|||
            urbs++;
 | 
			
		||||
            inflight = change_iso_inflight(s, pid, ep, -1);
 | 
			
		||||
            if (inflight == 0 && is_iso_started(s, pid, ep)) {
 | 
			
		||||
                fprintf(stderr, "husb: out of buffers for iso stream\n");
 | 
			
		||||
                /* can be latency issues, or simply end of stream */
 | 
			
		||||
                trace_usb_host_iso_out_of_bufs(s->bus_num, s->addr, ep);
 | 
			
		||||
            }
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1136,7 +1138,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
 | 
			
		|||
    USBDescriptor *d;
 | 
			
		||||
    bool active = false;
 | 
			
		||||
 | 
			
		||||
    usb_ep_init(&s->dev);
 | 
			
		||||
    usb_ep_reset(&s->dev);
 | 
			
		||||
 | 
			
		||||
    for (i = 0;; i += d->bLength) {
 | 
			
		||||
        if (i+2 >= s->descr_len) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1239,7 +1241,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
 | 
			
		|||
    return 0;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
    usb_ep_init(&s->dev);
 | 
			
		||||
    usb_ep_reset(&s->dev);
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1326,6 +1328,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
 | 
			
		|||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    usb_ep_init(&dev->dev);
 | 
			
		||||
    ret = usb_linux_update_endp_table(dev);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1033,6 +1033,8 @@ static int usbredir_handle_status(USBRedirDevice *dev,
 | 
			
		|||
    case usb_redir_inval:
 | 
			
		||||
        WARNING("got invalid param error from usb-host?\n");
 | 
			
		||||
        return USB_RET_NAK;
 | 
			
		||||
    case usb_redir_babble:
 | 
			
		||||
        return USB_RET_BABBLE;
 | 
			
		||||
    case usb_redir_ioerror:
 | 
			
		||||
    case usb_redir_timeout:
 | 
			
		||||
    default:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -368,8 +368,10 @@ usb_host_urb_complete(int bus, int addr, void *aurb, int status, int length, int
 | 
			
		|||
usb_host_urb_canceled(int bus, int addr, void *aurb) "dev %d:%d, aurb %p"
 | 
			
		||||
usb_host_ep_set_halt(int bus, int addr, int ep) "dev %d:%d, ep %d"
 | 
			
		||||
usb_host_ep_clear_halt(int bus, int addr, int ep) "dev %d:%d, ep %d"
 | 
			
		||||
usb_host_ep_start_iso(int bus, int addr, int ep) "dev %d:%d, ep %d"
 | 
			
		||||
usb_host_ep_stop_iso(int bus, int addr, int ep) "dev %d:%d, ep %d"
 | 
			
		||||
usb_host_iso_start(int bus, int addr, int ep) "dev %d:%d, ep %d"
 | 
			
		||||
usb_host_iso_stop(int bus, int addr, int ep) "dev %d:%d, ep %d"
 | 
			
		||||
usb_host_iso_out_of_bufs(int bus, int addr, int ep) "dev %d:%d, ep %d"
 | 
			
		||||
usb_host_iso_many_urbs(int bus, int addr, int count) "dev %d:%d, count %d"
 | 
			
		||||
usb_host_reset(int bus, int addr) "dev %d:%d"
 | 
			
		||||
usb_host_auto_scan_enabled(void)
 | 
			
		||||
usb_host_auto_scan_disabled(void)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue