aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/pc_piix.c4
-rw-r--r--hw/usb.h5
-rw-r--r--hw/usb/bus.c17
-rw-r--r--hw/usb/core.c17
-rw-r--r--hw/usb/desc.c126
-rw-r--r--hw/usb/desc.h63
-rw-r--r--hw/usb/dev-hub.c43
-rw-r--r--hw/usb/hcd-ehci.c16
-rw-r--r--hw/usb/hcd-uhci.c16
-rw-r--r--hw/usb/host-linux.c237
-rw-r--r--hw/usb/redirect.c11
11 files changed, 368 insertions, 187 deletions
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 907d723..6a75718 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -377,6 +377,10 @@ static QEMUMachine pc_machine_v1_1 = {
.driver = "apic",\
.property = "vapic",\
.value = "off",\
+ },{\
+ .driver = "USB",\
+ .property = "full-path",\
+ .value = "no",\
}
static QEMUMachine pc_machine_v1_0 = {
diff --git a/hw/usb.h b/hw/usb.h
index e95085f..ae7ccda 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -182,12 +182,17 @@ struct USBEndpoint {
QTAILQ_HEAD(, USBPacket) queue;
};
+enum USBDeviceFlags {
+ USB_DEV_FLAG_FULL_PATH,
+};
+
/* definition of a USB device */
struct USBDevice {
DeviceState qdev;
USBPort *port;
char *port_path;
void *opaque;
+ uint32_t flags;
/* Actual connected speed */
int speed;
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index d3f8358..2068640 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -19,6 +19,8 @@ static struct BusInfo usb_bus_info = {
.get_fw_dev_path = usb_get_fw_dev_path,
.props = (Property[]) {
DEFINE_PROP_STRING("port", USBDevice, port_path),
+ DEFINE_PROP_BIT("full-path", USBDevice, flags,
+ USB_DEV_FLAG_FULL_PATH, true),
DEFINE_PROP_END_OF_LIST()
},
};
@@ -460,7 +462,20 @@ static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
static char *usb_get_dev_path(DeviceState *qdev)
{
USBDevice *dev = USB_DEVICE(qdev);
- return g_strdup(dev->port->path);
+ DeviceState *hcd = qdev->parent_bus->parent;
+ char *id = NULL;
+
+ if ((dev->flags & (1 << USB_DEV_FLAG_FULL_PATH)) &&
+ hcd && hcd->parent_bus && hcd->parent_bus->info->get_dev_path) {
+ id = hcd->parent_bus->info->get_dev_path(hcd);
+ }
+ if (id) {
+ char *ret = g_strdup_printf("%s/%s", id, dev->port->path);
+ g_free(id);
+ return ret;
+ } else {
+ return g_strdup(dev->port->path);
+ }
}
static char *usb_get_fw_dev_path(DeviceState *qdev)
diff --git a/hw/usb/core.c b/hw/usb/core.c
index a4048fe..9a14a53 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -484,12 +484,17 @@ void usb_packet_check_state(USBPacket *p, USBPacketState expected)
void usb_packet_set_state(USBPacket *p, USBPacketState state)
{
- USBDevice *dev = p->ep->dev;
- USBBus *bus = usb_bus_from_device(dev);
-
- trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p,
- usb_packet_state_name(p->state),
- usb_packet_state_name(state));
+ if (p->ep) {
+ USBDevice *dev = p->ep->dev;
+ USBBus *bus = usb_bus_from_device(dev);
+ trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p,
+ usb_packet_state_name(p->state),
+ usb_packet_state_name(state));
+ } else {
+ trace_usb_packet_state_change(-1, "", -1, p,
+ usb_packet_state_name(p->state),
+ usb_packet_state_name(state));
+ }
p->state = state;
}
diff --git a/hw/usb/desc.c b/hw/usb/desc.c
index 9847a75..3c77368 100644
--- a/hw/usb/desc.c
+++ b/hw/usb/desc.c
@@ -18,32 +18,33 @@ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
uint8_t *dest, size_t len)
{
uint8_t bLength = 0x12;
+ USBDescriptor *d = (void *)dest;
if (len < bLength) {
return -1;
}
- dest[0x00] = bLength;
- dest[0x01] = USB_DT_DEVICE;
-
- dest[0x02] = usb_lo(dev->bcdUSB);
- dest[0x03] = usb_hi(dev->bcdUSB);
- dest[0x04] = dev->bDeviceClass;
- dest[0x05] = dev->bDeviceSubClass;
- dest[0x06] = dev->bDeviceProtocol;
- dest[0x07] = dev->bMaxPacketSize0;
-
- dest[0x08] = usb_lo(id->idVendor);
- dest[0x09] = usb_hi(id->idVendor);
- dest[0x0a] = usb_lo(id->idProduct);
- dest[0x0b] = usb_hi(id->idProduct);
- dest[0x0c] = usb_lo(id->bcdDevice);
- dest[0x0d] = usb_hi(id->bcdDevice);
- dest[0x0e] = id->iManufacturer;
- dest[0x0f] = id->iProduct;
- dest[0x10] = id->iSerialNumber;
-
- dest[0x11] = dev->bNumConfigurations;
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_DEVICE;
+
+ d->u.device.bcdUSB_lo = usb_lo(dev->bcdUSB);
+ d->u.device.bcdUSB_hi = usb_hi(dev->bcdUSB);
+ d->u.device.bDeviceClass = dev->bDeviceClass;
+ d->u.device.bDeviceSubClass = dev->bDeviceSubClass;
+ d->u.device.bDeviceProtocol = dev->bDeviceProtocol;
+ d->u.device.bMaxPacketSize0 = dev->bMaxPacketSize0;
+
+ d->u.device.idVendor_lo = usb_lo(id->idVendor);
+ d->u.device.idVendor_hi = usb_hi(id->idVendor);
+ d->u.device.idProduct_lo = usb_lo(id->idProduct);
+ d->u.device.idProduct_hi = usb_hi(id->idProduct);
+ d->u.device.bcdDevice_lo = usb_lo(id->bcdDevice);
+ d->u.device.bcdDevice_hi = usb_hi(id->bcdDevice);
+ d->u.device.iManufacturer = id->iManufacturer;
+ d->u.device.iProduct = id->iProduct;
+ d->u.device.iSerialNumber = id->iSerialNumber;
+
+ d->u.device.bNumConfigurations = dev->bNumConfigurations;
return bLength;
}
@@ -52,22 +53,23 @@ int usb_desc_device_qualifier(const USBDescDevice *dev,
uint8_t *dest, size_t len)
{
uint8_t bLength = 0x0a;
+ USBDescriptor *d = (void *)dest;
if (len < bLength) {
return -1;
}
- dest[0x00] = bLength;
- dest[0x01] = USB_DT_DEVICE_QUALIFIER;
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
- dest[0x02] = usb_lo(dev->bcdUSB);
- dest[0x03] = usb_hi(dev->bcdUSB);
- dest[0x04] = dev->bDeviceClass;
- dest[0x05] = dev->bDeviceSubClass;
- dest[0x06] = dev->bDeviceProtocol;
- dest[0x07] = dev->bMaxPacketSize0;
- dest[0x08] = dev->bNumConfigurations;
- dest[0x09] = 0; /* reserved */
+ d->u.device_qualifier.bcdUSB_lo = usb_lo(dev->bcdUSB);
+ d->u.device_qualifier.bcdUSB_hi = usb_hi(dev->bcdUSB);
+ d->u.device_qualifier.bDeviceClass = dev->bDeviceClass;
+ d->u.device_qualifier.bDeviceSubClass = dev->bDeviceSubClass;
+ d->u.device_qualifier.bDeviceProtocol = dev->bDeviceProtocol;
+ d->u.device_qualifier.bMaxPacketSize0 = dev->bMaxPacketSize0;
+ d->u.device_qualifier.bNumConfigurations = dev->bNumConfigurations;
+ d->u.device_qualifier.bReserved = 0;
return bLength;
}
@@ -76,22 +78,24 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
uint16_t wTotalLength = 0;
+ USBDescriptor *d = (void *)dest;
int i, rc;
if (len < bLength) {
return -1;
}
- dest[0x00] = bLength;
- dest[0x01] = USB_DT_CONFIG;
- dest[0x04] = conf->bNumInterfaces;
- dest[0x05] = conf->bConfigurationValue;
- dest[0x06] = conf->iConfiguration;
- dest[0x07] = conf->bmAttributes;
- dest[0x08] = conf->bMaxPower;
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_CONFIG;
+
+ d->u.config.bNumInterfaces = conf->bNumInterfaces;
+ d->u.config.bConfigurationValue = conf->bConfigurationValue;
+ d->u.config.iConfiguration = conf->iConfiguration;
+ d->u.config.bmAttributes = conf->bmAttributes;
+ d->u.config.bMaxPower = conf->bMaxPower;
wTotalLength += bLength;
- /* handle grouped interfaces if any*/
+ /* handle grouped interfaces if any */
for (i = 0; i < conf->nif_groups; i++) {
rc = usb_desc_iface_group(&(conf->if_groups[i]),
dest + wTotalLength,
@@ -111,8 +115,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
wTotalLength += rc;
}
- dest[0x02] = usb_lo(wTotalLength);
- dest[0x03] = usb_hi(wTotalLength);
+ d->u.config.wTotalLength_lo = usb_lo(wTotalLength);
+ d->u.config.wTotalLength_hi = usb_hi(wTotalLength);
return wTotalLength;
}
@@ -155,20 +159,22 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
int i, rc, pos = 0;
+ USBDescriptor *d = (void *)dest;
if (len < bLength) {
return -1;
}
- dest[0x00] = bLength;
- dest[0x01] = USB_DT_INTERFACE;
- dest[0x02] = iface->bInterfaceNumber;
- dest[0x03] = iface->bAlternateSetting;
- dest[0x04] = iface->bNumEndpoints;
- dest[0x05] = iface->bInterfaceClass;
- dest[0x06] = iface->bInterfaceSubClass;
- dest[0x07] = iface->bInterfaceProtocol;
- dest[0x08] = iface->iInterface;
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_INTERFACE;
+
+ d->u.interface.bInterfaceNumber = iface->bInterfaceNumber;
+ d->u.interface.bAlternateSetting = iface->bAlternateSetting;
+ d->u.interface.bNumEndpoints = iface->bNumEndpoints;
+ d->u.interface.bInterfaceClass = iface->bInterfaceClass;
+ d->u.interface.bInterfaceSubClass = iface->bInterfaceSubClass;
+ d->u.interface.bInterfaceProtocol = iface->bInterfaceProtocol;
+ d->u.interface.iInterface = iface->iInterface;
pos += bLength;
for (i = 0; i < iface->ndesc; i++) {
@@ -194,21 +200,23 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
{
uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
uint8_t extralen = ep->extra ? ep->extra[0] : 0;
+ USBDescriptor *d = (void *)dest;
if (len < bLength + extralen) {
return -1;
}
- dest[0x00] = bLength;
- dest[0x01] = USB_DT_ENDPOINT;
- dest[0x02] = ep->bEndpointAddress;
- dest[0x03] = ep->bmAttributes;
- dest[0x04] = usb_lo(ep->wMaxPacketSize);
- dest[0x05] = usb_hi(ep->wMaxPacketSize);
- dest[0x06] = ep->bInterval;
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_ENDPOINT;
+
+ d->u.endpoint.bEndpointAddress = ep->bEndpointAddress;
+ d->u.endpoint.bmAttributes = ep->bmAttributes;
+ d->u.endpoint.wMaxPacketSize_lo = usb_lo(ep->wMaxPacketSize);
+ d->u.endpoint.wMaxPacketSize_hi = usb_hi(ep->wMaxPacketSize);
+ d->u.endpoint.bInterval = ep->bInterval;
if (ep->is_audio) {
- dest[0x07] = ep->bRefresh;
- dest[0x08] = ep->bSynchAddress;
+ d->u.endpoint.bRefresh = ep->bRefresh;
+ d->u.endpoint.bSynchAddress = ep->bSynchAddress;
}
if (ep->extra) {
memcpy(dest + bLength, ep->extra, extralen);
diff --git a/hw/usb/desc.h b/hw/usb/desc.h
index d6e07ea..d164e8f 100644
--- a/hw/usb/desc.h
+++ b/hw/usb/desc.h
@@ -3,6 +3,69 @@
#include <inttypes.h>
+/* binary representation */
+typedef struct USBDescriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ union {
+ struct {
+ uint8_t bcdUSB_lo;
+ uint8_t bcdUSB_hi;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
+ uint8_t idVendor_lo;
+ uint8_t idVendor_hi;
+ uint8_t idProduct_lo;
+ uint8_t idProduct_hi;
+ uint8_t bcdDevice_lo;
+ uint8_t bcdDevice_hi;
+ uint8_t iManufacturer;
+ uint8_t iProduct;
+ uint8_t iSerialNumber;
+ uint8_t bNumConfigurations;
+ } device;
+ struct {
+ uint8_t bcdUSB_lo;
+ uint8_t bcdUSB_hi;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
+ uint8_t bNumConfigurations;
+ uint8_t bReserved;
+ } device_qualifier;
+ struct {
+ uint8_t wTotalLength_lo;
+ uint8_t wTotalLength_hi;
+ uint8_t bNumInterfaces;
+ uint8_t bConfigurationValue;
+ uint8_t iConfiguration;
+ uint8_t bmAttributes;
+ uint8_t bMaxPower;
+ } config;
+ struct {
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t iInterface;
+ } interface;
+ struct {
+ uint8_t bEndpointAddress;
+ uint8_t bmAttributes;
+ uint8_t wMaxPacketSize_lo;
+ uint8_t wMaxPacketSize_hi;
+ uint8_t bInterval;
+ uint8_t bRefresh; /* only audio ep */
+ uint8_t bSynchAddress; /* only audio ep */
+ } endpoint;
+ } u;
+} QEMU_PACKED USBDescriptor;
+
struct USBDescID {
uint16_t idVendor;
uint16_t idProduct;
diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c
index eb4e711..9c91665 100644
--- a/hw/usb/dev-hub.c
+++ b/hw/usb/dev-hub.c
@@ -22,11 +22,10 @@
* THE SOFTWARE.
*/
#include "qemu-common.h"
+#include "trace.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
-//#define DEBUG
-
#define NUM_PORTS 8
typedef struct USBHubPort {
@@ -157,6 +156,7 @@ static void usb_hub_attach(USBPort *port1)
USBHubState *s = port1->opaque;
USBHubPort *port = &s->ports[port1->index];
+ trace_usb_hub_attach(s->dev.addr, port1->index + 1);
port->wPortStatus |= PORT_STAT_CONNECTION;
port->wPortChange |= PORT_STAT_C_CONNECTION;
if (port->port.dev->speed == USB_SPEED_LOW) {
@@ -172,6 +172,7 @@ static void usb_hub_detach(USBPort *port1)
USBHubState *s = port1->opaque;
USBHubPort *port = &s->ports[port1->index];
+ trace_usb_hub_detach(s->dev.addr, port1->index + 1);
usb_wakeup(s->intr);
/* Let upstream know the device on this port is gone */
@@ -247,6 +248,7 @@ static void usb_hub_handle_reset(USBDevice *dev)
USBHubPort *port;
int i;
+ trace_usb_hub_reset(s->dev.addr);
for (i = 0; i < NUM_PORTS; i++) {
port = s->ports + i;
port->wPortStatus = PORT_STAT_POWER;
@@ -261,12 +263,39 @@ static void usb_hub_handle_reset(USBDevice *dev)
}
}
+static const char *feature_name(int feature)
+{
+ static const char *name[] = {
+ [PORT_CONNECTION] = "connection",
+ [PORT_ENABLE] = "enable",
+ [PORT_SUSPEND] = "suspend",
+ [PORT_OVERCURRENT] = "overcurrent",
+ [PORT_RESET] = "reset",
+ [PORT_POWER] = "power",
+ [PORT_LOWSPEED] = "lowspeed",
+ [PORT_HIGHSPEED] = "highspeed",
+ [PORT_C_CONNECTION] = "change connection",
+ [PORT_C_ENABLE] = "change enable",
+ [PORT_C_SUSPEND] = "change suspend",
+ [PORT_C_OVERCURRENT] = "change overcurrent",
+ [PORT_C_RESET] = "change reset",
+ [PORT_TEST] = "test",
+ [PORT_INDICATOR] = "indicator",
+ };
+ if (feature < 0 || feature >= ARRAY_SIZE(name)) {
+ return "?";
+ }
+ return name[feature] ?: "?";
+}
+
static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
USBHubState *s = (USBHubState *)dev;
int ret;
+ trace_usb_hub_control(s->dev.addr, request, value, index, length);
+
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
@@ -295,6 +324,9 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
goto fail;
}
port = &s->ports[n];
+ trace_usb_hub_get_port_status(s->dev.addr, index,
+ port->wPortStatus,
+ port->wPortChange);
data[0] = port->wPortStatus;
data[1] = port->wPortStatus >> 8;
data[2] = port->wPortChange;
@@ -315,6 +347,10 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
unsigned int n = index - 1;
USBHubPort *port;
USBDevice *dev;
+
+ trace_usb_hub_set_port_feature(s->dev.addr, index,
+ feature_name(value));
+
if (n >= NUM_PORTS) {
goto fail;
}
@@ -345,6 +381,9 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
unsigned int n = index - 1;
USBHubPort *port;
+ trace_usb_hub_clear_port_feature(s->dev.addr, index,
+ feature_name(value));
+
if (n >= NUM_PORTS) {
goto fail;
}
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 60f9f5b..23631a4 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -403,7 +403,6 @@ struct EHCIState {
/*
* Internal states, shadow registers, etc
*/
- uint32_t sofv;
QEMUTimer *frame_timer;
int attach_poll_counter;
int astate; // Current state in asynchronous schedule
@@ -797,7 +796,6 @@ static void ehci_child_detach(USBPort *port, USBDevice *child)
if (portsc & PORTSC_POWNER) {
USBPort *companion = s->companion_ports[port->index];
companion->ops->child_detach(companion, child);
- companion->dev = NULL;
return;
}
@@ -1103,10 +1101,6 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
val &= USBINTR_MASK;
break;
- case FRINDEX:
- s->sofv = val >> 3;
- break;
-
case CONFIGFLAG:
val &= 0x1;
if (val) {
@@ -2015,7 +2009,6 @@ static void ehci_advance_state(EHCIState *ehci,
fprintf(stderr, "processing error - resetting ehci HC\n");
ehci_reset(ehci);
again = 0;
- assert(0);
}
}
while (again);
@@ -2150,13 +2143,14 @@ static void ehci_frame_timer(void *opaque)
if ( !(ehci->usbsts & USBSTS_HALT)) {
ehci->frindex += 8;
- if (ehci->frindex > 0x00001fff) {
- ehci->frindex = 0;
+ if (ehci->frindex == 0x00002000) {
ehci_set_interrupt(ehci, USBSTS_FLR);
}
- ehci->sofv = (ehci->frindex - 1) >> 3;
- ehci->sofv &= 0x000003ff;
+ if (ehci->frindex == 0x00004000) {
+ ehci_set_interrupt(ehci, USBSTS_FLR);
+ ehci->frindex = 0;
+ }
}
if (frames - i > ehci->maxframes) {
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index e55dad9..266d550 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -795,7 +795,8 @@ out:
return TD_RESULT_NEXT_QH;
}
-static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *int_mask)
+static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
+ uint32_t *int_mask, bool queuing)
{
UHCIAsync *async;
int len = 0, max_len;
@@ -814,6 +815,12 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
if (!async->done)
return TD_RESULT_ASYNC_CONT;
+ if (queuing) {
+ /* we are busy filling the queue, we are not prepared
+ to consume completed packages then, just leave them
+ in async state */
+ return TD_RESULT_ASYNC_CONT;
+ }
uhci_async_unlink(async);
goto done;
@@ -964,7 +971,10 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
break;
}
trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token);
- ret = uhci_handle_td(s, plink, &ptd, &int_mask);
+ ret = uhci_handle_td(s, plink, &ptd, &int_mask, true);
+ if (ret == TD_RESULT_ASYNC_CONT) {
+ break;
+ }
assert(ret == TD_RESULT_ASYNC_START);
assert(int_mask == 0);
plink = ptd.link;
@@ -1045,7 +1055,7 @@ static void uhci_process_frame(UHCIState *s)
trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token);
old_td_ctrl = td.ctrl;
- ret = uhci_handle_td(s, link, &td, &int_mask);
+ ret = uhci_handle_td(s, link, &td, &int_mask, false);
if (old_td_ctrl != td.ctrl) {
/* update the status bits of the TD */
val = cpu_to_le32(td.ctrl);
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index 90919c2..061a1b7 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -42,6 +42,7 @@
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
#include "hw/usb.h"
+#include "hw/usb/desc.h"
/* We redefine it to avoid version problems */
struct usb_ctrltransfer {
@@ -94,6 +95,10 @@ struct USBAutoFilter {
uint32_t product_id;
};
+enum USBHostDeviceOptions {
+ USB_HOST_OPT_PIPELINE,
+};
+
typedef struct USBHostDevice {
USBDevice dev;
int fd;
@@ -104,6 +109,7 @@ typedef struct USBHostDevice {
int descr_len;
int closing;
uint32_t iso_urb_count;
+ uint32_t options;
Notifier exit;
struct endp_data ep_in[USB_MAX_ENDPOINTS];
@@ -115,6 +121,7 @@ typedef struct USBHostDevice {
int addr;
char port[MAX_PORTLEN];
struct USBAutoFilter match;
+ int32_t bootindex;
int seen, errcount;
QTAILQ_ENTRY(USBHostDevice) next;
@@ -374,10 +381,10 @@ static void async_complete(void *opaque)
}
if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
- trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p, p->result);
usb_generic_async_ctrl_complete(&s->dev, p);
} else if (!aurb->more) {
- trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p, p->result);
usb_packet_complete(&s->dev, p);
}
}
@@ -391,12 +398,14 @@ static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
AsyncURB *aurb;
+ trace_usb_host_req_canceled(s->bus_num, s->addr, p);
+
QLIST_FOREACH(aurb, &s->aurbs, next) {
if (p != aurb->packet) {
continue;
}
- DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb);
+ trace_usb_host_urb_canceled(s->bus_num, s->addr, aurb);
/* Mark it as dead (see async_complete above) */
aurb->packet = NULL;
@@ -844,12 +853,12 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
uint8_t *pbuf;
uint8_t ep;
- trace_usb_host_req_data(s->bus_num, s->addr,
+ trace_usb_host_req_data(s->bus_num, s->addr, p,
p->pid == USB_TOKEN_IN,
p->ep->nr, p->iov.size);
if (!is_valid(s, p->pid, p->ep->nr)) {
- trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK);
return USB_RET_NAK;
}
@@ -864,7 +873,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg);
if (ret < 0) {
perror("USBDEVFS_CLEAR_HALT");
- trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK);
return USB_RET_NAK;
}
clear_halt(s, p->pid, p->ep->nr);
@@ -919,11 +928,13 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
switch(errno) {
case ETIMEDOUT:
- trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p,
+ USB_RET_NAK);
return USB_RET_NAK;
case EPIPE:
default:
- trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_STALL);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p,
+ USB_RET_STALL);
return USB_RET_STALL;
}
}
@@ -1030,17 +1041,23 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
*/
/* Note request is (bRequestType << 8) | bRequest */
- trace_usb_host_req_control(s->bus_num, s->addr, request, value, index);
+ trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
switch (request) {
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
- return usb_host_set_address(s, value);
+ ret = usb_host_set_address(s, value);
+ trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
+ return ret;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
- return usb_host_set_config(s, value & 0xff);
+ ret = usb_host_set_config(s, value & 0xff);
+ trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
+ return ret;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
- return usb_host_set_interface(s, index, value);
+ ret = usb_host_set_interface(s, index, value);
+ trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
+ return ret;
}
/* The rest are asynchronous */
@@ -1092,120 +1109,128 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
return USB_RET_ASYNC;
}
-static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
- uint8_t configuration, uint8_t interface)
-{
- char device_name[64], line[1024];
- int alt_setting;
-
- sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
- (int)configuration, (int)interface);
-
- if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
- device_name)) {
- /* Assume alt 0 on error */
- return 0;
- }
- if (sscanf(line, "%d", &alt_setting) != 1) {
- /* Assume alt 0 on error */
- return 0;
- }
- return alt_setting;
-}
-
/* returns 1 on problem encountered or 0 for success */
static int usb_linux_update_endp_table(USBHostDevice *s)
{
- uint8_t *descriptors;
- uint8_t devep, type, alt_interface;
- uint16_t raw;
- int interface, length, i, ep, pid;
+ static const char *tname[] = {
+ [USB_ENDPOINT_XFER_CONTROL] = "control",
+ [USB_ENDPOINT_XFER_ISOC] = "isoc",
+ [USB_ENDPOINT_XFER_BULK] = "bulk",
+ [USB_ENDPOINT_XFER_INT] = "int",
+ };
+ uint8_t devep, type;
+ uint16_t mps, v, p;
+ int ep, pid;
+ unsigned int i, configuration = -1, interface = -1, altsetting = -1;
struct endp_data *epd;
+ USBDescriptor *d;
+ bool active = false;
usb_ep_init(&s->dev);
- if (s->dev.configuration == 0) {
- /* not configured yet -- leave all endpoints disabled */
- return 0;
- }
-
- /* get the desired configuration, interface, and endpoint descriptors
- * from device description */
- descriptors = &s->descr[18];
- length = s->descr_len - 18;
- i = 0;
-
- while (i < length) {
- if (descriptors[i + 1] != USB_DT_CONFIG) {
- fprintf(stderr, "invalid descriptor data\n");
- return 1;
- } else if (descriptors[i + 5] != s->dev.configuration) {
- DPRINTF("not requested configuration %d\n", s->dev.configuration);
- i += (descriptors[i + 3] << 8) + descriptors[i + 2];
- continue;
+ for (i = 0;; i += d->bLength) {
+ if (i+2 >= s->descr_len) {
+ break;
}
- i += descriptors[i];
-
- if (descriptors[i + 1] != USB_DT_INTERFACE ||
- (descriptors[i + 1] == USB_DT_INTERFACE &&
- descriptors[i + 4] == 0)) {
- i += descriptors[i];
- continue;
+ d = (void *)(s->descr + i);
+ if (d->bLength < 2) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "descriptor too short");
+ goto error;
}
-
- interface = descriptors[i + 2];
- alt_interface = usb_linux_get_alt_setting(s, s->dev.configuration,
- interface);
-
- /* the current interface descriptor is the active interface
- * and has endpoints */
- if (descriptors[i + 3] != alt_interface) {
- i += descriptors[i];
- continue;
- }
-
- /* advance to the endpoints */
- while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) {
- i += descriptors[i];
+ if (i + d->bLength > s->descr_len) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "descriptor too long");
+ goto error;
}
-
- if (i >= length)
+ switch (d->bDescriptorType) {
+ case 0:
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "invalid descriptor type");
+ goto error;
+ case USB_DT_DEVICE:
+ if (d->bLength < 0x12) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "device descriptor too short");
+ goto error;
+ }
+ v = (d->u.device.idVendor_hi << 8) | d->u.device.idVendor_lo;
+ p = (d->u.device.idProduct_hi << 8) | d->u.device.idProduct_lo;
+ trace_usb_host_parse_device(s->bus_num, s->addr, v, p);
break;
-
- while (i < length) {
- if (descriptors[i + 1] != USB_DT_ENDPOINT) {
- break;
+ case USB_DT_CONFIG:
+ if (d->bLength < 0x09) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "config descriptor too short");
+ goto error;
}
-
- devep = descriptors[i + 2];
+ configuration = d->u.config.bConfigurationValue;
+ active = (configuration == s->dev.configuration);
+ trace_usb_host_parse_config(s->bus_num, s->addr,
+ configuration, active);
+ break;
+ case USB_DT_INTERFACE:
+ if (d->bLength < 0x09) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "interface descriptor too short");
+ goto error;
+ }
+ interface = d->u.interface.bInterfaceNumber;
+ altsetting = d->u.interface.bAlternateSetting;
+ active = (configuration == s->dev.configuration) &&
+ (altsetting == s->dev.altsetting[interface]);
+ trace_usb_host_parse_interface(s->bus_num, s->addr,
+ interface, altsetting, active);
+ break;
+ case USB_DT_ENDPOINT:
+ if (d->bLength < 0x07) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "endpoint descriptor too short");
+ goto error;
+ }
+ devep = d->u.endpoint.bEndpointAddress;
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");
- return 1;
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "invalid endpoint address");
+ goto error;
}
- type = descriptors[i + 3] & 0x3;
- raw = descriptors[i + 4] + (descriptors[i + 5] << 8);
- usb_ep_set_max_packet_size(&s->dev, pid, ep, raw);
- assert(usb_ep_get_type(&s->dev, pid, ep) ==
- USB_ENDPOINT_XFER_INVALID);
- usb_ep_set_type(&s->dev, pid, ep, type);
- usb_ep_set_ifnum(&s->dev, pid, ep, interface);
- if (type == USB_ENDPOINT_XFER_BULK) {
- usb_ep_set_pipeline(&s->dev, pid, ep, true);
- }
+ type = d->u.endpoint.bmAttributes & 0x3;
+ mps = d->u.endpoint.wMaxPacketSize_lo |
+ (d->u.endpoint.wMaxPacketSize_hi << 8);
+ trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep,
+ (devep & USB_DIR_IN) ? "in" : "out",
+ tname[type], active);
+
+ if (active) {
+ usb_ep_set_max_packet_size(&s->dev, pid, ep, mps);
+ assert(usb_ep_get_type(&s->dev, pid, ep) ==
+ USB_ENDPOINT_XFER_INVALID);
+ usb_ep_set_type(&s->dev, pid, ep, type);
+ usb_ep_set_ifnum(&s->dev, pid, ep, interface);
+ if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) &&
+ (type == USB_ENDPOINT_XFER_BULK)) {
+ usb_ep_set_pipeline(&s->dev, pid, ep, true);
+ }
- epd = get_endp(s, pid, ep);
- epd->halted = 0;
+ epd = get_endp(s, pid, ep);
+ epd->halted = 0;
+ }
- i += descriptors[i];
+ break;
+ default:
+ trace_usb_host_parse_unknown(s->bus_num, s->addr,
+ d->bLength, d->bDescriptorType);
+ break;
}
}
-#ifdef DEBUG
- usb_ep_dump(&s->dev);
-#endif
return 0;
+
+error:
+ usb_ep_init(&s->dev);
+ return 1;
}
/*
@@ -1403,6 +1428,7 @@ static int usb_host_initfn(USBDevice *dev)
if (s->match.bus_num != 0 && s->match.port != NULL) {
usb_host_claim_port(s);
}
+ add_boot_device_path(s->bootindex, &dev->qdev, NULL);
return 0;
}
@@ -1418,6 +1444,9 @@ static Property usb_host_dev_properties[] = {
DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0),
DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4),
+ DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex, -1),
+ DEFINE_PROP_BIT("pipeline", USBHostDevice, options,
+ USB_HOST_OPT_PIPELINE, true),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 8e9f175..94ab463 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -74,6 +74,7 @@ struct USBRedirDevice {
CharDriverState *cs;
uint8_t debug;
char *filter_str;
+ int32_t bootindex;
/* Data passed from chardev the fd_read cb to the usbredirparser read cb */
const uint8_t *read_buf;
int read_buf_size;
@@ -835,7 +836,13 @@ static void usbredir_do_attach(void *opaque)
{
USBRedirDevice *dev = opaque;
- usb_device_attach(&dev->dev);
+ if (usb_device_attach(&dev->dev) != 0) {
+ usbredir_device_disconnect(dev);
+ if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
+ usbredirparser_send_filter_reject(dev->parser);
+ usbredirparser_do_write(dev->parser);
+ }
+ }
}
/*
@@ -923,6 +930,7 @@ static int usbredir_initfn(USBDevice *udev)
qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
usbredir_chardev_read, usbredir_chardev_event, dev);
+ add_boot_device_path(dev->bootindex, &udev->qdev, NULL);
return 0;
}
@@ -1452,6 +1460,7 @@ static Property usbredir_properties[] = {
DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str),
+ DEFINE_PROP_INT32("bootindex", USBRedirDevice, bootindex, -1),
DEFINE_PROP_END_OF_LIST(),
};