Merge remote-tracking branch 'kraxel/usb.19' into staging
This commit is contained in:
commit
b4dabf9587
37
docs/ich9-ehci-uhci.cfg
Normal file
37
docs/ich9-ehci-uhci.cfg
Normal file
@ -0,0 +1,37 @@
|
||||
###########################################################################
|
||||
#
|
||||
# You can pass this file directly to qemu using the -readconfig
|
||||
# command line switch.
|
||||
#
|
||||
# This config file creates a EHCI adapter with companion UHCI
|
||||
# controllers as multifunction device in PCI slot "1d".
|
||||
#
|
||||
# Specify "bus=ehci.0" when creating usb devices to hook them up
|
||||
# there.
|
||||
#
|
||||
|
||||
[device "ehci"]
|
||||
driver = "ich9-usb-ehci1"
|
||||
addr = "1d.7"
|
||||
multifunction = "on"
|
||||
|
||||
[device "uhci-1"]
|
||||
driver = "ich9-usb-uhci1"
|
||||
addr = "1d.0"
|
||||
multifunction = "on"
|
||||
masterbus = "ehci.0"
|
||||
firstport = "0"
|
||||
|
||||
[device "uhci-2"]
|
||||
driver = "ich9-usb-uhci2"
|
||||
addr = "1d.1"
|
||||
multifunction = "on"
|
||||
masterbus = "ehci.0"
|
||||
firstport = "2"
|
||||
|
||||
[device "uhci-3"]
|
||||
driver = "ich9-usb-uhci3"
|
||||
addr = "1d.2"
|
||||
multifunction = "on"
|
||||
masterbus = "ehci.0"
|
||||
firstport = "4"
|
@ -2,11 +2,13 @@
|
||||
USB 2.0 Quick Start
|
||||
===================
|
||||
|
||||
The QEMU EHCI Adapter does *not* support companion controllers. That
|
||||
implies there are two completely separate USB busses: One USB 1.1 bus
|
||||
driven by the UHCI controller and one USB 2.0 bus driven by the EHCI
|
||||
controller. Devices must be attached to the correct controller
|
||||
manually.
|
||||
The QEMU EHCI Adapter can be used with and without companion
|
||||
controllers. See below for the companion controller mode.
|
||||
|
||||
When not running in companion controller mode there are two completely
|
||||
separate USB busses: One USB 1.1 bus driven by the UHCI controller and
|
||||
one USB 2.0 bus driven by the EHCI controller. Devices must be
|
||||
attached to the correct controller manually.
|
||||
|
||||
The '-usb' switch will make qemu create the UHCI controller as part of
|
||||
the PIIX3 chipset. The USB 1.1 bus will carry the name "usb.0".
|
||||
@ -32,6 +34,27 @@ This attaches a usb tablet to the UHCI adapter and a usb mass storage
|
||||
device to the EHCI adapter.
|
||||
|
||||
|
||||
Companion controller support
|
||||
----------------------------
|
||||
|
||||
Companion controller support has been added recently. The operational
|
||||
model described above with two completely separate busses still works
|
||||
fine. Additionally the UHCI and OHCI controllers got the ability to
|
||||
attach to a usb bus created by EHCI as companion controllers. This is
|
||||
done by specifying the masterbus and firstport properties. masterbus
|
||||
specifies the bus name the controller should attach to. firstport
|
||||
specifies the first port the controller should attach to, which is
|
||||
needed as usually one ehci controller with six ports has three uhci
|
||||
companion controllers with two ports each.
|
||||
|
||||
There is a config file in docs which will do all this for you, just
|
||||
try ...
|
||||
|
||||
qemu -readconfig docs/ich9-ehci-uhci.cfg
|
||||
|
||||
... then use "bus=ehci.0" to assign your usb devices to that bus.
|
||||
|
||||
|
||||
More USB tips & tricks
|
||||
======================
|
||||
|
||||
|
@ -247,16 +247,21 @@ static void softusb_attach(USBPort *port)
|
||||
{
|
||||
}
|
||||
|
||||
static void softusb_device_destroy(USBBus *bus, USBDevice *dev)
|
||||
static void softusb_detach(USBPort *port)
|
||||
{
|
||||
}
|
||||
|
||||
static void softusb_child_detach(USBPort *port, USBDevice *child)
|
||||
{
|
||||
}
|
||||
|
||||
static USBPortOps softusb_ops = {
|
||||
.attach = softusb_attach,
|
||||
.detach = softusb_detach,
|
||||
.child_detach = softusb_child_detach,
|
||||
};
|
||||
|
||||
static USBBusOps softusb_bus_ops = {
|
||||
.device_destroy = softusb_device_destroy,
|
||||
};
|
||||
|
||||
static void milkymist_softusb_reset(DeviceState *d)
|
||||
|
@ -109,6 +109,14 @@
|
||||
#define PCI_DEVICE_ID_INTEL_82371AB 0x7111
|
||||
#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112
|
||||
#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
|
||||
#define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934
|
||||
#define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935
|
||||
#define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936
|
||||
#define PCI_DEVICE_ID_INTEL_82801I_UHCI4 0x2937
|
||||
#define PCI_DEVICE_ID_INTEL_82801I_UHCI5 0x2938
|
||||
#define PCI_DEVICE_ID_INTEL_82801I_UHCI6 0x2939
|
||||
#define PCI_DEVICE_ID_INTEL_82801I_EHCI1 0x293a
|
||||
#define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c
|
||||
|
||||
#define PCI_VENDOR_ID_XEN 0x5853
|
||||
#define PCI_DEVICE_ID_XEN_PLATFORM 0x0001
|
||||
|
24
hw/usb-bt.c
24
hw/usb-bt.c
@ -99,13 +99,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
@ -120,13 +120,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0x09,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0x09,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
@ -141,13 +141,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0x11,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0x11,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
@ -162,13 +162,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0x19,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0x19,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
@ -183,13 +183,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0x21,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0x21,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
@ -204,13 +204,13 @@ static const USBDescIface desc_iface_bluetooth[] = {
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0x31,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = 0x31,
|
||||
.bInterval = 0x01,
|
||||
},
|
||||
|
46
hw/usb-bus.c
46
hw/usb-bus.c
@ -82,12 +82,10 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
|
||||
static int usb_qdev_exit(DeviceState *qdev)
|
||||
{
|
||||
USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
|
||||
USBBus *bus = usb_bus_from_device(dev);
|
||||
|
||||
if (dev->attached) {
|
||||
usb_device_detach(dev);
|
||||
}
|
||||
bus->ops->device_destroy(bus, dev);
|
||||
if (dev->info->handle_destroy) {
|
||||
dev->info->handle_destroy(dev);
|
||||
}
|
||||
@ -140,19 +138,55 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name)
|
||||
return dev;
|
||||
}
|
||||
|
||||
void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
|
||||
USBPortOps *ops, int speedmask)
|
||||
static void usb_fill_port(USBPort *port, void *opaque, int index,
|
||||
USBPortOps *ops, int speedmask)
|
||||
{
|
||||
port->opaque = opaque;
|
||||
port->index = index;
|
||||
port->opaque = opaque;
|
||||
port->index = index;
|
||||
port->ops = ops;
|
||||
port->speedmask = speedmask;
|
||||
usb_port_location(port, NULL, index + 1);
|
||||
}
|
||||
|
||||
void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
|
||||
USBPortOps *ops, int speedmask)
|
||||
{
|
||||
usb_fill_port(port, opaque, index, ops, speedmask);
|
||||
QTAILQ_INSERT_TAIL(&bus->free, port, next);
|
||||
bus->nfree++;
|
||||
}
|
||||
|
||||
int usb_register_companion(const char *masterbus, USBPort *ports[],
|
||||
uint32_t portcount, uint32_t firstport,
|
||||
void *opaque, USBPortOps *ops, int speedmask)
|
||||
{
|
||||
USBBus *bus;
|
||||
int i;
|
||||
|
||||
QTAILQ_FOREACH(bus, &busses, next) {
|
||||
if (strcmp(bus->qbus.name, masterbus) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bus || !bus->ops->register_companion) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
|
||||
"an USB masterbus");
|
||||
if (bus) {
|
||||
error_printf_unless_qmp(
|
||||
"USB bus '%s' does not allow companion controllers\n",
|
||||
masterbus);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < portcount; i++) {
|
||||
usb_fill_port(ports[i], opaque, i, ops, speedmask);
|
||||
}
|
||||
|
||||
return bus->ops->register_companion(bus, ports, portcount, firstport);
|
||||
}
|
||||
|
||||
void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr)
|
||||
{
|
||||
if (upstream) {
|
||||
|
270
hw/usb-ehci.c
270
hw/usb-ehci.c
@ -20,9 +20,6 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* TODO:
|
||||
* o Downstream port handoff
|
||||
*/
|
||||
|
||||
#include "hw.h"
|
||||
@ -103,10 +100,10 @@
|
||||
#define PORTSC_BEGIN PORTSC
|
||||
#define PORTSC_END (PORTSC + 4 * NB_PORTS)
|
||||
/*
|
||||
* Bits that are reserverd or are read-only are masked out of values
|
||||
* Bits that are reserved or are read-only are masked out of values
|
||||
* written to us by software
|
||||
*/
|
||||
#define PORTSC_RO_MASK 0x007021c5
|
||||
#define PORTSC_RO_MASK 0x007001c0
|
||||
#define PORTSC_RWC_MASK 0x0000002a
|
||||
#define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable
|
||||
#define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable
|
||||
@ -133,7 +130,7 @@
|
||||
#define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ)
|
||||
|
||||
#define NB_MAXINTRATE 8 // Max rate at which controller issues ints
|
||||
#define NB_PORTS 4 // Number of downstream ports
|
||||
#define NB_PORTS 6 // Number of downstream ports
|
||||
#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction
|
||||
#define MAX_ITERATIONS 20 // Max number of QH before we break the loop
|
||||
#define MAX_QH 100 // Max allowable queue heads in a chain
|
||||
@ -373,7 +370,7 @@ struct EHCIState {
|
||||
qemu_irq irq;
|
||||
target_phys_addr_t mem_base;
|
||||
int mem;
|
||||
int num_ports;
|
||||
int companion_count;
|
||||
|
||||
/* properties */
|
||||
uint32_t freq;
|
||||
@ -409,6 +406,7 @@ struct EHCIState {
|
||||
int astate; // Current state in asynchronous schedule
|
||||
int pstate; // Current state in periodic schedule
|
||||
USBPort ports[NB_PORTS];
|
||||
USBPort *companion_ports[NB_PORTS];
|
||||
uint32_t usbsts_pending;
|
||||
QTAILQ_HEAD(, EHCIQueue) queues;
|
||||
|
||||
@ -731,17 +729,17 @@ static void ehci_attach(USBPort *port)
|
||||
|
||||
trace_usb_ehci_port_attach(port->index, port->dev->product_desc);
|
||||
|
||||
if (*portsc & PORTSC_POWNER) {
|
||||
USBPort *companion = s->companion_ports[port->index];
|
||||
companion->dev = port->dev;
|
||||
companion->ops->attach(companion);
|
||||
return;
|
||||
}
|
||||
|
||||
*portsc |= PORTSC_CONNECT;
|
||||
*portsc |= PORTSC_CSC;
|
||||
|
||||
/*
|
||||
* If a high speed device is attached then we own this port(indicated
|
||||
* by zero in the PORTSC_POWNER bit field) so set the status bit
|
||||
* and set an interrupt if enabled.
|
||||
*/
|
||||
if ( !(*portsc & PORTSC_POWNER)) {
|
||||
ehci_set_interrupt(s, USBSTS_PCD);
|
||||
}
|
||||
ehci_set_interrupt(s, USBSTS_PCD);
|
||||
}
|
||||
|
||||
static void ehci_detach(USBPort *port)
|
||||
@ -751,17 +749,88 @@ static void ehci_detach(USBPort *port)
|
||||
|
||||
trace_usb_ehci_port_detach(port->index);
|
||||
|
||||
*portsc &= ~PORTSC_CONNECT;
|
||||
if (*portsc & PORTSC_POWNER) {
|
||||
USBPort *companion = s->companion_ports[port->index];
|
||||
companion->ops->detach(companion);
|
||||
companion->dev = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
ehci_queues_rip_device(s, port->dev);
|
||||
|
||||
*portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
|
||||
*portsc |= PORTSC_CSC;
|
||||
|
||||
/*
|
||||
* If a high speed device is attached then we own this port(indicated
|
||||
* by zero in the PORTSC_POWNER bit field) so set the status bit
|
||||
* and set an interrupt if enabled.
|
||||
*/
|
||||
if ( !(*portsc & PORTSC_POWNER)) {
|
||||
ehci_set_interrupt(s, USBSTS_PCD);
|
||||
ehci_set_interrupt(s, USBSTS_PCD);
|
||||
}
|
||||
|
||||
static void ehci_child_detach(USBPort *port, USBDevice *child)
|
||||
{
|
||||
EHCIState *s = port->opaque;
|
||||
uint32_t portsc = s->portsc[port->index];
|
||||
|
||||
if (portsc & PORTSC_POWNER) {
|
||||
USBPort *companion = s->companion_ports[port->index];
|
||||
companion->ops->child_detach(companion, child);
|
||||
companion->dev = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
ehci_queues_rip_device(s, child);
|
||||
}
|
||||
|
||||
static void ehci_wakeup(USBPort *port)
|
||||
{
|
||||
EHCIState *s = port->opaque;
|
||||
uint32_t portsc = s->portsc[port->index];
|
||||
|
||||
if (portsc & PORTSC_POWNER) {
|
||||
USBPort *companion = s->companion_ports[port->index];
|
||||
if (companion->ops->wakeup) {
|
||||
companion->ops->wakeup(companion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ehci_register_companion(USBBus *bus, USBPort *ports[],
|
||||
uint32_t portcount, uint32_t firstport)
|
||||
{
|
||||
EHCIState *s = container_of(bus, EHCIState, bus);
|
||||
uint32_t i;
|
||||
|
||||
if (firstport + portcount > NB_PORTS) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport",
|
||||
"firstport on masterbus");
|
||||
error_printf_unless_qmp(
|
||||
"firstport value of %u makes companion take ports %u - %u, which "
|
||||
"is outside of the valid range of 0 - %u\n", firstport, firstport,
|
||||
firstport + portcount - 1, NB_PORTS - 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < portcount; i++) {
|
||||
if (s->companion_ports[firstport + i]) {
|
||||
qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
|
||||
"an USB masterbus");
|
||||
error_printf_unless_qmp(
|
||||
"port %u on masterbus %s already has a companion assigned\n",
|
||||
firstport + i, bus->qbus.name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < portcount; i++) {
|
||||
s->companion_ports[firstport + i] = ports[i];
|
||||
s->ports[firstport + i].speedmask |=
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL;
|
||||
/* Ensure devs attached before the initial reset go to the companion */
|
||||
s->portsc[firstport + i] = PORTSC_POWNER;
|
||||
}
|
||||
|
||||
s->companion_count++;
|
||||
s->mmio[0x05] = (s->companion_count << 4) | portcount;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 4.1 host controller initialization */
|
||||
@ -769,9 +838,21 @@ static void ehci_reset(void *opaque)
|
||||
{
|
||||
EHCIState *s = opaque;
|
||||
int i;
|
||||
USBDevice *devs[NB_PORTS];
|
||||
|
||||
trace_usb_ehci_reset();
|
||||
|
||||
/*
|
||||
* Do the detach before touching portsc, so that it correctly gets send to
|
||||
* us or to our companion based on PORTSC_POWNER before the reset.
|
||||
*/
|
||||
for(i = 0; i < NB_PORTS; i++) {
|
||||
devs[i] = s->ports[i].dev;
|
||||
if (devs[i]) {
|
||||
usb_attach(&s->ports[i], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
|
||||
|
||||
s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
|
||||
@ -783,10 +864,13 @@ static void ehci_reset(void *opaque)
|
||||
s->attach_poll_counter = 0;
|
||||
|
||||
for(i = 0; i < NB_PORTS; i++) {
|
||||
s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
|
||||
|
||||
if (s->ports[i].dev) {
|
||||
usb_attach(&s->ports[i], s->ports[i].dev);
|
||||
if (s->companion_ports[i]) {
|
||||
s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
|
||||
} else {
|
||||
s->portsc[i] = PORTSC_PPOWER;
|
||||
}
|
||||
if (devs[i]) {
|
||||
usb_attach(&s->ports[i], devs[i]);
|
||||
}
|
||||
}
|
||||
ehci_queues_rip_all(s);
|
||||
@ -836,43 +920,67 @@ static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
|
||||
{
|
||||
USBDevice *dev = s->ports[port].dev;
|
||||
uint32_t *portsc = &s->portsc[port];
|
||||
uint32_t orig;
|
||||
|
||||
if (s->companion_ports[port] == NULL)
|
||||
return;
|
||||
|
||||
owner = owner & PORTSC_POWNER;
|
||||
orig = *portsc & PORTSC_POWNER;
|
||||
|
||||
if (!(owner ^ orig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev) {
|
||||
usb_attach(&s->ports[port], NULL);
|
||||
}
|
||||
|
||||
*portsc &= ~PORTSC_POWNER;
|
||||
*portsc |= owner;
|
||||
|
||||
if (dev) {
|
||||
usb_attach(&s->ports[port], dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
|
||||
{
|
||||
uint32_t *portsc = &s->portsc[port];
|
||||
int rwc;
|
||||
USBDevice *dev = s->ports[port].dev;
|
||||
|
||||
rwc = val & PORTSC_RWC_MASK;
|
||||
/* Clear rwc bits */
|
||||
*portsc &= ~(val & PORTSC_RWC_MASK);
|
||||
/* The guest may clear, but not set the PED bit */
|
||||
*portsc &= val | ~PORTSC_PED;
|
||||
/* POWNER is masked out by RO_MASK as it is RO when we've no companion */
|
||||
handle_port_owner_write(s, port, val);
|
||||
/* And finally apply RO_MASK */
|
||||
val &= PORTSC_RO_MASK;
|
||||
|
||||
// handle_read_write_clear(&val, portsc, PORTSC_PEDC | PORTSC_CSC);
|
||||
|
||||
*portsc &= ~rwc;
|
||||
|
||||
if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) {
|
||||
trace_usb_ehci_port_reset(port, 1);
|
||||
}
|
||||
|
||||
if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
|
||||
trace_usb_ehci_port_reset(port, 0);
|
||||
usb_attach(&s->ports[port], dev);
|
||||
|
||||
// TODO how to handle reset of ports with no device
|
||||
if (dev) {
|
||||
usb_attach(&s->ports[port], dev);
|
||||
usb_send_msg(dev, USB_MSG_RESET);
|
||||
}
|
||||
|
||||
if (s->ports[port].dev) {
|
||||
*portsc &= ~PORTSC_CSC;
|
||||
}
|
||||
|
||||
/* Table 2.16 Set the enable bit(and enable bit change) to indicate
|
||||
/*
|
||||
* Table 2.16 Set the enable bit(and enable bit change) to indicate
|
||||
* to SW that this port has a high speed device attached
|
||||
*
|
||||
* TODO - when to disable?
|
||||
*/
|
||||
val |= PORTSC_PED;
|
||||
val |= PORTSC_PEDC;
|
||||
if (dev && (dev->speedmask & USB_SPEED_MASK_HIGH)) {
|
||||
val |= PORTSC_PED;
|
||||
}
|
||||
}
|
||||
|
||||
*portsc &= ~PORTSC_RO_MASK;
|
||||
@ -955,7 +1063,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
|
||||
val &= 0x1;
|
||||
if (val) {
|
||||
for(i = 0; i < NB_PORTS; i++)
|
||||
s->portsc[i] &= ~PORTSC_POWNER;
|
||||
handle_port_owner_write(s, i, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1111,10 +1219,19 @@ static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet)
|
||||
static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
|
||||
{
|
||||
EHCIQueue *q = container_of(packet, EHCIQueue, packet);
|
||||
EHCIQueue *q;
|
||||
EHCIState *s = port->opaque;
|
||||
uint32_t portsc = s->portsc[port->index];
|
||||
|
||||
if (portsc & PORTSC_POWNER) {
|
||||
USBPort *companion = s->companion_ports[port->index];
|
||||
companion->ops->complete(companion, packet);
|
||||
return;
|
||||
}
|
||||
|
||||
q = container_of(packet, EHCIQueue, packet);
|
||||
trace_usb_ehci_queue_action(q, "wakeup");
|
||||
assert(q->async == EHCI_ASYNC_INFLIGHT);
|
||||
q->async = EHCI_ASYNC_FINISHED;
|
||||
@ -1244,8 +1361,6 @@ static int ehci_execute(EHCIQueue *q)
|
||||
port = &q->ehci->ports[i];
|
||||
dev = port->dev;
|
||||
|
||||
// TODO sometime we will also need to check if we are the port owner
|
||||
|
||||
if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) {
|
||||
DPRINTF("Port %d, no exec, not connected(%08X)\n",
|
||||
i, q->ehci->portsc[i]);
|
||||
@ -1338,8 +1453,6 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||
port = &ehci->ports[j];
|
||||
dev = port->dev;
|
||||
|
||||
// TODO sometime we will also need to check if we are the port owner
|
||||
|
||||
if (!(ehci->portsc[j] &(PORTSC_CONNECT))) {
|
||||
continue;
|
||||
}
|
||||
@ -2117,38 +2230,48 @@ static void ehci_map(PCIDevice *pci_dev, int region_num,
|
||||
cpu_register_physical_memory(addr, size, s->mem);
|
||||
}
|
||||
|
||||
static void ehci_device_destroy(USBBus *bus, USBDevice *dev)
|
||||
{
|
||||
EHCIState *s = container_of(bus, EHCIState, bus);
|
||||
|
||||
ehci_queues_rip_device(s, dev);
|
||||
}
|
||||
|
||||
static int usb_ehci_initfn(PCIDevice *dev);
|
||||
|
||||
static USBPortOps ehci_port_ops = {
|
||||
.attach = ehci_attach,
|
||||
.detach = ehci_detach,
|
||||
.child_detach = ehci_child_detach,
|
||||
.wakeup = ehci_wakeup,
|
||||
.complete = ehci_async_complete_packet,
|
||||
};
|
||||
|
||||
static USBBusOps ehci_bus_ops = {
|
||||
.device_destroy = ehci_device_destroy,
|
||||
.register_companion = ehci_register_companion,
|
||||
};
|
||||
|
||||
static PCIDeviceInfo ehci_info = {
|
||||
.qdev.name = "usb-ehci",
|
||||
.qdev.size = sizeof(EHCIState),
|
||||
.init = usb_ehci_initfn,
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801D,
|
||||
.revision = 0x10,
|
||||
.class_id = PCI_CLASS_SERIAL_USB,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ),
|
||||
DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
static Property ehci_properties[] = {
|
||||
DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ),
|
||||
DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static PCIDeviceInfo ehci_info[] = {
|
||||
{
|
||||
.qdev.name = "usb-ehci",
|
||||
.qdev.size = sizeof(EHCIState),
|
||||
.init = usb_ehci_initfn,
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */
|
||||
.revision = 0x10,
|
||||
.class_id = PCI_CLASS_SERIAL_USB,
|
||||
.qdev.props = ehci_properties,
|
||||
},{
|
||||
.qdev.name = "ich9-usb-ehci1",
|
||||
.qdev.size = sizeof(EHCIState),
|
||||
.init = usb_ehci_initfn,
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1,
|
||||
.revision = 0x03,
|
||||
.class_id = PCI_CLASS_SERIAL_USB,
|
||||
.qdev.props = ehci_properties,
|
||||
},{
|
||||
/* end of list */
|
||||
}
|
||||
};
|
||||
|
||||
static int usb_ehci_initfn(PCIDevice *dev)
|
||||
@ -2206,7 +2329,6 @@ static int usb_ehci_initfn(PCIDevice *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);
|
||||
usb_port_location(&s->ports[i], NULL, i+1);
|
||||
s->ports[i].dev = 0;
|
||||
}
|
||||
|
||||
@ -2228,7 +2350,7 @@ static int usb_ehci_initfn(PCIDevice *dev)
|
||||
|
||||
static void ehci_register(void)
|
||||
{
|
||||
pci_qdev_register(&ehci_info);
|
||||
pci_qdev_register_many(ehci_info);
|
||||
}
|
||||
device_init(ehci_register);
|
||||
|
||||
|
90
hw/usb-hub.c
90
hw/usb-hub.c
@ -138,74 +138,6 @@ static const USBDesc desc_hub = {
|
||||
.str = desc_strings,
|
||||
};
|
||||
|
||||
static const uint8_t qemu_hub_dev_descriptor[] = {
|
||||
0x12, /* u8 bLength; */
|
||||
0x01, /* u8 bDescriptorType; Device */
|
||||
0x10, 0x01, /* u16 bcdUSB; v1.1 */
|
||||
|
||||
0x09, /* u8 bDeviceClass; HUB_CLASSCODE */
|
||||
0x00, /* u8 bDeviceSubClass; */
|
||||
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
|
||||
0x08, /* u8 bMaxPacketSize0; 8 Bytes */
|
||||
|
||||
0x00, 0x00, /* u16 idVendor; */
|
||||
0x00, 0x00, /* u16 idProduct; */
|
||||
0x01, 0x01, /* u16 bcdDevice */
|
||||
|
||||
0x03, /* u8 iManufacturer; */
|
||||
0x02, /* u8 iProduct; */
|
||||
0x01, /* u8 iSerialNumber; */
|
||||
0x01 /* u8 bNumConfigurations; */
|
||||
};
|
||||
|
||||
/* XXX: patch interrupt size */
|
||||
static const uint8_t qemu_hub_config_descriptor[] = {
|
||||
|
||||
/* one configuration */
|
||||
0x09, /* u8 bLength; */
|
||||
0x02, /* u8 bDescriptorType; Configuration */
|
||||
0x19, 0x00, /* u16 wTotalLength; */
|
||||
0x01, /* u8 bNumInterfaces; (1) */
|
||||
0x01, /* u8 bConfigurationValue; */
|
||||
0x00, /* u8 iConfiguration; */
|
||||
0xe0, /* u8 bmAttributes;
|
||||
Bit 7: must be set,
|
||||
6: Self-powered,
|
||||
5: Remote wakeup,
|
||||
4..0: resvd */
|
||||
0x00, /* u8 MaxPower; */
|
||||
|
||||
/* USB 1.1:
|
||||
* USB 2.0, single TT organization (mandatory):
|
||||
* one interface, protocol 0
|
||||
*
|
||||
* USB 2.0, multiple TT organization (optional):
|
||||
* two interfaces, protocols 1 (like single TT)
|
||||
* and 2 (multiple TT mode) ... config is
|
||||
* sometimes settable
|
||||
* NOT IMPLEMENTED
|
||||
*/
|
||||
|
||||
/* one interface */
|
||||
0x09, /* u8 if_bLength; */
|
||||
0x04, /* u8 if_bDescriptorType; Interface */
|
||||
0x00, /* u8 if_bInterfaceNumber; */
|
||||
0x00, /* u8 if_bAlternateSetting; */
|
||||
0x01, /* u8 if_bNumEndpoints; */
|
||||
0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */
|
||||
0x00, /* u8 if_bInterfaceSubClass; */
|
||||
0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
|
||||
0x00, /* u8 if_iInterface; */
|
||||
|
||||
/* one endpoint (status change endpoint) */
|
||||
0x07, /* u8 ep_bLength; */
|
||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
||||
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
|
||||
0x03, /* u8 ep_bmAttributes; Interrupt */
|
||||
0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
|
||||
0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
||||
};
|
||||
|
||||
static const uint8_t qemu_hub_hub_descriptor[] =
|
||||
{
|
||||
0x00, /* u8 bLength; patched in later */
|
||||
@ -238,6 +170,9 @@ static void usb_hub_detach(USBPort *port1)
|
||||
USBHubState *s = port1->opaque;
|
||||
USBHubPort *port = &s->ports[port1->index];
|
||||
|
||||
/* Let upstream know the device on this port is gone */
|
||||
s->dev.port->ops->child_detach(s->dev.port, port1->dev);
|
||||
|
||||
port->wPortStatus &= ~PORT_STAT_CONNECTION;
|
||||
port->wPortChange |= PORT_STAT_C_CONNECTION;
|
||||
if (port->wPortStatus & PORT_STAT_ENABLE) {
|
||||
@ -246,10 +181,18 @@ static void usb_hub_detach(USBPort *port1)
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_hub_wakeup(USBDevice *dev)
|
||||
static void usb_hub_child_detach(USBPort *port1, USBDevice *child)
|
||||
{
|
||||
USBHubState *s = dev->port->opaque;
|
||||
USBHubPort *port = &s->ports[dev->port->index];
|
||||
USBHubState *s = port1->opaque;
|
||||
|
||||
/* Pass along upstream */
|
||||
s->dev.port->ops->child_detach(s->dev.port, child);
|
||||
}
|
||||
|
||||
static void usb_hub_wakeup(USBPort *port1)
|
||||
{
|
||||
USBHubState *s = port1->opaque;
|
||||
USBHubPort *port = &s->ports[port1->index];
|
||||
|
||||
if (port->wPortStatus & PORT_STAT_SUSPEND) {
|
||||
port->wPortChange |= PORT_STAT_C_SUSPEND;
|
||||
@ -257,9 +200,9 @@ static void usb_hub_wakeup(USBDevice *dev)
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_hub_complete(USBDevice *dev, USBPacket *packet)
|
||||
static void usb_hub_complete(USBPort *port, USBPacket *packet)
|
||||
{
|
||||
USBHubState *s = dev->port->opaque;
|
||||
USBHubState *s = port->opaque;
|
||||
|
||||
/*
|
||||
* Just pass it along upstream for now.
|
||||
@ -537,6 +480,7 @@ static void usb_hub_handle_destroy(USBDevice *dev)
|
||||
static USBPortOps usb_hub_port_ops = {
|
||||
.attach = usb_hub_attach,
|
||||
.detach = usb_hub_detach,
|
||||
.child_detach = usb_hub_child_detach,
|
||||
.wakeup = usb_hub_wakeup,
|
||||
.complete = usb_hub_complete,
|
||||
};
|
||||
|
@ -261,17 +261,18 @@
|
||||
|
||||
static void musb_attach(USBPort *port);
|
||||
static void musb_detach(USBPort *port);
|
||||
static void musb_schedule_cb(USBDevice *dev, USBPacket *p);
|
||||
static void musb_device_destroy(USBBus *bus, USBDevice *dev);
|
||||
static void musb_child_detach(USBPort *port, USBDevice *child);
|
||||
static void musb_schedule_cb(USBPort *port, USBPacket *p);
|
||||
static void musb_async_cancel_device(MUSBState *s, USBDevice *dev);
|
||||
|
||||
static USBPortOps musb_port_ops = {
|
||||
.attach = musb_attach,
|
||||
.detach = musb_detach,
|
||||
.child_detach = musb_child_detach,
|
||||
.complete = musb_schedule_cb,
|
||||
};
|
||||
|
||||
static USBBusOps musb_bus_ops = {
|
||||
.device_destroy = musb_device_destroy,
|
||||
};
|
||||
|
||||
typedef struct MUSBPacket MUSBPacket;
|
||||
@ -369,7 +370,6 @@ struct MUSBState *musb_init(qemu_irq *irqs)
|
||||
usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */);
|
||||
usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||
usb_port_location(&s->port, NULL, 1);
|
||||
|
||||
return s;
|
||||
}
|
||||
@ -498,10 +498,19 @@ static void musb_detach(USBPort *port)
|
||||
{
|
||||
MUSBState *s = (MUSBState *) port->opaque;
|
||||
|
||||
musb_async_cancel_device(s, port->dev);
|
||||
|
||||
musb_intr_set(s, musb_irq_disconnect, 1);
|
||||
musb_session_update(s, 1, s->session);
|
||||
}
|
||||
|
||||
static void musb_child_detach(USBPort *port, USBDevice *child)
|
||||
{
|
||||
MUSBState *s = (MUSBState *) port->opaque;
|
||||
|
||||
musb_async_cancel_device(s, child);
|
||||
}
|
||||
|
||||
static void musb_cb_tick0(void *opaque)
|
||||
{
|
||||
MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
|
||||
@ -518,7 +527,7 @@ static void musb_cb_tick1(void *opaque)
|
||||
|
||||
#define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0)
|
||||
|
||||
static void musb_schedule_cb(USBDevice *dev, USBPacket *packey)
|
||||
static void musb_schedule_cb(USBPort *port, USBPacket *packey)
|
||||
{
|
||||
MUSBPacket *p = container_of(packey, MUSBPacket, p);
|
||||
MUSBEndPoint *ep = p->ep;
|
||||
@ -616,7 +625,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
||||
}
|
||||
|
||||
ep->status[dir] = ret;
|
||||
usb_packet_complete(s->port.dev, &ep->packey[dir].p);
|
||||
musb_schedule_cb(&s->port, &ep->packey[dir].p);
|
||||
}
|
||||
|
||||
static void musb_tx_packet_complete(USBPacket *packey, void *opaque)
|
||||
@ -783,9 +792,8 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
|
||||
musb_rx_intr_set(s, epnum, 1);
|
||||
}
|
||||
|
||||
static void musb_device_destroy(USBBus *bus, USBDevice *dev)
|
||||
static void musb_async_cancel_device(MUSBState *s, USBDevice *dev)
|
||||
{
|
||||
MUSBState *s = container_of(bus, MUSBState, bus);
|
||||
int ep, dir;
|
||||
|
||||
for (ep = 0; ep < 16; ep++) {
|
||||
|
@ -124,6 +124,7 @@ struct ohci_hcca {
|
||||
};
|
||||
|
||||
static void ohci_bus_stop(OHCIState *ohci);
|
||||
static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
|
||||
|
||||
/* Bitfields for the first word of an Endpoint Desciptor. */
|
||||
#define OHCI_ED_FA_SHIFT 0
|
||||
@ -326,6 +327,7 @@ static void ohci_attach(USBPort *port1)
|
||||
{
|
||||
OHCIState *s = port1->opaque;
|
||||
OHCIPort *port = &s->rhport[port1->index];
|
||||
uint32_t old_state = port->ctrl;
|
||||
|
||||
/* set connect status */
|
||||
port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
|
||||
@ -343,6 +345,10 @@ static void ohci_attach(USBPort *port1)
|
||||
}
|
||||
|
||||
DPRINTF("usb-ohci: Attached port %d\n", port1->index);
|
||||
|
||||
if (old_state != port->ctrl) {
|
||||
ohci_set_interrupt(s, OHCI_INTR_RHSC);
|
||||
}
|
||||
}
|
||||
|
||||
static void ohci_detach(USBPort *port1)
|
||||
@ -351,6 +357,8 @@ static void ohci_detach(USBPort *port1)
|
||||
OHCIPort *port = &s->rhport[port1->index];
|
||||
uint32_t old_state = port->ctrl;
|
||||
|
||||
ohci_async_cancel_device(s, port1->dev);
|
||||
|
||||
/* set connect status */
|
||||
if (port->ctrl & OHCI_PORT_CCS) {
|
||||
port->ctrl &= ~OHCI_PORT_CCS;
|
||||
@ -363,19 +371,18 @@ static void ohci_detach(USBPort *port1)
|
||||
}
|
||||
DPRINTF("usb-ohci: Detached port %d\n", port1->index);
|
||||
|
||||
if (old_state != port->ctrl)
|
||||
if (old_state != port->ctrl) {
|
||||
ohci_set_interrupt(s, OHCI_INTR_RHSC);
|
||||
}
|
||||
}
|
||||
|
||||
static void ohci_wakeup(USBDevice *dev)
|
||||
static void ohci_wakeup(USBPort *port1)
|
||||
{
|
||||
USBBus *bus = usb_bus_from_device(dev);
|
||||
OHCIState *s = container_of(bus, OHCIState, bus);
|
||||
int portnum = dev->port->index;
|
||||
OHCIPort *port = &s->rhport[portnum];
|
||||
OHCIState *s = port1->opaque;
|
||||
OHCIPort *port = &s->rhport[port1->index];
|
||||
uint32_t intr = 0;
|
||||
if (port->ctrl & OHCI_PORT_PSS) {
|
||||
DPRINTF("usb-ohci: port %d: wakeup\n", portnum);
|
||||
DPRINTF("usb-ohci: port %d: wakeup\n", port1->index);
|
||||
port->ctrl |= OHCI_PORT_PSSC;
|
||||
port->ctrl &= ~OHCI_PORT_PSS;
|
||||
intr = OHCI_INTR_RHSC;
|
||||
@ -394,6 +401,13 @@ static void ohci_wakeup(USBDevice *dev)
|
||||
ohci_set_interrupt(s, intr);
|
||||
}
|
||||
|
||||
static void ohci_child_detach(USBPort *port1, USBDevice *child)
|
||||
{
|
||||
OHCIState *s = port1->opaque;
|
||||
|
||||
ohci_async_cancel_device(s, child);
|
||||
}
|
||||
|
||||
/* Reset the controller */
|
||||
static void ohci_reset(void *opaque)
|
||||
{
|
||||
@ -602,7 +616,7 @@ static void ohci_copy_iso_td(OHCIState *ohci,
|
||||
|
||||
static void ohci_process_lists(OHCIState *ohci, int completion);
|
||||
|
||||
static void ohci_async_complete_packet(USBDevice *dev, USBPacket *packet)
|
||||
static void ohci_async_complete_packet(USBPort *port, USBPacket *packet)
|
||||
{
|
||||
OHCIState *ohci = container_of(packet, OHCIState, usb_packet);
|
||||
#ifdef DEBUG_PACKET
|
||||
@ -1675,10 +1689,8 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
|
||||
}
|
||||
}
|
||||
|
||||
static void ohci_device_destroy(USBBus *bus, USBDevice *dev)
|
||||
static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev)
|
||||
{
|
||||
OHCIState *ohci = container_of(bus, OHCIState, bus);
|
||||
|
||||
if (ohci->async_td && ohci->usb_packet.owner == dev) {
|
||||
usb_cancel_packet(&ohci->usb_packet);
|
||||
ohci->async_td = 0;
|
||||
@ -1702,16 +1714,17 @@ static CPUWriteMemoryFunc * const ohci_writefn[3]={
|
||||
static USBPortOps ohci_port_ops = {
|
||||
.attach = ohci_attach,
|
||||
.detach = ohci_detach,
|
||||
.child_detach = ohci_child_detach,
|
||||
.wakeup = ohci_wakeup,
|
||||
.complete = ohci_async_complete_packet,
|
||||
};
|
||||
|
||||
static USBBusOps ohci_bus_ops = {
|
||||
.device_destroy = ohci_device_destroy,
|
||||
};
|
||||
|
||||
static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
|
||||
int num_ports, uint32_t localmem_base)
|
||||
static int usb_ohci_init(OHCIState *ohci, DeviceState *dev,
|
||||
int num_ports, uint32_t localmem_base,
|
||||
char *masterbus, uint32_t firstport)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -1731,39 +1744,58 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
|
||||
usb_frame_time, usb_bit_time);
|
||||
}
|
||||
|
||||
ohci->num_ports = num_ports;
|
||||
if (masterbus) {
|
||||
USBPort *ports[OHCI_MAX_PORTS];
|
||||
for(i = 0; i < num_ports; i++) {
|
||||
ports[i] = &ohci->rhport[i].port;
|
||||
}
|
||||
if (usb_register_companion(masterbus, ports, num_ports,
|
||||
firstport, ohci, &ohci_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
usb_bus_new(&ohci->bus, &ohci_bus_ops, dev);
|
||||
for (i = 0; i < num_ports; i++) {
|
||||
usb_register_port(&ohci->bus, &ohci->rhport[i].port,
|
||||
ohci, i, &ohci_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||
}
|
||||
}
|
||||
|
||||
ohci->mem = cpu_register_io_memory(ohci_readfn, ohci_writefn, ohci,
|
||||
DEVICE_LITTLE_ENDIAN);
|
||||
ohci->localmem_base = localmem_base;
|
||||
|
||||
ohci->name = dev->info->name;
|
||||
|
||||
usb_bus_new(&ohci->bus, &ohci_bus_ops, dev);
|
||||
ohci->num_ports = num_ports;
|
||||
for (i = 0; i < num_ports; i++) {
|
||||
usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, &ohci_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||
usb_port_location(&ohci->rhport[i].port, NULL, i+1);
|
||||
}
|
||||
|
||||
ohci->async_td = 0;
|
||||
qemu_register_reset(ohci_reset, ohci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
PCIDevice pci_dev;
|
||||
OHCIState state;
|
||||
char *masterbus;
|
||||
uint32_t num_ports;
|
||||
uint32_t firstport;
|
||||
} OHCIPCIState;
|
||||
|
||||
static int usb_ohci_initfn_pci(struct PCIDevice *dev)
|
||||
{
|
||||
OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, dev);
|
||||
int num_ports = 3;
|
||||
|
||||
ohci->pci_dev.config[PCI_CLASS_PROG] = 0x10; /* OHCI */
|
||||
/* TODO: RST# value should be 0. */
|
||||
ohci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
|
||||
|
||||
usb_ohci_init(&ohci->state, &dev->qdev, num_ports, 0);
|
||||
if (usb_ohci_init(&ohci->state, &dev->qdev, ohci->num_ports, 0,
|
||||
ohci->masterbus, ohci->firstport) != 0) {
|
||||
return -1;
|
||||
}
|
||||
ohci->state.irq = ohci->pci_dev.irq[0];
|
||||
|
||||
/* TODO: avoid cast below by using dev */
|
||||
@ -1787,7 +1819,8 @@ static int ohci_init_pxa(SysBusDevice *dev)
|
||||
{
|
||||
OHCISysBusState *s = FROM_SYSBUS(OHCISysBusState, dev);
|
||||
|
||||
usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset);
|
||||
/* Cannot fail as we pass NULL for masterbus */
|
||||
usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0);
|
||||
sysbus_init_irq(dev, &s->ohci.irq);
|
||||
sysbus_init_mmio(dev, 0x1000, s->ohci.mem);
|
||||
|
||||
@ -1802,6 +1835,12 @@ static PCIDeviceInfo ohci_pci_info = {
|
||||
.vendor_id = PCI_VENDOR_ID_APPLE,
|
||||
.device_id = PCI_DEVICE_ID_APPLE_IPID_USB,
|
||||
.class_id = PCI_CLASS_SERIAL_USB,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus),
|
||||
DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3),
|
||||
DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
|
||||
static SysBusDeviceInfo ohci_sysbus_info = {
|
||||
|
@ -132,7 +132,7 @@ typedef struct UHCIPort {
|
||||
|
||||
struct UHCIState {
|
||||
PCIDevice dev;
|
||||
USBBus bus;
|
||||
USBBus bus; /* Note unused when we're a companion controller */
|
||||
uint16_t cmd; /* cmd register */
|
||||
uint16_t status;
|
||||
uint16_t intr; /* interrupt enable register */
|
||||
@ -150,6 +150,10 @@ struct UHCIState {
|
||||
/* Active packets */
|
||||
QTAILQ_HEAD(,UHCIAsync) async_pending;
|
||||
uint8_t num_ports_vmstate;
|
||||
|
||||
/* Properties */
|
||||
char *masterbus;
|
||||
uint32_t firstport;
|
||||
};
|
||||
|
||||
typedef struct UHCI_TD {
|
||||
@ -606,6 +610,8 @@ static void uhci_detach(USBPort *port1)
|
||||
UHCIState *s = port1->opaque;
|
||||
UHCIPort *port = &s->ports[port1->index];
|
||||
|
||||
uhci_async_cancel_device(s, port1->dev);
|
||||
|
||||
/* set connect status */
|
||||
if (port->ctrl & UHCI_PORT_CCS) {
|
||||
port->ctrl &= ~UHCI_PORT_CCS;
|
||||
@ -620,11 +626,17 @@ static void uhci_detach(USBPort *port1)
|
||||
uhci_resume(s);
|
||||
}
|
||||
|
||||
static void uhci_wakeup(USBDevice *dev)
|
||||
static void uhci_child_detach(USBPort *port1, USBDevice *child)
|
||||
{
|
||||
USBBus *bus = usb_bus_from_device(dev);
|
||||
UHCIState *s = container_of(bus, UHCIState, bus);
|
||||
UHCIPort *port = s->ports + dev->port->index;
|
||||
UHCIState *s = port1->opaque;
|
||||
|
||||
uhci_async_cancel_device(s, child);
|
||||
}
|
||||
|
||||
static void uhci_wakeup(USBPort *port1)
|
||||
{
|
||||
UHCIState *s = port1->opaque;
|
||||
UHCIPort *port = &s->ports[port1->index];
|
||||
|
||||
if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) {
|
||||
port->ctrl |= UHCI_PORT_RD;
|
||||
@ -657,7 +669,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void uhci_async_complete(USBDevice *dev, USBPacket *packet);
|
||||
static void uhci_async_complete(USBPort *port, USBPacket *packet);
|
||||
static void uhci_process_frame(UHCIState *s);
|
||||
|
||||
/* return -1 if fatal error (frame must be stopped)
|
||||
@ -849,7 +861,7 @@ done:
|
||||
return len;
|
||||
}
|
||||
|
||||
static void uhci_async_complete(USBDevice *dev, USBPacket *packet)
|
||||
static void uhci_async_complete(USBPort *port, USBPacket *packet)
|
||||
{
|
||||
UHCIAsync *async = container_of(packet, UHCIAsync, packet);
|
||||
UHCIState *s = async->uhci;
|
||||
@ -1096,22 +1108,15 @@ static void uhci_map(PCIDevice *pci_dev, int region_num,
|
||||
register_ioport_read(addr, 32, 1, uhci_ioport_readb, s);
|
||||
}
|
||||
|
||||
static void uhci_device_destroy(USBBus *bus, USBDevice *dev)
|
||||
{
|
||||
UHCIState *s = container_of(bus, UHCIState, bus);
|
||||
|
||||
uhci_async_cancel_device(s, dev);
|
||||
}
|
||||
|
||||
static USBPortOps uhci_port_ops = {
|
||||
.attach = uhci_attach,
|
||||
.detach = uhci_detach,
|
||||
.child_detach = uhci_child_detach,
|
||||
.wakeup = uhci_wakeup,
|
||||
.complete = uhci_async_complete,
|
||||
};
|
||||
|
||||
static USBBusOps uhci_bus_ops = {
|
||||
.device_destroy = uhci_device_destroy,
|
||||
};
|
||||
|
||||
static int usb_uhci_common_initfn(PCIDevice *dev)
|
||||
@ -1125,11 +1130,22 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
|
||||
pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3
|
||||
pci_conf[USB_SBRN] = USB_RELEASE_1; // release number
|
||||
|
||||
usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev);
|
||||
for(i = 0; i < NB_PORTS; i++) {
|
||||
usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||
usb_port_location(&s->ports[i].port, NULL, i+1);
|
||||
if (s->masterbus) {
|
||||
USBPort *ports[NB_PORTS];
|
||||
for(i = 0; i < NB_PORTS; i++) {
|
||||
ports[i] = &s->ports[i].port;
|
||||
}
|
||||
if (usb_register_companion(s->masterbus, ports, NB_PORTS,
|
||||
s->firstport, s, &uhci_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev);
|
||||
for (i = 0; i < NB_PORTS; i++) {
|
||||
usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||
}
|
||||
}
|
||||
s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s);
|
||||
s->num_ports_vmstate = NB_PORTS;
|
||||
@ -1160,6 +1176,12 @@ static int usb_uhci_vt82c686b_initfn(PCIDevice *dev)
|
||||
return usb_uhci_common_initfn(dev);
|
||||
}
|
||||
|
||||
static Property uhci_properties[] = {
|
||||
DEFINE_PROP_STRING("masterbus", UHCIState, masterbus),
|
||||
DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static PCIDeviceInfo uhci_info[] = {
|
||||
{
|
||||
.qdev.name = "piix3-usb-uhci",
|
||||
@ -1170,6 +1192,7 @@ static PCIDeviceInfo uhci_info[] = {
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82371SB_2,
|
||||
.revision = 0x01,
|
||||
.class_id = PCI_CLASS_SERIAL_USB,
|
||||
.qdev.props = uhci_properties,
|
||||
},{
|
||||
.qdev.name = "piix4-usb-uhci",
|
||||
.qdev.size = sizeof(UHCIState),
|
||||
@ -1179,6 +1202,7 @@ static PCIDeviceInfo uhci_info[] = {
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82371AB_2,
|
||||
.revision = 0x01,
|
||||
.class_id = PCI_CLASS_SERIAL_USB,
|
||||
.qdev.props = uhci_properties,
|
||||
},{
|
||||
.qdev.name = "vt82c686b-usb-uhci",
|
||||
.qdev.size = sizeof(UHCIState),
|
||||
@ -1188,6 +1212,37 @@ static PCIDeviceInfo uhci_info[] = {
|
||||
.device_id = PCI_DEVICE_ID_VIA_UHCI,
|
||||
.revision = 0x01,
|
||||
.class_id = PCI_CLASS_SERIAL_USB,
|
||||
.qdev.props = uhci_properties,
|
||||
},{
|
||||
.qdev.name = "ich9-usb-uhci1",
|
||||
.qdev.size = sizeof(UHCIState),
|
||||
.qdev.vmsd = &vmstate_uhci,
|
||||
.init = usb_uhci_common_initfn,
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1,
|
||||
.revision = 0x03,
|
||||
.class_id = PCI_CLASS_SERIAL_USB,
|
||||
.qdev.props = uhci_properties,
|
||||
},{
|
||||
.qdev.name = "ich9-usb-uhci2",
|
||||
.qdev.size = sizeof(UHCIState),
|
||||
.qdev.vmsd = &vmstate_uhci,
|
||||
.init = usb_uhci_common_initfn,
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2,
|
||||
.revision = 0x03,
|
||||
.class_id = PCI_CLASS_SERIAL_USB,
|
||||
.qdev.props = uhci_properties,
|
||||
},{
|
||||
.qdev.name = "ich9-usb-uhci3",
|
||||
.qdev.size = sizeof(UHCIState),
|
||||
.qdev.vmsd = &vmstate_uhci,
|
||||
.init = usb_uhci_common_initfn,
|
||||
.vendor_id = PCI_VENDOR_ID_INTEL,
|
||||
.device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3,
|
||||
.revision = 0x03,
|
||||
.class_id = PCI_CLASS_SERIAL_USB,
|
||||
.qdev.props = uhci_properties,
|
||||
},{
|
||||
/* end of list */
|
||||
}
|
||||
|
13
hw/usb.c
13
hw/usb.c
@ -40,19 +40,18 @@ void usb_attach(USBPort *port, USBDevice *dev)
|
||||
} else {
|
||||
/* detach */
|
||||
dev = port->dev;
|
||||
assert(dev);
|
||||
port->ops->detach(port);
|
||||
if (dev) {
|
||||
usb_send_msg(dev, USB_MSG_DETACH);
|
||||
dev->port = NULL;
|
||||
port->dev = NULL;
|
||||
}
|
||||
usb_send_msg(dev, USB_MSG_DETACH);
|
||||
dev->port = NULL;
|
||||
port->dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_wakeup(USBDevice *dev)
|
||||
{
|
||||
if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
|
||||
dev->port->ops->wakeup(dev);
|
||||
dev->port->ops->wakeup(dev->port);
|
||||
}
|
||||
}
|
||||
|
||||
@ -335,7 +334,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
/* Note: p->owner != dev is possible in case dev is a hub */
|
||||
assert(p->owner != NULL);
|
||||
dev->port->ops->complete(dev, p);
|
||||
dev->port->ops->complete(dev->port, p);
|
||||
p->owner = NULL;
|
||||
}
|
||||
|
||||
|
20
hw/usb.h
20
hw/usb.h
@ -252,8 +252,18 @@ struct USBDeviceInfo {
|
||||
typedef struct USBPortOps {
|
||||
void (*attach)(USBPort *port);
|
||||
void (*detach)(USBPort *port);
|
||||
void (*wakeup)(USBDevice *dev);
|
||||
void (*complete)(USBDevice *dev, USBPacket *p);
|
||||
/*
|
||||
* This gets called when a device downstream from the device attached to
|
||||
* the port (iow attached through a hub) gets detached.
|
||||
*/
|
||||
void (*child_detach)(USBPort *port, USBDevice *child);
|
||||
void (*wakeup)(USBPort *port);
|
||||
/*
|
||||
* Note that port->dev will be different then the device from which
|
||||
* the packet originated when a hub is involved, if you want the orginating
|
||||
* device use p->owner
|
||||
*/
|
||||
void (*complete)(USBPort *port, USBPacket *p);
|
||||
} USBPortOps;
|
||||
|
||||
/* USB port on which a device can be connected */
|
||||
@ -344,7 +354,8 @@ struct USBBus {
|
||||
};
|
||||
|
||||
struct USBBusOps {
|
||||
void (*device_destroy)(USBBus *bus, USBDevice *dev);
|
||||
int (*register_companion)(USBBus *bus, USBPort *ports[],
|
||||
uint32_t portcount, uint32_t firstport);
|
||||
};
|
||||
|
||||
void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
|
||||
@ -356,6 +367,9 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name);
|
||||
USBDevice *usbdevice_create(const char *cmdline);
|
||||
void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
|
||||
USBPortOps *ops, int speedmask);
|
||||
int usb_register_companion(const char *masterbus, USBPort *ports[],
|
||||
uint32_t portcount, uint32_t firstport,
|
||||
void *opaque, USBPortOps *ops, int speedmask);
|
||||
void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr);
|
||||
void usb_unregister_port(USBBus *bus, USBPort *port);
|
||||
int usb_device_attach(USBDevice *dev);
|
||||
|
Loading…
Reference in New Issue
Block a user