mirror of
https://github.com/qemu/qemu.git
synced 2024-11-25 03:43:37 +08:00
Merge remote-tracking branch 'kraxel/usb.69' into staging
* kraxel/usb.69: (31 commits) usb-redir: Allow redirecting super speed devices to high speed controllers usb-redir: Allow to attach USB 2.0 devices to 1.1 host controller usb-redir: Use reject rather the disconnect on bad ep info usb-redir: Add an usbredir_setup_usb_eps() helper function usb-redir: Add support for input pipelining usb-redir: Add support for 32 bits bulk packet length combined-packet: Add a workaround for Linux usbfs + live migration usb: Add packet combining functions uhci: Don't crash on device disconnect uhci: Add a uhci_handle_td_error() helper function usb/ehci-pci: add helper to create ich9 usb controllers usb/ehci-pci: add ich9 00:1a.* variant usb/ehci-pci: dynamic type generation uhci: add ich9 00:1a.* variants uhci: stick irq routing info into UHCIInfo too. uhci: dynamic type generation xilinx_zynq: add USB controllers usb/ehci: add sysbus variant usb/ehci: split into multiple source files usb/ehci: Guard definition of EHCI_DEBUG ... Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
commit
de0a36cd01
2
configure
vendored
2
configure
vendored
@ -2788,7 +2788,7 @@ fi
|
||||
|
||||
# check for usbredirparser for usb network redirection support
|
||||
if test "$usb_redir" != "no" ; then
|
||||
if $pkg_config --atleast-version=0.5 libusbredirparser-0.5 >/dev/null 2>&1 ; then
|
||||
if $pkg_config --atleast-version=0.5.3 libusbredirparser-0.5 >/dev/null 2>&1 ; then
|
||||
usb_redir="yes"
|
||||
usb_redir_cflags=$($pkg_config --cflags libusbredirparser-0.5 2>/dev/null)
|
||||
usb_redir_libs=$($pkg_config --libs libusbredirparser-0.5 2>/dev/null)
|
||||
|
15
hw/usb.h
15
hw/usb.h
@ -160,6 +160,7 @@ typedef struct USBBusOps USBBusOps;
|
||||
typedef struct USBPort USBPort;
|
||||
typedef struct USBDevice USBDevice;
|
||||
typedef struct USBPacket USBPacket;
|
||||
typedef struct USBCombinedPacket USBCombinedPacket;
|
||||
typedef struct USBEndpoint USBEndpoint;
|
||||
|
||||
typedef struct USBDesc USBDesc;
|
||||
@ -356,7 +357,15 @@ struct USBPacket {
|
||||
int result; /* transfer length or USB_RET_* status code */
|
||||
/* Internal use by the USB layer. */
|
||||
USBPacketState state;
|
||||
USBCombinedPacket *combined;
|
||||
QTAILQ_ENTRY(USBPacket) queue;
|
||||
QTAILQ_ENTRY(USBPacket) combined_entry;
|
||||
};
|
||||
|
||||
struct USBCombinedPacket {
|
||||
USBPacket *first;
|
||||
QTAILQ_HEAD(packets_head, USBPacket) packets;
|
||||
QEMUIOVector iov;
|
||||
};
|
||||
|
||||
void usb_packet_init(USBPacket *p);
|
||||
@ -399,6 +408,10 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled);
|
||||
USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
|
||||
uint64_t id);
|
||||
|
||||
void usb_ep_combine_input_packets(USBEndpoint *ep);
|
||||
void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p);
|
||||
void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p);
|
||||
|
||||
void usb_attach(USBPort *port);
|
||||
void usb_detach(USBPort *port);
|
||||
void usb_port_reset(USBPort *port);
|
||||
@ -524,5 +537,7 @@ const char *usb_device_get_product_desc(USBDevice *dev);
|
||||
|
||||
const USBDesc *usb_device_get_usb_desc(USBDevice *dev);
|
||||
|
||||
int ehci_create_ich9_with_companions(PCIBus *bus, int slot);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o
|
||||
common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o
|
||||
common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o
|
||||
common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o hcd-ehci-sysbus.o
|
||||
common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o
|
||||
common-obj-y += libhw.o
|
||||
|
||||
common-obj-$(CONFIG_SMARTCARD) += dev-smartcard-reader.o
|
||||
common-obj-$(CONFIG_USB_REDIR) += redirect.o
|
||||
|
||||
common-obj-y += core.o bus.o desc.o dev-hub.o
|
||||
common-obj-y += core.o combined-packet.o bus.o desc.o dev-hub.o
|
||||
common-obj-y += host-$(HOST_USB).o dev-bluetooth.o
|
||||
common-obj-y += dev-hid.o dev-storage.o dev-wacom.o
|
||||
common-obj-y += dev-serial.o dev-network.o dev-audio.o
|
||||
|
182
hw/usb/combined-packet.c
Normal file
182
hw/usb/combined-packet.c
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* QEMU USB packet combining code (for input pipelining)
|
||||
*
|
||||
* Copyright(c) 2012 Red Hat, Inc.
|
||||
*
|
||||
* Red Hat Authors:
|
||||
* Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or(at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "qemu-common.h"
|
||||
#include "hw/usb.h"
|
||||
#include "iov.h"
|
||||
#include "trace.h"
|
||||
|
||||
static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p)
|
||||
{
|
||||
qemu_iovec_concat(&combined->iov, &p->iov, 0, p->iov.size);
|
||||
QTAILQ_INSERT_TAIL(&combined->packets, p, combined_entry);
|
||||
p->combined = combined;
|
||||
}
|
||||
|
||||
static void usb_combined_packet_remove(USBCombinedPacket *combined,
|
||||
USBPacket *p)
|
||||
{
|
||||
assert(p->combined == combined);
|
||||
p->combined = NULL;
|
||||
QTAILQ_REMOVE(&combined->packets, p, combined_entry);
|
||||
}
|
||||
|
||||
/* Also handles completion of non combined packets for pipelined input eps */
|
||||
void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBCombinedPacket *combined = p->combined;
|
||||
USBEndpoint *ep = p->ep;
|
||||
USBPacket *next;
|
||||
enum { completing, complete, leftover };
|
||||
int result, state = completing;
|
||||
bool short_not_ok;
|
||||
|
||||
if (combined == NULL) {
|
||||
usb_packet_complete_one(dev, p);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets));
|
||||
|
||||
result = combined->first->result;
|
||||
short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) {
|
||||
if (state == completing) {
|
||||
/* Distribute data over uncombined packets */
|
||||
if (result >= p->iov.size) {
|
||||
p->result = p->iov.size;
|
||||
} else {
|
||||
/* Send short or error packet to complete the transfer */
|
||||
p->result = result;
|
||||
state = complete;
|
||||
}
|
||||
p->short_not_ok = short_not_ok;
|
||||
usb_combined_packet_remove(combined, p);
|
||||
usb_packet_complete_one(dev, p);
|
||||
result -= p->result;
|
||||
} else {
|
||||
/* Remove any leftover packets from the queue */
|
||||
state = leftover;
|
||||
p->result = USB_RET_REMOVE_FROM_QUEUE;
|
||||
dev->port->ops->complete(dev->port, p);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we had leftover packets the hcd driver will have cancelled them
|
||||
* and usb_combined_packet_cancel has already freed combined!
|
||||
*/
|
||||
if (state != leftover) {
|
||||
g_free(combined);
|
||||
}
|
||||
leave:
|
||||
/* Check if there are packets in the queue waiting for our completion */
|
||||
usb_ep_combine_input_packets(ep);
|
||||
}
|
||||
|
||||
/* May only be called for combined packets! */
|
||||
void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBCombinedPacket *combined = p->combined;
|
||||
assert(combined != NULL);
|
||||
|
||||
usb_combined_packet_remove(combined, p);
|
||||
if (p == combined->first) {
|
||||
usb_device_cancel_packet(dev, p);
|
||||
}
|
||||
if (QTAILQ_EMPTY(&combined->packets)) {
|
||||
g_free(combined);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Large input transfers can get split into multiple input packets, this
|
||||
* function recombines them, removing the short_not_ok checks which all but
|
||||
* the last packet of such splits transfers have, thereby allowing input
|
||||
* transfer pipelining (which we cannot do on short_not_ok transfers)
|
||||
*/
|
||||
void usb_ep_combine_input_packets(USBEndpoint *ep)
|
||||
{
|
||||
USBPacket *p, *u, *next, *prev = NULL, *first = NULL;
|
||||
USBPort *port = ep->dev->port;
|
||||
int ret, totalsize;
|
||||
|
||||
assert(ep->pipeline);
|
||||
assert(ep->pid == USB_TOKEN_IN);
|
||||
|
||||
QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) {
|
||||
/* Empty the queue on a halt */
|
||||
if (ep->halted) {
|
||||
p->result = USB_RET_REMOVE_FROM_QUEUE;
|
||||
port->ops->complete(port, p);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip packets already submitted to the device */
|
||||
if (p->state == USB_PACKET_ASYNC) {
|
||||
prev = p;
|
||||
continue;
|
||||
}
|
||||
usb_packet_check_state(p, USB_PACKET_QUEUED);
|
||||
|
||||
/*
|
||||
* If the previous (combined) packet has the short_not_ok flag set
|
||||
* stop, as we must not submit packets to the device after a transfer
|
||||
* ending with short_not_ok packet.
|
||||
*/
|
||||
if (prev && prev->short_not_ok) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
if (first->combined == NULL) {
|
||||
USBCombinedPacket *combined = g_new0(USBCombinedPacket, 1);
|
||||
|
||||
combined->first = first;
|
||||
QTAILQ_INIT(&combined->packets);
|
||||
qemu_iovec_init(&combined->iov, 2);
|
||||
usb_combined_packet_add(combined, first);
|
||||
}
|
||||
usb_combined_packet_add(first->combined, p);
|
||||
} else {
|
||||
first = p;
|
||||
}
|
||||
|
||||
/* Is this packet the last one of a (combined) transfer? */
|
||||
totalsize = (p->combined) ? p->combined->iov.size : p->iov.size;
|
||||
if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok ||
|
||||
next == NULL ||
|
||||
/* Work around for Linux usbfs bulk splitting + migration */
|
||||
(totalsize == 16348 && p->int_req)) {
|
||||
ret = usb_device_handle_data(ep->dev, first);
|
||||
assert(ret == USB_RET_ASYNC);
|
||||
if (first->combined) {
|
||||
QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) {
|
||||
usb_packet_set_state(u, USB_PACKET_ASYNC);
|
||||
}
|
||||
} else {
|
||||
usb_packet_set_state(first, USB_PACKET_ASYNC);
|
||||
}
|
||||
first = NULL;
|
||||
prev = p;
|
||||
}
|
||||
}
|
||||
}
|
@ -545,6 +545,7 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
|
||||
p->parameter = 0;
|
||||
p->short_not_ok = short_not_ok;
|
||||
p->int_req = int_req;
|
||||
p->combined = NULL;
|
||||
qemu_iovec_reset(&p->iov);
|
||||
usb_packet_set_state(p, USB_PACKET_SETUP);
|
||||
}
|
||||
|
200
hw/usb/hcd-ehci-pci.c
Normal file
200
hw/usb/hcd-ehci-pci.c
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* QEMU USB EHCI Emulation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or(at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hw/usb/hcd-ehci.h"
|
||||
#include "hw/pci.h"
|
||||
|
||||
typedef struct EHCIPCIState {
|
||||
PCIDevice pcidev;
|
||||
EHCIState ehci;
|
||||
} EHCIPCIState;
|
||||
|
||||
typedef struct EHCIPCIInfo {
|
||||
const char *name;
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint8_t revision;
|
||||
} EHCIPCIInfo;
|
||||
|
||||
static int usb_ehci_pci_initfn(PCIDevice *dev)
|
||||
{
|
||||
EHCIPCIState *i = DO_UPCAST(EHCIPCIState, pcidev, dev);
|
||||
EHCIState *s = &i->ehci;
|
||||
uint8_t *pci_conf = dev->config;
|
||||
|
||||
pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20);
|
||||
|
||||
/* capabilities pointer */
|
||||
pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00);
|
||||
/* pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); */
|
||||
|
||||
pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */
|
||||
pci_set_byte(&pci_conf[PCI_MIN_GNT], 0);
|
||||
pci_set_byte(&pci_conf[PCI_MAX_LAT], 0);
|
||||
|
||||
/* pci_conf[0x50] = 0x01; *//* power management caps */
|
||||
|
||||
pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); /* release # (2.1.4) */
|
||||
pci_set_byte(&pci_conf[0x61], 0x20); /* frame length adjustment (2.1.5) */
|
||||
pci_set_word(&pci_conf[0x62], 0x00); /* port wake up capability (2.1.6) */
|
||||
|
||||
pci_conf[0x64] = 0x00;
|
||||
pci_conf[0x65] = 0x00;
|
||||
pci_conf[0x66] = 0x00;
|
||||
pci_conf[0x67] = 0x00;
|
||||
pci_conf[0x68] = 0x01;
|
||||
pci_conf[0x69] = 0x00;
|
||||
pci_conf[0x6a] = 0x00;
|
||||
pci_conf[0x6b] = 0x00; /* USBLEGSUP */
|
||||
pci_conf[0x6c] = 0x00;
|
||||
pci_conf[0x6d] = 0x00;
|
||||
pci_conf[0x6e] = 0x00;
|
||||
pci_conf[0x6f] = 0xc0; /* USBLEFCTLSTS */
|
||||
|
||||
s->caps[0x09] = 0x68; /* EECP */
|
||||
|
||||
s->irq = dev->irq[3];
|
||||
s->dma = pci_dma_context(dev);
|
||||
|
||||
s->capsbase = 0x00;
|
||||
s->opregbase = 0x20;
|
||||
|
||||
usb_ehci_initfn(s, DEVICE(dev));
|
||||
pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Property ehci_pci_properties[] = {
|
||||
DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.maxframes, 128),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_ehci_pci = {
|
||||
.name = "ehci",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState),
|
||||
VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState),
|
||||
}
|
||||
};
|
||||
|
||||
static void ehci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
EHCIPCIInfo *i = data;
|
||||
|
||||
k->init = usb_ehci_pci_initfn;
|
||||
k->vendor_id = i->vendor_id;
|
||||
k->device_id = i->device_id;
|
||||
k->revision = i->revision;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
dc->vmsd = &vmstate_ehci;
|
||||
dc->props = ehci_pci_properties;
|
||||
}
|
||||
|
||||
static struct EHCIPCIInfo ehci_pci_info[] = {
|
||||
{
|
||||
.name = "usb-ehci",
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */
|
||||
.revision = 0x10,
|
||||
},{
|
||||
.name = "ich9-usb-ehci1", /* 00:1d.7 */
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1,
|
||||
.revision = 0x03,
|
||||
},{
|
||||
.name = "ich9-usb-ehci2", /* 00:1a.7 */
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI2,
|
||||
.revision = 0x03,
|
||||
}
|
||||
};
|
||||
|
||||
static void ehci_pci_register_types(void)
|
||||
{
|
||||
TypeInfo ehci_type_info = {
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(EHCIPCIState),
|
||||
.class_init = ehci_class_init,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ehci_pci_info); i++) {
|
||||
ehci_type_info.name = ehci_pci_info[i].name;
|
||||
ehci_type_info.class_data = ehci_pci_info + i;
|
||||
type_register(&ehci_type_info);
|
||||
}
|
||||
}
|
||||
|
||||
type_init(ehci_pci_register_types)
|
||||
|
||||
struct ehci_companions {
|
||||
const char *name;
|
||||
int func;
|
||||
int port;
|
||||
};
|
||||
|
||||
static const struct ehci_companions ich9_1d[] = {
|
||||
{ .name = "ich9-usb-uhci1", .func = 0, .port = 0 },
|
||||
{ .name = "ich9-usb-uhci2", .func = 1, .port = 2 },
|
||||
{ .name = "ich9-usb-uhci3", .func = 2, .port = 4 },
|
||||
};
|
||||
|
||||
static const struct ehci_companions ich9_1a[] = {
|
||||
{ .name = "ich9-usb-uhci4", .func = 0, .port = 0 },
|
||||
{ .name = "ich9-usb-uhci5", .func = 1, .port = 2 },
|
||||
{ .name = "ich9-usb-uhci6", .func = 2, .port = 4 },
|
||||
};
|
||||
|
||||
int ehci_create_ich9_with_companions(PCIBus *bus, int slot)
|
||||
{
|
||||
const struct ehci_companions *comp;
|
||||
PCIDevice *ehci, *uhci;
|
||||
BusState *usbbus;
|
||||
const char *name;
|
||||
int i;
|
||||
|
||||
switch (slot) {
|
||||
case 0x1d:
|
||||
name = "ich9-usb-ehci1";
|
||||
comp = ich9_1d;
|
||||
break;
|
||||
case 0x1a:
|
||||
name = "ich9-usb-ehci2";
|
||||
comp = ich9_1a;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
ehci = pci_create_multifunction(bus, PCI_DEVFN(slot, 7), true, name);
|
||||
qdev_init_nofail(&ehci->qdev);
|
||||
usbbus = QLIST_FIRST(&ehci->qdev.child_bus);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
uhci = pci_create_multifunction(bus, PCI_DEVFN(slot, comp[i].func),
|
||||
true, comp[i].name);
|
||||
qdev_prop_set_string(&uhci->qdev, "masterbus", usbbus->name);
|
||||
qdev_prop_set_uint32(&uhci->qdev, "firstport", comp[i].port);
|
||||
qdev_init_nofail(&uhci->qdev);
|
||||
}
|
||||
return 0;
|
||||
}
|
77
hw/usb/hcd-ehci-sysbus.c
Normal file
77
hw/usb/hcd-ehci-sysbus.c
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* QEMU USB EHCI Emulation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or(at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hw/usb/hcd-ehci.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
typedef struct EHCISysBusState {
|
||||
SysBusDevice busdev;
|
||||
EHCIState ehci;
|
||||
} EHCISysBusState;
|
||||
|
||||
static const VMStateDescription vmstate_ehci_sysbus = {
|
||||
.name = "ehci-sysbus",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT(ehci, EHCISysBusState, 2, vmstate_ehci, EHCIState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property ehci_sysbus_properties[] = {
|
||||
DEFINE_PROP_UINT32("maxframes", EHCISysBusState, ehci.maxframes, 128),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static int usb_ehci_sysbus_initfn(SysBusDevice *dev)
|
||||
{
|
||||
EHCISysBusState *i = FROM_SYSBUS(EHCISysBusState, dev);
|
||||
EHCIState *s = &i->ehci;
|
||||
|
||||
s->capsbase = 0x100;
|
||||
s->opregbase = 0x140;
|
||||
|
||||
usb_ehci_initfn(s, DEVICE(dev));
|
||||
sysbus_init_irq(dev, &s->irq);
|
||||
sysbus_init_mmio(dev, &s->mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ehci_sysbus_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = usb_ehci_sysbus_initfn;
|
||||
dc->vmsd = &vmstate_ehci_sysbus;
|
||||
dc->props = ehci_sysbus_properties;
|
||||
}
|
||||
|
||||
TypeInfo ehci_xlnx_type_info = {
|
||||
.name = "xlnx,ps7-usb",
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(EHCISysBusState),
|
||||
.class_init = ehci_sysbus_class_init,
|
||||
};
|
||||
|
||||
static void ehci_sysbus_register_types(void)
|
||||
{
|
||||
type_register_static(&ehci_xlnx_type_info);
|
||||
}
|
||||
|
||||
type_init(ehci_sysbus_register_types)
|
@ -27,41 +27,21 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "hw/usb.h"
|
||||
#include "hw/pci.h"
|
||||
#include "monitor.h"
|
||||
#include "trace.h"
|
||||
#include "dma.h"
|
||||
#include "sysemu.h"
|
||||
|
||||
#define EHCI_DEBUG 0
|
||||
|
||||
#if EHCI_DEBUG
|
||||
#define DPRINTF printf
|
||||
#else
|
||||
#define DPRINTF(...)
|
||||
#endif
|
||||
#include "hw/usb/hcd-ehci.h"
|
||||
|
||||
/* internal processing - reset HC to try and recover */
|
||||
#define USB_RET_PROCERR (-99)
|
||||
|
||||
#define MMIO_SIZE 0x1000
|
||||
|
||||
/* Capability Registers Base Address - section 2.2 */
|
||||
#define CAPREGBASE 0x0000
|
||||
#define CAPLENGTH CAPREGBASE + 0x0000 // 1-byte, 0x0001 reserved
|
||||
#define HCIVERSION CAPREGBASE + 0x0002 // 2-bytes, i/f version #
|
||||
#define HCSPARAMS CAPREGBASE + 0x0004 // 4-bytes, structural params
|
||||
#define HCCPARAMS CAPREGBASE + 0x0008 // 4-bytes, capability params
|
||||
#define CAPLENGTH 0x0000 /* 1-byte, 0x0001 reserved */
|
||||
#define HCIVERSION 0x0002 /* 2-bytes, i/f version # */
|
||||
#define HCSPARAMS 0x0004 /* 4-bytes, structural params */
|
||||
#define HCCPARAMS 0x0008 /* 4-bytes, capability params */
|
||||
#define EECP HCCPARAMS + 1
|
||||
#define HCSPPORTROUTE1 CAPREGBASE + 0x000c
|
||||
#define HCSPPORTROUTE2 CAPREGBASE + 0x0010
|
||||
#define HCSPPORTROUTE1 0x000c
|
||||
#define HCSPPORTROUTE2 0x0010
|
||||
|
||||
#define OPREGBASE 0x0020 // Operational Registers Base Address
|
||||
|
||||
#define USBCMD OPREGBASE + 0x0000
|
||||
#define USBCMD 0x0000
|
||||
#define USBCMD_RUNSTOP (1 << 0) // run / Stop
|
||||
#define USBCMD_HCRESET (1 << 1) // HC Reset
|
||||
#define USBCMD_FLS (3 << 2) // Frame List Size
|
||||
@ -75,7 +55,7 @@
|
||||
#define USBCMD_ITC (0x7f << 16) // Int Threshold Control
|
||||
#define USBCMD_ITC_SH 16 // Int Threshold Control Shift
|
||||
|
||||
#define USBSTS OPREGBASE + 0x0004
|
||||
#define USBSTS 0x0004
|
||||
#define USBSTS_RO_MASK 0x0000003f
|
||||
#define USBSTS_INT (1 << 0) // USB Interrupt
|
||||
#define USBSTS_ERRINT (1 << 1) // Error Interrupt
|
||||
@ -92,20 +72,17 @@
|
||||
* Interrupt enable bits correspond to the interrupt active bits in USBSTS
|
||||
* so no need to redefine here.
|
||||
*/
|
||||
#define USBINTR OPREGBASE + 0x0008
|
||||
#define USBINTR 0x0008
|
||||
#define USBINTR_MASK 0x0000003f
|
||||
|
||||
#define FRINDEX OPREGBASE + 0x000c
|
||||
#define CTRLDSSEGMENT OPREGBASE + 0x0010
|
||||
#define PERIODICLISTBASE OPREGBASE + 0x0014
|
||||
#define ASYNCLISTADDR OPREGBASE + 0x0018
|
||||
#define FRINDEX 0x000c
|
||||
#define CTRLDSSEGMENT 0x0010
|
||||
#define PERIODICLISTBASE 0x0014
|
||||
#define ASYNCLISTADDR 0x0018
|
||||
#define ASYNCLISTADDR_MASK 0xffffffe0
|
||||
|
||||
#define CONFIGFLAG OPREGBASE + 0x0040
|
||||
#define CONFIGFLAG 0x0040
|
||||
|
||||
#define PORTSC (OPREGBASE + 0x0044)
|
||||
#define PORTSC_BEGIN PORTSC
|
||||
#define PORTSC_END (PORTSC + 4 * NB_PORTS)
|
||||
/*
|
||||
* Bits that are reserved or are read-only are masked out of values
|
||||
* written to us by software
|
||||
@ -137,7 +114,6 @@
|
||||
#define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ)
|
||||
|
||||
#define NB_MAXINTRATE 8 // Max rate at which controller issues ints
|
||||
#define NB_PORTS 6 // Number of downstream ports
|
||||
#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction
|
||||
#define MAX_QH 100 // Max allowable queue heads in a chain
|
||||
#define MIN_FR_PER_TICK 3 // Min frames to process when catching up
|
||||
@ -174,278 +150,6 @@ typedef enum {
|
||||
#define NLPTR_TYPE_STITD 2 // split xaction, isoc xfer descriptor
|
||||
#define NLPTR_TYPE_FSTN 3 // frame span traversal node
|
||||
|
||||
|
||||
/* EHCI spec version 1.0 Section 3.3
|
||||
*/
|
||||
typedef struct EHCIitd {
|
||||
uint32_t next;
|
||||
|
||||
uint32_t transact[8];
|
||||
#define ITD_XACT_ACTIVE (1 << 31)
|
||||
#define ITD_XACT_DBERROR (1 << 30)
|
||||
#define ITD_XACT_BABBLE (1 << 29)
|
||||
#define ITD_XACT_XACTERR (1 << 28)
|
||||
#define ITD_XACT_LENGTH_MASK 0x0fff0000
|
||||
#define ITD_XACT_LENGTH_SH 16
|
||||
#define ITD_XACT_IOC (1 << 15)
|
||||
#define ITD_XACT_PGSEL_MASK 0x00007000
|
||||
#define ITD_XACT_PGSEL_SH 12
|
||||
#define ITD_XACT_OFFSET_MASK 0x00000fff
|
||||
|
||||
uint32_t bufptr[7];
|
||||
#define ITD_BUFPTR_MASK 0xfffff000
|
||||
#define ITD_BUFPTR_SH 12
|
||||
#define ITD_BUFPTR_EP_MASK 0x00000f00
|
||||
#define ITD_BUFPTR_EP_SH 8
|
||||
#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f
|
||||
#define ITD_BUFPTR_DEVADDR_SH 0
|
||||
#define ITD_BUFPTR_DIRECTION (1 << 11)
|
||||
#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff
|
||||
#define ITD_BUFPTR_MAXPKT_SH 0
|
||||
#define ITD_BUFPTR_MULT_MASK 0x00000003
|
||||
#define ITD_BUFPTR_MULT_SH 0
|
||||
} EHCIitd;
|
||||
|
||||
/* EHCI spec version 1.0 Section 3.4
|
||||
*/
|
||||
typedef struct EHCIsitd {
|
||||
uint32_t next; // Standard next link pointer
|
||||
uint32_t epchar;
|
||||
#define SITD_EPCHAR_IO (1 << 31)
|
||||
#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000
|
||||
#define SITD_EPCHAR_PORTNUM_SH 24
|
||||
#define SITD_EPCHAR_HUBADD_MASK 0x007f0000
|
||||
#define SITD_EPCHAR_HUBADDR_SH 16
|
||||
#define SITD_EPCHAR_EPNUM_MASK 0x00000f00
|
||||
#define SITD_EPCHAR_EPNUM_SH 8
|
||||
#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f
|
||||
|
||||
uint32_t uframe;
|
||||
#define SITD_UFRAME_CMASK_MASK 0x0000ff00
|
||||
#define SITD_UFRAME_CMASK_SH 8
|
||||
#define SITD_UFRAME_SMASK_MASK 0x000000ff
|
||||
|
||||
uint32_t results;
|
||||
#define SITD_RESULTS_IOC (1 << 31)
|
||||
#define SITD_RESULTS_PGSEL (1 << 30)
|
||||
#define SITD_RESULTS_TBYTES_MASK 0x03ff0000
|
||||
#define SITD_RESULTS_TYBYTES_SH 16
|
||||
#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00
|
||||
#define SITD_RESULTS_CPROGMASK_SH 8
|
||||
#define SITD_RESULTS_ACTIVE (1 << 7)
|
||||
#define SITD_RESULTS_ERR (1 << 6)
|
||||
#define SITD_RESULTS_DBERR (1 << 5)
|
||||
#define SITD_RESULTS_BABBLE (1 << 4)
|
||||
#define SITD_RESULTS_XACTERR (1 << 3)
|
||||
#define SITD_RESULTS_MISSEDUF (1 << 2)
|
||||
#define SITD_RESULTS_SPLITXSTATE (1 << 1)
|
||||
|
||||
uint32_t bufptr[2];
|
||||
#define SITD_BUFPTR_MASK 0xfffff000
|
||||
#define SITD_BUFPTR_CURROFF_MASK 0x00000fff
|
||||
#define SITD_BUFPTR_TPOS_MASK 0x00000018
|
||||
#define SITD_BUFPTR_TPOS_SH 3
|
||||
#define SITD_BUFPTR_TCNT_MASK 0x00000007
|
||||
|
||||
uint32_t backptr; // Standard next link pointer
|
||||
} EHCIsitd;
|
||||
|
||||
/* EHCI spec version 1.0 Section 3.5
|
||||
*/
|
||||
typedef struct EHCIqtd {
|
||||
uint32_t next; // Standard next link pointer
|
||||
uint32_t altnext; // Standard next link pointer
|
||||
uint32_t token;
|
||||
#define QTD_TOKEN_DTOGGLE (1 << 31)
|
||||
#define QTD_TOKEN_TBYTES_MASK 0x7fff0000
|
||||
#define QTD_TOKEN_TBYTES_SH 16
|
||||
#define QTD_TOKEN_IOC (1 << 15)
|
||||
#define QTD_TOKEN_CPAGE_MASK 0x00007000
|
||||
#define QTD_TOKEN_CPAGE_SH 12
|
||||
#define QTD_TOKEN_CERR_MASK 0x00000c00
|
||||
#define QTD_TOKEN_CERR_SH 10
|
||||
#define QTD_TOKEN_PID_MASK 0x00000300
|
||||
#define QTD_TOKEN_PID_SH 8
|
||||
#define QTD_TOKEN_ACTIVE (1 << 7)
|
||||
#define QTD_TOKEN_HALT (1 << 6)
|
||||
#define QTD_TOKEN_DBERR (1 << 5)
|
||||
#define QTD_TOKEN_BABBLE (1 << 4)
|
||||
#define QTD_TOKEN_XACTERR (1 << 3)
|
||||
#define QTD_TOKEN_MISSEDUF (1 << 2)
|
||||
#define QTD_TOKEN_SPLITXSTATE (1 << 1)
|
||||
#define QTD_TOKEN_PING (1 << 0)
|
||||
|
||||
uint32_t bufptr[5]; // Standard buffer pointer
|
||||
#define QTD_BUFPTR_MASK 0xfffff000
|
||||
#define QTD_BUFPTR_SH 12
|
||||
} EHCIqtd;
|
||||
|
||||
/* EHCI spec version 1.0 Section 3.6
|
||||
*/
|
||||
typedef struct EHCIqh {
|
||||
uint32_t next; // Standard next link pointer
|
||||
|
||||
/* endpoint characteristics */
|
||||
uint32_t epchar;
|
||||
#define QH_EPCHAR_RL_MASK 0xf0000000
|
||||
#define QH_EPCHAR_RL_SH 28
|
||||
#define QH_EPCHAR_C (1 << 27)
|
||||
#define QH_EPCHAR_MPLEN_MASK 0x07FF0000
|
||||
#define QH_EPCHAR_MPLEN_SH 16
|
||||
#define QH_EPCHAR_H (1 << 15)
|
||||
#define QH_EPCHAR_DTC (1 << 14)
|
||||
#define QH_EPCHAR_EPS_MASK 0x00003000
|
||||
#define QH_EPCHAR_EPS_SH 12
|
||||
#define EHCI_QH_EPS_FULL 0
|
||||
#define EHCI_QH_EPS_LOW 1
|
||||
#define EHCI_QH_EPS_HIGH 2
|
||||
#define EHCI_QH_EPS_RESERVED 3
|
||||
|
||||
#define QH_EPCHAR_EP_MASK 0x00000f00
|
||||
#define QH_EPCHAR_EP_SH 8
|
||||
#define QH_EPCHAR_I (1 << 7)
|
||||
#define QH_EPCHAR_DEVADDR_MASK 0x0000007f
|
||||
#define QH_EPCHAR_DEVADDR_SH 0
|
||||
|
||||
/* endpoint capabilities */
|
||||
uint32_t epcap;
|
||||
#define QH_EPCAP_MULT_MASK 0xc0000000
|
||||
#define QH_EPCAP_MULT_SH 30
|
||||
#define QH_EPCAP_PORTNUM_MASK 0x3f800000
|
||||
#define QH_EPCAP_PORTNUM_SH 23
|
||||
#define QH_EPCAP_HUBADDR_MASK 0x007f0000
|
||||
#define QH_EPCAP_HUBADDR_SH 16
|
||||
#define QH_EPCAP_CMASK_MASK 0x0000ff00
|
||||
#define QH_EPCAP_CMASK_SH 8
|
||||
#define QH_EPCAP_SMASK_MASK 0x000000ff
|
||||
#define QH_EPCAP_SMASK_SH 0
|
||||
|
||||
uint32_t current_qtd; // Standard next link pointer
|
||||
uint32_t next_qtd; // Standard next link pointer
|
||||
uint32_t altnext_qtd;
|
||||
#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e
|
||||
#define QH_ALTNEXT_NAKCNT_SH 1
|
||||
|
||||
uint32_t token; // Same as QTD token
|
||||
uint32_t bufptr[5]; // Standard buffer pointer
|
||||
#define BUFPTR_CPROGMASK_MASK 0x000000ff
|
||||
#define BUFPTR_FRAMETAG_MASK 0x0000001f
|
||||
#define BUFPTR_SBYTES_MASK 0x00000fe0
|
||||
#define BUFPTR_SBYTES_SH 5
|
||||
} EHCIqh;
|
||||
|
||||
/* EHCI spec version 1.0 Section 3.7
|
||||
*/
|
||||
typedef struct EHCIfstn {
|
||||
uint32_t next; // Standard next link pointer
|
||||
uint32_t backptr; // Standard next link pointer
|
||||
} EHCIfstn;
|
||||
|
||||
typedef struct EHCIPacket EHCIPacket;
|
||||
typedef struct EHCIQueue EHCIQueue;
|
||||
typedef struct EHCIState EHCIState;
|
||||
|
||||
enum async_state {
|
||||
EHCI_ASYNC_NONE = 0,
|
||||
EHCI_ASYNC_INITIALIZED,
|
||||
EHCI_ASYNC_INFLIGHT,
|
||||
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;
|
||||
enum async_state async;
|
||||
int usb_status;
|
||||
};
|
||||
|
||||
struct EHCIQueue {
|
||||
EHCIState *ehci;
|
||||
QTAILQ_ENTRY(EHCIQueue) next;
|
||||
uint32_t seen;
|
||||
uint64_t ts;
|
||||
int async;
|
||||
int transact_ctr;
|
||||
|
||||
/* 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 */
|
||||
uint32_t qtdaddr; /* address QTD read from */
|
||||
USBDevice *dev;
|
||||
QTAILQ_HEAD(pkts_head, EHCIPacket) packets;
|
||||
};
|
||||
|
||||
typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
|
||||
|
||||
struct EHCIState {
|
||||
PCIDevice dev;
|
||||
USBBus bus;
|
||||
qemu_irq irq;
|
||||
MemoryRegion mem;
|
||||
MemoryRegion mem_caps;
|
||||
MemoryRegion mem_opreg;
|
||||
MemoryRegion mem_ports;
|
||||
int companion_count;
|
||||
|
||||
/* properties */
|
||||
uint32_t maxframes;
|
||||
|
||||
/*
|
||||
* EHCI spec version 1.0 Section 2.3
|
||||
* Host Controller Operational Registers
|
||||
*/
|
||||
uint8_t caps[OPREGBASE];
|
||||
union {
|
||||
uint32_t opreg[(PORTSC_BEGIN-OPREGBASE)/sizeof(uint32_t)];
|
||||
struct {
|
||||
uint32_t usbcmd;
|
||||
uint32_t usbsts;
|
||||
uint32_t usbintr;
|
||||
uint32_t frindex;
|
||||
uint32_t ctrldssegment;
|
||||
uint32_t periodiclistbase;
|
||||
uint32_t asynclistaddr;
|
||||
uint32_t notused[9];
|
||||
uint32_t configflag;
|
||||
};
|
||||
};
|
||||
uint32_t portsc[NB_PORTS];
|
||||
|
||||
/*
|
||||
* Internal states, shadow registers, etc
|
||||
*/
|
||||
QEMUTimer *frame_timer;
|
||||
QEMUBH *async_bh;
|
||||
uint32_t astate; /* Current state in asynchronous schedule */
|
||||
uint32_t pstate; /* Current state in periodic schedule */
|
||||
USBPort ports[NB_PORTS];
|
||||
USBPort *companion_ports[NB_PORTS];
|
||||
uint32_t usbsts_pending;
|
||||
uint32_t usbsts_frindex;
|
||||
EHCIQueueHead aqueues;
|
||||
EHCIQueueHead pqueues;
|
||||
|
||||
/* which address to look at next */
|
||||
uint32_t a_fetch_addr;
|
||||
uint32_t p_fetch_addr;
|
||||
|
||||
USBPacket ipacket;
|
||||
QEMUSGList isgl;
|
||||
|
||||
uint64_t last_run_ns;
|
||||
uint32_t async_stepdown;
|
||||
bool int_req_by_async;
|
||||
};
|
||||
|
||||
#define SET_LAST_RUN_CLOCK(s) \
|
||||
(s)->last_run_ns = qemu_get_clock_ns(vm_clock);
|
||||
|
||||
@ -506,8 +210,7 @@ static const char *state2str(uint32_t state)
|
||||
|
||||
static const char *addr2str(hwaddr addr)
|
||||
{
|
||||
return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names),
|
||||
addr + OPREGBASE);
|
||||
return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr);
|
||||
}
|
||||
|
||||
static void ehci_trace_usbsts(uint32_t mask, int state)
|
||||
@ -1115,7 +818,7 @@ static uint64_t ehci_opreg_read(void *ptr, hwaddr addr,
|
||||
uint32_t val;
|
||||
|
||||
val = s->opreg[addr >> 2];
|
||||
trace_usb_ehci_opreg_read(addr + OPREGBASE, addr2str(addr), val);
|
||||
trace_usb_ehci_opreg_read(addr + s->opregbase, addr2str(addr), val);
|
||||
return val;
|
||||
}
|
||||
|
||||
@ -1211,9 +914,9 @@ static void ehci_opreg_write(void *ptr, hwaddr addr,
|
||||
uint32_t old = *mmio;
|
||||
int i;
|
||||
|
||||
trace_usb_ehci_opreg_write(addr + OPREGBASE, addr2str(addr), val);
|
||||
trace_usb_ehci_opreg_write(addr + s->opregbase, addr2str(addr), val);
|
||||
|
||||
switch (addr + OPREGBASE) {
|
||||
switch (addr) {
|
||||
case USBCMD:
|
||||
if (val & USBCMD_HCRESET) {
|
||||
ehci_reset(s);
|
||||
@ -1291,7 +994,8 @@ static void ehci_opreg_write(void *ptr, hwaddr addr,
|
||||
}
|
||||
|
||||
*mmio = val;
|
||||
trace_usb_ehci_opreg_change(addr + OPREGBASE, addr2str(addr), *mmio, old);
|
||||
trace_usb_ehci_opreg_change(addr + s->opregbase, addr2str(addr),
|
||||
*mmio, old);
|
||||
}
|
||||
|
||||
|
||||
@ -1304,7 +1008,7 @@ static inline int get_dwords(EHCIState *ehci, uint32_t addr,
|
||||
int i;
|
||||
|
||||
for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
|
||||
pci_dma_read(&ehci->dev, addr, buf, sizeof(*buf));
|
||||
dma_memory_read(ehci->dma, addr, buf, sizeof(*buf));
|
||||
*buf = le32_to_cpu(*buf);
|
||||
}
|
||||
|
||||
@ -1319,7 +1023,7 @@ static inline int put_dwords(EHCIState *ehci, uint32_t addr,
|
||||
|
||||
for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
|
||||
uint32_t tmp = cpu_to_le32(*buf);
|
||||
pci_dma_write(&ehci->dev, addr, &tmp, sizeof(tmp));
|
||||
dma_memory_write(ehci->dma, addr, &tmp, sizeof(tmp));
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -1402,7 +1106,7 @@ static int ehci_init_transfer(EHCIPacket *p)
|
||||
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);
|
||||
qemu_sglist_init(&p->sgl, 5, p->queue->ehci->dma);
|
||||
|
||||
while (bytes > 0) {
|
||||
if (cpage > 4) {
|
||||
@ -1647,7 +1351,7 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||
return USB_RET_PROCERR;
|
||||
}
|
||||
|
||||
pci_dma_sglist_init(&ehci->isgl, &ehci->dev, 2);
|
||||
qemu_sglist_init(&ehci->isgl, 2, ehci->dma);
|
||||
if (off + len > 4096) {
|
||||
/* transfer crosses page border */
|
||||
uint32_t len2 = off + len - 4096;
|
||||
@ -2402,7 +2106,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
|
||||
}
|
||||
list |= ((ehci->frindex & 0x1ff8) >> 1);
|
||||
|
||||
pci_dma_read(&ehci->dev, list, &entry, sizeof entry);
|
||||
dma_memory_read(ehci->dma, list, &entry, sizeof entry);
|
||||
entry = le32_to_cpu(entry);
|
||||
|
||||
DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n",
|
||||
@ -2552,8 +2256,6 @@ static const MemoryRegionOps ehci_mmio_port_ops = {
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static int usb_ehci_initfn(PCIDevice *dev);
|
||||
|
||||
static USBPortOps ehci_port_ops = {
|
||||
.attach = ehci_attach,
|
||||
.detach = ehci_detach,
|
||||
@ -2612,13 +2314,12 @@ static void usb_ehci_vm_state_change(void *opaque, int running, RunState state)
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ehci = {
|
||||
.name = "ehci",
|
||||
const VMStateDescription vmstate_ehci = {
|
||||
.name = "ehci-core",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = usb_ehci_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(dev, EHCIState),
|
||||
/* mmio registers */
|
||||
VMSTATE_UINT32(usbcmd, EHCIState),
|
||||
VMSTATE_UINT32(usbsts, EHCIState),
|
||||
@ -2649,90 +2350,12 @@ static const VMStateDescription vmstate_ehci = {
|
||||
}
|
||||
};
|
||||
|
||||
static Property ehci_properties[] = {
|
||||
DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ehci_class_init(ObjectClass *klass, void *data)
|
||||
void usb_ehci_initfn(EHCIState *s, DeviceState *dev)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = usb_ehci_initfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
k->device_id = PCI_DEVICE_ID_INTEL_82801D; /* ich4 */
|
||||
k->revision = 0x10;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
dc->vmsd = &vmstate_ehci;
|
||||
dc->props = ehci_properties;
|
||||
}
|
||||
|
||||
static TypeInfo ehci_info = {
|
||||
.name = "usb-ehci",
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(EHCIState),
|
||||
.class_init = ehci_class_init,
|
||||
};
|
||||
|
||||
static void ich9_ehci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = usb_ehci_initfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
k->device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1;
|
||||
k->revision = 0x03;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
dc->vmsd = &vmstate_ehci;
|
||||
dc->props = ehci_properties;
|
||||
}
|
||||
|
||||
static TypeInfo ich9_ehci_info = {
|
||||
.name = "ich9-usb-ehci1",
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(EHCIState),
|
||||
.class_init = ich9_ehci_class_init,
|
||||
};
|
||||
|
||||
static int usb_ehci_initfn(PCIDevice *dev)
|
||||
{
|
||||
EHCIState *s = DO_UPCAST(EHCIState, dev, dev);
|
||||
uint8_t *pci_conf = s->dev.config;
|
||||
int i;
|
||||
|
||||
pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20);
|
||||
|
||||
/* capabilities pointer */
|
||||
pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00);
|
||||
//pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50);
|
||||
|
||||
pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */
|
||||
pci_set_byte(&pci_conf[PCI_MIN_GNT], 0);
|
||||
pci_set_byte(&pci_conf[PCI_MAX_LAT], 0);
|
||||
|
||||
// pci_conf[0x50] = 0x01; // power management caps
|
||||
|
||||
pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); // release number (2.1.4)
|
||||
pci_set_byte(&pci_conf[0x61], 0x20); // frame length adjustment (2.1.5)
|
||||
pci_set_word(&pci_conf[0x62], 0x00); // port wake up capability (2.1.6)
|
||||
|
||||
pci_conf[0x64] = 0x00;
|
||||
pci_conf[0x65] = 0x00;
|
||||
pci_conf[0x66] = 0x00;
|
||||
pci_conf[0x67] = 0x00;
|
||||
pci_conf[0x68] = 0x01;
|
||||
pci_conf[0x69] = 0x00;
|
||||
pci_conf[0x6a] = 0x00;
|
||||
pci_conf[0x6b] = 0x00; // USBLEGSUP
|
||||
pci_conf[0x6c] = 0x00;
|
||||
pci_conf[0x6d] = 0x00;
|
||||
pci_conf[0x6e] = 0x00;
|
||||
pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS
|
||||
|
||||
/* 2.2 host controller interface version */
|
||||
s->caps[0x00] = (uint8_t) OPREGBASE;
|
||||
s->caps[0x00] = (uint8_t)(s->opregbase - s->capsbase);
|
||||
s->caps[0x01] = 0x00;
|
||||
s->caps[0x02] = 0x00;
|
||||
s->caps[0x03] = 0x01; /* HC version */
|
||||
@ -2741,13 +2364,10 @@ static int usb_ehci_initfn(PCIDevice *dev)
|
||||
s->caps[0x06] = 0x00;
|
||||
s->caps[0x07] = 0x00;
|
||||
s->caps[0x08] = 0x80; /* We can cache whole frame, no 64-bit */
|
||||
s->caps[0x09] = 0x68; /* EECP */
|
||||
s->caps[0x0a] = 0x00;
|
||||
s->caps[0x0b] = 0x00;
|
||||
|
||||
s->irq = s->dev.irq[3];
|
||||
|
||||
usb_bus_new(&s->bus, &ehci_bus_ops, &s->dev.qdev);
|
||||
usb_bus_new(&s->bus, &ehci_bus_ops, dev);
|
||||
for(i = 0; i < NB_PORTS; i++) {
|
||||
usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops,
|
||||
USB_SPEED_MASK_HIGH);
|
||||
@ -2765,29 +2385,18 @@ static int usb_ehci_initfn(PCIDevice *dev)
|
||||
|
||||
memory_region_init(&s->mem, "ehci", MMIO_SIZE);
|
||||
memory_region_init_io(&s->mem_caps, &ehci_mmio_caps_ops, s,
|
||||
"capabilities", OPREGBASE);
|
||||
"capabilities", CAPA_SIZE);
|
||||
memory_region_init_io(&s->mem_opreg, &ehci_mmio_opreg_ops, s,
|
||||
"operational", PORTSC_BEGIN - OPREGBASE);
|
||||
"operational", PORTSC_BEGIN);
|
||||
memory_region_init_io(&s->mem_ports, &ehci_mmio_port_ops, s,
|
||||
"ports", PORTSC_END - PORTSC_BEGIN);
|
||||
|
||||
memory_region_add_subregion(&s->mem, 0, &s->mem_caps);
|
||||
memory_region_add_subregion(&s->mem, OPREGBASE, &s->mem_opreg);
|
||||
memory_region_add_subregion(&s->mem, PORTSC_BEGIN, &s->mem_ports);
|
||||
|
||||
pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
|
||||
|
||||
return 0;
|
||||
memory_region_add_subregion(&s->mem, s->capsbase, &s->mem_caps);
|
||||
memory_region_add_subregion(&s->mem, s->opregbase, &s->mem_opreg);
|
||||
memory_region_add_subregion(&s->mem, s->opregbase + PORTSC_BEGIN,
|
||||
&s->mem_ports);
|
||||
}
|
||||
|
||||
static void ehci_register_types(void)
|
||||
{
|
||||
type_register_static(&ehci_info);
|
||||
type_register_static(&ich9_ehci_info);
|
||||
}
|
||||
|
||||
type_init(ehci_register_types)
|
||||
|
||||
/*
|
||||
* vim: expandtab ts=4
|
||||
*/
|
||||
|
320
hw/usb/hcd-ehci.h
Normal file
320
hw/usb/hcd-ehci.h
Normal file
@ -0,0 +1,320 @@
|
||||
/*
|
||||
* QEMU USB EHCI Emulation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or(at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "hw/usb.h"
|
||||
#include "monitor.h"
|
||||
#include "trace.h"
|
||||
#include "dma.h"
|
||||
#include "sysemu.h"
|
||||
|
||||
#ifndef EHCI_DEBUG
|
||||
#define EHCI_DEBUG 0
|
||||
#endif
|
||||
|
||||
#if EHCI_DEBUG
|
||||
#define DPRINTF printf
|
||||
#else
|
||||
#define DPRINTF(...)
|
||||
#endif
|
||||
|
||||
#define MMIO_SIZE 0x1000
|
||||
#define CAPA_SIZE 0x10
|
||||
|
||||
#define PORTSC 0x0044
|
||||
#define PORTSC_BEGIN PORTSC
|
||||
#define PORTSC_END (PORTSC + 4 * NB_PORTS)
|
||||
|
||||
#define NB_PORTS 6 /* Number of downstream ports */
|
||||
|
||||
typedef struct EHCIPacket EHCIPacket;
|
||||
typedef struct EHCIQueue EHCIQueue;
|
||||
typedef struct EHCIState EHCIState;
|
||||
|
||||
/* EHCI spec version 1.0 Section 3.3
|
||||
*/
|
||||
typedef struct EHCIitd {
|
||||
uint32_t next;
|
||||
|
||||
uint32_t transact[8];
|
||||
#define ITD_XACT_ACTIVE (1 << 31)
|
||||
#define ITD_XACT_DBERROR (1 << 30)
|
||||
#define ITD_XACT_BABBLE (1 << 29)
|
||||
#define ITD_XACT_XACTERR (1 << 28)
|
||||
#define ITD_XACT_LENGTH_MASK 0x0fff0000
|
||||
#define ITD_XACT_LENGTH_SH 16
|
||||
#define ITD_XACT_IOC (1 << 15)
|
||||
#define ITD_XACT_PGSEL_MASK 0x00007000
|
||||
#define ITD_XACT_PGSEL_SH 12
|
||||
#define ITD_XACT_OFFSET_MASK 0x00000fff
|
||||
|
||||
uint32_t bufptr[7];
|
||||
#define ITD_BUFPTR_MASK 0xfffff000
|
||||
#define ITD_BUFPTR_SH 12
|
||||
#define ITD_BUFPTR_EP_MASK 0x00000f00
|
||||
#define ITD_BUFPTR_EP_SH 8
|
||||
#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f
|
||||
#define ITD_BUFPTR_DEVADDR_SH 0
|
||||
#define ITD_BUFPTR_DIRECTION (1 << 11)
|
||||
#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff
|
||||
#define ITD_BUFPTR_MAXPKT_SH 0
|
||||
#define ITD_BUFPTR_MULT_MASK 0x00000003
|
||||
#define ITD_BUFPTR_MULT_SH 0
|
||||
} EHCIitd;
|
||||
|
||||
/* EHCI spec version 1.0 Section 3.4
|
||||
*/
|
||||
typedef struct EHCIsitd {
|
||||
uint32_t next; /* Standard next link pointer */
|
||||
uint32_t epchar;
|
||||
#define SITD_EPCHAR_IO (1 << 31)
|
||||
#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000
|
||||
#define SITD_EPCHAR_PORTNUM_SH 24
|
||||
#define SITD_EPCHAR_HUBADD_MASK 0x007f0000
|
||||
#define SITD_EPCHAR_HUBADDR_SH 16
|
||||
#define SITD_EPCHAR_EPNUM_MASK 0x00000f00
|
||||
#define SITD_EPCHAR_EPNUM_SH 8
|
||||
#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f
|
||||
|
||||
uint32_t uframe;
|
||||
#define SITD_UFRAME_CMASK_MASK 0x0000ff00
|
||||
#define SITD_UFRAME_CMASK_SH 8
|
||||
#define SITD_UFRAME_SMASK_MASK 0x000000ff
|
||||
|
||||
uint32_t results;
|
||||
#define SITD_RESULTS_IOC (1 << 31)
|
||||
#define SITD_RESULTS_PGSEL (1 << 30)
|
||||
#define SITD_RESULTS_TBYTES_MASK 0x03ff0000
|
||||
#define SITD_RESULTS_TYBYTES_SH 16
|
||||
#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00
|
||||
#define SITD_RESULTS_CPROGMASK_SH 8
|
||||
#define SITD_RESULTS_ACTIVE (1 << 7)
|
||||
#define SITD_RESULTS_ERR (1 << 6)
|
||||
#define SITD_RESULTS_DBERR (1 << 5)
|
||||
#define SITD_RESULTS_BABBLE (1 << 4)
|
||||
#define SITD_RESULTS_XACTERR (1 << 3)
|
||||
#define SITD_RESULTS_MISSEDUF (1 << 2)
|
||||
#define SITD_RESULTS_SPLITXSTATE (1 << 1)
|
||||
|
||||
uint32_t bufptr[2];
|
||||
#define SITD_BUFPTR_MASK 0xfffff000
|
||||
#define SITD_BUFPTR_CURROFF_MASK 0x00000fff
|
||||
#define SITD_BUFPTR_TPOS_MASK 0x00000018
|
||||
#define SITD_BUFPTR_TPOS_SH 3
|
||||
#define SITD_BUFPTR_TCNT_MASK 0x00000007
|
||||
|
||||
uint32_t backptr; /* Standard next link pointer */
|
||||
} EHCIsitd;
|
||||
|
||||
/* EHCI spec version 1.0 Section 3.5
|
||||
*/
|
||||
typedef struct EHCIqtd {
|
||||
uint32_t next; /* Standard next link pointer */
|
||||
uint32_t altnext; /* Standard next link pointer */
|
||||
uint32_t token;
|
||||
#define QTD_TOKEN_DTOGGLE (1 << 31)
|
||||
#define QTD_TOKEN_TBYTES_MASK 0x7fff0000
|
||||
#define QTD_TOKEN_TBYTES_SH 16
|
||||
#define QTD_TOKEN_IOC (1 << 15)
|
||||
#define QTD_TOKEN_CPAGE_MASK 0x00007000
|
||||
#define QTD_TOKEN_CPAGE_SH 12
|
||||
#define QTD_TOKEN_CERR_MASK 0x00000c00
|
||||
#define QTD_TOKEN_CERR_SH 10
|
||||
#define QTD_TOKEN_PID_MASK 0x00000300
|
||||
#define QTD_TOKEN_PID_SH 8
|
||||
#define QTD_TOKEN_ACTIVE (1 << 7)
|
||||
#define QTD_TOKEN_HALT (1 << 6)
|
||||
#define QTD_TOKEN_DBERR (1 << 5)
|
||||
#define QTD_TOKEN_BABBLE (1 << 4)
|
||||
#define QTD_TOKEN_XACTERR (1 << 3)
|
||||
#define QTD_TOKEN_MISSEDUF (1 << 2)
|
||||
#define QTD_TOKEN_SPLITXSTATE (1 << 1)
|
||||
#define QTD_TOKEN_PING (1 << 0)
|
||||
|
||||
uint32_t bufptr[5]; /* Standard buffer pointer */
|
||||
#define QTD_BUFPTR_MASK 0xfffff000
|
||||
#define QTD_BUFPTR_SH 12
|
||||
} EHCIqtd;
|
||||
|
||||
/* EHCI spec version 1.0 Section 3.6
|
||||
*/
|
||||
typedef struct EHCIqh {
|
||||
uint32_t next; /* Standard next link pointer */
|
||||
|
||||
/* endpoint characteristics */
|
||||
uint32_t epchar;
|
||||
#define QH_EPCHAR_RL_MASK 0xf0000000
|
||||
#define QH_EPCHAR_RL_SH 28
|
||||
#define QH_EPCHAR_C (1 << 27)
|
||||
#define QH_EPCHAR_MPLEN_MASK 0x07FF0000
|
||||
#define QH_EPCHAR_MPLEN_SH 16
|
||||
#define QH_EPCHAR_H (1 << 15)
|
||||
#define QH_EPCHAR_DTC (1 << 14)
|
||||
#define QH_EPCHAR_EPS_MASK 0x00003000
|
||||
#define QH_EPCHAR_EPS_SH 12
|
||||
#define EHCI_QH_EPS_FULL 0
|
||||
#define EHCI_QH_EPS_LOW 1
|
||||
#define EHCI_QH_EPS_HIGH 2
|
||||
#define EHCI_QH_EPS_RESERVED 3
|
||||
|
||||
#define QH_EPCHAR_EP_MASK 0x00000f00
|
||||
#define QH_EPCHAR_EP_SH 8
|
||||
#define QH_EPCHAR_I (1 << 7)
|
||||
#define QH_EPCHAR_DEVADDR_MASK 0x0000007f
|
||||
#define QH_EPCHAR_DEVADDR_SH 0
|
||||
|
||||
/* endpoint capabilities */
|
||||
uint32_t epcap;
|
||||
#define QH_EPCAP_MULT_MASK 0xc0000000
|
||||
#define QH_EPCAP_MULT_SH 30
|
||||
#define QH_EPCAP_PORTNUM_MASK 0x3f800000
|
||||
#define QH_EPCAP_PORTNUM_SH 23
|
||||
#define QH_EPCAP_HUBADDR_MASK 0x007f0000
|
||||
#define QH_EPCAP_HUBADDR_SH 16
|
||||
#define QH_EPCAP_CMASK_MASK 0x0000ff00
|
||||
#define QH_EPCAP_CMASK_SH 8
|
||||
#define QH_EPCAP_SMASK_MASK 0x000000ff
|
||||
#define QH_EPCAP_SMASK_SH 0
|
||||
|
||||
uint32_t current_qtd; /* Standard next link pointer */
|
||||
uint32_t next_qtd; /* Standard next link pointer */
|
||||
uint32_t altnext_qtd;
|
||||
#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e
|
||||
#define QH_ALTNEXT_NAKCNT_SH 1
|
||||
|
||||
uint32_t token; /* Same as QTD token */
|
||||
uint32_t bufptr[5]; /* Standard buffer pointer */
|
||||
#define BUFPTR_CPROGMASK_MASK 0x000000ff
|
||||
#define BUFPTR_FRAMETAG_MASK 0x0000001f
|
||||
#define BUFPTR_SBYTES_MASK 0x00000fe0
|
||||
#define BUFPTR_SBYTES_SH 5
|
||||
} EHCIqh;
|
||||
|
||||
/* EHCI spec version 1.0 Section 3.7
|
||||
*/
|
||||
typedef struct EHCIfstn {
|
||||
uint32_t next; /* Standard next link pointer */
|
||||
uint32_t backptr; /* Standard next link pointer */
|
||||
} EHCIfstn;
|
||||
|
||||
enum async_state {
|
||||
EHCI_ASYNC_NONE = 0,
|
||||
EHCI_ASYNC_INITIALIZED,
|
||||
EHCI_ASYNC_INFLIGHT,
|
||||
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;
|
||||
enum async_state async;
|
||||
int usb_status;
|
||||
};
|
||||
|
||||
struct EHCIQueue {
|
||||
EHCIState *ehci;
|
||||
QTAILQ_ENTRY(EHCIQueue) next;
|
||||
uint32_t seen;
|
||||
uint64_t ts;
|
||||
int async;
|
||||
int transact_ctr;
|
||||
|
||||
/* 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 */
|
||||
uint32_t qtdaddr; /* address QTD read from */
|
||||
USBDevice *dev;
|
||||
QTAILQ_HEAD(pkts_head, EHCIPacket) packets;
|
||||
};
|
||||
|
||||
typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
|
||||
|
||||
struct EHCIState {
|
||||
USBBus bus;
|
||||
qemu_irq irq;
|
||||
MemoryRegion mem;
|
||||
DMAContext *dma;
|
||||
MemoryRegion mem_caps;
|
||||
MemoryRegion mem_opreg;
|
||||
MemoryRegion mem_ports;
|
||||
int companion_count;
|
||||
uint16_t capsbase;
|
||||
uint16_t opregbase;
|
||||
|
||||
/* properties */
|
||||
uint32_t maxframes;
|
||||
|
||||
/*
|
||||
* EHCI spec version 1.0 Section 2.3
|
||||
* Host Controller Operational Registers
|
||||
*/
|
||||
uint8_t caps[CAPA_SIZE];
|
||||
union {
|
||||
uint32_t opreg[PORTSC_BEGIN/sizeof(uint32_t)];
|
||||
struct {
|
||||
uint32_t usbcmd;
|
||||
uint32_t usbsts;
|
||||
uint32_t usbintr;
|
||||
uint32_t frindex;
|
||||
uint32_t ctrldssegment;
|
||||
uint32_t periodiclistbase;
|
||||
uint32_t asynclistaddr;
|
||||
uint32_t notused[9];
|
||||
uint32_t configflag;
|
||||
};
|
||||
};
|
||||
uint32_t portsc[NB_PORTS];
|
||||
|
||||
/*
|
||||
* Internal states, shadow registers, etc
|
||||
*/
|
||||
QEMUTimer *frame_timer;
|
||||
QEMUBH *async_bh;
|
||||
uint32_t astate; /* Current state in asynchronous schedule */
|
||||
uint32_t pstate; /* Current state in periodic schedule */
|
||||
USBPort ports[NB_PORTS];
|
||||
USBPort *companion_ports[NB_PORTS];
|
||||
uint32_t usbsts_pending;
|
||||
uint32_t usbsts_frindex;
|
||||
EHCIQueueHead aqueues;
|
||||
EHCIQueueHead pqueues;
|
||||
|
||||
/* which address to look at next */
|
||||
uint32_t a_fetch_addr;
|
||||
uint32_t p_fetch_addr;
|
||||
|
||||
USBPacket ipacket;
|
||||
QEMUSGList isgl;
|
||||
|
||||
uint64_t last_run_ns;
|
||||
uint32_t async_stepdown;
|
||||
bool int_req_by_async;
|
||||
};
|
||||
|
||||
extern const VMStateDescription vmstate_ehci;
|
||||
|
||||
void usb_ehci_initfn(EHCIState *s, DeviceState *dev);
|
@ -88,6 +88,23 @@ enum {
|
||||
typedef struct UHCIState UHCIState;
|
||||
typedef struct UHCIAsync UHCIAsync;
|
||||
typedef struct UHCIQueue UHCIQueue;
|
||||
typedef struct UHCIInfo UHCIInfo;
|
||||
typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass;
|
||||
|
||||
struct UHCIInfo {
|
||||
const char *name;
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint8_t revision;
|
||||
uint8_t irq_pin;
|
||||
int (*initfn)(PCIDevice *dev);
|
||||
bool unplug;
|
||||
};
|
||||
|
||||
struct UHCIPCIDeviceClass {
|
||||
PCIDeviceClass parent_class;
|
||||
UHCIInfo info;
|
||||
};
|
||||
|
||||
/*
|
||||
* Pending async transaction.
|
||||
@ -718,9 +735,52 @@ static void uhci_read_td(UHCIState *s, UHCI_TD *td, uint32_t link)
|
||||
le32_to_cpus(&td->buffer);
|
||||
}
|
||||
|
||||
static int uhci_handle_td_error(UHCIState *s, UHCI_TD *td, uint32_t td_addr,
|
||||
int status, uint32_t *int_mask)
|
||||
{
|
||||
uint32_t queue_token = uhci_queue_token(td);
|
||||
int ret;
|
||||
|
||||
switch (status) {
|
||||
case USB_RET_NAK:
|
||||
td->ctrl |= TD_CTRL_NAK;
|
||||
return TD_RESULT_NEXT_QH;
|
||||
|
||||
case USB_RET_STALL:
|
||||
td->ctrl |= TD_CTRL_STALL;
|
||||
trace_usb_uhci_packet_complete_stall(queue_token, td_addr);
|
||||
ret = TD_RESULT_NEXT_QH;
|
||||
break;
|
||||
|
||||
case USB_RET_BABBLE:
|
||||
td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
|
||||
/* frame interrupted */
|
||||
trace_usb_uhci_packet_complete_babble(queue_token, td_addr);
|
||||
ret = TD_RESULT_STOP_FRAME;
|
||||
break;
|
||||
|
||||
case USB_RET_IOERROR:
|
||||
case USB_RET_NODEV:
|
||||
default:
|
||||
td->ctrl |= TD_CTRL_TIMEOUT;
|
||||
td->ctrl &= ~(3 << TD_CTRL_ERROR_SHIFT);
|
||||
trace_usb_uhci_packet_complete_error(queue_token, td_addr);
|
||||
ret = TD_RESULT_NEXT_QH;
|
||||
break;
|
||||
}
|
||||
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
s->status |= UHCI_STS_USBERR;
|
||||
if (td->ctrl & TD_CTRL_IOC) {
|
||||
*int_mask |= 0x01;
|
||||
}
|
||||
uhci_update_irq(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask)
|
||||
{
|
||||
int len = 0, max_len, err, ret;
|
||||
int len = 0, max_len, ret;
|
||||
uint8_t pid;
|
||||
|
||||
max_len = ((td->token >> 21) + 1) & 0x7ff;
|
||||
@ -731,8 +791,9 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
|
||||
if (td->ctrl & TD_CTRL_IOS)
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret < 0) {
|
||||
return uhci_handle_td_error(s, td, async->td_addr, ret, int_mask);
|
||||
}
|
||||
|
||||
len = async->packet.result;
|
||||
td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
|
||||
@ -758,46 +819,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
|
||||
trace_usb_uhci_packet_complete_success(async->queue->token,
|
||||
async->td_addr);
|
||||
return TD_RESULT_COMPLETE;
|
||||
|
||||
out:
|
||||
switch(ret) {
|
||||
case USB_RET_NAK:
|
||||
td->ctrl |= TD_CTRL_NAK;
|
||||
return TD_RESULT_NEXT_QH;
|
||||
|
||||
case USB_RET_STALL:
|
||||
td->ctrl |= TD_CTRL_STALL;
|
||||
trace_usb_uhci_packet_complete_stall(async->queue->token,
|
||||
async->td_addr);
|
||||
err = TD_RESULT_NEXT_QH;
|
||||
break;
|
||||
|
||||
case USB_RET_BABBLE:
|
||||
td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
|
||||
/* frame interrupted */
|
||||
trace_usb_uhci_packet_complete_babble(async->queue->token,
|
||||
async->td_addr);
|
||||
err = TD_RESULT_STOP_FRAME;
|
||||
break;
|
||||
|
||||
case USB_RET_IOERROR:
|
||||
case USB_RET_NODEV:
|
||||
default:
|
||||
td->ctrl |= TD_CTRL_TIMEOUT;
|
||||
td->ctrl &= ~(3 << TD_CTRL_ERROR_SHIFT);
|
||||
trace_usb_uhci_packet_complete_error(async->queue->token,
|
||||
async->td_addr);
|
||||
err = TD_RESULT_NEXT_QH;
|
||||
break;
|
||||
}
|
||||
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
s->status |= UHCI_STS_USBERR;
|
||||
if (td->ctrl & TD_CTRL_IOC) {
|
||||
*int_mask |= 0x01;
|
||||
}
|
||||
uhci_update_irq(s);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
|
||||
@ -875,6 +896,11 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
|
||||
if (q == NULL) {
|
||||
USBDevice *dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
|
||||
USBEndpoint *ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
|
||||
|
||||
if (ep == NULL) {
|
||||
return uhci_handle_td_error(s, td, td_addr, USB_RET_NODEV,
|
||||
int_mask);
|
||||
}
|
||||
q = uhci_queue_new(s, qh_addr, td, ep);
|
||||
}
|
||||
async = uhci_async_alloc(q, td_addr);
|
||||
@ -1208,6 +1234,7 @@ static USBBusOps uhci_bus_ops = {
|
||||
static int usb_uhci_common_initfn(PCIDevice *dev)
|
||||
{
|
||||
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
|
||||
UHCIPCIDeviceClass *u = container_of(pc, UHCIPCIDeviceClass, parent_class);
|
||||
UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
|
||||
uint8_t *pci_conf = s->dev.config;
|
||||
int i;
|
||||
@ -1216,20 +1243,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
|
||||
/* TODO: reset value should be 0. */
|
||||
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;
|
||||
}
|
||||
s->irq_pin = u->info.irq_pin;
|
||||
pci_config_set_interrupt_pin(pci_conf, s->irq_pin + 1);
|
||||
|
||||
if (s->masterbus) {
|
||||
@ -1293,143 +1307,107 @@ static Property uhci_properties[] = {
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void piix3_uhci_class_init(ObjectClass *klass, void *data)
|
||||
static void uhci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
UHCIPCIDeviceClass *u = container_of(k, UHCIPCIDeviceClass, parent_class);
|
||||
UHCIInfo *info = data;
|
||||
|
||||
k->init = usb_uhci_common_initfn;
|
||||
k->exit = usb_uhci_exit;
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
k->device_id = PCI_DEVICE_ID_INTEL_82371SB_2;
|
||||
k->revision = 0x01;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
k->init = info->initfn ? info->initfn : usb_uhci_common_initfn;
|
||||
k->exit = info->unplug ? usb_uhci_exit : NULL;
|
||||
k->vendor_id = info->vendor_id;
|
||||
k->device_id = info->device_id;
|
||||
k->revision = info->revision;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
dc->vmsd = &vmstate_uhci;
|
||||
dc->props = uhci_properties;
|
||||
u->info = *info;
|
||||
}
|
||||
|
||||
static TypeInfo piix3_uhci_info = {
|
||||
.name = "piix3-usb-uhci",
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(UHCIState),
|
||||
.class_init = piix3_uhci_class_init,
|
||||
};
|
||||
|
||||
static void piix4_uhci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = usb_uhci_common_initfn;
|
||||
k->exit = usb_uhci_exit;
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
k->device_id = PCI_DEVICE_ID_INTEL_82371AB_2;
|
||||
k->revision = 0x01;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
dc->vmsd = &vmstate_uhci;
|
||||
dc->props = uhci_properties;
|
||||
}
|
||||
|
||||
static TypeInfo piix4_uhci_info = {
|
||||
.name = "piix4-usb-uhci",
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(UHCIState),
|
||||
.class_init = piix4_uhci_class_init,
|
||||
};
|
||||
|
||||
static void vt82c686b_uhci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = usb_uhci_vt82c686b_initfn;
|
||||
k->exit = usb_uhci_exit;
|
||||
k->vendor_id = PCI_VENDOR_ID_VIA;
|
||||
k->device_id = PCI_DEVICE_ID_VIA_UHCI;
|
||||
k->revision = 0x01;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
dc->vmsd = &vmstate_uhci;
|
||||
dc->props = uhci_properties;
|
||||
}
|
||||
|
||||
static TypeInfo vt82c686b_uhci_info = {
|
||||
.name = "vt82c686b-usb-uhci",
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(UHCIState),
|
||||
.class_init = vt82c686b_uhci_class_init,
|
||||
};
|
||||
|
||||
static void ich9_uhci1_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = usb_uhci_common_initfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1;
|
||||
k->revision = 0x03;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
dc->vmsd = &vmstate_uhci;
|
||||
dc->props = uhci_properties;
|
||||
}
|
||||
|
||||
static TypeInfo ich9_uhci1_info = {
|
||||
.name = "ich9-usb-uhci1",
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(UHCIState),
|
||||
.class_init = ich9_uhci1_class_init,
|
||||
};
|
||||
|
||||
static void ich9_uhci2_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = usb_uhci_common_initfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2;
|
||||
k->revision = 0x03;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
dc->vmsd = &vmstate_uhci;
|
||||
dc->props = uhci_properties;
|
||||
}
|
||||
|
||||
static TypeInfo ich9_uhci2_info = {
|
||||
.name = "ich9-usb-uhci2",
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(UHCIState),
|
||||
.class_init = ich9_uhci2_class_init,
|
||||
};
|
||||
|
||||
static void ich9_uhci3_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = usb_uhci_common_initfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3;
|
||||
k->revision = 0x03;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
dc->vmsd = &vmstate_uhci;
|
||||
dc->props = uhci_properties;
|
||||
}
|
||||
|
||||
static TypeInfo ich9_uhci3_info = {
|
||||
.name = "ich9-usb-uhci3",
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(UHCIState),
|
||||
.class_init = ich9_uhci3_class_init,
|
||||
static UHCIInfo uhci_info[] = {
|
||||
{
|
||||
.name = "piix3-usb-uhci",
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82371SB_2,
|
||||
.revision = 0x01,
|
||||
.irq_pin = 3,
|
||||
.unplug = true,
|
||||
},{
|
||||
.name = "piix4-usb-uhci",
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82371AB_2,
|
||||
.revision = 0x01,
|
||||
.irq_pin = 3,
|
||||
.unplug = true,
|
||||
},{
|
||||
.name = "vt82c686b-usb-uhci",
|
||||
.vendor_id = PCI_VENDOR_ID_VIA,
|
||||
.device_id = PCI_DEVICE_ID_VIA_UHCI,
|
||||
.revision = 0x01,
|
||||
.irq_pin = 3,
|
||||
.initfn = usb_uhci_vt82c686b_initfn,
|
||||
.unplug = true,
|
||||
},{
|
||||
.name = "ich9-usb-uhci1", /* 00:1d.0 */
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1,
|
||||
.revision = 0x03,
|
||||
.irq_pin = 0,
|
||||
.unplug = false,
|
||||
},{
|
||||
.name = "ich9-usb-uhci2", /* 00:1d.1 */
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2,
|
||||
.revision = 0x03,
|
||||
.irq_pin = 1,
|
||||
.unplug = false,
|
||||
},{
|
||||
.name = "ich9-usb-uhci3", /* 00:1d.2 */
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3,
|
||||
.revision = 0x03,
|
||||
.irq_pin = 2,
|
||||
.unplug = false,
|
||||
},{
|
||||
.name = "ich9-usb-uhci4", /* 00:1a.0 */
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI4,
|
||||
.revision = 0x03,
|
||||
.irq_pin = 0,
|
||||
.unplug = false,
|
||||
},{
|
||||
.name = "ich9-usb-uhci5", /* 00:1a.1 */
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI5,
|
||||
.revision = 0x03,
|
||||
.irq_pin = 1,
|
||||
.unplug = false,
|
||||
},{
|
||||
.name = "ich9-usb-uhci6", /* 00:1a.2 */
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI6,
|
||||
.revision = 0x03,
|
||||
.irq_pin = 2,
|
||||
.unplug = false,
|
||||
}
|
||||
};
|
||||
|
||||
static void uhci_register_types(void)
|
||||
{
|
||||
type_register_static(&piix3_uhci_info);
|
||||
type_register_static(&piix4_uhci_info);
|
||||
type_register_static(&vt82c686b_uhci_info);
|
||||
type_register_static(&ich9_uhci1_info);
|
||||
type_register_static(&ich9_uhci2_info);
|
||||
type_register_static(&ich9_uhci3_info);
|
||||
TypeInfo uhci_type_info = {
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(UHCIState),
|
||||
.class_size = sizeof(UHCIPCIDeviceClass),
|
||||
.class_init = uhci_class_init,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(uhci_info); i++) {
|
||||
uhci_type_info.name = uhci_info[i].name;
|
||||
uhci_type_info.class_data = uhci_info + i;
|
||||
type_register(&uhci_type_info);
|
||||
}
|
||||
}
|
||||
|
||||
type_init(uhci_register_types)
|
||||
|
@ -146,6 +146,21 @@ typedef struct XHCITRB {
|
||||
bool ccs;
|
||||
} XHCITRB;
|
||||
|
||||
enum {
|
||||
PLS_U0 = 0,
|
||||
PLS_U1 = 1,
|
||||
PLS_U2 = 2,
|
||||
PLS_U3 = 3,
|
||||
PLS_DISABLED = 4,
|
||||
PLS_RX_DETECT = 5,
|
||||
PLS_INACTIVE = 6,
|
||||
PLS_POLLING = 7,
|
||||
PLS_RECOVERY = 8,
|
||||
PLS_HOT_RESET = 9,
|
||||
PLS_COMPILANCE_MODE = 10,
|
||||
PLS_TEST_MODE = 11,
|
||||
PLS_RESUME = 15,
|
||||
};
|
||||
|
||||
typedef enum TRBType {
|
||||
TRB_RESERVED = 0,
|
||||
@ -287,6 +302,16 @@ typedef enum TRBCCode {
|
||||
|
||||
typedef struct XHCIState XHCIState;
|
||||
|
||||
#define get_field(data, field) \
|
||||
(((data) >> field##_SHIFT) & field##_MASK)
|
||||
|
||||
#define set_field(data, newval, field) do { \
|
||||
uint32_t val = *data; \
|
||||
val &= ~(field##_MASK << field##_SHIFT); \
|
||||
val |= ((newval) & field##_MASK) << field##_SHIFT; \
|
||||
*data = val; \
|
||||
} while (0)
|
||||
|
||||
typedef enum EPType {
|
||||
ET_INVALID = 0,
|
||||
ET_ISO_OUT,
|
||||
@ -458,6 +483,8 @@ enum xhci_flags {
|
||||
|
||||
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
|
||||
unsigned int epid);
|
||||
static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
|
||||
unsigned int epid);
|
||||
static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v);
|
||||
static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v);
|
||||
|
||||
@ -1050,8 +1077,7 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
|
||||
|
||||
slot = &xhci->slots[slotid-1];
|
||||
if (slot->eps[epid-1]) {
|
||||
fprintf(stderr, "xhci: slot %d ep %d already enabled!\n", slotid, epid);
|
||||
return CC_TRB_ERROR;
|
||||
xhci_disable_ep(xhci, slotid, epid);
|
||||
}
|
||||
|
||||
epctx = g_malloc(sizeof(XHCIEPContext));
|
||||
@ -1894,6 +1920,9 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
|
||||
}
|
||||
|
||||
for (i = 0; i < xhci->numslots; i++) {
|
||||
if (i == slotid-1) {
|
||||
continue;
|
||||
}
|
||||
if (xhci->slots[i].uport == uport) {
|
||||
fprintf(stderr, "xhci: port %s already assigned to slot %d\n",
|
||||
uport->path, i+1);
|
||||
@ -1911,6 +1940,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
|
||||
slot->devaddr = xhci->devaddr++;
|
||||
slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot->devaddr;
|
||||
DPRINTF("xhci: device address is %d\n", slot->devaddr);
|
||||
usb_device_reset(dev);
|
||||
usb_device_handle_control(dev, NULL,
|
||||
DeviceOutRequest | USB_REQ_SET_ADDRESS,
|
||||
slot->devaddr, 0, 0, NULL);
|
||||
@ -2312,35 +2342,86 @@ static void xhci_process_commands(XHCIState *xhci)
|
||||
}
|
||||
}
|
||||
|
||||
static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
|
||||
static bool xhci_port_have_device(XHCIPort *port)
|
||||
{
|
||||
if (!port->uport->dev || !port->uport->dev->attached) {
|
||||
return false; /* no device present */
|
||||
}
|
||||
if (!((1 << port->uport->dev->speed) & port->speedmask)) {
|
||||
return false; /* speed mismatch */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void xhci_port_notify(XHCIPort *port, uint32_t bits)
|
||||
{
|
||||
XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
|
||||
port->portnr << 24 };
|
||||
|
||||
if ((port->portsc & bits) == bits) {
|
||||
return;
|
||||
}
|
||||
port->portsc |= bits;
|
||||
if (!xhci_running(port->xhci)) {
|
||||
return;
|
||||
}
|
||||
xhci_event(port->xhci, &ev, 0);
|
||||
}
|
||||
|
||||
static void xhci_port_update(XHCIPort *port, int is_detach)
|
||||
{
|
||||
uint32_t pls = PLS_RX_DETECT;
|
||||
|
||||
port->portsc = PORTSC_PP;
|
||||
if (port->uport->dev && port->uport->dev->attached && !is_detach &&
|
||||
(1 << port->uport->dev->speed) & port->speedmask) {
|
||||
if (!is_detach && xhci_port_have_device(port)) {
|
||||
port->portsc |= PORTSC_CCS;
|
||||
switch (port->uport->dev->speed) {
|
||||
case USB_SPEED_LOW:
|
||||
port->portsc |= PORTSC_SPEED_LOW;
|
||||
pls = PLS_POLLING;
|
||||
break;
|
||||
case USB_SPEED_FULL:
|
||||
port->portsc |= PORTSC_SPEED_FULL;
|
||||
pls = PLS_POLLING;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
port->portsc |= PORTSC_SPEED_HIGH;
|
||||
pls = PLS_POLLING;
|
||||
break;
|
||||
case USB_SPEED_SUPER:
|
||||
port->portsc |= PORTSC_SPEED_SUPER;
|
||||
port->portsc |= PORTSC_PED;
|
||||
pls = PLS_U0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
set_field(&port->portsc, pls, PORTSC_PLS);
|
||||
trace_usb_xhci_port_link(port->portnr, pls);
|
||||
xhci_port_notify(port, PORTSC_CSC);
|
||||
}
|
||||
|
||||
if (xhci_running(xhci)) {
|
||||
port->portsc |= PORTSC_CSC;
|
||||
XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
|
||||
port->portnr << 24};
|
||||
xhci_event(xhci, &ev, 0);
|
||||
DPRINTF("xhci: port change event for port %d\n", port->portnr);
|
||||
static void xhci_port_reset(XHCIPort *port)
|
||||
{
|
||||
trace_usb_xhci_port_reset(port->portnr);
|
||||
|
||||
if (!xhci_port_have_device(port)) {
|
||||
return;
|
||||
}
|
||||
|
||||
usb_device_reset(port->uport->dev);
|
||||
|
||||
switch (port->uport->dev->speed) {
|
||||
case USB_SPEED_LOW:
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_HIGH:
|
||||
set_field(&port->portsc, PLS_U0, PORTSC_PLS);
|
||||
trace_usb_xhci_port_link(port->portnr, PLS_U0);
|
||||
port->portsc |= PORTSC_PED;
|
||||
break;
|
||||
}
|
||||
|
||||
port->portsc &= ~PORTSC_PR;
|
||||
xhci_port_notify(port, PORTSC_PRC);
|
||||
}
|
||||
|
||||
static void xhci_reset(DeviceState *dev)
|
||||
@ -2368,7 +2449,7 @@ static void xhci_reset(DeviceState *dev)
|
||||
}
|
||||
|
||||
for (i = 0; i < xhci->numports; i++) {
|
||||
xhci_update_port(xhci, xhci->ports + i, 0);
|
||||
xhci_port_update(xhci->ports + i, 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < xhci->numintrs; i++) {
|
||||
@ -2499,19 +2580,18 @@ static void xhci_port_write(void *ptr, hwaddr reg,
|
||||
PORTSC_PRC|PORTSC_PLC|PORTSC_CEC));
|
||||
if (val & PORTSC_LWS) {
|
||||
/* overwrite PLS only when LWS=1 */
|
||||
portsc &= ~(PORTSC_PLS_MASK << PORTSC_PLS_SHIFT);
|
||||
portsc |= val & (PORTSC_PLS_MASK << PORTSC_PLS_SHIFT);
|
||||
uint32_t pls = get_field(val, PORTSC_PLS);
|
||||
set_field(&portsc, pls, PORTSC_PLS);
|
||||
trace_usb_xhci_port_link(port->portnr, pls);
|
||||
}
|
||||
/* read/write bits */
|
||||
portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE);
|
||||
portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE));
|
||||
port->portsc = portsc;
|
||||
/* write-1-to-start bits */
|
||||
if (val & PORTSC_PR) {
|
||||
DPRINTF("xhci: port %d reset\n", port);
|
||||
usb_device_reset(port->uport->dev);
|
||||
portsc |= PORTSC_PRC | PORTSC_PED;
|
||||
xhci_port_reset(port);
|
||||
}
|
||||
port->portsc = portsc;
|
||||
break;
|
||||
case 0x04: /* PORTPMSC */
|
||||
case 0x08: /* PORTLI */
|
||||
@ -2815,7 +2895,7 @@ static void xhci_attach(USBPort *usbport)
|
||||
XHCIState *xhci = usbport->opaque;
|
||||
XHCIPort *port = xhci_lookup_port(xhci, usbport);
|
||||
|
||||
xhci_update_port(xhci, port, 0);
|
||||
xhci_port_update(port, 0);
|
||||
}
|
||||
|
||||
static void xhci_detach(USBPort *usbport)
|
||||
@ -2823,27 +2903,19 @@ static void xhci_detach(USBPort *usbport)
|
||||
XHCIState *xhci = usbport->opaque;
|
||||
XHCIPort *port = xhci_lookup_port(xhci, usbport);
|
||||
|
||||
xhci_update_port(xhci, port, 1);
|
||||
xhci_port_update(port, 1);
|
||||
}
|
||||
|
||||
static void xhci_wakeup(USBPort *usbport)
|
||||
{
|
||||
XHCIState *xhci = usbport->opaque;
|
||||
XHCIPort *port = xhci_lookup_port(xhci, usbport);
|
||||
XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
|
||||
port->portnr << 24};
|
||||
uint32_t pls;
|
||||
|
||||
pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK;
|
||||
if (pls != 3) {
|
||||
if (get_field(port->portsc, PORTSC_PLS) != PLS_U3) {
|
||||
return;
|
||||
}
|
||||
port->portsc |= 0xf << PORTSC_PLS_SHIFT;
|
||||
if (port->portsc & PORTSC_PLC) {
|
||||
return;
|
||||
}
|
||||
port->portsc |= PORTSC_PLC;
|
||||
xhci_event(xhci, &ev, 0);
|
||||
set_field(&port->portsc, PLS_RESUME, PORTSC_PLS);
|
||||
xhci_port_notify(port, PORTSC_PLC);
|
||||
}
|
||||
|
||||
static void xhci_complete(USBPort *port, USBPacket *packet)
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "qemu-timer.h"
|
||||
#include "monitor.h"
|
||||
#include "sysemu.h"
|
||||
#include "iov.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/ioctl.h>
|
||||
@ -105,6 +106,7 @@ struct USBRedirDevice {
|
||||
struct usb_redir_interface_info_header interface_info;
|
||||
struct usbredirfilter_rule *filter_rules;
|
||||
int filter_rules_count;
|
||||
int compatible_speedmask;
|
||||
};
|
||||
|
||||
static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
|
||||
@ -312,6 +314,11 @@ static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
|
||||
{
|
||||
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
|
||||
|
||||
if (p->combined) {
|
||||
usb_combined_packet_cancel(udev, p);
|
||||
return;
|
||||
}
|
||||
|
||||
packet_id_queue_add(&dev->cancelled, p->id);
|
||||
usbredirparser_send_cancel_data_packet(dev->parser, p->id);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
@ -331,6 +338,10 @@ static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev,
|
||||
static USBPacket *p;
|
||||
|
||||
QTAILQ_FOREACH(p, &ep->queue, queue) {
|
||||
/* Skip combined packets, except for the first */
|
||||
if (p->combined && p != p->combined->first) {
|
||||
continue;
|
||||
}
|
||||
packet_id_queue_add(&dev->already_in_flight, p->id);
|
||||
}
|
||||
}
|
||||
@ -565,26 +576,36 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
||||
uint8_t ep)
|
||||
{
|
||||
struct usb_redir_bulk_packet_header bulk_packet;
|
||||
size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
|
||||
|
||||
DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id);
|
||||
DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id);
|
||||
|
||||
if (usbredir_already_in_flight(dev, p->id)) {
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
|
||||
bulk_packet.endpoint = ep;
|
||||
bulk_packet.length = p->iov.size;
|
||||
bulk_packet.length = size;
|
||||
bulk_packet.stream_id = 0;
|
||||
bulk_packet.length_high = size >> 16;
|
||||
assert(bulk_packet.length_high == 0 ||
|
||||
usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_32bits_bulk_length));
|
||||
|
||||
if (ep & USB_DIR_IN) {
|
||||
usbredirparser_send_bulk_packet(dev->parser, p->id,
|
||||
&bulk_packet, NULL, 0);
|
||||
} else {
|
||||
uint8_t buf[p->iov.size];
|
||||
usb_packet_copy(p, buf, p->iov.size);
|
||||
usbredir_log_data(dev, "bulk data out:", buf, p->iov.size);
|
||||
uint8_t buf[size];
|
||||
if (p->combined) {
|
||||
iov_to_buf(p->combined->iov.iov, p->combined->iov.niov,
|
||||
0, buf, size);
|
||||
} else {
|
||||
usb_packet_copy(p, buf, size);
|
||||
}
|
||||
usbredir_log_data(dev, "bulk data out:", buf, size);
|
||||
usbredirparser_send_bulk_packet(dev->parser, p->id,
|
||||
&bulk_packet, buf, p->iov.size);
|
||||
&bulk_packet, buf, size);
|
||||
}
|
||||
usbredirparser_do_write(dev->parser);
|
||||
return USB_RET_ASYNC;
|
||||
@ -701,6 +722,10 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
return usbredir_handle_iso_data(dev, p, ep);
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (p->state == USB_PACKET_SETUP && p->pid == USB_TOKEN_IN &&
|
||||
p->ep->pipeline) {
|
||||
return USB_RET_ADD_TO_QUEUE;
|
||||
}
|
||||
return usbredir_handle_bulk_data(dev, p, ep);
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
return usbredir_handle_interrupt_data(dev, p, ep);
|
||||
@ -711,6 +736,13 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
|
||||
}
|
||||
}
|
||||
|
||||
static void usbredir_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
|
||||
{
|
||||
if (ep->pid == USB_TOKEN_IN && ep->pipeline) {
|
||||
usb_ep_combine_input_packets(ep);
|
||||
}
|
||||
}
|
||||
|
||||
static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
|
||||
int config)
|
||||
{
|
||||
@ -896,6 +928,7 @@ static void usbredir_create_parser(USBRedirDevice *dev)
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
|
||||
|
||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
flags |= usbredirparser_fl_no_hello;
|
||||
@ -930,6 +963,7 @@ static void usbredir_do_attach(void *opaque)
|
||||
}
|
||||
|
||||
if (usb_device_attach(&dev->dev) != 0) {
|
||||
WARNING("rejecting device due to speed mismatch\n");
|
||||
usbredir_reject_device(dev);
|
||||
}
|
||||
}
|
||||
@ -1036,6 +1070,9 @@ static int usbredir_initfn(USBDevice *udev)
|
||||
/* We'll do the attach once we receive the speed from the usb-host */
|
||||
udev->auto_attach = 0;
|
||||
|
||||
/* Will be cleared during setup when we find conflicts */
|
||||
dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH;
|
||||
|
||||
/* Let the backend know we are ready */
|
||||
qemu_chr_fe_open(dev->cs);
|
||||
qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
|
||||
@ -1176,10 +1213,13 @@ static void usbredir_device_connect(void *priv,
|
||||
case usb_redir_speed_low:
|
||||
speed = "low speed";
|
||||
dev->dev.speed = USB_SPEED_LOW;
|
||||
dev->compatible_speedmask &= ~USB_SPEED_MASK_FULL;
|
||||
dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH;
|
||||
break;
|
||||
case usb_redir_speed_full:
|
||||
speed = "full speed";
|
||||
dev->dev.speed = USB_SPEED_FULL;
|
||||
dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH;
|
||||
break;
|
||||
case usb_redir_speed_high:
|
||||
speed = "high speed";
|
||||
@ -1209,7 +1249,7 @@ static void usbredir_device_connect(void *priv,
|
||||
device_connect->device_class);
|
||||
}
|
||||
|
||||
dev->dev.speedmask = (1 << dev->dev.speed);
|
||||
dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask;
|
||||
dev->device_info = *device_connect;
|
||||
|
||||
if (usbredir_check_filter(dev)) {
|
||||
@ -1249,6 +1289,7 @@ static void usbredir_device_disconnect(void *priv)
|
||||
dev->interface_info.interface_count = NO_INTERFACE_INFO;
|
||||
dev->dev.addr = 0;
|
||||
dev->dev.speed = 0;
|
||||
dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH;
|
||||
}
|
||||
|
||||
static void usbredir_interface_info(void *priv,
|
||||
@ -1270,6 +1311,12 @@ static void usbredir_interface_info(void *priv,
|
||||
}
|
||||
}
|
||||
|
||||
static void usbredir_mark_speed_incompatible(USBRedirDevice *dev, int speed)
|
||||
{
|
||||
dev->compatible_speedmask &= ~(1 << speed);
|
||||
dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask;
|
||||
}
|
||||
|
||||
static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep)
|
||||
{
|
||||
if (uep->type != USB_ENDPOINT_XFER_BULK) {
|
||||
@ -1278,27 +1325,64 @@ static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep)
|
||||
if (uep->pid == USB_TOKEN_OUT) {
|
||||
uep->pipeline = true;
|
||||
}
|
||||
if (uep->pid == USB_TOKEN_IN && uep->max_packet_size != 0 &&
|
||||
usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_32bits_bulk_length)) {
|
||||
uep->pipeline = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbredir_setup_usb_eps(USBRedirDevice *dev)
|
||||
{
|
||||
struct USBEndpoint *usb_ep;
|
||||
int i, pid;
|
||||
|
||||
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
||||
pid = (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT;
|
||||
usb_ep = usb_ep_get(&dev->dev, pid, i & 0x0f);
|
||||
usb_ep->type = dev->endpoint[i].type;
|
||||
usb_ep->ifnum = dev->endpoint[i].interface;
|
||||
usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
|
||||
usbredir_set_pipeline(dev, usb_ep);
|
||||
}
|
||||
}
|
||||
|
||||
static void usbredir_ep_info(void *priv,
|
||||
struct usb_redir_ep_info_header *ep_info)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
struct USBEndpoint *usb_ep;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
||||
dev->endpoint[i].type = ep_info->type[i];
|
||||
dev->endpoint[i].interval = ep_info->interval[i];
|
||||
dev->endpoint[i].interface = ep_info->interface[i];
|
||||
if (usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_ep_info_max_packet_size)) {
|
||||
dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i];
|
||||
}
|
||||
switch (dev->endpoint[i].type) {
|
||||
case usb_redir_type_invalid:
|
||||
break;
|
||||
case usb_redir_type_iso:
|
||||
usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL);
|
||||
usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH);
|
||||
/* Fall through */
|
||||
case usb_redir_type_interrupt:
|
||||
if (!usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_ep_info_max_packet_size) ||
|
||||
ep_info->max_packet_size[i] > 64) {
|
||||
usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL);
|
||||
}
|
||||
if (!usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_ep_info_max_packet_size) ||
|
||||
ep_info->max_packet_size[i] > 1024) {
|
||||
usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH);
|
||||
}
|
||||
if (dev->endpoint[i].interval == 0) {
|
||||
ERROR("Received 0 interval for isoc or irq endpoint\n");
|
||||
usbredir_device_disconnect(dev);
|
||||
usbredir_reject_device(dev);
|
||||
return;
|
||||
}
|
||||
/* Fall through */
|
||||
case usb_redir_type_control:
|
||||
@ -1308,21 +1392,19 @@ static void usbredir_ep_info(void *priv,
|
||||
break;
|
||||
default:
|
||||
ERROR("Received invalid endpoint type\n");
|
||||
usbredir_device_disconnect(dev);
|
||||
usbredir_reject_device(dev);
|
||||
return;
|
||||
}
|
||||
usb_ep = usb_ep_get(&dev->dev,
|
||||
(i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT,
|
||||
i & 0x0f);
|
||||
usb_ep->type = dev->endpoint[i].type;
|
||||
usb_ep->ifnum = dev->endpoint[i].interface;
|
||||
if (usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_ep_info_max_packet_size)) {
|
||||
dev->endpoint[i].max_packet_size =
|
||||
usb_ep->max_packet_size = ep_info->max_packet_size[i];
|
||||
}
|
||||
usbredir_set_pipeline(dev, usb_ep);
|
||||
}
|
||||
/* The new ep info may have caused a speed incompatibility, recheck */
|
||||
if (dev->dev.attached &&
|
||||
!(dev->dev.port->speedmask & dev->dev.speedmask)) {
|
||||
ERROR("Device no longer matches speed after endpoint info change, "
|
||||
"disconnecting!\n");
|
||||
usbredir_reject_device(dev);
|
||||
return;
|
||||
}
|
||||
usbredir_setup_usb_eps(dev);
|
||||
}
|
||||
|
||||
static void usbredir_configuration_status(void *priv, uint64_t id,
|
||||
@ -1427,6 +1509,17 @@ static void usbredir_control_packet(void *priv, uint64_t id,
|
||||
DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status,
|
||||
len, id);
|
||||
|
||||
/* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices
|
||||
* to work redirected to a not superspeed capable hcd */
|
||||
if (dev->dev.speed == USB_SPEED_SUPER &&
|
||||
!((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER)) &&
|
||||
control_packet->requesttype == 0x80 &&
|
||||
control_packet->request == 6 &&
|
||||
control_packet->value == 0x100 && control_packet->index == 0 &&
|
||||
data_len >= 18 && data[7] == 9) {
|
||||
data[7] = 64;
|
||||
}
|
||||
|
||||
p = usbredir_find_packet_by_id(dev, 0, id);
|
||||
if (p) {
|
||||
len = usbredir_handle_status(dev, control_packet->status, len);
|
||||
@ -1452,7 +1545,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
uint8_t ep = bulk_packet->endpoint;
|
||||
int len = bulk_packet->length;
|
||||
int len = (bulk_packet->length_high << 16) | bulk_packet->length;
|
||||
USBPacket *p;
|
||||
|
||||
DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n",
|
||||
@ -1460,11 +1553,17 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
|
||||
|
||||
p = usbredir_find_packet_by_id(dev, ep, id);
|
||||
if (p) {
|
||||
size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
|
||||
len = usbredir_handle_status(dev, bulk_packet->status, len);
|
||||
if (len > 0) {
|
||||
usbredir_log_data(dev, "bulk data in:", data, data_len);
|
||||
if (data_len <= p->iov.size) {
|
||||
usb_packet_copy(p, data, data_len);
|
||||
if (data_len <= size) {
|
||||
if (p->combined) {
|
||||
iov_from_buf(p->combined->iov.iov, p->combined->iov.niov,
|
||||
0, data, data_len);
|
||||
} else {
|
||||
usb_packet_copy(p, data, data_len);
|
||||
}
|
||||
} else {
|
||||
ERROR("bulk got more data then requested (%d > %zd)\n",
|
||||
data_len, p->iov.size);
|
||||
@ -1472,7 +1571,11 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
|
||||
}
|
||||
}
|
||||
p->result = len;
|
||||
usb_packet_complete(&dev->dev, p);
|
||||
if (p->pid == USB_TOKEN_IN && p->ep->pipeline) {
|
||||
usb_combined_input_packet_complete(&dev->dev, p);
|
||||
} else {
|
||||
usb_packet_complete(&dev->dev, p);
|
||||
}
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
@ -1554,8 +1657,6 @@ static void usbredir_pre_save(void *priv)
|
||||
static int usbredir_post_load(void *priv, int version_id)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
struct USBEndpoint *usb_ep;
|
||||
int i;
|
||||
|
||||
switch (dev->device_info.speed) {
|
||||
case usb_redir_speed_low:
|
||||
@ -1575,15 +1676,8 @@ static int usbredir_post_load(void *priv, int version_id)
|
||||
}
|
||||
dev->dev.speedmask = (1 << dev->dev.speed);
|
||||
|
||||
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
||||
usb_ep = usb_ep_get(&dev->dev,
|
||||
(i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT,
|
||||
i & 0x0f);
|
||||
usb_ep->type = dev->endpoint[i].type;
|
||||
usb_ep->ifnum = dev->endpoint[i].interface;
|
||||
usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
|
||||
usbredir_set_pipeline(dev, usb_ep);
|
||||
}
|
||||
usbredir_setup_usb_eps(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1879,6 +1973,7 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data)
|
||||
uc->handle_reset = usbredir_handle_reset;
|
||||
uc->handle_data = usbredir_handle_data;
|
||||
uc->handle_control = usbredir_handle_control;
|
||||
uc->flush_ep_queue = usbredir_flush_ep_queue;
|
||||
dc->vmsd = &usbredir_vmstate;
|
||||
dc->props = usbredir_properties;
|
||||
}
|
||||
|
@ -166,6 +166,9 @@ static void zynq_init(QEMUMachineInitArgs *args)
|
||||
zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false);
|
||||
zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true);
|
||||
|
||||
sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]);
|
||||
sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[75-IRQ_OFFSET]);
|
||||
|
||||
sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]);
|
||||
sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]);
|
||||
|
||||
|
@ -338,6 +338,8 @@ usb_xhci_irq_msix_use(uint32_t nr) "nr %d"
|
||||
usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d"
|
||||
usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "v %d, idx %d, %s, %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_port_reset(uint32_t port) "port %d"
|
||||
usb_xhci_port_link(uint32_t port, uint32_t pls) "port %d, pls %d"
|
||||
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"
|
||||
|
Loading…
Reference in New Issue
Block a user