diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2011-10-14 10:46:35 -0500 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2011-10-14 10:46:35 -0500 |
commit | bdc792d3875ee0e8c824bcd7820ab08ff2bd5e24 (patch) | |
tree | ce9dae3c409a2614c0d58dbc0894ecef9e8dcc76 | |
parent | 36f490b176afab3096e9c2877b365354f67e269d (diff) | |
parent | 80cf7cf74f29a219e02b50f27c12b1c792ebf99b (diff) |
Merge remote-tracking branch 'kraxel/usb.28' into staging
-rw-r--r-- | hw/usb-ehci.c | 4 | ||||
-rw-r--r-- | hw/usb-hid.c | 11 | ||||
-rw-r--r-- | hw/usb-hub.c | 12 | ||||
-rw-r--r-- | hw/usb-msd.c | 5 | ||||
-rw-r--r-- | hw/usb-ohci.c | 41 | ||||
-rw-r--r-- | hw/usb-uhci.c | 2 | ||||
-rw-r--r-- | hw/usb.c | 12 | ||||
-rw-r--r-- | hw/usb.h | 1 | ||||
-rw-r--r-- | usb-linux.c | 176 |
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); } } @@ -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) { @@ -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; } |