aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2011-10-14 10:46:35 -0500
committerAnthony Liguori <aliguori@us.ibm.com>2011-10-14 10:46:35 -0500
commitbdc792d3875ee0e8c824bcd7820ab08ff2bd5e24 (patch)
treece9dae3c409a2614c0d58dbc0894ecef9e8dcc76
parent36f490b176afab3096e9c2877b365354f67e269d (diff)
parent80cf7cf74f29a219e02b50f27c12b1c792ebf99b (diff)
Merge remote-tracking branch 'kraxel/usb.28' into staging
-rw-r--r--hw/usb-ehci.c4
-rw-r--r--hw/usb-hid.c11
-rw-r--r--hw/usb-hub.c12
-rw-r--r--hw/usb-msd.c5
-rw-r--r--hw/usb-ohci.c41
-rw-r--r--hw/usb-uhci.c2
-rw-r--r--hw/usb.c12
-rw-r--r--hw/usb.h1
-rw-r--r--usb-linux.c176
9 files changed, 180 insertions, 84 deletions
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index 27376a2351..bd374c1de6 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -880,6 +880,7 @@ static void ehci_reset(void *opaque)
}
if (devs[i] && devs[i]->attached) {
usb_attach(&s->ports[i]);
+ usb_send_msg(devs[i], USB_MSG_RESET);
}
}
ehci_queues_rip_all(s);
@@ -978,8 +979,7 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
trace_usb_ehci_port_reset(port, 0);
if (dev && dev->attached) {
- usb_attach(&s->ports[port]);
- usb_send_msg(dev, USB_MSG_RESET);
+ usb_reset(&s->ports[port]);
*portsc &= ~PORTSC_CSC;
}
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index ba79466401..a110c74dda 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -527,10 +527,21 @@ static int usb_keyboard_initfn(USBDevice *dev)
return usb_hid_initfn(dev, HID_KEYBOARD);
}
+static int usb_ptr_post_load(void *opaque, int version_id)
+{
+ USBHIDState *s = opaque;
+
+ if (s->dev.remote_wakeup) {
+ hid_pointer_activate(&s->hid);
+ }
+ return 0;
+}
+
static const VMStateDescription vmstate_usb_ptr = {
.name = "usb-ptr",
.version_id = 1,
.minimum_version_id = 1,
+ .post_load = usb_ptr_post_load,
.fields = (VMStateField []) {
VMSTATE_USB_DEVICE(dev, USBHIDState),
VMSTATE_HID_POINTER_DEVICE(hid, USBHIDState),
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index 286e3ad85d..09c65160c2 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -207,10 +207,14 @@ static void usb_hub_complete(USBPort *port, USBPacket *packet)
/*
* Just pass it along upstream for now.
*
- * If we ever inplement usb 2.0 split transactions this will
+ * If we ever implement usb 2.0 split transactions this will
* become a little more complicated ...
+ *
+ * Can't use usb_packet_complete() here because packet->owner is
+ * cleared already, go call the ->complete() callback directly
+ * instead.
*/
- usb_packet_complete(&s->dev, packet);
+ s->dev.port->ops->complete(s->dev.port, packet);
}
static void usb_hub_handle_reset(USBDevice *dev)
@@ -289,7 +293,7 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
port->wPortStatus |= PORT_STAT_SUSPEND;
break;
case PORT_RESET:
- if (dev) {
+ if (dev && dev->attached) {
usb_send_msg(dev, USB_MSG_RESET);
port->wPortChange |= PORT_STAT_C_RESET;
/* set enable bit */
@@ -429,7 +433,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
for(i = 0; i < NUM_PORTS; i++) {
port = &s->ports[i];
dev = port->port.dev;
- if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
+ if (dev && dev->attached && (port->wPortStatus & PORT_STAT_ENABLE)) {
ret = usb_handle_packet(dev, p);
if (ret != USB_RET_NODEV) {
return ret;
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index e92434cc94..08d2d2ac77 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -325,7 +325,10 @@ static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
{
MSDState *s = DO_UPCAST(MSDState, dev, dev);
- scsi_req_cancel(s->req);
+
+ if (s->req) {
+ scsi_req_cancel(s->req);
+ }
}
static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
index c3be65a2e9..c2981c59a4 100644
--- a/hw/usb-ohci.c
+++ b/hw/usb-ohci.c
@@ -150,7 +150,7 @@ static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
#define OHCI_TD_DI_SHIFT 21
#define OHCI_TD_DI_MASK (7<<OHCI_TD_DI_SHIFT)
#define OHCI_TD_T0 (1<<24)
-#define OHCI_TD_T1 (1<<24)
+#define OHCI_TD_T1 (1<<25)
#define OHCI_TD_EC_SHIFT 26
#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT)
#define OHCI_TD_CC_SHIFT 28
@@ -449,7 +449,7 @@ static void ohci_reset(void *opaque)
port = &ohci->rhport[i];
port->ctrl = 0;
if (port->port.dev && port->port.dev->attached) {
- usb_attach(&port->port);
+ usb_reset(&port->port);
}
}
if (ohci->async_td) {
@@ -872,7 +872,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
{
int dir;
- size_t len = 0;
+ size_t len = 0, pktlen = 0;
#ifdef DEBUG_PACKET
const char *str = NULL;
#endif
@@ -940,20 +940,30 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
len = (td.be - td.cbp) + 1;
}
- if (len && dir != OHCI_TD_DIR_IN && !completion) {
- ohci_copy_td(ohci, &td, ohci->usb_buf, len, 0);
+ pktlen = len;
+ if (len && dir != OHCI_TD_DIR_IN) {
+ /* The endpoint may not allow us to transfer it all now */
+ pktlen = (ed->flags & OHCI_ED_MPS_MASK) >> OHCI_ED_MPS_SHIFT;
+ if (pktlen > len) {
+ pktlen = len;
+ }
+ if (!completion) {
+ ohci_copy_td(ohci, &td, ohci->usb_buf, pktlen, 0);
+ }
}
}
flag_r = (td.flags & OHCI_TD_R) != 0;
#ifdef DEBUG_PACKET
- DPRINTF(" TD @ 0x%.8x %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
- addr, (int64_t)len, str, flag_r, td.cbp, td.be);
+ DPRINTF(" TD @ 0x%.8x %" PRId64 " of %" PRId64
+ " bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
+ addr, (int64_t)pktlen, (int64_t)len, str, flag_r, td.cbp, td.be);
- if (len > 0 && dir != OHCI_TD_DIR_IN) {
+ if (pktlen > 0 && dir != OHCI_TD_DIR_IN) {
DPRINTF(" data:");
- for (i = 0; i < len; i++)
+ for (i = 0; i < pktlen; i++) {
printf(" %.2x", ohci->usb_buf[i]);
+ }
DPRINTF("\n");
}
#endif
@@ -982,7 +992,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
usb_packet_setup(&ohci->usb_packet, pid,
OHCI_BM(ed->flags, ED_FA),
OHCI_BM(ed->flags, ED_EN));
- usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
+ usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret != USB_RET_NODEV)
break;
@@ -1005,12 +1015,12 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
DPRINTF("\n");
#endif
} else {
- ret = len;
+ ret = pktlen;
}
}
/* Writeback */
- if (ret == len || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) {
+ if (ret == pktlen || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) {
/* Transmission succeeded. */
if (ret == len) {
td.cbp = 0;
@@ -1026,6 +1036,12 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR);
OHCI_SET_BM(td.flags, TD_EC, 0);
+ if ((dir != OHCI_TD_DIR_IN) && (ret != len)) {
+ /* Partial packet transfer: TD not ready to retire yet */
+ goto exit_no_retire;
+ }
+
+ /* Setting ED_C is part of the TD retirement process */
ed->head &= ~OHCI_ED_C;
if (td.flags & OHCI_TD_T0)
ed->head |= OHCI_ED_C;
@@ -1066,6 +1082,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
i = OHCI_BM(td.flags, TD_DI);
if (i < ohci->done_count)
ohci->done_count = i;
+exit_no_retire:
ohci_put_td(ohci, addr, &td);
return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR;
}
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index 17992cf003..171d7870b7 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -341,7 +341,7 @@ static void uhci_reset(void *opaque)
port = &s->ports[i];
port->ctrl = 0x0080;
if (port->port.dev && port->port.dev->attached) {
- usb_attach(&port->port);
+ usb_reset(&port->port);
}
}
diff --git a/hw/usb.c b/hw/usb.c
index fa90204c5e..2216efe077 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -33,6 +33,7 @@ void usb_attach(USBPort *port)
assert(dev != NULL);
assert(dev->attached);
+ assert(dev->state == USB_STATE_NOTATTACHED);
port->ops->attach(port);
usb_send_msg(dev, USB_MSG_ATTACH);
}
@@ -42,10 +43,21 @@ void usb_detach(USBPort *port)
USBDevice *dev = port->dev;
assert(dev != NULL);
+ assert(dev->state != USB_STATE_NOTATTACHED);
port->ops->detach(port);
usb_send_msg(dev, USB_MSG_DETACH);
}
+void usb_reset(USBPort *port)
+{
+ USBDevice *dev = port->dev;
+
+ assert(dev != NULL);
+ usb_detach(port);
+ usb_attach(port);
+ usb_send_msg(dev, USB_MSG_RESET);
+}
+
void usb_wakeup(USBDevice *dev)
{
if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
diff --git a/hw/usb.h b/hw/usb.h
index c08d469496..c6e1870e59 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -306,6 +306,7 @@ void usb_cancel_packet(USBPacket * p);
void usb_attach(USBPort *port);
void usb_detach(USBPort *port);
+void usb_reset(USBPort *port);
void usb_wakeup(USBDevice *dev);
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
diff --git a/usb-linux.c b/usb-linux.c
index 2075c4c67a..7d4d1d7bcf 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -411,6 +411,100 @@ static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
}
}
+static int usb_host_claim_port(USBHostDevice *s)
+{
+#ifdef USBDEVFS_CLAIM_PORT
+ 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)) {
+ return -1;
+ }
+ if (sscanf(line, "%d", &hub_addr) != 1) {
+ return -1;
+ }
+
+ if (!usb_host_device_path) {
+ return -1;
+ }
+ 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) {
+ return -1;
+ }
+
+ ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &portnr);
+ if (ret < 0) {
+ close(s->hub_fd);
+ s->hub_fd = -1;
+ return -1;
+ }
+
+ trace_usb_host_claim_port(s->match.bus_num, hub_addr, portnr);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+static int usb_host_disconnect_ifaces(USBHostDevice *dev, int nb_interfaces)
+{
+ /* earlier Linux 2.4 do not support that */
+#ifdef USBDEVFS_DISCONNECT
+ struct usbdevfs_ioctl ctrl;
+ int ret, interface;
+
+ for (interface = 0; interface < nb_interfaces; interface++) {
+ ctrl.ioctl_code = USBDEVFS_DISCONNECT;
+ ctrl.ifno = interface;
+ ctrl.data = 0;
+ ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
+ if (ret < 0 && errno != ENODATA) {
+ perror("USBDEVFS_DISCONNECT");
+ return -1;
+ }
+ }
+#endif
+ return 0;
+}
+
+static int usb_linux_get_num_interfaces(USBHostDevice *s)
+{
+ char device_name[64], line[1024];
+ int num_interfaces = 0;
+
+ if (usb_fs_type != USB_FS_SYS) {
+ return -1;
+ }
+
+ sprintf(device_name, "%d-%s", s->bus_num, s->port);
+ if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces",
+ device_name)) {
+ return -1;
+ }
+ if (sscanf(line, "%d", &num_interfaces) != 1) {
+ return -1;
+ }
+ return num_interfaces;
+}
+
static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
{
const char *op = NULL;
@@ -462,22 +556,9 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
}
nb_interfaces = dev->descr[i + 4];
-#ifdef USBDEVFS_DISCONNECT
- /* earlier Linux 2.4 do not support that */
- {
- struct usbdevfs_ioctl ctrl;
- for (interface = 0; interface < nb_interfaces; interface++) {
- ctrl.ioctl_code = USBDEVFS_DISCONNECT;
- ctrl.ifno = interface;
- ctrl.data = 0;
- op = "USBDEVFS_DISCONNECT";
- ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
- if (ret < 0 && errno != ENODATA) {
- goto fail;
- }
- }
+ if (usb_host_disconnect_ifaces(dev, nb_interfaces) < 0) {
+ goto fail;
}
-#endif
/* XXX: only grab if all interfaces are free */
for (interface = 0; interface < nb_interfaces; interface++) {
@@ -840,14 +921,28 @@ static int usb_host_set_address(USBHostDevice *s, int addr)
static int usb_host_set_config(USBHostDevice *s, int config)
{
+ int ret, first = 1;
+
trace_usb_host_set_config(s->bus_num, s->addr, config);
usb_host_release_interfaces(s);
- int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
+again:
+ ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
+ if (ret < 0 && errno == EBUSY && first) {
+ /* happens if usb device is in use by host drivers */
+ int count = usb_linux_get_num_interfaces(s);
+ if (count > 0) {
+ DPRINTF("husb: busy -> disconnecting %d interfaces\n", count);
+ usb_host_disconnect_ifaces(s, count);
+ first = 0;
+ goto again;
+ }
+ }
+
if (ret < 0) {
return ctrl_error();
}
@@ -1301,56 +1396,9 @@ static int usb_host_initfn(USBDevice *dev)
qemu_add_exit_notifier(&s->exit);
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);
+ usb_host_claim_port(s);
}
-out:
-#endif
-
return 0;
}