Merge remote-tracking branch 'kraxel/usb.19' into staging

This commit is contained in:
Anthony Liguori 2011-07-19 08:03:20 -05:00
commit b4dabf9587
13 changed files with 523 additions and 235 deletions

37
docs/ich9-ehci-uhci.cfg Normal file
View 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"

View File

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

View File

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

View File

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

View File

@ -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,
},

View File

@ -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) {

View File

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

View File

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

View File

@ -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++) {

View File

@ -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 = {

View File

@ -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 */
}

View File

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

View File

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