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
  ...
This commit is contained in:
Anthony Liguori 2012-06-11 12:07:00 -05:00
commit 39cde84517
7 changed files with 696 additions and 373 deletions

View File

@ -1507,10 +1507,9 @@ static void put_scsi_requests(QEMUFile *f, void *pv, size_t size)
QTAILQ_FOREACH(req, &s->requests, next) {
assert(!req->io_canceled);
assert(req->status == -1);
assert(req->retry);
assert(req->enqueued);
qemu_put_sbyte(f, 1);
qemu_put_sbyte(f, req->retry ? 1 : 2);
qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf));
qemu_put_be32s(f, &req->tag);
qemu_put_be32s(f, &req->lun);
@ -1528,8 +1527,9 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size)
{
SCSIDevice *s = pv;
SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus);
int8_t sbyte;
while (qemu_get_sbyte(f)) {
while ((sbyte = qemu_get_sbyte(f)) > 0) {
uint8_t buf[SCSI_CMD_BUF_SIZE];
uint32_t tag;
uint32_t lun;
@ -1539,6 +1539,7 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size)
qemu_get_be32s(f, &tag);
qemu_get_be32s(f, &lun);
req = scsi_req_new(s, tag, lun, buf, NULL);
req->retry = (sbyte == 1);
if (bus->info->load_request) {
req->hba_private = bus->info->load_request(f, req);
}
@ -1547,7 +1548,6 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size)
}
/* Just restart it later. */
req->retry = true;
scsi_req_enqueue_internal(req);
/* At this point, the request will be kept alive by the reference

View File

@ -132,8 +132,14 @@ static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req)
qemu_put_be64s(f, &r->sector);
qemu_put_be32s(f, &r->sector_count);
qemu_put_be32s(f, &r->buflen);
if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
if (r->buflen) {
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
} else if (!req->retry) {
uint32_t len = r->iov.iov_len;
qemu_put_be32s(f, &len);
qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
}
}
}
@ -148,6 +154,12 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
scsi_init_iovec(r, r->buflen);
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
} else if (!r->req.retry) {
uint32_t len;
qemu_get_be32s(f, &len);
r->iov.iov_len = len;
assert(r->iov.iov_len <= r->buflen);
qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
}
}

View File

@ -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()
}
};

File diff suppressed because it is too large Load Diff

View File

@ -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(),
};

View File

@ -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;

View File

@ -257,19 +257,20 @@ usb_ehci_port_detach(uint32_t port) "detach port #%d"
usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d"
usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t len, uint32_t bufpos) "write %d, cpage %d, offset 0x%03x, addr 0x%08x, len %d, bufpos %d"
usb_ehci_queue_action(void *q, const char *action) "q %p: %s"
usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s"
# hw/usb/hcd-uhci.c
usb_uhci_reset(void) "=== RESET ==="
usb_uhci_schedule_start(void) ""
usb_uhci_schedule_stop(void) ""
usb_uhci_frame_start(uint32_t num) "nr %d"
usb_uhci_frame_stop_bandwidth(void) ""
usb_uhci_frame_loop_stop_idle(void) ""
usb_uhci_frame_loop_stop_bandwidth(void) ""
usb_uhci_frame_loop_continue(void) ""
usb_uhci_mmio_readw(uint32_t addr, uint32_t val) "addr %04x, ret 0x04%x"
usb_uhci_mmio_writew(uint32_t addr, uint32_t val) "addr %04x, val 0x04%x"
usb_uhci_mmio_readl(uint32_t addr, uint32_t val) "addr %04x, ret 0x08%x"
usb_uhci_mmio_writel(uint32_t addr, uint32_t val) "addr %04x, val 0x08%x"
usb_uhci_mmio_readw(uint32_t addr, uint32_t val) "addr 0x%04x, ret 0x%04x"
usb_uhci_mmio_writew(uint32_t addr, uint32_t val) "addr 0x%04x, val 0x%04x"
usb_uhci_mmio_readl(uint32_t addr, uint32_t val) "addr 0x%04x, ret 0x%08x"
usb_uhci_mmio_writel(uint32_t addr, uint32_t val) "addr 0x%04x, val 0x%08x"
usb_uhci_queue_add(uint32_t token) "token 0x%x"
usb_uhci_queue_del(uint32_t token) "token 0x%x"
usb_uhci_packet_add(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
@ -289,6 +290,41 @@ usb_uhci_td_nextqh(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x"
usb_uhci_td_async(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x"
usb_uhci_td_complete(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x"
# hw/usb/hcd-xhci.c
usb_xhci_reset(void) "=== RESET ==="
usb_xhci_run(void) ""
usb_xhci_stop(void) ""
usb_xhci_cap_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x"
usb_xhci_oper_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x"
usb_xhci_port_read(uint32_t port, uint32_t off, uint32_t val) "port %d, off 0x%04x, ret 0x%08x"
usb_xhci_runtime_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x"
usb_xhci_doorbell_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x"
usb_xhci_oper_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
usb_xhci_port_write(uint32_t port, uint32_t off, uint32_t val) "port %d, off 0x%04x, val 0x%08x"
usb_xhci_runtime_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
usb_xhci_doorbell_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
usb_xhci_irq_intx(uint32_t level) "level %d"
usb_xhci_irq_msi(uint32_t nr) "nr %d"
usb_xhci_queue_event(uint32_t idx, const char *name, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, p %016" PRIx64 ", s %08x, c 0x%08x"
usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x"
usb_xhci_slot_enable(uint32_t slotid) "slotid %d"
usb_xhci_slot_disable(uint32_t slotid) "slotid %d"
usb_xhci_slot_address(uint32_t slotid) "slotid %d"
usb_xhci_slot_configure(uint32_t slotid) "slotid %d"
usb_xhci_slot_evaluate(uint32_t slotid) "slotid %d"
usb_xhci_slot_reset(uint32_t slotid) "slotid %d"
usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t length) "%p: slotid %d, epid %d, length %d"
usb_xhci_xfer_async(void *xfer) "%p"
usb_xhci_xfer_nak(void *xfer) "%p"
usb_xhci_xfer_retry(void *xfer) "%p"
usb_xhci_xfer_success(void *xfer, uint32_t bytes) "%p: len %d"
usb_xhci_xfer_error(void *xfer, uint32_t ret) "%p: ret %d"
# hw/usb/desc.c
usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device qualifier, len %d, ret %d"