Merge remote-tracking branch 'kraxel/usb.68' into staging
* kraxel/usb.68: (36 commits) xhci: fix usb name in caps xhci: make number of interrupters and slots configurable xhci: allow disabling interrupters xhci: flush endpoint context unconditinally xhci: fix function name in error message uhci: Use only one queue for ctrl endpoints uhci: Retry to fill the queue while waiting for td completion uhci: Always mark a queue valid when we encounter it uhci: When the guest marks a pending td non-active, cancel the queue uhci: Detect guest td re-use uhci: Verify queue has not been changed by guest uhci: Immediately free queues on device disconnect uhci: Store ep in UHCIQueue uhci: Make uhci_fill_queue() actually operate on an UHCIQueue uhci: Add uhci_read_td() helper function uhci: Rename UHCIAsync->td to UHCIAsync->td_addr uhci: Move emptying of the queue's asyncs' queue to uhci_queue_free uhci: Drop unnecessary forward declaration of some static functions uhci: Don't retry on error uhci: cleanup: Add an unlink call to uhci_async_cancel() ... Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
commit
6b0e6468e3
28
hw/usb.h
28
hw/usb.h
@ -38,12 +38,14 @@
|
||||
#define USB_TOKEN_IN 0x69 /* device -> host */
|
||||
#define USB_TOKEN_OUT 0xe1 /* host -> device */
|
||||
|
||||
#define USB_RET_NODEV (-1)
|
||||
#define USB_RET_NAK (-2)
|
||||
#define USB_RET_STALL (-3)
|
||||
#define USB_RET_BABBLE (-4)
|
||||
#define USB_RET_IOERROR (-5)
|
||||
#define USB_RET_ASYNC (-6)
|
||||
#define USB_RET_NODEV (-1)
|
||||
#define USB_RET_NAK (-2)
|
||||
#define USB_RET_STALL (-3)
|
||||
#define USB_RET_BABBLE (-4)
|
||||
#define USB_RET_IOERROR (-5)
|
||||
#define USB_RET_ASYNC (-6)
|
||||
#define USB_RET_ADD_TO_QUEUE (-7)
|
||||
#define USB_RET_REMOVE_FROM_QUEUE (-8)
|
||||
|
||||
#define USB_SPEED_LOW 0
|
||||
#define USB_SPEED_FULL 1
|
||||
@ -293,6 +295,12 @@ typedef struct USBDeviceClass {
|
||||
void (*set_interface)(USBDevice *dev, int interface,
|
||||
int alt_old, int alt_new);
|
||||
|
||||
/*
|
||||
* Called when the hcd is done queuing packets for an endpoint, only
|
||||
* necessary for devices which can return USB_RET_ADD_TO_QUEUE.
|
||||
*/
|
||||
void (*flush_ep_queue)(USBDevice *dev, USBEndpoint *ep);
|
||||
|
||||
const char *product_desc;
|
||||
const USBDesc *usb_desc;
|
||||
} USBDeviceClass;
|
||||
@ -343,6 +351,8 @@ struct USBPacket {
|
||||
USBEndpoint *ep;
|
||||
QEMUIOVector iov;
|
||||
uint64_t parameter; /* control transfers */
|
||||
bool short_not_ok;
|
||||
bool int_req;
|
||||
int result; /* transfer length or USB_RET_* status code */
|
||||
/* Internal use by the USB layer. */
|
||||
USBPacketState state;
|
||||
@ -352,7 +362,8 @@ struct USBPacket {
|
||||
void usb_packet_init(USBPacket *p);
|
||||
void usb_packet_set_state(USBPacket *p, USBPacketState state);
|
||||
void usb_packet_check_state(USBPacket *p, USBPacketState expected);
|
||||
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id);
|
||||
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
|
||||
bool short_not_ok, bool int_req);
|
||||
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
|
||||
int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
|
||||
void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl);
|
||||
@ -370,6 +381,7 @@ USBDevice *usb_find_device(USBPort *port, uint8_t addr);
|
||||
|
||||
int usb_handle_packet(USBDevice *dev, USBPacket *p);
|
||||
void usb_packet_complete(USBDevice *dev, USBPacket *p);
|
||||
void usb_packet_complete_one(USBDevice *dev, USBPacket *p);
|
||||
void usb_cancel_packet(USBPacket * p);
|
||||
|
||||
void usb_ep_init(USBDevice *dev);
|
||||
@ -506,6 +518,8 @@ int usb_device_handle_data(USBDevice *dev, USBPacket *p);
|
||||
void usb_device_set_interface(USBDevice *dev, int interface,
|
||||
int alt_old, int alt_new);
|
||||
|
||||
void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep);
|
||||
|
||||
const char *usb_device_get_product_desc(USBDevice *dev);
|
||||
|
||||
const USBDesc *usb_device_get_usb_desc(USBDevice *dev);
|
||||
|
@ -181,6 +181,14 @@ void usb_device_set_interface(USBDevice *dev, int interface,
|
||||
}
|
||||
}
|
||||
|
||||
void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
|
||||
{
|
||||
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
|
||||
if (klass->flush_ep_queue) {
|
||||
klass->flush_ep_queue(dev, ep);
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_qdev_init(DeviceState *qdev)
|
||||
{
|
||||
USBDevice *dev = USB_DEVICE(qdev);
|
||||
|
@ -391,8 +391,13 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
|
||||
if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
|
||||
ret = usb_process_one(p);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
assert(p->ep->type != USB_ENDPOINT_XFER_ISOC);
|
||||
usb_packet_set_state(p, USB_PACKET_ASYNC);
|
||||
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
|
||||
} else if (ret == USB_RET_ADD_TO_QUEUE) {
|
||||
usb_packet_set_state(p, USB_PACKET_QUEUED);
|
||||
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
|
||||
ret = USB_RET_ASYNC;
|
||||
} else {
|
||||
/*
|
||||
* When pipelining is enabled usb-devices must always return async,
|
||||
@ -412,13 +417,14 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __usb_packet_complete(USBDevice *dev, USBPacket *p)
|
||||
void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBEndpoint *ep = p->ep;
|
||||
|
||||
assert(QTAILQ_FIRST(&ep->queue) == p);
|
||||
assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK);
|
||||
|
||||
if (p->result < 0) {
|
||||
if (p->result < 0 || (p->short_not_ok && (p->result < p->iov.size))) {
|
||||
ep->halted = true;
|
||||
}
|
||||
usb_packet_set_state(p, USB_PACKET_COMPLETE);
|
||||
@ -435,11 +441,16 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
||||
int ret;
|
||||
|
||||
usb_packet_check_state(p, USB_PACKET_ASYNC);
|
||||
assert(QTAILQ_FIRST(&ep->queue) == p);
|
||||
__usb_packet_complete(dev, p);
|
||||
usb_packet_complete_one(dev, p);
|
||||
|
||||
while (!ep->halted && !QTAILQ_EMPTY(&ep->queue)) {
|
||||
while (!QTAILQ_EMPTY(&ep->queue)) {
|
||||
p = QTAILQ_FIRST(&ep->queue);
|
||||
if (ep->halted) {
|
||||
/* Empty the queue on a halt */
|
||||
p->result = USB_RET_REMOVE_FROM_QUEUE;
|
||||
dev->port->ops->complete(dev->port, p);
|
||||
continue;
|
||||
}
|
||||
if (p->state == USB_PACKET_ASYNC) {
|
||||
break;
|
||||
}
|
||||
@ -450,7 +461,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
||||
break;
|
||||
}
|
||||
p->result = ret;
|
||||
__usb_packet_complete(ep->dev, p);
|
||||
usb_packet_complete_one(ep->dev, p);
|
||||
}
|
||||
}
|
||||
|
||||
@ -522,7 +533,8 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
|
||||
p->state = state;
|
||||
}
|
||||
|
||||
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id)
|
||||
void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
|
||||
bool short_not_ok, bool int_req)
|
||||
{
|
||||
assert(!usb_packet_is_inflight(p));
|
||||
assert(p->iov.iov != NULL);
|
||||
@ -531,6 +543,8 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id)
|
||||
p->ep = ep;
|
||||
p->result = 0;
|
||||
p->parameter = 0;
|
||||
p->short_not_ok = short_not_ok;
|
||||
p->int_req = int_req;
|
||||
qemu_iovec_reset(&p->iov);
|
||||
usb_packet_set_state(p, USB_PACKET_SETUP);
|
||||
}
|
||||
|
@ -362,7 +362,6 @@ struct EHCIPacket {
|
||||
USBPacket packet;
|
||||
QEMUSGList sgl;
|
||||
int pid;
|
||||
uint32_t tbytes;
|
||||
enum async_state async;
|
||||
int usb_status;
|
||||
};
|
||||
@ -382,7 +381,7 @@ struct EHCIQueue {
|
||||
uint32_t qhaddr; /* address QH read from */
|
||||
uint32_t qtdaddr; /* address QTD read from */
|
||||
USBDevice *dev;
|
||||
QTAILQ_HEAD(, EHCIPacket) packets;
|
||||
QTAILQ_HEAD(pkts_head, EHCIPacket) packets;
|
||||
};
|
||||
|
||||
typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
|
||||
@ -444,6 +443,7 @@ struct EHCIState {
|
||||
|
||||
uint64_t last_run_ns;
|
||||
uint32_t async_stepdown;
|
||||
bool int_req_by_async;
|
||||
};
|
||||
|
||||
#define SET_LAST_RUN_CLOCK(s) \
|
||||
@ -488,6 +488,7 @@ static const char *ehci_mmio_names[] = {
|
||||
|
||||
static int ehci_state_executing(EHCIQueue *q);
|
||||
static int ehci_state_writeback(EHCIQueue *q);
|
||||
static int ehci_fill_queue(EHCIPacket *p);
|
||||
|
||||
static const char *nr2str(const char **n, size_t len, uint32_t nr)
|
||||
{
|
||||
@ -1245,7 +1246,7 @@ static void ehci_opreg_write(void *ptr, hwaddr addr,
|
||||
s->usbcmd = val; /* Set usbcmd for ehci_update_halt() */
|
||||
ehci_update_halt(s);
|
||||
s->async_stepdown = 0;
|
||||
qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
|
||||
qemu_bh_schedule(s->async_bh);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1456,8 +1457,15 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
|
||||
}
|
||||
|
||||
p = container_of(packet, EHCIPacket, packet);
|
||||
trace_usb_ehci_packet_action(p->queue, p, "wakeup");
|
||||
assert(p->async == EHCI_ASYNC_INFLIGHT);
|
||||
|
||||
if (packet->result == USB_RET_REMOVE_FROM_QUEUE) {
|
||||
trace_usb_ehci_packet_action(p->queue, p, "remove");
|
||||
ehci_free_packet(p);
|
||||
return;
|
||||
}
|
||||
|
||||
trace_usb_ehci_packet_action(p->queue, p, "wakeup");
|
||||
p->async = EHCI_ASYNC_FINISHED;
|
||||
p->usb_status = packet->result;
|
||||
|
||||
@ -1505,15 +1513,20 @@ static void ehci_execute_complete(EHCIQueue *q)
|
||||
}
|
||||
} else {
|
||||
// TODO check 4.12 for splits
|
||||
uint32_t tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES);
|
||||
|
||||
if (p->tbytes && p->pid == USB_TOKEN_IN) {
|
||||
p->tbytes -= p->usb_status;
|
||||
if (tbytes && p->pid == USB_TOKEN_IN) {
|
||||
tbytes -= p->usb_status;
|
||||
if (tbytes) {
|
||||
/* 4.15.1.2 must raise int on a short input packet */
|
||||
ehci_raise_irq(q->ehci, USBSTS_INT);
|
||||
}
|
||||
} else {
|
||||
p->tbytes = 0;
|
||||
tbytes = 0;
|
||||
}
|
||||
|
||||
DPRINTF("updating tbytes to %d\n", p->tbytes);
|
||||
set_field(&q->qh.token, p->tbytes, QTD_TOKEN_TBYTES);
|
||||
DPRINTF("updating tbytes to %d\n", tbytes);
|
||||
set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES);
|
||||
}
|
||||
ehci_finish_transfer(q, p->usb_status);
|
||||
usb_packet_unmap(&p->packet, &p->sgl);
|
||||
@ -1525,6 +1538,9 @@ static void ehci_execute_complete(EHCIQueue *q)
|
||||
|
||||
if (q->qh.token & QTD_TOKEN_IOC) {
|
||||
ehci_raise_irq(q->ehci, USBSTS_INT);
|
||||
if (q->async) {
|
||||
q->ehci->int_req_by_async = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1535,6 +1551,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
|
||||
USBEndpoint *ep;
|
||||
int ret;
|
||||
int endp;
|
||||
bool spd;
|
||||
|
||||
assert(p->async == EHCI_ASYNC_NONE ||
|
||||
p->async == EHCI_ASYNC_INITIALIZED);
|
||||
@ -1544,8 +1561,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
|
||||
return USB_RET_PROCERR;
|
||||
}
|
||||
|
||||
p->tbytes = (p->qtd.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
|
||||
if (p->tbytes > BUFF_SIZE) {
|
||||
if (get_field(p->qtd.token, QTD_TOKEN_TBYTES) > BUFF_SIZE) {
|
||||
ehci_trace_guest_bug(p->queue->ehci,
|
||||
"guest requested more bytes than allowed");
|
||||
return USB_RET_PROCERR;
|
||||
@ -1575,17 +1591,18 @@ static int ehci_execute(EHCIPacket *p, const char *action)
|
||||
return USB_RET_PROCERR;
|
||||
}
|
||||
|
||||
usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
|
||||
spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0);
|
||||
usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr, spd,
|
||||
(p->qtd.token & QTD_TOKEN_IOC) != 0);
|
||||
usb_packet_map(&p->packet, &p->sgl);
|
||||
p->async = EHCI_ASYNC_INITIALIZED;
|
||||
}
|
||||
|
||||
trace_usb_ehci_packet_action(p->queue, p, action);
|
||||
ret = usb_handle_packet(p->queue->dev, &p->packet);
|
||||
DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
|
||||
"(total %d) endp %x ret %d\n",
|
||||
DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd endp %x ret %d\n",
|
||||
q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
|
||||
q->packet.iov.size, q->tbytes, endp, ret);
|
||||
q->packet.iov.size, endp, ret);
|
||||
|
||||
if (ret > BUFF_SIZE) {
|
||||
fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
|
||||
@ -1646,10 +1663,10 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||
dev = ehci_find_device(ehci, devaddr);
|
||||
ep = usb_ep_get(dev, pid, endp);
|
||||
if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) {
|
||||
usb_packet_setup(&ehci->ipacket, pid, ep, addr);
|
||||
usb_packet_setup(&ehci->ipacket, pid, ep, addr, false,
|
||||
(itd->transact[i] & ITD_XACT_IOC) != 0);
|
||||
usb_packet_map(&ehci->ipacket, &ehci->isgl);
|
||||
ret = usb_handle_packet(dev, &ehci->ipacket);
|
||||
assert(ret != USB_RET_ASYNC);
|
||||
usb_packet_unmap(&ehci->ipacket, &ehci->isgl);
|
||||
} else {
|
||||
DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
|
||||
@ -1988,7 +2005,7 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
|
||||
{
|
||||
EHCIqtd qtd;
|
||||
EHCIPacket *p;
|
||||
int again = 0;
|
||||
int again = 1;
|
||||
|
||||
get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
|
||||
sizeof(EHCIqtd) >> 2);
|
||||
@ -2016,7 +2033,6 @@ static int ehci_state_fetchqtd(EHCIQueue *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:
|
||||
@ -2025,6 +2041,9 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
|
||||
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
|
||||
break;
|
||||
case EHCI_ASYNC_INFLIGHT:
|
||||
/* Check if the guest has added new tds to the queue */
|
||||
again = (ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head)) ==
|
||||
USB_RET_PROCERR) ? -1 : 1;
|
||||
/* Unfinished async handled packet, go horizontal */
|
||||
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
||||
break;
|
||||
@ -2036,13 +2055,11 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
|
||||
ehci_set_state(q->ehci, q->async, EST_EXECUTING);
|
||||
break;
|
||||
}
|
||||
again = 1;
|
||||
} else {
|
||||
p = ehci_alloc_packet(q);
|
||||
p->qtdaddr = q->qtdaddr;
|
||||
p->qtd = qtd;
|
||||
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
|
||||
again = 1;
|
||||
}
|
||||
|
||||
return again;
|
||||
@ -2065,18 +2082,23 @@ static int ehci_state_horizqh(EHCIQueue *q)
|
||||
|
||||
static int ehci_fill_queue(EHCIPacket *p)
|
||||
{
|
||||
USBEndpoint *ep = p->packet.ep;
|
||||
EHCIQueue *q = p->queue;
|
||||
EHCIqtd qtd = p->qtd;
|
||||
uint32_t qtdaddr;
|
||||
uint32_t qtdaddr, start_addr = p->qtdaddr;
|
||||
|
||||
for (;;) {
|
||||
if (NLPTR_TBIT(qtd.altnext) == 0) {
|
||||
break;
|
||||
}
|
||||
if (NLPTR_TBIT(qtd.next) != 0) {
|
||||
break;
|
||||
}
|
||||
qtdaddr = qtd.next;
|
||||
/*
|
||||
* Detect circular td lists, Windows creates these, counting on the
|
||||
* active bit going low after execution to make the queue stop.
|
||||
*/
|
||||
if (qtdaddr == start_addr) {
|
||||
break;
|
||||
}
|
||||
get_dwords(q->ehci, NLPTR_GET(qtdaddr),
|
||||
(uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
|
||||
ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd);
|
||||
@ -2093,6 +2115,9 @@ static int ehci_fill_queue(EHCIPacket *p)
|
||||
assert(p->usb_status == USB_RET_ASYNC);
|
||||
p->async = EHCI_ASYNC_INFLIGHT;
|
||||
}
|
||||
if (p->usb_status != USB_RET_PROCERR) {
|
||||
usb_device_flush_ep_queue(ep->dev, ep);
|
||||
}
|
||||
return p->usb_status;
|
||||
}
|
||||
|
||||
@ -2198,19 +2223,6 @@ static int ehci_state_writeback(EHCIQueue *q)
|
||||
* bit is clear.
|
||||
*/
|
||||
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);
|
||||
again = 1;
|
||||
} else {
|
||||
@ -2502,18 +2514,19 @@ static void ehci_frame_timer(void *opaque)
|
||||
}
|
||||
|
||||
if (need_timer) {
|
||||
expire_time = t_now + (get_ticks_per_sec()
|
||||
/* If we've raised int, we speed up the timer, so that we quickly
|
||||
* notice any new packets queued up in response */
|
||||
if (ehci->int_req_by_async && (ehci->usbsts & USBSTS_INT)) {
|
||||
expire_time = t_now + get_ticks_per_sec() / (FRAME_TIMER_FREQ * 2);
|
||||
ehci->int_req_by_async = false;
|
||||
} else {
|
||||
expire_time = t_now + (get_ticks_per_sec()
|
||||
* (ehci->async_stepdown+1) / FRAME_TIMER_FREQ);
|
||||
}
|
||||
qemu_mod_timer(ehci->frame_timer, expire_time);
|
||||
}
|
||||
}
|
||||
|
||||
static void ehci_async_bh(void *opaque)
|
||||
{
|
||||
EHCIState *ehci = opaque;
|
||||
ehci_advance_async_state(ehci);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ehci_mmio_caps_ops = {
|
||||
.read = ehci_caps_read,
|
||||
.valid.min_access_size = 1,
|
||||
@ -2742,7 +2755,7 @@ static int usb_ehci_initfn(PCIDevice *dev)
|
||||
}
|
||||
|
||||
s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s);
|
||||
s->async_bh = qemu_bh_new(ehci_async_bh, s);
|
||||
s->async_bh = qemu_bh_new(ehci_frame_timer, s);
|
||||
QTAILQ_INIT(&s->aqueues);
|
||||
QTAILQ_INIT(&s->pqueues);
|
||||
usb_packet_init(&s->ipacket);
|
||||
|
@ -627,7 +627,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
||||
dev = usb_find_device(&s->port, ep->faddr[idx]);
|
||||
uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
|
||||
usb_packet_setup(&ep->packey[dir].p, pid, uep,
|
||||
(dev->addr << 16) | (uep->nr << 8) | pid);
|
||||
(dev->addr << 16) | (uep->nr << 8) | pid, false, true);
|
||||
usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
|
||||
ep->packey[dir].ep = ep;
|
||||
ep->packey[dir].dir = dir;
|
||||
@ -635,6 +635,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
||||
ret = usb_handle_packet(dev, &ep->packey[dir].p);
|
||||
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
usb_device_flush_ep_queue(dev, uep);
|
||||
ep->status[dir] = len;
|
||||
return;
|
||||
}
|
||||
|
@ -810,12 +810,15 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
||||
if (completion) {
|
||||
ret = ohci->usb_packet.result;
|
||||
} else {
|
||||
bool int_req = relative_frame_number == frame_count &&
|
||||
OHCI_BM(iso_td.flags, TD_DI) == 0;
|
||||
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
||||
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
||||
usb_packet_setup(&ohci->usb_packet, pid, ep, addr);
|
||||
usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false, int_req);
|
||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
|
||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
usb_device_flush_ep_queue(dev, ep);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -1011,13 +1014,15 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
}
|
||||
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
||||
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
||||
usb_packet_setup(&ohci->usb_packet, pid, ep, addr);
|
||||
usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r,
|
||||
OHCI_BM(td.flags, TD_DI) == 0);
|
||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
|
||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||
#ifdef DEBUG_PACKET
|
||||
DPRINTF("ret=%d\n", ret);
|
||||
#endif
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
usb_device_flush_ep_queue(dev, ep);
|
||||
ohci->async_td = addr;
|
||||
return 1;
|
||||
}
|
||||
|
@ -100,16 +100,17 @@ struct UHCIAsync {
|
||||
QEMUSGList sgl;
|
||||
UHCIQueue *queue;
|
||||
QTAILQ_ENTRY(UHCIAsync) next;
|
||||
uint32_t td;
|
||||
uint8_t isoc;
|
||||
uint32_t td_addr;
|
||||
uint8_t done;
|
||||
};
|
||||
|
||||
struct UHCIQueue {
|
||||
uint32_t qh_addr;
|
||||
uint32_t token;
|
||||
UHCIState *uhci;
|
||||
USBEndpoint *ep;
|
||||
QTAILQ_ENTRY(UHCIQueue) next;
|
||||
QTAILQ_HEAD(, UHCIAsync) asyncs;
|
||||
QTAILQ_HEAD(asyncs_head, UHCIAsync) asyncs;
|
||||
int8_t valid;
|
||||
};
|
||||
|
||||
@ -161,13 +162,55 @@ typedef struct UHCI_QH {
|
||||
uint32_t el_link;
|
||||
} UHCI_QH;
|
||||
|
||||
static void uhci_async_cancel(UHCIAsync *async);
|
||||
static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td);
|
||||
|
||||
static inline int32_t uhci_queue_token(UHCI_TD *td)
|
||||
{
|
||||
/* covers ep, dev, pid -> identifies the endpoint */
|
||||
return td->token & 0x7ffff;
|
||||
if ((td->token & (0xf << 15)) == 0) {
|
||||
/* ctrl ep, cover ep and dev, not pid! */
|
||||
return td->token & 0x7ff00;
|
||||
} else {
|
||||
/* covers ep, dev, pid -> identifies the endpoint */
|
||||
return td->token & 0x7ffff;
|
||||
}
|
||||
}
|
||||
|
||||
static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td)
|
||||
static UHCIQueue *uhci_queue_new(UHCIState *s, uint32_t qh_addr, UHCI_TD *td,
|
||||
USBEndpoint *ep)
|
||||
{
|
||||
UHCIQueue *queue;
|
||||
|
||||
queue = g_new0(UHCIQueue, 1);
|
||||
queue->uhci = s;
|
||||
queue->qh_addr = qh_addr;
|
||||
queue->token = uhci_queue_token(td);
|
||||
queue->ep = ep;
|
||||
QTAILQ_INIT(&queue->asyncs);
|
||||
QTAILQ_INSERT_HEAD(&s->queues, queue, next);
|
||||
/* valid needs to be large enough to handle 10 frame delay
|
||||
* for initial isochronous requests */
|
||||
queue->valid = 32;
|
||||
trace_usb_uhci_queue_add(queue->token);
|
||||
return queue;
|
||||
}
|
||||
|
||||
static void uhci_queue_free(UHCIQueue *queue, const char *reason)
|
||||
{
|
||||
UHCIState *s = queue->uhci;
|
||||
UHCIAsync *async;
|
||||
|
||||
while (!QTAILQ_EMPTY(&queue->asyncs)) {
|
||||
async = QTAILQ_FIRST(&queue->asyncs);
|
||||
uhci_async_cancel(async);
|
||||
}
|
||||
|
||||
trace_usb_uhci_queue_del(queue->token, reason);
|
||||
QTAILQ_REMOVE(&s->queues, queue, next);
|
||||
g_free(queue);
|
||||
}
|
||||
|
||||
static UHCIQueue *uhci_queue_find(UHCIState *s, UHCI_TD *td)
|
||||
{
|
||||
uint32_t token = uhci_queue_token(td);
|
||||
UHCIQueue *queue;
|
||||
@ -177,41 +220,36 @@ static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td)
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
|
||||
queue = g_new0(UHCIQueue, 1);
|
||||
queue->uhci = s;
|
||||
queue->token = token;
|
||||
QTAILQ_INIT(&queue->asyncs);
|
||||
QTAILQ_INSERT_HEAD(&s->queues, queue, next);
|
||||
trace_usb_uhci_queue_add(queue->token);
|
||||
return queue;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void uhci_queue_free(UHCIQueue *queue)
|
||||
static bool uhci_queue_verify(UHCIQueue *queue, uint32_t qh_addr, UHCI_TD *td,
|
||||
uint32_t td_addr, bool queuing)
|
||||
{
|
||||
UHCIState *s = queue->uhci;
|
||||
UHCIAsync *first = QTAILQ_FIRST(&queue->asyncs);
|
||||
|
||||
trace_usb_uhci_queue_del(queue->token);
|
||||
QTAILQ_REMOVE(&s->queues, queue, next);
|
||||
g_free(queue);
|
||||
return queue->qh_addr == qh_addr &&
|
||||
queue->token == uhci_queue_token(td) &&
|
||||
(queuing || !(td->ctrl & TD_CTRL_ACTIVE) || first == NULL ||
|
||||
first->td_addr == td_addr);
|
||||
}
|
||||
|
||||
static UHCIAsync *uhci_async_alloc(UHCIQueue *queue, uint32_t addr)
|
||||
static UHCIAsync *uhci_async_alloc(UHCIQueue *queue, uint32_t td_addr)
|
||||
{
|
||||
UHCIAsync *async = g_new0(UHCIAsync, 1);
|
||||
|
||||
async->queue = queue;
|
||||
async->td = addr;
|
||||
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);
|
||||
trace_usb_uhci_packet_add(async->queue->token, async->td_addr);
|
||||
|
||||
return async;
|
||||
}
|
||||
|
||||
static void uhci_async_free(UHCIAsync *async)
|
||||
{
|
||||
trace_usb_uhci_packet_del(async->queue->token, async->td);
|
||||
trace_usb_uhci_packet_del(async->queue->token, async->td_addr);
|
||||
usb_packet_cleanup(&async->packet);
|
||||
qemu_sglist_destroy(&async->sgl);
|
||||
g_free(async);
|
||||
@ -221,21 +259,24 @@ static void uhci_async_link(UHCIAsync *async)
|
||||
{
|
||||
UHCIQueue *queue = async->queue;
|
||||
QTAILQ_INSERT_TAIL(&queue->asyncs, async, next);
|
||||
trace_usb_uhci_packet_link_async(async->queue->token, async->td);
|
||||
trace_usb_uhci_packet_link_async(async->queue->token, async->td_addr);
|
||||
}
|
||||
|
||||
static void uhci_async_unlink(UHCIAsync *async)
|
||||
{
|
||||
UHCIQueue *queue = async->queue;
|
||||
QTAILQ_REMOVE(&queue->asyncs, async, next);
|
||||
trace_usb_uhci_packet_unlink_async(async->queue->token, async->td);
|
||||
trace_usb_uhci_packet_unlink_async(async->queue->token, async->td_addr);
|
||||
}
|
||||
|
||||
static void uhci_async_cancel(UHCIAsync *async)
|
||||
{
|
||||
trace_usb_uhci_packet_cancel(async->queue->token, async->td, async->done);
|
||||
uhci_async_unlink(async);
|
||||
trace_usb_uhci_packet_cancel(async->queue->token, async->td_addr,
|
||||
async->done);
|
||||
if (!async->done)
|
||||
usb_cancel_packet(&async->packet);
|
||||
usb_packet_unmap(&async->packet, &async->sgl);
|
||||
uhci_async_free(async);
|
||||
}
|
||||
|
||||
@ -258,34 +299,21 @@ static void uhci_async_validate_begin(UHCIState *s)
|
||||
static void uhci_async_validate_end(UHCIState *s)
|
||||
{
|
||||
UHCIQueue *queue, *n;
|
||||
UHCIAsync *async;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
|
||||
if (queue->valid > 0) {
|
||||
continue;
|
||||
if (!queue->valid) {
|
||||
uhci_queue_free(queue, "validate-end");
|
||||
}
|
||||
while (!QTAILQ_EMPTY(&queue->asyncs)) {
|
||||
async = QTAILQ_FIRST(&queue->asyncs);
|
||||
uhci_async_unlink(async);
|
||||
uhci_async_cancel(async);
|
||||
}
|
||||
uhci_queue_free(queue);
|
||||
}
|
||||
}
|
||||
|
||||
static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
|
||||
{
|
||||
UHCIQueue *queue;
|
||||
UHCIAsync *curr, *n;
|
||||
UHCIQueue *queue, *n;
|
||||
|
||||
QTAILQ_FOREACH(queue, &s->queues, next) {
|
||||
QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
|
||||
if (!usb_packet_is_inflight(&curr->packet) ||
|
||||
curr->packet.ep->dev != dev) {
|
||||
continue;
|
||||
}
|
||||
uhci_async_unlink(curr);
|
||||
uhci_async_cancel(curr);
|
||||
QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
|
||||
if (queue->ep->dev == dev) {
|
||||
uhci_queue_free(queue, "cancel-device");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -293,38 +321,24 @@ static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
|
||||
static void uhci_async_cancel_all(UHCIState *s)
|
||||
{
|
||||
UHCIQueue *queue, *nq;
|
||||
UHCIAsync *curr, *n;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(queue, &s->queues, next, nq) {
|
||||
QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
|
||||
uhci_async_unlink(curr);
|
||||
uhci_async_cancel(curr);
|
||||
}
|
||||
uhci_queue_free(queue);
|
||||
uhci_queue_free(queue, "cancel-all");
|
||||
}
|
||||
}
|
||||
|
||||
static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, UHCI_TD *td)
|
||||
static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t td_addr)
|
||||
{
|
||||
uint32_t token = uhci_queue_token(td);
|
||||
UHCIQueue *queue;
|
||||
UHCIAsync *async;
|
||||
|
||||
QTAILQ_FOREACH(queue, &s->queues, next) {
|
||||
if (queue->token == token) {
|
||||
break;
|
||||
QTAILQ_FOREACH(async, &queue->asyncs, next) {
|
||||
if (async->td_addr == td_addr) {
|
||||
return async;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (queue == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(async, &queue->asyncs, next) {
|
||||
if (async->td == addr) {
|
||||
return async;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -695,13 +709,15 @@ static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void uhci_async_complete(USBPort *port, USBPacket *packet);
|
||||
static void uhci_process_frame(UHCIState *s);
|
||||
static void uhci_read_td(UHCIState *s, UHCI_TD *td, uint32_t link)
|
||||
{
|
||||
pci_dma_read(&s->dev, link & ~0xf, td, sizeof(*td));
|
||||
le32_to_cpus(&td->link);
|
||||
le32_to_cpus(&td->ctrl);
|
||||
le32_to_cpus(&td->token);
|
||||
le32_to_cpus(&td->buffer);
|
||||
}
|
||||
|
||||
/* return -1 if fatal error (frame must be stopped)
|
||||
0 if TD successful
|
||||
1 if TD unsuccessful or inactive
|
||||
*/
|
||||
static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask)
|
||||
{
|
||||
int len = 0, max_len, err, ret;
|
||||
@ -733,100 +749,94 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
|
||||
*int_mask |= 0x02;
|
||||
/* short packet: do not update QH */
|
||||
trace_usb_uhci_packet_complete_shortxfer(async->queue->token,
|
||||
async->td);
|
||||
async->td_addr);
|
||||
return TD_RESULT_NEXT_QH;
|
||||
}
|
||||
}
|
||||
|
||||
/* success */
|
||||
trace_usb_uhci_packet_complete_success(async->queue->token, async->td);
|
||||
trace_usb_uhci_packet_complete_success(async->queue->token,
|
||||
async->td_addr);
|
||||
return TD_RESULT_COMPLETE;
|
||||
|
||||
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) {
|
||||
case USB_RET_NAK:
|
||||
td->ctrl |= TD_CTRL_NAK;
|
||||
return TD_RESULT_NEXT_QH;
|
||||
|
||||
case USB_RET_STALL:
|
||||
td->ctrl |= TD_CTRL_STALL;
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
s->status |= UHCI_STS_USBERR;
|
||||
if (td->ctrl & TD_CTRL_IOC) {
|
||||
*int_mask |= 0x01;
|
||||
}
|
||||
uhci_update_irq(s);
|
||||
trace_usb_uhci_packet_complete_stall(async->queue->token, async->td);
|
||||
return TD_RESULT_NEXT_QH;
|
||||
trace_usb_uhci_packet_complete_stall(async->queue->token,
|
||||
async->td_addr);
|
||||
err = TD_RESULT_NEXT_QH;
|
||||
break;
|
||||
|
||||
case USB_RET_BABBLE:
|
||||
td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
s->status |= UHCI_STS_USBERR;
|
||||
if (td->ctrl & TD_CTRL_IOC) {
|
||||
*int_mask |= 0x01;
|
||||
}
|
||||
uhci_update_irq(s);
|
||||
/* frame interrupted */
|
||||
trace_usb_uhci_packet_complete_babble(async->queue->token, async->td);
|
||||
return TD_RESULT_STOP_FRAME;
|
||||
|
||||
case USB_RET_NAK:
|
||||
td->ctrl |= TD_CTRL_NAK;
|
||||
if (pid == USB_TOKEN_SETUP)
|
||||
break;
|
||||
return TD_RESULT_NEXT_QH;
|
||||
trace_usb_uhci_packet_complete_babble(async->queue->token,
|
||||
async->td_addr);
|
||||
err = TD_RESULT_STOP_FRAME;
|
||||
break;
|
||||
|
||||
case USB_RET_IOERROR:
|
||||
case USB_RET_NODEV:
|
||||
default:
|
||||
break;
|
||||
td->ctrl |= TD_CTRL_TIMEOUT;
|
||||
td->ctrl &= ~(3 << TD_CTRL_ERROR_SHIFT);
|
||||
trace_usb_uhci_packet_complete_error(async->queue->token,
|
||||
async->td_addr);
|
||||
err = TD_RESULT_NEXT_QH;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Retry the TD if error count is not zero */
|
||||
|
||||
td->ctrl |= TD_CTRL_TIMEOUT;
|
||||
err = (td->ctrl >> TD_CTRL_ERROR_SHIFT) & 3;
|
||||
if (err != 0) {
|
||||
err--;
|
||||
if (err == 0) {
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
s->status |= UHCI_STS_USBERR;
|
||||
if (td->ctrl & TD_CTRL_IOC)
|
||||
*int_mask |= 0x01;
|
||||
uhci_update_irq(s);
|
||||
trace_usb_uhci_packet_complete_error(async->queue->token,
|
||||
async->td);
|
||||
}
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
s->status |= UHCI_STS_USBERR;
|
||||
if (td->ctrl & TD_CTRL_IOC) {
|
||||
*int_mask |= 0x01;
|
||||
}
|
||||
td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) |
|
||||
(err << TD_CTRL_ERROR_SHIFT);
|
||||
return TD_RESULT_NEXT_QH;
|
||||
uhci_update_irq(s);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
|
||||
uint32_t *int_mask, bool queuing)
|
||||
static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
|
||||
UHCI_TD *td, uint32_t td_addr, uint32_t *int_mask)
|
||||
{
|
||||
UHCIAsync *async;
|
||||
int len = 0, max_len;
|
||||
uint8_t pid;
|
||||
USBDevice *dev;
|
||||
USBEndpoint *ep;
|
||||
bool spd;
|
||||
bool queuing = (q != NULL);
|
||||
uint8_t pid = td->token & 0xff;
|
||||
UHCIAsync *async = uhci_async_find_td(s, td_addr);
|
||||
|
||||
if (async) {
|
||||
if (uhci_queue_verify(async->queue, qh_addr, td, td_addr, queuing)) {
|
||||
assert(q == NULL || q == async->queue);
|
||||
q = async->queue;
|
||||
} else {
|
||||
uhci_queue_free(async->queue, "guest re-used pending td");
|
||||
async = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (q == NULL) {
|
||||
q = uhci_queue_find(s, td);
|
||||
if (q && !uhci_queue_verify(q, qh_addr, td, td_addr, queuing)) {
|
||||
uhci_queue_free(q, "guest re-used qh");
|
||||
q = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (q) {
|
||||
q->valid = 32;
|
||||
}
|
||||
|
||||
/* Is active ? */
|
||||
if (!(td->ctrl & TD_CTRL_ACTIVE)) {
|
||||
if (async) {
|
||||
/* Guest marked a pending td non-active, cancel the queue */
|
||||
uhci_queue_free(async->queue, "pending td non-active");
|
||||
}
|
||||
/*
|
||||
* ehci11d spec page 22: "Even if the Active bit in the TD is already
|
||||
* cleared when the TD is fetched ... an IOC interrupt is generated"
|
||||
@ -837,56 +847,60 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
|
||||
return TD_RESULT_NEXT_QH;
|
||||
}
|
||||
|
||||
async = uhci_async_find_td(s, addr, td);
|
||||
if (async) {
|
||||
/* Already submitted */
|
||||
async->queue->valid = 32;
|
||||
|
||||
if (!async->done)
|
||||
return TD_RESULT_ASYNC_CONT;
|
||||
if (queuing) {
|
||||
/* we are busy filling the queue, we are not prepared
|
||||
to consume completed packages then, just leave them
|
||||
in async state */
|
||||
return TD_RESULT_ASYNC_CONT;
|
||||
}
|
||||
if (!async->done) {
|
||||
UHCI_TD last_td;
|
||||
UHCIAsync *last = QTAILQ_LAST(&async->queue->asyncs, asyncs_head);
|
||||
/*
|
||||
* While we are waiting for the current td to complete, the guest
|
||||
* may have added more tds to the queue. Note we re-read the td
|
||||
* rather then caching it, as we want to see guest made changes!
|
||||
*/
|
||||
uhci_read_td(s, &last_td, last->td_addr);
|
||||
uhci_queue_fill(async->queue, &last_td);
|
||||
|
||||
return TD_RESULT_ASYNC_CONT;
|
||||
}
|
||||
uhci_async_unlink(async);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Allocate new packet */
|
||||
async = uhci_async_alloc(uhci_queue_get(s, td), addr);
|
||||
|
||||
/* valid needs to be large enough to handle 10 frame delay
|
||||
* for initial isochronous requests
|
||||
*/
|
||||
async->queue->valid = 32;
|
||||
async->isoc = td->ctrl & TD_CTRL_IOS;
|
||||
if (q == NULL) {
|
||||
USBDevice *dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
|
||||
USBEndpoint *ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
|
||||
q = uhci_queue_new(s, qh_addr, td, ep);
|
||||
}
|
||||
async = uhci_async_alloc(q, td_addr);
|
||||
|
||||
max_len = ((td->token >> 21) + 1) & 0x7ff;
|
||||
pid = td->token & 0xff;
|
||||
|
||||
dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
|
||||
ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
|
||||
usb_packet_setup(&async->packet, pid, ep, addr);
|
||||
spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0);
|
||||
usb_packet_setup(&async->packet, pid, q->ep, 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);
|
||||
|
||||
switch(pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
case USB_TOKEN_SETUP:
|
||||
len = usb_handle_packet(dev, &async->packet);
|
||||
len = usb_handle_packet(q->ep->dev, &async->packet);
|
||||
if (len >= 0)
|
||||
len = max_len;
|
||||
break;
|
||||
|
||||
case USB_TOKEN_IN:
|
||||
len = usb_handle_packet(dev, &async->packet);
|
||||
len = usb_handle_packet(q->ep->dev, &async->packet);
|
||||
break;
|
||||
|
||||
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);
|
||||
@ -895,6 +909,9 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
|
||||
|
||||
if (len == USB_RET_ASYNC) {
|
||||
uhci_async_link(async);
|
||||
if (!queuing) {
|
||||
uhci_queue_fill(q, td);
|
||||
}
|
||||
return TD_RESULT_ASYNC_START;
|
||||
}
|
||||
|
||||
@ -912,30 +929,15 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet)
|
||||
UHCIAsync *async = container_of(packet, UHCIAsync, packet);
|
||||
UHCIState *s = async->queue->uhci;
|
||||
|
||||
if (async->isoc) {
|
||||
UHCI_TD td;
|
||||
uint32_t link = async->td;
|
||||
uint32_t int_mask = 0, val;
|
||||
|
||||
pci_dma_read(&s->dev, link & ~0xf, &td, sizeof(td));
|
||||
le32_to_cpus(&td.link);
|
||||
le32_to_cpus(&td.ctrl);
|
||||
le32_to_cpus(&td.token);
|
||||
le32_to_cpus(&td.buffer);
|
||||
|
||||
if (packet->result == USB_RET_REMOVE_FROM_QUEUE) {
|
||||
uhci_async_unlink(async);
|
||||
uhci_complete_td(s, &td, async, &int_mask);
|
||||
s->pending_int_mask |= int_mask;
|
||||
uhci_async_cancel(async);
|
||||
return;
|
||||
}
|
||||
|
||||
/* update the status bits of the TD */
|
||||
val = cpu_to_le32(td.ctrl);
|
||||
pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
|
||||
uhci_async_free(async);
|
||||
} else {
|
||||
async->done = 1;
|
||||
if (s->frame_bytes < s->frame_bandwidth) {
|
||||
qemu_bh_schedule(s->bh);
|
||||
}
|
||||
async->done = 1;
|
||||
if (s->frame_bytes < s->frame_bandwidth) {
|
||||
qemu_bh_schedule(s->bh);
|
||||
}
|
||||
}
|
||||
|
||||
@ -981,38 +983,31 @@ static int qhdb_insert(QhDb *db, uint32_t addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
|
||||
static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td)
|
||||
{
|
||||
uint32_t int_mask = 0;
|
||||
uint32_t plink = td->link;
|
||||
uint32_t token = uhci_queue_token(td);
|
||||
UHCI_TD ptd;
|
||||
int ret;
|
||||
|
||||
while (is_valid(plink)) {
|
||||
pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd));
|
||||
le32_to_cpus(&ptd.link);
|
||||
le32_to_cpus(&ptd.ctrl);
|
||||
le32_to_cpus(&ptd.token);
|
||||
le32_to_cpus(&ptd.buffer);
|
||||
uhci_read_td(q->uhci, &ptd, plink);
|
||||
if (!(ptd.ctrl & TD_CTRL_ACTIVE)) {
|
||||
break;
|
||||
}
|
||||
if (uhci_queue_token(&ptd) != token) {
|
||||
if (uhci_queue_token(&ptd) != q->token) {
|
||||
break;
|
||||
}
|
||||
trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token);
|
||||
ret = uhci_handle_td(s, plink, &ptd, &int_mask, true);
|
||||
ret = uhci_handle_td(q->uhci, q, q->qh_addr, &ptd, plink, &int_mask);
|
||||
if (ret == TD_RESULT_ASYNC_CONT) {
|
||||
break;
|
||||
}
|
||||
assert(ret == TD_RESULT_ASYNC_START);
|
||||
assert(int_mask == 0);
|
||||
if (ptd.ctrl & TD_CTRL_SPD) {
|
||||
break;
|
||||
}
|
||||
plink = ptd.link;
|
||||
}
|
||||
usb_device_flush_ep_queue(q->ep->dev, q->ep);
|
||||
}
|
||||
|
||||
static void uhci_process_frame(UHCIState *s)
|
||||
@ -1081,15 +1076,11 @@ static void uhci_process_frame(UHCIState *s)
|
||||
}
|
||||
|
||||
/* TD */
|
||||
pci_dma_read(&s->dev, link & ~0xf, &td, sizeof(td));
|
||||
le32_to_cpus(&td.link);
|
||||
le32_to_cpus(&td.ctrl);
|
||||
le32_to_cpus(&td.token);
|
||||
le32_to_cpus(&td.buffer);
|
||||
uhci_read_td(s, &td, link);
|
||||
trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token);
|
||||
|
||||
old_td_ctrl = td.ctrl;
|
||||
ret = uhci_handle_td(s, link, &td, &int_mask, false);
|
||||
ret = uhci_handle_td(s, NULL, curr_qh, &td, link, &int_mask);
|
||||
if (old_td_ctrl != td.ctrl) {
|
||||
/* update the status bits of the TD */
|
||||
val = cpu_to_le32(td.ctrl);
|
||||
@ -1108,9 +1099,6 @@ static void uhci_process_frame(UHCIState *s)
|
||||
|
||||
case TD_RESULT_ASYNC_START:
|
||||
trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf);
|
||||
if (is_valid(td.link) && !(td.ctrl & TD_CTRL_SPD)) {
|
||||
uhci_fill_queue(s, &td);
|
||||
}
|
||||
link = curr_qh ? qh.link : td.link;
|
||||
continue;
|
||||
|
||||
|
@ -322,6 +322,7 @@ typedef struct XHCITransfer {
|
||||
bool running_retry;
|
||||
bool cancelled;
|
||||
bool complete;
|
||||
bool int_req;
|
||||
unsigned int iso_pkts;
|
||||
unsigned int slotid;
|
||||
unsigned int epid;
|
||||
@ -416,6 +417,8 @@ struct XHCIState {
|
||||
/* properties */
|
||||
uint32_t numports_2;
|
||||
uint32_t numports_3;
|
||||
uint32_t numintrs;
|
||||
uint32_t numslots;
|
||||
uint32_t flags;
|
||||
|
||||
/* Operational Registers */
|
||||
@ -815,8 +818,8 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
|
||||
dma_addr_t erdp;
|
||||
unsigned int dp_idx;
|
||||
|
||||
if (v >= MAXINTRS) {
|
||||
DPRINTF("intr nr out of range (%d >= %d)\n", v, MAXINTRS);
|
||||
if (v >= xhci->numintrs) {
|
||||
DPRINTF("intr nr out of range (%d >= %d)\n", v, xhci->numintrs);
|
||||
return;
|
||||
}
|
||||
intr = &xhci->intr[v];
|
||||
@ -963,6 +966,12 @@ static void xhci_er_reset(XHCIState *xhci, int v)
|
||||
XHCIInterrupter *intr = &xhci->intr[v];
|
||||
XHCIEvRingSeg seg;
|
||||
|
||||
if (intr->erstsz == 0) {
|
||||
/* disabled */
|
||||
intr->er_start = 0;
|
||||
intr->er_size = 0;
|
||||
return;
|
||||
}
|
||||
/* cache the (sole) event ring segment location */
|
||||
if (intr->erstsz != 1) {
|
||||
fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", intr->erstsz);
|
||||
@ -1008,9 +1017,6 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
|
||||
uint32_t state)
|
||||
{
|
||||
uint32_t ctx[5];
|
||||
if (epctx->state == state) {
|
||||
return;
|
||||
}
|
||||
|
||||
pci_dma_read(&xhci->pci_dev, epctx->pctx, ctx, sizeof(ctx));
|
||||
ctx[0] &= ~EP_STATE_MASK;
|
||||
@ -1039,7 +1045,7 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
||||
int i;
|
||||
|
||||
trace_usb_xhci_ep_enable(slotid, epid);
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
assert(epid >= 1 && epid <= 31);
|
||||
|
||||
slot = &xhci->slots[slotid-1];
|
||||
@ -1082,13 +1088,42 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
||||
return CC_SUCCESS;
|
||||
}
|
||||
|
||||
static int xhci_ep_nuke_one_xfer(XHCITransfer *t)
|
||||
{
|
||||
int killed = 0;
|
||||
|
||||
if (t->running_async) {
|
||||
usb_cancel_packet(&t->packet);
|
||||
t->running_async = 0;
|
||||
t->cancelled = 1;
|
||||
DPRINTF("xhci: cancelling transfer, waiting for it to complete\n");
|
||||
killed = 1;
|
||||
}
|
||||
if (t->running_retry) {
|
||||
XHCIEPContext *epctx = t->xhci->slots[t->slotid-1].eps[t->epid-1];
|
||||
if (epctx) {
|
||||
epctx->retry = NULL;
|
||||
qemu_del_timer(epctx->kick_timer);
|
||||
}
|
||||
t->running_retry = 0;
|
||||
}
|
||||
if (t->trbs) {
|
||||
g_free(t->trbs);
|
||||
}
|
||||
|
||||
t->trbs = NULL;
|
||||
t->trb_count = t->trb_alloced = 0;
|
||||
|
||||
return killed;
|
||||
}
|
||||
|
||||
static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
|
||||
unsigned int epid)
|
||||
{
|
||||
XHCISlot *slot;
|
||||
XHCIEPContext *epctx;
|
||||
int i, xferi, killed = 0;
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
assert(epid >= 1 && epid <= 31);
|
||||
|
||||
DPRINTF("xhci_ep_nuke_xfers(%d, %d)\n", slotid, epid);
|
||||
@ -1103,25 +1138,7 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
|
||||
|
||||
xferi = epctx->next_xfer;
|
||||
for (i = 0; i < TD_QUEUE; i++) {
|
||||
XHCITransfer *t = &epctx->transfers[xferi];
|
||||
if (t->running_async) {
|
||||
usb_cancel_packet(&t->packet);
|
||||
t->running_async = 0;
|
||||
t->cancelled = 1;
|
||||
DPRINTF("xhci: cancelling transfer %d, waiting for it to complete...\n", i);
|
||||
killed++;
|
||||
}
|
||||
if (t->running_retry) {
|
||||
t->running_retry = 0;
|
||||
epctx->retry = NULL;
|
||||
qemu_del_timer(epctx->kick_timer);
|
||||
}
|
||||
if (t->trbs) {
|
||||
g_free(t->trbs);
|
||||
}
|
||||
|
||||
t->trbs = NULL;
|
||||
t->trb_count = t->trb_alloced = 0;
|
||||
killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi]);
|
||||
xferi = (xferi + 1) % TD_QUEUE;
|
||||
}
|
||||
return killed;
|
||||
@ -1134,7 +1151,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
|
||||
XHCIEPContext *epctx;
|
||||
|
||||
trace_usb_xhci_ep_disable(slotid, epid);
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
assert(epid >= 1 && epid <= 31);
|
||||
|
||||
slot = &xhci->slots[slotid-1];
|
||||
@ -1164,7 +1181,7 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid,
|
||||
XHCIEPContext *epctx;
|
||||
|
||||
trace_usb_xhci_ep_stop(slotid, epid);
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
|
||||
if (epid < 1 || epid > 31) {
|
||||
fprintf(stderr, "xhci: bad ep %d\n", epid);
|
||||
@ -1198,7 +1215,7 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
|
||||
USBDevice *dev;
|
||||
|
||||
trace_usb_xhci_ep_reset(slotid, epid);
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
|
||||
if (epid < 1 || epid > 31) {
|
||||
fprintf(stderr, "xhci: bad ep %d\n", epid);
|
||||
@ -1248,7 +1265,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
|
||||
XHCIEPContext *epctx;
|
||||
dma_addr_t dequeue;
|
||||
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
|
||||
if (epid < 1 || epid > 31) {
|
||||
fprintf(stderr, "xhci: bad ep %d\n", epid);
|
||||
@ -1281,18 +1298,22 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
|
||||
return CC_SUCCESS;
|
||||
}
|
||||
|
||||
static int xhci_xfer_map(XHCITransfer *xfer)
|
||||
static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer)
|
||||
{
|
||||
int in_xfer = (xfer->packet.pid == USB_TOKEN_IN);
|
||||
XHCIState *xhci = xfer->xhci;
|
||||
int i;
|
||||
|
||||
xfer->int_req = false;
|
||||
pci_dma_sglist_init(&xfer->sgl, &xhci->pci_dev, xfer->trb_count);
|
||||
for (i = 0; i < xfer->trb_count; i++) {
|
||||
XHCITRB *trb = &xfer->trbs[i];
|
||||
dma_addr_t addr;
|
||||
unsigned int chunk = 0;
|
||||
|
||||
if (trb->control & TRB_TR_IOC) {
|
||||
xfer->int_req = true;
|
||||
}
|
||||
|
||||
switch (TRB_TYPE(*trb)) {
|
||||
case TR_DATA:
|
||||
if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) {
|
||||
@ -1317,7 +1338,6 @@ static int xhci_xfer_map(XHCITransfer *xfer)
|
||||
}
|
||||
}
|
||||
|
||||
usb_packet_map(&xfer->packet, &xfer->sgl);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@ -1435,8 +1455,10 @@ static int xhci_setup_packet(XHCITransfer *xfer)
|
||||
ep = usb_ep_get(dev, dir, xfer->epid >> 1);
|
||||
}
|
||||
|
||||
usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr);
|
||||
xhci_xfer_map(xfer);
|
||||
xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */
|
||||
usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr, false,
|
||||
xfer->int_req);
|
||||
usb_packet_map(&xfer->packet, &xfer->sgl);
|
||||
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
|
||||
xfer->packet.pid, dev->addr, ep->nr);
|
||||
return 0;
|
||||
@ -1641,12 +1663,13 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext
|
||||
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
|
||||
{
|
||||
XHCIEPContext *epctx;
|
||||
USBEndpoint *ep = NULL;
|
||||
uint64_t mfindex;
|
||||
int length;
|
||||
int i;
|
||||
|
||||
trace_usb_xhci_ep_kick(slotid, epid);
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
assert(epid >= 1 && epid <= 31);
|
||||
|
||||
if (!xhci->slots[slotid-1].enabled) {
|
||||
@ -1734,12 +1757,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
|
||||
if (epid == 1) {
|
||||
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
|
||||
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
|
||||
ep = xfer->packet.ep;
|
||||
} else {
|
||||
fprintf(stderr, "xhci: error firing CTL transfer\n");
|
||||
}
|
||||
} else {
|
||||
if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
|
||||
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
|
||||
ep = xfer->packet.ep;
|
||||
} else {
|
||||
if (!xfer->iso_xfer) {
|
||||
fprintf(stderr, "xhci: error firing data transfer\n");
|
||||
@ -1756,12 +1781,15 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ep) {
|
||||
usb_device_flush_ep_queue(ep->dev, ep);
|
||||
}
|
||||
}
|
||||
|
||||
static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid)
|
||||
{
|
||||
trace_usb_xhci_slot_enable(slotid);
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
xhci->slots[slotid-1].enabled = 1;
|
||||
xhci->slots[slotid-1].uport = NULL;
|
||||
memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31);
|
||||
@ -1774,7 +1802,7 @@ static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid)
|
||||
int i;
|
||||
|
||||
trace_usb_xhci_slot_disable(slotid);
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
|
||||
for (i = 1; i <= 31; i++) {
|
||||
if (xhci->slots[slotid-1].eps[i-1]) {
|
||||
@ -1826,7 +1854,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
|
||||
TRBCCode res;
|
||||
|
||||
trace_usb_xhci_slot_address(slotid);
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
|
||||
dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
|
||||
pci_dma_read(&xhci->pci_dev, dcbaap + 8*slotid, &poctx, sizeof(poctx));
|
||||
@ -1865,7 +1893,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
|
||||
return CC_USB_TRANSACTION_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXSLOTS; i++) {
|
||||
for (i = 0; i < xhci->numslots; i++) {
|
||||
if (xhci->slots[i].uport == uport) {
|
||||
fprintf(stderr, "xhci: port %s already assigned to slot %d\n",
|
||||
uport->path, i+1);
|
||||
@ -1914,7 +1942,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
|
||||
TRBCCode res;
|
||||
|
||||
trace_usb_xhci_slot_configure(slotid);
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
|
||||
ictx = xhci_mask64(pictx);
|
||||
octx = xhci->slots[slotid-1].ctx;
|
||||
@ -2002,7 +2030,7 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
|
||||
uint32_t slot_ctx[4];
|
||||
|
||||
trace_usb_xhci_slot_evaluate(slotid);
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
|
||||
ictx = xhci_mask64(pictx);
|
||||
octx = xhci->slots[slotid-1].ctx;
|
||||
@ -2065,7 +2093,7 @@ static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid)
|
||||
int i;
|
||||
|
||||
trace_usb_xhci_slot_reset(slotid);
|
||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
||||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
|
||||
octx = xhci->slots[slotid-1].ctx;
|
||||
|
||||
@ -2091,7 +2119,7 @@ static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *tr
|
||||
{
|
||||
unsigned int slotid;
|
||||
slotid = (trb->control >> TRB_CR_SLOTID_SHIFT) & TRB_CR_SLOTID_MASK;
|
||||
if (slotid < 1 || slotid > MAXSLOTS) {
|
||||
if (slotid < 1 || slotid > xhci->numslots) {
|
||||
fprintf(stderr, "xhci: bad slot id %d\n", slotid);
|
||||
event->ccode = CC_TRB_ERROR;
|
||||
return 0;
|
||||
@ -2183,12 +2211,12 @@ static void xhci_process_commands(XHCIState *xhci)
|
||||
event.ptr = addr;
|
||||
switch (type) {
|
||||
case CR_ENABLE_SLOT:
|
||||
for (i = 0; i < MAXSLOTS; i++) {
|
||||
for (i = 0; i < xhci->numslots; i++) {
|
||||
if (!xhci->slots[i].enabled) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= MAXSLOTS) {
|
||||
if (i >= xhci->numslots) {
|
||||
fprintf(stderr, "xhci: no device slots available\n");
|
||||
event.ccode = CC_NO_SLOTS_ERROR;
|
||||
} else {
|
||||
@ -2335,7 +2363,7 @@ static void xhci_reset(DeviceState *dev)
|
||||
xhci->config = 0;
|
||||
xhci->devaddr = 2;
|
||||
|
||||
for (i = 0; i < MAXSLOTS; i++) {
|
||||
for (i = 0; i < xhci->numslots; i++) {
|
||||
xhci_disable_slot(xhci, i+1);
|
||||
}
|
||||
|
||||
@ -2343,7 +2371,7 @@ static void xhci_reset(DeviceState *dev)
|
||||
xhci_update_port(xhci, xhci->ports + i, 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXINTRS; i++) {
|
||||
for (i = 0; i < xhci->numintrs; i++) {
|
||||
xhci->intr[i].iman = 0;
|
||||
xhci->intr[i].imod = 0;
|
||||
xhci->intr[i].erstsz = 0;
|
||||
@ -2375,7 +2403,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
|
||||
break;
|
||||
case 0x04: /* HCSPARAMS 1 */
|
||||
ret = ((xhci->numports_2+xhci->numports_3)<<24)
|
||||
| (MAXINTRS<<8) | MAXSLOTS;
|
||||
| (xhci->numintrs<<8) | xhci->numslots;
|
||||
break;
|
||||
case 0x08: /* HCSPARAMS 2 */
|
||||
ret = 0x0000000f;
|
||||
@ -2402,7 +2430,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
|
||||
ret = 0x02000402; /* USB 2.0 */
|
||||
break;
|
||||
case 0x24: /* Supported Protocol:04 */
|
||||
ret = 0x20425455; /* "USB " */
|
||||
ret = 0x20425355; /* "USB " */
|
||||
break;
|
||||
case 0x28: /* Supported Protocol:08 */
|
||||
ret = 0x00000001 | (xhci->numports_2<<8);
|
||||
@ -2414,7 +2442,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
|
||||
ret = 0x03000002; /* USB 3.0 */
|
||||
break;
|
||||
case 0x34: /* Supported Protocol:04 */
|
||||
ret = 0x20425455; /* "USB " */
|
||||
ret = 0x20425355; /* "USB " */
|
||||
break;
|
||||
case 0x38: /* Supported Protocol:08 */
|
||||
ret = 0x00000000 | (xhci->numports_2+1) | (xhci->numports_3<<8);
|
||||
@ -2653,7 +2681,7 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
|
||||
trace_usb_xhci_runtime_write(reg, val);
|
||||
|
||||
if (reg < 0x20) {
|
||||
fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg);
|
||||
fprintf(stderr, "%s: reg 0x%x unimplemented\n", __func__, (int)reg);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2730,7 +2758,7 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
|
||||
(uint32_t)val);
|
||||
}
|
||||
} else {
|
||||
if (reg > MAXSLOTS) {
|
||||
if (reg > xhci->numslots) {
|
||||
fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg);
|
||||
} else if (val > 31) {
|
||||
fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n",
|
||||
@ -2822,6 +2850,10 @@ static void xhci_complete(USBPort *port, USBPacket *packet)
|
||||
{
|
||||
XHCITransfer *xfer = container_of(packet, XHCITransfer, packet);
|
||||
|
||||
if (packet->result == USB_RET_REMOVE_FROM_QUEUE) {
|
||||
xhci_ep_nuke_one_xfer(xfer);
|
||||
return;
|
||||
}
|
||||
xhci_complete_packet(xfer, packet->result);
|
||||
xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
|
||||
}
|
||||
@ -2832,7 +2864,7 @@ static void xhci_child_detach(USBPort *uport, USBDevice *child)
|
||||
XHCIState *xhci = container_of(bus, XHCIState, bus);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAXSLOTS; i++) {
|
||||
for (i = 0; i < xhci->numslots; i++) {
|
||||
if (xhci->slots[i].uport == uport) {
|
||||
xhci->slots[i].uport = NULL;
|
||||
}
|
||||
@ -2852,7 +2884,7 @@ static int xhci_find_slotid(XHCIState *xhci, USBDevice *dev)
|
||||
XHCISlot *slot;
|
||||
int slotid;
|
||||
|
||||
for (slotid = 1; slotid <= MAXSLOTS; slotid++) {
|
||||
for (slotid = 1; slotid <= xhci->numslots; slotid++) {
|
||||
slot = &xhci->slots[slotid-1];
|
||||
if (slot->devaddr == dev->addr) {
|
||||
return slotid;
|
||||
@ -2948,6 +2980,19 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
|
||||
|
||||
usb_xhci_init(xhci, &dev->qdev);
|
||||
|
||||
if (xhci->numintrs > MAXINTRS) {
|
||||
xhci->numintrs = MAXINTRS;
|
||||
}
|
||||
if (xhci->numintrs < 1) {
|
||||
xhci->numintrs = 1;
|
||||
}
|
||||
if (xhci->numslots > MAXSLOTS) {
|
||||
xhci->numslots = MAXSLOTS;
|
||||
}
|
||||
if (xhci->numslots < 1) {
|
||||
xhci->numslots = 1;
|
||||
}
|
||||
|
||||
xhci->mfwrap_timer = qemu_new_timer_ns(vm_clock, xhci_mfwrap_timer, xhci);
|
||||
|
||||
xhci->irq = xhci->pci_dev.irq[0];
|
||||
@ -2984,10 +3029,10 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
|
||||
assert(ret >= 0);
|
||||
|
||||
if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) {
|
||||
msi_init(&xhci->pci_dev, 0x70, MAXINTRS, true, false);
|
||||
msi_init(&xhci->pci_dev, 0x70, xhci->numintrs, true, false);
|
||||
}
|
||||
if (xhci->flags & (1 << XHCI_FLAG_USE_MSI_X)) {
|
||||
msix_init(&xhci->pci_dev, MAXINTRS,
|
||||
msix_init(&xhci->pci_dev, xhci->numintrs,
|
||||
&xhci->mem, 0, OFF_MSIX_TABLE,
|
||||
&xhci->mem, 0, OFF_MSIX_PBA,
|
||||
0x90);
|
||||
@ -3002,10 +3047,12 @@ static const VMStateDescription vmstate_xhci = {
|
||||
};
|
||||
|
||||
static Property xhci_properties[] = {
|
||||
DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true),
|
||||
DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true),
|
||||
DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4),
|
||||
DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4),
|
||||
DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true),
|
||||
DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true),
|
||||
DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS),
|
||||
DEFINE_PROP_UINT32("slots", XHCIState, numslots, MAXSLOTS),
|
||||
DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4),
|
||||
DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
@ -1224,7 +1224,8 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
|
||||
usb_ep_set_type(&s->dev, pid, ep, type);
|
||||
usb_ep_set_ifnum(&s->dev, pid, ep, interface);
|
||||
if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) &&
|
||||
(type == USB_ENDPOINT_XFER_BULK)) {
|
||||
(type == USB_ENDPOINT_XFER_BULK) &&
|
||||
(pid == USB_TOKEN_OUT)) {
|
||||
usb_ep_set_pipeline(&s->dev, pid, ep, true);
|
||||
}
|
||||
|
||||
|
@ -1270,6 +1270,16 @@ static void usbredir_interface_info(void *priv,
|
||||
}
|
||||
}
|
||||
|
||||
static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep)
|
||||
{
|
||||
if (uep->type != USB_ENDPOINT_XFER_BULK) {
|
||||
return;
|
||||
}
|
||||
if (uep->pid == USB_TOKEN_OUT) {
|
||||
uep->pipeline = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbredir_ep_info(void *priv,
|
||||
struct usb_redir_ep_info_header *ep_info)
|
||||
{
|
||||
@ -1311,9 +1321,7 @@ static void usbredir_ep_info(void *priv,
|
||||
dev->endpoint[i].max_packet_size =
|
||||
usb_ep->max_packet_size = ep_info->max_packet_size[i];
|
||||
}
|
||||
if (ep_info->type[i] == usb_redir_type_bulk) {
|
||||
usb_ep->pipeline = true;
|
||||
}
|
||||
usbredir_set_pipeline(dev, usb_ep);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1574,9 +1582,7 @@ static int usbredir_post_load(void *priv, int version_id)
|
||||
usb_ep->type = dev->endpoint[i].type;
|
||||
usb_ep->ifnum = dev->endpoint[i].interface;
|
||||
usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
|
||||
if (dev->endpoint[i].type == usb_redir_type_bulk) {
|
||||
usb_ep->pipeline = true;
|
||||
}
|
||||
usbredir_set_pipeline(dev, usb_ep);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ usb_uhci_mmio_writew(uint32_t addr, uint32_t val) "addr 0x%04x, val 0x%04x"
|
||||
usb_uhci_mmio_readl(uint32_t addr, uint32_t val) "addr 0x%04x, ret 0x%08x"
|
||||
usb_uhci_mmio_writel(uint32_t addr, uint32_t val) "addr 0x%04x, val 0x%08x"
|
||||
usb_uhci_queue_add(uint32_t token) "token 0x%x"
|
||||
usb_uhci_queue_del(uint32_t token) "token 0x%x"
|
||||
usb_uhci_queue_del(uint32_t token, const char *reason) "token 0x%x: %s"
|
||||
usb_uhci_packet_add(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
|
||||
usb_uhci_packet_link_async(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
|
||||
usb_uhci_packet_unlink_async(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
|
||||
|
Loading…
Reference in New Issue
Block a user