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

This commit is contained in:
Anthony Liguori 2011-09-08 08:57:33 -05:00
commit a60fce0bcc
13 changed files with 561 additions and 456 deletions

View File

@ -771,13 +771,12 @@ static void tusb6010_reset(DeviceState *dev)
for (i = 0; i < 15; i++) { for (i = 0; i < 15; i++) {
s->rx_config[i] = s->tx_config[i] = 0; s->rx_config[i] = s->tx_config[i] = 0;
} }
musb_reset(s->musb);
} }
static int tusb6010_init(SysBusDevice *dev) static int tusb6010_init(SysBusDevice *dev)
{ {
TUSBState *s = FROM_SYSBUS(TUSBState, dev); TUSBState *s = FROM_SYSBUS(TUSBState, dev);
qemu_irq *musb_irqs;
int i;
s->otg_timer = qemu_new_timer_ns(vm_clock, tusb_otg_tick, s); s->otg_timer = qemu_new_timer_ns(vm_clock, tusb_otg_tick, s);
s->pwr_timer = qemu_new_timer_ns(vm_clock, tusb_power_tick, s); s->pwr_timer = qemu_new_timer_ns(vm_clock, tusb_power_tick, s);
memory_region_init_io(&s->iomem[1], &tusb_async_ops, s, "tusb-async", memory_region_init_io(&s->iomem[1], &tusb_async_ops, s, "tusb-async",
@ -785,12 +784,8 @@ static int tusb6010_init(SysBusDevice *dev)
sysbus_init_mmio_region(dev, &s->iomem[0]); sysbus_init_mmio_region(dev, &s->iomem[0]);
sysbus_init_mmio_region(dev, &s->iomem[1]); sysbus_init_mmio_region(dev, &s->iomem[1]);
sysbus_init_irq(dev, &s->irq); sysbus_init_irq(dev, &s->irq);
qdev_init_gpio_in(&dev->qdev, tusb6010_irq, __musb_irq_max + 1); qdev_init_gpio_in(&dev->qdev, tusb6010_irq, musb_irq_max + 1);
musb_irqs = g_new0(qemu_irq, __musb_irq_max); s->musb = musb_init(&dev->qdev, 1);
for (i = 0; i < __musb_irq_max; i++) {
musb_irqs[i] = qdev_get_gpio_in(&dev->qdev, i + 1);
}
s->musb = musb_init(musb_irqs);
return 0; return 0;
} }

View File

@ -3,6 +3,7 @@
#include "qdev.h" #include "qdev.h"
#include "sysemu.h" #include "sysemu.h"
#include "monitor.h" #include "monitor.h"
#include "trace.h"
static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
@ -73,9 +74,13 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
dev->info = info; dev->info = info;
dev->auto_attach = 1; dev->auto_attach = 1;
QLIST_INIT(&dev->strings); QLIST_INIT(&dev->strings);
rc = dev->info->init(dev); rc = usb_claim_port(dev);
if (rc == 0 && dev->auto_attach) if (rc == 0) {
rc = dev->info->init(dev);
}
if (rc == 0 && dev->auto_attach) {
rc = usb_device_attach(dev); rc = usb_device_attach(dev);
}
return rc; return rc;
} }
@ -89,6 +94,9 @@ static int usb_qdev_exit(DeviceState *qdev)
if (dev->info->handle_destroy) { if (dev->info->handle_destroy) {
dev->info->handle_destroy(dev); dev->info->handle_destroy(dev);
} }
if (dev->port) {
usb_release_port(dev);
}
return 0; return 0;
} }
@ -205,21 +213,13 @@ void usb_unregister_port(USBBus *bus, USBPort *port)
bus->nfree--; bus->nfree--;
} }
static int do_attach(USBDevice *dev) int usb_claim_port(USBDevice *dev)
{ {
USBBus *bus = usb_bus_from_device(dev); USBBus *bus = usb_bus_from_device(dev);
USBPort *port; USBPort *port;
if (dev->attached) { assert(dev->port == NULL);
error_report("Error: tried to attach usb device %s twice\n",
dev->product_desc);
return -1;
}
if (bus->nfree == 0) {
error_report("Error: tried to attach usb device %s to a bus with no free ports\n",
dev->product_desc);
return -1;
}
if (dev->port_path) { if (dev->port_path) {
QTAILQ_FOREACH(port, &bus->free, next) { QTAILQ_FOREACH(port, &bus->free, next) {
if (strcmp(port->path, dev->port_path) == 0) { if (strcmp(port->path, dev->port_path) == 0) {
@ -227,68 +227,86 @@ static int do_attach(USBDevice *dev)
} }
} }
if (port == NULL) { if (port == NULL) {
error_report("Error: usb port %s (bus %s) not found\n", error_report("Error: usb port %s (bus %s) not found (in use?)\n",
dev->port_path, bus->qbus.name); dev->port_path, bus->qbus.name);
return -1; return -1;
} }
} else { } else {
if (bus->nfree == 1 && strcmp(dev->qdev.info->name, "usb-hub") != 0) {
/* Create a new hub and chain it on */
usb_create_simple(bus, "usb-hub");
}
if (bus->nfree == 0) {
error_report("Error: tried to attach usb device %s to a bus "
"with no free ports\n", dev->product_desc);
return -1;
}
port = QTAILQ_FIRST(&bus->free); port = QTAILQ_FIRST(&bus->free);
} }
if (!(port->speedmask & dev->speedmask)) { trace_usb_port_claim(bus->busnr, port->path);
error_report("Warning: speed mismatch trying to attach usb device %s to bus %s\n",
dev->product_desc, bus->qbus.name);
return -1;
}
dev->attached++;
QTAILQ_REMOVE(&bus->free, port, next); QTAILQ_REMOVE(&bus->free, port, next);
bus->nfree--; bus->nfree--;
usb_attach(port, dev); dev->port = port;
port->dev = dev;
QTAILQ_INSERT_TAIL(&bus->used, port, next); QTAILQ_INSERT_TAIL(&bus->used, port, next);
bus->nused++; bus->nused++;
return 0; return 0;
} }
void usb_release_port(USBDevice *dev)
{
USBBus *bus = usb_bus_from_device(dev);
USBPort *port = dev->port;
assert(port != NULL);
trace_usb_port_release(bus->busnr, port->path);
QTAILQ_REMOVE(&bus->used, port, next);
bus->nused--;
dev->port = NULL;
port->dev = NULL;
QTAILQ_INSERT_TAIL(&bus->free, port, next);
bus->nfree++;
}
int usb_device_attach(USBDevice *dev) int usb_device_attach(USBDevice *dev)
{ {
USBBus *bus = usb_bus_from_device(dev); USBBus *bus = usb_bus_from_device(dev);
USBPort *port = dev->port;
if (bus->nfree == 1 && dev->port_path == NULL) { assert(port != NULL);
/* Create a new hub and chain it on assert(!dev->attached);
(unless a physical port location is specified). */ trace_usb_port_attach(bus->busnr, port->path);
usb_create_simple(bus, "usb-hub");
if (!(port->speedmask & dev->speedmask)) {
error_report("Warning: speed mismatch trying to attach "
"usb device %s to bus %s\n",
dev->product_desc, bus->qbus.name);
return -1;
} }
return do_attach(dev);
dev->attached++;
usb_attach(port);
return 0;
} }
int usb_device_detach(USBDevice *dev) int usb_device_detach(USBDevice *dev)
{ {
USBBus *bus = usb_bus_from_device(dev); USBBus *bus = usb_bus_from_device(dev);
USBPort *port; USBPort *port = dev->port;
if (!dev->attached) {
error_report("Error: tried to detach unattached usb device %s\n",
dev->product_desc);
return -1;
}
dev->attached--;
QTAILQ_FOREACH(port, &bus->used, next) {
if (port->dev == dev)
break;
}
assert(port != NULL); assert(port != NULL);
assert(dev->attached);
trace_usb_port_detach(bus->busnr, port->path);
QTAILQ_REMOVE(&bus->used, port, next); usb_detach(port);
bus->nused--; dev->attached--;
usb_attach(port, NULL);
QTAILQ_INSERT_TAIL(&bus->free, port, next);
bus->nfree++;
return 0; return 0;
} }

View File

@ -37,6 +37,7 @@
#include "qemu-common.h" #include "qemu-common.h"
#include "qemu-error.h" #include "qemu-error.h"
#include "usb.h" #include "usb.h"
#include "usb-desc.h"
#include "monitor.h" #include "monitor.h"
#include "hw/ccid.h" #include "hw/ccid.h"
@ -306,56 +307,7 @@ typedef struct USBCCIDState {
* 0dc3:1004 Athena Smartcard Solutions, Inc. * 0dc3:1004 Athena Smartcard Solutions, Inc.
*/ */
static const uint8_t qemu_ccid_dev_descriptor[] = { static const uint8_t qemu_ccid_descriptor[] = {
0x12, /* u8 bLength; */
USB_DT_DEVICE, /* u8 bDescriptorType; Device */
0x10, 0x01, /* u16 bcdUSB; v1.1 */
0x00, /* u8 bDeviceClass; */
0x00, /* u8 bDeviceSubClass; */
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
0x40, /* u8 bMaxPacketSize0; 8 Bytes (valid: 8,16,32,64) */
/* Vendor and product id are arbitrary. */
/* u16 idVendor */
CCID_VENDOR_ID & 0xff, CCID_VENDOR_ID >> 8,
/* u16 idProduct */
CCID_PRODUCT_ID & 0xff, CCID_PRODUCT_ID >> 8,
/* u16 bcdDevice */
CCID_DEVICE_VERSION & 0xff, CCID_DEVICE_VERSION >> 8,
0x01, /* u8 iManufacturer; */
0x02, /* u8 iProduct; */
0x03, /* u8 iSerialNumber; */
0x01, /* u8 bNumConfigurations; */
};
static const uint8_t qemu_ccid_config_descriptor[] = {
/* one configuration */
0x09, /* u8 bLength; */
USB_DT_CONFIG, /* u8 bDescriptorType; Configuration */
0x5d, 0x00, /* u16 wTotalLength; 9+9+54+7+7+7 */
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 */
100/2, /* u8 MaxPower; 50 == 100mA */
/* one interface */
0x09, /* u8 if_bLength; */
USB_DT_INTERFACE, /* u8 if_bDescriptorType; Interface */
0x00, /* u8 if_bInterfaceNumber; */
0x00, /* u8 if_bAlternateSetting; */
0x03, /* u8 if_bNumEndpoints; */
0x0b, /* u8 if_bInterfaceClass; Smart Card Device Class */
0x00, /* u8 if_bInterfaceSubClass; Subclass code */
0x00, /* u8 if_bInterfaceProtocol; Protocol code */
0x04, /* u8 if_iInterface; Index of string descriptor */
/* Smart Card Device Class Descriptor */ /* Smart Card Device Class Descriptor */
0x36, /* u8 bLength; */ 0x36, /* u8 bLength; */
0x21, /* u8 bDescriptorType; Functional */ 0x21, /* u8 bDescriptorType; Functional */
@ -439,38 +391,81 @@ static const uint8_t qemu_ccid_config_descriptor[] = {
* 02h PIN Modification * 02h PIN Modification
*/ */
0x01, /* u8 bMaxCCIDBusySlots; */ 0x01, /* u8 bMaxCCIDBusySlots; */
};
/* Interrupt-IN endpoint */ enum {
0x07, /* u8 ep_bLength; */ STR_MANUFACTURER = 1,
/* u8 ep_bDescriptorType; Endpoint */ STR_PRODUCT,
USB_DT_ENDPOINT, STR_SERIALNUMBER,
/* u8 ep_bEndpointAddress; IN Endpoint 1 */ STR_INTERFACE,
0x80 | CCID_INT_IN_EP, };
0x03, /* u8 ep_bmAttributes; Interrupt */
/* u16 ep_wMaxPacketSize; */
CCID_MAX_PACKET_SIZE & 0xff, (CCID_MAX_PACKET_SIZE >> 8),
0xff, /* u8 ep_bInterval; */
/* Bulk-In endpoint */ static const USBDescStrings desc_strings = {
0x07, /* u8 ep_bLength; */ [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
/* u8 ep_bDescriptorType; Endpoint */ [STR_PRODUCT] = "QEMU USB CCID",
USB_DT_ENDPOINT, [STR_SERIALNUMBER] = "1",
/* u8 ep_bEndpointAddress; IN Endpoint 2 */ [STR_INTERFACE] = "CCID Interface",
0x80 | CCID_BULK_IN_EP, };
0x02, /* u8 ep_bmAttributes; Bulk */
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
0x00, /* u8 ep_bInterval; */
/* Bulk-Out endpoint */ static const USBDescIface desc_iface0 = {
0x07, /* u8 ep_bLength; */ .bInterfaceNumber = 0,
/* u8 ep_bDescriptorType; Endpoint */ .bNumEndpoints = 3,
USB_DT_ENDPOINT, .bInterfaceClass = 0x0b,
/* u8 ep_bEndpointAddress; OUT Endpoint 3 */ .bInterfaceSubClass = 0x00,
CCID_BULK_OUT_EP, .bInterfaceProtocol = 0x00,
0x02, /* u8 ep_bmAttributes; Bulk */ .iInterface = STR_INTERFACE,
0x40, 0x00, /* u16 ep_wMaxPacketSize; */ .ndesc = 1,
0x00, /* u8 ep_bInterval; */ .descs = (USBDescOther[]) {
{
/* smartcard descriptor */
.data = qemu_ccid_descriptor,
},
},
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_IN | CCID_INT_IN_EP,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.bInterval = 255,
.wMaxPacketSize = 64,
},{
.bEndpointAddress = USB_DIR_IN | CCID_BULK_IN_EP,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64,
},{
.bEndpointAddress = USB_DIR_OUT | CCID_BULK_OUT_EP,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64,
},
}
};
static const USBDescDevice desc_device = {
.bcdUSB = 0x0110,
.bMaxPacketSize0 = 64,
.bNumConfigurations = 1,
.confs = (USBDescConfig[]) {
{
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.bmAttributes = 0xa0,
.bMaxPower = 50,
.nif = 1,
.ifs = &desc_iface0,
},
},
};
static const USBDesc desc_ccid = {
.id = {
.idVendor = CCID_VENDOR_ID,
.idProduct = CCID_PRODUCT_ID,
.bcdDevice = CCID_DEVICE_VERSION,
.iManufacturer = STR_MANUFACTURER,
.iProduct = STR_PRODUCT,
.iSerialNumber = STR_SERIALNUMBER,
},
.full = &desc_device,
.str = desc_strings,
}; };
static bool ccid_has_pending_answers(USBCCIDState *s) static bool ccid_has_pending_answers(USBCCIDState *s)
@ -610,87 +605,12 @@ static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
int ret = 0; int ret = 0;
DPRINTF(s, 1, "got control %x, value %x\n", request, value); DPRINTF(s, 1, "got control %x, value %x\n", request, value);
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
}
switch (request) { switch (request) {
case DeviceRequest | USB_REQ_GET_STATUS:
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
data[1] = 0x00;
ret = 2;
break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
if (value == USB_DEVICE_REMOTE_WAKEUP) {
dev->remote_wakeup = 0;
} else {
goto fail;
}
ret = 0;
break;
case DeviceOutRequest | USB_REQ_SET_FEATURE:
if (value == USB_DEVICE_REMOTE_WAKEUP) {
dev->remote_wakeup = 1;
} else {
goto fail;
}
ret = 0;
break;
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
dev->addr = value;
ret = 0;
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch (value >> 8) {
case USB_DT_DEVICE:
memcpy(data, qemu_ccid_dev_descriptor,
sizeof(qemu_ccid_dev_descriptor));
ret = sizeof(qemu_ccid_dev_descriptor);
break;
case USB_DT_CONFIG:
memcpy(data, qemu_ccid_config_descriptor,
sizeof(qemu_ccid_config_descriptor));
ret = sizeof(qemu_ccid_config_descriptor);
break;
case USB_DT_STRING:
switch (value & 0xff) {
case 0:
/* language ids */
data[0] = 4;
data[1] = 3;
data[2] = 0x09;
data[3] = 0x04;
ret = 4;
break;
case 1:
/* vendor description */
ret = set_usb_string(data, CCID_VENDOR_DESCRIPTION);
break;
case 2:
/* product description */
ret = set_usb_string(data, CCID_PRODUCT_DESCRIPTION);
break;
case 3:
/* serial number */
ret = set_usb_string(data, CCID_SERIAL_NUMBER_STRING);
break;
case 4:
/* interface name */
ret = set_usb_string(data, CCID_INTERFACE_NAME);
break;
default:
goto fail;
}
break;
default:
goto fail;
}
break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
data[0] = 1;
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
/* Only one configuration - we just ignore the request */
ret = 0;
break;
case DeviceRequest | USB_REQ_GET_INTERFACE: case DeviceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0; data[0] = 0;
ret = 1; ret = 1;
@ -698,9 +618,6 @@ static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
case InterfaceOutRequest | USB_REQ_SET_INTERFACE: case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
ret = 0; ret = 0;
break; break;
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
ret = 0;
break;
/* Class specific requests. */ /* Class specific requests. */
case InterfaceOutClass | CCID_CONTROL_ABORT: case InterfaceOutClass | CCID_CONTROL_ABORT:
@ -716,7 +633,6 @@ static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
ret = USB_RET_STALL; ret = USB_RET_STALL;
break; break;
default: default:
fail:
DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n", DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n",
request, value); request, value);
ret = USB_RET_STALL; ret = USB_RET_STALL;
@ -895,6 +811,7 @@ static void ccid_on_slot_change(USBCCIDState *s, bool full)
s->bmSlotICCState |= SLOT_0_CHANGED_MASK; s->bmSlotICCState |= SLOT_0_CHANGED_MASK;
} }
s->notify_slot_change = true; s->notify_slot_change = true;
usb_wakeup(&s->dev);
} }
static void ccid_write_data_block_error( static void ccid_write_data_block_error(
@ -1075,6 +992,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
break; break;
default: default:
DPRINTF(s, 1, "Bad endpoint\n"); DPRINTF(s, 1, "Bad endpoint\n");
ret = USB_RET_STALL;
break; break;
} }
break; break;
@ -1256,6 +1174,7 @@ static int ccid_initfn(USBDevice *dev)
{ {
USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
usb_desc_init(dev);
qbus_create_inplace(&s->bus.qbus, &ccid_bus_info, &dev->qdev, NULL); qbus_create_inplace(&s->bus.qbus, &ccid_bus_info, &dev->qdev, NULL);
s->bus.qbus.allow_hotplug = 1; s->bus.qbus.allow_hotplug = 1;
s->card = NULL; s->card = NULL;
@ -1381,6 +1300,7 @@ static struct USBDeviceInfo ccid_info = {
.qdev.desc = "CCID Rev 1.1 smartcard reader", .qdev.desc = "CCID Rev 1.1 smartcard reader",
.qdev.size = sizeof(USBCCIDState), .qdev.size = sizeof(USBCCIDState),
.init = ccid_initfn, .init = ccid_initfn,
.usb_desc = &desc_ccid,
.handle_packet = usb_generic_handle_packet, .handle_packet = usb_generic_handle_packet,
.handle_reset = ccid_handle_reset, .handle_reset = ccid_handle_reset,
.handle_control = ccid_handle_control, .handle_control = ccid_handle_control,

View File

@ -75,7 +75,7 @@ struct USBDescEndpoint {
struct USBDescOther { struct USBDescOther {
uint8_t length; uint8_t length;
uint8_t *data; const uint8_t *data;
}; };
typedef const char *USBDescStrings[256]; typedef const char *USBDescStrings[256];

View File

@ -149,6 +149,7 @@ typedef enum {
EST_FETCHENTRY, EST_FETCHENTRY,
EST_FETCHQH, EST_FETCHQH,
EST_FETCHITD, EST_FETCHITD,
EST_FETCHSITD,
EST_ADVANCEQUEUE, EST_ADVANCEQUEUE,
EST_FETCHQTD, EST_FETCHQTD,
EST_EXECUTE, EST_EXECUTE,
@ -646,6 +647,13 @@ static void ehci_trace_itd(EHCIState *s, target_phys_addr_t addr, EHCIitd *itd)
get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR)); get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR));
} }
static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr,
EHCIsitd *sitd)
{
trace_usb_ehci_sitd(addr, sitd->next,
(bool)(sitd->results & SITD_RESULTS_ACTIVE));
}
/* queue management */ /* queue management */
static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async) static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async)
@ -849,8 +857,8 @@ static void ehci_reset(void *opaque)
*/ */
for(i = 0; i < NB_PORTS; i++) { for(i = 0; i < NB_PORTS; i++) {
devs[i] = s->ports[i].dev; devs[i] = s->ports[i].dev;
if (devs[i]) { if (devs[i] && devs[i]->attached) {
usb_attach(&s->ports[i], NULL); usb_detach(&s->ports[i]);
} }
} }
@ -870,8 +878,8 @@ static void ehci_reset(void *opaque)
} else { } else {
s->portsc[i] = PORTSC_PPOWER; s->portsc[i] = PORTSC_PPOWER;
} }
if (devs[i]) { if (devs[i] && devs[i]->attached) {
usb_attach(&s->ports[i], devs[i]); usb_attach(&s->ports[i]);
} }
} }
ehci_queues_rip_all(s); ehci_queues_rip_all(s);
@ -937,15 +945,15 @@ static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
return; return;
} }
if (dev) { if (dev && dev->attached) {
usb_attach(&s->ports[port], NULL); usb_detach(&s->ports[port]);
} }
*portsc &= ~PORTSC_POWNER; *portsc &= ~PORTSC_POWNER;
*portsc |= owner; *portsc |= owner;
if (dev) { if (dev && dev->attached) {
usb_attach(&s->ports[port], dev); usb_attach(&s->ports[port]);
} }
} }
@ -969,8 +977,8 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) { if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
trace_usb_ehci_port_reset(port, 0); trace_usb_ehci_port_reset(port, 0);
if (dev) { if (dev && dev->attached) {
usb_attach(&s->ports[port], dev); usb_attach(&s->ports[port]);
usb_send_msg(dev, USB_MSG_RESET); usb_send_msg(dev, USB_MSG_RESET);
*portsc &= ~PORTSC_CSC; *portsc &= ~PORTSC_CSC;
} }
@ -979,7 +987,7 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
* 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 * to SW that this port has a high speed device attached
*/ */
if (dev && (dev->speedmask & USB_SPEED_MASK_HIGH)) { if (dev && dev->attached && (dev->speedmask & USB_SPEED_MASK_HIGH)) {
val |= PORTSC_PED; val |= PORTSC_PED;
} }
} }
@ -1584,8 +1592,13 @@ static int ehci_state_fetchentry(EHCIState *ehci, int async)
again = 1; again = 1;
break; break;
case NLPTR_TYPE_STITD:
ehci_set_state(ehci, async, EST_FETCHSITD);
again = 1;
break;
default: default:
// TODO: handle siTD and FSTN types /* TODO: handle FSTN type */
fprintf(stderr, "FETCHENTRY: entry at %X is of type %d " fprintf(stderr, "FETCHENTRY: entry at %X is of type %d "
"which is not supported yet\n", entry, NLPTR_TYPE_GET(entry)); "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry));
return -1; return -1;
@ -1701,6 +1714,30 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async)
return 1; return 1;
} }
static int ehci_state_fetchsitd(EHCIState *ehci, int async)
{
uint32_t entry;
EHCIsitd sitd;
assert(!async);
entry = ehci_get_fetch_addr(ehci, async);
get_dwords(NLPTR_GET(entry), (uint32_t *)&sitd,
sizeof(EHCIsitd) >> 2);
ehci_trace_sitd(ehci, entry, &sitd);
if (!(sitd.results & SITD_RESULTS_ACTIVE)) {
/* siTD is not active, nothing to do */;
} else {
/* TODO: split transfers are not implemented */
fprintf(stderr, "WARNING: Skipping active siTD\n");
}
ehci_set_fetch_addr(ehci, async, sitd.next);
ehci_set_state(ehci, async, EST_FETCHENTRY);
return 1;
}
/* Section 4.10.2 - paragraph 3 */ /* Section 4.10.2 - paragraph 3 */
static int ehci_state_advqueue(EHCIQueue *q, int async) static int ehci_state_advqueue(EHCIQueue *q, int async)
{ {
@ -1976,6 +2013,10 @@ static void ehci_advance_state(EHCIState *ehci,
again = ehci_state_fetchitd(ehci, async); again = ehci_state_fetchitd(ehci, async);
break; break;
case EST_FETCHSITD:
again = ehci_state_fetchsitd(ehci, async);
break;
case EST_ADVANCEQUEUE: case EST_ADVANCEQUEUE:
again = ehci_state_advqueue(q, async); again = ehci_state_advqueue(q, async);
break; break;

View File

@ -213,16 +213,6 @@ static void usb_hub_complete(USBPort *port, USBPacket *packet)
usb_packet_complete(&s->dev, packet); usb_packet_complete(&s->dev, packet);
} }
static void usb_hub_handle_attach(USBDevice *dev)
{
USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
int i;
for (i = 0; i < NUM_PORTS; i++) {
usb_port_location(&s->ports[i].port, dev->port, i+1);
}
}
static void usb_hub_handle_reset(USBDevice *dev) static void usb_hub_handle_reset(USBDevice *dev)
{ {
/* XXX: do it */ /* XXX: do it */
@ -499,6 +489,7 @@ static int usb_hub_initfn(USBDevice *dev)
usb_register_port(usb_bus_from_device(dev), usb_register_port(usb_bus_from_device(dev),
&port->port, s, i, &usb_hub_port_ops, &port->port, s, i, &usb_hub_port_ops,
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
usb_port_location(&port->port, dev->port, i+1);
port->wPortStatus = PORT_STAT_POWER; port->wPortStatus = PORT_STAT_POWER;
port->wPortChange = 0; port->wPortChange = 0;
} }
@ -537,7 +528,6 @@ static struct USBDeviceInfo hub_info = {
.usb_desc = &desc_hub, .usb_desc = &desc_hub,
.init = usb_hub_initfn, .init = usb_hub_initfn,
.handle_packet = usb_hub_handle_packet, .handle_packet = usb_hub_handle_packet,
.handle_attach = usb_hub_handle_attach,
.handle_reset = usb_hub_handle_reset, .handle_reset = usb_hub_handle_reset,
.handle_control = usb_hub_handle_control, .handle_control = usb_hub_handle_control,
.handle_data = usb_hub_handle_data, .handle_data = usb_hub_handle_data,

View File

@ -314,7 +314,7 @@ struct MUSBEndPoint {
}; };
struct MUSBState { struct MUSBState {
qemu_irq *irqs; qemu_irq irqs[musb_irq_max];
USBBus bus; USBBus bus;
USBPort port; USBPort port;
@ -340,14 +340,12 @@ struct MUSBState {
MUSBEndPoint ep[16]; MUSBEndPoint ep[16];
}; };
struct MUSBState *musb_init(qemu_irq *irqs) void musb_reset(MUSBState *s)
{ {
MUSBState *s = g_malloc0(sizeof(*s));
int i; int i;
s->irqs = irqs;
s->faddr = 0x00; s->faddr = 0x00;
s->devctl = 0;
s->power = MGC_M_POWER_HSENAB; s->power = MGC_M_POWER_HSENAB;
s->tx_intr = 0x0000; s->tx_intr = 0x0000;
s->rx_intr = 0x0000; s->rx_intr = 0x0000;
@ -357,6 +355,10 @@ struct MUSBState *musb_init(qemu_irq *irqs)
s->mask = 0x06; s->mask = 0x06;
s->idx = 0; s->idx = 0;
s->setup_len = 0;
s->session = 0;
memset(s->buf, 0, sizeof(s->buf));
/* TODO: _DW */ /* TODO: _DW */
s->ep[0].config = MGC_M_CONFIGDATA_SOFTCONE | MGC_M_CONFIGDATA_DYNFIFO; s->ep[0].config = MGC_M_CONFIGDATA_SOFTCONE | MGC_M_CONFIGDATA_DYNFIFO;
for (i = 0; i < 16; i ++) { for (i = 0; i < 16; i ++) {
@ -368,8 +370,20 @@ struct MUSBState *musb_init(qemu_irq *irqs)
usb_packet_init(&s->ep[i].packey[0].p); usb_packet_init(&s->ep[i].packey[0].p);
usb_packet_init(&s->ep[i].packey[1].p); usb_packet_init(&s->ep[i].packey[1].p);
} }
}
usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */); struct MUSBState *musb_init(DeviceState *parent_device, int gpio_base)
{
MUSBState *s = g_malloc0(sizeof(*s));
int i;
for (i = 0; i < musb_irq_max; i++) {
s->irqs[i] = qdev_get_gpio_in(parent_device, gpio_base + i);
}
musb_reset(s);
usb_bus_new(&s->bus, &musb_bus_ops, parent_device);
usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops, usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops,
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);

View File

@ -448,8 +448,8 @@ static void ohci_reset(void *opaque)
{ {
port = &ohci->rhport[i]; port = &ohci->rhport[i];
port->ctrl = 0; port->ctrl = 0;
if (port->port.dev) { if (port->port.dev && port->port.dev->attached) {
usb_attach(&port->port, port->port.dev); usb_attach(&port->port);
} }
} }
if (ohci->async_td) { if (ohci->async_td) {

View File

@ -340,8 +340,8 @@ static void uhci_reset(void *opaque)
for(i = 0; i < NB_PORTS; i++) { for(i = 0; i < NB_PORTS; i++) {
port = &s->ports[i]; port = &s->ports[i];
port->ctrl = 0x0080; port->ctrl = 0x0080;
if (port->port.dev) { if (port->port.dev && port->port.dev->attached) {
usb_attach(&port->port, port->port.dev); usb_attach(&port->port);
} }
} }
@ -446,7 +446,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
for(i = 0; i < NB_PORTS; i++) { for(i = 0; i < NB_PORTS; i++) {
port = &s->ports[i]; port = &s->ports[i];
dev = port->port.dev; dev = port->port.dev;
if (dev) { if (dev && dev->attached) {
usb_send_msg(dev, USB_MSG_RESET); usb_send_msg(dev, USB_MSG_RESET);
} }
} }
@ -486,7 +486,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
return; return;
port = &s->ports[n]; port = &s->ports[n];
dev = port->port.dev; dev = port->port.dev;
if (dev) { if (dev && dev->attached) {
/* port reset */ /* port reset */
if ( (val & UHCI_PORT_RESET) && if ( (val & UHCI_PORT_RESET) &&
!(port->ctrl & UHCI_PORT_RESET) ) { !(port->ctrl & UHCI_PORT_RESET) ) {
@ -660,8 +660,9 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
UHCIPort *port = &s->ports[i]; UHCIPort *port = &s->ports[i];
USBDevice *dev = port->port.dev; USBDevice *dev = port->port.dev;
if (dev && (port->ctrl & UHCI_PORT_EN)) if (dev && dev->attached && (port->ctrl & UHCI_PORT_EN)) {
ret = usb_handle_packet(dev, p); ret = usb_handle_packet(dev, p);
}
} }
DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size); DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size);

View File

@ -27,26 +27,23 @@
#include "usb.h" #include "usb.h"
#include "iov.h" #include "iov.h"
void usb_attach(USBPort *port, USBDevice *dev) void usb_attach(USBPort *port)
{ {
if (dev != NULL) { USBDevice *dev = port->dev;
/* attach */
if (port->dev) { assert(dev != NULL);
usb_attach(port, NULL); assert(dev->attached);
} port->ops->attach(port);
dev->port = port; usb_send_msg(dev, USB_MSG_ATTACH);
port->dev = dev; }
port->ops->attach(port);
usb_send_msg(dev, USB_MSG_ATTACH); void usb_detach(USBPort *port)
} else { {
/* detach */ USBDevice *dev = port->dev;
dev = port->dev;
assert(dev); assert(dev != NULL);
port->ops->detach(port); port->ops->detach(port);
usb_send_msg(dev, USB_MSG_DETACH); usb_send_msg(dev, USB_MSG_DETACH);
dev->port = NULL;
port->dev = NULL;
}
} }
void usb_wakeup(USBDevice *dev) void usb_wakeup(USBDevice *dev)
@ -338,8 +335,8 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
{ {
/* Note: p->owner != dev is possible in case dev is a hub */ /* Note: p->owner != dev is possible in case dev is a hub */
assert(p->owner != NULL); assert(p->owner != NULL);
dev->port->ops->complete(dev->port, p);
p->owner = NULL; p->owner = NULL;
dev->port->ops->complete(dev->port, p);
} }
/* Cancel an active packet. The packed must have been deferred by /* Cancel an active packet. The packed must have been deferred by

View File

@ -304,7 +304,8 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p);
void usb_packet_complete(USBDevice *dev, USBPacket *p); void usb_packet_complete(USBDevice *dev, USBPacket *p);
void usb_cancel_packet(USBPacket * p); void usb_cancel_packet(USBPacket * p);
void usb_attach(USBPort *port, USBDevice *dev); void usb_attach(USBPort *port);
void usb_detach(USBPort *port);
void usb_wakeup(USBDevice *dev); void usb_wakeup(USBDevice *dev);
int usb_generic_handle_packet(USBDevice *s, USBPacket *p); int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
@ -337,11 +338,13 @@ enum musb_irq_source_e {
musb_irq_tx, musb_irq_tx,
musb_set_vbus, musb_set_vbus,
musb_set_session, musb_set_session,
__musb_irq_max, /* Add new interrupts here */
musb_irq_max, /* total number of interrupts defined */
}; };
typedef struct MUSBState MUSBState; typedef struct MUSBState MUSBState;
MUSBState *musb_init(qemu_irq *irqs); MUSBState *musb_init(DeviceState *parent_device, int gpio_base);
void musb_reset(MUSBState *s);
uint32_t musb_core_intr_get(MUSBState *s); uint32_t musb_core_intr_get(MUSBState *s);
void musb_core_intr_clear(MUSBState *s, uint32_t mask); void musb_core_intr_clear(MUSBState *s, uint32_t mask);
void musb_set_size(MUSBState *s, int epnum, int size, int is_tx); void musb_set_size(MUSBState *s, int epnum, int size, int is_tx);
@ -378,6 +381,8 @@ int usb_register_companion(const char *masterbus, USBPort *ports[],
void *opaque, USBPortOps *ops, int speedmask); void *opaque, USBPortOps *ops, int speedmask);
void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr); void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr);
void usb_unregister_port(USBBus *bus, USBPort *port); void usb_unregister_port(USBBus *bus, USBPort *port);
int usb_claim_port(USBDevice *dev);
void usb_release_port(USBDevice *dev);
int usb_device_attach(USBDevice *dev); int usb_device_attach(USBDevice *dev);
int usb_device_detach(USBDevice *dev); int usb_device_detach(USBDevice *dev);
int usb_device_delete_addr(int busnr, int addr); int usb_device_delete_addr(int busnr, int addr);

View File

@ -209,6 +209,12 @@ sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "get flags
sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x" sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x"
sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64"" sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64""
# hw/usb-bus.c
usb_port_claim(int bus, const char *port) "bus %d, port %s"
usb_port_attach(int bus, const char *port) "bus %d, port %s"
usb_port_detach(int bus, const char *port) "bus %d, port %s"
usb_port_release(int bus, const char *port) "bus %d, port %s"
# hw/usb-ehci.c # hw/usb-ehci.c
usb_ehci_reset(void) "=== RESET ===" usb_ehci_reset(void) "=== RESET ==="
usb_ehci_mmio_readl(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x" usb_ehci_mmio_readl(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x"
@ -223,6 +229,7 @@ usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t altnext) "q %p
usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ %08x - tbytes %d, cpage %d, cerr %d, pid %d" usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ %08x - tbytes %d, cpage %d, cerr %d, pid %d"
usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ %08x - ioc %d, active %d, halt %d, babble %d, xacterr %d" usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ %08x - ioc %d, active %d, halt %d, babble %d, xacterr %d"
usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d" usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d"
usb_ehci_sitd(uint32_t addr, uint32_t nxt, uint32_t active) "ITD @ %08x: next %08x - active %d"
usb_ehci_port_attach(uint32_t port, const char *device) "attach port #%d - %s" usb_ehci_port_attach(uint32_t port, const char *device) "attach port #%d - %s"
usb_ehci_port_detach(uint32_t port) "detach port #%d" usb_ehci_port_detach(uint32_t port) "detach port #%d"
usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d" usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d"
@ -240,6 +247,31 @@ usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d"
usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d" usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d" usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
# usb-linux.c
usb_host_open_started(int bus, int addr) "dev %d:%d"
usb_host_open_success(int bus, int addr) "dev %d:%d"
usb_host_open_failure(int bus, int addr) "dev %d:%d"
usb_host_disconnect(int bus, int addr) "dev %d:%d"
usb_host_close(int bus, int addr) "dev %d:%d"
usb_host_set_address(int bus, int addr, int config) "dev %d:%d, address %d"
usb_host_set_config(int bus, int addr, int config) "dev %d:%d, config %d"
usb_host_set_interface(int bus, int addr, int interface, int alt) "dev %d:%d, interface %d, alt %d"
usb_host_claim_interfaces(int bus, int addr, int config, int nif) "dev %d:%d, config %d, nif %d"
usb_host_release_interfaces(int bus, int addr) "dev %d:%d"
usb_host_req_control(int bus, int addr, int req, int value, int index) "dev %d:%d, req 0x%x, value %d, index %d"
usb_host_req_data(int bus, int addr, int in, int ep, int size) "dev %d:%d, in %d, ep %d, size %d"
usb_host_req_complete(int bus, int addr, int status) "dev %d:%d, status %d"
usb_host_urb_submit(int bus, int addr, void *aurb, int length, int more) "dev %d:%d, aurb %p, length %d, more %d"
usb_host_urb_complete(int bus, int addr, void *aurb, int status, int length, int more) "dev %d:%d, aurb %p, status %d, length %d, more %d"
usb_host_ep_set_halt(int bus, int addr, int ep) "dev %d:%d, ep %d"
usb_host_ep_clear_halt(int bus, int addr, int ep) "dev %d:%d, ep %d"
usb_host_ep_start_iso(int bus, int addr, int ep) "dev %d:%d, ep %d"
usb_host_ep_stop_iso(int bus, int addr, int ep) "dev %d:%d, ep %d"
usb_host_reset(int bus, int addr) "dev %d:%d"
usb_host_auto_scan_enabled(void)
usb_host_auto_scan_disabled(void)
usb_host_claim_port(int bus, int hub, int port) "bus %d, hub addr %d, port %d"
# hw/scsi-bus.c # hw/scsi-bus.c
scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d" scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d"
scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d" scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"

View File

@ -34,6 +34,7 @@
#include "qemu-timer.h" #include "qemu-timer.h"
#include "monitor.h" #include "monitor.h"
#include "sysemu.h" #include "sysemu.h"
#include "trace.h"
#include <dirent.h> #include <dirent.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
@ -53,7 +54,7 @@ struct usb_ctrltransfer {
void *data; void *data;
}; };
typedef int USBScanFunc(void *opaque, int bus_num, int addr, char *port, typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
int class_id, int vendor_id, int product_id, int class_id, int vendor_id, int product_id,
const char *product_name, int speed); const char *product_name, int speed);
@ -114,6 +115,7 @@ struct USBAutoFilter {
typedef struct USBHostDevice { typedef struct USBHostDevice {
USBDevice dev; USBDevice dev;
int fd; int fd;
int hub_fd;
uint8_t descr[8192]; uint8_t descr[8192];
int descr_len; int descr_len;
@ -123,7 +125,8 @@ typedef struct USBHostDevice {
uint32_t iso_urb_count; uint32_t iso_urb_count;
Notifier exit; Notifier exit;
struct endp_data endp_table[MAX_ENDPOINTS]; struct endp_data ep_in[MAX_ENDPOINTS];
struct endp_data ep_out[MAX_ENDPOINTS];
QLIST_HEAD(, AsyncURB) aurbs; QLIST_HEAD(, AsyncURB) aurbs;
/* Host side address */ /* Host side address */
@ -131,6 +134,7 @@ typedef struct USBHostDevice {
int addr; int addr;
char port[MAX_PORTLEN]; char port[MAX_PORTLEN];
struct USBAutoFilter match; struct USBAutoFilter match;
int seen, errcount;
QTAILQ_ENTRY(USBHostDevice) next; QTAILQ_ENTRY(USBHostDevice) next;
} USBHostDevice; } USBHostDevice;
@ -142,95 +146,107 @@ static int parse_filter(const char *spec, struct USBAutoFilter *f);
static void usb_host_auto_check(void *unused); static void usb_host_auto_check(void *unused);
static int usb_host_read_file(char *line, size_t line_size, static int usb_host_read_file(char *line, size_t line_size,
const char *device_file, const char *device_name); const char *device_file, const char *device_name);
static int usb_linux_update_endp_table(USBHostDevice *s);
static struct endp_data *get_endp(USBHostDevice *s, int ep) static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep)
{ {
return s->endp_table + ep - 1; struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out;
assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
assert(ep > 0 && ep <= MAX_ENDPOINTS);
return eps + ep - 1;
} }
static int is_isoc(USBHostDevice *s, int ep) static int is_isoc(USBHostDevice *s, int pid, int ep)
{ {
return get_endp(s, ep)->type == USBDEVFS_URB_TYPE_ISO; return get_endp(s, pid, ep)->type == USBDEVFS_URB_TYPE_ISO;
} }
static int is_valid(USBHostDevice *s, int ep) static int is_valid(USBHostDevice *s, int pid, int ep)
{ {
return get_endp(s, ep)->type != INVALID_EP_TYPE; return get_endp(s, pid, ep)->type != INVALID_EP_TYPE;
} }
static int is_halted(USBHostDevice *s, int ep) static int is_halted(USBHostDevice *s, int pid, int ep)
{ {
return get_endp(s, ep)->halted; return get_endp(s, pid, ep)->halted;
} }
static void clear_halt(USBHostDevice *s, int ep) static void clear_halt(USBHostDevice *s, int pid, int ep)
{ {
get_endp(s, ep)->halted = 0; trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep);
get_endp(s, pid, ep)->halted = 0;
} }
static void set_halt(USBHostDevice *s, int ep) static void set_halt(USBHostDevice *s, int pid, int ep)
{ {
get_endp(s, ep)->halted = 1; if (ep != 0) {
trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep);
get_endp(s, pid, ep)->halted = 1;
}
} }
static int is_iso_started(USBHostDevice *s, int ep) static int is_iso_started(USBHostDevice *s, int pid, int ep)
{ {
return get_endp(s, ep)->iso_started; return get_endp(s, pid, ep)->iso_started;
} }
static void clear_iso_started(USBHostDevice *s, int ep) static void clear_iso_started(USBHostDevice *s, int pid, int ep)
{ {
get_endp(s, ep)->iso_started = 0; trace_usb_host_ep_stop_iso(s->bus_num, s->addr, ep);
get_endp(s, pid, ep)->iso_started = 0;
} }
static void set_iso_started(USBHostDevice *s, int ep) static void set_iso_started(USBHostDevice *s, int pid, int ep)
{ {
struct endp_data *e = get_endp(s, ep); struct endp_data *e = get_endp(s, pid, ep);
trace_usb_host_ep_start_iso(s->bus_num, s->addr, ep);
if (!e->iso_started) { if (!e->iso_started) {
e->iso_started = 1; e->iso_started = 1;
e->inflight = 0; e->inflight = 0;
} }
} }
static int change_iso_inflight(USBHostDevice *s, int ep, int value) static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value)
{ {
struct endp_data *e = get_endp(s, ep); struct endp_data *e = get_endp(s, pid, ep);
e->inflight += value; e->inflight += value;
return e->inflight; return e->inflight;
} }
static void set_iso_urb(USBHostDevice *s, int ep, AsyncURB *iso_urb) static void set_iso_urb(USBHostDevice *s, int pid, int ep, AsyncURB *iso_urb)
{ {
get_endp(s, ep)->iso_urb = iso_urb; get_endp(s, pid, ep)->iso_urb = iso_urb;
} }
static AsyncURB *get_iso_urb(USBHostDevice *s, int ep) static AsyncURB *get_iso_urb(USBHostDevice *s, int pid, int ep)
{ {
return get_endp(s, ep)->iso_urb; return get_endp(s, pid, ep)->iso_urb;
} }
static void set_iso_urb_idx(USBHostDevice *s, int ep, int i) static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i)
{ {
get_endp(s, ep)->iso_urb_idx = i; get_endp(s, pid, ep)->iso_urb_idx = i;
} }
static int get_iso_urb_idx(USBHostDevice *s, int ep) static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep)
{ {
return get_endp(s, ep)->iso_urb_idx; return get_endp(s, pid, ep)->iso_urb_idx;
} }
static void set_iso_buffer_used(USBHostDevice *s, int ep, int i) static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i)
{ {
get_endp(s, ep)->iso_buffer_used = i; get_endp(s, pid, ep)->iso_buffer_used = i;
} }
static int get_iso_buffer_used(USBHostDevice *s, int ep) static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep)
{ {
return get_endp(s, ep)->iso_buffer_used; return get_endp(s, pid, ep)->iso_buffer_used;
} }
static void set_max_packet_size(USBHostDevice *s, int ep, uint8_t *descriptor) static void set_max_packet_size(USBHostDevice *s, int pid, int ep,
uint8_t *descriptor)
{ {
int raw = descriptor[4] + (descriptor[5] << 8); int raw = descriptor[4] + (descriptor[5] << 8);
int size, microframes; int size, microframes;
@ -241,12 +257,12 @@ static void set_max_packet_size(USBHostDevice *s, int ep, uint8_t *descriptor)
case 2: microframes = 3; break; case 2: microframes = 3; break;
default: microframes = 1; break; default: microframes = 1; break;
} }
get_endp(s, ep)->max_packet_size = size * microframes; get_endp(s, pid, ep)->max_packet_size = size * microframes;
} }
static int get_max_packet_size(USBHostDevice *s, int ep) static int get_max_packet_size(USBHostDevice *s, int pid, int ep)
{ {
return get_endp(s, ep)->max_packet_size; return get_endp(s, pid, ep)->max_packet_size;
} }
/* /*
@ -285,8 +301,6 @@ static void async_free(AsyncURB *aurb)
static void do_disconnect(USBHostDevice *s) static void do_disconnect(USBHostDevice *s)
{ {
printf("husb: device %d.%d disconnected\n",
s->bus_num, s->addr);
usb_host_close(s); usb_host_close(s);
usb_host_auto_check(NULL); usb_host_auto_check(NULL);
} }
@ -308,12 +322,15 @@ static void async_complete(void *opaque)
} }
return; return;
} }
if (errno == ENODEV && !s->closing) { if (errno == ENODEV) {
do_disconnect(s); if (!s->closing) {
trace_usb_host_disconnect(s->bus_num, s->addr);
do_disconnect(s);
}
return; return;
} }
DPRINTF("husb: async. reap urb failed errno %d\n", errno); perror("USBDEVFS_REAPURBNDELAY");
return; return;
} }
@ -324,19 +341,24 @@ static void async_complete(void *opaque)
anything else (it is handled further in usb_host_handle_iso_data) */ anything else (it is handled further in usb_host_handle_iso_data) */
if (aurb->iso_frame_idx == -1) { if (aurb->iso_frame_idx == -1) {
int inflight; int inflight;
int pid = (aurb->urb.endpoint & USB_DIR_IN) ?
USB_TOKEN_IN : USB_TOKEN_OUT;
int ep = aurb->urb.endpoint & 0xf;
if (aurb->urb.status == -EPIPE) { if (aurb->urb.status == -EPIPE) {
set_halt(s, aurb->urb.endpoint & 0xf); set_halt(s, pid, ep);
} }
aurb->iso_frame_idx = 0; aurb->iso_frame_idx = 0;
urbs++; urbs++;
inflight = change_iso_inflight(s, aurb->urb.endpoint & 0xf, -1); inflight = change_iso_inflight(s, pid, ep, -1);
if (inflight == 0 && is_iso_started(s, aurb->urb.endpoint & 0xf)) { if (inflight == 0 && is_iso_started(s, pid, ep)) {
fprintf(stderr, "husb: out of buffers for iso stream\n"); fprintf(stderr, "husb: out of buffers for iso stream\n");
} }
continue; continue;
} }
p = aurb->packet; p = aurb->packet;
trace_usb_host_urb_complete(s->bus_num, s->addr, aurb, aurb->urb.status,
aurb->urb.actual_length, aurb->more);
if (p) { if (p) {
switch (aurb->urb.status) { switch (aurb->urb.status) {
@ -345,7 +367,7 @@ static void async_complete(void *opaque)
break; break;
case -EPIPE: case -EPIPE:
set_halt(s, p->devep); set_halt(s, p->pid, p->devep);
p->result = USB_RET_STALL; p->result = USB_RET_STALL;
break; break;
@ -355,8 +377,10 @@ static void async_complete(void *opaque)
} }
if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) { if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
usb_generic_async_ctrl_complete(&s->dev, p); usb_generic_async_ctrl_complete(&s->dev, p);
} else if (!aurb->more) { } else if (!aurb->more) {
trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
usb_packet_complete(&s->dev, p); usb_packet_complete(&s->dev, p);
} }
} }
@ -394,8 +418,11 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
int interface, nb_interfaces; int interface, nb_interfaces;
int ret, i; int ret, i;
if (configuration == 0) /* address state - ignore */ if (configuration == 0) { /* address state - ignore */
dev->ninterfaces = 0;
dev->configuration = 0;
return 1; return 1;
}
DPRINTF("husb: claiming interfaces. config %d\n", configuration); DPRINTF("husb: claiming interfaces. config %d\n", configuration);
@ -418,9 +445,9 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
} }
config_descr_len = dev->descr[i]; config_descr_len = dev->descr[i];
printf("husb: config #%d need %d\n", dev->descr[i + 5], configuration); DPRINTF("husb: config #%d need %d\n", dev->descr[i + 5], configuration);
if (configuration < 0 || configuration == dev->descr[i + 5]) { if (configuration == dev->descr[i + 5]) {
configuration = dev->descr[i + 5]; configuration = dev->descr[i + 5];
break; break;
} }
@ -457,17 +484,12 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
op = "USBDEVFS_CLAIMINTERFACE"; op = "USBDEVFS_CLAIMINTERFACE";
ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface); ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
if (ret < 0) { if (ret < 0) {
if (errno == EBUSY) {
printf("husb: update iface. device already grabbed\n");
} else {
perror("husb: failed to claim interface");
}
goto fail; goto fail;
} }
} }
printf("husb: %d interfaces claimed for configuration %d\n", trace_usb_host_claim_interfaces(dev->bus_num, dev->addr,
nb_interfaces, configuration); nb_interfaces, configuration);
dev->ninterfaces = nb_interfaces; dev->ninterfaces = nb_interfaces;
dev->configuration = configuration; dev->configuration = configuration;
@ -485,16 +507,15 @@ static int usb_host_release_interfaces(USBHostDevice *s)
{ {
int ret, i; int ret, i;
DPRINTF("husb: releasing interfaces\n"); trace_usb_host_release_interfaces(s->bus_num, s->addr);
for (i = 0; i < s->ninterfaces; i++) { for (i = 0; i < s->ninterfaces; i++) {
ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i); ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
if (ret < 0) { if (ret < 0) {
perror("husb: failed to release interface"); perror("USBDEVFS_RELEASEINTERFACE");
return 0; return 0;
} }
} }
return 1; return 1;
} }
@ -502,11 +523,12 @@ static void usb_host_handle_reset(USBDevice *dev)
{ {
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
DPRINTF("husb: reset device %u.%u\n", s->bus_num, s->addr); trace_usb_host_reset(s->bus_num, s->addr);
ioctl(s->fd, USBDEVFS_RESET); ioctl(s->fd, USBDEVFS_RESET);
usb_host_claim_interfaces(s, s->configuration); usb_host_claim_interfaces(s, 0);
usb_linux_update_endp_table(s);
} }
static void usb_host_handle_destroy(USBDevice *dev) static void usb_host_handle_destroy(USBDevice *dev)
@ -514,19 +536,20 @@ static void usb_host_handle_destroy(USBDevice *dev)
USBHostDevice *s = (USBHostDevice *)dev; USBHostDevice *s = (USBHostDevice *)dev;
usb_host_close(s); usb_host_close(s);
if (s->hub_fd != -1) {
close(s->hub_fd);
}
QTAILQ_REMOVE(&hostdevs, s, next); QTAILQ_REMOVE(&hostdevs, s, next);
qemu_remove_exit_notifier(&s->exit); qemu_remove_exit_notifier(&s->exit);
} }
static int usb_linux_update_endp_table(USBHostDevice *s);
/* iso data is special, we need to keep enough urbs in flight to make sure /* iso data is special, we need to keep enough urbs in flight to make sure
that the controller never runs out of them, otherwise the device will that the controller never runs out of them, otherwise the device will
likely suffer a buffer underrun / overrun. */ likely suffer a buffer underrun / overrun. */
static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, uint8_t ep, int in) static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep)
{ {
AsyncURB *aurb; AsyncURB *aurb;
int i, j, len = get_max_packet_size(s, ep); int i, j, len = get_max_packet_size(s, pid, ep);
aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb)); aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb));
for (i = 0; i < s->iso_urb_count; i++) { for (i = 0; i < s->iso_urb_count; i++) {
@ -538,23 +561,23 @@ static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, uint8_t ep, int in)
aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB; aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB;
for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++) for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++)
aurb[i].urb.iso_frame_desc[j].length = len; aurb[i].urb.iso_frame_desc[j].length = len;
if (in) { if (pid == USB_TOKEN_IN) {
aurb[i].urb.endpoint |= 0x80; aurb[i].urb.endpoint |= 0x80;
/* Mark as fully consumed (idle) */ /* Mark as fully consumed (idle) */
aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB; aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB;
} }
} }
set_iso_urb(s, ep, aurb); set_iso_urb(s, pid, ep, aurb);
return aurb; return aurb;
} }
static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep) static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep)
{ {
AsyncURB *aurb; AsyncURB *aurb;
int i, ret, killed = 0, free = 1; int i, ret, killed = 0, free = 1;
aurb = get_iso_urb(s, ep); aurb = get_iso_urb(s, pid, ep);
if (!aurb) { if (!aurb) {
return; return;
} }
@ -564,7 +587,7 @@ static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep)
if (aurb[i].iso_frame_idx == -1) { if (aurb[i].iso_frame_idx == -1) {
ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]); ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]);
if (ret < 0) { if (ret < 0) {
printf("husb: discard isoc in urb failed errno %d\n", errno); perror("USBDEVFS_DISCARDURB");
free = 0; free = 0;
continue; continue;
} }
@ -585,9 +608,9 @@ static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep)
g_free(aurb); g_free(aurb);
else else
printf("husb: leaking iso urbs because of discard failure\n"); printf("husb: leaking iso urbs because of discard failure\n");
set_iso_urb(s, ep, NULL); set_iso_urb(s, pid, ep, NULL);
set_iso_urb_idx(s, ep, 0); set_iso_urb_idx(s, pid, ep, 0);
clear_iso_started(s, ep); clear_iso_started(s, pid, ep);
} }
static int urb_status_to_usb_ret(int status) static int urb_status_to_usb_ret(int status)
@ -606,16 +629,16 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
int i, j, ret, max_packet_size, offset, len = 0; int i, j, ret, max_packet_size, offset, len = 0;
uint8_t *buf; uint8_t *buf;
max_packet_size = get_max_packet_size(s, p->devep); max_packet_size = get_max_packet_size(s, p->pid, p->devep);
if (max_packet_size == 0) if (max_packet_size == 0)
return USB_RET_NAK; return USB_RET_NAK;
aurb = get_iso_urb(s, p->devep); aurb = get_iso_urb(s, p->pid, p->devep);
if (!aurb) { if (!aurb) {
aurb = usb_host_alloc_iso(s, p->devep, in); aurb = usb_host_alloc_iso(s, p->pid, p->devep);
} }
i = get_iso_urb_idx(s, p->devep); i = get_iso_urb_idx(s, p->pid, p->devep);
j = aurb[i].iso_frame_idx; j = aurb[i].iso_frame_idx;
if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) { if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
if (in) { if (in) {
@ -642,7 +665,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
} }
} else { } else {
len = p->iov.size; len = p->iov.size;
offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep); offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->devep);
/* Check the frame fits */ /* Check the frame fits */
if (len > max_packet_size) { if (len > max_packet_size) {
@ -654,33 +677,33 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
usb_packet_copy(p, aurb[i].urb.buffer + offset, len); usb_packet_copy(p, aurb[i].urb.buffer + offset, len);
aurb[i].urb.iso_frame_desc[j].length = len; aurb[i].urb.iso_frame_desc[j].length = len;
offset += len; offset += len;
set_iso_buffer_used(s, p->devep, offset); set_iso_buffer_used(s, p->pid, p->devep, offset);
/* Start the stream once we have buffered enough data */ /* Start the stream once we have buffered enough data */
if (!is_iso_started(s, p->devep) && i == 1 && j == 8) { if (!is_iso_started(s, p->pid, p->devep) && i == 1 && j == 8) {
set_iso_started(s, p->devep); set_iso_started(s, p->pid, p->devep);
} }
} }
aurb[i].iso_frame_idx++; aurb[i].iso_frame_idx++;
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
i = (i + 1) % s->iso_urb_count; i = (i + 1) % s->iso_urb_count;
set_iso_urb_idx(s, p->devep, i); set_iso_urb_idx(s, p->pid, p->devep, i);
} }
} else { } else {
if (in) { if (in) {
set_iso_started(s, p->devep); set_iso_started(s, p->pid, p->devep);
} else { } else {
DPRINTF("hubs: iso out error no free buffer, dropping packet\n"); DPRINTF("hubs: iso out error no free buffer, dropping packet\n");
} }
} }
if (is_iso_started(s, p->devep)) { if (is_iso_started(s, p->pid, p->devep)) {
/* (Re)-submit all fully consumed / filled urbs */ /* (Re)-submit all fully consumed / filled urbs */
for (i = 0; i < s->iso_urb_count; i++) { for (i = 0; i < s->iso_urb_count; i++) {
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]); ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
if (ret < 0) { if (ret < 0) {
printf("husb error submitting iso urb %d: %d\n", i, errno); perror("USBDEVFS_SUBMITURB");
if (!in || len == 0) { if (!in || len == 0) {
switch(errno) { switch(errno) {
case ETIMEDOUT: case ETIMEDOUT:
@ -694,7 +717,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
break; break;
} }
aurb[i].iso_frame_idx = -1; aurb[i].iso_frame_idx = -1;
change_iso_inflight(s, p->devep, +1); change_iso_inflight(s, p->pid, p->devep, 1);
} }
} }
} }
@ -711,7 +734,12 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
uint8_t *pbuf; uint8_t *pbuf;
uint8_t ep; uint8_t ep;
if (!is_valid(s, p->devep)) { trace_usb_host_req_data(s->bus_num, s->addr,
p->pid == USB_TOKEN_IN,
p->devep, p->iov.size);
if (!is_valid(s, p->pid, p->devep)) {
trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
return USB_RET_NAK; return USB_RET_NAK;
} }
@ -721,17 +749,18 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
ep = p->devep; ep = p->devep;
} }
if (is_halted(s, p->devep)) { if (is_halted(s, p->pid, p->devep)) {
ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &ep); unsigned int arg = ep;
ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg);
if (ret < 0) { if (ret < 0) {
DPRINTF("husb: failed to clear halt. ep 0x%x errno %d\n", perror("USBDEVFS_CLEAR_HALT");
ep, errno); trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
return USB_RET_NAK; return USB_RET_NAK;
} }
clear_halt(s, p->devep); clear_halt(s, p->pid, p->devep);
} }
if (is_isoc(s, p->devep)) { if (is_isoc(s, p->pid, p->devep)) {
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
} }
@ -767,20 +796,24 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
aurb->more = 1; aurb->more = 1;
} }
trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
urb->buffer_length, aurb->more);
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n", DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
urb->endpoint, urb->buffer_length, aurb->more, p, aurb); urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
if (ret < 0) { if (ret < 0) {
DPRINTF("husb: submit failed. errno %d\n", errno); perror("USBDEVFS_SUBMITURB");
async_free(aurb); async_free(aurb);
switch(errno) { switch(errno) {
case ETIMEDOUT: case ETIMEDOUT:
trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
return USB_RET_NAK; return USB_RET_NAK;
case EPIPE: case EPIPE:
default: default:
trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_STALL);
return USB_RET_STALL; return USB_RET_STALL;
} }
} }
@ -800,13 +833,15 @@ static int ctrl_error(void)
static int usb_host_set_address(USBHostDevice *s, int addr) static int usb_host_set_address(USBHostDevice *s, int addr)
{ {
DPRINTF("husb: ctrl set addr %u\n", addr); trace_usb_host_set_address(s->bus_num, s->addr, addr);
s->dev.addr = addr; s->dev.addr = addr;
return 0; return 0;
} }
static int usb_host_set_config(USBHostDevice *s, int config) static int usb_host_set_config(USBHostDevice *s, int config)
{ {
trace_usb_host_set_config(s->bus_num, s->addr, config);
usb_host_release_interfaces(s); usb_host_release_interfaces(s);
int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config); int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
@ -817,6 +852,7 @@ static int usb_host_set_config(USBHostDevice *s, int config)
return ctrl_error(); return ctrl_error();
} }
usb_host_claim_interfaces(s, config); usb_host_claim_interfaces(s, config);
usb_linux_update_endp_table(s);
return 0; return 0;
} }
@ -825,9 +861,14 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
struct usbdevfs_setinterface si; struct usbdevfs_setinterface si;
int i, ret; int i, ret;
trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
for (i = 1; i <= MAX_ENDPOINTS; i++) { for (i = 1; i <= MAX_ENDPOINTS; i++) {
if (is_isoc(s, i)) { if (is_isoc(s, USB_TOKEN_IN, i)) {
usb_host_stop_n_free_iso(s, i); usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i);
}
if (is_isoc(s, USB_TOKEN_OUT, i)) {
usb_host_stop_n_free_iso(s, USB_TOKEN_OUT, i);
} }
} }
@ -859,8 +900,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
*/ */
/* Note request is (bRequestType << 8) | bRequest */ /* Note request is (bRequestType << 8) | bRequest */
DPRINTF("husb: ctrl type 0x%x req 0x%x val 0x%x index %u len %u\n", trace_usb_host_req_control(s->bus_num, s->addr, request, value, index);
request >> 8, request & 0xff, value, index, length);
switch (request) { switch (request) {
case DeviceOutRequest | USB_REQ_SET_ADDRESS: case DeviceOutRequest | USB_REQ_SET_ADDRESS:
@ -900,6 +940,8 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
urb->usercontext = s; urb->usercontext = s;
trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
urb->buffer_length, aurb->more);
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb); DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb);
@ -920,51 +962,6 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
return USB_RET_ASYNC; return USB_RET_ASYNC;
} }
static int usb_linux_get_configuration(USBHostDevice *s)
{
uint8_t configuration;
struct usb_ctrltransfer ct;
int ret;
if (usb_fs_type == USB_FS_SYS) {
char device_name[32], line[1024];
int configuration;
sprintf(device_name, "%d-%s", s->bus_num, s->port);
if (!usb_host_read_file(line, sizeof(line), "bConfigurationValue",
device_name)) {
goto usbdevfs;
}
if (sscanf(line, "%d", &configuration) != 1) {
goto usbdevfs;
}
return configuration;
}
usbdevfs:
ct.bRequestType = USB_DIR_IN;
ct.bRequest = USB_REQ_GET_CONFIGURATION;
ct.wValue = 0;
ct.wIndex = 0;
ct.wLength = 1;
ct.data = &configuration;
ct.timeout = 50;
ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
if (ret < 0) {
perror("usb_linux_get_configuration");
return -1;
}
/* in address state */
if (configuration == 0) {
return -1;
}
return configuration;
}
static uint8_t usb_linux_get_alt_setting(USBHostDevice *s, static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
uint8_t configuration, uint8_t interface) uint8_t configuration, uint8_t interface)
{ {
@ -1010,16 +1007,19 @@ usbdevfs:
static int usb_linux_update_endp_table(USBHostDevice *s) static int usb_linux_update_endp_table(USBHostDevice *s)
{ {
uint8_t *descriptors; uint8_t *descriptors;
uint8_t devep, type, configuration, alt_interface; uint8_t devep, type, alt_interface;
int interface, length, i; int interface, length, i, ep, pid;
struct endp_data *epd;
for (i = 0; i < MAX_ENDPOINTS; i++) for (i = 0; i < MAX_ENDPOINTS; i++) {
s->endp_table[i].type = INVALID_EP_TYPE; s->ep_in[i].type = INVALID_EP_TYPE;
s->ep_out[i].type = INVALID_EP_TYPE;
}
i = usb_linux_get_configuration(s); if (s->configuration == 0) {
if (i < 0) /* not configured yet -- leave all endpoints disabled */
return 1; return 0;
configuration = i; }
/* get the desired configuration, interface, and endpoint descriptors /* get the desired configuration, interface, and endpoint descriptors
* from device description */ * from device description */
@ -1028,8 +1028,9 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
i = 0; i = 0;
if (descriptors[i + 1] != USB_DT_CONFIG || if (descriptors[i + 1] != USB_DT_CONFIG ||
descriptors[i + 5] != configuration) { descriptors[i + 5] != s->configuration) {
DPRINTF("invalid descriptor data - configuration\n"); fprintf(stderr, "invalid descriptor data - configuration %d\n",
s->configuration);
return 1; return 1;
} }
i += descriptors[i]; i += descriptors[i];
@ -1043,7 +1044,8 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
} }
interface = descriptors[i + 2]; interface = descriptors[i + 2];
alt_interface = usb_linux_get_alt_setting(s, configuration, interface); alt_interface = usb_linux_get_alt_setting(s, s->configuration,
interface);
/* the current interface descriptor is the active interface /* the current interface descriptor is the active interface
* and has endpoints */ * and has endpoints */
@ -1066,7 +1068,9 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
} }
devep = descriptors[i + 2]; devep = descriptors[i + 2];
if ((devep & 0x0f) == 0) { pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
ep = devep & 0xf;
if (ep == 0) {
fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n"); fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n");
return 1; return 1;
} }
@ -1077,7 +1081,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
break; break;
case 0x01: case 0x01:
type = USBDEVFS_URB_TYPE_ISO; type = USBDEVFS_URB_TYPE_ISO;
set_max_packet_size(s, (devep & 0xf), descriptors + i); set_max_packet_size(s, pid, ep, descriptors + i);
break; break;
case 0x02: case 0x02:
type = USBDEVFS_URB_TYPE_BULK; type = USBDEVFS_URB_TYPE_BULK;
@ -1089,8 +1093,10 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
DPRINTF("usb_host: malformed endpoint type\n"); DPRINTF("usb_host: malformed endpoint type\n");
type = USBDEVFS_URB_TYPE_BULK; type = USBDEVFS_URB_TYPE_BULK;
} }
s->endp_table[(devep & 0xf) - 1].type = type; epd = get_endp(s, pid, ep);
s->endp_table[(devep & 0xf) - 1].halted = 0; assert(epd->type == INVALID_EP_TYPE);
epd->type = type;
epd->halted = 0;
i += descriptors[i]; i += descriptors[i];
} }
@ -1135,15 +1141,17 @@ static int usb_linux_full_speed_compat(USBHostDevice *dev)
} }
static int usb_host_open(USBHostDevice *dev, int bus_num, static int usb_host_open(USBHostDevice *dev, int bus_num,
int addr, char *port, const char *prod_name, int speed) int addr, const char *port,
const char *prod_name, int speed)
{ {
int fd = -1, ret; int fd = -1, ret;
char buf[1024]; char buf[1024];
trace_usb_host_open_started(bus_num, addr);
if (dev->fd != -1) { if (dev->fd != -1) {
goto fail; goto fail;
} }
printf("husb: open device %d.%d\n", bus_num, addr);
if (!usb_host_device_path) { if (!usb_host_device_path) {
perror("husb: USB Host Device Path not set"); perror("husb: USB Host Device Path not set");
@ -1182,13 +1190,8 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
#endif #endif
/* /* start unconfigured -- we'll wait for the guest to set a configuration */
* Initial configuration is -1 which makes us claim first if (!usb_host_claim_interfaces(dev, 0)) {
* available config. We used to start with 1, which does not
* always work. I've seen devices where first config starts
* with 2.
*/
if (!usb_host_claim_interfaces(dev, -1)) {
goto fail; goto fail;
} }
@ -1218,7 +1221,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
dev->dev.speedmask |= USB_SPEED_MASK_FULL; dev->dev.speedmask |= USB_SPEED_MASK_FULL;
} }
printf("husb: grabbed usb device %d.%d\n", bus_num, addr); trace_usb_host_open_success(bus_num, addr);
if (!prod_name || prod_name[0] == '\0') { if (!prod_name || prod_name[0] == '\0') {
snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
@ -1239,6 +1242,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
return 0; return 0;
fail: fail:
trace_usb_host_open_failure(bus_num, addr);
if (dev->fd != -1) { if (dev->fd != -1) {
close(dev->fd); close(dev->fd);
dev->fd = -1; dev->fd = -1;
@ -1254,11 +1258,16 @@ static int usb_host_close(USBHostDevice *dev)
return -1; return -1;
} }
trace_usb_host_close(dev->bus_num, dev->addr);
qemu_set_fd_handler(dev->fd, NULL, NULL, NULL); qemu_set_fd_handler(dev->fd, NULL, NULL, NULL);
dev->closing = 1; dev->closing = 1;
for (i = 1; i <= MAX_ENDPOINTS; i++) { for (i = 1; i <= MAX_ENDPOINTS; i++) {
if (is_isoc(dev, i)) { if (is_isoc(dev, USB_TOKEN_IN, i)) {
usb_host_stop_n_free_iso(dev, i); usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i);
}
if (is_isoc(dev, USB_TOKEN_OUT, i)) {
usb_host_stop_n_free_iso(dev, USB_TOKEN_OUT, i);
} }
} }
async_complete(dev); async_complete(dev);
@ -1285,17 +1294,76 @@ static int usb_host_initfn(USBDevice *dev)
dev->auto_attach = 0; dev->auto_attach = 0;
s->fd = -1; s->fd = -1;
s->hub_fd = -1;
QTAILQ_INSERT_TAIL(&hostdevs, s, next); QTAILQ_INSERT_TAIL(&hostdevs, s, next);
s->exit.notify = usb_host_exit_notifier; s->exit.notify = usb_host_exit_notifier;
qemu_add_exit_notifier(&s->exit); qemu_add_exit_notifier(&s->exit);
usb_host_auto_check(NULL); usb_host_auto_check(NULL);
#ifdef USBDEVFS_CLAIM_PORT
if (s->match.bus_num != 0 && s->match.port != NULL) {
char *h, hub_name[64], line[1024];
int hub_addr, portnr, ret;
snprintf(hub_name, sizeof(hub_name), "%d-%s",
s->match.bus_num, s->match.port);
/* try strip off last ".$portnr" to get hub */
h = strrchr(hub_name, '.');
if (h != NULL) {
portnr = atoi(h+1);
*h = '\0';
} else {
/* no dot in there -> it is the root hub */
snprintf(hub_name, sizeof(hub_name), "usb%d",
s->match.bus_num);
portnr = atoi(s->match.port);
}
if (!usb_host_read_file(line, sizeof(line), "devnum",
hub_name)) {
goto out;
}
if (sscanf(line, "%d", &hub_addr) != 1) {
goto out;
}
if (!usb_host_device_path) {
goto out;
}
snprintf(line, sizeof(line), "%s/%03d/%03d",
usb_host_device_path, s->match.bus_num, hub_addr);
s->hub_fd = open(line, O_RDWR | O_NONBLOCK);
if (s->hub_fd < 0) {
goto out;
}
ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &portnr);
if (ret < 0) {
close(s->hub_fd);
s->hub_fd = -1;
goto out;
}
trace_usb_host_claim_port(s->match.bus_num, hub_addr, portnr);
}
out:
#endif
return 0; return 0;
} }
static const VMStateDescription vmstate_usb_host = {
.name = "usb-host",
.unmigratable = 1,
};
static struct USBDeviceInfo usb_host_dev_info = { static struct USBDeviceInfo usb_host_dev_info = {
.product_desc = "USB Host Device", .product_desc = "USB Host Device",
.qdev.name = "usb-host", .qdev.name = "usb-host",
.qdev.size = sizeof(USBHostDevice), .qdev.size = sizeof(USBHostDevice),
.qdev.vmsd = &vmstate_usb_host,
.init = usb_host_initfn, .init = usb_host_initfn,
.handle_packet = usb_generic_handle_packet, .handle_packet = usb_generic_handle_packet,
.cancel_packet = usb_host_async_cancel, .cancel_packet = usb_host_async_cancel,
@ -1421,7 +1489,8 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func)
FILE *f = NULL; FILE *f = NULL;
char line[1024]; char line[1024];
char buf[1024]; char buf[1024];
int bus_num, addr, speed, device_count, class_id, product_id, vendor_id; int bus_num, addr, speed, device_count;
int class_id, product_id, vendor_id, port;
char product_name[512]; char product_name[512];
int ret = 0; int ret = 0;
@ -1437,7 +1506,7 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func)
} }
device_count = 0; device_count = 0;
bus_num = addr = class_id = product_id = vendor_id = 0; bus_num = addr = class_id = product_id = vendor_id = port = 0;
speed = -1; /* Can't get the speed from /[proc|dev]/bus/usb/devices */ speed = -1; /* Can't get the speed from /[proc|dev]/bus/usb/devices */
for(;;) { for(;;) {
if (fgets(line, sizeof(line), f) == NULL) { if (fgets(line, sizeof(line), f) == NULL) {
@ -1459,6 +1528,10 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func)
goto fail; goto fail;
} }
bus_num = atoi(buf); bus_num = atoi(buf);
if (get_tag_value(buf, sizeof(buf), line, "Port=", " ") < 0) {
goto fail;
}
port = atoi(buf);
if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0) { if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0) {
goto fail; goto fail;
} }
@ -1504,7 +1577,12 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func)
} }
if (device_count && (vendor_id || product_id)) { if (device_count && (vendor_id || product_id)) {
/* Add the last device. */ /* Add the last device. */
ret = func(opaque, bus_num, addr, 0, class_id, vendor_id, if (port > 0) {
snprintf(buf, sizeof(buf), "%d", port);
} else {
snprintf(buf, sizeof(buf), "?");
}
ret = func(opaque, bus_num, addr, buf, class_id, vendor_id,
product_id, product_name, speed); product_id, product_name, speed);
} }
the_end: the_end:
@ -1713,7 +1791,8 @@ static int usb_host_scan(void *opaque, USBScanFunc *func)
static QEMUTimer *usb_auto_timer; static QEMUTimer *usb_auto_timer;
static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port, static int usb_host_auto_scan(void *opaque, int bus_num,
int addr, const char *port,
int class_id, int vendor_id, int product_id, int class_id, int vendor_id, int product_id,
const char *product_name, int speed) const char *product_name, int speed)
{ {
@ -1745,6 +1824,10 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port,
continue; continue;
} }
/* We got a match */ /* We got a match */
s->seen++;
if (s->errcount >= 3) {
return 0;
}
/* Already attached ? */ /* Already attached ? */
if (s->fd != -1) { if (s->fd != -1) {
@ -1752,7 +1835,9 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port,
} }
DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr); DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
usb_host_open(s, bus_num, addr, port, product_name, speed); if (usb_host_open(s, bus_num, addr, port, product_name, speed) < 0) {
s->errcount++;
}
break; break;
} }
@ -1770,12 +1855,17 @@ static void usb_host_auto_check(void *unused)
if (s->fd == -1) { if (s->fd == -1) {
unconnected++; unconnected++;
} }
if (s->seen == 0) {
s->errcount = 0;
}
s->seen = 0;
} }
if (unconnected == 0) { if (unconnected == 0) {
/* nothing to watch */ /* nothing to watch */
if (usb_auto_timer) { if (usb_auto_timer) {
qemu_del_timer(usb_auto_timer); qemu_del_timer(usb_auto_timer);
trace_usb_host_auto_scan_disabled();
} }
return; return;
} }
@ -1785,6 +1875,7 @@ static void usb_host_auto_check(void *unused)
if (!usb_auto_timer) { if (!usb_auto_timer) {
return; return;
} }
trace_usb_host_auto_scan_enabled();
} }
qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000); qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000);
} }
@ -1875,7 +1966,8 @@ static const char *usb_class_str(uint8_t class)
return p->class_name; return p->class_name;
} }
static void usb_info_device(Monitor *mon, int bus_num, int addr, char *port, static void usb_info_device(Monitor *mon, int bus_num,
int addr, const char *port,
int class_id, int vendor_id, int product_id, int class_id, int vendor_id, int product_id,
const char *product_name, const char *product_name,
int speed) int speed)
@ -1916,7 +2008,7 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, char *port,
} }
static int usb_host_info_device(void *opaque, int bus_num, int addr, static int usb_host_info_device(void *opaque, int bus_num, int addr,
char *path, int class_id, const char *path, int class_id,
int vendor_id, int product_id, int vendor_id, int product_id,
const char *product_name, const char *product_name,
int speed) int speed)