#include "qemu-timer.h"
#include "iov.h"
#include "dma.h"
+#include "trace.h"
//#define DEBUG
//#define DEBUG_DUMP_DATA
#define NB_PORTS 2
-#ifdef DEBUG
-#define DPRINTF printf
-
-static const char *pid2str(int pid)
-{
- switch (pid) {
- case USB_TOKEN_SETUP: return "SETUP";
- case USB_TOKEN_IN: return "IN";
- case USB_TOKEN_OUT: return "OUT";
- }
- return "?";
-}
-
-#else
-#define DPRINTF(...)
-#endif
+enum {
+ TD_RESULT_STOP_FRAME = 10,
+ TD_RESULT_COMPLETE,
+ TD_RESULT_NEXT_QH,
+ TD_RESULT_ASYNC_START,
+ TD_RESULT_ASYNC_CONT,
+};
typedef struct UHCIState UHCIState;
typedef struct UHCIAsync UHCIAsync;
uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
int64_t expire_time;
QEMUTimer *frame_timer;
+ uint32_t frame_bytes;
UHCIPort ports[NB_PORTS];
/* Interrupts that should be raised at the end of the current frame. */
queue->token = token;
QTAILQ_INIT(&queue->asyncs);
QTAILQ_INSERT_HEAD(&s->queues, queue, next);
+ trace_usb_uhci_queue_add(queue->token);
return queue;
}
{
UHCIState *s = queue->uhci;
+ trace_usb_uhci_queue_del(queue->token);
QTAILQ_REMOVE(&s->queues, queue, next);
g_free(queue);
}
-static UHCIAsync *uhci_async_alloc(UHCIQueue *queue)
+static UHCIAsync *uhci_async_alloc(UHCIQueue *queue, uint32_t addr)
{
UHCIAsync *async = g_new0(UHCIAsync, 1);
async->queue = queue;
+ async->td = addr;
usb_packet_init(&async->packet);
pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1);
+ trace_usb_uhci_packet_add(async->queue->token, async->td);
return async;
}
static void uhci_async_free(UHCIAsync *async)
{
+ trace_usb_uhci_packet_del(async->queue->token, async->td);
usb_packet_cleanup(&async->packet);
qemu_sglist_destroy(&async->sgl);
g_free(async);
{
UHCIQueue *queue = async->queue;
QTAILQ_INSERT_TAIL(&queue->asyncs, async, next);
+ trace_usb_uhci_packet_link_async(async->queue->token, async->td);
}
static void uhci_async_unlink(UHCIAsync *async)
{
UHCIQueue *queue = async->queue;
QTAILQ_REMOVE(&queue->asyncs, async, next);
+ trace_usb_uhci_packet_unlink_async(async->queue->token, async->td);
}
static void uhci_async_cancel(UHCIAsync *async)
{
- DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n",
- async->td, async->token, async->done);
-
+ trace_usb_uhci_packet_cancel(async->queue->token, async->td, async->done);
if (!async->done)
usb_cancel_packet(&async->packet);
uhci_async_free(async);
uhci_async_unlink(curr);
uhci_async_cancel(curr);
}
+ uhci_queue_free(queue);
}
}
int i;
UHCIPort *port;
- DPRINTF("uhci: full reset\n");
+ trace_usb_uhci_reset();
pci_conf = s->dev.config;
}
uhci_async_cancel_all(s);
+ uhci_update_irq(s);
}
static void uhci_pre_save(void *opaque)
UHCIState *s = opaque;
addr &= 0x1f;
- DPRINTF("uhci: writew port=0x%04x val=0x%04x\n", addr, val);
+ trace_usb_uhci_mmio_writew(addr, val);
switch(addr) {
case 0x00:
if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) {
/* start frame processing */
+ trace_usb_uhci_schedule_start();
s->expire_time = qemu_get_clock_ns(vm_clock) +
(get_ticks_per_sec() / FRAME_TIMER_FREQ);
qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
break;
}
- DPRINTF("uhci: readw port=0x%04x val=0x%04x\n", addr, val);
+ trace_usb_uhci_mmio_readw(addr, val);
return val;
}
UHCIState *s = opaque;
addr &= 0x1f;
- DPRINTF("uhci: writel port=0x%04x val=0x%08x\n", addr, val);
+ trace_usb_uhci_mmio_writel(addr, val);
switch(addr) {
case 0x08:
val = 0xffffffff;
break;
}
+ trace_usb_uhci_mmio_readl(addr, val);
return val;
}
if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
*int_mask |= 0x02;
/* short packet: do not update QH */
- DPRINTF("uhci: short packet. td 0x%x token 0x%x\n", async->td, async->token);
- return 1;
+ trace_usb_uhci_packet_complete_shortxfer(async->queue->token,
+ async->td);
+ return TD_RESULT_NEXT_QH;
}
}
/* success */
- return 0;
+ trace_usb_uhci_packet_complete_success(async->queue->token, async->td);
+ return TD_RESULT_COMPLETE;
out:
switch(ret) {
*int_mask |= 0x01;
}
uhci_update_irq(s);
- return 1;
+ trace_usb_uhci_packet_complete_stall(async->queue->token, async->td);
+ return TD_RESULT_NEXT_QH;
case USB_RET_BABBLE:
td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
}
uhci_update_irq(s);
/* frame interrupted */
- return -1;
+ trace_usb_uhci_packet_complete_babble(async->queue->token, async->td);
+ return TD_RESULT_STOP_FRAME;
case USB_RET_NAK:
td->ctrl |= TD_CTRL_NAK;
if (pid == USB_TOKEN_SETUP)
break;
- return 1;
+ return TD_RESULT_NEXT_QH;
case USB_RET_IOERROR:
case USB_RET_NODEV:
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)) |
(err << TD_CTRL_ERROR_SHIFT);
- return 1;
+ return TD_RESULT_NEXT_QH;
}
-static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *int_mask)
+static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
+ uint32_t *int_mask, bool queuing)
{
UHCIAsync *async;
int len = 0, max_len;
/* Is active ? */
if (!(td->ctrl & TD_CTRL_ACTIVE))
- return 1;
+ return TD_RESULT_NEXT_QH;
async = uhci_async_find_td(s, addr, td);
if (async) {
async->queue->valid = 32;
if (!async->done)
- return 1;
+ return TD_RESULT_ASYNC_CONT;
+ if (queuing) {
+ /* we are busy filling the queue, we are not prepared
+ to consume completed packages then, just leave them
+ in async state */
+ return TD_RESULT_ASYNC_CONT;
+ }
uhci_async_unlink(async);
goto done;
}
/* Allocate new packet */
- async = uhci_async_alloc(uhci_queue_get(s, td));
- if (!async)
- return 1;
+ async = uhci_async_alloc(uhci_queue_get(s, td), addr);
/* valid needs to be large enough to handle 10 frame delay
* for initial isochronous requests
*/
async->queue->valid = 32;
- async->td = addr;
async->isoc = td->ctrl & TD_CTRL_IOS;
max_len = ((td->token >> 21) + 1) & 0x7ff;
uhci_async_free(async);
s->status |= UHCI_STS_HCPERR;
uhci_update_irq(s);
- return -1;
+ return TD_RESULT_STOP_FRAME;
}
if (len == USB_RET_ASYNC) {
uhci_async_link(async);
- return 2;
+ return TD_RESULT_ASYNC_START;
}
async->packet.result = len;
UHCIAsync *async = container_of(packet, UHCIAsync, packet);
UHCIState *s = async->queue->uhci;
- DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
-
if (async->isoc) {
UHCI_TD td;
uint32_t link = async->td;
if (uhci_queue_token(&ptd) != token) {
break;
}
- ret = uhci_handle_td(s, plink, &ptd, &int_mask);
- assert(ret == 2); /* got USB_RET_ASYNC */
+ trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token);
+ ret = uhci_handle_td(s, plink, &ptd, &int_mask, true);
+ if (ret == TD_RESULT_ASYNC_CONT) {
+ break;
+ }
+ assert(ret == TD_RESULT_ASYNC_START);
assert(int_mask == 0);
plink = ptd.link;
}
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;
frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2);
- DPRINTF("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr);
-
pci_dma_read(&s->dev, frame_addr, &link, 4);
le32_to_cpus(&link);
qhdb_reset(&qhdb);
for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) {
+ if (s->frame_bytes >= 1280) {
+ /* 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);
if (qhdb_insert(&qhdb, link)) {
/*
* 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.
*/
- DPRINTF("uhci: detected loop. qh 0x%x\n", link);
if (td_count == 0) {
- DPRINTF("uhci: no transaction last round, stop\n");
- break;
- } else if (bytes_count >= 1280) {
- DPRINTF("uhci: bandwidth limit reached, stop\n");
+ trace_usb_uhci_frame_loop_stop_idle();
break;
} else {
+ trace_usb_uhci_frame_loop_continue();
td_count = 0;
qhdb_reset(&qhdb);
qhdb_insert(&qhdb, link);
le32_to_cpus(&qh.link);
le32_to_cpus(&qh.el_link);
- DPRINTF("uhci: QH 0x%x load. link 0x%x elink 0x%x\n",
- link, qh.link, qh.el_link);
-
if (!is_valid(qh.el_link)) {
/* QH w/o elements */
curr_qh = 0;
le32_to_cpus(&td.ctrl);
le32_to_cpus(&td.token);
le32_to_cpus(&td.buffer);
-
- DPRINTF("uhci: TD 0x%x load. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
- link, td.link, td.ctrl, td.token, curr_qh);
+ trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token);
old_td_ctrl = td.ctrl;
- ret = uhci_handle_td(s, link, &td, &int_mask);
+ ret = uhci_handle_td(s, link, &td, &int_mask, false);
if (old_td_ctrl != td.ctrl) {
/* update the status bits of the TD */
val = cpu_to_le32(td.ctrl);
}
switch (ret) {
- case -1: /* interrupted frame */
+ case TD_RESULT_STOP_FRAME: /* interrupted frame */
goto out;
- case 1: /* goto next queue */
- DPRINTF("uhci: TD 0x%x skip. "
- "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
- link, td.link, td.ctrl, td.token, curr_qh);
+ case TD_RESULT_NEXT_QH:
+ case TD_RESULT_ASYNC_CONT:
+ trace_usb_uhci_td_nextqh(curr_qh & ~0xf, link & ~0xf);
link = curr_qh ? qh.link : td.link;
continue;
- case 2: /* got USB_RET_ASYNC */
- DPRINTF("uhci: TD 0x%x async. "
- "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
- link, td.link, td.ctrl, td.token, curr_qh);
+ case TD_RESULT_ASYNC_START:
+ trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf);
if (is_valid(td.link)) {
uhci_fill_queue(s, &td);
}
link = curr_qh ? qh.link : td.link;
continue;
- case 0: /* completed TD */
- DPRINTF("uhci: TD 0x%x done. "
- "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
- link, td.link, td.ctrl, td.token, curr_qh);
-
+ case TD_RESULT_COMPLETE:
+ 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 */
if (!depth_first(link)) {
/* done with this QH */
-
- DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
- curr_qh, qh.link, qh.el_link);
-
curr_qh = 0;
link = qh.link;
}
/* prepare the timer for the next frame */
s->expire_time += (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+ s->frame_bytes = 0;
if (!(s->cmd & UHCI_CMD_RS)) {
/* Full stop */
+ trace_usb_uhci_schedule_stop();
qemu_del_timer(s->frame_timer);
+ uhci_async_cancel_all(s);
/* set hchalted bit in status - UHCI11D 2.1.2 */
s->status |= UHCI_STS_HCHALTED;
-
- DPRINTF("uhci: halted\n");
return;
}
/* Start new frame */
s->frnum = (s->frnum + 1) & 0x7ff;
- DPRINTF("uhci: new frame #%u\n" , s->frnum);
+ trace_usb_uhci_frame_start(s->frnum);
uhci_async_validate_begin(s);