mirror of https://github.com/xqemu/xqemu.git
Merge remote-tracking branch 'kraxel/usb.61' into staging
* kraxel/usb.61: uas: move transfer kickoff ehci: Fix interrupt endpoints no longer working ehci: handle TD deactivation of inflight packets ehci: add ehci_cancel_queue() ehci: simplify ehci_state_executing ehci: Remove unnecessary ehci_flush_qh call ehci: Schedule async-bh when IAAD bit gets set ehci: Fix NULL ptr deref when unplugging an USB dev with an iso stream active usb: unique packet ids usb: Halt ep queue en cancel pending packets on a packet error fix info qtree indention
This commit is contained in:
commit
23aec6005a
|
@ -543,7 +543,7 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
|
||||||
qdev_print_props(mon, dev, DEVICE_CLASS(class)->props, indent);
|
qdev_print_props(mon, dev, DEVICE_CLASS(class)->props, indent);
|
||||||
class = object_class_get_parent(class);
|
class = object_class_get_parent(class);
|
||||||
} while (class != object_class_by_name(TYPE_DEVICE));
|
} while (class != object_class_by_name(TYPE_DEVICE));
|
||||||
bus_print_dev(dev->parent_bus, mon, dev, indent + 2);
|
bus_print_dev(dev->parent_bus, mon, dev, indent);
|
||||||
QLIST_FOREACH(child, &dev->child_bus, sibling) {
|
QLIST_FOREACH(child, &dev->child_bus, sibling) {
|
||||||
qbus_print(mon, child, indent);
|
qbus_print(mon, child, indent);
|
||||||
}
|
}
|
||||||
|
|
4
hw/usb.h
4
hw/usb.h
|
@ -179,6 +179,7 @@ struct USBEndpoint {
|
||||||
uint8_t ifnum;
|
uint8_t ifnum;
|
||||||
int max_packet_size;
|
int max_packet_size;
|
||||||
bool pipeline;
|
bool pipeline;
|
||||||
|
bool halted;
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
QTAILQ_HEAD(, USBPacket) queue;
|
QTAILQ_HEAD(, USBPacket) queue;
|
||||||
};
|
};
|
||||||
|
@ -331,6 +332,7 @@ typedef enum USBPacketState {
|
||||||
struct USBPacket {
|
struct USBPacket {
|
||||||
/* Data fields for use by the driver. */
|
/* Data fields for use by the driver. */
|
||||||
int pid;
|
int pid;
|
||||||
|
uint64_t id;
|
||||||
USBEndpoint *ep;
|
USBEndpoint *ep;
|
||||||
QEMUIOVector iov;
|
QEMUIOVector iov;
|
||||||
uint64_t parameter; /* control transfers */
|
uint64_t parameter; /* control transfers */
|
||||||
|
@ -343,7 +345,7 @@ struct USBPacket {
|
||||||
void usb_packet_init(USBPacket *p);
|
void usb_packet_init(USBPacket *p);
|
||||||
void usb_packet_set_state(USBPacket *p, USBPacketState state);
|
void usb_packet_set_state(USBPacket *p, USBPacketState state);
|
||||||
void usb_packet_check_state(USBPacket *p, USBPacketState expected);
|
void usb_packet_check_state(USBPacket *p, USBPacketState expected);
|
||||||
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep);
|
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id);
|
||||||
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
|
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
|
||||||
int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
|
int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
|
||||||
void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl);
|
void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl);
|
||||||
|
|
|
@ -382,12 +382,23 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
|
||||||
usb_packet_check_state(p, USB_PACKET_SETUP);
|
usb_packet_check_state(p, USB_PACKET_SETUP);
|
||||||
assert(p->ep != NULL);
|
assert(p->ep != NULL);
|
||||||
|
|
||||||
|
/* Submitting a new packet clears halt */
|
||||||
|
if (p->ep->halted) {
|
||||||
|
assert(QTAILQ_EMPTY(&p->ep->queue));
|
||||||
|
p->ep->halted = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
|
if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
|
||||||
ret = usb_process_one(p);
|
ret = usb_process_one(p);
|
||||||
if (ret == USB_RET_ASYNC) {
|
if (ret == USB_RET_ASYNC) {
|
||||||
usb_packet_set_state(p, USB_PACKET_ASYNC);
|
usb_packet_set_state(p, USB_PACKET_ASYNC);
|
||||||
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
|
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
|
||||||
} else {
|
} else {
|
||||||
|
/*
|
||||||
|
* When pipelining is enabled usb-devices must always return async,
|
||||||
|
* otherwise packets can complete out of order!
|
||||||
|
*/
|
||||||
|
assert(!p->ep->pipeline);
|
||||||
p->result = ret;
|
p->result = ret;
|
||||||
usb_packet_set_state(p, USB_PACKET_COMPLETE);
|
usb_packet_set_state(p, USB_PACKET_COMPLETE);
|
||||||
}
|
}
|
||||||
|
@ -399,6 +410,20 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __usb_packet_complete(USBDevice *dev, USBPacket *p)
|
||||||
|
{
|
||||||
|
USBEndpoint *ep = p->ep;
|
||||||
|
|
||||||
|
assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK);
|
||||||
|
|
||||||
|
if (p->result < 0) {
|
||||||
|
ep->halted = true;
|
||||||
|
}
|
||||||
|
usb_packet_set_state(p, USB_PACKET_COMPLETE);
|
||||||
|
QTAILQ_REMOVE(&ep->queue, p, queue);
|
||||||
|
dev->port->ops->complete(dev->port, p);
|
||||||
|
}
|
||||||
|
|
||||||
/* Notify the controller that an async packet is complete. This should only
|
/* Notify the controller that an async packet is complete. This should only
|
||||||
be called for packets previously deferred by returning USB_RET_ASYNC from
|
be called for packets previously deferred by returning USB_RET_ASYNC from
|
||||||
handle_packet. */
|
handle_packet. */
|
||||||
|
@ -409,11 +434,9 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
||||||
|
|
||||||
usb_packet_check_state(p, USB_PACKET_ASYNC);
|
usb_packet_check_state(p, USB_PACKET_ASYNC);
|
||||||
assert(QTAILQ_FIRST(&ep->queue) == p);
|
assert(QTAILQ_FIRST(&ep->queue) == p);
|
||||||
usb_packet_set_state(p, USB_PACKET_COMPLETE);
|
__usb_packet_complete(dev, p);
|
||||||
QTAILQ_REMOVE(&ep->queue, p, queue);
|
|
||||||
dev->port->ops->complete(dev->port, p);
|
|
||||||
|
|
||||||
while (!QTAILQ_EMPTY(&ep->queue)) {
|
while (!ep->halted && !QTAILQ_EMPTY(&ep->queue)) {
|
||||||
p = QTAILQ_FIRST(&ep->queue);
|
p = QTAILQ_FIRST(&ep->queue);
|
||||||
if (p->state == USB_PACKET_ASYNC) {
|
if (p->state == USB_PACKET_ASYNC) {
|
||||||
break;
|
break;
|
||||||
|
@ -425,9 +448,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
p->result = ret;
|
p->result = ret;
|
||||||
usb_packet_set_state(p, USB_PACKET_COMPLETE);
|
__usb_packet_complete(ep->dev, p);
|
||||||
QTAILQ_REMOVE(&ep->queue, p, queue);
|
|
||||||
dev->port->ops->complete(dev->port, p);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,10 +520,11 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
|
||||||
p->state = state;
|
p->state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep)
|
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id)
|
||||||
{
|
{
|
||||||
assert(!usb_packet_is_inflight(p));
|
assert(!usb_packet_is_inflight(p));
|
||||||
assert(p->iov.iov != NULL);
|
assert(p->iov.iov != NULL);
|
||||||
|
p->id = id;
|
||||||
p->pid = pid;
|
p->pid = pid;
|
||||||
p->ep = ep;
|
p->ep = ep;
|
||||||
p->result = 0;
|
p->result = 0;
|
||||||
|
|
|
@ -424,6 +424,7 @@ static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
|
||||||
}
|
}
|
||||||
QTAILQ_REMOVE(&uas->requests, req, next);
|
QTAILQ_REMOVE(&uas->requests, req, next);
|
||||||
g_free(req);
|
g_free(req);
|
||||||
|
usb_uas_start_next_transfer(uas);
|
||||||
}
|
}
|
||||||
|
|
||||||
static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag)
|
static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag)
|
||||||
|
@ -456,7 +457,6 @@ static void usb_uas_scsi_command_complete(SCSIRequest *r,
|
||||||
uint32_t status, size_t resid)
|
uint32_t status, size_t resid)
|
||||||
{
|
{
|
||||||
UASRequest *req = r->hba_private;
|
UASRequest *req = r->hba_private;
|
||||||
UASDevice *uas = req->uas;
|
|
||||||
|
|
||||||
trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid);
|
trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid);
|
||||||
req->complete = true;
|
req->complete = true;
|
||||||
|
@ -465,7 +465,6 @@ static void usb_uas_scsi_command_complete(SCSIRequest *r,
|
||||||
}
|
}
|
||||||
usb_uas_queue_sense(req, status);
|
usb_uas_queue_sense(req, status);
|
||||||
scsi_req_unref(req->req);
|
scsi_req_unref(req->req);
|
||||||
usb_uas_start_next_transfer(uas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_uas_scsi_request_cancelled(SCSIRequest *r)
|
static void usb_uas_scsi_request_cancelled(SCSIRequest *r)
|
||||||
|
|
|
@ -766,15 +766,27 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async)
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ehci_cancel_queue(EHCIQueue *q)
|
||||||
|
{
|
||||||
|
EHCIPacket *p;
|
||||||
|
|
||||||
|
p = QTAILQ_FIRST(&q->packets);
|
||||||
|
if (p == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_usb_ehci_queue_action(q, "cancel");
|
||||||
|
do {
|
||||||
|
ehci_free_packet(p);
|
||||||
|
} while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static void ehci_free_queue(EHCIQueue *q)
|
static void ehci_free_queue(EHCIQueue *q)
|
||||||
{
|
{
|
||||||
EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
|
EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
|
||||||
EHCIPacket *p;
|
|
||||||
|
|
||||||
trace_usb_ehci_queue_action(q, "free");
|
trace_usb_ehci_queue_action(q, "free");
|
||||||
while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
|
ehci_cancel_queue(q);
|
||||||
ehci_free_packet(p);
|
|
||||||
}
|
|
||||||
QTAILQ_REMOVE(head, q, next);
|
QTAILQ_REMOVE(head, q, next);
|
||||||
g_free(q);
|
g_free(q);
|
||||||
}
|
}
|
||||||
|
@ -1194,6 +1206,15 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
|
||||||
val &= ~USBCMD_FLS;
|
val &= ~USBCMD_FLS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (val & USBCMD_IAAD) {
|
||||||
|
/*
|
||||||
|
* Process IAAD immediately, otherwise the Linux IAAD watchdog may
|
||||||
|
* trigger and re-use a qh without us seeing the unlink.
|
||||||
|
*/
|
||||||
|
s->async_stepdown = 0;
|
||||||
|
qemu_bh_schedule(s->async_bh);
|
||||||
|
}
|
||||||
|
|
||||||
if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) !=
|
if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) !=
|
||||||
((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) {
|
((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) {
|
||||||
if (s->pstate == EST_INACTIVE) {
|
if (s->pstate == EST_INACTIVE) {
|
||||||
|
@ -1530,7 +1551,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
|
||||||
endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
|
endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
|
||||||
ep = usb_ep_get(p->queue->dev, p->pid, endp);
|
ep = usb_ep_get(p->queue->dev, p->pid, endp);
|
||||||
|
|
||||||
usb_packet_setup(&p->packet, p->pid, ep);
|
usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
|
||||||
usb_packet_map(&p->packet, &p->sgl);
|
usb_packet_map(&p->packet, &p->sgl);
|
||||||
|
|
||||||
trace_usb_ehci_packet_action(p->queue, p, action);
|
trace_usb_ehci_packet_action(p->queue, p, action);
|
||||||
|
@ -1552,7 +1573,8 @@ static int ehci_execute(EHCIPacket *p, const char *action)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int ehci_process_itd(EHCIState *ehci,
|
static int ehci_process_itd(EHCIState *ehci,
|
||||||
EHCIitd *itd)
|
EHCIitd *itd,
|
||||||
|
uint32_t addr)
|
||||||
{
|
{
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
USBEndpoint *ep;
|
USBEndpoint *ep;
|
||||||
|
@ -1597,8 +1619,8 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||||
|
|
||||||
dev = ehci_find_device(ehci, devaddr);
|
dev = ehci_find_device(ehci, devaddr);
|
||||||
ep = usb_ep_get(dev, pid, endp);
|
ep = usb_ep_get(dev, pid, endp);
|
||||||
if (ep->type == USB_ENDPOINT_XFER_ISOC) {
|
if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) {
|
||||||
usb_packet_setup(&ehci->ipacket, pid, ep);
|
usb_packet_setup(&ehci->ipacket, pid, ep, addr);
|
||||||
usb_packet_map(&ehci->ipacket, &ehci->isgl);
|
usb_packet_map(&ehci->ipacket, &ehci->isgl);
|
||||||
ret = usb_handle_packet(dev, &ehci->ipacket);
|
ret = usb_handle_packet(dev, &ehci->ipacket);
|
||||||
assert(ret != USB_RET_ASYNC);
|
assert(ret != USB_RET_ASYNC);
|
||||||
|
@ -1786,9 +1808,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
|
||||||
if (q->dev != NULL && q->dev->addr != devaddr) {
|
if (q->dev != NULL && q->dev->addr != devaddr) {
|
||||||
if (!QTAILQ_EMPTY(&q->packets)) {
|
if (!QTAILQ_EMPTY(&q->packets)) {
|
||||||
/* should not happen (guest bug) */
|
/* should not happen (guest bug) */
|
||||||
while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
|
ehci_cancel_queue(q);
|
||||||
ehci_free_packet(p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
q->dev = NULL;
|
q->dev = NULL;
|
||||||
}
|
}
|
||||||
|
@ -1796,11 +1816,6 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
|
||||||
q->dev = ehci_find_device(q->ehci, devaddr);
|
q->dev = ehci_find_device(q->ehci, devaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p && p->async == EHCI_ASYNC_INFLIGHT) {
|
|
||||||
/* I/O still in progress -- skip queue */
|
|
||||||
ehci_set_state(ehci, async, EST_HORIZONTALQH);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (p && p->async == EHCI_ASYNC_FINISHED) {
|
if (p && p->async == EHCI_ASYNC_FINISHED) {
|
||||||
/* I/O finished -- continue processing queue */
|
/* I/O finished -- continue processing queue */
|
||||||
trace_usb_ehci_packet_action(p->queue, p, "complete");
|
trace_usb_ehci_packet_action(p->queue, p, "complete");
|
||||||
|
@ -1862,7 +1877,7 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async)
|
||||||
sizeof(EHCIitd) >> 2);
|
sizeof(EHCIitd) >> 2);
|
||||||
ehci_trace_itd(ehci, entry, &itd);
|
ehci_trace_itd(ehci, entry, &itd);
|
||||||
|
|
||||||
if (ehci_process_itd(ehci, &itd) != 0) {
|
if (ehci_process_itd(ehci, &itd, entry) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1949,29 +1964,50 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
|
||||||
ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);
|
ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);
|
||||||
|
|
||||||
p = QTAILQ_FIRST(&q->packets);
|
p = QTAILQ_FIRST(&q->packets);
|
||||||
while (p != NULL && p->qtdaddr != q->qtdaddr) {
|
|
||||||
/* should not happen (guest bug) */
|
|
||||||
ehci_free_packet(p);
|
|
||||||
p = QTAILQ_FIRST(&q->packets);
|
|
||||||
}
|
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
ehci_qh_do_overlay(q);
|
if (p->qtdaddr != q->qtdaddr ||
|
||||||
ehci_flush_qh(q);
|
(!NLPTR_TBIT(p->qtd.next) && (p->qtd.next != qtd.next)) ||
|
||||||
if (p->async == EHCI_ASYNC_INFLIGHT) {
|
(!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) ||
|
||||||
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
p->qtd.bufptr[0] != qtd.bufptr[0]) {
|
||||||
|
/* guest bug: guest updated active QH or qTD underneath us */
|
||||||
|
ehci_cancel_queue(q);
|
||||||
|
p = NULL;
|
||||||
} else {
|
} else {
|
||||||
|
p->qtd = qtd;
|
||||||
|
ehci_qh_do_overlay(q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
|
||||||
|
if (p != NULL) {
|
||||||
|
/* transfer canceled by guest (clear active) */
|
||||||
|
ehci_cancel_queue(q);
|
||||||
|
p = NULL;
|
||||||
|
}
|
||||||
|
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
||||||
|
again = 1;
|
||||||
|
} else if (p != NULL) {
|
||||||
|
switch (p->async) {
|
||||||
|
case EHCI_ASYNC_NONE:
|
||||||
|
/* Previously nacked packet (likely interrupt ep) */
|
||||||
|
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
|
||||||
|
break;
|
||||||
|
case EHCI_ASYNC_INFLIGHT:
|
||||||
|
/* Unfinyshed async handled packet, go horizontal */
|
||||||
|
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
||||||
|
break;
|
||||||
|
case EHCI_ASYNC_FINISHED:
|
||||||
|
/* Should never happen, as this case is caught by fetchqh */
|
||||||
ehci_set_state(q->ehci, q->async, EST_EXECUTING);
|
ehci_set_state(q->ehci, q->async, EST_EXECUTING);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
again = 1;
|
again = 1;
|
||||||
} else if (qtd.token & QTD_TOKEN_ACTIVE) {
|
} else {
|
||||||
p = ehci_alloc_packet(q);
|
p = ehci_alloc_packet(q);
|
||||||
p->qtdaddr = q->qtdaddr;
|
p->qtdaddr = q->qtdaddr;
|
||||||
p->qtd = qtd;
|
p->qtd = qtd;
|
||||||
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
|
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
|
||||||
again = 1;
|
again = 1;
|
||||||
} else {
|
|
||||||
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
|
||||||
again = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return again;
|
return again;
|
||||||
|
@ -2075,19 +2111,11 @@ out:
|
||||||
static int ehci_state_executing(EHCIQueue *q)
|
static int ehci_state_executing(EHCIQueue *q)
|
||||||
{
|
{
|
||||||
EHCIPacket *p = QTAILQ_FIRST(&q->packets);
|
EHCIPacket *p = QTAILQ_FIRST(&q->packets);
|
||||||
int again = 0;
|
|
||||||
|
|
||||||
assert(p != NULL);
|
assert(p != NULL);
|
||||||
assert(p->qtdaddr == q->qtdaddr);
|
assert(p->qtdaddr == q->qtdaddr);
|
||||||
|
|
||||||
ehci_execute_complete(q);
|
ehci_execute_complete(q);
|
||||||
if (p->usb_status == USB_RET_ASYNC) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (p->usb_status == USB_RET_PROCERR) {
|
|
||||||
again = -1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4.10.3
|
// 4.10.3
|
||||||
if (!q->async) {
|
if (!q->async) {
|
||||||
|
@ -2105,11 +2133,8 @@ static int ehci_state_executing(EHCIQueue *q)
|
||||||
ehci_set_state(q->ehci, q->async, EST_WRITEBACK);
|
ehci_set_state(q->ehci, q->async, EST_WRITEBACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
again = 1;
|
|
||||||
|
|
||||||
out:
|
|
||||||
ehci_flush_qh(q);
|
ehci_flush_qh(q);
|
||||||
return again;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2138,6 +2163,19 @@ static int ehci_state_writeback(EHCIQueue *q)
|
||||||
* bit is clear.
|
* bit is clear.
|
||||||
*/
|
*/
|
||||||
if (q->qh.token & QTD_TOKEN_HALT) {
|
if (q->qh.token & QTD_TOKEN_HALT) {
|
||||||
|
/*
|
||||||
|
* We should not do any further processing on a halted queue!
|
||||||
|
* This is esp. important for bulk endpoints with pipelining enabled
|
||||||
|
* (redirection to a real USB device), where we must cancel all the
|
||||||
|
* transfers after this one so that:
|
||||||
|
* 1) If they've completed already, they are not processed further
|
||||||
|
* causing more stalls, originating from the same failed transfer
|
||||||
|
* 2) If still in flight, they are cancelled before the guest does
|
||||||
|
* a clear stall, otherwise the guest and device can loose sync!
|
||||||
|
*/
|
||||||
|
while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
|
||||||
|
ehci_free_packet(p);
|
||||||
|
}
|
||||||
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
||||||
again = 1;
|
again = 1;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -626,7 +626,8 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
||||||
/* A wild guess on the FADDR semantics... */
|
/* A wild guess on the FADDR semantics... */
|
||||||
dev = usb_find_device(&s->port, ep->faddr[idx]);
|
dev = usb_find_device(&s->port, ep->faddr[idx]);
|
||||||
uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
|
uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
|
||||||
usb_packet_setup(&ep->packey[dir].p, pid, uep);
|
usb_packet_setup(&ep->packey[dir].p, pid, uep,
|
||||||
|
(dev->addr << 16) | (uep->nr << 8) | pid);
|
||||||
usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
|
usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
|
||||||
ep->packey[dir].ep = ep;
|
ep->packey[dir].ep = ep;
|
||||||
ep->packey[dir].dir = dir;
|
ep->packey[dir].dir = dir;
|
||||||
|
|
|
@ -812,7 +812,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
||||||
} else {
|
} else {
|
||||||
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
||||||
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
||||||
usb_packet_setup(&ohci->usb_packet, pid, ep);
|
usb_packet_setup(&ohci->usb_packet, pid, ep, addr);
|
||||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
|
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
|
||||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||||
if (ret == USB_RET_ASYNC) {
|
if (ret == USB_RET_ASYNC) {
|
||||||
|
@ -1011,7 +1011,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||||
}
|
}
|
||||||
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
||||||
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
||||||
usb_packet_setup(&ohci->usb_packet, pid, ep);
|
usb_packet_setup(&ohci->usb_packet, pid, ep, addr);
|
||||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
|
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
|
||||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||||
#ifdef DEBUG_PACKET
|
#ifdef DEBUG_PACKET
|
||||||
|
|
|
@ -748,6 +748,22 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
|
||||||
return TD_RESULT_COMPLETE;
|
return TD_RESULT_COMPLETE;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
/*
|
||||||
|
* We should not do any further processing on a queue with errors!
|
||||||
|
* This is esp. important for bulk endpoints with pipelining enabled
|
||||||
|
* (redirection to a real USB device), where we must cancel all the
|
||||||
|
* transfers after this one so that:
|
||||||
|
* 1) If they've completed already, they are not processed further
|
||||||
|
* causing more stalls, originating from the same failed transfer
|
||||||
|
* 2) If still in flight, they are cancelled before the guest does
|
||||||
|
* a clear stall, otherwise the guest and device can loose sync!
|
||||||
|
*/
|
||||||
|
while (!QTAILQ_EMPTY(&async->queue->asyncs)) {
|
||||||
|
UHCIAsync *as = QTAILQ_FIRST(&async->queue->asyncs);
|
||||||
|
uhci_async_unlink(as);
|
||||||
|
uhci_async_cancel(as);
|
||||||
|
}
|
||||||
|
|
||||||
switch(ret) {
|
switch(ret) {
|
||||||
case USB_RET_STALL:
|
case USB_RET_STALL:
|
||||||
td->ctrl |= TD_CTRL_STALL;
|
td->ctrl |= TD_CTRL_STALL;
|
||||||
|
@ -850,7 +866,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
|
||||||
|
|
||||||
dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
|
dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
|
||||||
ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
|
ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
|
||||||
usb_packet_setup(&async->packet, pid, ep);
|
usb_packet_setup(&async->packet, pid, ep, addr);
|
||||||
qemu_sglist_add(&async->sgl, td->buffer, max_len);
|
qemu_sglist_add(&async->sgl, td->buffer, max_len);
|
||||||
usb_packet_map(&async->packet, &async->sgl);
|
usb_packet_map(&async->packet, &async->sgl);
|
||||||
|
|
||||||
|
|
|
@ -1392,7 +1392,7 @@ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
|
||||||
|
|
||||||
dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
|
dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
|
||||||
ep = usb_ep_get(dev, dir, xfer->epid >> 1);
|
ep = usb_ep_get(dev, dir, xfer->epid >> 1);
|
||||||
usb_packet_setup(&xfer->packet, dir, ep);
|
usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr);
|
||||||
usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length);
|
usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length);
|
||||||
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
|
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
|
||||||
xfer->packet.pid, dev->addr, ep->nr);
|
xfer->packet.pid, dev->addr, ep->nr);
|
||||||
|
|
Loading…
Reference in New Issue