static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token)
{
UHCIAsync *async = s->async_pending;
+ UHCIAsync *match = NULL;
+ int count = 0;
+
+ /*
+ * We're looking for the best match here. ie both td addr and token.
+ * Otherwise we return last good match. ie just token.
+ * It's ok to match just token because it identifies the transaction
+ * rather well, token includes: device addr, endpoint, size, etc.
+ *
+ * Also since we queue async transactions in reverse order by returning
+ * last good match we restores the order.
+ *
+ * It's expected that we wont have a ton of outstanding transactions.
+ * If we ever do we'd want to optimize this algorithm.
+ */
while (async) {
- if (async->td == addr) {
- if (async->token == token)
- return async;
-
- /*
- * TD was reused for a different transfer.
- * Invalidate the original one asap.
- */
- if (async->valid > 0) {
- async->valid = 0;
- dprintf("husb: bad reuse. td 0x%x\n", async->td);
+ if (async->token == token) {
+ /* Good match */
+ match = async;
+
+ if (async->td == addr) {
+ /* Best match */
+ break;
}
}
async = async->next;
+ count++;
}
- return NULL;
+
+ if (count > 64)
+ fprintf(stderr, "uhci: warning lots of async transactions\n");
+
+ return match;
}
static void uhci_attach(USBPort *port1, USBDevice *dev);
dprintf("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n",
pid2str(p->pid), p->devaddr, p->devep, p->len);
- if (p->pid == USB_TOKEN_OUT)
+ if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP)
dump_data(p->data, p->len);
ret = USB_RET_NODEV;
static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *int_mask)
{
UHCIAsync *async;
- int len = 0, max_len, ret = 0;
+ int len = 0, max_len;
uint8_t pid;
/* Is active ? */
async = uhci_async_find_td(s, addr, td->token);
if (async) {
/* Already submitted */
- async->valid = 10;
+ async->valid = 32;
if (!async->done)
return 1;
case USB_TOKEN_OUT:
case USB_TOKEN_SETUP:
cpu_physical_memory_read(td->buffer, async->buffer, max_len);
- ret = uhci_broadcast_packet(s, &async->packet);
- len = max_len;
+ len = uhci_broadcast_packet(s, &async->packet);
+ if (len >= 0)
+ len = max_len;
break;
case USB_TOKEN_IN:
- ret = uhci_broadcast_packet(s, &async->packet);
+ len = uhci_broadcast_packet(s, &async->packet);
break;
default:
return -1;
}
- if (ret == USB_RET_ASYNC) {
+ if (len == USB_RET_ASYNC) {
uhci_async_link(s, async);
return 2;
}
- async->packet.len = ret;
+ async->packet.len = len;
done:
- ret = uhci_complete_td(s, td, async, int_mask);
+ len = uhci_complete_td(s, td, async, int_mask);
uhci_async_free(s, async);
- return ret;
+ return len;
}
static void uhci_async_complete(USBPacket *packet, void *opaque)