aboutsummaryrefslogtreecommitdiff
path: root/hw/usb
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2012-06-11 12:07:00 -0500
committerAnthony Liguori <aliguori@us.ibm.com>2012-06-11 12:07:00 -0500
commit39cde84517ce1bcef294dc9e9e0940475c46fab2 (patch)
tree9befc1caef49e7ca0c09badf6b60df7d9be6ee3d /hw/usb
parentdbaf26b3b22daae3be6a89b965e43503e7c3b912 (diff)
parentf020ed36fef7780e81a94543ae6388d8dc144ab6 (diff)
Merge remote-tracking branch 'kraxel/usb.52' into staging
* kraxel/usb.52: (37 commits) ehci: rework frame skipping ehci: adaptive wakeup rate. ehci: create ehci_update_frindex ehci: remove unused attach_poll_counter ehci: fix halt status handling ehci: update status bits in ehci_set_state ehci: add ehci_*_enabled() helpers ehci: fix reset ehci: kick async schedule on wakeup ehci: schedule async bh on async packet completion ehci: move async schedule to bottom half ehci: add async field to EHCIQueue ehci: tweak queue initialization ehci: add queuing support ehci: move ehci_flush_qh ehci: cache USBDevice in EHCIQueue ehci: make ehci_execute work on EHCIPacket instead of EHCIQueue ehci: add EHCIPacket xhci: trace: slots xhci: trace: transfers ...
Diffstat (limited to 'hw/usb')
-rw-r--r--hw/usb/dev-storage.c69
-rw-r--r--hw/usb/hcd-ehci.c610
-rw-r--r--hw/usb/hcd-uhci.c67
-rw-r--r--hw/usb/hcd-xhci.c251
4 files changed, 636 insertions, 361 deletions
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index a96c0b9e5e..097d7b4a6d 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -48,10 +48,9 @@ struct usb_msd_csw {
typedef struct {
USBDevice dev;
enum USBMSDMode mode;
+ uint32_t scsi_off;
uint32_t scsi_len;
- uint8_t *scsi_buf;
uint32_t data_len;
- uint32_t residue;
struct usb_msd_csw csw;
SCSIRequest *req;
SCSIBus bus;
@@ -179,9 +178,9 @@ static void usb_msd_copy_data(MSDState *s, USBPacket *p)
len = p->iov.size - p->result;
if (len > s->scsi_len)
len = s->scsi_len;
- usb_packet_copy(p, s->scsi_buf, len);
+ usb_packet_copy(p, scsi_req_get_buf(s->req) + s->scsi_off, len);
s->scsi_len -= len;
- s->scsi_buf += len;
+ s->scsi_off += len;
s->data_len -= len;
if (s->scsi_len == 0 || s->data_len == 0) {
scsi_req_continue(s->req);
@@ -201,6 +200,18 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
memset(&s->csw, 0, sizeof(s->csw));
}
+static void usb_msd_packet_complete(MSDState *s)
+{
+ USBPacket *p = s->packet;
+
+ /* Set s->packet to NULL before calling usb_packet_complete
+ because another request may be issued before
+ usb_packet_complete returns. */
+ DPRINTF("Packet complete %p\n", p);
+ s->packet = NULL;
+ usb_packet_complete(&s->dev, p);
+}
+
static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
@@ -208,17 +219,12 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
s->scsi_len = len;
- s->scsi_buf = scsi_req_get_buf(req);
+ s->scsi_off = 0;
if (p) {
usb_msd_copy_data(s, p);
p = s->packet;
if (p && p->result == p->iov.size) {
- /* Set s->packet to NULL before calling usb_packet_complete
- because another request may be issued before
- usb_packet_complete returns. */
- DPRINTF("Packet complete %p\n", p);
- s->packet = NULL;
- usb_packet_complete(&s->dev, p);
+ usb_msd_packet_complete(s);
}
}
}
@@ -229,11 +235,10 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r
USBPacket *p = s->packet;
DPRINTF("Command complete %d tag 0x%x\n", status, req->tag);
- s->residue = s->data_len;
s->csw.sig = cpu_to_le32(0x53425355);
s->csw.tag = cpu_to_le32(req->tag);
- s->csw.residue = cpu_to_le32(s->residue);
+ s->csw.residue = cpu_to_le32(s->data_len);
s->csw.status = status != 0;
if (s->packet) {
@@ -252,8 +257,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r
s->mode = USB_MSDM_CSW;
}
}
- s->packet = NULL;
- usb_packet_complete(&s->dev, p);
+ usb_msd_packet_complete(s);
} else if (s->data_len == 0) {
s->mode = USB_MSDM_CSW;
}
@@ -283,10 +287,8 @@ static void usb_msd_handle_reset(USBDevice *dev)
assert(s->req == NULL);
if (s->packet) {
- USBPacket *p = s->packet;
- s->packet = NULL;
- p->result = USB_RET_STALL;
- usb_packet_complete(dev, p);
+ s->packet->result = USB_RET_STALL;
+ usb_msd_packet_complete(s);
}
s->mode = USB_MSDM_CBW;
@@ -378,7 +380,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
}
DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
tag, cbw.flags, cbw.cmd_len, s->data_len);
- s->residue = 0;
+ assert(le32_to_cpu(s->csw.residue) == 0);
s->scsi_len = 0;
s->req = scsi_req_new(s->scsi_dev, tag, 0, cbw.cmd, NULL);
scsi_req_enqueue(s->req);
@@ -397,7 +399,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
if (s->scsi_len) {
usb_msd_copy_data(s, p);
}
- if (s->residue) {
+ if (le32_to_cpu(s->csw.residue)) {
int len = p->iov.size - p->result;
if (len) {
usb_packet_skip(p, len);
@@ -458,7 +460,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
if (s->scsi_len) {
usb_msd_copy_data(s, p);
}
- if (s->residue) {
+ if (le32_to_cpu(s->csw.residue)) {
int len = p->iov.size - p->result;
if (len) {
usb_packet_skip(p, len);
@@ -504,6 +506,17 @@ static void usb_msd_password_cb(void *opaque, int err)
qdev_unplug(&s->dev.qdev, NULL);
}
+static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
+{
+ MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
+
+ /* nothing to load, just store req in our state struct */
+ assert(s->req == NULL);
+ scsi_req_ref(req);
+ s->req = req;
+ return NULL;
+}
+
static const struct SCSIBusInfo usb_msd_scsi_info = {
.tcq = false,
.max_target = 0,
@@ -511,7 +524,8 @@ static const struct SCSIBusInfo usb_msd_scsi_info = {
.transfer_data = usb_msd_transfer_data,
.complete = usb_msd_command_complete,
- .cancel = usb_msd_request_cancelled
+ .cancel = usb_msd_request_cancelled,
+ .load_request = usb_msd_load_request,
};
static int usb_msd_initfn(USBDevice *dev)
@@ -631,11 +645,18 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename)
static const VMStateDescription vmstate_usb_msd = {
.name = "usb-storage",
- .unmigratable = 1, /* FIXME: handle transactions which are in flight */
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField []) {
VMSTATE_USB_DEVICE(dev, MSDState),
+ VMSTATE_UINT32(mode, MSDState),
+ VMSTATE_UINT32(scsi_len, MSDState),
+ VMSTATE_UINT32(scsi_off, MSDState),
+ VMSTATE_UINT32(data_len, MSDState),
+ VMSTATE_UINT32(csw.sig, MSDState),
+ VMSTATE_UINT32(csw.tag, MSDState),
+ VMSTATE_UINT32(csw.residue, MSDState),
+ VMSTATE_UINT8(csw.status, MSDState),
VMSTATE_END_OF_LIST()
}
};
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index e759c996ce..5298204d9d 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -334,6 +334,7 @@ typedef struct EHCIfstn {
uint32_t backptr; // Standard next link pointer
} EHCIfstn;
+typedef struct EHCIPacket EHCIPacket;
typedef struct EHCIQueue EHCIQueue;
typedef struct EHCIState EHCIState;
@@ -343,26 +344,36 @@ enum async_state {
EHCI_ASYNC_FINISHED,
};
+struct EHCIPacket {
+ EHCIQueue *queue;
+ QTAILQ_ENTRY(EHCIPacket) next;
+
+ EHCIqtd qtd; /* copy of current QTD (being worked on) */
+ uint32_t qtdaddr; /* address QTD read from */
+
+ USBPacket packet;
+ QEMUSGList sgl;
+ int pid;
+ uint32_t tbytes;
+ enum async_state async;
+ int usb_status;
+};
+
struct EHCIQueue {
EHCIState *ehci;
QTAILQ_ENTRY(EHCIQueue) next;
uint32_t seen;
uint64_t ts;
+ int async;
/* cached data from guest - needs to be flushed
* when guest removes an entry (doorbell, handshake sequence)
*/
- EHCIqh qh; // copy of current QH (being worked on)
- uint32_t qhaddr; // address QH read from
- EHCIqtd qtd; // copy of current QTD (being worked on)
- uint32_t qtdaddr; // address QTD read from
-
- USBPacket packet;
- QEMUSGList sgl;
- int pid;
- uint32_t tbytes;
- enum async_state async;
- int usb_status;
+ EHCIqh qh; /* copy of current QH (being worked on) */
+ uint32_t qhaddr; /* address QH read from */
+ uint32_t qtdaddr; /* address QTD read from */
+ USBDevice *dev;
+ QTAILQ_HEAD(, EHCIPacket) packets;
};
typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
@@ -375,7 +386,6 @@ struct EHCIState {
int companion_count;
/* properties */
- uint32_t freq;
uint32_t maxframes;
/*
@@ -403,7 +413,7 @@ struct EHCIState {
* Internal states, shadow registers, etc
*/
QEMUTimer *frame_timer;
- int attach_poll_counter;
+ QEMUBH *async_bh;
int astate; // Current state in asynchronous schedule
int pstate; // Current state in periodic schedule
USBPort ports[NB_PORTS];
@@ -419,6 +429,7 @@ struct EHCIState {
QEMUSGList isgl;
uint64_t last_run_ns;
+ uint32_t async_stepdown;
};
#define SET_LAST_RUN_CLOCK(s) \
@@ -574,14 +585,37 @@ static inline void ehci_commit_interrupt(EHCIState *s)
s->usbsts_pending = 0;
}
+static void ehci_update_halt(EHCIState *s)
+{
+ if (s->usbcmd & USBCMD_RUNSTOP) {
+ ehci_clear_usbsts(s, USBSTS_HALT);
+ } else {
+ if (s->astate == EST_INACTIVE && s->pstate == EST_INACTIVE) {
+ ehci_set_usbsts(s, USBSTS_HALT);
+ }
+ }
+}
+
static void ehci_set_state(EHCIState *s, int async, int state)
{
if (async) {
trace_usb_ehci_state("async", state2str(state));
s->astate = state;
+ if (s->astate == EST_INACTIVE) {
+ ehci_clear_usbsts(s, USBSTS_ASS);
+ ehci_update_halt(s);
+ } else {
+ ehci_set_usbsts(s, USBSTS_ASS);
+ }
} else {
trace_usb_ehci_state("periodic", state2str(state));
s->pstate = state;
+ if (s->pstate == EST_INACTIVE) {
+ ehci_clear_usbsts(s, USBSTS_PSS);
+ ehci_update_halt(s);
+ } else {
+ ehci_set_usbsts(s, USBSTS_PSS);
+ }
}
}
@@ -655,27 +689,71 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr,
(bool)(sitd->results & SITD_RESULTS_ACTIVE));
}
+static inline bool ehci_enabled(EHCIState *s)
+{
+ return s->usbcmd & USBCMD_RUNSTOP;
+}
+
+static inline bool ehci_async_enabled(EHCIState *s)
+{
+ return ehci_enabled(s) && (s->usbcmd & USBCMD_ASE);
+}
+
+static inline bool ehci_periodic_enabled(EHCIState *s)
+{
+ return ehci_enabled(s) && (s->usbcmd & USBCMD_PSE);
+}
+
+/* packet management */
+
+static EHCIPacket *ehci_alloc_packet(EHCIQueue *q)
+{
+ EHCIPacket *p;
+
+ p = g_new0(EHCIPacket, 1);
+ p->queue = q;
+ usb_packet_init(&p->packet);
+ QTAILQ_INSERT_TAIL(&q->packets, p, next);
+ trace_usb_ehci_packet_action(p->queue, p, "alloc");
+ return p;
+}
+
+static void ehci_free_packet(EHCIPacket *p)
+{
+ trace_usb_ehci_packet_action(p->queue, p, "free");
+ if (p->async == EHCI_ASYNC_INFLIGHT) {
+ usb_cancel_packet(&p->packet);
+ }
+ QTAILQ_REMOVE(&p->queue->packets, p, next);
+ usb_packet_cleanup(&p->packet);
+ g_free(p);
+}
+
/* queue management */
-static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async)
+static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async)
{
EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
EHCIQueue *q;
q = g_malloc0(sizeof(*q));
q->ehci = ehci;
- usb_packet_init(&q->packet);
+ q->qhaddr = addr;
+ q->async = async;
+ QTAILQ_INIT(&q->packets);
QTAILQ_INSERT_HEAD(head, q, next);
trace_usb_ehci_queue_action(q, "alloc");
return q;
}
-static void ehci_free_queue(EHCIQueue *q, int async)
+static void ehci_free_queue(EHCIQueue *q)
{
- EHCIQueueHead *head = async ? &q->ehci->aqueues : &q->ehci->pqueues;
+ EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
+ EHCIPacket *p;
+
trace_usb_ehci_queue_action(q, "free");
- if (q->async == EHCI_ASYNC_INFLIGHT) {
- usb_cancel_packet(&q->packet);
+ while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
+ ehci_free_packet(p);
}
QTAILQ_REMOVE(head, q, next);
g_free(q);
@@ -698,6 +776,7 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
{
EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+ uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4;
EHCIQueue *q, *tmp;
QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
@@ -706,11 +785,10 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
q->ts = ehci->last_run_ns;
continue;
}
- if (!flush && ehci->last_run_ns < q->ts + 250000000) {
- /* allow 0.25 sec idle */
+ if (!flush && ehci->last_run_ns < q->ts + maxage) {
continue;
}
- ehci_free_queue(q, async);
+ ehci_free_queue(q);
}
}
@@ -720,11 +798,10 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async)
EHCIQueue *q, *tmp;
QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
- if (!usb_packet_is_inflight(&q->packet) ||
- q->packet.ep->dev != dev) {
+ if (q->dev != dev) {
continue;
}
- ehci_free_queue(q, async);
+ ehci_free_queue(q);
}
}
@@ -734,7 +811,7 @@ static void ehci_queues_rip_all(EHCIState *ehci, int async)
EHCIQueue *q, *tmp;
QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
- ehci_free_queue(q, async);
+ ehci_free_queue(q);
}
}
@@ -812,6 +889,8 @@ static void ehci_wakeup(USBPort *port)
USBPort *companion = s->companion_ports[port->index];
if (companion->ops->wakeup) {
companion->ops->wakeup(companion);
+ } else {
+ qemu_bh_schedule(s->async_bh);
}
}
}
@@ -904,7 +983,6 @@ static void ehci_reset(void *opaque)
s->astate = EST_INACTIVE;
s->pstate = EST_INACTIVE;
- s->attach_poll_counter = 0;
for(i = 0; i < NB_PORTS; i++) {
if (s->companion_ports[i]) {
@@ -920,6 +998,7 @@ static void ehci_reset(void *opaque)
ehci_queues_rip_all(s, 0);
ehci_queues_rip_all(s, 1);
qemu_del_timer(s->frame_timer);
+ qemu_bh_cancel(s->async_bh);
}
static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
@@ -1064,22 +1143,20 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
/* Do any register specific pre-write processing here. */
switch(addr) {
case USBCMD:
- if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) {
- qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
- SET_LAST_RUN_CLOCK(s);
- ehci_clear_usbsts(s, USBSTS_HALT);
- }
-
- if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) {
- qemu_del_timer(s->frame_timer);
- ehci_queues_rip_all(s, 0);
- ehci_queues_rip_all(s, 1);
- ehci_set_usbsts(s, USBSTS_HALT);
- }
-
if (val & USBCMD_HCRESET) {
ehci_reset(s);
val = s->usbcmd;
+ break;
+ }
+
+ if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) !=
+ ((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) {
+ if (s->pstate == EST_INACTIVE) {
+ SET_LAST_RUN_CLOCK(s);
+ }
+ ehci_update_halt(s);
+ s->async_stepdown = 0;
+ qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
}
/* not supporting dynamic frame list size at the moment */
@@ -1114,7 +1191,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
break;
case PERIODICLISTBASE:
- if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+ if (ehci_periodic_enabled(s)) {
fprintf(stderr,
"ehci: PERIODIC list base register set while periodic schedule\n"
" is enabled and HC is enabled\n");
@@ -1122,7 +1199,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
break;
case ASYNCLISTADDR:
- if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+ if (ehci_async_enabled(s)) {
fprintf(stderr,
"ehci: ASYNC list address register set while async schedule\n"
" is enabled and HC is enabled\n");
@@ -1169,21 +1246,25 @@ static inline int put_dwords(EHCIState *ehci, uint32_t addr,
static int ehci_qh_do_overlay(EHCIQueue *q)
{
+ EHCIPacket *p = QTAILQ_FIRST(&q->packets);
int i;
int dtoggle;
int ping;
int eps;
int reload;
+ assert(p != NULL);
+ assert(p->qtdaddr == q->qtdaddr);
+
// remember values in fields to preserve in qh after overlay
dtoggle = q->qh.token & QTD_TOKEN_DTOGGLE;
ping = q->qh.token & QTD_TOKEN_PING;
- q->qh.current_qtd = q->qtdaddr;
- q->qh.next_qtd = q->qtd.next;
- q->qh.altnext_qtd = q->qtd.altnext;
- q->qh.token = q->qtd.token;
+ q->qh.current_qtd = p->qtdaddr;
+ q->qh.next_qtd = p->qtd.next;
+ q->qh.altnext_qtd = p->qtd.altnext;
+ q->qh.token = p->qtd.token;
eps = get_field(q->qh.epchar, QH_EPCHAR_EPS);
@@ -1196,7 +1277,7 @@ static int ehci_qh_do_overlay(EHCIQueue *q)
set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
for (i = 0; i < 5; i++) {
- q->qh.bufptr[i] = q->qtd.bufptr[i];
+ q->qh.bufptr[i] = p->qtd.bufptr[i];
}
if (!(q->qh.epchar & QH_EPCHAR_DTC)) {
@@ -1214,15 +1295,15 @@ static int ehci_qh_do_overlay(EHCIQueue *q)
return 0;
}
-static int ehci_init_transfer(EHCIQueue *q)
+static int ehci_init_transfer(EHCIPacket *p)
{
uint32_t cpage, offset, bytes, plen;
dma_addr_t page;
- cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
- bytes = get_field(q->qh.token, QTD_TOKEN_TBYTES);
- offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
- pci_dma_sglist_init(&q->sgl, &q->ehci->dev, 5);
+ cpage = get_field(p->qtd.token, QTD_TOKEN_CPAGE);
+ bytes = get_field(p->qtd.token, QTD_TOKEN_TBYTES);
+ offset = p->qtd.bufptr[0] & ~QTD_BUFPTR_MASK;
+ pci_dma_sglist_init(&p->sgl, &p->queue->ehci->dev, 5);
while (bytes > 0) {
if (cpage > 4) {
@@ -1230,7 +1311,7 @@ static int ehci_init_transfer(EHCIQueue *q)
return USB_RET_PROCERR;
}
- page = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK;
+ page = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK;
page += offset;
plen = bytes;
if (plen > 4096 - offset) {
@@ -1239,7 +1320,7 @@ static int ehci_init_transfer(EHCIQueue *q)
cpage++;
}
- qemu_sglist_add(&q->sgl, page, plen);
+ qemu_sglist_add(&p->sgl, page, plen);
bytes -= plen;
}
return 0;
@@ -1249,8 +1330,6 @@ static void ehci_finish_transfer(EHCIQueue *q, int status)
{
uint32_t cpage, offset;
- qemu_sglist_destroy(&q->sgl);
-
if (status > 0) {
/* update cpage & offset */
cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
@@ -1268,7 +1347,7 @@ static void ehci_finish_transfer(EHCIQueue *q, int status)
static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
{
- EHCIQueue *q;
+ EHCIPacket *p;
EHCIState *s = port->opaque;
uint32_t portsc = s->portsc[port->index];
@@ -1278,23 +1357,31 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
return;
}
- q = container_of(packet, EHCIQueue, packet);
- trace_usb_ehci_queue_action(q, "wakeup");
- assert(q->async == EHCI_ASYNC_INFLIGHT);
- q->async = EHCI_ASYNC_FINISHED;
- q->usb_status = packet->result;
+ p = container_of(packet, EHCIPacket, packet);
+ trace_usb_ehci_packet_action(p->queue, p, "wakeup");
+ assert(p->async == EHCI_ASYNC_INFLIGHT);
+ p->async = EHCI_ASYNC_FINISHED;
+ p->usb_status = packet->result;
+
+ if (p->queue->async) {
+ qemu_bh_schedule(p->queue->ehci->async_bh);
+ }
}
static void ehci_execute_complete(EHCIQueue *q)
{
- assert(q->async != EHCI_ASYNC_INFLIGHT);
- q->async = EHCI_ASYNC_NONE;
+ EHCIPacket *p = QTAILQ_FIRST(&q->packets);
+
+ assert(p != NULL);
+ assert(p->qtdaddr == q->qtdaddr);
+ assert(p->async != EHCI_ASYNC_INFLIGHT);
+ p->async = EHCI_ASYNC_NONE;
DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
- if (q->usb_status < 0) {
- switch(q->usb_status) {
+ if (p->usb_status < 0) {
+ switch (p->usb_status) {
case USB_RET_IOERROR:
case USB_RET_NODEV:
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
@@ -1314,28 +1401,29 @@ static void ehci_execute_complete(EHCIQueue *q)
break;
default:
/* should not be triggerable */
- fprintf(stderr, "USB invalid response %d to handle\n", q->usb_status);
+ fprintf(stderr, "USB invalid response %d\n", p->usb_status);
assert(0);
break;
}
- } else if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) {
- q->usb_status = USB_RET_BABBLE;
+ } else if ((p->usb_status > p->tbytes) && (p->pid == USB_TOKEN_IN)) {
+ p->usb_status = USB_RET_BABBLE;
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
ehci_record_interrupt(q->ehci, USBSTS_ERRINT);
} else {
// TODO check 4.12 for splits
- if (q->tbytes && q->pid == USB_TOKEN_IN) {
- q->tbytes -= q->usb_status;
+ if (p->tbytes && p->pid == USB_TOKEN_IN) {
+ p->tbytes -= p->usb_status;
} else {
- q->tbytes = 0;
+ p->tbytes = 0;
}
- DPRINTF("updating tbytes to %d\n", q->tbytes);
- set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES);
+ DPRINTF("updating tbytes to %d\n", p->tbytes);
+ set_field(&q->qh.token, p->tbytes, QTD_TOKEN_TBYTES);
}
- ehci_finish_transfer(q, q->usb_status);
- usb_packet_unmap(&q->packet);
+ ehci_finish_transfer(q, p->usb_status);
+ qemu_sglist_destroy(&p->sgl);
+ usb_packet_unmap(&p->packet);
q->qh.token ^= QTD_TOKEN_DTOGGLE;
q->qh.token &= ~QTD_TOKEN_ACTIVE;
@@ -1347,48 +1435,51 @@ static void ehci_execute_complete(EHCIQueue *q)
// 4.10.3
-static int ehci_execute(EHCIQueue *q)
+static int ehci_execute(EHCIPacket *p, const char *action)
{
- USBDevice *dev;
USBEndpoint *ep;
int ret;
int endp;
- int devadr;
- if ( !(q->qh.token & QTD_TOKEN_ACTIVE)) {
- fprintf(stderr, "Attempting to execute inactive QH\n");
+ if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) {
+ fprintf(stderr, "Attempting to execute inactive qtd\n");
return USB_RET_PROCERR;
}
- q->tbytes = (q->qh.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
- if (q->tbytes > BUFF_SIZE) {
+ p->tbytes = (p->qtd.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
+ if (p->tbytes > BUFF_SIZE) {
fprintf(stderr, "Request for more bytes than allowed\n");
return USB_RET_PROCERR;
}
- q->pid = (q->qh.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
- switch(q->pid) {
- case 0: q->pid = USB_TOKEN_OUT; break;
- case 1: q->pid = USB_TOKEN_IN; break;
- case 2: q->pid = USB_TOKEN_SETUP; break;
- default: fprintf(stderr, "bad token\n"); break;
+ p->pid = (p->qtd.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
+ switch (p->pid) {
+ case 0:
+ p->pid = USB_TOKEN_OUT;
+ break;
+ case 1:
+ p->pid = USB_TOKEN_IN;
+ break;
+ case 2:
+ p->pid = USB_TOKEN_SETUP;
+ break;
+ default:
+ fprintf(stderr, "bad token\n");
+ break;
}
- if (ehci_init_transfer(q) != 0) {
+ if (ehci_init_transfer(p) != 0) {
return USB_RET_PROCERR;
}
- endp = get_field(q->qh.epchar, QH_EPCHAR_EP);
- devadr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
+ endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
+ ep = usb_ep_get(p->queue->dev, p->pid, endp);
- /* TODO: associating device with ehci port */
- dev = ehci_find_device(q->ehci, devadr);
- ep = usb_ep_get(dev, q->pid, endp);
+ usb_packet_setup(&p->packet, p->pid, ep);
+ usb_packet_map(&p->packet, &p->sgl);
- usb_packet_setup(&q->packet, q->pid, ep);
- usb_packet_map(&q->packet, &q->sgl);
-
- ret = usb_handle_packet(dev, &q->packet);
+ 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",
q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
@@ -1504,6 +1595,24 @@ static int ehci_process_itd(EHCIState *ehci,
return 0;
}
+
+/*
+ * Write the qh back to guest physical memory. This step isn't
+ * in the EHCI spec but we need to do it since we don't share
+ * physical memory with our guest VM.
+ *
+ * The first three dwords are read-only for the EHCI, so skip them
+ * when writing back the qh.
+ */
+static void ehci_flush_qh(EHCIQueue *q)
+{
+ uint32_t *qh = (uint32_t *) &q->qh;
+ uint32_t dwords = sizeof(EHCIqh) >> 2;
+ uint32_t addr = NLPTR_GET(q->qhaddr);
+
+ put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3);
+}
+
/* This state is the entry point for asynchronous schedule
* processing. Entry here consitutes a EHCI start event state (4.8.5)
*/
@@ -1601,17 +1710,18 @@ out:
static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
{
- uint32_t entry;
+ EHCIPacket *p;
+ uint32_t entry, devaddr;
EHCIQueue *q;
entry = ehci_get_fetch_addr(ehci, async);
q = ehci_find_queue_by_qh(ehci, entry, async);
if (NULL == q) {
- q = ehci_alloc_queue(ehci, async);
+ q = ehci_alloc_queue(ehci, entry, async);
}
- q->qhaddr = entry;
- q->seen++;
+ p = QTAILQ_FIRST(&q->packets);
+ q->seen++;
if (q->seen > 1) {
/* we are going in circles -- stop processing */
ehci_set_state(ehci, async, EST_ACTIVE);
@@ -1623,14 +1733,28 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
(uint32_t *) &q->qh, sizeof(EHCIqh) >> 2);
ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh);
- if (q->async == EHCI_ASYNC_INFLIGHT) {
+ devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
+ if (q->dev != NULL && q->dev->addr != devaddr) {
+ if (!QTAILQ_EMPTY(&q->packets)) {
+ /* should not happen (guest bug) */
+ while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
+ ehci_free_packet(p);
+ }
+ }
+ q->dev = NULL;
+ }
+ if (q->dev == NULL) {
+ q->dev = ehci_find_device(q->ehci, devaddr);
+ }
+
+ if (p && p->async == EHCI_ASYNC_INFLIGHT) {
/* I/O still in progress -- skip queue */
ehci_set_state(ehci, async, EST_HORIZONTALQH);
goto out;
}
- if (q->async == EHCI_ASYNC_FINISHED) {
+ if (p && p->async == EHCI_ASYNC_FINISHED) {
/* I/O finished -- continue processing queue */
- trace_usb_ehci_queue_action(q, "resume");
+ trace_usb_ehci_packet_action(p->queue, p, "complete");
ehci_set_state(ehci, async, EST_EXECUTING);
goto out;
}
@@ -1726,7 +1850,7 @@ static int ehci_state_fetchsitd(EHCIState *ehci, int async)
}
/* Section 4.10.2 - paragraph 3 */
-static int ehci_state_advqueue(EHCIQueue *q, int async)
+static int ehci_state_advqueue(EHCIQueue *q)
{
#if 0
/* TO-DO: 4.10.2 - paragraph 2
@@ -1745,81 +1869,117 @@ static int ehci_state_advqueue(EHCIQueue *q, int async)
if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
(NLPTR_TBIT(q->qh.altnext_qtd) == 0)) {
q->qtdaddr = q->qh.altnext_qtd;
- ehci_set_state(q->ehci, async, EST_FETCHQTD);
+ ehci_set_state(q->ehci, q->async, EST_FETCHQTD);
/*
* next qTD is valid
*/
} else if (NLPTR_TBIT(q->qh.next_qtd) == 0) {
q->qtdaddr = q->qh.next_qtd;
- ehci_set_state(q->ehci, async, EST_FETCHQTD);
+ ehci_set_state(q->ehci, q->async, EST_FETCHQTD);
/*
* no valid qTD, try next QH
*/
} else {
- ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
}
return 1;
}
/* Section 4.10.2 - paragraph 4 */
-static int ehci_state_fetchqtd(EHCIQueue *q, int async)
+static int ehci_state_fetchqtd(EHCIQueue *q)
{
+ EHCIqtd qtd;
+ EHCIPacket *p;
int again = 0;
- get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qtd,
+ get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd,
sizeof(EHCIqtd) >> 2);
- ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &q->qtd);
+ ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);
- if (q->qtd.token & QTD_TOKEN_ACTIVE) {
- ehci_set_state(q->ehci, async, EST_EXECUTE);
+ p = QTAILQ_FIRST(&q->packets);
+ while (p != NULL && p->qtdaddr != q->qtdaddr) {
+ /* should not happen (guest bug) */
+ ehci_free_packet(p);
+ p = QTAILQ_FIRST(&q->packets);
+ }
+ if (p != NULL) {
+ ehci_qh_do_overlay(q);
+ ehci_flush_qh(q);
+ if (p->async == EHCI_ASYNC_INFLIGHT) {
+ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
+ } else {
+ ehci_set_state(q->ehci, q->async, EST_EXECUTING);
+ }
+ again = 1;
+ } else if (qtd.token & QTD_TOKEN_ACTIVE) {
+ p = ehci_alloc_packet(q);
+ p->qtdaddr = q->qtdaddr;
+ p->qtd = qtd;
+ ehci_set_state(q->ehci, q->async, EST_EXECUTE);
again = 1;
} else {
- ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
again = 1;
}
return again;
}
-static int ehci_state_horizqh(EHCIQueue *q, int async)
+static int ehci_state_horizqh(EHCIQueue *q)
{
int again = 0;
- if (ehci_get_fetch_addr(q->ehci, async) != q->qh.next) {
- ehci_set_fetch_addr(q->ehci, async, q->qh.next);
- ehci_set_state(q->ehci, async, EST_FETCHENTRY);
+ if (ehci_get_fetch_addr(q->ehci, q->async) != q->qh.next) {
+ ehci_set_fetch_addr(q->ehci, q->async, q->qh.next);
+ ehci_set_state(q->ehci, q->async, EST_FETCHENTRY);
again = 1;
} else {
- ehci_set_state(q->ehci, async, EST_ACTIVE);
+ ehci_set_state(q->ehci, q->async, EST_ACTIVE);
}
return again;
}
-/*
- * Write the qh back to guest physical memory. This step isn't
- * in the EHCI spec but we need to do it since we don't share
- * physical memory with our guest VM.
- *
- * The first three dwords are read-only for the EHCI, so skip them
- * when writing back the qh.
- */
-static void ehci_flush_qh(EHCIQueue *q)
+static void ehci_fill_queue(EHCIPacket *p)
{
- uint32_t *qh = (uint32_t *) &q->qh;
- uint32_t dwords = sizeof(EHCIqh) >> 2;
- uint32_t addr = NLPTR_GET(q->qhaddr);
+ EHCIQueue *q = p->queue;
+ EHCIqtd qtd = p->qtd;
+ uint32_t qtdaddr;
- put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3);
+ for (;;) {
+ if (NLPTR_TBIT(qtd.altnext) == 0) {
+ break;
+ }
+ if (NLPTR_TBIT(qtd.next) != 0) {
+ break;
+ }
+ qtdaddr = qtd.next;
+ get_dwords(q->ehci, NLPTR_GET(qtdaddr),
+ (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
+ ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd);
+ if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
+ break;
+ }
+ p = ehci_alloc_packet(q);
+ p->qtdaddr = qtdaddr;
+ p->qtd = qtd;
+ p->usb_status = ehci_execute(p, "queue");
+ assert(p->usb_status = USB_RET_ASYNC);
+ p->async = EHCI_ASYNC_INFLIGHT;
+ }
}
-static int ehci_state_execute(EHCIQueue *q, int async)
+static int ehci_state_execute(EHCIQueue *q)
{
+ EHCIPacket *p = QTAILQ_FIRST(&q->packets);
int again = 0;
+ assert(p != NULL);
+ assert(p->qtdaddr == q->qtdaddr);
+
if (ehci_qh_do_overlay(q) != 0) {
return -1;
}
@@ -1828,55 +1988,60 @@ static int ehci_state_execute(EHCIQueue *q, int async)
// TODO write back ptr to async list when done or out of time
// TODO Windows does not seem to ever set the MULT field
- if (!async) {
+ if (!q->async) {
int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
if (!transactCtr) {
- ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
again = 1;
goto out;
}
}
- if (async) {
+ if (q->async) {
ehci_set_usbsts(q->ehci, USBSTS_REC);
}
- q->usb_status = ehci_execute(q);
- if (q->usb_status == USB_RET_PROCERR) {
+ p->usb_status = ehci_execute(p, "process");
+ if (p->usb_status == USB_RET_PROCERR) {
again = -1;
goto out;
}
- if (q->usb_status == USB_RET_ASYNC) {
+ if (p->usb_status == USB_RET_ASYNC) {
ehci_flush_qh(q);
- trace_usb_ehci_queue_action(q, "suspend");
- q->async = EHCI_ASYNC_INFLIGHT;
- ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+ trace_usb_ehci_packet_action(p->queue, p, "async");
+ p->async = EHCI_ASYNC_INFLIGHT;
+ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
again = 1;
+ ehci_fill_queue(p);
goto out;
}
- ehci_set_state(q->ehci, async, EST_EXECUTING);
+ ehci_set_state(q->ehci, q->async, EST_EXECUTING);
again = 1;
out:
return again;
}
-static int ehci_state_executing(EHCIQueue *q, int async)
+static int ehci_state_executing(EHCIQueue *q)
{
+ EHCIPacket *p = QTAILQ_FIRST(&q->packets);
int again = 0;
+ assert(p != NULL);
+ assert(p->qtdaddr == q->qtdaddr);
+
ehci_execute_complete(q);
- if (q->usb_status == USB_RET_ASYNC) {
+ if (p->usb_status == USB_RET_ASYNC) {
goto out;
}
- if (q->usb_status == USB_RET_PROCERR) {
+ if (p->usb_status == USB_RET_PROCERR) {
again = -1;
goto out;
}
// 4.10.3
- if (!async) {
+ if (!q->async) {
int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
transactCtr--;
set_field(&q->qh.epcap, transactCtr, QH_EPCAP_MULT);
@@ -1885,10 +2050,10 @@ static int ehci_state_executing(EHCIQueue *q, int async)
}
/* 4.10.5 */
- if (q->usb_status == USB_RET_NAK) {
- ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+ if (p->usb_status == USB_RET_NAK) {
+ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
} else {
- ehci_set_state(q->ehci, async, EST_WRITEBACK);
+ ehci_set_state(q->ehci, q->async, EST_WRITEBACK);
}
again = 1;
@@ -1899,14 +2064,19 @@ out:
}
-static int ehci_state_writeback(EHCIQueue *q, int async)
+static int ehci_state_writeback(EHCIQueue *q)
{
+ EHCIPacket *p = QTAILQ_FIRST(&q->packets);
int again = 0;
/* Write back the QTD from the QH area */
- ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), (EHCIqtd*) &q->qh.next_qtd);
- put_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qh.next_qtd,
+ assert(p != NULL);
+ assert(p->qtdaddr == q->qtdaddr);
+
+ ehci_trace_qtd(q, NLPTR_GET(p->qtdaddr), (EHCIqtd *) &q->qh.next_qtd);
+ put_dwords(q->ehci, NLPTR_GET(p->qtdaddr), (uint32_t *) &q->qh.next_qtd,
sizeof(EHCIqtd) >> 2);
+ ehci_free_packet(p);
/*
* EHCI specs say go horizontal here.
@@ -1917,10 +2087,10 @@ static int ehci_state_writeback(EHCIQueue *q, int async)
* bit is clear.
*/
if (q->qh.token & QTD_TOKEN_HALT) {
- ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
+ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
again = 1;
} else {
- ehci_set_state(q->ehci, async, EST_ADVANCEQUEUE);
+ ehci_set_state(q->ehci, q->async, EST_ADVANCEQUEUE);
again = 1;
}
return again;
@@ -1930,8 +2100,7 @@ static int ehci_state_writeback(EHCIQueue *q, int async)
* This is the state machine that is common to both async and periodic
*/
-static void ehci_advance_state(EHCIState *ehci,
- int async)
+static void ehci_advance_state(EHCIState *ehci, int async)
{
EHCIQueue *q = NULL;
int again;
@@ -1948,7 +2117,12 @@ static void ehci_advance_state(EHCIState *ehci,
case EST_FETCHQH:
q = ehci_state_fetchqh(ehci, async);
- again = q ? 1 : 0;
+ if (q != NULL) {
+ assert(q->async == async);
+ again = 1;
+ } else {
+ again = 0;
+ }
break;
case EST_FETCHITD:
@@ -1960,29 +2134,35 @@ static void ehci_advance_state(EHCIState *ehci,
break;
case EST_ADVANCEQUEUE:
- again = ehci_state_advqueue(q, async);
+ again = ehci_state_advqueue(q);
break;
case EST_FETCHQTD:
- again = ehci_state_fetchqtd(q, async);
+ again = ehci_state_fetchqtd(q);
break;
case EST_HORIZONTALQH:
- again = ehci_state_horizqh(q, async);
+ again = ehci_state_horizqh(q);
break;
case EST_EXECUTE:
- again = ehci_state_execute(q, async);
+ again = ehci_state_execute(q);
+ if (async) {
+ ehci->async_stepdown = 0;
+ }
break;
case EST_EXECUTING:
assert(q != NULL);
- again = ehci_state_executing(q, async);
+ if (async) {
+ ehci->async_stepdown = 0;
+ }
+ again = ehci_state_executing(q);
break;
case EST_WRITEBACK:
assert(q != NULL);
- again = ehci_state_writeback(q, async);
+ again = ehci_state_writeback(q);
break;
default:
@@ -2009,17 +2189,15 @@ static void ehci_advance_async_state(EHCIState *ehci)
switch(ehci_get_state(ehci, async)) {
case EST_INACTIVE:
- if (!(ehci->usbcmd & USBCMD_ASE)) {
+ if (!ehci_async_enabled(ehci)) {
break;
}
- ehci_set_usbsts(ehci, USBSTS_ASS);
ehci_set_state(ehci, async, EST_ACTIVE);
// No break, fall through to ACTIVE
case EST_ACTIVE:
- if ( !(ehci->usbcmd & USBCMD_ASE)) {
+ if (!ehci_async_enabled(ehci)) {
ehci_queues_rip_all(ehci, async);
- ehci_clear_usbsts(ehci, USBSTS_ASS);
ehci_set_state(ehci, async, EST_INACTIVE);
break;
}
@@ -2070,17 +2248,15 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
switch(ehci_get_state(ehci, async)) {
case EST_INACTIVE:
- if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) {
- ehci_set_usbsts(ehci, USBSTS_PSS);
+ if (!(ehci->frindex & 7) && ehci_periodic_enabled(ehci)) {
ehci_set_state(ehci, async, EST_ACTIVE);
// No break, fall through to ACTIVE
} else
break;
case EST_ACTIVE:
- if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) {
+ if (!(ehci->frindex & 7) && !ehci_periodic_enabled(ehci)) {
ehci_queues_rip_all(ehci, async);
- ehci_clear_usbsts(ehci, USBSTS_PSS);
ehci_set_state(ehci, async, EST_INACTIVE);
break;
}
@@ -2111,58 +2287,86 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
}
}
+static void ehci_update_frindex(EHCIState *ehci, int frames)
+{
+ int i;
+
+ if (!ehci_enabled(ehci)) {
+ return;
+ }
+
+ for (i = 0; i < frames; i++) {
+ ehci->frindex += 8;
+
+ if (ehci->frindex == 0x00002000) {
+ ehci_set_interrupt(ehci, USBSTS_FLR);
+ }
+
+ if (ehci->frindex == 0x00004000) {
+ ehci_set_interrupt(ehci, USBSTS_FLR);
+ ehci->frindex = 0;
+ }
+ }
+}
+
static void ehci_frame_timer(void *opaque)
{
EHCIState *ehci = opaque;
+ int schedules = 0;
int64_t expire_time, t_now;
uint64_t ns_elapsed;
- int frames;
+ int frames, skipped_frames;
int i;
- int skipped_frames = 0;
t_now = qemu_get_clock_ns(vm_clock);
- expire_time = t_now + (get_ticks_per_sec() / ehci->freq);
-
ns_elapsed = t_now - ehci->last_run_ns;
frames = ns_elapsed / FRAME_TIMER_NS;
- for (i = 0; i < frames; i++) {
- if ( !(ehci->usbsts & USBSTS_HALT)) {
- ehci->frindex += 8;
-
- if (ehci->frindex == 0x00002000) {
- ehci_set_interrupt(ehci, USBSTS_FLR);
- }
+ if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) {
+ schedules++;
+ expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ);
- if (ehci->frindex == 0x00004000) {
- ehci_set_interrupt(ehci, USBSTS_FLR);
- ehci->frindex = 0;
- }
+ if (frames > ehci->maxframes) {
+ skipped_frames = frames - ehci->maxframes;
+ ehci_update_frindex(ehci, skipped_frames);
+ ehci->last_run_ns += FRAME_TIMER_NS * skipped_frames;
+ frames -= skipped_frames;
+ DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames);
}
- if (frames - i > ehci->maxframes) {
- skipped_frames++;
- } else {
+ for (i = 0; i < frames; i++) {
+ ehci_update_frindex(ehci, 1);
ehci_advance_periodic_state(ehci);
+ ehci->last_run_ns += FRAME_TIMER_NS;
}
-
- ehci->last_run_ns += FRAME_TIMER_NS;
- }
-
-#if 0
- if (skipped_frames) {
- DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames);
+ } else {
+ if (ehci->async_stepdown < ehci->maxframes / 2) {
+ ehci->async_stepdown++;
+ }
+ expire_time = t_now + (get_ticks_per_sec()
+ * ehci->async_stepdown / FRAME_TIMER_FREQ);
+ ehci_update_frindex(ehci, frames);
+ ehci->last_run_ns += FRAME_TIMER_NS * frames;
}
-#endif
/* Async is not inside loop since it executes everything it can once
* called
*/
- ehci_advance_async_state(ehci);
+ if (ehci_async_enabled(ehci) || ehci->astate != EST_INACTIVE) {
+ schedules++;
+ qemu_bh_schedule(ehci->async_bh);
+ }
- qemu_mod_timer(ehci->frame_timer, expire_time);
+ if (schedules) {
+ 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_mem_ops = {
.old_mmio = {
@@ -2192,7 +2396,6 @@ static const VMStateDescription vmstate_ehci = {
};
static Property ehci_properties[] = {
- DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ),
DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2298,6 +2501,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);
QTAILQ_INIT(&s->aqueues);
QTAILQ_INIT(&s->pqueues);
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 9e211a0bb4..9871e24f50 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -131,10 +131,14 @@ struct UHCIState {
uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
int64_t expire_time;
QEMUTimer *frame_timer;
+ QEMUBH *bh;
+ uint32_t frame_bytes;
+ uint32_t frame_bandwidth;
UHCIPort ports[NB_PORTS];
/* Interrupts that should be raised at the end of the current frame. */
uint32_t pending_int_mask;
+ int irq_pin;
/* Active packets */
QTAILQ_HEAD(, UHCIQueue) queues;
@@ -337,7 +341,7 @@ static void uhci_update_irq(UHCIState *s)
} else {
level = 0;
}
- qemu_set_irq(s->dev.irq[3], level);
+ qemu_set_irq(s->dev.irq[s->irq_pin], level);
}
static void uhci_reset(void *opaque)
@@ -369,16 +373,10 @@ static void uhci_reset(void *opaque)
}
uhci_async_cancel_all(s);
+ qemu_bh_cancel(s->bh);
uhci_update_irq(s);
}
-static void uhci_pre_save(void *opaque)
-{
- UHCIState *s = opaque;
-
- uhci_async_cancel_all(s);
-}
-
static const VMStateDescription vmstate_uhci_port = {
.name = "uhci port",
.version_id = 1,
@@ -395,7 +393,6 @@ static const VMStateDescription vmstate_uhci = {
.version_id = 2,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
- .pre_save = uhci_pre_save,
.fields = (VMStateField []) {
VMSTATE_PCI_DEVICE(dev, UHCIState),
VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState),
@@ -905,7 +902,9 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet)
uhci_async_free(async);
} else {
async->done = 1;
- uhci_process_frame(s);
+ if (s->frame_bytes < s->frame_bandwidth) {
+ qemu_bh_schedule(s->bh);
+ }
}
}
@@ -985,7 +984,7 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
static void uhci_process_frame(UHCIState *s)
{
uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
- uint32_t curr_qh, td_count = 0, bytes_count = 0;
+ uint32_t curr_qh, td_count = 0;
int cnt, ret;
UHCI_TD td;
UHCI_QH qh;
@@ -1002,6 +1001,12 @@ static void uhci_process_frame(UHCIState *s)
qhdb_reset(&qhdb);
for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) {
+ if (s->frame_bytes >= s->frame_bandwidth) {
+ /* We've reached the usb 1.1 bandwidth, which is
+ 1280 bytes/frame, stop processing */
+ trace_usb_uhci_frame_stop_bandwidth();
+ break;
+ }
if (is_qh(link)) {
/* QH */
trace_usb_uhci_qh_load(link & ~0xf);
@@ -1011,18 +1016,12 @@ static void uhci_process_frame(UHCIState *s)
* We're going in circles. Which is not a bug because
* HCD is allowed to do that as part of the BW management.
*
- * Stop processing here if
- * (a) no transaction has been done since we've been
- * here last time, or
- * (b) we've reached the usb 1.1 bandwidth, which is
- * 1280 bytes/frame.
+ * Stop processing here if no transaction has been done
+ * since we've been here last time.
*/
if (td_count == 0) {
trace_usb_uhci_frame_loop_stop_idle();
break;
- } else if (bytes_count >= 1280) {
- trace_usb_uhci_frame_loop_stop_bandwidth();
- break;
} else {
trace_usb_uhci_frame_loop_continue();
td_count = 0;
@@ -1085,7 +1084,7 @@ static void uhci_process_frame(UHCIState *s)
trace_usb_uhci_td_complete(curr_qh & ~0xf, link & ~0xf);
link = td.link;
td_count++;
- bytes_count += (td.ctrl & 0x7ff) + 1;
+ s->frame_bytes += (td.ctrl & 0x7ff) + 1;
if (curr_qh) {
/* update QH element link */
@@ -1112,12 +1111,20 @@ out:
s->pending_int_mask |= int_mask;
}
+static void uhci_bh(void *opaque)
+{
+ UHCIState *s = opaque;
+ uhci_process_frame(s);
+}
+
static void uhci_frame_timer(void *opaque)
{
UHCIState *s = opaque;
/* prepare the timer for the next frame */
s->expire_time += (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+ s->frame_bytes = 0;
+ qemu_bh_cancel(s->bh);
if (!(s->cmd & UHCI_CMD_RS)) {
/* Full stop */
@@ -1178,15 +1185,31 @@ static USBBusOps uhci_bus_ops = {
static int usb_uhci_common_initfn(PCIDevice *dev)
{
+ PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
uint8_t *pci_conf = s->dev.config;
int i;
pci_conf[PCI_CLASS_PROG] = 0x00;
/* TODO: reset value should be 0. */
- pci_conf[PCI_INTERRUPT_PIN] = 4; /* interrupt pin D */
pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
+ switch (pc->device_id) {
+ case PCI_DEVICE_ID_INTEL_82801I_UHCI1:
+ s->irq_pin = 0; /* A */
+ break;
+ case PCI_DEVICE_ID_INTEL_82801I_UHCI2:
+ s->irq_pin = 1; /* B */
+ break;
+ case PCI_DEVICE_ID_INTEL_82801I_UHCI3:
+ s->irq_pin = 2; /* C */
+ break;
+ default:
+ s->irq_pin = 3; /* D */
+ break;
+ }
+ pci_config_set_interrupt_pin(pci_conf, s->irq_pin + 1);
+
if (s->masterbus) {
USBPort *ports[NB_PORTS];
for(i = 0; i < NB_PORTS; i++) {
@@ -1204,6 +1227,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
}
}
+ s->bh = qemu_bh_new(uhci_bh, s);
s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
s->num_ports_vmstate = NB_PORTS;
QTAILQ_INIT(&s->queues);
@@ -1244,6 +1268,7 @@ static int usb_uhci_exit(PCIDevice *dev)
static Property uhci_properties[] = {
DEFINE_PROP_STRING("masterbus", UHCIState, masterbus),
DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0),
+ DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 5cf1a64699..6c2ff024e0 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -23,6 +23,7 @@
#include "hw/usb.h"
#include "hw/pci.h"
#include "hw/msi.h"
+#include "trace.h"
//#define DEBUG_XHCI
//#define DEBUG_DATA
@@ -421,7 +422,6 @@ typedef struct XHCIEvRingSeg {
uint32_t rsvd;
} XHCIEvRingSeg;
-#ifdef DEBUG_XHCI
static const char *TRBType_names[] = {
[TRB_RESERVED] = "TRB_RESERVED",
[TR_NORMAL] = "TR_NORMAL",
@@ -473,7 +473,6 @@ static const char *trb_name(XHCITRB *trb)
return lookup_name(TRB_TYPE(*trb), TRBType_names,
ARRAY_SIZE(TRBType_names));
}
-#endif
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
unsigned int epid);
@@ -505,14 +504,13 @@ static void xhci_irq_update(XHCIState *xhci)
level = 1;
}
- DPRINTF("xhci_irq_update(): %d\n", level);
-
if (xhci->msi && msi_enabled(&xhci->pci_dev)) {
if (level) {
- DPRINTF("xhci_irq_update(): MSI signal\n");
+ trace_usb_xhci_irq_msi(0);
msi_notify(&xhci->pci_dev, 0);
}
} else {
+ trace_usb_xhci_irq_intx(level);
qemu_set_irq(xhci->irq, level);
}
}
@@ -542,9 +540,8 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event)
}
ev_trb.control = cpu_to_le32(ev_trb.control);
- DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x %s\n",
- xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control,
- trb_name(&ev_trb));
+ trace_usb_xhci_queue_event(xhci->er_ep_idx, trb_name(&ev_trb),
+ ev_trb.parameter, ev_trb.status, ev_trb.control);
addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx;
pci_dma_write(&xhci->pci_dev, addr, &ev_trb, TRB_SIZE);
@@ -704,10 +701,8 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
le32_to_cpus(&trb->status);
le32_to_cpus(&trb->control);
- DPRINTF("xhci: TRB fetched [" DMA_ADDR_FMT "]: "
- "%016" PRIx64 " %08x %08x %s\n",
- ring->dequeue, trb->parameter, trb->status, trb->control,
- trb_name(trb));
+ trace_usb_xhci_fetch_trb(ring->dequeue, trb_name(trb),
+ trb->parameter, trb->status, trb->control);
if ((trb->control & TRB_C) != ring->ccs) {
return 0;
@@ -746,10 +741,6 @@ static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring)
le32_to_cpus(&trb.status);
le32_to_cpus(&trb.control);
- DPRINTF("xhci: TRB peeked [" DMA_ADDR_FMT "]: "
- "%016" PRIx64 " %08x %08x\n",
- dequeue, trb.parameter, trb.status, trb.control);
-
if ((trb.control & TRB_C) != ccs) {
return -length;
}
@@ -812,14 +803,13 @@ static void xhci_er_reset(XHCIState *xhci)
static void xhci_run(XHCIState *xhci)
{
- DPRINTF("xhci_run()\n");
-
+ trace_usb_xhci_run();
xhci->usbsts &= ~USBSTS_HCH;
}
static void xhci_stop(XHCIState *xhci)
{
- DPRINTF("xhci_stop()\n");
+ trace_usb_xhci_stop();
xhci->usbsts |= USBSTS_HCH;
xhci->crcr_low &= ~CRCR_CRR;
}
@@ -852,11 +842,10 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
dma_addr_t dequeue;
int i;
+ trace_usb_xhci_ep_enable(slotid, epid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
assert(epid >= 1 && epid <= 31);
- DPRINTF("xhci_enable_ep(%d, %d)\n", slotid, epid);
-
slot = &xhci->slots[slotid-1];
if (slot->eps[epid-1]) {
fprintf(stderr, "xhci: slot %d ep %d already enabled!\n", slotid, epid);
@@ -971,11 +960,10 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
XHCISlot *slot;
XHCIEPContext *epctx;
+ trace_usb_xhci_ep_disable(slotid, epid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
assert(epid >= 1 && epid <= 31);
- DPRINTF("xhci_disable_ep(%d, %d)\n", slotid, epid);
-
slot = &xhci->slots[slotid-1];
if (!slot->eps[epid-1]) {
@@ -1001,8 +989,7 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid,
XHCISlot *slot;
XHCIEPContext *epctx;
- DPRINTF("xhci_stop_ep(%d, %d)\n", slotid, epid);
-
+ trace_usb_xhci_ep_stop(slotid, epid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
if (epid < 1 || epid > 31) {
@@ -1036,10 +1023,9 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
XHCIEPContext *epctx;
USBDevice *dev;
+ trace_usb_xhci_ep_reset(slotid, epid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
- DPRINTF("xhci_reset_ep(%d, %d)\n", slotid, epid);
-
if (epid < 1 || epid > 31) {
fprintf(stderr, "xhci: bad ep %d\n", epid);
return CC_TRB_ERROR;
@@ -1416,12 +1402,14 @@ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
static int xhci_complete_packet(XHCITransfer *xfer, int ret)
{
if (ret == USB_RET_ASYNC) {
+ trace_usb_xhci_xfer_async(xfer);
xfer->running_async = 1;
xfer->running_retry = 0;
xfer->complete = 0;
xfer->cancelled = 0;
return 0;
} else if (ret == USB_RET_NAK) {
+ trace_usb_xhci_xfer_nak(xfer);
xfer->running_async = 0;
xfer->running_retry = 1;
xfer->complete = 0;
@@ -1436,10 +1424,12 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
if (ret >= 0) {
xfer->status = CC_SUCCESS;
xhci_xfer_data(xfer, xfer->data, ret, xfer->in_xfer, 0, 1);
+ trace_usb_xhci_xfer_success(xfer, ret);
return 0;
}
/* error */
+ trace_usb_xhci_xfer_error(xfer, ret);
switch (ret) {
case USB_RET_NODEV:
xfer->status = CC_USB_TRANSACTION_ERROR;
@@ -1475,11 +1465,12 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
USBDevice *dev;
int ret;
- DPRINTF("xhci_fire_ctl_transfer(slot=%d)\n", xfer->slotid);
-
trb_setup = &xfer->trbs[0];
trb_status = &xfer->trbs[xfer->trb_count-1];
+ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid,
+ trb_setup->parameter >> 48);
+
/* at most one Event Data TRB allowed after STATUS */
if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
trb_status--;
@@ -1620,15 +1611,14 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext
unsigned int length = 0;
XHCITRB *trb;
- DPRINTF("xhci_fire_transfer(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
-
for (i = 0; i < xfer->trb_count; i++) {
trb = &xfer->trbs[i];
if (TRB_TYPE(*trb) == TR_NORMAL || TRB_TYPE(*trb) == TR_ISOCH) {
length += trb->status & 0x1ffff;
}
}
- DPRINTF("xhci: total TD length=%d\n", length);
+
+ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length);
if (!epctx->has_bg) {
xfer->data_length = length;
@@ -1664,9 +1654,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
int length;
int i;
+ trace_usb_xhci_ep_kick(slotid, epid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
assert(epid >= 1 && epid <= 31);
- DPRINTF("xhci_kick_ep(%d, %d)\n", slotid, epid);
if (!xhci->slots[slotid-1].enabled) {
fprintf(stderr, "xhci: xhci_kick_ep for disabled slot %d\n", slotid);
@@ -1684,15 +1674,13 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
XHCITransfer *xfer = epctx->retry;
int result;
- DPRINTF("xhci: retry nack'ed transfer ...\n");
+ trace_usb_xhci_xfer_retry(xfer);
assert(xfer->running_retry);
xhci_setup_packet(xfer, xfer->packet.ep->dev);
result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
if (result == USB_RET_NAK) {
- DPRINTF("xhci: ... xfer still nacked\n");
return;
}
- DPRINTF("xhci: ... result %d\n", result);
xhci_complete_packet(xfer, result);
assert(!xfer->running_retry);
epctx->retry = NULL;
@@ -1708,21 +1696,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
while (1) {
XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
if (xfer->running_async || xfer->running_retry || xfer->backgrounded) {
- DPRINTF("xhci: ep is busy (#%d,%d,%d,%d)\n",
- epctx->next_xfer, xfer->running_async,
- xfer->running_retry, xfer->backgrounded);
break;
- } else {
- DPRINTF("xhci: ep: using #%d\n", epctx->next_xfer);
}
length = xhci_ring_chain_length(xhci, &epctx->ring);
if (length < 0) {
- DPRINTF("xhci: incomplete TD (%d TRBs)\n", -length);
break;
} else if (length == 0) {
break;
}
- DPRINTF("xhci: fetching %d-TRB TD\n", length);
if (xfer->trbs && xfer->trb_alloced < length) {
xfer->trb_count = 0;
xfer->trb_alloced = 0;
@@ -1757,7 +1738,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
}
if (epctx->state == EP_HALTED) {
- DPRINTF("xhci: ep halted, stopping schedule\n");
break;
}
if (xfer->running_retry) {
@@ -1770,8 +1750,8 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid)
{
+ trace_usb_xhci_slot_enable(slotid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
- DPRINTF("xhci_enable_slot(%d)\n", slotid);
xhci->slots[slotid-1].enabled = 1;
xhci->slots[slotid-1].port = 0;
memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31);
@@ -1783,8 +1763,8 @@ static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid)
{
int i;
+ trace_usb_xhci_slot_disable(slotid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
- DPRINTF("xhci_disable_slot(%d)\n", slotid);
for (i = 1; i <= 31; i++) {
if (xhci->slots[slotid-1].eps[i-1]) {
@@ -1810,8 +1790,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
int i;
TRBCCode res;
+ trace_usb_xhci_slot_address(slotid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
- DPRINTF("xhci_address_slot(%d)\n", slotid);
dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
pci_dma_read(&xhci->pci_dev, dcbaap + 8*slotid, &poctx, sizeof(poctx));
@@ -1897,8 +1877,8 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
int i;
TRBCCode res;
+ trace_usb_xhci_slot_configure(slotid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
- DPRINTF("xhci_configure_slot(%d)\n", slotid);
ictx = xhci_mask64(pictx);
octx = xhci->slots[slotid-1].ctx;
@@ -1985,8 +1965,8 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
uint32_t islot_ctx[4];
uint32_t slot_ctx[4];
+ trace_usb_xhci_slot_evaluate(slotid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
- DPRINTF("xhci_evaluate_slot(%d)\n", slotid);
ictx = xhci_mask64(pictx);
octx = xhci->slots[slotid-1].ctx;
@@ -2048,8 +2028,8 @@ static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid)
dma_addr_t octx;
int i;
+ trace_usb_xhci_slot_reset(slotid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
- DPRINTF("xhci_reset_slot(%d)\n", slotid);
octx = xhci->slots[slotid-1].ctx;
@@ -2296,12 +2276,12 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
}
}
-static void xhci_reset(void *opaque)
+static void xhci_reset(DeviceState *dev)
{
- XHCIState *xhci = opaque;
+ XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev.qdev, dev);
int i;
- DPRINTF("xhci: full reset\n");
+ trace_usb_xhci_reset();
if (!(xhci->usbsts & USBSTS_HCH)) {
fprintf(stderr, "xhci: reset while running!\n");
}
@@ -2342,77 +2322,98 @@ static void xhci_reset(void *opaque)
static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
{
- DPRINTF("xhci_cap_read(0x%x)\n", reg);
+ uint32_t ret;
switch (reg) {
case 0x00: /* HCIVERSION, CAPLENGTH */
- return 0x01000000 | LEN_CAP;
+ ret = 0x01000000 | LEN_CAP;
+ break;
case 0x04: /* HCSPARAMS 1 */
- return (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS;
+ ret = (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS;
+ break;
case 0x08: /* HCSPARAMS 2 */
- return 0x0000000f;
+ ret = 0x0000000f;
+ break;
case 0x0c: /* HCSPARAMS 3 */
- return 0x00000000;
+ ret = 0x00000000;
+ break;
case 0x10: /* HCCPARAMS */
-#if TARGET_PHYS_ADDR_BITS > 32
- return 0x00081001;
-#else
- return 0x00081000;
-#endif
+ if (sizeof(dma_addr_t) == 4) {
+ ret = 0x00081000;
+ } else {
+ ret = 0x00081001;
+ }
+ break;
case 0x14: /* DBOFF */
- return OFF_DOORBELL;
+ ret = OFF_DOORBELL;
+ break;
case 0x18: /* RTSOFF */
- return OFF_RUNTIME;
+ ret = OFF_RUNTIME;
+ break;
/* extended capabilities */
case 0x20: /* Supported Protocol:00 */
-#if USB3_PORTS > 0
- return 0x02000402; /* USB 2.0 */
-#else
- return 0x02000002; /* USB 2.0 */
-#endif
+ ret = 0x02000402; /* USB 2.0 */
+ break;
case 0x24: /* Supported Protocol:04 */
- return 0x20425455; /* "USB " */
+ ret = 0x20425455; /* "USB " */
+ break;
case 0x28: /* Supported Protocol:08 */
- return 0x00000001 | (USB2_PORTS<<8);
+ ret = 0x00000001 | (USB2_PORTS<<8);
+ break;
case 0x2c: /* Supported Protocol:0c */
- return 0x00000000; /* reserved */
-#if USB3_PORTS > 0
+ ret = 0x00000000; /* reserved */
+ break;
case 0x30: /* Supported Protocol:00 */
- return 0x03000002; /* USB 3.0 */
+ ret = 0x03000002; /* USB 3.0 */
+ break;
case 0x34: /* Supported Protocol:04 */
- return 0x20425455; /* "USB " */
+ ret = 0x20425455; /* "USB " */
+ break;
case 0x38: /* Supported Protocol:08 */
- return 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8);
+ ret = 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8);
+ break;
case 0x3c: /* Supported Protocol:0c */
- return 0x00000000; /* reserved */
-#endif
+ ret = 0x00000000; /* reserved */
+ break;
default:
fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", reg);
+ ret = 0;
}
- return 0;
+
+ trace_usb_xhci_cap_read(reg, ret);
+ return ret;
}
static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg)
{
uint32_t port = reg >> 4;
+ uint32_t ret;
+
if (port >= MAXPORTS) {
fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
- return 0;
+ ret = 0;
+ goto out;
}
switch (reg & 0xf) {
case 0x00: /* PORTSC */
- return xhci->ports[port].portsc;
+ ret = xhci->ports[port].portsc;
+ break;
case 0x04: /* PORTPMSC */
case 0x08: /* PORTLI */
- return 0;
+ ret = 0;
+ break;
case 0x0c: /* reserved */
default:
fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n",
port, reg);
- return 0;
+ ret = 0;
}
+
+out:
+ trace_usb_xhci_port_read(port, reg & 0x0f, ret);
+ return ret;
}
static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
@@ -2420,6 +2421,8 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
uint32_t port = reg >> 4;
uint32_t portsc;
+ trace_usb_xhci_port_write(port, reg & 0x0f, val);
+
if (port >= MAXPORTS) {
fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
return;
@@ -2457,7 +2460,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
{
- DPRINTF("xhci_oper_read(0x%x)\n", reg);
+ uint32_t ret;
if (reg >= 0x400) {
return xhci_port_read(xhci, reg - 0x400);
@@ -2465,38 +2468,50 @@ static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
switch (reg) {
case 0x00: /* USBCMD */
- return xhci->usbcmd;
+ ret = xhci->usbcmd;
+ break;
case 0x04: /* USBSTS */
- return xhci->usbsts;
+ ret = xhci->usbsts;
+ break;
case 0x08: /* PAGESIZE */
- return 1; /* 4KiB */
+ ret = 1; /* 4KiB */
+ break;
case 0x14: /* DNCTRL */
- return xhci->dnctrl;
+ ret = xhci->dnctrl;
+ break;
case 0x18: /* CRCR low */
- return xhci->crcr_low & ~0xe;
+ ret = xhci->crcr_low & ~0xe;
+ break;
case 0x1c: /* CRCR high */
- return xhci->crcr_high;
+ ret = xhci->crcr_high;
+ break;
case 0x30: /* DCBAAP low */
- return xhci->dcbaap_low;
+ ret = xhci->dcbaap_low;
+ break;
case 0x34: /* DCBAAP high */
- return xhci->dcbaap_high;
+ ret = xhci->dcbaap_high;
+ break;
case 0x38: /* CONFIG */
- return xhci->config;
+ ret = xhci->config;
+ break;
default:
fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", reg);
+ ret = 0;
}
- return 0;
+
+ trace_usb_xhci_oper_read(reg, ret);
+ return ret;
}
static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
{
- DPRINTF("xhci_oper_write(0x%x, 0x%08x)\n", reg, val);
-
if (reg >= 0x400) {
xhci_port_write(xhci, reg - 0x400, val);
return;
}
+ trace_usb_xhci_oper_write(reg, val);
+
switch (reg) {
case 0x00: /* USBCMD */
if ((val & USBCMD_RS) && !(xhci->usbcmd & USBCMD_RS)) {
@@ -2506,7 +2521,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
}
xhci->usbcmd = val & 0xc0f;
if (val & USBCMD_HCRST) {
- xhci_reset(xhci);
+ xhci_reset(&xhci->pci_dev.qdev);
}
xhci_irq_update(xhci);
break;
@@ -2552,35 +2567,46 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
{
- DPRINTF("xhci_runtime_read(0x%x)\n", reg);
+ uint32_t ret;
switch (reg) {
case 0x00: /* MFINDEX */
fprintf(stderr, "xhci_runtime_read: MFINDEX not yet implemented\n");
- return xhci->mfindex;
+ ret = xhci->mfindex;
+ break;
case 0x20: /* IMAN */
- return xhci->iman;
+ ret = xhci->iman;
+ break;
case 0x24: /* IMOD */
- return xhci->imod;
+ ret = xhci->imod;
+ break;
case 0x28: /* ERSTSZ */
- return xhci->erstsz;
+ ret = xhci->erstsz;
+ break;
case 0x30: /* ERSTBA low */
- return xhci->erstba_low;
+ ret = xhci->erstba_low;
+ break;
case 0x34: /* ERSTBA high */
- return xhci->erstba_high;
+ ret = xhci->erstba_high;
+ break;
case 0x38: /* ERDP low */
- return xhci->erdp_low;
+ ret = xhci->erdp_low;
+ break;
case 0x3c: /* ERDP high */
- return xhci->erdp_high;
+ ret = xhci->erdp_high;
+ break;
default:
fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg);
+ ret = 0;
}
- return 0;
+
+ trace_usb_xhci_runtime_read(reg, ret);
+ return ret;
}
static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
{
- DPRINTF("xhci_runtime_write(0x%x, 0x%08x)\n", reg, val);
+ trace_usb_xhci_runtime_read(reg, val);
switch (reg) {
case 0x20: /* IMAN */
@@ -2623,14 +2649,14 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
static uint32_t xhci_doorbell_read(XHCIState *xhci, uint32_t reg)
{
- DPRINTF("xhci_doorbell_read(0x%x)\n", reg);
/* doorbells always read as 0 */
+ trace_usb_xhci_doorbell_read(reg, 0);
return 0;
}
static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val)
{
- DPRINTF("xhci_doorbell_write(0x%x, 0x%08x)\n", reg, val);
+ trace_usb_xhci_doorbell_write(reg, val);
if (!xhci_running(xhci)) {
fprintf(stderr, "xhci: wrote doorbell while xHC stopped or paused\n");
@@ -2831,8 +2857,6 @@ static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
for (i = 0; i < MAXSLOTS; i++) {
xhci->slots[i].enabled = 0;
}
-
- qemu_register_reset(xhci_reset, xhci);
}
static int usb_xhci_initfn(struct PCIDevice *dev)
@@ -2895,6 +2919,7 @@ static void xhci_class_init(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_xhci;
dc->props = xhci_properties;
+ dc->reset = xhci_reset;
k->init = usb_xhci_initfn;
k->vendor_id = PCI_VENDOR_ID_NEC;
k->device_id = PCI_DEVICE_ID_NEC_UPD720200;