xhci: add qemu-xhci device, some followup cleanups.

ccid: better sanity checking.
 ehci: fix memory leak
 ohci: bugfixes.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJYq+jbAAoJEEy22O7T6HE4gukP/jqP9SxKcZyEIFalmnOzR+Wq
 5N20ZRuAJXAFEWG+FLOz+TC9tYCJ3IEpDwkEEtlFXdZgc8SF+YlOhWLRvTHSQYfy
 LZbYgoQnRsE+kpYVaADJ+lGYGRM/RjPPLkt54FF6SDX9jtNbv7nSyIvTXHkNrjZd
 cnGxCy4yu4S1kXhCURhkHiK8SjloexrwwtkfLfOVApFIpXcfb2enWIN79pbb6a1I
 8GdYaTedQtr+//FasDagFWpHgpCiknA0A4C+C0E7tGtBkGa1IIoq0RB/82ZApoNB
 r8aM7SwhjsrRLGniIQNxXK93I5TX2SjHYeeIt37yvI89h5NAk6nzW6Dk9UbAWITq
 YOm9xOD27d8PtsBuEa9cVB2NJv5rflGyl+dsP0VbwKmhzMS/NU44cCLkcpDXsaIp
 R09vIuCCBoAyae9IgrFty6wJrpv2HmuLUCJelU3lOBSCGihSBCjTlLLnzSEkXV6o
 9b+sfPTC5/d1U2sivCW7T0LxqIgyFew76NGdSO5Y86CqB+sYEQv3kwR6zcV/3WlE
 DonQLAPMtDdFwAoR/Aww7dhsNSMERYDz76xg6P7+FgbwG59iGzcqNkpsMyAAOvSS
 wVl1RkAm2Ln3Vo4re9fQJtWF/MLeDnZe7I1SQJdXF5NcRWCEPLd8tK+WFeg9HcPD
 99jIUosbJ2prKtcKkNhI
 =5lEp
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kraxel/tags/pull-usb-20170221-1' into staging

xhci: add qemu-xhci device, some followup cleanups.
ccid: better sanity checking.
ehci: fix memory leak
ohci: bugfixes.

# gpg: Signature made Tue 21 Feb 2017 07:14:35 GMT
# gpg:                using RSA key 0x4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>"
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>"
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>"
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/pull-usb-20170221-1:
  usb-ccid: add check message size checks
  usb-ccid: move header size check
  usb-ccid: better bulk_out error handling
  xhci: drop via vendor command handling
  xhci: fix nec vendor quirk handling
  xhci: add qemu xhci controller
  xhci: drop ER_FULL_HACK workaround
  xhci: apply limits to loops
  usb: ohci: limit the number of link eds
  usb: ohci: fix error return code in servicing iso td
  usb: ehci: fix memory leak in ehci

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
master
Peter Maydell 2017-02-21 09:35:15 +00:00
commit b856256179
9 changed files with 191 additions and 221 deletions

View File

@ -61,6 +61,7 @@ PCI devices (other than virtio):
1b36:0009 PCI Expander Bridge (-device pxb)
1b36:000a PCI-PCI bridge (multiseat)
1b36:000b PCIe Expander Bridge (-device pxb-pcie)
1b36:000d PCI xhci usb host adapter
All these devices are documented in docs/specs.

View File

@ -1001,80 +1001,92 @@ static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
CCID_Header *ccid_header;
if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
p->status = USB_RET_STALL;
return;
goto err;
}
ccid_header = (CCID_Header *)s->bulk_out_data;
usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size);
s->bulk_out_pos += p->iov.size;
if (p->iov.size == CCID_MAX_PACKET_SIZE) {
if (s->bulk_out_pos < 10) {
DPRINTF(s, 1, "%s: header incomplete\n", __func__);
goto err;
}
ccid_header = (CCID_Header *)s->bulk_out_data;
if ((s->bulk_out_pos - 10 < ccid_header->dwLength) &&
(p->iov.size == CCID_MAX_PACKET_SIZE)) {
DPRINTF(s, D_VERBOSE,
"usb-ccid: bulk_in: expecting more packets (%zd/%d)\n",
p->iov.size, ccid_header->dwLength);
"usb-ccid: bulk_in: expecting more packets (%d/%d)\n",
s->bulk_out_pos - 10, ccid_header->dwLength);
return;
}
if (s->bulk_out_pos < 10) {
if (s->bulk_out_pos - 10 != ccid_header->dwLength) {
DPRINTF(s, 1,
"%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n",
__func__);
} else {
DPRINTF(s, D_MORE_INFO, "%s %x %s\n", __func__,
ccid_header->bMessageType,
ccid_message_type_to_str(ccid_header->bMessageType));
switch (ccid_header->bMessageType) {
case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus:
ccid_write_slot_status(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn:
DPRINTF(s, 1, "%s: PowerOn: %d\n", __func__,
"usb-ccid: bulk_in: message size mismatch (got %d, expected %d)\n",
s->bulk_out_pos - 10, ccid_header->dwLength);
goto err;
}
DPRINTF(s, D_MORE_INFO, "%s %x %s\n", __func__,
ccid_header->bMessageType,
ccid_message_type_to_str(ccid_header->bMessageType));
switch (ccid_header->bMessageType) {
case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus:
ccid_write_slot_status(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn:
DPRINTF(s, 1, "%s: PowerOn: %d\n", __func__,
((CCID_IccPowerOn *)(ccid_header))->bPowerSelect);
s->powered = true;
if (!ccid_card_inserted(s)) {
ccid_report_error_failed(s, ERROR_ICC_MUTE);
}
/* atr is written regardless of error. */
ccid_write_data_block_atr(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff:
ccid_reset_error_status(s);
s->powered = false;
ccid_write_slot_status(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock:
ccid_on_apdu_from_guest(s, (CCID_XferBlock *)s->bulk_out_data);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters:
ccid_reset_error_status(s);
ccid_set_parameters(s, ccid_header);
ccid_write_parameters(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters:
ccid_reset_error_status(s);
ccid_reset_parameters(s);
ccid_write_parameters(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters:
ccid_reset_error_status(s);
ccid_write_parameters(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical:
ccid_report_error_failed(s, 0);
ccid_write_slot_status(s, ccid_header);
break;
default:
DPRINTF(s, 1,
s->powered = true;
if (!ccid_card_inserted(s)) {
ccid_report_error_failed(s, ERROR_ICC_MUTE);
}
/* atr is written regardless of error. */
ccid_write_data_block_atr(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff:
ccid_reset_error_status(s);
s->powered = false;
ccid_write_slot_status(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock:
ccid_on_apdu_from_guest(s, (CCID_XferBlock *)s->bulk_out_data);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters:
ccid_reset_error_status(s);
ccid_set_parameters(s, ccid_header);
ccid_write_parameters(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters:
ccid_reset_error_status(s);
ccid_reset_parameters(s);
ccid_write_parameters(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters:
ccid_reset_error_status(s);
ccid_write_parameters(s, ccid_header);
break;
case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical:
ccid_report_error_failed(s, 0);
ccid_write_slot_status(s, ccid_header);
break;
default:
DPRINTF(s, 1,
"handle_data: ERROR: unhandled message type %Xh\n",
ccid_header->bMessageType);
/*
* The caller is expecting the device to respond, tell it we
* don't support the operation.
*/
ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED);
ccid_write_slot_status(s, ccid_header);
break;
}
/*
* The caller is expecting the device to respond, tell it we
* don't support the operation.
*/
ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED);
ccid_write_slot_status(s, ccid_header);
break;
}
s->bulk_out_pos = 0;
return;
err:
p->status = USB_RET_STALL;
s->bulk_out_pos = 0;
return;
}
static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)

View File

@ -89,6 +89,14 @@ static void usb_ehci_pci_init(Object *obj)
usb_ehci_init(s, DEVICE(obj));
}
static void usb_ehci_pci_finalize(Object *obj)
{
EHCIPCIState *i = PCI_EHCI(obj);
EHCIState *s = &i->ehci;
usb_ehci_finalize(s);
}
static void usb_ehci_pci_exit(PCIDevice *dev)
{
EHCIPCIState *i = PCI_EHCI(dev);
@ -159,6 +167,7 @@ static const TypeInfo ehci_pci_type_info = {
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(EHCIPCIState),
.instance_init = usb_ehci_pci_init,
.instance_finalize = usb_ehci_pci_finalize,
.abstract = true,
.class_init = ehci_class_init,
};

View File

@ -2545,6 +2545,11 @@ void usb_ehci_init(EHCIState *s, DeviceState *dev)
&s->mem_ports);
}
void usb_ehci_finalize(EHCIState *s)
{
usb_packet_cleanup(&s->ipacket);
}
/*
* vim: expandtab ts=4
*/

View File

@ -323,6 +323,7 @@ struct EHCIState {
extern const VMStateDescription vmstate_ehci;
void usb_ehci_init(EHCIState *s, DeviceState *dev);
void usb_ehci_finalize(EHCIState *s);
void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp);
void usb_ehci_unrealize(EHCIState *s, DeviceState *dev, Error **errp);
void ehci_reset(void *opaque);

View File

@ -42,6 +42,8 @@
#define OHCI_MAX_PORTS 15
#define ED_LINK_LIMIT 4
static int64_t usb_frame_time;
static int64_t usb_bit_time;
@ -725,7 +727,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
if (ohci_read_iso_td(ohci, addr, &iso_td)) {
trace_usb_ohci_iso_td_read_failed(addr);
ohci_die(ohci);
return 0;
return 1;
}
starting_frame = OHCI_BM(iso_td.flags, TD_SF);
@ -1184,7 +1186,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
uint32_t next_ed;
uint32_t cur;
int active;
uint32_t link_cnt = 0;
active = 0;
if (head == 0)
@ -1199,6 +1201,11 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
next_ed = ed.next & OHCI_DPTR_MASK;
if (++link_cnt > ED_LINK_LIMIT) {
ohci_die(ohci);
return 0;
}
if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) {
uint32_t addr;
/* Cancel pending packets for ED that have been paused. */

View File

@ -49,11 +49,10 @@
/* Very pessimistic, let's hope it's enough for all cases */
#define EV_QUEUE (((3 * 24) + 16) * MAXSLOTS)
/* Do not deliver ER Full events. NEC's driver does some things not bound
* to the specs when it gets them */
#define ER_FULL_HACK
#define TRB_LINK_LIMIT 4
#define COMMAND_LIMIT 256
#define TRANSFER_LIMIT 256
#define LEN_CAP 0x40
#define LEN_OPER (0x400 + 0x10 * MAXPORTS)
@ -199,7 +198,6 @@ typedef enum TRBType {
ER_DEVICE_NOTIFICATION,
ER_MFINDEX_WRAP,
/* vendor specific bits */
CR_VENDOR_VIA_CHALLENGE_RESPONSE = 48,
CR_VENDOR_NEC_FIRMWARE_REVISION = 49,
CR_VENDOR_NEC_CHALLENGE_RESPONSE = 50,
} TRBType;
@ -431,12 +429,14 @@ typedef struct XHCIInterrupter {
uint32_t erdp_low;
uint32_t erdp_high;
bool msix_used, er_pcs, er_full;
bool msix_used, er_pcs;
dma_addr_t er_start;
uint32_t er_size;
unsigned int er_ep_idx;
/* kept for live migration compat only */
bool er_full_unused;
XHCIEvent ev_buffer[EV_QUEUE];
unsigned int ev_buffer_put;
unsigned int ev_buffer_get;
@ -486,9 +486,13 @@ struct XHCIState {
XHCIInterrupter intr[MAXINTRS];
XHCIRing cmd_ring;
bool nec_quirks;
};
#define TYPE_XHCI "nec-usb-xhci"
#define TYPE_XHCI "base-xhci"
#define TYPE_NEC_XHCI "nec-usb-xhci"
#define TYPE_QEMU_XHCI "qemu-xhci"
#define XHCI(obj) \
OBJECT_CHECK(XHCIState, (obj), TYPE_XHCI)
@ -549,7 +553,6 @@ static const char *TRBType_names[] = {
[ER_HOST_CONTROLLER] = "ER_HOST_CONTROLLER",
[ER_DEVICE_NOTIFICATION] = "ER_DEVICE_NOTIFICATION",
[ER_MFINDEX_WRAP] = "ER_MFINDEX_WRAP",
[CR_VENDOR_VIA_CHALLENGE_RESPONSE] = "CR_VENDOR_VIA_CHALLENGE_RESPONSE",
[CR_VENDOR_NEC_FIRMWARE_REVISION] = "CR_VENDOR_NEC_FIRMWARE_REVISION",
[CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE",
};
@ -826,7 +829,7 @@ static void xhci_intr_raise(XHCIState *xhci, int v)
static inline int xhci_running(XHCIState *xhci)
{
return !(xhci->usbsts & USBSTS_HCH) && !xhci->intr[0].er_full;
return !(xhci->usbsts & USBSTS_HCH);
}
static void xhci_die(XHCIState *xhci)
@ -865,74 +868,6 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
}
}
static void xhci_events_update(XHCIState *xhci, int v)
{
XHCIInterrupter *intr = &xhci->intr[v];
dma_addr_t erdp;
unsigned int dp_idx;
bool do_irq = 0;
if (xhci->usbsts & USBSTS_HCH) {
return;
}
erdp = xhci_addr64(intr->erdp_low, intr->erdp_high);
if (erdp < intr->er_start ||
erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) {
DPRINTF("xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp);
DPRINTF("xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n",
v, intr->er_start, intr->er_size);
xhci_die(xhci);
return;
}
dp_idx = (erdp - intr->er_start) / TRB_SIZE;
assert(dp_idx < intr->er_size);
/* NEC didn't read section 4.9.4 of the spec (v1.0 p139 top Note) and thus
* deadlocks when the ER is full. Hack it by holding off events until
* the driver decides to free at least half of the ring */
if (intr->er_full) {
int er_free = dp_idx - intr->er_ep_idx;
if (er_free <= 0) {
er_free += intr->er_size;
}
if (er_free < (intr->er_size/2)) {
DPRINTF("xhci_events_update(): event ring still "
"more than half full (hack)\n");
return;
}
}
while (intr->ev_buffer_put != intr->ev_buffer_get) {
assert(intr->er_full);
if (((intr->er_ep_idx+1) % intr->er_size) == dp_idx) {
DPRINTF("xhci_events_update(): event ring full again\n");
#ifndef ER_FULL_HACK
XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
xhci_write_event(xhci, &full, v);
#endif
do_irq = 1;
break;
}
XHCIEvent *event = &intr->ev_buffer[intr->ev_buffer_get];
xhci_write_event(xhci, event, v);
intr->ev_buffer_get++;
do_irq = 1;
if (intr->ev_buffer_get == EV_QUEUE) {
intr->ev_buffer_get = 0;
}
}
if (do_irq) {
xhci_intr_raise(xhci, v);
}
if (intr->er_full && intr->ev_buffer_put == intr->ev_buffer_get) {
DPRINTF("xhci_events_update(): event ring no longer full\n");
intr->er_full = 0;
}
}
static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
{
XHCIInterrupter *intr;
@ -945,19 +880,6 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
}
intr = &xhci->intr[v];
if (intr->er_full) {
DPRINTF("xhci_event(): ER full, queueing\n");
if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) {
DPRINTF("xhci: event queue full, dropping event!\n");
return;
}
intr->ev_buffer[intr->ev_buffer_put++] = *event;
if (intr->ev_buffer_put == EV_QUEUE) {
intr->ev_buffer_put = 0;
}
return;
}
erdp = xhci_addr64(intr->erdp_low, intr->erdp_high);
if (erdp < intr->er_start ||
erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) {
@ -971,21 +893,12 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
dp_idx = (erdp - intr->er_start) / TRB_SIZE;
assert(dp_idx < intr->er_size);
if ((intr->er_ep_idx+1) % intr->er_size == dp_idx) {
DPRINTF("xhci_event(): ER full, queueing\n");
#ifndef ER_FULL_HACK
if ((intr->er_ep_idx + 2) % intr->er_size == dp_idx) {
DPRINTF("xhci: ER %d full, send ring full error\n", v);
XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
xhci_write_event(xhci, &full);
#endif
intr->er_full = 1;
if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) {
DPRINTF("xhci: event queue full, dropping event!\n");
return;
}
intr->ev_buffer[intr->ev_buffer_put++] = *event;
if (intr->ev_buffer_put == EV_QUEUE) {
intr->ev_buffer_put = 0;
}
xhci_write_event(xhci, &full, v);
} else if ((intr->er_ep_idx + 1) % intr->er_size == dp_idx) {
DPRINTF("xhci: ER %d full, drop event\n", v);
} else {
xhci_write_event(xhci, event, v);
}
@ -1032,6 +945,7 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
return type;
} else {
if (++link_cnt > TRB_LINK_LIMIT) {
trace_usb_xhci_enforced_limit("trb-link");
return 0;
}
ring->dequeue = xhci_mask64(trb->parameter);
@ -1124,7 +1038,6 @@ static void xhci_er_reset(XHCIState *xhci, int v)
intr->er_ep_idx = 0;
intr->er_pcs = 1;
intr->er_full = 0;
DPRINTF("xhci: event ring[%d]:" DMA_ADDR_FMT " [%d]\n",
v, intr->er_start, intr->er_size);
@ -2150,6 +2063,7 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
XHCIRing *ring;
USBEndpoint *ep = NULL;
uint64_t mfindex;
unsigned int count = 0;
int length;
int i;
@ -2262,6 +2176,10 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
epctx->retry = xfer;
break;
}
if (count++ > TRANSFER_LIMIT) {
trace_usb_xhci_enforced_limit("transfers");
break;
}
}
epctx->kick_active--;
@ -2702,39 +2620,13 @@ static uint32_t xhci_nec_challenge(uint32_t hi, uint32_t lo)
return ~val;
}
static void xhci_via_challenge(XHCIState *xhci, uint64_t addr)
{
PCIDevice *pci_dev = PCI_DEVICE(xhci);
uint32_t buf[8];
uint32_t obuf[8];
dma_addr_t paddr = xhci_mask64(addr);
pci_dma_read(pci_dev, paddr, &buf, 32);
memcpy(obuf, buf, sizeof(obuf));
if ((buf[0] & 0xff) == 2) {
obuf[0] = 0x49932000 + 0x54dc200 * buf[2] + 0x7429b578 * buf[3];
obuf[0] |= (buf[2] * buf[3]) & 0xff;
obuf[1] = 0x0132bb37 + 0xe89 * buf[2] + 0xf09 * buf[3];
obuf[2] = 0x0066c2e9 + 0x2091 * buf[2] + 0x19bd * buf[3];
obuf[3] = 0xd5281342 + 0x2cc9691 * buf[2] + 0x2367662 * buf[3];
obuf[4] = 0x0123c75c + 0x1595 * buf[2] + 0x19ec * buf[3];
obuf[5] = 0x00f695de + 0x26fd * buf[2] + 0x3e9 * buf[3];
obuf[6] = obuf[2] ^ obuf[3] ^ 0x29472956;
obuf[7] = obuf[2] ^ obuf[3] ^ 0x65866593;
}
pci_dma_write(pci_dev, paddr, &obuf, 32);
}
static void xhci_process_commands(XHCIState *xhci)
{
XHCITRB trb;
TRBType type;
XHCIEvent event = {ER_COMMAND_COMPLETE, CC_SUCCESS};
dma_addr_t addr;
unsigned int i, slotid = 0;
unsigned int i, slotid = 0, count = 0;
DPRINTF("xhci_process_commands()\n");
if (!xhci_running(xhci)) {
@ -2823,24 +2715,27 @@ static void xhci_process_commands(XHCIState *xhci)
case CR_GET_PORT_BANDWIDTH:
event.ccode = xhci_get_port_bandwidth(xhci, trb.parameter);
break;
case CR_VENDOR_VIA_CHALLENGE_RESPONSE:
xhci_via_challenge(xhci, trb.parameter);
break;
case CR_VENDOR_NEC_FIRMWARE_REVISION:
event.type = 48; /* NEC reply */
event.length = 0x3025;
if (xhci->nec_quirks) {
event.type = 48; /* NEC reply */
event.length = 0x3025;
} else {
event.ccode = CC_TRB_ERROR;
}
break;
case CR_VENDOR_NEC_CHALLENGE_RESPONSE:
{
uint32_t chi = trb.parameter >> 32;
uint32_t clo = trb.parameter;
uint32_t val = xhci_nec_challenge(chi, clo);
event.length = val & 0xFFFF;
event.epid = val >> 16;
slotid = val >> 24;
event.type = 48; /* NEC reply */
}
break;
if (xhci->nec_quirks) {
uint32_t chi = trb.parameter >> 32;
uint32_t clo = trb.parameter;
uint32_t val = xhci_nec_challenge(chi, clo);
event.length = val & 0xFFFF;
event.epid = val >> 16;
slotid = val >> 24;
event.type = 48; /* NEC reply */
} else {
event.ccode = CC_TRB_ERROR;
}
break;
default:
trace_usb_xhci_unimplemented("command", type);
event.ccode = CC_TRB_ERROR;
@ -2848,6 +2743,11 @@ static void xhci_process_commands(XHCIState *xhci)
}
event.slotid = slotid;
xhci_event(xhci, &event, 0);
if (count++ > COMMAND_LIMIT) {
trace_usb_xhci_enforced_limit("commands");
return;
}
}
}
@ -2978,7 +2878,6 @@ static void xhci_reset(DeviceState *dev)
xhci->intr[i].er_ep_idx = 0;
xhci->intr[i].er_pcs = 1;
xhci->intr[i].er_full = 0;
xhci->intr[i].ev_buffer_put = 0;
xhci->intr[i].ev_buffer_get = 0;
}
@ -3343,9 +3242,12 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
intr->erstsz = val & 0xffff;
break;
case 0x10: /* ERSTBA low */
/* XXX NEC driver bug: it doesn't align this to 64 bytes
intr->erstba_low = val & 0xffffffc0; */
intr->erstba_low = val & 0xfffffff0;
if (xhci->nec_quirks) {
/* NEC driver bug: it doesn't align this to 64 bytes */
intr->erstba_low = val & 0xfffffff0;
} else {
intr->erstba_low = val & 0xffffffc0;
}
break;
case 0x14: /* ERSTBA high */
intr->erstba_high = val;
@ -3368,7 +3270,6 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
break;
case 0x1c: /* ERDP high */
intr->erdp_high = val;
xhci_events_update(xhci, v);
break;
default:
trace_usb_xhci_unimplemented("oper write", reg);
@ -3641,6 +3542,9 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
dev->config[PCI_CACHE_LINE_SIZE] = 0x10;
dev->config[0x60] = 0x30; /* release number */
if (strcmp(object_get_typename(OBJECT(dev)), TYPE_NEC_XHCI) == 0) {
xhci->nec_quirks = true;
}
if (xhci->numintrs > MAXINTRS) {
xhci->numintrs = MAXINTRS;
}
@ -3866,8 +3770,7 @@ static const VMStateDescription vmstate_xhci_event = {
static bool xhci_er_full(void *opaque, int version_id)
{
struct XHCIInterrupter *intr = opaque;
return intr->er_full;
return false;
}
static const VMStateDescription vmstate_xhci_intr = {
@ -3891,7 +3794,7 @@ static const VMStateDescription vmstate_xhci_intr = {
VMSTATE_UINT32(er_ep_idx, XHCIInterrupter),
/* event queue (used if ring is full) */
VMSTATE_BOOL(er_full, XHCIInterrupter),
VMSTATE_BOOL(er_full_unused, XHCIInterrupter),
VMSTATE_UINT32_TEST(ev_buffer_put, XHCIInterrupter, xhci_er_full),
VMSTATE_UINT32_TEST(ev_buffer_get, XHCIInterrupter, xhci_er_full),
VMSTATE_STRUCT_ARRAY_TEST(ev_buffer, XHCIInterrupter, EV_QUEUE,
@ -3963,10 +3866,7 @@ static void xhci_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_USB, dc->categories);
k->realize = usb_xhci_realize;
k->exit = usb_xhci_exit;
k->vendor_id = PCI_VENDOR_ID_NEC;
k->device_id = PCI_DEVICE_ID_NEC_UPD720200;
k->class_id = PCI_CLASS_SERIAL_USB;
k->revision = 0x03;
k->is_express = 1;
}
@ -3975,11 +3875,44 @@ static const TypeInfo xhci_info = {
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XHCIState),
.class_init = xhci_class_init,
.abstract = true,
};
static void nec_xhci_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->vendor_id = PCI_VENDOR_ID_NEC;
k->device_id = PCI_DEVICE_ID_NEC_UPD720200;
k->revision = 0x03;
}
static const TypeInfo nec_xhci_info = {
.name = TYPE_NEC_XHCI,
.parent = TYPE_XHCI,
.class_init = nec_xhci_class_init,
};
static void qemu_xhci_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->vendor_id = PCI_VENDOR_ID_REDHAT;
k->device_id = PCI_DEVICE_ID_REDHAT_XHCI;
k->revision = 0x01;
}
static const TypeInfo qemu_xhci_info = {
.name = TYPE_QEMU_XHCI,
.parent = TYPE_XHCI,
.class_init = qemu_xhci_class_init,
};
static void xhci_register_types(void)
{
type_register_static(&xhci_info);
type_register_static(&nec_xhci_info);
type_register_static(&qemu_xhci_info);
}
type_init(xhci_register_types)

View File

@ -174,6 +174,7 @@ 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"
usb_xhci_unimplemented(const char *item, int nr) "%s (0x%x)"
usb_xhci_enforced_limit(const char *item) "%s"
# hw/usb/desc.c
usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"

View File

@ -97,6 +97,7 @@
#define PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT 0x000a
#define PCI_DEVICE_ID_REDHAT_PXB_PCIE 0x000b
#define PCI_DEVICE_ID_REDHAT_PCIE_RP 0x000c
#define PCI_DEVICE_ID_REDHAT_XHCI 0x000d
#define PCI_DEVICE_ID_REDHAT_QXL 0x0100
#define FMT_PCIBUS PRIx64