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_IN 0x69 /* device -> host */
|
||||||
#define USB_TOKEN_OUT 0xe1 /* host -> device */
|
#define USB_TOKEN_OUT 0xe1 /* host -> device */
|
||||||
|
|
||||||
#define USB_RET_NODEV (-1)
|
#define USB_RET_NODEV (-1)
|
||||||
#define USB_RET_NAK (-2)
|
#define USB_RET_NAK (-2)
|
||||||
#define USB_RET_STALL (-3)
|
#define USB_RET_STALL (-3)
|
||||||
#define USB_RET_BABBLE (-4)
|
#define USB_RET_BABBLE (-4)
|
||||||
#define USB_RET_IOERROR (-5)
|
#define USB_RET_IOERROR (-5)
|
||||||
#define USB_RET_ASYNC (-6)
|
#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_LOW 0
|
||||||
#define USB_SPEED_FULL 1
|
#define USB_SPEED_FULL 1
|
||||||
@ -293,6 +295,12 @@ typedef struct USBDeviceClass {
|
|||||||
void (*set_interface)(USBDevice *dev, int interface,
|
void (*set_interface)(USBDevice *dev, int interface,
|
||||||
int alt_old, int alt_new);
|
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 char *product_desc;
|
||||||
const USBDesc *usb_desc;
|
const USBDesc *usb_desc;
|
||||||
} USBDeviceClass;
|
} USBDeviceClass;
|
||||||
@ -343,6 +351,8 @@ struct USBPacket {
|
|||||||
USBEndpoint *ep;
|
USBEndpoint *ep;
|
||||||
QEMUIOVector iov;
|
QEMUIOVector iov;
|
||||||
uint64_t parameter; /* control transfers */
|
uint64_t parameter; /* control transfers */
|
||||||
|
bool short_not_ok;
|
||||||
|
bool int_req;
|
||||||
int result; /* transfer length or USB_RET_* status code */
|
int result; /* transfer length or USB_RET_* status code */
|
||||||
/* Internal use by the USB layer. */
|
/* Internal use by the USB layer. */
|
||||||
USBPacketState state;
|
USBPacketState state;
|
||||||
@ -352,7 +362,8 @@ 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, 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);
|
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);
|
||||||
@ -370,6 +381,7 @@ USBDevice *usb_find_device(USBPort *port, uint8_t addr);
|
|||||||
|
|
||||||
int usb_handle_packet(USBDevice *dev, USBPacket *p);
|
int usb_handle_packet(USBDevice *dev, USBPacket *p);
|
||||||
void usb_packet_complete(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_cancel_packet(USBPacket * p);
|
||||||
|
|
||||||
void usb_ep_init(USBDevice *dev);
|
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,
|
void usb_device_set_interface(USBDevice *dev, int interface,
|
||||||
int alt_old, int alt_new);
|
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 char *usb_device_get_product_desc(USBDevice *dev);
|
||||||
|
|
||||||
const USBDesc *usb_device_get_usb_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)
|
static int usb_qdev_init(DeviceState *qdev)
|
||||||
{
|
{
|
||||||
USBDevice *dev = USB_DEVICE(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) {
|
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) {
|
||||||
|
assert(p->ep->type != USB_ENDPOINT_XFER_ISOC);
|
||||||
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 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 {
|
} else {
|
||||||
/*
|
/*
|
||||||
* When pipelining is enabled usb-devices must always return async,
|
* When pipelining is enabled usb-devices must always return async,
|
||||||
@ -412,13 +417,14 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __usb_packet_complete(USBDevice *dev, USBPacket *p)
|
void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
|
||||||
{
|
{
|
||||||
USBEndpoint *ep = p->ep;
|
USBEndpoint *ep = p->ep;
|
||||||
|
|
||||||
|
assert(QTAILQ_FIRST(&ep->queue) == p);
|
||||||
assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK);
|
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;
|
ep->halted = true;
|
||||||
}
|
}
|
||||||
usb_packet_set_state(p, USB_PACKET_COMPLETE);
|
usb_packet_set_state(p, USB_PACKET_COMPLETE);
|
||||||
@ -435,11 +441,16 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
usb_packet_check_state(p, USB_PACKET_ASYNC);
|
usb_packet_check_state(p, USB_PACKET_ASYNC);
|
||||||
assert(QTAILQ_FIRST(&ep->queue) == p);
|
usb_packet_complete_one(dev, p);
|
||||||
__usb_packet_complete(dev, p);
|
|
||||||
|
|
||||||
while (!ep->halted && !QTAILQ_EMPTY(&ep->queue)) {
|
while (!QTAILQ_EMPTY(&ep->queue)) {
|
||||||
p = QTAILQ_FIRST(&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) {
|
if (p->state == USB_PACKET_ASYNC) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -450,7 +461,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
p->result = ret;
|
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;
|
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(!usb_packet_is_inflight(p));
|
||||||
assert(p->iov.iov != NULL);
|
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->ep = ep;
|
||||||
p->result = 0;
|
p->result = 0;
|
||||||
p->parameter = 0;
|
p->parameter = 0;
|
||||||
|
p->short_not_ok = short_not_ok;
|
||||||
|
p->int_req = int_req;
|
||||||
qemu_iovec_reset(&p->iov);
|
qemu_iovec_reset(&p->iov);
|
||||||
usb_packet_set_state(p, USB_PACKET_SETUP);
|
usb_packet_set_state(p, USB_PACKET_SETUP);
|
||||||
}
|
}
|
||||||
|
@ -362,7 +362,6 @@ struct EHCIPacket {
|
|||||||
USBPacket packet;
|
USBPacket packet;
|
||||||
QEMUSGList sgl;
|
QEMUSGList sgl;
|
||||||
int pid;
|
int pid;
|
||||||
uint32_t tbytes;
|
|
||||||
enum async_state async;
|
enum async_state async;
|
||||||
int usb_status;
|
int usb_status;
|
||||||
};
|
};
|
||||||
@ -382,7 +381,7 @@ struct EHCIQueue {
|
|||||||
uint32_t qhaddr; /* address QH read from */
|
uint32_t qhaddr; /* address QH read from */
|
||||||
uint32_t qtdaddr; /* address QTD read from */
|
uint32_t qtdaddr; /* address QTD read from */
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
QTAILQ_HEAD(, EHCIPacket) packets;
|
QTAILQ_HEAD(pkts_head, EHCIPacket) packets;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
|
typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
|
||||||
@ -444,6 +443,7 @@ struct EHCIState {
|
|||||||
|
|
||||||
uint64_t last_run_ns;
|
uint64_t last_run_ns;
|
||||||
uint32_t async_stepdown;
|
uint32_t async_stepdown;
|
||||||
|
bool int_req_by_async;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SET_LAST_RUN_CLOCK(s) \
|
#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_executing(EHCIQueue *q);
|
||||||
static int ehci_state_writeback(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)
|
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() */
|
s->usbcmd = val; /* Set usbcmd for ehci_update_halt() */
|
||||||
ehci_update_halt(s);
|
ehci_update_halt(s);
|
||||||
s->async_stepdown = 0;
|
s->async_stepdown = 0;
|
||||||
qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
|
qemu_bh_schedule(s->async_bh);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1456,8 +1457,15 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
|
|||||||
}
|
}
|
||||||
|
|
||||||
p = container_of(packet, EHCIPacket, packet);
|
p = container_of(packet, EHCIPacket, packet);
|
||||||
trace_usb_ehci_packet_action(p->queue, p, "wakeup");
|
|
||||||
assert(p->async == EHCI_ASYNC_INFLIGHT);
|
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->async = EHCI_ASYNC_FINISHED;
|
||||||
p->usb_status = packet->result;
|
p->usb_status = packet->result;
|
||||||
|
|
||||||
@ -1505,15 +1513,20 @@ static void ehci_execute_complete(EHCIQueue *q)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO check 4.12 for splits
|
// 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) {
|
if (tbytes && p->pid == USB_TOKEN_IN) {
|
||||||
p->tbytes -= p->usb_status;
|
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 {
|
} else {
|
||||||
p->tbytes = 0;
|
tbytes = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF("updating tbytes to %d\n", p->tbytes);
|
DPRINTF("updating tbytes to %d\n", tbytes);
|
||||||
set_field(&q->qh.token, p->tbytes, QTD_TOKEN_TBYTES);
|
set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES);
|
||||||
}
|
}
|
||||||
ehci_finish_transfer(q, p->usb_status);
|
ehci_finish_transfer(q, p->usb_status);
|
||||||
usb_packet_unmap(&p->packet, &p->sgl);
|
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) {
|
if (q->qh.token & QTD_TOKEN_IOC) {
|
||||||
ehci_raise_irq(q->ehci, USBSTS_INT);
|
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;
|
USBEndpoint *ep;
|
||||||
int ret;
|
int ret;
|
||||||
int endp;
|
int endp;
|
||||||
|
bool spd;
|
||||||
|
|
||||||
assert(p->async == EHCI_ASYNC_NONE ||
|
assert(p->async == EHCI_ASYNC_NONE ||
|
||||||
p->async == EHCI_ASYNC_INITIALIZED);
|
p->async == EHCI_ASYNC_INITIALIZED);
|
||||||
@ -1544,8 +1561,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
|
|||||||
return USB_RET_PROCERR;
|
return USB_RET_PROCERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
p->tbytes = (p->qtd.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
|
if (get_field(p->qtd.token, QTD_TOKEN_TBYTES) > BUFF_SIZE) {
|
||||||
if (p->tbytes > BUFF_SIZE) {
|
|
||||||
ehci_trace_guest_bug(p->queue->ehci,
|
ehci_trace_guest_bug(p->queue->ehci,
|
||||||
"guest requested more bytes than allowed");
|
"guest requested more bytes than allowed");
|
||||||
return USB_RET_PROCERR;
|
return USB_RET_PROCERR;
|
||||||
@ -1575,17 +1591,18 @@ static int ehci_execute(EHCIPacket *p, const char *action)
|
|||||||
return USB_RET_PROCERR;
|
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);
|
usb_packet_map(&p->packet, &p->sgl);
|
||||||
p->async = EHCI_ASYNC_INITIALIZED;
|
p->async = EHCI_ASYNC_INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_usb_ehci_packet_action(p->queue, p, action);
|
trace_usb_ehci_packet_action(p->queue, p, action);
|
||||||
ret = usb_handle_packet(p->queue->dev, &p->packet);
|
ret = usb_handle_packet(p->queue->dev, &p->packet);
|
||||||
DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
|
DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd endp %x ret %d\n",
|
||||||
"(total %d) endp %x ret %d\n",
|
|
||||||
q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
|
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) {
|
if (ret > BUFF_SIZE) {
|
||||||
fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
|
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);
|
dev = ehci_find_device(ehci, devaddr);
|
||||||
ep = usb_ep_get(dev, pid, endp);
|
ep = usb_ep_get(dev, pid, endp);
|
||||||
if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) {
|
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);
|
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);
|
|
||||||
usb_packet_unmap(&ehci->ipacket, &ehci->isgl);
|
usb_packet_unmap(&ehci->ipacket, &ehci->isgl);
|
||||||
} else {
|
} else {
|
||||||
DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
|
DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
|
||||||
@ -1988,7 +2005,7 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
|
|||||||
{
|
{
|
||||||
EHCIqtd qtd;
|
EHCIqtd qtd;
|
||||||
EHCIPacket *p;
|
EHCIPacket *p;
|
||||||
int again = 0;
|
int again = 1;
|
||||||
|
|
||||||
get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
|
get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
|
||||||
sizeof(EHCIqtd) >> 2);
|
sizeof(EHCIqtd) >> 2);
|
||||||
@ -2016,7 +2033,6 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
|
|||||||
p = NULL;
|
p = NULL;
|
||||||
}
|
}
|
||||||
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
||||||
again = 1;
|
|
||||||
} else if (p != NULL) {
|
} else if (p != NULL) {
|
||||||
switch (p->async) {
|
switch (p->async) {
|
||||||
case EHCI_ASYNC_NONE:
|
case EHCI_ASYNC_NONE:
|
||||||
@ -2025,6 +2041,9 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
|
|||||||
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
|
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
|
||||||
break;
|
break;
|
||||||
case EHCI_ASYNC_INFLIGHT:
|
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 */
|
/* Unfinished async handled packet, go horizontal */
|
||||||
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
||||||
break;
|
break;
|
||||||
@ -2036,13 +2055,11 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
|
|||||||
ehci_set_state(q->ehci, q->async, EST_EXECUTING);
|
ehci_set_state(q->ehci, q->async, EST_EXECUTING);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
again = 1;
|
|
||||||
} else {
|
} 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return again;
|
return again;
|
||||||
@ -2065,18 +2082,23 @@ static int ehci_state_horizqh(EHCIQueue *q)
|
|||||||
|
|
||||||
static int ehci_fill_queue(EHCIPacket *p)
|
static int ehci_fill_queue(EHCIPacket *p)
|
||||||
{
|
{
|
||||||
|
USBEndpoint *ep = p->packet.ep;
|
||||||
EHCIQueue *q = p->queue;
|
EHCIQueue *q = p->queue;
|
||||||
EHCIqtd qtd = p->qtd;
|
EHCIqtd qtd = p->qtd;
|
||||||
uint32_t qtdaddr;
|
uint32_t qtdaddr, start_addr = p->qtdaddr;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (NLPTR_TBIT(qtd.altnext) == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (NLPTR_TBIT(qtd.next) != 0) {
|
if (NLPTR_TBIT(qtd.next) != 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
qtdaddr = qtd.next;
|
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),
|
get_dwords(q->ehci, NLPTR_GET(qtdaddr),
|
||||||
(uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
|
(uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
|
||||||
ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd);
|
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);
|
assert(p->usb_status == USB_RET_ASYNC);
|
||||||
p->async = EHCI_ASYNC_INFLIGHT;
|
p->async = EHCI_ASYNC_INFLIGHT;
|
||||||
}
|
}
|
||||||
|
if (p->usb_status != USB_RET_PROCERR) {
|
||||||
|
usb_device_flush_ep_queue(ep->dev, ep);
|
||||||
|
}
|
||||||
return p->usb_status;
|
return p->usb_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2198,19 +2223,6 @@ 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 {
|
||||||
@ -2502,18 +2514,19 @@ static void ehci_frame_timer(void *opaque)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (need_timer) {
|
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);
|
* (ehci->async_stepdown+1) / FRAME_TIMER_FREQ);
|
||||||
|
}
|
||||||
qemu_mod_timer(ehci->frame_timer, expire_time);
|
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 = {
|
static const MemoryRegionOps ehci_mmio_caps_ops = {
|
||||||
.read = ehci_caps_read,
|
.read = ehci_caps_read,
|
||||||
.valid.min_access_size = 1,
|
.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->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->aqueues);
|
||||||
QTAILQ_INIT(&s->pqueues);
|
QTAILQ_INIT(&s->pqueues);
|
||||||
usb_packet_init(&s->ipacket);
|
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]);
|
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);
|
(dev->addr << 16) | (uep->nr << 8) | pid, false, true);
|
||||||
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;
|
||||||
@ -635,6 +635,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
|||||||
ret = usb_handle_packet(dev, &ep->packey[dir].p);
|
ret = usb_handle_packet(dev, &ep->packey[dir].p);
|
||||||
|
|
||||||
if (ret == USB_RET_ASYNC) {
|
if (ret == USB_RET_ASYNC) {
|
||||||
|
usb_device_flush_ep_queue(dev, uep);
|
||||||
ep->status[dir] = len;
|
ep->status[dir] = len;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -810,12 +810,15 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||||||
if (completion) {
|
if (completion) {
|
||||||
ret = ohci->usb_packet.result;
|
ret = ohci->usb_packet.result;
|
||||||
} else {
|
} 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));
|
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, addr);
|
usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false, int_req);
|
||||||
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) {
|
||||||
|
usb_device_flush_ep_queue(dev, ep);
|
||||||
return 1;
|
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));
|
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, 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);
|
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
|
||||||
DPRINTF("ret=%d\n", ret);
|
DPRINTF("ret=%d\n", ret);
|
||||||
#endif
|
#endif
|
||||||
if (ret == USB_RET_ASYNC) {
|
if (ret == USB_RET_ASYNC) {
|
||||||
|
usb_device_flush_ep_queue(dev, ep);
|
||||||
ohci->async_td = addr;
|
ohci->async_td = addr;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -100,16 +100,17 @@ struct UHCIAsync {
|
|||||||
QEMUSGList sgl;
|
QEMUSGList sgl;
|
||||||
UHCIQueue *queue;
|
UHCIQueue *queue;
|
||||||
QTAILQ_ENTRY(UHCIAsync) next;
|
QTAILQ_ENTRY(UHCIAsync) next;
|
||||||
uint32_t td;
|
uint32_t td_addr;
|
||||||
uint8_t isoc;
|
|
||||||
uint8_t done;
|
uint8_t done;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UHCIQueue {
|
struct UHCIQueue {
|
||||||
|
uint32_t qh_addr;
|
||||||
uint32_t token;
|
uint32_t token;
|
||||||
UHCIState *uhci;
|
UHCIState *uhci;
|
||||||
|
USBEndpoint *ep;
|
||||||
QTAILQ_ENTRY(UHCIQueue) next;
|
QTAILQ_ENTRY(UHCIQueue) next;
|
||||||
QTAILQ_HEAD(, UHCIAsync) asyncs;
|
QTAILQ_HEAD(asyncs_head, UHCIAsync) asyncs;
|
||||||
int8_t valid;
|
int8_t valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -161,13 +162,55 @@ typedef struct UHCI_QH {
|
|||||||
uint32_t el_link;
|
uint32_t el_link;
|
||||||
} UHCI_QH;
|
} 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)
|
static inline int32_t uhci_queue_token(UHCI_TD *td)
|
||||||
{
|
{
|
||||||
/* covers ep, dev, pid -> identifies the endpoint */
|
if ((td->token & (0xf << 15)) == 0) {
|
||||||
return td->token & 0x7ffff;
|
/* 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);
|
uint32_t token = uhci_queue_token(td);
|
||||||
UHCIQueue *queue;
|
UHCIQueue *queue;
|
||||||
@ -177,41 +220,36 @@ static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td)
|
|||||||
return queue;
|
return queue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
return queue->qh_addr == qh_addr &&
|
||||||
QTAILQ_REMOVE(&s->queues, queue, next);
|
queue->token == uhci_queue_token(td) &&
|
||||||
g_free(queue);
|
(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);
|
UHCIAsync *async = g_new0(UHCIAsync, 1);
|
||||||
|
|
||||||
async->queue = queue;
|
async->queue = queue;
|
||||||
async->td = addr;
|
async->td_addr = td_addr;
|
||||||
usb_packet_init(&async->packet);
|
usb_packet_init(&async->packet);
|
||||||
pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
|
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;
|
return async;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_async_free(UHCIAsync *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);
|
usb_packet_cleanup(&async->packet);
|
||||||
qemu_sglist_destroy(&async->sgl);
|
qemu_sglist_destroy(&async->sgl);
|
||||||
g_free(async);
|
g_free(async);
|
||||||
@ -221,21 +259,24 @@ static void uhci_async_link(UHCIAsync *async)
|
|||||||
{
|
{
|
||||||
UHCIQueue *queue = async->queue;
|
UHCIQueue *queue = async->queue;
|
||||||
QTAILQ_INSERT_TAIL(&queue->asyncs, async, next);
|
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)
|
static void uhci_async_unlink(UHCIAsync *async)
|
||||||
{
|
{
|
||||||
UHCIQueue *queue = async->queue;
|
UHCIQueue *queue = async->queue;
|
||||||
QTAILQ_REMOVE(&queue->asyncs, async, next);
|
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)
|
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)
|
if (!async->done)
|
||||||
usb_cancel_packet(&async->packet);
|
usb_cancel_packet(&async->packet);
|
||||||
|
usb_packet_unmap(&async->packet, &async->sgl);
|
||||||
uhci_async_free(async);
|
uhci_async_free(async);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,34 +299,21 @@ static void uhci_async_validate_begin(UHCIState *s)
|
|||||||
static void uhci_async_validate_end(UHCIState *s)
|
static void uhci_async_validate_end(UHCIState *s)
|
||||||
{
|
{
|
||||||
UHCIQueue *queue, *n;
|
UHCIQueue *queue, *n;
|
||||||
UHCIAsync *async;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
|
QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
|
||||||
if (queue->valid > 0) {
|
if (!queue->valid) {
|
||||||
continue;
|
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)
|
static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
|
||||||
{
|
{
|
||||||
UHCIQueue *queue;
|
UHCIQueue *queue, *n;
|
||||||
UHCIAsync *curr, *n;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH(queue, &s->queues, next) {
|
QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) {
|
||||||
QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
|
if (queue->ep->dev == dev) {
|
||||||
if (!usb_packet_is_inflight(&curr->packet) ||
|
uhci_queue_free(queue, "cancel-device");
|
||||||
curr->packet.ep->dev != dev) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
uhci_async_unlink(curr);
|
|
||||||
uhci_async_cancel(curr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,38 +321,24 @@ static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev)
|
|||||||
static void uhci_async_cancel_all(UHCIState *s)
|
static void uhci_async_cancel_all(UHCIState *s)
|
||||||
{
|
{
|
||||||
UHCIQueue *queue, *nq;
|
UHCIQueue *queue, *nq;
|
||||||
UHCIAsync *curr, *n;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(queue, &s->queues, next, nq) {
|
QTAILQ_FOREACH_SAFE(queue, &s->queues, next, nq) {
|
||||||
QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) {
|
uhci_queue_free(queue, "cancel-all");
|
||||||
uhci_async_unlink(curr);
|
|
||||||
uhci_async_cancel(curr);
|
|
||||||
}
|
|
||||||
uhci_queue_free(queue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
UHCIQueue *queue;
|
||||||
UHCIAsync *async;
|
UHCIAsync *async;
|
||||||
|
|
||||||
QTAILQ_FOREACH(queue, &s->queues, next) {
|
QTAILQ_FOREACH(queue, &s->queues, next) {
|
||||||
if (queue->token == token) {
|
QTAILQ_FOREACH(async, &queue->asyncs, next) {
|
||||||
break;
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -695,13 +709,15 @@ static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_async_complete(USBPort *port, USBPacket *packet);
|
static void uhci_read_td(UHCIState *s, UHCI_TD *td, uint32_t link)
|
||||||
static void uhci_process_frame(UHCIState *s);
|
{
|
||||||
|
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)
|
static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask)
|
||||||
{
|
{
|
||||||
int len = 0, max_len, err, ret;
|
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;
|
*int_mask |= 0x02;
|
||||||
/* short packet: do not update QH */
|
/* short packet: do not update QH */
|
||||||
trace_usb_uhci_packet_complete_shortxfer(async->queue->token,
|
trace_usb_uhci_packet_complete_shortxfer(async->queue->token,
|
||||||
async->td);
|
async->td_addr);
|
||||||
return TD_RESULT_NEXT_QH;
|
return TD_RESULT_NEXT_QH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* success */
|
/* 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;
|
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_NAK:
|
||||||
|
td->ctrl |= TD_CTRL_NAK;
|
||||||
|
return TD_RESULT_NEXT_QH;
|
||||||
|
|
||||||
case USB_RET_STALL:
|
case USB_RET_STALL:
|
||||||
td->ctrl |= TD_CTRL_STALL;
|
td->ctrl |= TD_CTRL_STALL;
|
||||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
trace_usb_uhci_packet_complete_stall(async->queue->token,
|
||||||
s->status |= UHCI_STS_USBERR;
|
async->td_addr);
|
||||||
if (td->ctrl & TD_CTRL_IOC) {
|
err = TD_RESULT_NEXT_QH;
|
||||||
*int_mask |= 0x01;
|
break;
|
||||||
}
|
|
||||||
uhci_update_irq(s);
|
|
||||||
trace_usb_uhci_packet_complete_stall(async->queue->token, async->td);
|
|
||||||
return TD_RESULT_NEXT_QH;
|
|
||||||
|
|
||||||
case USB_RET_BABBLE:
|
case USB_RET_BABBLE:
|
||||||
td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
|
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 */
|
/* frame interrupted */
|
||||||
trace_usb_uhci_packet_complete_babble(async->queue->token, async->td);
|
trace_usb_uhci_packet_complete_babble(async->queue->token,
|
||||||
return TD_RESULT_STOP_FRAME;
|
async->td_addr);
|
||||||
|
err = TD_RESULT_STOP_FRAME;
|
||||||
case USB_RET_NAK:
|
break;
|
||||||
td->ctrl |= TD_CTRL_NAK;
|
|
||||||
if (pid == USB_TOKEN_SETUP)
|
|
||||||
break;
|
|
||||||
return TD_RESULT_NEXT_QH;
|
|
||||||
|
|
||||||
case USB_RET_IOERROR:
|
case USB_RET_IOERROR:
|
||||||
case USB_RET_NODEV:
|
case USB_RET_NODEV:
|
||||||
default:
|
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_ACTIVE;
|
||||||
|
s->status |= UHCI_STS_USBERR;
|
||||||
td->ctrl |= TD_CTRL_TIMEOUT;
|
if (td->ctrl & TD_CTRL_IOC) {
|
||||||
err = (td->ctrl >> TD_CTRL_ERROR_SHIFT) & 3;
|
*int_mask |= 0x01;
|
||||||
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 & ~(3 << TD_CTRL_ERROR_SHIFT)) |
|
uhci_update_irq(s);
|
||||||
(err << TD_CTRL_ERROR_SHIFT);
|
return err;
|
||||||
return TD_RESULT_NEXT_QH;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
|
static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
|
||||||
uint32_t *int_mask, bool queuing)
|
UHCI_TD *td, uint32_t td_addr, uint32_t *int_mask)
|
||||||
{
|
{
|
||||||
UHCIAsync *async;
|
|
||||||
int len = 0, max_len;
|
int len = 0, max_len;
|
||||||
uint8_t pid;
|
bool spd;
|
||||||
USBDevice *dev;
|
bool queuing = (q != NULL);
|
||||||
USBEndpoint *ep;
|
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 ? */
|
/* Is active ? */
|
||||||
if (!(td->ctrl & TD_CTRL_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
|
* 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"
|
* 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;
|
return TD_RESULT_NEXT_QH;
|
||||||
}
|
}
|
||||||
|
|
||||||
async = uhci_async_find_td(s, addr, td);
|
|
||||||
if (async) {
|
if (async) {
|
||||||
/* Already submitted */
|
|
||||||
async->queue->valid = 32;
|
|
||||||
|
|
||||||
if (!async->done)
|
|
||||||
return TD_RESULT_ASYNC_CONT;
|
|
||||||
if (queuing) {
|
if (queuing) {
|
||||||
/* we are busy filling the queue, we are not prepared
|
/* we are busy filling the queue, we are not prepared
|
||||||
to consume completed packages then, just leave them
|
to consume completed packages then, just leave them
|
||||||
in async state */
|
in async state */
|
||||||
return TD_RESULT_ASYNC_CONT;
|
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);
|
uhci_async_unlink(async);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate new packet */
|
/* Allocate new packet */
|
||||||
async = uhci_async_alloc(uhci_queue_get(s, td), addr);
|
if (q == NULL) {
|
||||||
|
USBDevice *dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
|
||||||
/* valid needs to be large enough to handle 10 frame delay
|
USBEndpoint *ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
|
||||||
* for initial isochronous requests
|
q = uhci_queue_new(s, qh_addr, td, ep);
|
||||||
*/
|
}
|
||||||
async->queue->valid = 32;
|
async = uhci_async_alloc(q, td_addr);
|
||||||
async->isoc = td->ctrl & TD_CTRL_IOS;
|
|
||||||
|
|
||||||
max_len = ((td->token >> 21) + 1) & 0x7ff;
|
max_len = ((td->token >> 21) + 1) & 0x7ff;
|
||||||
pid = td->token & 0xff;
|
spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0);
|
||||||
|
usb_packet_setup(&async->packet, pid, q->ep, td_addr, spd,
|
||||||
dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
|
(td->ctrl & TD_CTRL_IOC) != 0);
|
||||||
ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
|
|
||||||
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);
|
||||||
|
|
||||||
switch(pid) {
|
switch(pid) {
|
||||||
case USB_TOKEN_OUT:
|
case USB_TOKEN_OUT:
|
||||||
case USB_TOKEN_SETUP:
|
case USB_TOKEN_SETUP:
|
||||||
len = usb_handle_packet(dev, &async->packet);
|
len = usb_handle_packet(q->ep->dev, &async->packet);
|
||||||
if (len >= 0)
|
if (len >= 0)
|
||||||
len = max_len;
|
len = max_len;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case USB_TOKEN_IN:
|
case USB_TOKEN_IN:
|
||||||
len = usb_handle_packet(dev, &async->packet);
|
len = usb_handle_packet(q->ep->dev, &async->packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* invalid pid : frame interrupted */
|
/* invalid pid : frame interrupted */
|
||||||
|
usb_packet_unmap(&async->packet, &async->sgl);
|
||||||
uhci_async_free(async);
|
uhci_async_free(async);
|
||||||
s->status |= UHCI_STS_HCPERR;
|
s->status |= UHCI_STS_HCPERR;
|
||||||
uhci_update_irq(s);
|
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) {
|
if (len == USB_RET_ASYNC) {
|
||||||
uhci_async_link(async);
|
uhci_async_link(async);
|
||||||
|
if (!queuing) {
|
||||||
|
uhci_queue_fill(q, td);
|
||||||
|
}
|
||||||
return TD_RESULT_ASYNC_START;
|
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);
|
UHCIAsync *async = container_of(packet, UHCIAsync, packet);
|
||||||
UHCIState *s = async->queue->uhci;
|
UHCIState *s = async->queue->uhci;
|
||||||
|
|
||||||
if (async->isoc) {
|
if (packet->result == USB_RET_REMOVE_FROM_QUEUE) {
|
||||||
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);
|
|
||||||
|
|
||||||
uhci_async_unlink(async);
|
uhci_async_unlink(async);
|
||||||
uhci_complete_td(s, &td, async, &int_mask);
|
uhci_async_cancel(async);
|
||||||
s->pending_int_mask |= int_mask;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* update the status bits of the TD */
|
async->done = 1;
|
||||||
val = cpu_to_le32(td.ctrl);
|
if (s->frame_bytes < s->frame_bandwidth) {
|
||||||
pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val));
|
qemu_bh_schedule(s->bh);
|
||||||
uhci_async_free(async);
|
|
||||||
} else {
|
|
||||||
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;
|
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 int_mask = 0;
|
||||||
uint32_t plink = td->link;
|
uint32_t plink = td->link;
|
||||||
uint32_t token = uhci_queue_token(td);
|
|
||||||
UHCI_TD ptd;
|
UHCI_TD ptd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
while (is_valid(plink)) {
|
while (is_valid(plink)) {
|
||||||
pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd));
|
uhci_read_td(q->uhci, &ptd, plink);
|
||||||
le32_to_cpus(&ptd.link);
|
|
||||||
le32_to_cpus(&ptd.ctrl);
|
|
||||||
le32_to_cpus(&ptd.token);
|
|
||||||
le32_to_cpus(&ptd.buffer);
|
|
||||||
if (!(ptd.ctrl & TD_CTRL_ACTIVE)) {
|
if (!(ptd.ctrl & TD_CTRL_ACTIVE)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (uhci_queue_token(&ptd) != token) {
|
if (uhci_queue_token(&ptd) != q->token) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token);
|
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) {
|
if (ret == TD_RESULT_ASYNC_CONT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(ret == TD_RESULT_ASYNC_START);
|
assert(ret == TD_RESULT_ASYNC_START);
|
||||||
assert(int_mask == 0);
|
assert(int_mask == 0);
|
||||||
if (ptd.ctrl & TD_CTRL_SPD) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
plink = ptd.link;
|
plink = ptd.link;
|
||||||
}
|
}
|
||||||
|
usb_device_flush_ep_queue(q->ep->dev, q->ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_process_frame(UHCIState *s)
|
static void uhci_process_frame(UHCIState *s)
|
||||||
@ -1081,15 +1076,11 @@ static void uhci_process_frame(UHCIState *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* TD */
|
/* TD */
|
||||||
pci_dma_read(&s->dev, link & ~0xf, &td, sizeof(td));
|
uhci_read_td(s, &td, link);
|
||||||
le32_to_cpus(&td.link);
|
|
||||||
le32_to_cpus(&td.ctrl);
|
|
||||||
le32_to_cpus(&td.token);
|
|
||||||
le32_to_cpus(&td.buffer);
|
|
||||||
trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token);
|
trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token);
|
||||||
|
|
||||||
old_td_ctrl = td.ctrl;
|
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) {
|
if (old_td_ctrl != td.ctrl) {
|
||||||
/* update the status bits of the TD */
|
/* update the status bits of the TD */
|
||||||
val = cpu_to_le32(td.ctrl);
|
val = cpu_to_le32(td.ctrl);
|
||||||
@ -1108,9 +1099,6 @@ static void uhci_process_frame(UHCIState *s)
|
|||||||
|
|
||||||
case TD_RESULT_ASYNC_START:
|
case TD_RESULT_ASYNC_START:
|
||||||
trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf);
|
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;
|
link = curr_qh ? qh.link : td.link;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -322,6 +322,7 @@ typedef struct XHCITransfer {
|
|||||||
bool running_retry;
|
bool running_retry;
|
||||||
bool cancelled;
|
bool cancelled;
|
||||||
bool complete;
|
bool complete;
|
||||||
|
bool int_req;
|
||||||
unsigned int iso_pkts;
|
unsigned int iso_pkts;
|
||||||
unsigned int slotid;
|
unsigned int slotid;
|
||||||
unsigned int epid;
|
unsigned int epid;
|
||||||
@ -416,6 +417,8 @@ struct XHCIState {
|
|||||||
/* properties */
|
/* properties */
|
||||||
uint32_t numports_2;
|
uint32_t numports_2;
|
||||||
uint32_t numports_3;
|
uint32_t numports_3;
|
||||||
|
uint32_t numintrs;
|
||||||
|
uint32_t numslots;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
|
||||||
/* Operational Registers */
|
/* Operational Registers */
|
||||||
@ -815,8 +818,8 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
|
|||||||
dma_addr_t erdp;
|
dma_addr_t erdp;
|
||||||
unsigned int dp_idx;
|
unsigned int dp_idx;
|
||||||
|
|
||||||
if (v >= MAXINTRS) {
|
if (v >= xhci->numintrs) {
|
||||||
DPRINTF("intr nr out of range (%d >= %d)\n", v, MAXINTRS);
|
DPRINTF("intr nr out of range (%d >= %d)\n", v, xhci->numintrs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
intr = &xhci->intr[v];
|
intr = &xhci->intr[v];
|
||||||
@ -963,6 +966,12 @@ static void xhci_er_reset(XHCIState *xhci, int v)
|
|||||||
XHCIInterrupter *intr = &xhci->intr[v];
|
XHCIInterrupter *intr = &xhci->intr[v];
|
||||||
XHCIEvRingSeg seg;
|
XHCIEvRingSeg seg;
|
||||||
|
|
||||||
|
if (intr->erstsz == 0) {
|
||||||
|
/* disabled */
|
||||||
|
intr->er_start = 0;
|
||||||
|
intr->er_size = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
/* cache the (sole) event ring segment location */
|
/* cache the (sole) event ring segment location */
|
||||||
if (intr->erstsz != 1) {
|
if (intr->erstsz != 1) {
|
||||||
fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", intr->erstsz);
|
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 state)
|
||||||
{
|
{
|
||||||
uint32_t ctx[5];
|
uint32_t ctx[5];
|
||||||
if (epctx->state == state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pci_dma_read(&xhci->pci_dev, epctx->pctx, ctx, sizeof(ctx));
|
pci_dma_read(&xhci->pci_dev, epctx->pctx, ctx, sizeof(ctx));
|
||||||
ctx[0] &= ~EP_STATE_MASK;
|
ctx[0] &= ~EP_STATE_MASK;
|
||||||
@ -1039,7 +1045,7 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
trace_usb_xhci_ep_enable(slotid, epid);
|
trace_usb_xhci_ep_enable(slotid, epid);
|
||||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
assert(epid >= 1 && epid <= 31);
|
assert(epid >= 1 && epid <= 31);
|
||||||
|
|
||||||
slot = &xhci->slots[slotid-1];
|
slot = &xhci->slots[slotid-1];
|
||||||
@ -1082,13 +1088,42 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
|||||||
return CC_SUCCESS;
|
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,
|
static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
|
||||||
unsigned int epid)
|
unsigned int epid)
|
||||||
{
|
{
|
||||||
XHCISlot *slot;
|
XHCISlot *slot;
|
||||||
XHCIEPContext *epctx;
|
XHCIEPContext *epctx;
|
||||||
int i, xferi, killed = 0;
|
int i, xferi, killed = 0;
|
||||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
assert(epid >= 1 && epid <= 31);
|
assert(epid >= 1 && epid <= 31);
|
||||||
|
|
||||||
DPRINTF("xhci_ep_nuke_xfers(%d, %d)\n", slotid, epid);
|
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;
|
xferi = epctx->next_xfer;
|
||||||
for (i = 0; i < TD_QUEUE; i++) {
|
for (i = 0; i < TD_QUEUE; i++) {
|
||||||
XHCITransfer *t = &epctx->transfers[xferi];
|
killed += xhci_ep_nuke_one_xfer(&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;
|
|
||||||
xferi = (xferi + 1) % TD_QUEUE;
|
xferi = (xferi + 1) % TD_QUEUE;
|
||||||
}
|
}
|
||||||
return killed;
|
return killed;
|
||||||
@ -1134,7 +1151,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
|
|||||||
XHCIEPContext *epctx;
|
XHCIEPContext *epctx;
|
||||||
|
|
||||||
trace_usb_xhci_ep_disable(slotid, epid);
|
trace_usb_xhci_ep_disable(slotid, epid);
|
||||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
assert(epid >= 1 && epid <= 31);
|
assert(epid >= 1 && epid <= 31);
|
||||||
|
|
||||||
slot = &xhci->slots[slotid-1];
|
slot = &xhci->slots[slotid-1];
|
||||||
@ -1164,7 +1181,7 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid,
|
|||||||
XHCIEPContext *epctx;
|
XHCIEPContext *epctx;
|
||||||
|
|
||||||
trace_usb_xhci_ep_stop(slotid, epid);
|
trace_usb_xhci_ep_stop(slotid, epid);
|
||||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
|
|
||||||
if (epid < 1 || epid > 31) {
|
if (epid < 1 || epid > 31) {
|
||||||
fprintf(stderr, "xhci: bad ep %d\n", epid);
|
fprintf(stderr, "xhci: bad ep %d\n", epid);
|
||||||
@ -1198,7 +1215,7 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
|
|||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
|
|
||||||
trace_usb_xhci_ep_reset(slotid, epid);
|
trace_usb_xhci_ep_reset(slotid, epid);
|
||||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
|
|
||||||
if (epid < 1 || epid > 31) {
|
if (epid < 1 || epid > 31) {
|
||||||
fprintf(stderr, "xhci: bad ep %d\n", epid);
|
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;
|
XHCIEPContext *epctx;
|
||||||
dma_addr_t dequeue;
|
dma_addr_t dequeue;
|
||||||
|
|
||||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
|
|
||||||
if (epid < 1 || epid > 31) {
|
if (epid < 1 || epid > 31) {
|
||||||
fprintf(stderr, "xhci: bad ep %d\n", epid);
|
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;
|
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;
|
XHCIState *xhci = xfer->xhci;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
xfer->int_req = false;
|
||||||
pci_dma_sglist_init(&xfer->sgl, &xhci->pci_dev, xfer->trb_count);
|
pci_dma_sglist_init(&xfer->sgl, &xhci->pci_dev, xfer->trb_count);
|
||||||
for (i = 0; i < xfer->trb_count; i++) {
|
for (i = 0; i < xfer->trb_count; i++) {
|
||||||
XHCITRB *trb = &xfer->trbs[i];
|
XHCITRB *trb = &xfer->trbs[i];
|
||||||
dma_addr_t addr;
|
dma_addr_t addr;
|
||||||
unsigned int chunk = 0;
|
unsigned int chunk = 0;
|
||||||
|
|
||||||
|
if (trb->control & TRB_TR_IOC) {
|
||||||
|
xfer->int_req = true;
|
||||||
|
}
|
||||||
|
|
||||||
switch (TRB_TYPE(*trb)) {
|
switch (TRB_TYPE(*trb)) {
|
||||||
case TR_DATA:
|
case TR_DATA:
|
||||||
if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) {
|
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;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
@ -1435,8 +1455,10 @@ static int xhci_setup_packet(XHCITransfer *xfer)
|
|||||||
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, xfer->trbs[0].addr);
|
xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */
|
||||||
xhci_xfer_map(xfer);
|
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",
|
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);
|
||||||
return 0;
|
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)
|
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
|
||||||
{
|
{
|
||||||
XHCIEPContext *epctx;
|
XHCIEPContext *epctx;
|
||||||
|
USBEndpoint *ep = NULL;
|
||||||
uint64_t mfindex;
|
uint64_t mfindex;
|
||||||
int length;
|
int length;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
trace_usb_xhci_ep_kick(slotid, epid);
|
trace_usb_xhci_ep_kick(slotid, epid);
|
||||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
assert(epid >= 1 && epid <= 31);
|
assert(epid >= 1 && epid <= 31);
|
||||||
|
|
||||||
if (!xhci->slots[slotid-1].enabled) {
|
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 (epid == 1) {
|
||||||
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
|
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
|
||||||
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
|
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
|
||||||
|
ep = xfer->packet.ep;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "xhci: error firing CTL transfer\n");
|
fprintf(stderr, "xhci: error firing CTL transfer\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
|
if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
|
||||||
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
|
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
|
||||||
|
ep = xfer->packet.ep;
|
||||||
} else {
|
} else {
|
||||||
if (!xfer->iso_xfer) {
|
if (!xfer->iso_xfer) {
|
||||||
fprintf(stderr, "xhci: error firing data transfer\n");
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ep) {
|
||||||
|
usb_device_flush_ep_queue(ep->dev, ep);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid)
|
static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid)
|
||||||
{
|
{
|
||||||
trace_usb_xhci_slot_enable(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].enabled = 1;
|
||||||
xhci->slots[slotid-1].uport = NULL;
|
xhci->slots[slotid-1].uport = NULL;
|
||||||
memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31);
|
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;
|
int i;
|
||||||
|
|
||||||
trace_usb_xhci_slot_disable(slotid);
|
trace_usb_xhci_slot_disable(slotid);
|
||||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
|
|
||||||
for (i = 1; i <= 31; i++) {
|
for (i = 1; i <= 31; i++) {
|
||||||
if (xhci->slots[slotid-1].eps[i-1]) {
|
if (xhci->slots[slotid-1].eps[i-1]) {
|
||||||
@ -1826,7 +1854,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
|
|||||||
TRBCCode res;
|
TRBCCode res;
|
||||||
|
|
||||||
trace_usb_xhci_slot_address(slotid);
|
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);
|
dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
|
||||||
pci_dma_read(&xhci->pci_dev, dcbaap + 8*slotid, &poctx, sizeof(poctx));
|
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;
|
return CC_USB_TRANSACTION_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < MAXSLOTS; i++) {
|
for (i = 0; i < xhci->numslots; i++) {
|
||||||
if (xhci->slots[i].uport == uport) {
|
if (xhci->slots[i].uport == uport) {
|
||||||
fprintf(stderr, "xhci: port %s already assigned to slot %d\n",
|
fprintf(stderr, "xhci: port %s already assigned to slot %d\n",
|
||||||
uport->path, i+1);
|
uport->path, i+1);
|
||||||
@ -1914,7 +1942,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
|
|||||||
TRBCCode res;
|
TRBCCode res;
|
||||||
|
|
||||||
trace_usb_xhci_slot_configure(slotid);
|
trace_usb_xhci_slot_configure(slotid);
|
||||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
|
|
||||||
ictx = xhci_mask64(pictx);
|
ictx = xhci_mask64(pictx);
|
||||||
octx = xhci->slots[slotid-1].ctx;
|
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];
|
uint32_t slot_ctx[4];
|
||||||
|
|
||||||
trace_usb_xhci_slot_evaluate(slotid);
|
trace_usb_xhci_slot_evaluate(slotid);
|
||||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
|
|
||||||
ictx = xhci_mask64(pictx);
|
ictx = xhci_mask64(pictx);
|
||||||
octx = xhci->slots[slotid-1].ctx;
|
octx = xhci->slots[slotid-1].ctx;
|
||||||
@ -2065,7 +2093,7 @@ static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
trace_usb_xhci_slot_reset(slotid);
|
trace_usb_xhci_slot_reset(slotid);
|
||||||
assert(slotid >= 1 && slotid <= MAXSLOTS);
|
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||||
|
|
||||||
octx = xhci->slots[slotid-1].ctx;
|
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;
|
unsigned int slotid;
|
||||||
slotid = (trb->control >> TRB_CR_SLOTID_SHIFT) & TRB_CR_SLOTID_MASK;
|
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);
|
fprintf(stderr, "xhci: bad slot id %d\n", slotid);
|
||||||
event->ccode = CC_TRB_ERROR;
|
event->ccode = CC_TRB_ERROR;
|
||||||
return 0;
|
return 0;
|
||||||
@ -2183,12 +2211,12 @@ static void xhci_process_commands(XHCIState *xhci)
|
|||||||
event.ptr = addr;
|
event.ptr = addr;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CR_ENABLE_SLOT:
|
case CR_ENABLE_SLOT:
|
||||||
for (i = 0; i < MAXSLOTS; i++) {
|
for (i = 0; i < xhci->numslots; i++) {
|
||||||
if (!xhci->slots[i].enabled) {
|
if (!xhci->slots[i].enabled) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i >= MAXSLOTS) {
|
if (i >= xhci->numslots) {
|
||||||
fprintf(stderr, "xhci: no device slots available\n");
|
fprintf(stderr, "xhci: no device slots available\n");
|
||||||
event.ccode = CC_NO_SLOTS_ERROR;
|
event.ccode = CC_NO_SLOTS_ERROR;
|
||||||
} else {
|
} else {
|
||||||
@ -2335,7 +2363,7 @@ static void xhci_reset(DeviceState *dev)
|
|||||||
xhci->config = 0;
|
xhci->config = 0;
|
||||||
xhci->devaddr = 2;
|
xhci->devaddr = 2;
|
||||||
|
|
||||||
for (i = 0; i < MAXSLOTS; i++) {
|
for (i = 0; i < xhci->numslots; i++) {
|
||||||
xhci_disable_slot(xhci, i+1);
|
xhci_disable_slot(xhci, i+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2343,7 +2371,7 @@ static void xhci_reset(DeviceState *dev)
|
|||||||
xhci_update_port(xhci, xhci->ports + i, 0);
|
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].iman = 0;
|
||||||
xhci->intr[i].imod = 0;
|
xhci->intr[i].imod = 0;
|
||||||
xhci->intr[i].erstsz = 0;
|
xhci->intr[i].erstsz = 0;
|
||||||
@ -2375,7 +2403,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
|
|||||||
break;
|
break;
|
||||||
case 0x04: /* HCSPARAMS 1 */
|
case 0x04: /* HCSPARAMS 1 */
|
||||||
ret = ((xhci->numports_2+xhci->numports_3)<<24)
|
ret = ((xhci->numports_2+xhci->numports_3)<<24)
|
||||||
| (MAXINTRS<<8) | MAXSLOTS;
|
| (xhci->numintrs<<8) | xhci->numslots;
|
||||||
break;
|
break;
|
||||||
case 0x08: /* HCSPARAMS 2 */
|
case 0x08: /* HCSPARAMS 2 */
|
||||||
ret = 0x0000000f;
|
ret = 0x0000000f;
|
||||||
@ -2402,7 +2430,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
|
|||||||
ret = 0x02000402; /* USB 2.0 */
|
ret = 0x02000402; /* USB 2.0 */
|
||||||
break;
|
break;
|
||||||
case 0x24: /* Supported Protocol:04 */
|
case 0x24: /* Supported Protocol:04 */
|
||||||
ret = 0x20425455; /* "USB " */
|
ret = 0x20425355; /* "USB " */
|
||||||
break;
|
break;
|
||||||
case 0x28: /* Supported Protocol:08 */
|
case 0x28: /* Supported Protocol:08 */
|
||||||
ret = 0x00000001 | (xhci->numports_2<<8);
|
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 */
|
ret = 0x03000002; /* USB 3.0 */
|
||||||
break;
|
break;
|
||||||
case 0x34: /* Supported Protocol:04 */
|
case 0x34: /* Supported Protocol:04 */
|
||||||
ret = 0x20425455; /* "USB " */
|
ret = 0x20425355; /* "USB " */
|
||||||
break;
|
break;
|
||||||
case 0x38: /* Supported Protocol:08 */
|
case 0x38: /* Supported Protocol:08 */
|
||||||
ret = 0x00000000 | (xhci->numports_2+1) | (xhci->numports_3<<8);
|
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);
|
trace_usb_xhci_runtime_write(reg, val);
|
||||||
|
|
||||||
if (reg < 0x20) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2730,7 +2758,7 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
|
|||||||
(uint32_t)val);
|
(uint32_t)val);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (reg > MAXSLOTS) {
|
if (reg > xhci->numslots) {
|
||||||
fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg);
|
fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg);
|
||||||
} else if (val > 31) {
|
} else if (val > 31) {
|
||||||
fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n",
|
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);
|
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_complete_packet(xfer, packet->result);
|
||||||
xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
|
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);
|
XHCIState *xhci = container_of(bus, XHCIState, bus);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAXSLOTS; i++) {
|
for (i = 0; i < xhci->numslots; i++) {
|
||||||
if (xhci->slots[i].uport == uport) {
|
if (xhci->slots[i].uport == uport) {
|
||||||
xhci->slots[i].uport = NULL;
|
xhci->slots[i].uport = NULL;
|
||||||
}
|
}
|
||||||
@ -2852,7 +2884,7 @@ static int xhci_find_slotid(XHCIState *xhci, USBDevice *dev)
|
|||||||
XHCISlot *slot;
|
XHCISlot *slot;
|
||||||
int slotid;
|
int slotid;
|
||||||
|
|
||||||
for (slotid = 1; slotid <= MAXSLOTS; slotid++) {
|
for (slotid = 1; slotid <= xhci->numslots; slotid++) {
|
||||||
slot = &xhci->slots[slotid-1];
|
slot = &xhci->slots[slotid-1];
|
||||||
if (slot->devaddr == dev->addr) {
|
if (slot->devaddr == dev->addr) {
|
||||||
return slotid;
|
return slotid;
|
||||||
@ -2948,6 +2980,19 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
|
|||||||
|
|
||||||
usb_xhci_init(xhci, &dev->qdev);
|
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->mfwrap_timer = qemu_new_timer_ns(vm_clock, xhci_mfwrap_timer, xhci);
|
||||||
|
|
||||||
xhci->irq = xhci->pci_dev.irq[0];
|
xhci->irq = xhci->pci_dev.irq[0];
|
||||||
@ -2984,10 +3029,10 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
|
|||||||
assert(ret >= 0);
|
assert(ret >= 0);
|
||||||
|
|
||||||
if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) {
|
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)) {
|
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_TABLE,
|
||||||
&xhci->mem, 0, OFF_MSIX_PBA,
|
&xhci->mem, 0, OFF_MSIX_PBA,
|
||||||
0x90);
|
0x90);
|
||||||
@ -3002,10 +3047,12 @@ static const VMStateDescription vmstate_xhci = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static Property xhci_properties[] = {
|
static Property xhci_properties[] = {
|
||||||
DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true),
|
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_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true),
|
||||||
DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4),
|
DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS),
|
||||||
DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4),
|
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(),
|
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_type(&s->dev, pid, ep, type);
|
||||||
usb_ep_set_ifnum(&s->dev, pid, ep, interface);
|
usb_ep_set_ifnum(&s->dev, pid, ep, interface);
|
||||||
if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) &&
|
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);
|
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,
|
static void usbredir_ep_info(void *priv,
|
||||||
struct usb_redir_ep_info_header *ep_info)
|
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 =
|
dev->endpoint[i].max_packet_size =
|
||||||
usb_ep->max_packet_size = ep_info->max_packet_size[i];
|
usb_ep->max_packet_size = ep_info->max_packet_size[i];
|
||||||
}
|
}
|
||||||
if (ep_info->type[i] == usb_redir_type_bulk) {
|
usbredir_set_pipeline(dev, usb_ep);
|
||||||
usb_ep->pipeline = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1574,9 +1582,7 @@ static int usbredir_post_load(void *priv, int version_id)
|
|||||||
usb_ep->type = dev->endpoint[i].type;
|
usb_ep->type = dev->endpoint[i].type;
|
||||||
usb_ep->ifnum = dev->endpoint[i].interface;
|
usb_ep->ifnum = dev->endpoint[i].interface;
|
||||||
usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
|
usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
|
||||||
if (dev->endpoint[i].type == usb_redir_type_bulk) {
|
usbredir_set_pipeline(dev, usb_ep);
|
||||||
usb_ep->pipeline = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
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_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_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_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_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_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"
|
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