From 96e077ae347912dfce0e93f5958efc3ed6f311f4 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 9 Jun 2010 17:34:05 -0400 Subject: USB: fix failure path in usb_add_hcd() This patch (as1389) fixes some errors in the failure pathway of usb_add_hcd(). The actions it takes ought to be exactly the same as those taken by usb_remove_hcd(), but they aren't. In one case (removal of the usb_bus_attr_group), the two routines are brought into agreement by changing usb_remove_hcd(). All the other discrepancies are fixed by changing usb_add_hcd(). Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 12742f152f4..caae4625a1f 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2229,7 +2229,7 @@ int usb_add_hcd(struct usb_hcd *hcd, rhdev->speed = USB_SPEED_SUPER; break; default: - goto err_allocate_root_hub; + goto err_set_rh_speed; } hcd->self.root_hub = rhdev; @@ -2305,16 +2305,29 @@ int usb_add_hcd(struct usb_hcd *hcd, return retval; error_create_attr_group: + if (HC_IS_RUNNING(hcd->state)) + hcd->state = HC_STATE_QUIESCING; + spin_lock_irq(&hcd_root_hub_lock); + hcd->rh_registered = 0; + spin_unlock_irq(&hcd_root_hub_lock); + +#ifdef CONFIG_USB_SUSPEND + cancel_work_sync(&hcd->wakeup_work); +#endif mutex_lock(&usb_bus_list_lock); usb_disconnect(&hcd->self.root_hub); mutex_unlock(&usb_bus_list_lock); err_register_root_hub: hcd->driver->stop(hcd); + hcd->state = HC_STATE_HALT; + hcd->poll_rh = 0; + del_timer_sync(&hcd->rh_timer); err_hcd_driver_start: if (hcd->irq >= 0) free_irq(irqnum, hcd); err_request_irq: err_hcd_driver_setup: +err_set_rh_speed: hcd->self.root_hub = NULL; usb_put_dev(rhdev); err_allocate_root_hub: @@ -2337,6 +2350,8 @@ void usb_remove_hcd(struct usb_hcd *hcd) { dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); + sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group); + if (HC_IS_RUNNING (hcd->state)) hcd->state = HC_STATE_QUIESCING; @@ -2349,7 +2364,6 @@ void usb_remove_hcd(struct usb_hcd *hcd) cancel_work_sync(&hcd->wakeup_work); #endif - sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group); mutex_lock(&usb_bus_list_lock); usb_disconnect(&hcd->self.root_hub); mutex_unlock(&usb_bus_list_lock); -- cgit v1.2.3-70-g09d2 From 6d88e6792574497bfac9a81403cc47712040636f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 9 Jun 2010 17:34:17 -0400 Subject: USB: don't stop root-hub status polls too soon This patch (as1390) fixes a problem that crops up when a UHCI host controller is unbound from uhci-hcd while there are still some active URBs. The URBs have to be unlinked when the root hub is unregistered, and uhci-hcd relies upon root-hub status polls as part of its unlinking procedure. But usb_hcd_poll_rh_status() won't make those status calls if hcd->rh_registered is clear, and the flag is cleared _before_ the unregistration takes place. Since hcd->rh_registered is used for other things and needs to be cleared early, the solution is to add a new flag (rh_pollable) and use it instead. It gets cleared _after_ the root hub is unregistered. Now that the status polls don't end too soon, we have to make sure they also don't occur too late -- after the root hub's usb_device structure or the HCD's private structures are deallocated. Therefore the patch adds usb_get_device() and usb_put_device() calls to protect the root hub structure, and it adds an extra del_timer_sync() to prevent the root-hub timer from causing an unexpected status poll. This additional complexity would not be needed if the HCD framework had provided separate stop() and release() callbacks instead of just stop(). This lack could be fixed at some future time (although it would require changes to every host controller driver); when that happens this patch won't be needed any more. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 32 +++++++++++++++++++++++++------- include/linux/usb/hcd.h | 1 + 2 files changed, 26 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index caae4625a1f..53f14c82ff2 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -667,7 +667,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) unsigned long flags; char buffer[6]; /* Any root hubs with > 31 ports? */ - if (unlikely(!hcd->rh_registered)) + if (unlikely(!hcd->rh_pollable)) return; if (!hcd->uses_new_polling && !hcd->status_urb) return; @@ -2217,6 +2217,7 @@ int usb_add_hcd(struct usb_hcd *hcd, retval = -ENOMEM; goto err_allocate_root_hub; } + hcd->self.root_hub = rhdev; switch (hcd->driver->flags & HCD_MASK) { case HCD_USB11: @@ -2231,7 +2232,6 @@ int usb_add_hcd(struct usb_hcd *hcd, default: goto err_set_rh_speed; } - hcd->self.root_hub = rhdev; /* wakeup flag init defaults to "everything works" for root hubs, * but drivers can override it in reset() if needed, along with @@ -2246,6 +2246,7 @@ int usb_add_hcd(struct usb_hcd *hcd, dev_err(hcd->self.controller, "can't setup\n"); goto err_hcd_driver_setup; } + hcd->rh_pollable = 1; /* NOTE: root hub and controller capabilities may not be the same */ if (device_can_wakeup(hcd->self.controller) @@ -2315,9 +2316,12 @@ error_create_attr_group: cancel_work_sync(&hcd->wakeup_work); #endif mutex_lock(&usb_bus_list_lock); - usb_disconnect(&hcd->self.root_hub); + usb_disconnect(&rhdev); /* Sets rhdev to NULL */ mutex_unlock(&usb_bus_list_lock); err_register_root_hub: + hcd->rh_pollable = 0; + hcd->poll_rh = 0; + del_timer_sync(&hcd->rh_timer); hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; hcd->poll_rh = 0; @@ -2328,8 +2332,7 @@ err_hcd_driver_start: err_request_irq: err_hcd_driver_setup: err_set_rh_speed: - hcd->self.root_hub = NULL; - usb_put_dev(rhdev); + usb_put_dev(hcd->self.root_hub); err_allocate_root_hub: usb_deregister_bus(&hcd->self); err_register_bus: @@ -2348,9 +2351,12 @@ EXPORT_SYMBOL_GPL(usb_add_hcd); */ void usb_remove_hcd(struct usb_hcd *hcd) { + struct usb_device *rhdev = hcd->self.root_hub; + dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); - sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group); + usb_get_dev(rhdev); + sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group); if (HC_IS_RUNNING (hcd->state)) hcd->state = HC_STATE_QUIESCING; @@ -2365,17 +2371,29 @@ void usb_remove_hcd(struct usb_hcd *hcd) #endif mutex_lock(&usb_bus_list_lock); - usb_disconnect(&hcd->self.root_hub); + usb_disconnect(&rhdev); /* Sets rhdev to NULL */ mutex_unlock(&usb_bus_list_lock); + /* Prevent any more root-hub status calls from the timer. + * The HCD might still restart the timer (if a port status change + * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke + * the hub_status_data() callback. + */ + hcd->rh_pollable = 0; + hcd->poll_rh = 0; + del_timer_sync(&hcd->rh_timer); + hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; + /* In case the HCD restarted the timer, stop it again. */ hcd->poll_rh = 0; del_timer_sync(&hcd->rh_timer); if (hcd->irq >= 0) free_irq(hcd->irq, hcd); + + usb_put_dev(hcd->self.root_hub); usb_deregister_bus(&hcd->self); hcd_buffer_destroy(hcd); } diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 2e3a4ea1a3d..11b63819590 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -95,6 +95,7 @@ struct usb_hcd { #define HCD_FLAG_SAW_IRQ 0x00000002 unsigned rh_registered:1;/* is root hub registered? */ + unsigned rh_pollable:1; /* may we poll the root hub? */ /* The next flag is a stopgap, to be removed when all the HCDs * support the new root-hub polling mechanism. */ -- cgit v1.2.3-70-g09d2 From c548795abe0d3520b74e18f23ca0a0d72deddab9 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 9 Jun 2010 17:34:27 -0400 Subject: USB: add check to detect host controller hardware removal This patch (as1391) fixes a problem that can occur when USB host controller hardware is hot-unplugged. If no interrupts are generated by the unplug then the HCD may not realize that the controller is gone, and the subsequent unbind may hang waiting for interrupts that never arrive. The solution (for PCI-based controllers) is to call the HCD's interrupt handler at the start of usb_hcd_pci_remove(). If the hardware is gone, the handler will realize this when it tries to read the controller's status register. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 1cf2d1e79a5..7e2d5271b0c 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -292,6 +292,14 @@ void usb_hcd_pci_remove(struct pci_dev *dev) if (!hcd) return; + /* Fake an interrupt request in order to give the driver a chance + * to test whether the controller hardware has been removed (e.g., + * cardbus physical eject). + */ + local_irq_disable(); + usb_hcd_irq(0, hcd); + local_irq_enable(); + usb_remove_hcd(hcd); if (hcd->driver->flags & HCD_MEMORY) { iounmap(hcd->regs); -- cgit v1.2.3-70-g09d2 From ba297edde4dd7376832bafb23e032a40d5928b56 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 9 Jun 2010 17:34:39 -0400 Subject: USB: UHCI: acquire spinlock before calling start_rh() This patch (as1392) fixes a bug in uhci-hcd: The start_rh() routine is supposed to be called with the private spinlock held. If an IRQ comes in at just the wrong time, the driver will think the controller has died when in fact it simply hasn't start yet. The patch also addresses some issues that may prevent an URB from being unlinked after the controller has stopped. This is an abnormal occurrence (ordinarily the controller stops only when the entire bus is suspended and hence there are no active URBs), so the pathways haven't gotten much testing. These two changes may be a little more than is strictly necessary, but clearly they won't hurt. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 2 ++ drivers/usb/host/uhci-q.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 6637e52736d..d1dce2166ef 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -691,7 +691,9 @@ static int uhci_start(struct usb_hcd *hcd) configure_hc(uhci); uhci->is_initialized = 1; + spin_lock_irq(&uhci->lock); start_rh(uhci); + spin_unlock_irq(&uhci->lock); return 0; /* diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index acd582c0280..d3ade401848 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -565,7 +565,7 @@ static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) qh->unlink_frame = uhci->frame_number; /* Force an interrupt so we know when the QH is fully unlinked */ - if (list_empty(&uhci->skel_unlink_qh->node)) + if (list_empty(&uhci->skel_unlink_qh->node) || uhci->is_stopped) uhci_set_next_interrupt(uhci); /* Move the QH from its old list to the end of the unlinking list */ @@ -1667,7 +1667,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh) qh->advance_jiffies = jiffies; goto done; } - ret = 0; + ret = uhci->is_stopped; } /* The queue hasn't advanced; check for timeout */ -- cgit v1.2.3-70-g09d2 From 6c4b7f70ba5ffb7fa1d19d2518664ea6ddb3cbf3 Mon Sep 17 00:00:00 2001 From: Nicolas Kaiser Date: Wed, 9 Jun 2010 20:22:03 +0200 Subject: USB: speedtouch: fixed brace and spacing coding style issues Fixed brace coding style issues. Signed-off-by: Nicolas Kaiser Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/speedtch.c | 4 +++- drivers/usb/atm/usbatm.c | 9 +++------ 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 80f9617d3a1..db83468bde5 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -753,7 +753,9 @@ static struct usb_driver speedtch_usb_driver = { .id_table = speedtch_usb_ids }; -static void speedtch_release_interfaces(struct usb_device *usb_dev, int num_interfaces) { +static void speedtch_release_interfaces(struct usb_device *usb_dev, + int num_interfaces) +{ struct usb_interface *cur_intf; int i; diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 9b53e8df464..a67166e5f67 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -273,8 +273,7 @@ static void usbatm_complete(struct urb *urb) if (unlikely(status) && (!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) || - status != -EILSEQ )) - { + status != -EILSEQ )) { if (status == -ESHUTDOWN) return; @@ -516,8 +515,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance, target[3] |= 0x2; /* adjust PTI */ ctrl->len = 0; /* tag this skb finished */ - } - else + } else ctrl->crc = crc32_be(ctrl->crc, ptr, left); } @@ -1390,9 +1388,8 @@ static int usbatm_print_packet(const unsigned char *data, int len) for (i = 0; i < len;) { buffer[0] = '\0'; sprintf(buffer, "%.3d :", i); - for (j = 0; (j < 16) && (i < len); j++, i++) { + for (j = 0; (j < 16) && (i < len); j++, i++) sprintf(buffer, "%s %2.2x", buffer, data[i]); - } dbg("%s", buffer); } return i; -- cgit v1.2.3-70-g09d2 From 9196cc7bb46bece079398c9846050e8c8dc9235c Mon Sep 17 00:00:00 2001 From: Nicolas Kaiser Date: Wed, 9 Jun 2010 20:43:03 +0200 Subject: USB: speedtouch: fixed more brace and spacing coding style issues Fixed spacing coding style issues. Signed-off-by: Nicolas Kaiser Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/speedtch.c | 6 +++--- drivers/usb/atm/usbatm.c | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index db83468bde5..4716e707de5 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -759,7 +759,7 @@ static void speedtch_release_interfaces(struct usb_device *usb_dev, struct usb_interface *cur_intf; int i; - for(i = 0; i < num_interfaces; i++) + for (i = 0; i < num_interfaces; i++) if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) { usb_set_intfdata(cur_intf, NULL); usb_driver_release_interface(&speedtch_usb_driver, cur_intf); @@ -794,7 +794,7 @@ static int speedtch_bind(struct usbatm_data *usbatm, /* claim all interfaces */ - for (i=0; i < num_interfaces; i++) { + for (i = 0; i < num_interfaces; i++) { cur_intf = usb_ifnum_to_if(usb_dev, i); if ((i != ifnum) && cur_intf) { @@ -844,7 +844,7 @@ static int speedtch_bind(struct usbatm_data *usbatm, use_isoc = 0; /* fall back to bulk if endpoint not found */ - for (i=0; idesc.bNumEndpoints; i++) { + for (i = 0; i < desc->desc.bNumEndpoints; i++) { const struct usb_endpoint_descriptor *endpoint_desc = &desc->endpoint[i].desc; if ((endpoint_desc->bEndpointAddress == target_address)) { diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index a67166e5f67..05bf5a27b5b 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -84,8 +84,8 @@ #ifdef VERBOSE_DEBUG static int usbatm_print_packet(const unsigned char *data, int len); -#define PACKETDEBUG(arg...) usbatm_print_packet (arg) -#define vdbg(arg...) dbg (arg) +#define PACKETDEBUG(arg...) usbatm_print_packet(arg) +#define vdbg(arg...) dbg(arg) #else #define PACKETDEBUG(arg...) #define vdbg(arg...) @@ -273,7 +273,7 @@ static void usbatm_complete(struct urb *urb) if (unlikely(status) && (!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) || - status != -EILSEQ )) { + status != -EILSEQ)) { if (status == -ESHUTDOWN) return; @@ -493,7 +493,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance, ptr += data_len; __skb_pull(skb, data_len); - if(!left) + if (!left) continue; memset(ptr, 0, left); @@ -505,7 +505,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance, trailer[2] = ctrl->len >> 8; trailer[3] = ctrl->len; - ctrl->crc = ~ crc32_be(ctrl->crc, ptr, left - 4); + ctrl->crc = ~crc32_be(ctrl->crc, ptr, left - 4); trailer[4] = ctrl->crc >> 24; trailer[5] = ctrl->crc >> 16; @@ -1144,7 +1144,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, instance->tx_channel.endpoint = usb_sndbulkpipe(usb_dev, driver->bulk_out); /* tx buffer size must be a positive multiple of the stride */ - instance->tx_channel.buf_size = max (instance->tx_channel.stride, + instance->tx_channel.buf_size = max(instance->tx_channel.stride, snd_buf_bytes - (snd_buf_bytes % instance->tx_channel.stride)); /* rx buffer size must be a positive multiple of the endpoint maxpacket */ @@ -1157,7 +1157,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, goto fail_unbind; } - num_packets = max (1U, (rcv_buf_bytes + maxpacket / 2) / maxpacket); /* round */ + num_packets = max(1U, (rcv_buf_bytes + maxpacket / 2) / maxpacket); /* round */ if (num_packets * maxpacket > UDSL_MAX_BUF_SIZE) num_packets--; @@ -1260,7 +1260,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, usb_free_urb(instance->urbs[i]); } - kfree (instance); + kfree(instance); return error; } -- cgit v1.2.3-70-g09d2 From 202380d913d4bf219fdb070b90368cf0275e7b06 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 7 Jun 2010 09:11:33 -0700 Subject: USB: xhci: Remove obsolete debugging printk. When code to manipulate the command register was refactored from xhci_run() to xhci_start(), a debugging statement was left behind that no longer applies. Remove that statement. Signed-off-by: Sarah Sharp Reported-by: Sergei Shtylyov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 3998f72cd0c..343f1047f5d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -495,7 +495,6 @@ int xhci_run(struct usb_hcd *hcd) return -ENODEV; } - xhci_dbg(xhci, "// @%p = 0x%x\n", &xhci->op_regs->command, temp); if (doorbell) (*doorbell)(xhci); if (xhci->quirks & XHCI_NEC_HOST) -- cgit v1.2.3-70-g09d2 From 89b54397909dd1d76737b65f682d2aae0781a33c Mon Sep 17 00:00:00 2001 From: James Courtier-Dutton Date: Fri, 21 May 2010 11:53:25 +0100 Subject: USB: iuu_phoenix: Add support for different baud rates. Signed-off-by: James Courtier-Dutton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/iuu_phoenix.c | 54 ++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 74551cb2e8e..efc72113216 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -1,6 +1,8 @@ /* * Infinity Unlimited USB Phoenix driver * + * Copyright (C) 2010 James Courtier-Dutton (James@superbug.co.uk) + * Copyright (C) 2007 Alain Degreffe (eczema@ecze.com) * * Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás) @@ -40,7 +42,7 @@ static int debug; /* * Version Information */ -#define DRIVER_VERSION "v0.11" +#define DRIVER_VERSION "v0.12" #define DRIVER_DESC "Infinity USB Unlimited Phoenix driver" static const struct usb_device_id id_table[] = { @@ -81,6 +83,9 @@ struct iuu_private { u8 *dbgbuf; /* debug buffer */ u8 len; int vcc; /* vcc (either 3 or 5 V) */ + u32 baud; + u32 boost; + u32 clk; }; @@ -157,13 +162,14 @@ static int iuu_tiocmset(struct tty_struct *tty, struct file *file, port->number, set, clear); spin_lock_irqsave(&priv->lock, flags); - if (set & TIOCM_RTS) - priv->tiostatus = TIOCM_RTS; - if (!(set & TIOCM_RTS) && priv->tiostatus == TIOCM_RTS) { + if ((set & TIOCM_RTS) && !(priv->tiostatus == TIOCM_RTS)) { dbg("%s TIOCMSET RESET called !!!", __func__); priv->reset = 1; } + if (set & TIOCM_RTS) + priv->tiostatus = TIOCM_RTS; + spin_unlock_irqrestore(&priv->lock, flags); return 0; } @@ -851,20 +857,24 @@ static int iuu_uart_off(struct usb_serial_port *port) return status; } -static int iuu_uart_baud(struct usb_serial_port *port, u32 baud, +static int iuu_uart_baud(struct usb_serial_port *port, u32 baud_base, u32 *actual, u8 parity) { int status; + u32 baud; u8 *dataout; u8 DataCount = 0; u8 T1Frekvens = 0; u8 T1reload = 0; unsigned int T1FrekvensHZ = 0; + dbg("%s - enter baud_base=%d", __func__, baud_base); dataout = kmalloc(sizeof(u8) * 5, GFP_KERNEL); if (!dataout) return -ENOMEM; + /*baud = (((priv->clk / 35) * baud_base) / 100000); */ + baud = baud_base; if (baud < 1200 || baud > 230400) { kfree(dataout); @@ -948,15 +958,20 @@ static void iuu_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { const u32 supported_mask = CMSPAR|PARENB|PARODD; - + struct iuu_private *priv = usb_get_serial_port_data(port); unsigned int cflag = tty->termios->c_cflag; int status; u32 actual; u32 parity; int csize = CS7; - int baud = 9600; /* Fixed for the moment */ + int baud; u32 newval = cflag & supported_mask; + /* Just use the ospeed. ispeed should be the same. */ + baud = tty->termios->c_ospeed; + + dbg("%s - enter c_ospeed or baud=%d", __func__, baud); + /* compute the parity parameter */ parity = 0; if (cflag & CMSPAR) { /* Using mark space */ @@ -976,15 +991,15 @@ static void iuu_set_termios(struct tty_struct *tty, /* set it */ status = iuu_uart_baud(port, - (clockmode == 2) ? 16457 : 9600 * boost / 100, + baud * priv->boost / 100, &actual, parity); /* set the termios value to the real one, so the user now what has * changed. We support few fields so its easies to copy the old hw * settings back over and then adjust them */ - if (old_termios) - tty_termios_copy_hw(tty->termios, old_termios); + if (old_termios) + tty_termios_copy_hw(tty->termios, old_termios); if (status != 0) /* Set failed - return old bits */ return; /* Re-encode speed, parity and csize */ @@ -1018,6 +1033,7 @@ static void iuu_close(struct usb_serial_port *port) static void iuu_init_termios(struct tty_struct *tty) { + dbg("%s - enter", __func__); *(tty->termios) = tty_std_termios; tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 | TIOCM_CTS | CSTOPB | PARENB; @@ -1033,10 +1049,16 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) struct usb_serial *serial = port->serial; u8 *buf; int result; + int baud; u32 actual; struct iuu_private *priv = usb_get_serial_port_data(port); - dbg("%s - port %d", __func__, port->number); + baud = tty->termios->c_ospeed; + tty->termios->c_ispeed = baud; + /* Re-encode speed */ + tty_encode_baud_rate(tty, baud, baud); + + dbg("%s - port %d, baud %d", __func__, port->number, baud); usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); @@ -1071,23 +1093,29 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) iuu_uart_on(port); if (boost < 100) boost = 100; + priv->boost = boost; + priv->baud = baud; switch (clockmode) { case 2: /* 3.680 Mhz */ + priv->clk = IUU_CLK_3680000; iuu_clk(port, IUU_CLK_3680000 * boost / 100); result = - iuu_uart_baud(port, 9600 * boost / 100, &actual, + iuu_uart_baud(port, baud * boost / 100, &actual, IUU_PARITY_EVEN); break; case 3: /* 6.00 Mhz */ iuu_clk(port, IUU_CLK_6000000 * boost / 100); + priv->clk = IUU_CLK_6000000; + /* Ratio of 6000000 to 3500000 for baud 9600 */ result = iuu_uart_baud(port, 16457 * boost / 100, &actual, IUU_PARITY_EVEN); break; default: /* 3.579 Mhz */ iuu_clk(port, IUU_CLK_3579000 * boost / 100); + priv->clk = IUU_CLK_3579000; result = - iuu_uart_baud(port, 9600 * boost / 100, &actual, + iuu_uart_baud(port, baud * boost / 100, &actual, IUU_PARITY_EVEN); } -- cgit v1.2.3-70-g09d2 From 37cd681c022a0a46ebcff025897a57457d3ca8cd Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 21 May 2010 17:08:21 -0400 Subject: USB: EHCI: make CONFIG_USB_EHCI_TT_NEWSCHED default to Y This patch (as1382) changes the USB_EHCI_TT_NEWSCHED Kconfig option to be non-experimental and to default to Y. This option has existed for a long time, and I have not heard any complaints concerning it. By contrast, several people have reported that their devices could be made to work only by enabling the option. The point of changing the default is to cause the option to be enabled by distros that simply use the default settings for esoteric things like this. This change was motivated by Bugzilla #15649. Signed-off-by: Alan Stern CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index f865be2276d..2d926cec072 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -72,8 +72,9 @@ config USB_EHCI_ROOT_HUB_TT from ARC, and has since changed hands a few times. config USB_EHCI_TT_NEWSCHED - bool "Improved Transaction Translator scheduling (EXPERIMENTAL)" - depends on USB_EHCI_HCD && EXPERIMENTAL + bool "Improved Transaction Translator scheduling" + depends on USB_EHCI_HCD + default y ---help--- This changes the periodic scheduling code to fill more of the low and full speed bandwidth available from the Transaction Translator @@ -84,9 +85,11 @@ config USB_EHCI_TT_NEWSCHED If you have multiple periodic low/fullspeed devices connected to a highspeed USB hub which is connected to a highspeed USB Host Controller, and some of those devices will not work correctly - (possibly due to "ENOSPC" or "-28" errors), say Y. + (possibly due to "ENOSPC" or "-28" errors), say Y. Conversely, if + you have only one such device and it doesn't work, you could try + saying N. - If unsure, say N. + If unsure, say Y. config USB_EHCI_BIG_ENDIAN_MMIO bool -- cgit v1.2.3-70-g09d2 From be8a058b50a90282d32aa638720195e1c327c569 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 22 May 2010 10:26:22 +0200 Subject: USB: gadget: Use memdup_user Use memdup_user when user data is immediately copied into the allocated region. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression from,to,size,flag; position p; identifier l1,l2; @@ - to = \(kmalloc@p\|kzalloc@p\)(size,flag); + to = memdup_user(from,size); if ( - to==NULL + IS_ERR(to) || ...) { <+... when != goto l1; - -ENOMEM + PTR_ERR(to) ...+> } - if (copy_from_user(to, from, size) != 0) { - <+... when != goto l2; - -EFAULT - ...+> - } // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/inode.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index de8a8380350..63fc171c0ed 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -1867,13 +1867,9 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) buf += 4; length -= 4; - kbuf = kmalloc (length, GFP_KERNEL); - if (!kbuf) - return -ENOMEM; - if (copy_from_user (kbuf, buf, length)) { - kfree (kbuf); - return -EFAULT; - } + kbuf = memdup_user(buf, length); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); spin_lock_irq (&dev->lock); value = -EINVAL; -- cgit v1.2.3-70-g09d2 From 16be57259f4e664e4e423caa896963de1b7b8d14 Mon Sep 17 00:00:00 2001 From: "csanchez@neurowork.net" Date: Tue, 25 May 2010 10:38:22 -0500 Subject: USB: core driver: Fix Coding Styles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed coding styles in the core usb driver. Signed-off-by: Carlos Sánchez Acosta Signed-off-by: Alejandro Sánchez Acosta Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index a6bd53ace03..880f65baf58 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1742,9 +1742,8 @@ static int usb_runtime_suspend(struct device *dev) } /* Prevent the parent from suspending immediately after */ - else if (udev->parent) { + else if (udev->parent) udev->parent->last_busy = jiffies; - } } /* Runtime suspend for a USB interface doesn't mean anything. */ @@ -1786,7 +1785,7 @@ static int usb_runtime_idle(struct device *dev) return 0; } -static struct dev_pm_ops usb_bus_pm_ops = { +static const struct dev_pm_ops usb_bus_pm_ops = { .runtime_suspend = usb_runtime_suspend, .runtime_resume = usb_runtime_resume, .runtime_idle = usb_runtime_idle, @@ -1794,7 +1793,7 @@ static struct dev_pm_ops usb_bus_pm_ops = { #else -#define usb_bus_pm_ops (*(struct dev_pm_ops *) NULL) +#define usb_bus_pm_ops (*(const struct dev_pm_ops *) NULL) #endif /* CONFIG_USB_SUSPEND */ -- cgit v1.2.3-70-g09d2 From cd62aced31dee9a9a8e63da7bd564911891b3665 Mon Sep 17 00:00:00 2001 From: "csanchez@neurowork.net" Date: Tue, 25 May 2010 10:53:17 -0500 Subject: USB: core endpoint: Fix Coding Styles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed coding styles in the core usb endpoint. Signed-off-by: Carlos Sánchez Acosta Signed-off-by: Alejandro Sánchez Acosta Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/endpoint.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 4f84a41ee7a..3788e738e26 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -96,16 +96,21 @@ static ssize_t show_ep_interval(struct device *dev, switch (usb_endpoint_type(ep->desc)) { case USB_ENDPOINT_XFER_CONTROL: - if (ep->udev->speed == USB_SPEED_HIGH) /* uframes per NAK */ + if (ep->udev->speed == USB_SPEED_HIGH) + /* uframes per NAK */ interval = ep->desc->bInterval; break; + case USB_ENDPOINT_XFER_ISOC: interval = 1 << (ep->desc->bInterval - 1); break; + case USB_ENDPOINT_XFER_BULK: - if (ep->udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */ + if (ep->udev->speed == USB_SPEED_HIGH && !in) + /* uframes per NAK */ interval = ep->desc->bInterval; break; + case USB_ENDPOINT_XFER_INT: if (ep->udev->speed == USB_SPEED_HIGH) interval = 1 << (ep->desc->bInterval - 1); -- cgit v1.2.3-70-g09d2 From dc6eb27bdd3d214568f7d77a317c202c10222511 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 27 May 2010 18:10:08 +0200 Subject: USB: host: Eliminate NULL dereference The test above allows std to be NULL, so check that std is not NULL before doing the dereference. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r exists@ expression E,E1; identifier f; statement S1,S2,S3; @@ if ((E == NULL && ...) || ...) { ... when != if (...) S1 else S2 when != E = E1 * E->f ... when any } else S3 // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/whci/qset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index ab5a14fbfee..dc0ab8382f5 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c @@ -475,7 +475,7 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u || (prev_end & (WHCI_PAGE_SIZE-1)) || (dma_addr & (WHCI_PAGE_SIZE-1)) || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) { - if (std->len % qset->max_packet != 0) + if (std && std->len % qset->max_packet != 0) return -EINVAL; std = qset_new_std(whc, qset, urb, mem_flags); if (std == NULL) { -- cgit v1.2.3-70-g09d2 From a5cc8049ca8ec8b09b9649f32b6e37f94345ddb8 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 31 May 2010 20:23:19 -0700 Subject: USB: isd200.c: Remove unnecessary kmalloc cast Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/isd200.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index e9cbc1467f7..6b9982cd542 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1456,8 +1456,7 @@ static int isd200_init_info(struct us_data *us) int retStatus = ISD200_GOOD; struct isd200_info *info; - info = (struct isd200_info *) - kzalloc(sizeof(struct isd200_info), GFP_KERNEL); + info = kzalloc(sizeof(struct isd200_info), GFP_KERNEL); if (!info) retStatus = ISD200_ERROR; else { -- cgit v1.2.3-70-g09d2 From e644814a2ccbfe171d2fd2b9bca491ead1ae1a96 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 15 Jun 2010 17:04:44 +0300 Subject: usb: throw away custom hex digit methods Recent kernel has common method to convert hex digit to its value. Signed-off-by: Andy Shevchenko Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/ueagle-atm.c | 5 +++-- drivers/usb/gadget/u_ether.c | 15 ++------------- 2 files changed, 5 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index ebae9448014..5b3f555e01c 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -67,6 +67,7 @@ #include #include #include +#include #include @@ -2436,7 +2437,6 @@ UEA_ATTR(firmid, 0); /* Retrieve the device End System Identifier (MAC) */ -#define htoi(x) (isdigit(x) ? x-'0' : toupper(x)-'A'+10) static int uea_getesi(struct uea_softc *sc, u_char * esi) { unsigned char mac_str[2 * ETH_ALEN + 1]; @@ -2447,7 +2447,8 @@ static int uea_getesi(struct uea_softc *sc, u_char * esi) return 1; for (i = 0; i < ETH_ALEN; i++) - esi[i] = htoi(mac_str[2 * i]) * 16 + htoi(mac_str[2 * i + 1]); + esi[i] = hex_to_bin(mac_str[2 * i]) * 16 + + hex_to_bin(mac_str[2 * i + 1]); return 0; } diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 1da755a1c85..6bb876d6525 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -704,17 +704,6 @@ static char *host_addr; module_param(host_addr, charp, S_IRUGO); MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); - -static u8 __init nibble(unsigned char c) -{ - if (isdigit(c)) - return c - '0'; - c = toupper(c); - if (isxdigit(c)) - return 10 + c - 'A'; - return 0; -} - static int get_ether_addr(const char *str, u8 *dev_addr) { if (str) { @@ -725,8 +714,8 @@ static int get_ether_addr(const char *str, u8 *dev_addr) if ((*str == '.') || (*str == ':')) str++; - num = nibble(*str++) << 4; - num |= (nibble(*str++)); + num = hex_to_bin(*str++) << 4; + num |= hex_to_bin(*str++); dev_addr [i] = num; } if (is_valid_ether_addr(dev_addr)) -- cgit v1.2.3-70-g09d2 From aa4d8342988d0c1a79ff19b2ede1e81dfbb16ea5 Mon Sep 17 00:00:00 2001 From: Alek Du Date: Fri, 4 Jun 2010 15:47:54 +0800 Subject: USB: EHCI: EHCI 1.1 addendum: preparation EHCI 1.1 addendum introduced several energy efficiency extensions for EHCI USB host controllers: 1. LPM (link power management) 2. Per-port change 3. Shorter periodic frame list 4. Hardware prefetching This patch is intended to define the HW bits and debug interface for EHCI 1.1 addendum. The LPM and Per-port change patches will be sent out after this patch. Signed-off-by: Jacob Pan Signed-off-by: Alek Du Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-dbg.c | 144 ++++++++++++++++++++++++++++++++++++++++--- drivers/usb/host/ehci-hcd.c | 1 + drivers/usb/host/ehci.h | 1 + include/linux/usb/ehci_def.h | 23 +++++++ 4 files changed, 162 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 874d2000bf9..df5546bb836 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -98,13 +98,18 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label) HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); } else { ehci_dbg (ehci, - "%s hcc_params %04x thresh %d uframes %s%s%s\n", + "%s hcc_params %04x thresh %d uframes %s%s%s%s%s%s%s\n", label, params, HCC_ISOC_THRES(params), HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", HCC_CANPARK(params) ? " park" : "", - HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); + HCC_64BIT_ADDR(params) ? " 64 bit addr" : "", + HCC_LPM(params) ? " LPM" : "", + HCC_PER_PORT_CHANGE_EVENT(params) ? " ppce" : "", + HCC_HW_PREFETCH(params) ? " hw prefetch" : "", + HCC_32FRAME_PERIODIC_LIST(params) ? + " 32 peridic list" : ""); } } #else @@ -191,8 +196,9 @@ static int __maybe_unused dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) { return scnprintf (buf, len, - "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", + "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s", label, label [0] ? " " : "", status, + (status & STS_PPCE_MASK) ? " PPCE" : "", (status & STS_ASS) ? " Async" : "", (status & STS_PSS) ? " Periodic" : "", (status & STS_RECL) ? " Recl" : "", @@ -210,8 +216,9 @@ static int __maybe_unused dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable) { return scnprintf (buf, len, - "%s%sintrenable %02x%s%s%s%s%s%s", + "%s%sintrenable %02x%s%s%s%s%s%s%s", label, label [0] ? " " : "", enable, + (enable & STS_PPCE_MASK) ? " PPCE" : "", (enable & STS_IAA) ? " IAA" : "", (enable & STS_FATAL) ? " FATAL" : "", (enable & STS_FLR) ? " FLR" : "", @@ -228,9 +235,15 @@ static int dbg_command_buf (char *buf, unsigned len, const char *label, u32 command) { return scnprintf (buf, len, - "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", + "%s%scommand %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s " + "period=%s%s %s", label, label [0] ? " " : "", command, - (command & CMD_PARK) ? "park" : "(park)", + (command & CMD_HIRD) ? " HIRD" : "", + (command & CMD_PPCEE) ? " PPCEE" : "", + (command & CMD_FSP) ? " FSP" : "", + (command & CMD_ASPE) ? " ASPE" : "", + (command & CMD_PSPE) ? " PSPE" : "", + (command & CMD_PARK) ? " park" : "(park)", CMD_PARK_CNT (command), (command >> 16) & 0x3f, (command & CMD_LRESET) ? " LReset" : "", @@ -257,11 +270,22 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status) } return scnprintf (buf, len, - "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s", + "%s%sport:%d status %06x %d %s%s%s%s%s%s " + "sig=%s%s%s%s%s%s%s%s%s%s%s", label, label [0] ? " " : "", port, status, + status>>25,/*device address */ + (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ? + " ACK" : "", + (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ? + " NYET" : "", + (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ? + " STALL" : "", + (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ? + " ERR" : "", (status & PORT_POWER) ? " POWER" : "", (status & PORT_OWNER) ? " OWNER" : "", sig, + (status & PORT_LPM) ? " LPM" : "", (status & PORT_RESET) ? " RESET" : "", (status & PORT_SUSPEND) ? " SUSPEND" : "", (status & PORT_RESUME) ? " RESUME" : "", @@ -330,6 +354,13 @@ static int debug_async_open(struct inode *, struct file *); static int debug_periodic_open(struct inode *, struct file *); static int debug_registers_open(struct inode *, struct file *); static int debug_async_open(struct inode *, struct file *); +static int debug_lpm_open(struct inode *, struct file *); +static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); +static ssize_t debug_lpm_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos); +static int debug_lpm_close(struct inode *inode, struct file *file); + static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); static int debug_close(struct inode *, struct file *); @@ -351,6 +382,13 @@ static const struct file_operations debug_registers_fops = { .read = debug_output, .release = debug_close, }; +static const struct file_operations debug_lpm_fops = { + .owner = THIS_MODULE, + .open = debug_lpm_open, + .read = debug_lpm_read, + .write = debug_lpm_write, + .release = debug_lpm_close, +}; static struct dentry *ehci_debug_root; @@ -917,6 +955,94 @@ static int debug_registers_open(struct inode *inode, struct file *file) return file->private_data ? 0 : -ENOMEM; } +static int debug_lpm_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int debug_lpm_close(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t debug_lpm_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + /* TODO: show lpm stats */ + return 0; +} + +static ssize_t debug_lpm_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + char buf[50]; + size_t len; + u32 temp; + unsigned long port; + u32 __iomem *portsc ; + u32 params; + + hcd = bus_to_hcd(file->private_data); + ehci = hcd_to_ehci(hcd); + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + + if (strncmp(buf, "enable", 5) == 0) { + if (strict_strtoul(buf + 7, 10, &port)) + return -EINVAL; + params = ehci_readl(ehci, &ehci->caps->hcs_params); + if (port > HCS_N_PORTS(params)) { + ehci_dbg(ehci, "ERR: LPM on bad port %lu\n", port); + return -ENODEV; + } + portsc = &ehci->regs->port_status[port-1]; + temp = ehci_readl(ehci, portsc); + if (!(temp & PORT_DEV_ADDR)) { + ehci_dbg(ehci, "LPM: no device attached\n"); + return -ENODEV; + } + temp |= PORT_LPM; + ehci_writel(ehci, temp, portsc); + printk(KERN_INFO "force enable LPM for port %lu\n", port); + } else if (strncmp(buf, "hird=", 5) == 0) { + unsigned long hird; + if (strict_strtoul(buf + 5, 16, &hird)) + return -EINVAL; + printk(KERN_INFO "setting hird %s %lu\n", buf + 6, hird); + temp = ehci_readl(ehci, &ehci->regs->command); + temp &= ~CMD_HIRD; + temp |= hird << 24; + ehci_writel(ehci, temp, &ehci->regs->command); + } else if (strncmp(buf, "disable", 7) == 0) { + if (strict_strtoul(buf + 8, 10, &port)) + return -EINVAL; + params = ehci_readl(ehci, &ehci->caps->hcs_params); + if (port > HCS_N_PORTS(params)) { + ehci_dbg(ehci, "ERR: LPM off bad port %lu\n", port); + return -ENODEV; + } + portsc = &ehci->regs->port_status[port-1]; + temp = ehci_readl(ehci, portsc); + if (!(temp & PORT_DEV_ADDR)) { + ehci_dbg(ehci, "ERR: no device attached\n"); + return -ENODEV; + } + temp &= ~PORT_LPM; + ehci_writel(ehci, temp, portsc); + printk(KERN_INFO "disabled LPM for port %lu\n", port); + } else + return -EOPNOTSUPP; + return count; +} + static inline void create_debug_files (struct ehci_hcd *ehci) { struct usb_bus *bus = &ehci_to_hcd(ehci)->self; @@ -940,6 +1066,10 @@ static inline void create_debug_files (struct ehci_hcd *ehci) ehci->debug_registers = debugfs_create_file("registers", S_IRUGO, ehci->debug_dir, bus, &debug_registers_fops); + + ehci->debug_registers = debugfs_create_file("lpm", S_IRUGO|S_IWUGO, + ehci->debug_dir, bus, + &debug_lpm_fops); if (!ehci->debug_registers) goto registers_error; return; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index a3ef2a9d9dc..20ca6a9ff21 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 650a687f285..bfaac164636 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -157,6 +157,7 @@ struct ehci_hcd { /* one per controller */ struct dentry *debug_async; struct dentry *debug_periodic; struct dentry *debug_registers; + struct dentry *debug_lpm; #endif }; diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 80287af2a73..2e262cb1542 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -39,6 +39,12 @@ struct ehci_caps { #define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +/* EHCI 1.1 addendum */ +#define HCC_32FRAME_PERIODIC_LIST(p) ((p)&(1 << 19)) +#define HCC_PER_PORT_CHANGE_EVENT(p) ((p)&(1 << 18)) +#define HCC_LPM(p) ((p)&(1 << 17)) +#define HCC_HW_PREFETCH(p) ((p)&(1 << 16)) + #define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ #define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ #define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ @@ -54,6 +60,13 @@ struct ehci_regs { /* USBCMD: offset 0x00 */ u32 command; + +/* EHCI 1.1 addendum */ +#define CMD_HIRD (0xf<<24) /* host initiated resume duration */ +#define CMD_PPCEE (1<<15) /* per port change event enable */ +#define CMD_FSP (1<<14) /* fully synchronized prefetch */ +#define CMD_ASPE (1<<13) /* async schedule prefetch enable */ +#define CMD_PSPE (1<<12) /* periodic schedule prefetch enable */ /* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ #define CMD_PARK (1<<11) /* enable "park" on async qh */ #define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ @@ -67,6 +80,7 @@ struct ehci_regs { /* USBSTS: offset 0x04 */ u32 status; +#define STS_PPCE_MASK (0xff<<16) /* Per-Port change event 1-16 */ #define STS_ASS (1<<15) /* Async Schedule Status */ #define STS_PSS (1<<14) /* Periodic Schedule Status */ #define STS_RECL (1<<13) /* Reclamation */ @@ -100,6 +114,14 @@ struct ehci_regs { /* PORTSC: offset 0x44 */ u32 port_status[0]; /* up to N_PORTS */ +/* EHCI 1.1 addendum */ +#define PORTSC_SUSPEND_STS_ACK 0 +#define PORTSC_SUSPEND_STS_NYET 1 +#define PORTSC_SUSPEND_STS_STALL 2 +#define PORTSC_SUSPEND_STS_ERR 3 + +#define PORT_DEV_ADDR (0x7f<<25) /* device address */ +#define PORT_SSTS (0x3<<23) /* suspend status */ /* 31:23 reserved */ #define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ #define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ @@ -115,6 +137,7 @@ struct ehci_regs { #define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ /* 11:10 for detecting lowspeed devices (reset vs release ownership) */ /* 9 reserved */ +#define PORT_LPM (1<<9) /* LPM transaction */ #define PORT_RESET (1<<8) /* reset port */ #define PORT_SUSPEND (1<<7) /* suspend port */ #define PORT_RESUME (1<<6) /* resume it */ -- cgit v1.2.3-70-g09d2 From 48f24970144479c29b8cee6d2e1dbedf6dcf9cfb Mon Sep 17 00:00:00 2001 From: Alek Du Date: Fri, 4 Jun 2010 15:47:55 +0800 Subject: USB: EHCI: EHCI 1.1 addendum: Basic LPM feature support With this patch, the LPM capable EHCI host controller can put device into L1 sleep state which is a mode that can enter/exit quickly, and reduce power consumption. Signed-off-by: Jacob Pan Signed-off-by: Alek Du Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 4 ++- drivers/usb/host/ehci-hcd.c | 17 ++++++++++ drivers/usb/host/ehci-hub.c | 5 +++ drivers/usb/host/ehci-lpm.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-pci.c | 21 ++++++++++++ drivers/usb/host/ehci.h | 2 +- include/linux/usb/hcd.h | 4 +++ 7 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/host/ehci-lpm.c (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 70cccc75a36..9cd77a2af82 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2880,7 +2880,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, } retval = 0; - + /* notify HCD that we have a device connected and addressed */ + if (hcd->driver->update_device) + hcd->driver->update_device(hcd, udev); fail: if (retval) { hub_port_disable(hub, port1, 0); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 20ca6a9ff21..baf9b648bb1 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -101,6 +101,11 @@ static int ignore_oc = 0; module_param (ignore_oc, bool, S_IRUGO); MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications"); +/* for link power management(LPM) feature */ +static unsigned int hird; +module_param(hird, int, S_IRUGO); +MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n"); + #define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) /*-------------------------------------------------------------------------*/ @@ -305,6 +310,7 @@ static void end_unlink_async(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); #include "ehci-hub.c" +#include "ehci-lpm.c" #include "ehci-mem.c" #include "ehci-q.c" #include "ehci-sched.c" @@ -604,6 +610,17 @@ static int ehci_init(struct usb_hcd *hcd) default: BUG(); } } + if (HCC_LPM(hcc_params)) { + /* support link power management EHCI 1.1 addendum */ + ehci_dbg(ehci, "support lpm\n"); + ehci->has_lpm = 1; + if (hird > 0xf) { + ehci_dbg(ehci, "hird %d invalid, use default 0", + hird); + hird = 0; + } + temp |= hird << 24; + } ehci->command = temp; /* Accept arbitrarily long scatter-gather lists */ diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index e7d3d8def28..8a28dae8a37 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -790,6 +790,11 @@ static int ehci_hub_control ( status_reg); break; case USB_PORT_FEAT_C_CONNECTION: + if (ehci->has_lpm) { + /* clear PORTSC bits on disconnect */ + temp &= ~PORT_LPM; + temp &= ~PORT_DEV_ADDR; + } ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC, status_reg); break; diff --git a/drivers/usb/host/ehci-lpm.c b/drivers/usb/host/ehci-lpm.c new file mode 100644 index 00000000000..b4d4d63c13e --- /dev/null +++ b/drivers/usb/host/ehci-lpm.c @@ -0,0 +1,83 @@ +/* ehci-lpm.c EHCI HCD LPM support code + * Copyright (c) 2008 - 2010, Intel Corporation. + * Author: Jacob Pan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* this file is part of ehci-hcd.c */ +static int ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num) +{ + u32 __iomem portsc; + + ehci_dbg(ehci, "set dev address %d for port %d\n", dev_addr, port_num); + if (port_num > HCS_N_PORTS(ehci->hcs_params)) { + ehci_dbg(ehci, "invalid port number %d\n", port_num); + return -ENODEV; + } + portsc = ehci_readl(ehci, &ehci->regs->port_status[port_num-1]); + portsc &= ~PORT_DEV_ADDR; + portsc |= dev_addr<<25; + ehci_writel(ehci, portsc, &ehci->regs->port_status[port_num-1]); + return 0; +} + +/* + * this function is used to check if the device support LPM + * if yes, mark the PORTSC register with PORT_LPM bit + */ +static int ehci_lpm_check(struct ehci_hcd *ehci, int port) +{ + u32 __iomem *portsc ; + u32 val32; + int retval; + + portsc = &ehci->regs->port_status[port-1]; + val32 = ehci_readl(ehci, portsc); + if (!(val32 & PORT_DEV_ADDR)) { + ehci_dbg(ehci, "LPM: no device attached\n"); + return -ENODEV; + } + val32 |= PORT_LPM; + ehci_writel(ehci, val32, portsc); + msleep(5); + val32 |= PORT_SUSPEND; + ehci_dbg(ehci, "Sending LPM 0x%08x to port %d\n", val32, port); + ehci_writel(ehci, val32, portsc); + /* wait for ACK */ + msleep(10); + retval = handshake(ehci, &ehci->regs->port_status[port-1], PORT_SSTS, + PORTSC_SUSPEND_STS_ACK, 125); + dbg_port(ehci, "LPM", port, val32); + if (retval != -ETIMEDOUT) { + ehci_dbg(ehci, "LPM: device ACK for LPM\n"); + val32 |= PORT_LPM; + /* + * now device should be in L1 sleep, let's wake up the device + * so that we can complete enumeration. + */ + ehci_writel(ehci, val32, portsc); + msleep(10); + val32 |= PORT_RESUME; + ehci_writel(ehci, val32, portsc); + } else { + ehci_dbg(ehci, "LPM: device does not ACK, disable LPM %d\n", + retval); + val32 &= ~PORT_LPM; + retval = -ETIMEDOUT; + ehci_writel(ehci, val32, portsc); + } + + return retval; +} diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index d43d176161a..a307d550bda 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -361,6 +361,22 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) } #endif +static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int rc = 0; + + if (!udev->parent) /* udev is root hub itself, impossible */ + rc = -1; + /* we only support lpm device connected to root hub yet */ + if (ehci->has_lpm && !udev->parent->parent) { + rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum); + if (!rc) + rc = ehci_lpm_check(ehci, udev->portnum); + } + return rc; +} + static const struct hc_driver ehci_pci_hc_driver = { .description = hcd_name, .product_desc = "EHCI Host Controller", @@ -407,6 +423,11 @@ static const struct hc_driver ehci_pci_hc_driver = { .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + /* + * call back when device connected and addressed + */ + .update_device = ehci_update_device, + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bfaac164636..21f30a0c3d2 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -140,7 +140,7 @@ struct ehci_hcd { /* one per controller */ #define OHCI_HCCTRL_LEN 0x4 __hc32 *ohci_hcctrl_reg; unsigned has_hostpc:1; - + unsigned has_lpm:1; /* support link power management */ u8 sbrn; /* packed release number */ /* irq statistics */ diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 11b63819590..9b867e64a0f 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -300,6 +300,10 @@ struct hc_driver { int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); int (*reset_device)(struct usb_hcd *, struct usb_device *); + /* Notifies the HCD after a device is connected and its + * address is set + */ + int (*update_device)(struct usb_hcd *, struct usb_device *); }; extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); -- cgit v1.2.3-70-g09d2 From 5a9cdf332eae724b11906cb1712e3a662eba32b2 Mon Sep 17 00:00:00 2001 From: Alek Du Date: Fri, 4 Jun 2010 15:47:56 +0800 Subject: USB: EHCI: EHCI 1.1 addendum: Enable Per-port change detect bits This patch will enable Per-port event feature defined in EHCI 1.1 addendum. This feature addresses an issue where HCD is currently required to read and parse PORTSC for all enabled root hub ports. With this patch, the overhead will be reduced. Signed-off-by: Alek Du Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 19 +++++++++++++++++-- drivers/usb/host/ehci-hub.c | 9 +++++++++ drivers/usb/host/ehci.h | 1 + 3 files changed, 27 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index baf9b648bb1..8697ad19f31 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -584,6 +584,11 @@ static int ehci_init(struct usb_hcd *hcd) if (log2_irq_thresh < 0 || log2_irq_thresh > 6) log2_irq_thresh = 0; temp = 1 << (16 + log2_irq_thresh); + if (HCC_PER_PORT_CHANGE_EVENT(hcc_params)) { + ehci->has_ppcd = 1; + ehci_dbg(ehci, "enable per-port change event\n"); + temp |= CMD_PPCEE; + } if (HCC_CANPARK(hcc_params)) { /* HW default park == 3, on hardware that supports it (like * NVidia and ALI silicon), maximizes throughput on the async @@ -782,6 +787,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* remote wakeup [4.3.1] */ if (status & STS_PCD) { unsigned i = HCS_N_PORTS (ehci->hcs_params); + u32 ppcd = 0; /* kick root hub later */ pcd_status = status; @@ -790,9 +796,18 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) if (!(cmd & CMD_RUN)) usb_hcd_resume_root_hub(hcd); + /* get per-port change detect bits */ + if (ehci->has_ppcd) + ppcd = status >> 16; + while (i--) { - int pstatus = ehci_readl(ehci, - &ehci->regs->port_status [i]); + int pstatus; + + /* leverage per-port change bits feature */ + if (ehci->has_ppcd && !(ppcd & (1 << i))) + continue; + pstatus = ehci_readl(ehci, + &ehci->regs->port_status[i]); if (pstatus & PORT_OWNER) continue; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 8a28dae8a37..84e792d71c2 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -603,6 +603,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) u32 mask; int ports, i, retval = 1; unsigned long flags; + u32 ppcd = 0; /* if !USB_SUSPEND, root hub timers won't get shut down ... */ if (!HC_IS_RUNNING(hcd->state)) @@ -632,7 +633,15 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) /* port N changes (bit N)? */ spin_lock_irqsave (&ehci->lock, flags); + + /* get per-port change detect bits */ + if (ehci->has_ppcd) + ppcd = ehci_readl(ehci, &ehci->regs->status) >> 16; + for (i = 0; i < ports; i++) { + /* leverage per-port change bits feature */ + if (ehci->has_ppcd && !(ppcd & (1 << i))) + continue; temp = ehci_readl(ehci, &ehci->regs->port_status [i]); /* diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 21f30a0c3d2..e6c57cc416f 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -141,6 +141,7 @@ struct ehci_hcd { /* one per controller */ __hc32 *ohci_hcctrl_reg; unsigned has_hostpc:1; unsigned has_lpm:1; /* support link power management */ + unsigned has_ppcd:1; /* support per-port change bits */ u8 sbrn; /* packed release number */ /* irq statistics */ -- cgit v1.2.3-70-g09d2 From 3f37bca1038de01ab2666adde1954460c35ff3af Mon Sep 17 00:00:00 2001 From: Leann Ogasawara Date: Thu, 10 Jun 2010 14:51:51 -0700 Subject: USB: option: Remove duplicate AMOI_VENDOR_ID AMOI_VENDOR_ID is defined twice. Remove the duplicate entry and move the AMOI_PRODUCT_9508 definition to be grouped with the other AMOI product definitions. Originally-by: Ben Collins Signed-off-by: Leann Ogasawara Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 5cd30e4345c..a8c54b2116a 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -264,9 +264,6 @@ static void option_instat_callback(struct urb *urb); #define BANDRICH_PRODUCT_1011 0x1011 #define BANDRICH_PRODUCT_1012 0x1012 -#define AMOI_VENDOR_ID 0x1614 -#define AMOI_PRODUCT_9508 0x0800 - #define QUALCOMM_VENDOR_ID 0x05C6 #define CMOTECH_VENDOR_ID 0x16d8 @@ -483,7 +480,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC) }, - { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_9508) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, /* Novatel Merlin V640/XV620 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, /* Novatel Merlin V620/S620 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) }, /* Novatel Merlin EX720/V740/X720 */ -- cgit v1.2.3-70-g09d2 From c48271e0729a42a0692a5b7e1aa1552d7f2ff8ab Mon Sep 17 00:00:00 2001 From: Leann Ogasawara Date: Thu, 10 Jun 2010 15:49:24 -0700 Subject: Revert "USB: Adding support for HTC Smartphones to ipaq" ipaq already had this device id defined: { USB_DEVICE(0x0BB4, 0x00CF) }, /* HTC USB Modem */ Revert the commit which adds the duplicate entry. This reverts commit 04cab1329336d4577d6638360c905e360934b425. Originally-by: Ben Collins Signed-off-by: Leann Ogasawara Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ipaq.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 28913fa95fb..4735931b4c7 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -534,7 +534,6 @@ static struct usb_device_id ipaq_id_table [] = { { USB_DEVICE(0x413C, 0x4009) }, /* Dell Axim USB Sync */ { USB_DEVICE(0x4505, 0x0010) }, /* Smartphone */ { USB_DEVICE(0x5E04, 0xCE00) }, /* SAGEM Wireless Assistant */ - { USB_DEVICE(0x0BB4, 0x00CF) }, /* HTC smartphone modems */ { } /* Terminating entry */ }; -- cgit v1.2.3-70-g09d2 From f7244ce6530fc500a0d99cab0a110da7ff892e56 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 10 Jun 2010 19:20:43 -0700 Subject: USB: gadget: langwell_udc.c: printk needs a (unsigned long long) cast for a dma_t Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/langwell_udc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index a3913519fd5..c2d2a201f84 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -842,9 +842,9 @@ static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req, VDBG(dev, "req->mapped = 0\n"); } - DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n", - _ep->name, - _req, _req->length, _req->buf, _req->dma); + DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08llx\n", + _ep->name, + _req, _req->length, _req->buf, (unsigned long long)_req->dma); _req->status = -EINPROGRESS; _req->actual = 0; -- cgit v1.2.3-70-g09d2 From 83a3ac866d6931611d37ded24a2a2cc99fe36e9f Mon Sep 17 00:00:00 2001 From: Nicolas Kaiser Date: Wed, 16 Jun 2010 18:56:05 +0200 Subject: usb: conexant: fixed spacing and brace coding style issues Fixed spacing and brace coding style issues. Signed-off-by: Nicolas Kaiser Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/cxacru.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 101ffc965ee..593fc5e2d2e 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -564,7 +564,7 @@ static void cxacru_timeout_kill(unsigned long data) } static int cxacru_start_wait_urb(struct urb *urb, struct completion *done, - int* actual_length) + int *actual_length) { struct timer_list timer; @@ -952,7 +952,7 @@ static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw, put_unaligned(cpu_to_le32(addr), (__le32 *)(buf + offb)); offb += 4; addr += l; - if(l) + if (l) memcpy(buf + offb, data + offd, l); if (l < stride) memset(buf + offb + l, 0, stride - l); @@ -967,7 +967,7 @@ static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw, } offb = 0; } - } while(offd < size); + } while (offd < size); dbg("sent fw %#x", fw); ret = 0; @@ -1043,8 +1043,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance, if (instance->modem_type->boot_rom_patch) { val = cpu_to_le32(BR_ADDR); ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_STACK_ADDR, (u8 *) &val, 4); - } - else { + } else { ret = cxacru_fw(usb_dev, FW_GOTO_MEM, 0x0, 0x0, FW_ADDR, NULL, 0); } if (ret) { @@ -1068,7 +1067,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance, } static int cxacru_find_firmware(struct cxacru_data *instance, - char* phase, const struct firmware **fw_p) + char *phase, const struct firmware **fw_p) { struct usbatm_data *usbatm = instance->usbatm; struct device *dev = &usbatm->usb_intf->dev; -- cgit v1.2.3-70-g09d2 From 87eb1bead832b9880126fdbea74cc8ecb22b50c0 Mon Sep 17 00:00:00 2001 From: Yann Cantin Date: Sat, 5 Jun 2010 23:06:31 +0200 Subject: USB: Add a serial number parameter to g_file_storage module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch add a serial number parameter to the g_file_storage module. There's validity checks against the string passed to comply with the specs. Signed-off-by: Yann Cantin Cc: Michał Nazarewicz Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/file_storage.c | 69 ++++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index b49d86e3e45..2b6d3649d02 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -56,7 +56,7 @@ * following protocols: RBC (0x01), ATAPI or SFF-8020i (0x02), QIC-157 (0c03), * UFI (0x04), SFF-8070i (0x05), and transparent SCSI (0x06), selected by * the optional "protocol" module parameter. In addition, the default - * Vendor ID, Product ID, and release number can be overridden. + * Vendor ID, Product ID, release number and serial number can be overridden. * * There is support for multiple logical units (LUNs), each of which has * its own backing file. The number of LUNs can be set using the optional @@ -106,6 +106,7 @@ * vendor=0xVVVV Default 0x0525 (NetChip), USB Vendor ID * product=0xPPPP Default 0xa4a5 (FSG), USB Product ID * release=0xRRRR Override the USB release number (bcdDevice) + * serial=HHHH... Override serial number (string of hex chars) * buflen=N Default N=16384, buffer size used (will be * rounded down to a multiple of * PAGE_CACHE_SIZE) @@ -270,6 +271,8 @@ #define DRIVER_DESC "File-backed Storage Gadget" #define DRIVER_NAME "g_file_storage" +/* DRIVER_VERSION must be at least 6 characters long, as it is used + * to generate a fallback serial number. */ #define DRIVER_VERSION "20 November 2008" static char fsg_string_manufacturer[64]; @@ -314,6 +317,7 @@ static struct { unsigned short vendor; unsigned short product; unsigned short release; + char *serial_parm; unsigned int buflen; int transport_type; @@ -374,6 +378,9 @@ MODULE_PARM_DESC(product, "USB Product ID"); module_param_named(release, mod_data.release, ushort, S_IRUGO); MODULE_PARM_DESC(release, "USB release number"); +module_param_named(serial, mod_data.serial_parm, charp, S_IRUGO); +MODULE_PARM_DESC(serial, "USB serial number"); + module_param_named(buflen, mod_data.buflen, uint, S_IRUGO); MODULE_PARM_DESC(buflen, "I/O buffer size"); @@ -3197,6 +3204,7 @@ static int __init check_parameters(struct fsg_dev *fsg) { int prot; int gcnum; + int i; /* Store the default values */ mod_data.transport_type = USB_PR_BULK; @@ -3272,6 +3280,55 @@ static int __init check_parameters(struct fsg_dev *fsg) ERROR(fsg, "invalid buflen\n"); return -ETOOSMALL; } + + /* Serial string handling. + * On a real device, the serial string would be loaded + * from permanent storage. */ + if (mod_data.serial_parm) { + const char *ch; + unsigned len = 0; + + /* Sanity check : + * The CB[I] specification limits the serial string to + * 12 uppercase hexadecimal characters. + * BBB need at least 12 uppercase hexadecimal characters, + * with a maximum of 126. */ + for (ch = mod_data.serial_parm; *ch; ++ch) { + ++len; + if ((*ch < '0' || *ch > '9') && + (*ch < 'A' || *ch > 'F')) { /* not uppercase hex */ + WARNING(fsg, + "Invalid serial string character: %c; " + "Failing back to default\n", + *ch); + goto fill_serial; + } + } + if (len > 126 || + (mod_data.transport_type == USB_PR_BULK && len < 12) || + (mod_data.transport_type != USB_PR_BULK && len > 12)) { + WARNING(fsg, + "Invalid serial string length; " + "Failing back to default\n"); + goto fill_serial; + } + fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial_parm; + } else { +fill_serial: + /* Serial number not specified or invalid, make our own. + * We just encode it from the driver version string, + * 12 characters to comply with both CB[I] and BBB spec. + * Warning : Two devices running the same kernel will have + * the same fallback serial number. */ + for (i = 0; i < 12; i += 2) { + unsigned char c = DRIVER_VERSION[i / 2]; + + if (!c) + break; + sprintf(&fsg_string_serial[i], "%02X", c); + } + } + #endif /* CONFIG_USB_FILE_STORAGE_TEST */ return 0; @@ -3447,16 +3504,6 @@ static int __init fsg_bind(struct usb_gadget *gadget) init_utsname()->sysname, init_utsname()->release, gadget->name); - /* On a real device, serial[] would be loaded from permanent - * storage. We just encode it from the driver version string. */ - for (i = 0; i < sizeof fsg_string_serial - 2; i += 2) { - unsigned char c = DRIVER_VERSION[i / 2]; - - if (!c) - break; - sprintf(&fsg_string_serial[i], "%02X", c); - } - fsg->thread_task = kthread_create(fsg_main_thread, fsg, "file-storage-gadget"); if (IS_ERR(fsg->thread_task)) { -- cgit v1.2.3-70-g09d2 From 16f76a7654cf603ca13543e4f914ec8b6cd9ffb2 Mon Sep 17 00:00:00 2001 From: Nicolas Kaiser Date: Thu, 17 Jun 2010 11:55:49 +0200 Subject: usb: atm: fixed spacing and indentation coding style issues Fixed spacing and indentation coding style issues. Signed-off-by: Nicolas Kaiser Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/usbatm.h | 22 +++++++++++----------- drivers/usb/atm/xusbatm.c | 10 +++++----- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h index 0863f85fcc2..5fc48940521 100644 --- a/drivers/usb/atm/usbatm.h +++ b/drivers/usb/atm/usbatm.h @@ -48,7 +48,7 @@ dev_warn(&(instance)->usb_intf->dev, \ "failed assertion '%s' at line %d", \ __stringify(x), __LINE__); \ - } while(0) + } while (0) #endif #define usb_err(instance, format, arg...) \ @@ -59,7 +59,7 @@ dev_warn(&(instance)->usb_intf->dev , format , ## arg) #ifdef DEBUG #define usb_dbg(instance, format, arg...) \ - dev_printk(KERN_DEBUG , &(instance)->usb_intf->dev , format , ## arg) + dev_printk(KERN_DEBUG , &(instance)->usb_intf->dev , format , ## arg) #else #define usb_dbg(instance, format, arg...) \ do {} while (0) @@ -104,21 +104,21 @@ struct usbatm_data; /* * Assuming all methods exist and succeed, they are called in this order: * -* bind, heavy_init, atm_start, ..., atm_stop, unbind +* bind, heavy_init, atm_start, ..., atm_stop, unbind */ struct usbatm_driver { const char *driver_name; /* init device ... can sleep, or cause probe() failure */ - int (*bind) (struct usbatm_data *, struct usb_interface *, + int (*bind) (struct usbatm_data *, struct usb_interface *, const struct usb_device_id *id); /* additional device initialization that is too slow to be done in probe() */ - int (*heavy_init) (struct usbatm_data *, struct usb_interface *); + int (*heavy_init) (struct usbatm_data *, struct usb_interface *); /* cleanup device ... can sleep, but can't fail */ - void (*unbind) (struct usbatm_data *, struct usb_interface *); + void (*unbind) (struct usbatm_data *, struct usb_interface *); /* init ATM device ... can sleep, or cause ATM initialization failure */ int (*atm_start) (struct usbatm_data *, struct atm_dev *); @@ -126,9 +126,9 @@ struct usbatm_driver { /* cleanup ATM device ... can sleep, but can't fail */ void (*atm_stop) (struct usbatm_data *, struct atm_dev *); - int bulk_in; /* bulk rx endpoint */ - int isoc_in; /* isochronous rx endpoint */ - int bulk_out; /* bulk tx endpoint */ + int bulk_in; /* bulk rx endpoint */ + int isoc_in; /* isochronous rx endpoint */ + int bulk_out; /* bulk tx endpoint */ unsigned rx_padding; unsigned tx_padding; @@ -156,7 +156,7 @@ struct usbatm_channel { struct usbatm_data { /****************** * public fields * - ******************/ + ******************/ /* mini driver */ struct usbatm_driver *driver; @@ -174,7 +174,7 @@ struct usbatm_data { /******************************** * private fields - do not use * - ********************************/ + ********************************/ struct kref refcount; struct mutex serialize; diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c index 17d167bbd2d..48ee0c5ff28 100644 --- a/drivers/usb/atm/xusbatm.c +++ b/drivers/usb/atm/xusbatm.c @@ -49,13 +49,13 @@ static struct usbatm_driver xusbatm_drivers[XUSBATM_DRIVERS_MAX]; static struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1]; static struct usb_driver xusbatm_usb_driver; -static struct usb_interface *xusbatm_find_intf (struct usb_device *usb_dev, int altsetting, u8 ep) +static struct usb_interface *xusbatm_find_intf(struct usb_device *usb_dev, int altsetting, u8 ep) { struct usb_host_interface *alt; struct usb_interface *intf; int i, j; - for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) + for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) if ((intf = usb_dev->actconfig->interface[i]) && (alt = usb_altnum_to_altsetting(intf, altsetting))) for (j = 0; j < alt->desc.bNumEndpoints; j++) if (alt->endpoint[j].desc.bEndpointAddress == ep) @@ -63,7 +63,7 @@ static struct usb_interface *xusbatm_find_intf (struct usb_device *usb_dev, int return NULL; } -static int xusbatm_capture_intf (struct usbatm_data *usbatm, struct usb_device *usb_dev, +static int xusbatm_capture_intf(struct usbatm_data *usbatm, struct usb_device *usb_dev, struct usb_interface *intf, int altsetting, int claim) { int ifnum = intf->altsetting->desc.bInterfaceNumber; @@ -80,7 +80,7 @@ static int xusbatm_capture_intf (struct usbatm_data *usbatm, struct usb_device * return 0; } -static void xusbatm_release_intf (struct usb_device *usb_dev, struct usb_interface *intf, int claimed) +static void xusbatm_release_intf(struct usb_device *usb_dev, struct usb_interface *intf, int claimed) { if (claimed) { usb_set_intfdata(intf, NULL); @@ -147,7 +147,7 @@ static void xusbatm_unbind(struct usbatm_data *usbatm, usb_dbg(usbatm, "%s entered\n", __func__); - for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) { + for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) { struct usb_interface *cur_intf = usb_dev->actconfig->interface[i]; if (cur_intf && (usb_get_intfdata(cur_intf) == usbatm)) { -- cgit v1.2.3-70-g09d2 From 6e12ea4658487ba9c746e95b31014cb89f63703b Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Jun 2010 23:04:40 +0200 Subject: USB-BKL: Remove lock_kernel in usbfs update_sb() The code this is attempting to lock against does not use the BKL, so it's not needed. Most likely this code is still broken/racy (Al Viro also thinks so), but removing the BKL should not make it worse than before. Signed-off-by: Andi Kleen Cc: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/inode.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 1a27618b67d..095fa536669 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -265,13 +265,9 @@ static int remount(struct super_block *sb, int *flags, char *data) return -EINVAL; } - lock_kernel(); - if (usbfs_mount && usbfs_mount->mnt_sb) update_sb(usbfs_mount->mnt_sb); - unlock_kernel(); - return 0; } -- cgit v1.2.3-70-g09d2 From c532b29a6f6d31e84a7c88f995eebdc75ebd4248 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Jun 2010 23:04:41 +0200 Subject: USB-BKL: Convert usb_driver ioctl to unlocked_ioctl And audit all the users. None needed the BKL. That was easy because there was only very few around. Tested with allmodconfig build on x86-64 Signed-off-by: Andi Kleen Cc: Arnd Bergmann From: Andi Kleen --- drivers/usb/core/devio.c | 7 ++----- drivers/usb/core/hub.c | 3 ++- drivers/usb/misc/usbtest.c | 3 ++- include/linux/usb.h | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index c2f62a3993d..f1aaff6202a 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1668,13 +1668,10 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) default: if (intf->dev.driver) driver = to_usb_driver(intf->dev.driver); - if (driver == NULL || driver->ioctl == NULL) { + if (driver == NULL || driver->unlocked_ioctl == NULL) { retval = -ENOTTY; } else { - /* keep API that guarantees BKL */ - lock_kernel(); - retval = driver->ioctl(intf, ctl->ioctl_code, buf); - unlock_kernel(); + retval = driver->unlocked_ioctl(intf, ctl->ioctl_code, buf); if (retval == -ENOIOCTLCMD) retval = -ENOTTY; } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 9cd77a2af82..d337ef80bf4 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1294,6 +1294,7 @@ descriptor_error: return -ENODEV; } +/* No BKL needed */ static int hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) { @@ -3465,7 +3466,7 @@ static struct usb_driver hub_driver = { .reset_resume = hub_reset_resume, .pre_reset = hub_pre_reset, .post_reset = hub_post_reset, - .ioctl = hub_ioctl, + .unlocked_ioctl = hub_ioctl, .id_table = hub_id_table, .supports_autosuspend = 1, }; diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 16dffe99d9f..0cfbd789ddf 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1548,6 +1548,7 @@ fail: * off just killing the userspace task and waiting for it to exit. */ +/* No BKL needed */ static int usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) { @@ -2170,7 +2171,7 @@ static struct usb_driver usbtest_driver = { .name = "usbtest", .id_table = id_table, .probe = usbtest_probe, - .ioctl = usbtest_ioctl, + .unlocked_ioctl = usbtest_ioctl, .disconnect = usbtest_disconnect, .suspend = usbtest_suspend, .resume = usbtest_resume, diff --git a/include/linux/usb.h b/include/linux/usb.h index d5922a87799..e6cbc34901f 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -843,7 +843,7 @@ struct usb_driver { void (*disconnect) (struct usb_interface *intf); - int (*ioctl) (struct usb_interface *intf, unsigned int code, + int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code, void *buf); int (*suspend) (struct usb_interface *intf, pm_message_t message); -- cgit v1.2.3-70-g09d2 From 0daeed381c6a33fdbdc3b0e9f09d96f0a2a8a195 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Jun 2010 23:04:42 +0200 Subject: USB-BKL: Remove BKL use for usb serial driver probing The usb serial driver initialization tried to use the BKL to stop driver modules from unloading, but that didn't work anyways. There was already some code to do proper try_module_get, but it was conditional on having a new probe interface. I checked all the low level drivers and they all have proper .owner = THIS_MODULE, so it's ok to always use. The other problem was the usb_serial_driver_list needing protection by a lock. This was broken anyways because unregister did not necessarily have the BKL. I extended the extending table_lock mutex to protect this case too. With these changes the BKL can be removed here. Signed-off-by: Andi Kleen Cc: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 941c2d409f8..443468e9d66 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -653,6 +653,7 @@ exit: return id; } +/* Caller must hold table_lock */ static struct usb_serial_driver *search_serial_device( struct usb_interface *iface) { @@ -718,17 +719,23 @@ int usb_serial_probe(struct usb_interface *interface, int num_ports = 0; int max_endpoints; - lock_kernel(); /* guard against unloading a serial driver module */ + mutex_lock(&table_lock); type = search_serial_device(interface); if (!type) { - unlock_kernel(); + mutex_unlock(&table_lock); dbg("none matched"); return -ENODEV; } + if (!try_module_get(type->driver.owner)) { + mutex_unlock(&table_lock); + dev_err(&interface->dev, "module get failed, exiting\n"); + return -EIO; + } + mutex_unlock(&table_lock); + serial = create_serial(dev, interface, type); if (!serial) { - unlock_kernel(); dev_err(&interface->dev, "%s - out of memory\n", __func__); return -ENOMEM; } @@ -737,20 +744,11 @@ int usb_serial_probe(struct usb_interface *interface, if (type->probe) { const struct usb_device_id *id; - if (!try_module_get(type->driver.owner)) { - unlock_kernel(); - dev_err(&interface->dev, - "module get failed, exiting\n"); - kfree(serial); - return -EIO; - } - id = get_iface_id(type, interface); retval = type->probe(serial, id); module_put(type->driver.owner); if (retval) { - unlock_kernel(); dbg("sub driver rejected device"); kfree(serial); return retval; @@ -822,7 +820,6 @@ int usb_serial_probe(struct usb_interface *interface, * properly during a later invocation of usb_serial_probe */ if (num_bulk_in == 0 || num_bulk_out == 0) { - unlock_kernel(); dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n"); kfree(serial); return -ENODEV; @@ -835,7 +832,6 @@ int usb_serial_probe(struct usb_interface *interface, if (type == &usb_serial_generic_device) { num_ports = num_bulk_out; if (num_ports == 0) { - unlock_kernel(); dev_err(&interface->dev, "Generic device with no bulk out, not allowed.\n"); kfree(serial); @@ -847,7 +843,6 @@ int usb_serial_probe(struct usb_interface *interface, /* if this device type has a calc_num_ports function, call it */ if (type->calc_num_ports) { if (!try_module_get(type->driver.owner)) { - unlock_kernel(); dev_err(&interface->dev, "module get failed, exiting\n"); kfree(serial); @@ -878,7 +873,6 @@ int usb_serial_probe(struct usb_interface *interface, max_endpoints = max(max_endpoints, num_interrupt_out); max_endpoints = max(max_endpoints, (int)serial->num_ports); serial->num_port_pointers = max_endpoints; - unlock_kernel(); dbg("%s - setting up %d port structures for this device", __func__, max_endpoints); @@ -1349,6 +1343,7 @@ int usb_serial_register(struct usb_serial_driver *driver) driver->description = driver->driver.name; /* Add this device to our list of devices */ + mutex_lock(&table_lock); list_add(&driver->driver_list, &usb_serial_driver_list); retval = usb_serial_bus_register(driver); @@ -1360,6 +1355,7 @@ int usb_serial_register(struct usb_serial_driver *driver) printk(KERN_INFO "USB Serial support registered for %s\n", driver->description); + mutex_unlock(&table_lock); return retval; } EXPORT_SYMBOL_GPL(usb_serial_register); @@ -1370,8 +1366,10 @@ void usb_serial_deregister(struct usb_serial_driver *device) /* must be called with BKL held */ printk(KERN_INFO "USB Serial deregistering driver %s\n", device->description); + mutex_lock(&table_lock); list_del(&device->driver_list); usb_serial_bus_deregister(device); + mutex_unlock(&table_lock); } EXPORT_SYMBOL_GPL(usb_serial_deregister); -- cgit v1.2.3-70-g09d2 From 00b81fb23a4937a24cb010f41ac173a786eb4c55 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Jun 2010 23:04:43 +0200 Subject: USB-BKL: Remove BKL use in uhci-debug BKL was not really needed, just came from earlier push downs. The only part that's a bit dodgy is the lseek function. Would need another lock or atomic access to fpos on 32bit? Better to have a libfs lseek Signed-off-by: Andi Kleen Cc: Arnd Bergmann Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-debug.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 98cf0b26b96..c168999722d 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -495,18 +495,16 @@ static int uhci_debug_open(struct inode *inode, struct file *file) { struct uhci_hcd *uhci = inode->i_private; struct uhci_debug *up; - int ret = -ENOMEM; unsigned long flags; - lock_kernel(); up = kmalloc(sizeof(*up), GFP_KERNEL); if (!up) - goto out; + return -ENOMEM; up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL); if (!up->data) { kfree(up); - goto out; + return -ENOMEM; } up->size = 0; @@ -517,10 +515,7 @@ static int uhci_debug_open(struct inode *inode, struct file *file) file->private_data = up; - ret = 0; -out: - unlock_kernel(); - return ret; + return 0; } static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence) @@ -528,9 +523,9 @@ static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence) struct uhci_debug *up; loff_t new = -1; - lock_kernel(); up = file->private_data; + /* XXX: atomic 64bit seek access, but that needs to be fixed in the VFS */ switch (whence) { case 0: new = off; @@ -539,11 +534,10 @@ static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence) new = file->f_pos + off; break; } - if (new < 0 || new > up->size) { - unlock_kernel(); + + if (new < 0 || new > up->size) return -EINVAL; - } - unlock_kernel(); + return (file->f_pos = new); } -- cgit v1.2.3-70-g09d2 From 1548b13b75a2ec06f46220004e91c37818be6c18 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 1 Jun 2010 23:04:44 +0200 Subject: usb: gadget: Do not take BKL for gadget->ops->ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no gadget driver in the tree that actually implements the ioctl operation, so obviously it is not necessary to hold the BKL around the call. Signed-off-by: Arnd Bergmann Cc: David Brownell Cc: Michał Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_fs.c | 2 -- drivers/usb/gadget/inode.c | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 2aaa0f75c6c..c51c2131407 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -714,9 +714,7 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value) struct ffs_function *func = ffs->func; ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV; } else if (gadget->ops->ioctl) { - lock_kernel(); ret = gadget->ops->ioctl(gadget, code, value); - unlock_kernel(); } else { ret = -ENOTTY; } diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 63fc171c0ed..fc35406fc80 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -1299,11 +1299,9 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value) struct usb_gadget *gadget = dev->gadget; long ret = -ENOTTY; - if (gadget->ops->ioctl) { - lock_kernel(); + if (gadget->ops->ioctl) ret = gadget->ops->ioctl (gadget, code, value); - unlock_kernel(); - } + return ret; } -- cgit v1.2.3-70-g09d2 From 3b759c75febd8f9ce91a05705ec43eb7f4b5ed3d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 1 Jun 2010 23:04:45 +0200 Subject: USB: mon: kill BKL usage compat_ioctl does not use the BKL, so I assume that the native function does not need it either. The open function is already protected by the driver's mutex, the BKL is probably not needed here either. Signed-off-by: Arnd Bergmann Cc: Alan Stern Cc: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_bin.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 61c76b13f0f..1be0b9f9336 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -646,17 +646,14 @@ static int mon_bin_open(struct inode *inode, struct file *file) size_t size; int rc; - lock_kernel(); mutex_lock(&mon_lock); if ((mbus = mon_bus_lookup(iminor(inode))) == NULL) { mutex_unlock(&mon_lock); - unlock_kernel(); return -ENODEV; } if (mbus != &mon_bus0 && mbus->u_bus == NULL) { printk(KERN_ERR TAG ": consistency error on open\n"); mutex_unlock(&mon_lock); - unlock_kernel(); return -ENODEV; } @@ -689,7 +686,6 @@ static int mon_bin_open(struct inode *inode, struct file *file) file->private_data = rp; mutex_unlock(&mon_lock); - unlock_kernel(); return 0; err_allocbuff: @@ -698,7 +694,6 @@ err_allocvec: kfree(rp); err_alloc: mutex_unlock(&mon_lock); - unlock_kernel(); return rc; } @@ -954,7 +949,7 @@ static int mon_bin_queued(struct mon_reader_bin *rp) /* */ -static int mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct mon_reader_bin *rp = file->private_data; // struct mon_bus* mbus = rp->r.m_bus; @@ -1094,19 +1089,6 @@ static int mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return ret; } -static long mon_bin_unlocked_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret; - - lock_kernel(); - ret = mon_bin_ioctl(file, cmd, arg); - unlock_kernel(); - - return ret; -} - - #ifdef CONFIG_COMPAT static long mon_bin_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) @@ -1250,7 +1232,7 @@ static const struct file_operations mon_fops_binary = { .read = mon_bin_read, /* .write = mon_text_write, */ .poll = mon_bin_poll, - .unlocked_ioctl = mon_bin_unlocked_ioctl, + .unlocked_ioctl = mon_bin_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = mon_bin_compat_ioctl, #endif -- cgit v1.2.3-70-g09d2 From 26eca10e6ef64e15f250523a1e7e94ad40ac2bf8 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 16 Jun 2010 12:07:56 +0200 Subject: USB: gadget: g_mass_storage: static data instead of dynamic allocation This patch changes msg_do_config() function so that it uses a static object for a fsg_common structure instead of dynamically allocated. This is a micro-optimisation. Signed-off-by: Michal Nazarewicz Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/mass_storage.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 705cc1f7632..e68c00e0876 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -143,7 +143,9 @@ static int msg_thread_exits(struct fsg_common *common) static int __init msg_do_config(struct usb_configuration *c) { - struct fsg_common *common; + static struct fsg_common common; + + struct fsg_common *retp; struct fsg_config config; int ret; @@ -154,12 +156,13 @@ static int __init msg_do_config(struct usb_configuration *c) fsg_config_from_params(&config, &mod_data); config.thread_exits = msg_thread_exits; - common = fsg_common_init(0, c->cdev, &config); - if (IS_ERR(common)) - return PTR_ERR(common); - ret = fsg_add(c->cdev, c, common); - fsg_common_put(common); + retp = fsg_common_init(&common, c->cdev, &config); + if (IS_ERR(retp)) + return PTR_ERR(retp); + + ret = fsg_add(c->cdev, c, &common); + fsg_common_put(&common); return ret; } -- cgit v1.2.3-70-g09d2 From 1dc90985d1b29275607ddac59555199e7894f633 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 16 Jun 2010 12:07:57 +0200 Subject: USB: gadget: f_mass_storage: fsg_add() renamed to fsg_bind_config() Mass Storage Function had a bit unique name for function used to add it to USB configuration. Renamed as to match naming convention of other functions. Signed-off-by: Michal Nazarewicz Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 13 ++++++++++--- drivers/usb/gadget/mass_storage.c | 2 +- drivers/usb/gadget/multi.c | 4 ++-- 3 files changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 4ce899c9b16..8a95ebc7e3d 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2990,9 +2990,9 @@ static struct usb_gadget_strings *fsg_strings_array[] = { NULL, }; -static int fsg_add(struct usb_composite_dev *cdev, - struct usb_configuration *c, - struct fsg_common *common) +static int fsg_bind_config(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct fsg_common *common) { struct fsg_dev *fsg; int rc; @@ -3024,6 +3024,13 @@ static int fsg_add(struct usb_composite_dev *cdev, return rc; } +static inline int __deprecated __maybe_unused +fsg_add(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct fsg_common *common) +{ + return fsg_bind_config(cdev, c, common); +} /************************* Module parameters *************************/ diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index e68c00e0876..2b11e207982 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -161,7 +161,7 @@ static int __init msg_do_config(struct usb_configuration *c) if (IS_ERR(retp)) return PTR_ERR(retp); - ret = fsg_add(c->cdev, c, &common); + ret = fsg_bind_config(c->cdev, c, &common); fsg_common_put(&common); return ret; } diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index a930d7fd7e7..d3d31403302 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -172,7 +172,7 @@ static int __init rndis_do_config(struct usb_configuration *c) if (ret < 0) return ret; - ret = fsg_add(c->cdev, c, fsg_common); + ret = fsg_bind_config(c->cdev, c, fsg_common); if (ret < 0) return ret; @@ -208,7 +208,7 @@ static int __init cdc_do_config(struct usb_configuration *c) if (ret < 0) return ret; - ret = fsg_add(c->cdev, c, fsg_common); + ret = fsg_bind_config(c->cdev, c, fsg_common); if (ret < 0) return ret; -- cgit v1.2.3-70-g09d2 From 7898aee1dacbb246fee958f0a6102320b61768d9 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 16 Jun 2010 12:07:58 +0200 Subject: USB: gadget: f_fs: functionfs_add() renamed to functionfs_bind_config() FunctionFS had a bit unique name for function used to add it to USB configuration. Renamed as to match naming convention of other functions. Signed-off-by: Michal Nazarewicz Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_fs.c | 6 +++--- drivers/usb/gadget/g_ffs.c | 2 +- include/linux/usb/functionfs.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index c51c2131407..282b49e336b 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -1478,9 +1478,9 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) } -static int functionfs_add(struct usb_composite_dev *cdev, - struct usb_configuration *c, - struct ffs_data *ffs) +static int functionfs_bind_config(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct ffs_data *ffs) { struct ffs_function *func; int ret; diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index d1af253a910..da3a9e40349 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -388,7 +388,7 @@ static int __gfs_do_config(struct usb_configuration *c, return ret; } - ret = functionfs_add(c->cdev, c, gfs_ffs_data); + ret = functionfs_bind_config(c->cdev, c, gfs_ffs_data); if (unlikely(ret < 0)) return ret; diff --git a/include/linux/usb/functionfs.h b/include/linux/usb/functionfs.h index a34a2a043b2..6f649c13193 100644 --- a/include/linux/usb/functionfs.h +++ b/include/linux/usb/functionfs.h @@ -180,9 +180,9 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) static void functionfs_unbind(struct ffs_data *ffs) __attribute__((nonnull)); -static int functionfs_add(struct usb_composite_dev *cdev, - struct usb_configuration *c, - struct ffs_data *ffs) +static int functionfs_bind_config(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct ffs_data *ffs) __attribute__((warn_unused_result, nonnull)); -- cgit v1.2.3-70-g09d2 From f2adc4f8aaf272de9ac71dcb18d95ebe05fc3f94 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 16 Jun 2010 12:07:59 +0200 Subject: USB: gadget: composite: usb_string_ids_*() functions added usb_string_ids_tab() and usb_string_ids_n() functions added to the composite framework. The first accepts an array of usb_string object and for each registeres a string id and the second registeres a given number of ids and returns the first. This may simplify string ids registration since gadgets and composite functions won't have to call usb_string_id() several times and each time check for errer status -- all this will be done with a single call. Signed-off-by: Michal Nazarewicz Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 71 +++++++++++++++++++++++++++++++++++++++--- include/linux/usb/composite.h | 4 +++ 2 files changed, 71 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 391d169f8d0..125167e17ce 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -673,20 +673,83 @@ static int get_string(struct usb_composite_dev *cdev, * string IDs. Drivers for functions, configurations, or gadgets will * then store that ID in the appropriate descriptors and string table. * - * All string identifier should be allocated using this routine, to - * ensure that for example different functions don't wrongly assign - * different meanings to the same identifier. + * All string identifier should be allocated using this, + * @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure + * that for example different functions don't wrongly assign different + * meanings to the same identifier. */ int usb_string_id(struct usb_composite_dev *cdev) { if (cdev->next_string_id < 254) { - /* string id 0 is reserved */ + /* string id 0 is reserved by USB spec for list of + * supported languages */ + /* 255 reserved as well? -- mina86 */ cdev->next_string_id++; return cdev->next_string_id; } return -ENODEV; } +/** + * usb_string_ids() - allocate unused string IDs in batch + * @cdev: the device whose string descriptor IDs are being allocated + * @str: an array of usb_string objects to assign numbers to + * Context: single threaded during gadget setup + * + * @usb_string_ids() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then copy IDs from the string table to the appropriate descriptors + * and string table for other languages. + * + * All string identifier should be allocated using this, + * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for + * example different functions don't wrongly assign different meanings + * to the same identifier. + */ +int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str) +{ + int next = cdev->next_string_id; + + for (; str->s; ++str) { + if (unlikely(next >= 254)) + return -ENODEV; + str->id = ++next; + } + + cdev->next_string_id = next; + + return 0; +} + +/** + * usb_string_ids_n() - allocate unused string IDs in batch + * @cdev: the device whose string descriptor IDs are being allocated + * @n: number of string IDs to allocate + * Context: single threaded during gadget setup + * + * Returns the first requested ID. This ID and next @n-1 IDs are now + * valid IDs. At least providind that @n is non zore because if it + * is, returns last requested ID which is now very useful information. + * + * @usb_string_ids_n() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this, + * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for + * example different functions don't wrongly assign different meanings + * to the same identifier. + */ +int usb_string_ids_n(struct usb_composite_dev *c, unsigned n) +{ + unsigned next = c->next_string_id; + if (unlikely(n > 254 || (unsigned)next + n > 254)) + return -ENODEV; + c->next_string_id += n; + return next + 1; +} + + /*-------------------------------------------------------------------------*/ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 139353efad3..f378075c839 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -342,6 +342,10 @@ struct usb_composite_dev { }; extern int usb_string_id(struct usb_composite_dev *c); +extern int usb_string_ids_tab(struct usb_composite_dev *c, + struct usb_string *str); +extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n); + /* messaging utils */ #define DBG(d, fmt, args...) \ -- cgit v1.2.3-70-g09d2 From fd7c9a007f7d45df86974c3f83d67ab21cc21f1f Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 16 Jun 2010 12:08:00 +0200 Subject: USB: gadget: f_fs: use usb_string_ids_n() Use usb_string_ids_n() function to simplify string ids registeration. Signed-off-by: Michal Nazarewicz Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_fs.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 282b49e336b..e4f59505520 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -1375,7 +1375,8 @@ static void ffs_data_reset(struct ffs_data *ffs) static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) { - unsigned i, count; + struct usb_gadget_strings **lang; + int first_id; ENTER(); @@ -1383,7 +1384,9 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) || test_and_set_bit(FFS_FL_BOUND, &ffs->flags))) return -EBADFD; - ffs_data_get(ffs); + first_id = usb_string_ids_n(cdev, ffs->strings_count); + if (unlikely(first_id < 0)) + return first_id; ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); if (unlikely(!ffs->ep0req)) @@ -1391,25 +1394,16 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) ffs->ep0req->complete = ffs_ep0_complete; ffs->ep0req->context = ffs; - /* Get strings identifiers */ - for (count = ffs->strings_count, i = 0; i < count; ++i) { - struct usb_gadget_strings **lang; - - int id = usb_string_id(cdev); - if (unlikely(id < 0)) { - usb_ep_free_request(cdev->gadget->ep0, ffs->ep0req); - ffs->ep0req = NULL; - return id; - } - - lang = ffs->stringtabs; - do { - (*lang)->strings[i].id = id; - ++lang; - } while (*lang); + lang = ffs->stringtabs; + for (lang = ffs->stringtabs; *lang; ++lang) { + struct usb_string *str = (*lang)->strings; + int id = first_id; + for (; str->s; ++id, ++str) + str->id = id; } ffs->gadget = cdev->gadget; + ffs_data_get(ffs); return 0; } -- cgit v1.2.3-70-g09d2 From 248720529cb50c6b6cf2ec01748049c6b532a8cc Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 18 Jun 2010 15:59:43 +0200 Subject: USB: gadget: f_mass_storage: dead code removed The ep0req_name was never used in f_mass_storage hence it may be safely removed from the code. It was a leftover from File Storage Gadget which used it for debug messages. Signed-off-by: Michal Nazarewicz Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 8a95ebc7e3d..fa86b946dcd 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -333,7 +333,6 @@ struct fsg_common { struct usb_ep *ep0; /* Copy of gadget->ep0 */ struct usb_request *ep0req; /* Copy of cdev->req */ unsigned int ep0_req_tag; - const char *ep0req_name; struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; @@ -623,8 +622,6 @@ static int fsg_setup(struct usb_function *f, /* Respond with data/status */ req->length = min((u16)1, w_length); - fsg->common->ep0req_name = - ctrl->bRequestType & USB_DIR_IN ? "ep0-in" : "ep0-out"; return ep0_queue(fsg->common); } -- cgit v1.2.3-70-g09d2 From 279cc49a697d5b4f9477cd68eeb4ef20798400fe Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 21 Jun 2010 13:57:03 +0200 Subject: USB: gadget: g_multi: code clean up and refactoring The Multifunction Composite Gadget have been cleaned up and refactored so hopefully it looks prettier and works at least as good as before changes. A Kconfig has also been fixed to make it impossible to build FunctionFS gadget with no configurations. With this patch, if RNDIS is not chosen by the user CDC is force-selected. Signed-off-by: Michal Nazarewicz Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/multi.c | 262 +++++++++++++++++++++++++-------------------- 2 files changed, 147 insertions(+), 116 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 591ae9fde19..027f61b1f3d 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -863,6 +863,7 @@ config USB_G_NOKIA config USB_G_MULTI tristate "Multifunction Composite Gadget (EXPERIMENTAL)" depends on BLOCK && NET + select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS help The Multifunction Composite Gadget provides Ethernet (RNDIS and/or CDC Ethernet), mass storage and ACM serial link diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index d3d31403302..795d7623216 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -24,6 +24,7 @@ #include #include +#include #if defined USB_ETH_RNDIS @@ -35,14 +36,13 @@ #define DRIVER_DESC "Multifunction Composite Gadget" -#define DRIVER_VERSION "2009/07/21" -/*-------------------------------------------------------------------------*/ +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Michal Nazarewicz"); +MODULE_LICENSE("GPL"); -#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */ -#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */ -/*-------------------------------------------------------------------------*/ +/***************************** All the files... *****************************/ /* * kbuild is not very cooperative with respect to linking separately @@ -57,6 +57,8 @@ #include "config.c" #include "epautoconf.c" +#include "f_mass_storage.c" + #include "u_serial.c" #include "f_acm.c" @@ -68,13 +70,24 @@ #endif #include "u_ether.c" -#undef DBG /* u_ether.c has broken idea about macros */ -#undef VDBG /* so clean up after it */ -#undef ERROR -#undef INFO -#include "f_mass_storage.c" -/*-------------------------------------------------------------------------*/ + +/***************************** Device Descriptor ****************************/ + +#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */ +#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */ + + +enum { + __MULTI_NO_CONFIG, +#ifdef CONFIG_USB_G_MULTI_RNDIS + MULTI_RNDIS_CONFIG_NUM, +#endif +#ifdef CONFIG_USB_G_MULTI_CDC + MULTI_CDC_CONFIG_NUM, +#endif +}; + static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, @@ -82,80 +95,82 @@ static struct usb_device_descriptor device_desc = { .bcdUSB = cpu_to_le16(0x0200), - /* .bDeviceClass = USB_CLASS_COMM, */ - /* .bDeviceSubClass = 0, */ - /* .bDeviceProtocol = 0, */ - .bDeviceClass = 0xEF, + .bDeviceClass = USB_CLASS_MISC /* 0xEF */, .bDeviceSubClass = 2, .bDeviceProtocol = 1, - /* .bMaxPacketSize0 = f(hardware) */ /* Vendor and product id can be overridden by module parameters. */ .idVendor = cpu_to_le16(MULTI_VENDOR_NUM), .idProduct = cpu_to_le16(MULTI_PRODUCT_NUM), - /* .bcdDevice = f(hardware) */ - /* .iManufacturer = DYNAMIC */ - /* .iProduct = DYNAMIC */ - /* NO SERIAL NUMBER */ - .bNumConfigurations = 1, }; -static struct usb_otg_descriptor otg_descriptor = { - .bLength = sizeof otg_descriptor, - .bDescriptorType = USB_DT_OTG, - - /* REVISIT SRP-only hardware is possible, although - * it would not be called "OTG" ... - */ - .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, -}; static const struct usb_descriptor_header *otg_desc[] = { - (struct usb_descriptor_header *) &otg_descriptor, + (struct usb_descriptor_header *) &(struct usb_otg_descriptor){ + .bLength = sizeof(struct usb_otg_descriptor), + .bDescriptorType = USB_DT_OTG, + + /* + * REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, + }, NULL, }; -/* string IDs are assigned dynamically */ - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 +enum { + MULTI_STRING_MANUFACTURER_IDX, + MULTI_STRING_PRODUCT_IDX, +#ifdef CONFIG_USB_G_MULTI_RNDIS + MULTI_STRING_RNDIS_CONFIG_IDX, +#endif +#ifdef CONFIG_USB_G_MULTI_CDC + MULTI_STRING_CDC_CONFIG_IDX, +#endif +}; static char manufacturer[50]; static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = DRIVER_DESC, + [MULTI_STRING_MANUFACTURER_IDX].s = manufacturer, + [MULTI_STRING_PRODUCT_IDX].s = DRIVER_DESC, +#ifdef CONFIG_USB_G_MULTI_RNDIS + [MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS", +#endif +#ifdef CONFIG_USB_G_MULTI_CDC + [MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM", +#endif { } /* end of list */ }; -static struct usb_gadget_strings stringtab_dev = { - .language = 0x0409, /* en-us */ - .strings = strings_dev, -}; - static struct usb_gadget_strings *dev_strings[] = { - &stringtab_dev, + &(struct usb_gadget_strings){ + .language = 0x0409, /* en-us */ + .strings = strings_dev, + }, NULL, }; -static u8 hostaddr[ETH_ALEN]; /****************************** Configurations ******************************/ -static struct fsg_module_parameters mod_data = { - .stall = 1 -}; -FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); +static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; +FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); + +static struct fsg_common fsg_common; + +static u8 hostaddr[ETH_ALEN]; -static struct fsg_common *fsg_common; +/********** RNDIS **********/ #ifdef USB_ETH_RNDIS -static int __init rndis_do_config(struct usb_configuration *c) +static __ref int rndis_do_config(struct usb_configuration *c) { int ret; @@ -172,26 +187,42 @@ static int __init rndis_do_config(struct usb_configuration *c) if (ret < 0) return ret; - ret = fsg_bind_config(c->cdev, c, fsg_common); + ret = fsg_bind_config(c->cdev, c, &fsg_common); if (ret < 0) return ret; return 0; } -static struct usb_configuration rndis_config_driver = { - .label = "Multifunction Composite (RNDIS + MS + ACM)", - .bind = rndis_do_config, - .bConfigurationValue = 2, - /* .iConfiguration = DYNAMIC */ - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, -}; +static int rndis_config_register(struct usb_composite_dev *cdev) +{ + static struct usb_configuration config = { + .bind = rndis_do_config, + .bConfigurationValue = MULTI_RNDIS_CONFIG_NUM, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + }; + + config.label = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].s; + config.iConfiguration = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].id; + + return usb_add_config(cdev, &config); +} + +#else + +static int rndis_config_register(struct usb_composite_dev *cdev) +{ + return 0; +} #endif + +/********** CDC ECM **********/ + #ifdef CONFIG_USB_G_MULTI_CDC -static int __init cdc_do_config(struct usb_configuration *c) +static __ref int cdc_do_config(struct usb_configuration *c) { int ret; @@ -208,20 +239,33 @@ static int __init cdc_do_config(struct usb_configuration *c) if (ret < 0) return ret; - ret = fsg_bind_config(c->cdev, c, fsg_common); + ret = fsg_bind_config(c->cdev, c, &fsg_common); if (ret < 0) return ret; return 0; } -static struct usb_configuration cdc_config_driver = { - .label = "Multifunction Composite (CDC + MS + ACM)", - .bind = cdc_do_config, - .bConfigurationValue = 1, - /* .iConfiguration = DYNAMIC */ - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, -}; +static int cdc_config_register(struct usb_composite_dev *cdev) +{ + static struct usb_configuration config = { + .bind = cdc_do_config, + .bConfigurationValue = MULTI_CDC_CONFIG_NUM, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + }; + + config.label = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].s; + config.iConfiguration = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].id; + + return usb_add_config(cdev, &config); +} + +#else + +static int cdc_config_register(struct usb_composite_dev *cdev) +{ + return 0; +} #endif @@ -230,7 +274,7 @@ static struct usb_configuration cdc_config_driver = { /****************************** Gadget Bind ******************************/ -static int __init multi_bind(struct usb_composite_dev *cdev) +static int __ref multi_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; int status, gcnum; @@ -252,67 +296,56 @@ static int __init multi_bind(struct usb_composite_dev *cdev) goto fail0; /* set up mass storage function */ - fsg_common = fsg_common_from_params(0, cdev, &mod_data); - if (IS_ERR(fsg_common)) { - status = PTR_ERR(fsg_common); - goto fail1; + { + void *retp; + retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); + if (IS_ERR(retp)) { + status = PTR_ERR(retp); + goto fail1; + } } - + /* set bcdDevice */ gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) + if (gcnum >= 0) { device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); - else { - /* We assume that can_support_ecm() tells the truth; - * but if the controller isn't recognized at all then - * that assumption is a bit more likely to be wrong. - */ - WARNING(cdev, "controller '%s' not recognized\n", - gadget->name); + } else { + WARNING(cdev, "controller '%s' not recognized\n", gadget->name); device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099); } - - /* Allocate string descriptor numbers ... note that string - * contents can be overridden by the composite_dev glue. - */ - - /* device descriptor strings: manufacturer, product */ + /* allocate string descriptor numbers */ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", init_utsname()->sysname, init_utsname()->release, gadget->name); - status = usb_string_id(cdev); - if (status < 0) - goto fail2; - strings_dev[STRING_MANUFACTURER_IDX].id = status; - device_desc.iManufacturer = status; - status = usb_string_id(cdev); - if (status < 0) + status = usb_string_ids_tab(cdev, strings_dev); + if (unlikely(status < 0)) goto fail2; - strings_dev[STRING_PRODUCT_IDX].id = status; - device_desc.iProduct = status; -#ifdef USB_ETH_RNDIS - /* register our first configuration */ - status = usb_add_config(cdev, &rndis_config_driver); - if (status < 0) + device_desc.iManufacturer = + strings_dev[MULTI_STRING_MANUFACTURER_IDX].id; + device_desc.iProduct = + strings_dev[MULTI_STRING_PRODUCT_IDX].id; + + /* register configurations */ + status = rndis_config_register(cdev); + if (unlikely(status < 0)) goto fail2; -#endif -#ifdef CONFIG_USB_G_MULTI_CDC - /* register our second configuration */ - status = usb_add_config(cdev, &cdc_config_driver); - if (status < 0) + status = cdc_config_register(cdev); + if (unlikely(status < 0)) goto fail2; -#endif - dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); - fsg_common_put(fsg_common); + /* we're done */ + dev_info(&gadget->dev, DRIVER_DESC "\n"); + fsg_common_put(&fsg_common); return 0; + + /* error recovery */ fail2: - fsg_common_put(fsg_common); + fsg_common_put(&fsg_common); fail1: gserial_cleanup(); fail0: @@ -339,18 +372,15 @@ static struct usb_composite_driver multi_driver = { .unbind = __exit_p(multi_unbind), }; -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Michal Nazarewicz"); -MODULE_LICENSE("GPL"); -static int __init g_multi_init(void) +static int __init multi_init(void) { return usb_composite_register(&multi_driver); } -module_init(g_multi_init); +module_init(multi_init); -static void __exit g_multi_cleanup(void) +static void __exit multi_exit(void) { usb_composite_unregister(&multi_driver); } -module_exit(g_multi_cleanup); +module_exit(multi_exit); -- cgit v1.2.3-70-g09d2 From 3f3e12d050052032a51f75e72e540322e2a7da2b Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 21 Jun 2010 13:57:08 +0200 Subject: USB: gadget: composite: added disconnect callback Added a disconnect() callback to composite devices which is called by composite glue when its disconnect callback is called by gadget. Signed-off-by: Michal Nazarewicz Signed-off-by: Kyungmin Park Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 2 ++ include/linux/usb/composite.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 125167e17ce..e483f80822d 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -956,6 +956,8 @@ static void composite_disconnect(struct usb_gadget *gadget) spin_lock_irqsave(&cdev->lock, flags); if (cdev->config) reset_config(cdev); + if (composite->disconnect) + composite->disconnect(cdev); spin_unlock_irqrestore(&cdev->lock, flags); } diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index f378075c839..890bc147219 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -276,6 +276,8 @@ struct usb_composite_driver { int (*bind)(struct usb_composite_dev *); int (*unbind)(struct usb_composite_dev *); + void (*disconnect)(struct usb_composite_dev *); + /* global suspend hooks */ void (*suspend)(struct usb_composite_dev *); void (*resume)(struct usb_composite_dev *); -- cgit v1.2.3-70-g09d2 From 8876f5e7d3b2a320777dd4f6f5301d474c97a06c Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 21 Jun 2010 13:57:09 +0200 Subject: USB: gadget: f_mass_storage: added eject callback Added pre_eject() and post_eject() callbacks which are called before and after removable logical unit is ejected. The first can prevent logical unit from being ejected. This commit also changes the way callbacks are passed to the function from gadget. A fsg_operations structure has been created which lists all callbacks -- this is passed to the fsg_config. This is important because it changes the way thread_exits() callback is passed. Signed-off-by: Michal Nazarewicz Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_mass_storage.c | 109 +++++++++++++++++++++++------------- drivers/usb/gadget/mass_storage.c | 5 +- 2 files changed, 74 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index fa86b946dcd..32cce029f65 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -316,6 +316,27 @@ static const char fsg_string_interface[] = "Mass Storage"; /*-------------------------------------------------------------------------*/ struct fsg_dev; +struct fsg_common; + +/* FSF callback functions */ +struct fsg_operations { + /* Callback function to call when thread exits. If no + * callback is set or it returns value lower then zero MSF + * will force eject all LUNs it operates on (including those + * marked as non-removable or with prevent_medium_removal flag + * set). */ + int (*thread_exits)(struct fsg_common *common); + + /* Called prior to ejection. Negative return means error, + * zero means to continue with ejection, positive means not to + * eject. */ + int (*pre_eject)(struct fsg_common *common, + struct fsg_lun *lun, int num); + /* Called after ejection. Negative return means error, zero + * or positive is just a success. */ + int (*post_eject)(struct fsg_common *common, + struct fsg_lun *lun, int num); +}; /* Data shared by all the FSG instances. */ @@ -368,8 +389,8 @@ struct fsg_common { struct completion thread_notifier; struct task_struct *thread_task; - /* Callback function to call when thread exits. */ - int (*thread_exits)(struct fsg_common *common); + /* Callback functions. */ + const struct fsg_operations *ops; /* Gadget's private data. */ void *private_data; @@ -393,12 +414,8 @@ struct fsg_config { const char *lun_name_format; const char *thread_name; - /* Callback function to call when thread exits. If no - * callback is set or it returns value lower then zero MSF - * will force eject all LUNs it operates on (including those - * marked as non-removable or with prevent_medium_removal flag - * set). */ - int (*thread_exits)(struct fsg_common *common); + /* Callback functions. */ + const struct fsg_operations *ops; /* Gadget's private data. */ void *private_data; @@ -434,6 +451,7 @@ static inline int __fsg_is_set(struct fsg_common *common, if (common->fsg) return 1; ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); + WARN_ON(1); return 0; } @@ -1392,43 +1410,55 @@ static int do_start_stop(struct fsg_common *common) } else if (!curlun->removable) { curlun->sense_data = SS_INVALID_COMMAND; return -EINVAL; - } - - loej = common->cmnd[4] & 0x02; - start = common->cmnd[4] & 0x01; - - /* eject code from file_storage.c:do_start_stop() */ - - if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */ - (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */ + } else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */ + (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - if (!start) { - /* Are we allowed to unload the media? */ - if (curlun->prevent_medium_removal) { - LDBG(curlun, "unload attempt prevented\n"); - curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; - return -EINVAL; - } - if (loej) { /* Simulate an unload/eject */ - up_read(&common->filesem); - down_write(&common->filesem); - fsg_lun_close(curlun); - up_write(&common->filesem); - down_read(&common->filesem); - } - } else { + loej = common->cmnd[4] & 0x02; + start = common->cmnd[4] & 0x01; - /* Our emulation doesn't support mounting; the medium is - * available for use as soon as it is loaded. */ + /* Our emulation doesn't support mounting; the medium is + * available for use as soon as it is loaded. */ + if (start) { if (!fsg_lun_is_open(curlun)) { curlun->sense_data = SS_MEDIUM_NOT_PRESENT; return -EINVAL; } + return 0; } - return 0; + + /* Are we allowed to unload the media? */ + if (curlun->prevent_medium_removal) { + LDBG(curlun, "unload attempt prevented\n"); + curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; + return -EINVAL; + } + + if (!loej) + return 0; + + /* Simulate an unload/eject */ + if (common->ops && common->ops->pre_eject) { + int r = common->ops->pre_eject(common, curlun, + curlun - common->luns); + if (unlikely(r < 0)) + return r; + else if (r) + return 0; + } + + up_read(&common->filesem); + down_write(&common->filesem); + fsg_lun_close(curlun); + up_write(&common->filesem); + down_read(&common->filesem); + + return common->ops && common->ops->post_eject + ? min(0, common->ops->post_eject(common, curlun, + curlun - common->luns)) + : 0; } @@ -2607,7 +2637,8 @@ static int fsg_main_thread(void *common_) common->thread_task = NULL; spin_unlock_irq(&common->lock); - if (!common->thread_exits || common->thread_exits(common) < 0) { + if (!common->ops || !common->ops->thread_exits + || common->ops->thread_exits(common) < 0) { struct fsg_lun *curlun = common->luns; unsigned i = common->nluns; @@ -2683,6 +2714,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, common->free_storage_on_release = 0; } + common->ops = cfg->ops; common->private_data = cfg->private_data; common->gadget = gadget; @@ -2804,7 +2836,6 @@ buffhds_first_it: /* Tell the thread to start working */ - common->thread_exits = cfg->thread_exits; common->thread_task = kthread_create(fsg_main_thread, common, OR(cfg->thread_name, "file-storage")); @@ -3100,8 +3131,8 @@ fsg_config_from_params(struct fsg_config *cfg, cfg->product_name = 0; cfg->release = 0xffff; - cfg->thread_exits = 0; - cfg->private_data = 0; + cfg->ops = NULL; + cfg->private_data = NULL; /* Finalise */ cfg->can_stall = params->stall; diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 2b11e207982..306098f2d92 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -143,6 +143,9 @@ static int msg_thread_exits(struct fsg_common *common) static int __init msg_do_config(struct usb_configuration *c) { + static const struct fsg_operations ops = { + .thread_exits = msg_thread_exits, + }; static struct fsg_common common; struct fsg_common *retp; @@ -155,7 +158,7 @@ static int __init msg_do_config(struct usb_configuration *c) } fsg_config_from_params(&config, &mod_data); - config.thread_exits = msg_thread_exits; + config.ops = &ops; retp = fsg_common_init(&common, c->cdev, &config); if (IS_ERR(retp)) -- cgit v1.2.3-70-g09d2 From 89ba85d4015b7fa738b35bcc228075c117a9a578 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 21 Jun 2010 13:57:04 +0200 Subject: USB: gadget: section mismatch warning fixed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In may gadgets bind and bind like functions were in a init section as they were only run during initialisation. However, being callback functions they were referenced from structures in “normal” sections. Changing the tag from “__init” to “__ref” fixes the warnings. Signed-off-by: Michal Nazarewicz Cc: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/audio.c | 4 ++-- drivers/usb/gadget/cdc2.c | 4 ++-- drivers/usb/gadget/ether.c | 6 +++--- drivers/usb/gadget/f_loopback.c | 4 ++-- drivers/usb/gadget/f_sourcesink.c | 2 +- drivers/usb/gadget/file_storage.c | 2 +- drivers/usb/gadget/gmidi.c | 2 +- drivers/usb/gadget/hid.c | 4 ++-- drivers/usb/gadget/mass_storage.c | 4 ++-- drivers/usb/gadget/printer.c | 2 +- drivers/usb/gadget/serial.c | 4 ++-- drivers/usb/gadget/webcam.c | 4 ++-- drivers/usb/gadget/zero.c | 2 +- 13 files changed, 22 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index a62af7b5909..b744ccd0f34 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -89,7 +89,7 @@ static const struct usb_descriptor_header *otg_desc[] = { /*-------------------------------------------------------------------------*/ -static int __init audio_do_config(struct usb_configuration *c) +static int __ref audio_do_config(struct usb_configuration *c) { /* FIXME alloc iConfiguration string, set it in c->strings */ @@ -113,7 +113,7 @@ static struct usb_configuration audio_config_driver = { /*-------------------------------------------------------------------------*/ -static int __init audio_bind(struct usb_composite_dev *cdev) +static int __ref audio_bind(struct usb_composite_dev *cdev) { int gcnum; int status; diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 928137d3dbd..1f5ba2fd4c1 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -129,7 +129,7 @@ static u8 hostaddr[ETH_ALEN]; /* * We _always_ have both CDC ECM and CDC ACM functions. */ -static int __init cdc_do_config(struct usb_configuration *c) +static int __ref cdc_do_config(struct usb_configuration *c) { int status; @@ -159,7 +159,7 @@ static struct usb_configuration cdc_config_driver = { /*-------------------------------------------------------------------------*/ -static int __init cdc_bind(struct usb_composite_dev *cdev) +static int __ref cdc_bind(struct usb_composite_dev *cdev) { int gcnum; struct usb_gadget *gadget = cdev->gadget; diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 400f80372d9..114fa024c22 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -237,7 +237,7 @@ static u8 hostaddr[ETH_ALEN]; * the first one present. That's to make Microsoft's drivers happy, * and to follow DOCSIS 1.0 (cable modem standard). */ -static int __init rndis_do_config(struct usb_configuration *c) +static int __ref rndis_do_config(struct usb_configuration *c) { /* FIXME alloc iConfiguration string, set it in c->strings */ @@ -270,7 +270,7 @@ MODULE_PARM_DESC(use_eem, "use CDC EEM mode"); /* * We _always_ have an ECM, CDC Subset, or EEM configuration. */ -static int __init eth_do_config(struct usb_configuration *c) +static int __ref eth_do_config(struct usb_configuration *c) { /* FIXME alloc iConfiguration string, set it in c->strings */ @@ -297,7 +297,7 @@ static struct usb_configuration eth_config_driver = { /*-------------------------------------------------------------------------*/ -static int __init eth_bind(struct usb_composite_dev *cdev) +static int __ref eth_bind(struct usb_composite_dev *cdev) { int gcnum; struct usb_gadget *gadget = cdev->gadget; diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index e91d1b16d9b..43225879c3c 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -324,7 +324,7 @@ static void loopback_disable(struct usb_function *f) /*-------------------------------------------------------------------------*/ -static int __init loopback_bind_config(struct usb_configuration *c) +static int __ref loopback_bind_config(struct usb_configuration *c) { struct f_loopback *loop; int status; @@ -346,7 +346,7 @@ static int __init loopback_bind_config(struct usb_configuration *c) return status; } -static struct usb_configuration loopback_driver = { +static struct usb_configuration loopback_driver = { .label = "loopback", .strings = loopback_strings, .bind = loopback_bind_config, diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index 6d3cc443d91..685d768f336 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -404,7 +404,7 @@ static void sourcesink_disable(struct usb_function *f) /*-------------------------------------------------------------------------*/ -static int __init sourcesink_bind_config(struct usb_configuration *c) +static int __ref sourcesink_bind_config(struct usb_configuration *c) { struct f_sourcesink *ss; int status; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 2b6d3649d02..d57c09f764d 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -3335,7 +3335,7 @@ fill_serial: } -static int __init fsg_bind(struct usb_gadget *gadget) +static int __ref fsg_bind(struct usb_gadget *gadget) { struct fsg_dev *fsg = the_fsg; int rc; diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index b7bf88019b0..1b413a5cc3f 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -1157,7 +1157,7 @@ fail: /* * Creates an output endpoint, and initializes output ports. */ -static int __init gmidi_bind(struct usb_gadget *gadget) +static int __ref gmidi_bind(struct usb_gadget *gadget) { struct gmidi_device *dev; struct usb_ep *in_ep, *out_ep; diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 775722686ed..735495bf841 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -127,7 +127,7 @@ static struct usb_gadget_strings *dev_strings[] = { /****************************** Configurations ******************************/ -static int __init do_config(struct usb_configuration *c) +static int __ref do_config(struct usb_configuration *c) { struct hidg_func_node *e; int func = 0, status = 0; @@ -156,7 +156,7 @@ static struct usb_configuration config_driver = { /****************************** Gadget Bind ******************************/ -static int __init hid_bind(struct usb_composite_dev *cdev) +static int __ref hid_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; struct list_head *tmp; diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 306098f2d92..585f2559484 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -141,7 +141,7 @@ static int msg_thread_exits(struct fsg_common *common) return 0; } -static int __init msg_do_config(struct usb_configuration *c) +static int __ref msg_do_config(struct usb_configuration *c) { static const struct fsg_operations ops = { .thread_exits = msg_thread_exits, @@ -182,7 +182,7 @@ static struct usb_configuration msg_config_driver = { /****************************** Gadget Bind ******************************/ -static int __init msg_bind(struct usb_composite_dev *cdev) +static int __ref msg_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; int status; diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 4c3ac5c4223..88f05996aa0 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -1346,7 +1346,7 @@ printer_unbind(struct usb_gadget *gadget) set_gadget_data(gadget, NULL); } -static int __init +static int __ref printer_bind(struct usb_gadget *gadget) { struct printer_dev *dev; diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index f46a60962da..b22eedbc7dc 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -137,7 +137,7 @@ MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); /*-------------------------------------------------------------------------*/ -static int __init serial_bind_config(struct usb_configuration *c) +static int __ref serial_bind_config(struct usb_configuration *c) { unsigned i; int status = 0; @@ -161,7 +161,7 @@ static struct usb_configuration serial_config_driver = { .bmAttributes = USB_CONFIG_ATT_SELFPOWER, }; -static int __init gs_bind(struct usb_composite_dev *cdev) +static int __ref gs_bind(struct usb_composite_dev *cdev) { int gcnum; struct usb_gadget *gadget = cdev->gadget; diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index 288d21155ab..de1deb7a3c6 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -308,7 +308,7 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = { * USB configuration */ -static int __init +static int __ref webcam_config_bind(struct usb_configuration *c) { return uvc_bind_config(c, uvc_control_cls, uvc_fs_streaming_cls, @@ -330,7 +330,7 @@ webcam_unbind(struct usb_composite_dev *cdev) return 0; } -static int __init +static int __ref webcam_bind(struct usb_composite_dev *cdev) { int ret; diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 807280d069f..cf353920bb1 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -264,7 +264,7 @@ static void zero_resume(struct usb_composite_dev *cdev) /*-------------------------------------------------------------------------*/ -static int __init zero_bind(struct usb_composite_dev *cdev) +static int __ref zero_bind(struct usb_composite_dev *cdev) { int gcnum; struct usb_gadget *gadget = cdev->gadget; -- cgit v1.2.3-70-g09d2 From 541c7d432f76771079e7c295d596ea47cc6a3030 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 22 Jun 2010 16:39:10 -0400 Subject: USB: convert usb_hcd bitfields into atomic flags This patch (as1393) converts several of the single-bit fields in struct usb_hcd to atomic flags. This is for safety's sake; not all CPUs can update bitfield values atomically, and these flags are used in multiple contexts. The flag fields that are set only during registration or removal can remain as they are, since non-atomic accesses at those times will not cause any problems. (Strictly speaking, the authorized_default flag should become atomic as well. I didn't bother with it because it gets changed only via sysfs. It can be done later, if anyone wants.) Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/vhci_hcd.c | 6 +++--- drivers/usb/c67x00/c67x00-hcd.c | 4 ++-- drivers/usb/core/hcd.c | 26 ++++++++++++-------------- drivers/usb/gadget/dummy_hcd.c | 6 +++--- drivers/usb/host/ehci-dbg.c | 2 +- drivers/usb/host/ehci-hcd.c | 1 - drivers/usb/host/ehci-hub.c | 2 +- drivers/usb/host/ehci-q.c | 3 +-- drivers/usb/host/ehci-sched.c | 9 +++------ drivers/usb/host/hwa-hc.c | 4 ++-- drivers/usb/host/isp1760-hcd.c | 3 +-- drivers/usb/host/ohci-dbg.c | 4 ++-- drivers/usb/host/ohci-hcd.c | 6 +++--- drivers/usb/host/ohci-hub.c | 16 ++++++++++------ drivers/usb/host/oxu210hp-hcd.c | 7 ++----- drivers/usb/host/uhci-hcd.c | 21 ++++++++++++--------- drivers/usb/host/uhci-hub.c | 4 ++-- drivers/usb/host/whci/hcd.c | 2 +- drivers/usb/host/xhci.c | 3 +-- drivers/usb/musb/musb_virthub.c | 2 +- include/linux/usb/hcd.h | 22 +++++++++++++++++----- 21 files changed, 80 insertions(+), 73 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c index be5d8db9816..0574d848b90 100644 --- a/drivers/staging/usbip/vhci_hcd.c +++ b/drivers/staging/usbip/vhci_hcd.c @@ -215,7 +215,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf) vhci = hcd_to_vhci(hcd); spin_lock_irqsave(&vhci->lock, flags); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { usbip_dbg_vhci_rh("hw accessible flag in on?\n"); goto done; } @@ -269,7 +269,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u32 prev_port_status[VHCI_NPORTS]; - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) return -ETIMEDOUT; /* @@ -1041,7 +1041,7 @@ static int vhci_bus_resume(struct usb_hcd *hcd) dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); spin_lock_irq(&vhci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { rc = -ESHUTDOWN; } else { /* vhci->rh_state = DUMMY_RH_RUNNING; diff --git a/drivers/usb/c67x00/c67x00-hcd.c b/drivers/usb/c67x00/c67x00-hcd.c index a22b887f4e9..d3e1356d091 100644 --- a/drivers/usb/c67x00/c67x00-hcd.c +++ b/drivers/usb/c67x00/c67x00-hcd.c @@ -264,7 +264,7 @@ static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 int_status, u16 msg) if (unlikely(hcd->state == HC_STATE_HALT)) return; - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) return; /* Handle Start of frame events */ @@ -282,7 +282,7 @@ static int c67x00_hcd_start(struct usb_hcd *hcd) { hcd->uses_new_polling = 1; hcd->state = HC_STATE_RUNNING; - hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); return 0; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 53f14c82ff2..f2fe7c8e991 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -679,7 +679,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) spin_lock_irqsave(&hcd_root_hub_lock, flags); urb = hcd->status_urb; if (urb) { - hcd->poll_pending = 0; + clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags); hcd->status_urb = NULL; urb->actual_length = length; memcpy(urb->transfer_buffer, buffer, length); @@ -690,7 +690,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) spin_lock(&hcd_root_hub_lock); } else { length = 0; - hcd->poll_pending = 1; + set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags); } spin_unlock_irqrestore(&hcd_root_hub_lock, flags); } @@ -699,7 +699,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) * exceed that limit if HZ is 100. The math is more clunky than * maybe expected, this is to make sure that all timers for USB devices * fire at the same time to give the CPU a break inbetween */ - if (hcd->uses_new_polling ? hcd->poll_rh : + if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) : (length == 0 && hcd->status_urb != NULL)) mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4)); } @@ -736,7 +736,7 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4)); /* If a status change has already occurred, report it ASAP */ - else if (hcd->poll_pending) + else if (HCD_POLL_PENDING(hcd)) mod_timer(&hcd->rh_timer, jiffies); retval = 0; done: @@ -1150,8 +1150,7 @@ int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb, * finish unlinking the initial failed usb_set_address() * or device descriptor fetch. */ - if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) && - !is_root_hub(urb->dev)) { + if (!HCD_SAW_IRQ(hcd) && !is_root_hub(urb->dev)) { dev_warn(hcd->self.controller, "Unlink after no-IRQ? " "Controller is probably using the wrong IRQ.\n"); set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); @@ -2063,8 +2062,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) */ local_irq_save(flags); - if (unlikely(hcd->state == HC_STATE_HALT || - !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { + if (unlikely(hcd->state == HC_STATE_HALT || !HCD_HW_ACCESSIBLE(hcd))) { rc = IRQ_NONE; } else if (hcd->driver->irq(hcd) == IRQ_NONE) { rc = IRQ_NONE; @@ -2098,7 +2096,7 @@ void usb_hc_died (struct usb_hcd *hcd) spin_lock_irqsave (&hcd_root_hub_lock, flags); if (hcd->rh_registered) { - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); /* make khubd clean up old urbs and devices */ usb_set_device_state (hcd->self.root_hub, @@ -2301,7 +2299,7 @@ int usb_add_hcd(struct usb_hcd *hcd, retval); goto error_create_attr_group; } - if (hcd->uses_new_polling && hcd->poll_rh) + if (hcd->uses_new_polling && HCD_POLL_RH(hcd)) usb_hcd_poll_rh_status(hcd); return retval; @@ -2320,11 +2318,11 @@ error_create_attr_group: mutex_unlock(&usb_bus_list_lock); err_register_root_hub: hcd->rh_pollable = 0; - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); err_hcd_driver_start: if (hcd->irq >= 0) @@ -2380,14 +2378,14 @@ void usb_remove_hcd(struct usb_hcd *hcd) * the hub_status_data() callback. */ hcd->rh_pollable = 0; - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; /* In case the HCD restarted the timer, stop it again. */ - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); if (hcd->irq >= 0) diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 4f9e578cde9..dc6546248ed 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -1542,7 +1542,7 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf) dum = hcd_to_dummy (hcd); spin_lock_irqsave (&dum->lock, flags); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) goto done; if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) { @@ -1588,7 +1588,7 @@ static int dummy_hub_control ( int retval = 0; unsigned long flags; - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) return -ETIMEDOUT; dum = hcd_to_dummy (hcd); @@ -1739,7 +1739,7 @@ static int dummy_bus_resume (struct usb_hcd *hcd) dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__); spin_lock_irq (&dum->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { rc = -ESHUTDOWN; } else { dum->rh_state = DUMMY_RH_RUNNING; diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index df5546bb836..4498efb49b9 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -712,7 +712,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) spin_lock_irqsave (&ehci->lock, flags); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { size = scnprintf (next, size, "bus %s, device %s\n" "%s\n" diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8697ad19f31..2a19336c982 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -642,7 +642,6 @@ static int ehci_run (struct usb_hcd *hcd) u32 hcc_params; hcd->uses_new_polling = 1; - hcd->poll_rh = 0; /* EHCI spec section 4.1 */ if ((retval = ehci_reset(ehci)) != 0) { diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 84e792d71c2..0931f5a7dec 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -316,7 +316,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep(5); spin_lock_irq (&ehci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { spin_unlock_irq(&ehci->lock); return -ESHUTDOWN; } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 11a79c4f4a9..233c288e3f9 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1126,8 +1126,7 @@ submit_async ( #endif spin_lock_irqsave (&ehci->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &ehci_to_hcd(ehci)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) { rc = -ESHUTDOWN; goto done; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 805ec633a65..d640346f9b5 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -880,8 +880,7 @@ static int intr_submit ( spin_lock_irqsave (&ehci->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &ehci_to_hcd(ehci)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) { status = -ESHUTDOWN; goto done_not_linked; } @@ -1815,8 +1814,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, /* schedule ... need to lock */ spin_lock_irqsave (&ehci->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &ehci_to_hcd(ehci)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) { status = -ESHUTDOWN; goto done_not_linked; } @@ -2201,8 +2199,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, /* schedule ... need to lock */ spin_lock_irqsave (&ehci->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &ehci_to_hcd(ehci)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) { status = -ESHUTDOWN; goto done_not_linked; } diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index 35742f8c7cd..9bfac657572 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -159,7 +159,7 @@ static int hwahc_op_start(struct usb_hcd *usb_hcd) goto error_set_cluster_id; usb_hcd->uses_new_polling = 1; - usb_hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags); usb_hcd->state = HC_STATE_RUNNING; result = 0; out: @@ -776,7 +776,7 @@ static int hwahc_probe(struct usb_interface *usb_iface, goto error_alloc; } usb_hcd->wireless = 1; - usb_hcd->flags |= HCD_FLAG_SAW_IRQ; + set_bit(HCD_FLAG_SAW_IRQ, &usb_hcd->flags); wusbhc = usb_hcd_to_wusbhc(usb_hcd); hwahc = container_of(wusbhc, struct hwahc, wusbhc); hwahc_init(hwahc); diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index dbcafa29c77..d1a3dfc9a40 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -482,7 +482,6 @@ static int isp1760_run(struct usb_hcd *hcd) u32 chipid; hcd->uses_new_polling = 1; - hcd->poll_rh = 0; hcd->state = HC_STATE_RUNNING; isp1760_enable_interrupts(hcd); @@ -1450,7 +1449,7 @@ static int isp1760_prepare_enqueue(struct isp1760_hcd *priv, struct urb *urb, epnum = urb->ep->desc.bEndpointAddress; spin_lock_irqsave(&priv->lock, flags); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &priv_to_hcd(priv)->flags)) { + if (!HCD_HW_ACCESSIBLE(priv_to_hcd(priv))) { rc = -ESHUTDOWN; goto done; } diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 8ad2441b028..36abd2baa3e 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -645,7 +645,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) hcd->product_desc, hcd_name); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { size -= scnprintf (next, size, "SUSPENDED (no register access)\n"); goto done; @@ -687,7 +687,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) next += temp; temp = scnprintf (next, size, "hub poll timer %s\n", - ohci_to_hcd(ohci)->poll_rh ? "ON" : "off"); + HCD_POLL_RH(ohci_to_hcd(ohci)) ? "ON" : "off"); size -= temp; next += temp; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 02864a237a2..c3b4ccc7337 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -212,7 +212,7 @@ static int ohci_urb_enqueue ( spin_lock_irqsave (&ohci->lock, flags); /* don't submit to a dead HC */ - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { retval = -ENODEV; goto fail; } @@ -685,7 +685,7 @@ retry: } /* use rhsc irqs after khubd is fully initialized */ - hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); hcd->uses_new_polling = 1; /* start controller operations */ @@ -822,7 +822,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) else if (ints & OHCI_INTR_RD) { ohci_vdbg(ohci, "resume detect\n"); ohci_writel(ohci, OHCI_INTR_RD, ®s->intrstatus); - hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); if (ohci->autostop) { spin_lock (&ohci->lock); ohci_rh_resume (ohci); diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 65cac8cc892..4dd39022c38 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -284,7 +284,7 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) spin_lock_irq (&ohci->lock); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) + if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) rc = -ESHUTDOWN; else rc = ohci_rh_suspend (ohci, 0); @@ -302,7 +302,7 @@ static int ohci_bus_resume (struct usb_hcd *hcd) spin_lock_irq (&ohci->lock); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) + if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) rc = -ESHUTDOWN; else rc = ohci_rh_resume (ohci); @@ -489,7 +489,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) unsigned long flags; spin_lock_irqsave (&ohci->lock, flags); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) goto done; /* undocumented erratum seen on at least rev D */ @@ -533,8 +533,12 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) } } - hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed, - any_connected, rhsc_status); + if (ohci_root_hub_state_changes(ohci, changed, + any_connected, rhsc_status)) + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); + else + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + done: spin_unlock_irqrestore (&ohci->lock, flags); @@ -701,7 +705,7 @@ static int ohci_hub_control ( u32 temp; int retval = 0; - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) + if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) return -ESHUTDOWN; switch (typeReq) { diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index f608dfd09a8..d9c85a29273 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -1641,8 +1641,7 @@ static int submit_async(struct oxu_hcd *oxu, struct urb *urb, #endif spin_lock_irqsave(&oxu->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &oxu_to_hcd(oxu)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) { rc = -ESHUTDOWN; goto done; } @@ -2209,8 +2208,7 @@ static int intr_submit(struct oxu_hcd *oxu, struct urb *urb, spin_lock_irqsave(&oxu->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &oxu_to_hcd(oxu)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) { status = -ESHUTDOWN; goto done; } @@ -2715,7 +2713,6 @@ static int oxu_run(struct usb_hcd *hcd) u32 temp, hcc_params; hcd->uses_new_polling = 1; - hcd->poll_rh = 0; /* EHCI spec section 4.1 */ retval = ehci_reset(oxu); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index d1dce2166ef..2743ec770f0 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -140,7 +140,7 @@ static void finish_reset(struct uhci_hcd *uhci) uhci->rh_state = UHCI_RH_RESET; uhci->is_stopped = UHCI_IS_STOPPED; uhci_to_hcd(uhci)->state = HC_STATE_HALT; - uhci_to_hcd(uhci)->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); uhci->dead = 0; /* Full reset resurrects the controller */ } @@ -344,7 +344,10 @@ __acquires(uhci->lock) /* If interrupts don't work and remote wakeup is enabled then * the suspended root hub needs to be polled. */ - uhci_to_hcd(uhci)->poll_rh = (!int_enable && wakeup_enable); + if (!int_enable && wakeup_enable) + set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); + else + clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); uhci_scan_schedule(uhci); uhci_fsbr_off(uhci); @@ -363,7 +366,7 @@ static void start_rh(struct uhci_hcd *uhci) uhci->io_addr + USBINTR); mb(); uhci->rh_state = UHCI_RH_RUNNING; - uhci_to_hcd(uhci)->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); } static void wakeup_rh(struct uhci_hcd *uhci) @@ -733,7 +736,7 @@ static void uhci_stop(struct usb_hcd *hcd) struct uhci_hcd *uhci = hcd_to_uhci(hcd); spin_lock_irq(&uhci->lock); - if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && !uhci->dead) + if (HCD_HW_ACCESSIBLE(hcd) && !uhci->dead) uhci_hc_died(uhci); uhci_scan_schedule(uhci); spin_unlock_irq(&uhci->lock); @@ -750,7 +753,7 @@ static int uhci_rh_suspend(struct usb_hcd *hcd) int rc = 0; spin_lock_irq(&uhci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) rc = -ESHUTDOWN; else if (uhci->dead) ; /* Dead controllers tell no tales */ @@ -777,7 +780,7 @@ static int uhci_rh_resume(struct usb_hcd *hcd) int rc = 0; spin_lock_irq(&uhci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) rc = -ESHUTDOWN; else if (!uhci->dead) wakeup_rh(uhci); @@ -793,7 +796,7 @@ static int uhci_pci_suspend(struct usb_hcd *hcd) dev_dbg(uhci_dev(uhci), "%s\n", __func__); spin_lock_irq(&uhci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead) + if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) goto done_okay; /* Already suspended or dead */ if (uhci->rh_state > UHCI_RH_SUSPENDED) { @@ -807,7 +810,7 @@ static int uhci_pci_suspend(struct usb_hcd *hcd) */ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0); mb(); - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); /* FIXME: Enable non-PME# remote wakeup? */ @@ -860,7 +863,7 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) * the suspended root hub needs to be polled. */ if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) { - hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); usb_hcd_poll_rh_status(hcd); } return 0; diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 8270055848c..f0c58116c0a 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -190,7 +190,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) spin_lock_irqsave(&uhci->lock, flags); uhci_scan_schedule(uhci); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead) + if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) goto done; uhci_check_ports(uhci); @@ -246,7 +246,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wPortChange, wPortStatus; unsigned long flags; - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead) + if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) return -ETIMEDOUT; spin_lock_irqsave(&uhci->lock, flags); diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c index e0d3401285c..72b6892fda6 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/usb/host/whci/hcd.c @@ -68,7 +68,7 @@ static int whc_start(struct usb_hcd *usb_hcd) whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN); usb_hcd->uses_new_polling = 1; - usb_hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags); usb_hcd->state = HC_STATE_RUNNING; out: diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 343f1047f5d..5e73386b389 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -427,7 +427,6 @@ int xhci_run(struct usb_hcd *hcd) void (*doorbell)(struct xhci_hcd *) = NULL; hcd->uses_new_polling = 1; - hcd->poll_rh = 0; xhci_dbg(xhci, "xhci_run\n"); #if 0 /* FIXME: MSI not setup yet */ @@ -733,7 +732,7 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) ret = -EINVAL; goto exit; } - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { if (!in_interrupt()) xhci_dbg(xhci, "urb submitted during PCI suspend\n"); ret = -ESHUTDOWN; diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 92e85e027cf..43233c397b6 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -244,7 +244,7 @@ int musb_hub_control( spin_lock_irqsave(&musb->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) { spin_unlock_irqrestore(&musb->lock, flags); return -ESHUTDOWN; } diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 9b867e64a0f..f8f8fa7a56e 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -89,19 +89,31 @@ struct usb_hcd { */ const struct hc_driver *driver; /* hw-specific hooks */ - /* Flags that need to be manipulated atomically */ + /* Flags that need to be manipulated atomically because they can + * change while the host controller is running. Always use + * set_bit() or clear_bit() to change their values. + */ unsigned long flags; -#define HCD_FLAG_HW_ACCESSIBLE 0x00000001 -#define HCD_FLAG_SAW_IRQ 0x00000002 +#define HCD_FLAG_HW_ACCESSIBLE 0 /* at full power */ +#define HCD_FLAG_SAW_IRQ 1 +#define HCD_FLAG_POLL_RH 2 /* poll for rh status? */ +#define HCD_FLAG_POLL_PENDING 3 /* status has changed? */ + + /* The flags can be tested using these macros; they are likely to + * be slightly faster than test_bit(). + */ +#define HCD_HW_ACCESSIBLE(hcd) ((hcd)->flags & (1U << HCD_FLAG_HW_ACCESSIBLE)) +#define HCD_SAW_IRQ(hcd) ((hcd)->flags & (1U << HCD_FLAG_SAW_IRQ)) +#define HCD_POLL_RH(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_RH)) +#define HCD_POLL_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING)) + /* Flags that get set only during HCD registration or removal. */ unsigned rh_registered:1;/* is root hub registered? */ unsigned rh_pollable:1; /* may we poll the root hub? */ /* The next flag is a stopgap, to be removed when all the HCDs * support the new root-hub polling mechanism. */ unsigned uses_new_polling:1; - unsigned poll_rh:1; /* poll for rh status? */ - unsigned poll_pending:1; /* status has changed? */ unsigned wireless:1; /* Wireless USB HCD */ unsigned authorized_default:1; unsigned has_tt:1; /* Integrated TT in root hub */ -- cgit v1.2.3-70-g09d2 From f8dae531ec78ed34f8a845e5f4ff75f0adeb6b13 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 25 Jun 2010 16:29:27 +0200 Subject: USB: gadget: g_fs: code cleanup This commit cleans the g_fs gadget hopefully making it more readable. This is achieved by usage of the usb_string_ids_tab() function for batch string IDs registration as well as generalising configuration so that a single routine is used to add each configuration and bind interfaces. As an effect, the code is shorter and has fewer #ifdefs. Moreover, in some circumstances previous code #defined CONFIG_USB_FUNCTIONFS_GENERIC macro to prevent a situation where gadget with no configurations is built. This code removes the #define form source code and achieves the same effect using select in Kconfig. This patch also changes wording and names of the Kconfig options. Signed-off-by: Michal Nazarewicz Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 23 +++--- drivers/usb/gadget/g_ffs.c | 174 +++++++++++++-------------------------------- 2 files changed, 60 insertions(+), 137 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 027f61b1f3d..dd3b2510185 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -714,6 +714,7 @@ config USB_GADGETFS config USB_FUNCTIONFS tristate "Function Filesystem (EXPERIMENTAL)" depends on EXPERIMENTAL + select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS) help The Function Filesystem (FunctioFS) lets one create USB composite functions in user space in the same way as GadgetFS @@ -722,31 +723,31 @@ config USB_FUNCTIONFS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. + If you say "y" or "m" here you will be able what kind of + configurations the gadget will provide. + Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_ffs". config USB_FUNCTIONFS_ETH - bool "Include CDC ECM (Ethernet) function" + bool "Include configuration with CDC ECM (Ethernet)" depends on USB_FUNCTIONFS && NET help - Include an CDC ECM (Ethernet) funcion in the CDC ECM (Funcion) - Filesystem. If you also say "y" to the RNDIS query below the - gadget will have two configurations. + Include a configuration with CDC ECM funcion (Ethernet) and the + Funcion Filesystem. config USB_FUNCTIONFS_RNDIS - bool "Include RNDIS (Ethernet) function" + bool "Include configuration with RNDIS (Ethernet)" depends on USB_FUNCTIONFS && NET help - Include an RNDIS (Ethernet) funcion in the Funcion Filesystem. - If you also say "y" to the CDC ECM query above the gadget will - have two configurations. + Include a configuration with RNDIS funcion (Ethernet) and the Filesystem. config USB_FUNCTIONFS_GENERIC bool "Include 'pure' configuration" - depends on USB_FUNCTIONFS && (USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS) + depends on USB_FUNCTIONFS help - Include a configuration with FunctionFS and no Ethernet - configuration. + Include a configuration with the Function Filesystem alone with + no Ethernet interface. config USB_FILE_STORAGE tristate "File-backed Storage Gadget" diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index da3a9e40349..a9474f8d532 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -32,12 +32,13 @@ # include "u_ether.c" static u8 gfs_hostaddr[ETH_ALEN]; -#else -# if !defined CONFIG_USB_FUNCTIONFS_GENERIC -# define CONFIG_USB_FUNCTIONFS_GENERIC +# ifdef CONFIG_USB_FUNCTIONFS_ETH +static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); # endif +#else # define gether_cleanup() do { } while (0) # define gether_setup(gadget, hostaddr) ((int)0) +# define gfs_hostaddr NULL #endif #include "f_fs.c" @@ -107,15 +108,7 @@ static const struct usb_descriptor_header *gfs_otg_desc[] = { enum { GFS_STRING_MANUFACTURER_IDX, GFS_STRING_PRODUCT_IDX, -#ifdef CONFIG_USB_FUNCTIONFS_RNDIS - GFS_STRING_RNDIS_CONFIG_IDX, -#endif -#ifdef CONFIG_USB_FUNCTIONFS_ETH - GFS_STRING_ECM_CONFIG_IDX, -#endif -#ifdef CONFIG_USB_FUNCTIONFS_GENERIC - GFS_STRING_GENERIC_CONFIG_IDX, -#endif + GFS_STRING_FIRST_CONFIG_IDX, }; static char gfs_manufacturer[50]; @@ -126,13 +119,13 @@ static struct usb_string gfs_strings[] = { [GFS_STRING_MANUFACTURER_IDX].s = gfs_manufacturer, [GFS_STRING_PRODUCT_IDX].s = gfs_driver_desc, #ifdef CONFIG_USB_FUNCTIONFS_RNDIS - [GFS_STRING_RNDIS_CONFIG_IDX].s = "FunctionFS + RNDIS", + { .s = "FunctionFS + RNDIS" }, #endif #ifdef CONFIG_USB_FUNCTIONFS_ETH - [GFS_STRING_ECM_CONFIG_IDX].s = "FunctionFS + ECM", + { .s = "FunctionFS + ECM" }, #endif #ifdef CONFIG_USB_FUNCTIONFS_GENERIC - [GFS_STRING_GENERIC_CONFIG_IDX].s = "FunctionFS", + { .s = "FunctionFS" }, #endif { } /* end of list */ }; @@ -146,59 +139,33 @@ static struct usb_gadget_strings *gfs_dev_strings[] = { }; + +struct gfs_configuration { + struct usb_configuration c; + int (*eth)(struct usb_configuration *c, u8 *ethaddr); +} gfs_configurations[] = { #ifdef CONFIG_USB_FUNCTIONFS_RNDIS -static int gfs_do_rndis_config(struct usb_configuration *c); - -static struct usb_configuration gfs_rndis_config_driver = { - .label = "FunctionFS + RNDIS", - .bind = gfs_do_rndis_config, - .bConfigurationValue = 1, - /* .iConfiguration = DYNAMIC */ - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, -}; -# define gfs_add_rndis_config(cdev) \ - usb_add_config(cdev, &gfs_rndis_config_driver) -#else -# define gfs_add_rndis_config(cdev) 0 + { + .eth = rndis_bind_config, + }, #endif - #ifdef CONFIG_USB_FUNCTIONFS_ETH -static int gfs_do_ecm_config(struct usb_configuration *c); - -static struct usb_configuration gfs_ecm_config_driver = { - .label = "FunctionFS + ECM", - .bind = gfs_do_ecm_config, - .bConfigurationValue = 1, - /* .iConfiguration = DYNAMIC */ - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, -}; -# define gfs_add_ecm_config(cdev) \ - usb_add_config(cdev, &gfs_ecm_config_driver) -#else -# define gfs_add_ecm_config(cdev) 0 + { + .eth = eth_bind_config, + }, #endif - #ifdef CONFIG_USB_FUNCTIONFS_GENERIC -static int gfs_do_generic_config(struct usb_configuration *c); - -static struct usb_configuration gfs_generic_config_driver = { - .label = "FunctionFS", - .bind = gfs_do_generic_config, - .bConfigurationValue = 2, - /* .iConfiguration = DYNAMIC */ - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, -}; -# define gfs_add_generic_config(cdev) \ - usb_add_config(cdev, &gfs_generic_config_driver) -#else -# define gfs_add_generic_config(cdev) 0 + { + }, #endif +}; static int gfs_bind(struct usb_composite_dev *cdev); static int gfs_unbind(struct usb_composite_dev *cdev); +static int gfs_do_config(struct usb_configuration *c); static struct usb_composite_driver gfs_driver = { .name = gfs_short_name, @@ -267,7 +234,7 @@ static int functionfs_check_dev_callback(const char *dev_name) static int gfs_bind(struct usb_composite_dev *cdev) { - int ret; + int ret, i; ENTER(); @@ -284,57 +251,32 @@ static int gfs_bind(struct usb_composite_dev *cdev) snprintf(gfs_manufacturer, sizeof gfs_manufacturer, "%s %s with %s", init_utsname()->sysname, init_utsname()->release, cdev->gadget->name); - ret = usb_string_id(cdev); - if (unlikely(ret < 0)) - goto error; - gfs_strings[GFS_STRING_MANUFACTURER_IDX].id = ret; - gfs_dev_desc.iManufacturer = ret; - - ret = usb_string_id(cdev); - if (unlikely(ret < 0)) - goto error; - gfs_strings[GFS_STRING_PRODUCT_IDX].id = ret; - gfs_dev_desc.iProduct = ret; - -#ifdef CONFIG_USB_FUNCTIONFS_RNDIS - ret = usb_string_id(cdev); - if (unlikely(ret < 0)) - goto error; - gfs_strings[GFS_STRING_RNDIS_CONFIG_IDX].id = ret; - gfs_rndis_config_driver.iConfiguration = ret; -#endif -#ifdef CONFIG_USB_FUNCTIONFS_ETH - ret = usb_string_id(cdev); + ret = usb_string_ids_tab(cdev, gfs_strings); if (unlikely(ret < 0)) goto error; - gfs_strings[GFS_STRING_ECM_CONFIG_IDX].id = ret; - gfs_ecm_config_driver.iConfiguration = ret; -#endif -#ifdef CONFIG_USB_FUNCTIONFS_GENERIC - ret = usb_string_id(cdev); - if (unlikely(ret < 0)) - goto error; - gfs_strings[GFS_STRING_GENERIC_CONFIG_IDX].id = ret; - gfs_generic_config_driver.iConfiguration = ret; -#endif + gfs_dev_desc.iManufacturer = gfs_strings[GFS_STRING_MANUFACTURER_IDX].id; + gfs_dev_desc.iProduct = gfs_strings[GFS_STRING_PRODUCT_IDX].id; ret = functionfs_bind(gfs_ffs_data, cdev); if (unlikely(ret < 0)) goto error; - ret = gfs_add_rndis_config(cdev); - if (unlikely(ret < 0)) - goto error_unbind; + for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) { + struct gfs_configuration *c = gfs_configurations + i; - ret = gfs_add_ecm_config(cdev); - if (unlikely(ret < 0)) - goto error_unbind; + ret = GFS_STRING_FIRST_CONFIG_IDX + i; + c->c.label = gfs_strings[ret].s; + c->c.iConfiguration = gfs_strings[ret].id; + c->c.bind = gfs_do_config; + c->c.bConfigurationValue = 1 + i; + c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER; - ret = gfs_add_generic_config(cdev); - if (unlikely(ret < 0)) - goto error_unbind; + ret = usb_add_config(cdev, &c->c); + if (unlikely(ret < 0)) + goto error_unbind; + } return 0; @@ -368,10 +310,10 @@ static int gfs_unbind(struct usb_composite_dev *cdev) } -static int __gfs_do_config(struct usb_configuration *c, - int (*eth)(struct usb_configuration *c, u8 *ethaddr), - u8 *ethaddr) +static int gfs_do_config(struct usb_configuration *c) { + struct gfs_configuration *gc = + container_of(c, struct gfs_configuration, c); int ret; if (WARN_ON(!gfs_ffs_data)) @@ -382,8 +324,8 @@ static int __gfs_do_config(struct usb_configuration *c, c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - if (eth) { - ret = eth(c, ethaddr); + if (gc->eth) { + ret = gc->eth(c, gfs_hostaddr); if (unlikely(ret < 0)) return ret; } @@ -406,32 +348,12 @@ static int __gfs_do_config(struct usb_configuration *c, return 0; } -#ifdef CONFIG_USB_FUNCTIONFS_RNDIS -static int gfs_do_rndis_config(struct usb_configuration *c) -{ - ENTER(); - - return __gfs_do_config(c, rndis_bind_config, gfs_hostaddr); -} -#endif #ifdef CONFIG_USB_FUNCTIONFS_ETH -static int gfs_do_ecm_config(struct usb_configuration *c) -{ - ENTER(); - - return __gfs_do_config(c, - can_support_ecm(c->cdev->gadget) - ? ecm_bind_config : geth_bind_config, - gfs_hostaddr); -} -#endif - -#ifdef CONFIG_USB_FUNCTIONFS_GENERIC -static int gfs_do_generic_config(struct usb_configuration *c) +static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) { - ENTER(); - - return __gfs_do_config(c, NULL, NULL); + return can_support_ecm(c->cdev->gadget) + ? ecm_bind_config(c, ethaddr) + : geth_bind_config(c, ethaddr); } #endif -- cgit v1.2.3-70-g09d2 From 2138a1f1835274b1d131a1aafa1655f60b2af122 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 25 Jun 2010 14:01:49 -0400 Subject: USB: refactor the powermac-specific ASIC clock code This patch (as1383) takes the powermac-specific code from the PCI HCD glue layer and encapsulates it in its own subroutine. Signed-off-by: Alan Stern Acked-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 7e2d5271b0c..7c3b0af6224 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -332,6 +332,27 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); #ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PPC_PMAC +static void powermac_set_asic(struct pci_dev *pci_dev, int enable) +{ + /* Enanble or disable ASIC clocks for USB */ + if (machine_is(powermac)) { + struct device_node *of_node; + + of_node = pci_device_to_OF_node(pci_dev); + if (of_node) + pmac_call_feature(PMAC_FTR_USB_ENABLE, + of_node, 0, enable); + } +} + +#else + +static inline void powermac_set_asic(struct pci_dev *pci_dev, int enable) +{} + +#endif /* CONFIG_PPC_PMAC */ + static int check_root_hub_suspended(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -416,16 +437,7 @@ static int hcd_pci_suspend_noirq(struct device *dev) return retval; } -#ifdef CONFIG_PPC_PMAC - /* Disable ASIC clocks for USB */ - if (machine_is(powermac)) { - struct device_node *of_node; - - of_node = pci_device_to_OF_node(pci_dev); - if (of_node) - pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); - } -#endif + powermac_set_asic(pci_dev, 0); return retval; } @@ -433,17 +445,7 @@ static int hcd_pci_resume_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); -#ifdef CONFIG_PPC_PMAC - /* Reenable ASIC clocks for USB */ - if (machine_is(powermac)) { - struct device_node *of_node; - - of_node = pci_device_to_OF_node(pci_dev); - if (of_node) - pmac_call_feature(PMAC_FTR_USB_ENABLE, - of_node, 0, 1); - } -#endif + powermac_set_asic(pci_dev, 1); /* Go back to D0 and disable remote wakeup */ pci_back_from_sleep(pci_dev); -- cgit v1.2.3-70-g09d2 From 057c58bfb1dc9bbb75b8ba3b6c6336cfca63b9d0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 25 Jun 2010 14:02:03 -0400 Subject: USB: move PCI HCD resume routine This patch (as1384) moves the resume_common() routine in hcd-pci.c a little higher in the source file to avoid forward references in an upcoming patch. It also replaces the "hibernated" argument with a more general "event" argument, which will be useful when the routine is called during a runtime resume. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 77 +++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 7c3b0af6224..f0156de8db6 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -403,6 +403,43 @@ static int hcd_pci_suspend(struct device *dev) return retval; } +static int resume_common(struct device *dev, int event) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct usb_hcd *hcd = pci_get_drvdata(pci_dev); + int retval; + + if (hcd->state != HC_STATE_SUSPENDED) { + dev_dbg(dev, "can't resume, not suspended!\n"); + return 0; + } + + retval = pci_enable_device(pci_dev); + if (retval < 0) { + dev_err(dev, "can't re-enable after resume, %d!\n", retval); + return retval; + } + + pci_set_master(pci_dev); + + clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + + if (hcd->driver->pci_resume) { + /* This call should be made only during system resume, + * not during runtime resume. + */ + wait_for_companions(pci_dev, hcd); + + retval = hcd->driver->pci_resume(hcd, + event == PM_EVENT_RESTORE); + if (retval) { + dev_err(dev, "PCI post-resume error %d!\n", retval); + usb_hc_died(hcd); + } + } + return retval; +} + static int hcd_pci_suspend_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -452,50 +489,14 @@ static int hcd_pci_resume_noirq(struct device *dev) return 0; } -static int resume_common(struct device *dev, bool hibernated) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - struct usb_hcd *hcd = pci_get_drvdata(pci_dev); - int retval; - - if (hcd->state != HC_STATE_SUSPENDED) { - dev_dbg(dev, "can't resume, not suspended!\n"); - return 0; - } - - retval = pci_enable_device(pci_dev); - if (retval < 0) { - dev_err(dev, "can't re-enable after resume, %d!\n", retval); - return retval; - } - - pci_set_master(pci_dev); - - clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); - - if (hcd->driver->pci_resume) { - /* This call should be made only during system resume, - * not during runtime resume. - */ - wait_for_companions(pci_dev, hcd); - - retval = hcd->driver->pci_resume(hcd, hibernated); - if (retval) { - dev_err(dev, "PCI post-resume error %d!\n", retval); - usb_hc_died(hcd); - } - } - return retval; -} - static int hcd_pci_resume(struct device *dev) { - return resume_common(dev, false); + return resume_common(dev, PM_EVENT_RESUME); } static int hcd_pci_restore(struct device *dev) { - return resume_common(dev, true); + return resume_common(dev, PM_EVENT_RESTORE); } const struct dev_pm_ops usb_hcd_pci_pm_ops = { -- cgit v1.2.3-70-g09d2 From 4147200d25c423e627ab4487530b3d9f2ef829c8 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 25 Jun 2010 14:02:14 -0400 Subject: USB: add do_wakeup parameter for PCI HCD suspend This patch (as1385) adds a "do_wakeup" parameter to the pci_suspend method used by PCI-based host controller drivers. ehci-hcd in particular needs to know whether or not to enable wakeup when suspending a controller. Although that information is currently available through device_may_wakeup(), when support is added for runtime suspend this will no longer be true. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 4 +++- drivers/usb/host/ehci-au1xxx.c | 2 +- drivers/usb/host/ehci-fsl.c | 3 ++- drivers/usb/host/ehci-hub.c | 5 ++--- drivers/usb/host/ehci-pci.c | 4 ++-- drivers/usb/host/ehci.h | 8 ++++---- drivers/usb/host/ohci-pci.c | 2 +- drivers/usb/host/uhci-hcd.c | 2 +- include/linux/usb/hcd.h | 2 +- 9 files changed, 17 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index f0156de8db6..e387e394f87 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -386,7 +386,9 @@ static int hcd_pci_suspend(struct device *dev) return retval; if (hcd->driver->pci_suspend) { - retval = hcd->driver->pci_suspend(hcd); + bool do_wakeup = device_may_wakeup(dev); + + retval = hcd->driver->pci_suspend(hcd, do_wakeup); suspend_report_result(hcd->driver->pci_suspend, retval); if (retval) return retval; diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index faa61748db7..2baf8a84908 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -228,7 +228,7 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) * the root hub is either suspended or stopped. */ spin_lock_irqsave(&ehci->lock, flags); - ehci_prepare_ports_for_controller_suspend(ehci); + ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); ehci_writel(ehci, 0, &ehci->regs->intr_enable); (void)ehci_readl(ehci, &ehci->regs->intr_enable); diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 5cd967d2893..a416421abfa 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -313,7 +313,8 @@ static int ehci_fsl_drv_suspend(struct device *dev) struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); void __iomem *non_ehci = hcd->regs; - ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd)); + ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), + device_may_wakeup(dev)); if (!fsl_deep_sleep()) return 0; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 0931f5a7dec..1292a5b2197 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -107,7 +107,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) } static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, - bool suspending) + bool suspending, bool do_wakeup) { int port; u32 temp; @@ -117,8 +117,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, * when the controller is suspended or resumed. In all other * cases they don't need to be changed. */ - if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || - device_may_wakeup(ehci_to_hcd(ehci)->self.controller)) + if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup) return; /* clear phy low-power mode before changing wakeup flags */ diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index a307d550bda..f555e4f35a0 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -277,7 +277,7 @@ done: * Also they depend on separate root hub suspend/resume. */ -static int ehci_pci_suspend(struct usb_hcd *hcd) +static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); unsigned long flags; @@ -291,7 +291,7 @@ static int ehci_pci_suspend(struct usb_hcd *hcd) * the root hub is either suspended or stopped. */ spin_lock_irqsave (&ehci->lock, flags); - ehci_prepare_ports_for_controller_suspend(ehci); + ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup); ehci_writel(ehci, 0, &ehci->regs->intr_enable); (void)ehci_readl(ehci, &ehci->regs->intr_enable); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index e6c57cc416f..a4a63ce290e 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -540,11 +540,11 @@ struct ehci_fstn { /* Prepare the PORTSC wakeup flags during controller suspend/resume */ -#define ehci_prepare_ports_for_controller_suspend(ehci) \ - ehci_adjust_port_wakeup_flags(ehci, true); +#define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup) \ + ehci_adjust_port_wakeup_flags(ehci, true, do_wakeup); -#define ehci_prepare_ports_for_controller_resume(ehci) \ - ehci_adjust_port_wakeup_flags(ehci, false); +#define ehci_prepare_ports_for_controller_resume(ehci) \ + ehci_adjust_port_wakeup_flags(ehci, false, false); /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index b8a1148f248..6bdc8b25a6a 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -392,7 +392,7 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd) #ifdef CONFIG_PM -static int ohci_pci_suspend(struct usb_hcd *hcd) +static int ohci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); unsigned long flags; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 2743ec770f0..a7850f51fdc 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -788,7 +788,7 @@ static int uhci_rh_resume(struct usb_hcd *hcd) return rc; } -static int uhci_pci_suspend(struct usb_hcd *hcd) +static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); int rc = 0; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index f8f8fa7a56e..ae10020b402 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -211,7 +211,7 @@ struct hc_driver { * a whole, not just the root hub; they're for PCI bus glue. */ /* called after suspending the hub, before entering D3 etc */ - int (*pci_suspend)(struct usb_hcd *hcd); + int (*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup); /* called after entering D0 (etc), before resuming the hub */ int (*pci_resume)(struct usb_hcd *hcd, bool hibernated); -- cgit v1.2.3-70-g09d2 From ee0b9be829803e3ff5adec7456bd59a08425ffa1 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 25 Jun 2010 14:02:24 -0400 Subject: USB: controller resume should check the root hub This patch (as1394) adds code to ehci-hcd, ohci-hcd, and uhci-hcd for automatically resuming the root hub when the controller is resumed, if the root hub has a wakeup request pending on some port. During resume from system sleep this doesn't matter, because the root hubs will naturally be resumed along with every other device in the system. However it _will_ matter for runtime PM: If the controller is suspended and a remote wakeup request is received then the controller will autoresume, but we need to ensure that the root hub also autoresumes. Otherwise the wakeup request would be ignored, the controller would go back to sleep, and the cycle would repeat a large number of times (I saw this happen before the patch was written). Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 4 ++++ drivers/usb/host/ohci-hub.c | 7 ++++++- drivers/usb/host/uhci-hcd.c | 7 ++++--- drivers/usb/host/uhci-hub.c | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 1292a5b2197..796ea0c8900 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -166,6 +166,10 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg); } } + + /* Does the root hub have a port wakeup pending? */ + if (!suspending && (ehci_readl(ehci, &ehci->regs->status) & STS_PCD)) + usb_hcd_resume_root_hub(ehci_to_hcd(ehci)); } static int ehci_bus_suspend (struct usb_hcd *hcd) diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 4dd39022c38..cddcda95b57 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -355,6 +355,11 @@ static void ohci_finish_controller_resume(struct usb_hcd *hcd) ohci_readl(ohci, &ohci->regs->intrenable); msleep(20); } + + /* Does the root hub have a port wakeup pending? */ + if (ohci_readl(ohci, &ohci->regs->intrstatus) & + (OHCI_INTR_RD | OHCI_INTR_RHSC)) + usb_hcd_resume_root_hub(hcd); } /* Carry out polling-, autostop-, and autoresume-related state changes */ @@ -364,7 +369,7 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, int poll_rh = 1; int rhsc_enable; - /* Some broken controllers never turn off RHCS in the interrupt + /* Some broken controllers never turn off RHSC in the interrupt * status register. For their sake we won't re-enable RHSC * interrupts if the interrupt bit is already active. */ diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index a7850f51fdc..9d4d81248f9 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -862,10 +862,11 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) /* If interrupts don't work and remote wakeup is enabled then * the suspended root hub needs to be polled. */ - if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) { + if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) set_bit(HCD_FLAG_POLL_RH, &hcd->flags); - usb_hcd_poll_rh_status(hcd); - } + + /* Does the root hub have a port wakeup pending? */ + usb_hcd_poll_rh_status(hcd); return 0; } #endif diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index f0c58116c0a..6d59c0f77f2 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -200,7 +200,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) case UHCI_RH_SUSPENDING: case UHCI_RH_SUSPENDED: /* if port change, ask to be resumed */ - if (status) + if (status || uhci->resuming_ports) usb_hcd_resume_root_hub(hcd); break; -- cgit v1.2.3-70-g09d2 From ff2f07874362d34684296f2bd5547a099f33c6d4 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 25 Jun 2010 14:02:35 -0400 Subject: USB: fix race between root-hub wakeup & controller suspend This patch (as1395) adds code to hcd_pci_suspend() for handling wakeup races. This is another general race pattern, similar to the "open vs. unregister" race we're all familiar with. Here, the race is between suspending a device and receiving a wakeup request from one of the device's suspended children. In particular, if a root-hub wakeup is requested at about the same time as the corresponding USB controller is suspended, and if the controller is enabled for wakeup, then the controller should either fail to suspend or else wake right back up again. During system sleep this won't happen very much, especially since host controllers generally aren't enabled for wakeup during sleep. However it is definitely an issue for runtime PM. Something like this will be needed to prevent the controller from autosuspending while waiting for a root-hub resume to take place. (That is, in fact, the common case, for which there is an extra test.) Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 12 ++++++++++++ drivers/usb/core/hcd.c | 5 ++++- include/linux/usb/hcd.h | 2 ++ 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index e387e394f87..352577baa53 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -388,8 +388,20 @@ static int hcd_pci_suspend(struct device *dev) if (hcd->driver->pci_suspend) { bool do_wakeup = device_may_wakeup(dev); + /* Optimization: Don't suspend if a root-hub wakeup is + * pending and it would cause the HCD to wake up anyway. + */ + if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) + return -EBUSY; retval = hcd->driver->pci_suspend(hcd, do_wakeup); suspend_report_result(hcd->driver->pci_suspend, retval); + + /* Check again in case wakeup raced with pci_suspend */ + if (retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) { + if (hcd->driver->pci_resume) + hcd->driver->pci_resume(hcd, false); + retval = -EBUSY; + } if (retval) return retval; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index f2fe7c8e991..0358c05e6e8 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1940,6 +1940,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "usb %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume"); + clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); if (!hcd->driver->bus_resume) return -ENOENT; if (hcd->state == HC_STATE_RUNNING) @@ -1993,8 +1994,10 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) unsigned long flags; spin_lock_irqsave (&hcd_root_hub_lock, flags); - if (hcd->rh_registered) + if (hcd->rh_registered) { + set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); queue_work(pm_wq, &hcd->wakeup_work); + } spin_unlock_irqrestore (&hcd_root_hub_lock, flags); } EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index ae10020b402..3b571f1ffbb 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -98,6 +98,7 @@ struct usb_hcd { #define HCD_FLAG_SAW_IRQ 1 #define HCD_FLAG_POLL_RH 2 /* poll for rh status? */ #define HCD_FLAG_POLL_PENDING 3 /* status has changed? */ +#define HCD_FLAG_WAKEUP_PENDING 4 /* root hub is resuming? */ /* The flags can be tested using these macros; they are likely to * be slightly faster than test_bit(). @@ -106,6 +107,7 @@ struct usb_hcd { #define HCD_SAW_IRQ(hcd) ((hcd)->flags & (1U << HCD_FLAG_SAW_IRQ)) #define HCD_POLL_RH(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_RH)) #define HCD_POLL_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING)) +#define HCD_WAKEUP_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING)) /* Flags that get set only during HCD registration or removal. */ unsigned rh_registered:1;/* is root hub registered? */ -- cgit v1.2.3-70-g09d2 From 0d436b425e07f9e4b0fe571cec061f5d136f1d8b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 25 Jun 2010 14:02:49 -0400 Subject: USB: UHCI: add support for Intel's wakeup flags This patch (as1396) adds code to uhci-hcd to support the vendor-specific wakeup settings found in Intel's ICHx hardware. A couple of unnecessary memory barriers are removed. And the root hub isn't put back into the "suspended" state if power was lost during a system sleep -- there's not much point in doing so because the root hub will be resumed shortly. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 30 +++++++++++++++++------------- drivers/usb/host/uhci-hcd.h | 7 ++++++- 2 files changed, 23 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 9d4d81248f9..b04506036b6 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -176,6 +176,8 @@ static void check_and_reset_hc(struct uhci_hcd *uhci) */ static void configure_hc(struct uhci_hcd *uhci) { + struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); + /* Set the frame length to the default: 1 ms exactly */ outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF); @@ -191,8 +193,11 @@ static void configure_hc(struct uhci_hcd *uhci) mb(); /* Enable PIRQ */ - pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, - USBLEGSUP_DEFAULT); + pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT); + + /* Disable platform-specific non-PME# wakeup */ + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + pci_write_config_byte(pdev, USBRES_INTEL, 0); } @@ -791,6 +796,7 @@ static int uhci_rh_resume(struct usb_hcd *hcd) static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); + struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); int rc = 0; dev_dbg(uhci_dev(uhci), "%s\n", __func__); @@ -808,11 +814,15 @@ static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) /* All PCI host controllers are required to disable IRQ generation * at the source, so we must turn off PIRQ. */ - pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0); - mb(); + pci_write_config_word(pdev, USBLEGSUP, 0); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - /* FIXME: Enable non-PME# remote wakeup? */ + /* Enable platform-specific non-PME# wakeup */ + if (do_wakeup) { + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + pci_write_config_byte(pdev, USBRES_INTEL, + USBPORT1EN | USBPORT2EN); + } done_okay: clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); @@ -831,7 +841,6 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) * even if the controller was dead. */ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - mb(); spin_lock_irq(&uhci->lock); @@ -839,8 +848,6 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) if (hibernated) uhci_hc_died(uhci); - /* FIXME: Disable non-PME# remote wakeup? */ - /* The firmware or a boot kernel may have changed the controller * settings during a system wakeup. Check it and reconfigure * to avoid problems. @@ -850,12 +857,9 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) /* If the controller was dead before, it's back alive now */ configure_hc(uhci); - if (uhci->rh_state == UHCI_RH_RESET) { - - /* The controller had to be reset */ + /* Tell the core if the controller had to be reset */ + if (uhci->rh_state == UHCI_RH_RESET) usb_root_hub_lost_power(hcd->self.root_hub); - suspend_rh(uhci, UHCI_RH_SUSPENDED); - } spin_unlock_irq(&uhci->lock); diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 26bd1b2bcbf..49bf2790f9c 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -67,12 +67,17 @@ #define USBPORTSC_RES3 0x4000 /* reserved, write zeroes */ #define USBPORTSC_RES4 0x8000 /* reserved, write zeroes */ -/* Legacy support register */ +/* PCI legacy support register */ #define USBLEGSUP 0xc0 #define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ #define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */ #define USBLEGSUP_RO 0x5040 /* R/O and reserved bits */ +/* PCI Intel-specific resume-enable register */ +#define USBRES_INTEL 0xc4 +#define USBPORT1EN 0x01 +#define USBPORT2EN 0x02 + #define UHCI_PTR_BITS cpu_to_le32(0x000F) #define UHCI_PTR_TERM cpu_to_le32(0x0001) #define UHCI_PTR_QH cpu_to_le32(0x0002) -- cgit v1.2.3-70-g09d2 From 3da7cff4e79e4a7137b0dac1aaf6841b91bbff63 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 25 Jun 2010 14:02:57 -0400 Subject: USB: add runtime PM for PCI-based host controllers This patch (as1386) adds runtime-PM support for PCI-based USB host controllers. By default autosuspend is disallowed; the user must enable it by writing "auto" to the controller's power/control sysfs attribute. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 76 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 352577baa53..fe6b8d40a50 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -250,6 +250,9 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) if (retval != 0) goto err4; set_hs_companion(dev, hcd); + + if (pci_dev_run_wake(dev)) + pm_runtime_put_noidle(&dev->dev); return retval; err4: @@ -292,6 +295,9 @@ void usb_hcd_pci_remove(struct pci_dev *dev) if (!hcd) return; + if (pci_dev_run_wake(dev)) + pm_runtime_get_noresume(&dev->dev); + /* Fake an interrupt request in order to give the driver a chance * to test whether the controller hardware has been removed (e.g., * cardbus physical eject). @@ -325,12 +331,13 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev) if (!hcd) return; - if (hcd->driver->shutdown) + if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && + hcd->driver->shutdown) hcd->driver->shutdown(hcd); } EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM_OPS #ifdef CONFIG_PPC_PMAC static void powermac_set_asic(struct pci_dev *pci_dev, int enable) @@ -366,7 +373,7 @@ static int check_root_hub_suspended(struct device *dev) return 0; } -static int hcd_pci_suspend(struct device *dev) +static int suspend_common(struct device *dev, bool do_wakeup) { struct pci_dev *pci_dev = to_pci_dev(dev); struct usb_hcd *hcd = pci_get_drvdata(pci_dev); @@ -381,13 +388,7 @@ static int hcd_pci_suspend(struct device *dev) if (retval) return retval; - /* We might already be suspended (runtime PM -- not yet written) */ - if (pci_dev->current_state != PCI_D0) - return retval; - if (hcd->driver->pci_suspend) { - bool do_wakeup = device_may_wakeup(dev); - /* Optimization: Don't suspend if a root-hub wakeup is * pending and it would cause the HCD to wake up anyway. */ @@ -439,10 +440,8 @@ static int resume_common(struct device *dev, int event) clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); if (hcd->driver->pci_resume) { - /* This call should be made only during system resume, - * not during runtime resume. - */ - wait_for_companions(pci_dev, hcd); + if (event != PM_EVENT_AUTO_RESUME) + wait_for_companions(pci_dev, hcd); retval = hcd->driver->pci_resume(hcd, event == PM_EVENT_RESTORE); @@ -454,6 +453,13 @@ static int resume_common(struct device *dev, int event) return retval; } +#ifdef CONFIG_PM_SLEEP + +static int hcd_pci_suspend(struct device *dev) +{ + return suspend_common(dev, device_may_wakeup(dev)); +} + static int hcd_pci_suspend_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -513,6 +519,46 @@ static int hcd_pci_restore(struct device *dev) return resume_common(dev, PM_EVENT_RESTORE); } +#else + +#define hcd_pci_suspend NULL +#define hcd_pci_suspend_noirq NULL +#define hcd_pci_resume_noirq NULL +#define hcd_pci_resume NULL +#define hcd_pci_restore NULL + +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM_RUNTIME + +static int hcd_pci_runtime_suspend(struct device *dev) +{ + int retval; + + retval = suspend_common(dev, true); + if (retval == 0) + powermac_set_asic(to_pci_dev(dev), 0); + dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval); + return retval; +} + +static int hcd_pci_runtime_resume(struct device *dev) +{ + int retval; + + powermac_set_asic(to_pci_dev(dev), 1); + retval = resume_common(dev, PM_EVENT_AUTO_RESUME); + dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval); + return retval; +} + +#else + +#define hcd_pci_runtime_suspend NULL +#define hcd_pci_runtime_resume NULL + +#endif /* CONFIG_PM_RUNTIME */ + const struct dev_pm_ops usb_hcd_pci_pm_ops = { .suspend = hcd_pci_suspend, .suspend_noirq = hcd_pci_suspend_noirq, @@ -526,7 +572,9 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = { .poweroff_noirq = hcd_pci_suspend_noirq, .restore_noirq = hcd_pci_resume_noirq, .restore = hcd_pci_restore, + .runtime_suspend = hcd_pci_runtime_suspend, + .runtime_resume = hcd_pci_runtime_resume, }; EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops); -#endif /* CONFIG_PM_SLEEP */ +#endif /* CONFIG_PM_OPS */ -- cgit v1.2.3-70-g09d2 From 916de0272018482c35402d410869353003051eb9 Mon Sep 17 00:00:00 2001 From: Nicolas Kaiser Date: Fri, 25 Jun 2010 20:25:37 +0200 Subject: USB: usblp: fixed switch, brace, whitespace and spacing coding style issues Fixed switch, brace, whitespace and spacing coding style issues. Signed-off-by: Nicolas Kaiser Acked-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usblp.c | 371 +++++++++++++++++++++++----------------------- 1 file changed, 185 insertions(+), 186 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 84f9e52327f..e325162859b 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -135,7 +135,7 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H * ->lock locks what interrupt accesses. */ struct usblp { - struct usb_device *dev; /* USB device */ + struct usb_device *dev; /* USB device */ struct mutex wmut; struct mutex mut; spinlock_t lock; /* locks rcomplete, wcomplete */ @@ -169,7 +169,8 @@ struct usblp { }; #ifdef DEBUG -static void usblp_dump(struct usblp *usblp) { +static void usblp_dump(struct usblp *usblp) +{ int p; dbg("usblp=0x%p", usblp); @@ -216,8 +217,8 @@ static const struct quirk_printer_struct quirk_printers[] = { { 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */ { 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */ { 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */ - { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */ - { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */ + { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */ + { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */ { 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */ { 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */ { 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */ @@ -254,9 +255,8 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i /* High byte has the interface index. Low byte has the alternate setting. */ - if ((request == USBLP_REQ_GET_ID) && (type == USB_TYPE_CLASS)) { - index = (usblp->ifnum<<8)|usblp->protocol[usblp->current_protocol].alt_setting; - } + if ((request == USBLP_REQ_GET_ID) && (type == USB_TYPE_CLASS)) + index = (usblp->ifnum<<8)|usblp->protocol[usblp->current_protocol].alt_setting; retval = usb_control_msg(usblp->dev, dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0), @@ -372,7 +372,7 @@ static int usblp_check_status(struct usblp *usblp, int err) return newerr; } -static int handle_bidir (struct usblp *usblp) +static int handle_bidir(struct usblp *usblp) { if (usblp->bidir && usblp->used) { if (usblp_submit_read(usblp) < 0) @@ -395,14 +395,13 @@ static int usblp_open(struct inode *inode, struct file *file) if (minor < 0) return -ENODEV; - mutex_lock (&usblp_mutex); + mutex_lock(&usblp_mutex); retval = -ENODEV; intf = usb_find_interface(&usblp_driver, minor); - if (!intf) { + if (!intf) goto out; - } - usblp = usb_get_intfdata (intf); + usblp = usb_get_intfdata(intf); if (!usblp || !usblp->dev || !usblp->present) goto out; @@ -433,18 +432,18 @@ static int usblp_open(struct inode *inode, struct file *file) retval = -EIO; } out: - mutex_unlock (&usblp_mutex); + mutex_unlock(&usblp_mutex); return retval; } -static void usblp_cleanup (struct usblp *usblp) +static void usblp_cleanup(struct usblp *usblp) { printk(KERN_INFO "usblp%d: removed\n", usblp->minor); kfree(usblp->readbuf); - kfree (usblp->device_id_string); - kfree (usblp->statusbuf); - kfree (usblp); + kfree(usblp->device_id_string); + kfree(usblp->statusbuf); + kfree(usblp); } static void usblp_unlink_urbs(struct usblp *usblp) @@ -458,14 +457,14 @@ static int usblp_release(struct inode *inode, struct file *file) usblp->flags &= ~LP_ABORT; - mutex_lock (&usblp_mutex); + mutex_lock(&usblp_mutex); usblp->used = 0; if (usblp->present) { usblp_unlink_urbs(usblp); usb_autopm_put_interface(usblp->intf); - } else /* finish cleanup from disconnect */ - usblp_cleanup (usblp); - mutex_unlock (&usblp_mutex); + } else /* finish cleanup from disconnect */ + usblp_cleanup(usblp); + mutex_unlock(&usblp_mutex); return 0; } @@ -495,190 +494,190 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) int twoints[2]; int retval = 0; - mutex_lock (&usblp->mut); + mutex_lock(&usblp->mut); if (!usblp->present) { retval = -ENODEV; goto done; } dbg("usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)", cmd, _IOC_TYPE(cmd), - _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd) ); + _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd)); if (_IOC_TYPE(cmd) == 'P') /* new-style ioctl number */ switch (_IOC_NR(cmd)) { - case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */ - if (_IOC_DIR(cmd) != _IOC_READ) { - retval = -EINVAL; - goto done; - } + case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */ + if (_IOC_DIR(cmd) != _IOC_READ) { + retval = -EINVAL; + goto done; + } - length = usblp_cache_device_id_string(usblp); - if (length < 0) { - retval = length; - goto done; - } - if (length > _IOC_SIZE(cmd)) - length = _IOC_SIZE(cmd); /* truncate */ - - if (copy_to_user((void __user *) arg, - usblp->device_id_string, - (unsigned long) length)) { - retval = -EFAULT; - goto done; - } + length = usblp_cache_device_id_string(usblp); + if (length < 0) { + retval = length; + goto done; + } + if (length > _IOC_SIZE(cmd)) + length = _IOC_SIZE(cmd); /* truncate */ + + if (copy_to_user((void __user *) arg, + usblp->device_id_string, + (unsigned long) length)) { + retval = -EFAULT; + goto done; + } - break; + break; - case IOCNR_GET_PROTOCOLS: - if (_IOC_DIR(cmd) != _IOC_READ || - _IOC_SIZE(cmd) < sizeof(twoints)) { - retval = -EINVAL; - goto done; - } + case IOCNR_GET_PROTOCOLS: + if (_IOC_DIR(cmd) != _IOC_READ || + _IOC_SIZE(cmd) < sizeof(twoints)) { + retval = -EINVAL; + goto done; + } - twoints[0] = usblp->current_protocol; - twoints[1] = 0; - for (i = USBLP_FIRST_PROTOCOL; - i <= USBLP_LAST_PROTOCOL; i++) { - if (usblp->protocol[i].alt_setting >= 0) - twoints[1] |= (1<current_protocol; + twoints[1] = 0; + for (i = USBLP_FIRST_PROTOCOL; + i <= USBLP_LAST_PROTOCOL; i++) { + if (usblp->protocol[i].alt_setting >= 0) + twoints[1] |= (1<current_protocol); - } - break; + usblp_unlink_urbs(usblp); + retval = usblp_set_protocol(usblp, arg); + if (retval < 0) { + usblp_set_protocol(usblp, + usblp->current_protocol); + } + break; - case IOCNR_HP_SET_CHANNEL: - if (_IOC_DIR(cmd) != _IOC_WRITE || - le16_to_cpu(usblp->dev->descriptor.idVendor) != 0x03F0 || - usblp->quirks & USBLP_QUIRK_BIDIR) { - retval = -EINVAL; - goto done; - } + case IOCNR_HP_SET_CHANNEL: + if (_IOC_DIR(cmd) != _IOC_WRITE || + le16_to_cpu(usblp->dev->descriptor.idVendor) != 0x03F0 || + usblp->quirks & USBLP_QUIRK_BIDIR) { + retval = -EINVAL; + goto done; + } - err = usblp_hp_channel_change_request(usblp, - arg, &newChannel); - if (err < 0) { - dev_err(&usblp->dev->dev, - "usblp%d: error = %d setting " - "HP channel\n", - usblp->minor, err); - retval = -EIO; - goto done; - } + err = usblp_hp_channel_change_request(usblp, + arg, &newChannel); + if (err < 0) { + dev_err(&usblp->dev->dev, + "usblp%d: error = %d setting " + "HP channel\n", + usblp->minor, err); + retval = -EIO; + goto done; + } - dbg("usblp%d requested/got HP channel %ld/%d", - usblp->minor, arg, newChannel); - break; + dbg("usblp%d requested/got HP channel %ld/%d", + usblp->minor, arg, newChannel); + break; - case IOCNR_GET_BUS_ADDRESS: - if (_IOC_DIR(cmd) != _IOC_READ || - _IOC_SIZE(cmd) < sizeof(twoints)) { - retval = -EINVAL; - goto done; - } + case IOCNR_GET_BUS_ADDRESS: + if (_IOC_DIR(cmd) != _IOC_READ || + _IOC_SIZE(cmd) < sizeof(twoints)) { + retval = -EINVAL; + goto done; + } - twoints[0] = usblp->dev->bus->busnum; - twoints[1] = usblp->dev->devnum; - if (copy_to_user((void __user *)arg, - (unsigned char *)twoints, - sizeof(twoints))) { - retval = -EFAULT; - goto done; - } + twoints[0] = usblp->dev->bus->busnum; + twoints[1] = usblp->dev->devnum; + if (copy_to_user((void __user *)arg, + (unsigned char *)twoints, + sizeof(twoints))) { + retval = -EFAULT; + goto done; + } - dbg("usblp%d is bus=%d, device=%d", - usblp->minor, twoints[0], twoints[1]); - break; + dbg("usblp%d is bus=%d, device=%d", + usblp->minor, twoints[0], twoints[1]); + break; - case IOCNR_GET_VID_PID: - if (_IOC_DIR(cmd) != _IOC_READ || - _IOC_SIZE(cmd) < sizeof(twoints)) { - retval = -EINVAL; - goto done; - } + case IOCNR_GET_VID_PID: + if (_IOC_DIR(cmd) != _IOC_READ || + _IOC_SIZE(cmd) < sizeof(twoints)) { + retval = -EINVAL; + goto done; + } - twoints[0] = le16_to_cpu(usblp->dev->descriptor.idVendor); - twoints[1] = le16_to_cpu(usblp->dev->descriptor.idProduct); - if (copy_to_user((void __user *)arg, - (unsigned char *)twoints, - sizeof(twoints))) { - retval = -EFAULT; - goto done; - } + twoints[0] = le16_to_cpu(usblp->dev->descriptor.idVendor); + twoints[1] = le16_to_cpu(usblp->dev->descriptor.idProduct); + if (copy_to_user((void __user *)arg, + (unsigned char *)twoints, + sizeof(twoints))) { + retval = -EFAULT; + goto done; + } - dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X", - usblp->minor, twoints[0], twoints[1]); - break; + dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X", + usblp->minor, twoints[0], twoints[1]); + break; - case IOCNR_SOFT_RESET: - if (_IOC_DIR(cmd) != _IOC_NONE) { - retval = -EINVAL; - goto done; - } - retval = usblp_reset(usblp); - break; - default: - retval = -ENOTTY; + case IOCNR_SOFT_RESET: + if (_IOC_DIR(cmd) != _IOC_NONE) { + retval = -EINVAL; + goto done; + } + retval = usblp_reset(usblp); + break; + default: + retval = -ENOTTY; } else /* old-style ioctl value */ switch (cmd) { - case LPGETSTATUS: - if ((retval = usblp_read_status(usblp, usblp->statusbuf))) { - if (printk_ratelimit()) - printk(KERN_ERR "usblp%d:" - "failed reading printer status (%d)\n", - usblp->minor, retval); - retval = -EIO; - goto done; - } - status = *usblp->statusbuf; - if (copy_to_user ((void __user *)arg, &status, sizeof(int))) - retval = -EFAULT; - break; + case LPGETSTATUS: + if ((retval = usblp_read_status(usblp, usblp->statusbuf))) { + if (printk_ratelimit()) + printk(KERN_ERR "usblp%d:" + "failed reading printer status (%d)\n", + usblp->minor, retval); + retval = -EIO; + goto done; + } + status = *usblp->statusbuf; + if (copy_to_user((void __user *)arg, &status, sizeof(int))) + retval = -EFAULT; + break; - case LPABORT: - if (arg) - usblp->flags |= LP_ABORT; - else - usblp->flags &= ~LP_ABORT; - break; + case LPABORT: + if (arg) + usblp->flags |= LP_ABORT; + else + usblp->flags &= ~LP_ABORT; + break; - default: - retval = -ENOTTY; + default: + retval = -ENOTTY; } done: - mutex_unlock (&usblp->mut); + mutex_unlock(&usblp->mut); return retval; } @@ -840,7 +839,7 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t len, lo } done: - mutex_unlock (&usblp->mut); + mutex_unlock(&usblp->mut); return count; } @@ -1023,7 +1022,7 @@ raise_urb: * while you are sending print data, and you don't try to query the * printer status every couple of milliseconds, you will probably be OK. */ -static unsigned int usblp_quirks (__u16 vendor, __u16 product) +static unsigned int usblp_quirks(__u16 vendor, __u16 product) { int i; @@ -1031,7 +1030,7 @@ static unsigned int usblp_quirks (__u16 vendor, __u16 product) if (vendor == quirk_printers[i].vendorId && product == quirk_printers[i].productId) return quirk_printers[i].quirks; - } + } return 0; } @@ -1061,7 +1060,7 @@ static struct usb_class_driver usblp_class = { static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); - struct usblp *usblp = usb_get_intfdata (intf); + struct usblp *usblp = usb_get_intfdata(intf); if (usblp->device_id_string[0] == 0 && usblp->device_id_string[1] == 0) @@ -1075,7 +1074,7 @@ static DEVICE_ATTR(ieee1284_id, S_IRUGO, usblp_show_ieee1284_id, NULL); static int usblp_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *dev = interface_to_usbdev (intf); + struct usb_device *dev = interface_to_usbdev(intf); struct usblp *usblp; int protocol; int retval; @@ -1089,7 +1088,7 @@ static int usblp_probe(struct usb_interface *intf, } usblp->dev = dev; mutex_init(&usblp->wmut); - mutex_init (&usblp->mut); + mutex_init(&usblp->mut); spin_lock_init(&usblp->lock); init_waitqueue_head(&usblp->rwait); init_waitqueue_head(&usblp->wwait); @@ -1153,7 +1152,7 @@ static int usblp_probe(struct usb_interface *intf, usblp_check_status(usblp, 0); #endif - usb_set_intfdata (intf, usblp); + usb_set_intfdata(intf, usblp); usblp->present = 1; @@ -1177,7 +1176,7 @@ static int usblp_probe(struct usb_interface *intf, return 0; abort_intfdata: - usb_set_intfdata (intf, NULL); + usb_set_intfdata(intf, NULL); device_remove_file(&intf->dev, &dev_attr_ieee1284_id); abort: kfree(usblp->readbuf); @@ -1340,35 +1339,35 @@ static int usblp_cache_device_id_string(struct usblp *usblp) static void usblp_disconnect(struct usb_interface *intf) { - struct usblp *usblp = usb_get_intfdata (intf); + struct usblp *usblp = usb_get_intfdata(intf); usb_deregister_dev(intf, &usblp_class); if (!usblp || !usblp->dev) { dev_err(&intf->dev, "bogus disconnect\n"); - BUG (); + BUG(); } device_remove_file(&intf->dev, &dev_attr_ieee1284_id); - mutex_lock (&usblp_mutex); - mutex_lock (&usblp->mut); + mutex_lock(&usblp_mutex); + mutex_lock(&usblp->mut); usblp->present = 0; wake_up(&usblp->wwait); wake_up(&usblp->rwait); - usb_set_intfdata (intf, NULL); + usb_set_intfdata(intf, NULL); usblp_unlink_urbs(usblp); - mutex_unlock (&usblp->mut); + mutex_unlock(&usblp->mut); if (!usblp->used) - usblp_cleanup (usblp); - mutex_unlock (&usblp_mutex); + usblp_cleanup(usblp); + mutex_unlock(&usblp_mutex); } static int usblp_suspend(struct usb_interface *intf, pm_message_t message) { - struct usblp *usblp = usb_get_intfdata (intf); + struct usblp *usblp = usb_get_intfdata(intf); usblp_unlink_urbs(usblp); #if 0 /* XXX Do we want this? What if someone is reading, should we fail? */ @@ -1382,10 +1381,10 @@ static int usblp_suspend(struct usb_interface *intf, pm_message_t message) static int usblp_resume(struct usb_interface *intf) { - struct usblp *usblp = usb_get_intfdata (intf); + struct usblp *usblp = usb_get_intfdata(intf); int r; - r = handle_bidir (usblp); + r = handle_bidir(usblp); return r; } @@ -1401,7 +1400,7 @@ static const struct usb_device_id usblp_ids[] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, usblp_ids); +MODULE_DEVICE_TABLE(usb, usblp_ids); static struct usb_driver usblp_driver = { .name = "usblp", @@ -1426,8 +1425,8 @@ static void __exit usblp_exit(void) module_init(usblp_init); module_exit(usblp_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); module_param(proto_bias, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(proto_bias, "Favourite protocol number"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From f9ff70c25a7779cf6e2b0ff4aecf9c9b28eefae6 Mon Sep 17 00:00:00 2001 From: Nicolas Kaiser Date: Sun, 27 Jun 2010 17:27:51 +0200 Subject: USB: drivers/usb/Makefile: conditionally descend to 'early' Don't descend to the EARLY_PRINTK_DBGP directory unless it is actually used. Signed-off-by: Nicolas Kaiser Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 80b4008c89b..239f050efa3 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -41,7 +41,7 @@ obj-$(CONFIG_USB_MICROTEK) += image/ obj-$(CONFIG_USB_SERIAL) += serial/ obj-$(CONFIG_USB) += misc/ -obj-y += early/ +obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/ obj-$(CONFIG_USB_ATM) += atm/ obj-$(CONFIG_USB_SPEEDTOUCH) += atm/ -- cgit v1.2.3-70-g09d2 From e10fa4787f1fb9c8738dff955c272f30b7b63134 Mon Sep 17 00:00:00 2001 From: Kulikov Vasiliy Date: Mon, 28 Jun 2010 15:55:46 +0400 Subject: USB: xhci: trivial: use ARRAY_SIZE Change sizeof(x) / sizeof(*x) to ARRAY_SIZE(x). Signed-off-by: Kulikov Vasiliy Cc: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 2eb658d2639..6d8f7e32932 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1588,7 +1588,7 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags) unsigned int num_tests; int i, ret; - num_tests = sizeof(simple_test_vector) / sizeof(simple_test_vector[0]); + num_tests = ARRAY_SIZE(simple_test_vector); for (i = 0; i < num_tests; i++) { ret = xhci_test_trb_in_td(xhci, xhci->event_ring->first_seg, @@ -1601,7 +1601,7 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags) return ret; } - num_tests = sizeof(complex_test_vector) / sizeof(complex_test_vector[0]); + num_tests = ARRAY_SIZE(complex_test_vector); for (i = 0; i < num_tests; i++) { ret = xhci_test_trb_in_td(xhci, complex_test_vector[i].input_seg, -- cgit v1.2.3-70-g09d2 From 4307a28eb0128417d9a2b9d858d2bce70ee5b383 Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Mon, 28 Jun 2010 16:56:45 +0200 Subject: USB: EHCI: fix NULL pointer dererence in HCDs that use HCD_LOCAL_MEM If we use the HCD_LOCAL_MEM flag and dma_declare_coherent_memory() to enforce the host controller's local memory utilization we also need to disable native scatter-gather support, otherwise hcd_alloc_coherent() in map_urb_for_dma() is called with urb->transfer_buffer == NULL, that triggers a NULL pointer dereference. We can also consider to add a WARN_ON() and return an error code to better catch this problem in the future. At the moment no driver seems to hit this bug, so I should consider this a low-priority fix. Signed-off-by: Andrea Righi Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 5 +++++ drivers/usb/host/ehci-hcd.c | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 0358c05e6e8..c5753c79773 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1218,6 +1218,11 @@ static int hcd_alloc_coherent(struct usb_bus *bus, { unsigned char *vaddr; + if (*vaddr_handle == NULL) { + WARN_ON_ONCE(1); + return -EFAULT; + } + vaddr = hcd_buffer_alloc(bus, size + sizeof(vaddr), mem_flags, dma_handle); if (!vaddr) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2a19336c982..2e704fa3ced 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -629,7 +629,8 @@ static int ehci_init(struct usb_hcd *hcd) ehci->command = temp; /* Accept arbitrarily long scatter-gather lists */ - hcd->self.sg_tablesize = ~0; + if (!(hcd->driver->flags & HCD_LOCAL_MEM)) + hcd->self.sg_tablesize = ~0; return 0; } -- cgit v1.2.3-70-g09d2 From 1e413943fabdc228e86e4fbaa11e77efa861c23f Mon Sep 17 00:00:00 2001 From: stephane duverger Date: Tue, 29 Jun 2010 16:57:25 +0200 Subject: USB: gadget: compilation issue: missing TASK_INTERRUPTIBLE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Here is the patch for the following issue: drivers/usb/gadget/u_serial.c: In function ‘gs_start_tx’: drivers/usb/gadget/u_serial.c:369: error: ‘TASK_INTERRUPTIBLE’ undeclared (first use in this function) drivers/usb/gadget/u_serial.c:369: error: (Each undeclared identifier is reported only once drivers/usb/gadget/u_serial.c:369: error: for each function it appears in.) drivers/usb/gadget/u_serial.c: In function ‘gs_rx_push’: drivers/usb/gadget/u_serial.c:546: error: ‘TASK_INTERRUPTIBLE’ undeclared (first use in this function) drivers/usb/gadget/u_serial.c: In function ‘gs_close’: drivers/usb/gadget/u_serial.c:857: error: ‘TASK_INTERRUPTIBLE’ undeclared (first use in this function) drivers/usb/gadget/u_serial.c:857: error: implicit declaration of function ‘signal_pending’ drivers/usb/gadget/u_serial.c:857: error: implicit declaration of function ‘schedule_timeout’ drivers/usb/gadget/u_serial.c: In function ‘gserial_cleanup’: drivers/usb/gadget/u_serial.c:1190: error: ‘TASK_UNINTERRUPTIBLE’ undeclared (first use in this function) drivers/usb/gadget/u_serial.c:1190: error: implicit declaration of function ‘schedule’ drivers/usb/gadget/u_serial.c: In function ‘gserial_disconnect’: drivers/usb/gadget/u_serial.c:1311: error: ‘TASK_INTERRUPTIBLE’ undeclared (first use in this function) Signed-off-by: Stephane Duverger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/u_serial.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 3e8dcb5455e..01e5354a4c2 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -18,6 +18,7 @@ /* #define VERBOSE_DEBUG */ #include +#include #include #include #include -- cgit v1.2.3-70-g09d2 From d0893264db29b9bfdb1bc66e731f4ed7f8b52795 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 5 Jul 2010 16:38:04 +0200 Subject: USB: gadget: storage_common: comments updated Updated comment to describe why printing macros are needed even thought they are copied form the composite.h. Also, made multiline comments follow the coding standard. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/storage_common.c | 69 ++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 04c462ff0ea..a10faecfabc 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -57,10 +57,12 @@ #include -/* Thanks to NetChip Technologies for donating this product ID. +/* + * Thanks to NetChip Technologies for donating this product ID. * * DO NOT REUSE THESE IDs with any other driver!! Ever!! - * Instead: allocate your own, using normal USB-IF procedures. */ + * Instead: allocate your own, using normal USB-IF procedures. + */ #define FSG_VENDOR_ID 0x0525 /* NetChip */ #define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ @@ -84,14 +86,27 @@ #define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args) #define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args) -/* Keep those macros in sync with thos in - * include/linux/ubs/composite.h or else GCC will complain. If they +/* + * Keep those macros in sync with those in + * include/linux/usb/composite.h or else GCC will complain. If they * are identical (the same names of arguments, white spaces in the * same places) GCC will allow redefinition otherwise (even if some - * white space is removed or added) warning will be issued. No - * checking if those symbols is defined is performed because warning - * is desired when those macros were defined by someone else to mean - * something else. */ + * white space is removed or added) warning will be issued. + * + * Those macros are needed here because File Storage Gadget does not + * include the composite.h header. For composite gadgets those macros + * are redundant since composite.h is included any way. + * + * One could check whether those macros are already defined (which + * would indicate composite.h had been included) or not (which would + * indicate we were in FSG) but this is not done because a warning is + * desired if definitions here differ from the ones in composite.h. + * + * We want the definitions to match and be the same in File Storage + * Gadget as well as Mass Storage Function (and so composite gadgets + * using MSF). If someone changes them in composite.h it will produce + * a warning in this file when building MSF. + */ #define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args) #define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args) #define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args) @@ -313,9 +328,11 @@ struct fsg_buffhd { enum fsg_buffer_state state; struct fsg_buffhd *next; - /* The NetChip 2280 is faster, and handles some protocol faults + /* + * The NetChip 2280 is faster, and handles some protocol faults * better, if we don't submit any short bulk-out read requests. - * So we will record the intended request length here. */ + * So we will record the intended request length here. + */ unsigned int bulk_out_intended_length; struct usb_request *inreq; @@ -395,8 +412,10 @@ fsg_intf_desc = { .iInterface = FSG_STRING_INTERFACE, }; -/* Three full-speed endpoint descriptors: bulk-in, bulk-out, - * and interrupt-in. */ +/* + * Three full-speed endpoint descriptors: bulk-in, bulk-out, and + * interrupt-in. + */ static struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = { @@ -459,7 +478,7 @@ static struct usb_descriptor_header *fsg_fs_function[] = { * * That means alternate endpoint descriptors (bigger packets) * and a "device qualifier" ... plus more construction options - * for the config descriptor. + * for the configuration descriptor. */ static struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = { @@ -547,8 +566,10 @@ static struct usb_gadget_strings fsg_stringtab = { /*-------------------------------------------------------------------------*/ -/* If the next two routines are called while the gadget is registered, - * the caller must own fsg->filesem for writing. */ +/* + * If the next two routines are called while the gadget is registered, + * the caller must own fsg->filesem for writing. + */ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) { @@ -587,8 +608,10 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) goto out; } - /* If we can't read the file, it's no good. - * If we can't write the file, use it read-only. */ + /* + * If we can't read the file, it's no good. + * If we can't write the file, use it read-only. + */ if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) { LINFO(curlun, "file not readable: %s\n", filename); goto out; @@ -646,8 +669,10 @@ static void fsg_lun_close(struct fsg_lun *curlun) /*-------------------------------------------------------------------------*/ -/* Sync the file data, don't bother with the metadata. - * This code was copied from fs/buffer.c:sys_fdatasync(). */ +/* + * Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). + */ static int fsg_lun_fsync_sub(struct fsg_lun *curlun) { struct file *filp = curlun->filp; @@ -728,8 +753,10 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, if (sscanf(buf, "%d", &i) != 1) return -EINVAL; - /* Allow the write-enable status to change only while the backing file - * is closed. */ + /* + * Allow the write-enable status to change only while the + * backing file is closed. + */ down_read(filesem); if (fsg_lun_is_open(curlun)) { LDBG(curlun, "read-only status change prevented\n"); -- cgit v1.2.3-70-g09d2 From 1e29709e2e577a862dbffe1c89f8033255a322bb Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Fri, 2 Jul 2010 00:36:43 +0200 Subject: USB: serial: io_ti: Don't return 0 if writing the download record failed If the write download record failed we shouldn't return 0. Signed-off-by: Roel Kluin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_ti.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 0fca2659206..03696b91bd6 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1321,7 +1321,7 @@ static int download_fw(struct edgeport_serial *serial) kfree(header); kfree(rom_desc); kfree(ti_manuf_desc); - return status; + return -EINVAL; } kfree(vheader); -- cgit v1.2.3-70-g09d2 From 402e8dd697d9dbfc40645148d0f539a43b6fc3a6 Mon Sep 17 00:00:00 2001 From: Kulikov Vasiliy Date: Sat, 3 Jul 2010 20:04:47 +0400 Subject: USB: core: hcd-pci: use for_each_pci_dev() Use for_each_pci_dev() to simplify the code. Signed-off-by: Kulikov Vasiliy Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index fe6b8d40a50..c3f98543caa 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -66,10 +66,7 @@ static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd, * vice versa. */ companion = NULL; - for (;;) { - companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion); - if (!companion) - break; + for_each_pci_dev(companion) { if (companion->bus != pdev->bus || PCI_SLOT(companion->devfn) != slot) continue; -- cgit v1.2.3-70-g09d2 From 0936fb5e92a90476959447ad8ae5d780afbbd930 Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Thu, 8 Jul 2010 14:02:59 +0530 Subject: USB: musb: use correct register widths in register dumps DMA_ADDR and DMA_COUNT are 32-bit registers, not 16-bit. Marking them as 16-bit in the table causes only the lower 16-bits to be dumped and this is misleading. Signed-off-by: Anand Gadiyar Acked-by: Felipe Balbi Signed-off-by: Ajay Kumar Gupta Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_debugfs.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index bba76af0c0c..c79a5e30d43 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -92,29 +92,29 @@ static const struct musb_register_map musb_regmap[] = { { "LS_EOF1", 0x7E, 8 }, { "SOFT_RST", 0x7F, 8 }, { "DMA_CNTLch0", 0x204, 16 }, - { "DMA_ADDRch0", 0x208, 16 }, - { "DMA_COUNTch0", 0x20C, 16 }, + { "DMA_ADDRch0", 0x208, 32 }, + { "DMA_COUNTch0", 0x20C, 32 }, { "DMA_CNTLch1", 0x214, 16 }, - { "DMA_ADDRch1", 0x218, 16 }, - { "DMA_COUNTch1", 0x21C, 16 }, + { "DMA_ADDRch1", 0x218, 32 }, + { "DMA_COUNTch1", 0x21C, 32 }, { "DMA_CNTLch2", 0x224, 16 }, - { "DMA_ADDRch2", 0x228, 16 }, - { "DMA_COUNTch2", 0x22C, 16 }, + { "DMA_ADDRch2", 0x228, 32 }, + { "DMA_COUNTch2", 0x22C, 32 }, { "DMA_CNTLch3", 0x234, 16 }, - { "DMA_ADDRch3", 0x238, 16 }, - { "DMA_COUNTch3", 0x23C, 16 }, + { "DMA_ADDRch3", 0x238, 32 }, + { "DMA_COUNTch3", 0x23C, 32 }, { "DMA_CNTLch4", 0x244, 16 }, - { "DMA_ADDRch4", 0x248, 16 }, - { "DMA_COUNTch4", 0x24C, 16 }, + { "DMA_ADDRch4", 0x248, 32 }, + { "DMA_COUNTch4", 0x24C, 32 }, { "DMA_CNTLch5", 0x254, 16 }, - { "DMA_ADDRch5", 0x258, 16 }, - { "DMA_COUNTch5", 0x25C, 16 }, + { "DMA_ADDRch5", 0x258, 32 }, + { "DMA_COUNTch5", 0x25C, 32 }, { "DMA_CNTLch6", 0x264, 16 }, - { "DMA_ADDRch6", 0x268, 16 }, - { "DMA_COUNTch6", 0x26C, 16 }, + { "DMA_ADDRch6", 0x268, 32 }, + { "DMA_COUNTch6", 0x26C, 32 }, { "DMA_CNTLch7", 0x274, 16 }, - { "DMA_ADDRch7", 0x278, 16 }, - { "DMA_COUNTch7", 0x27C, 16 }, + { "DMA_ADDRch7", 0x278, 32 }, + { "DMA_COUNTch7", 0x27C, 32 }, { } /* Terminating Entry */ }; -- cgit v1.2.3-70-g09d2 From d709d22eea927e42c911ac0ad151b56aeafd1b76 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Gupta Date: Thu, 8 Jul 2010 14:03:00 +0530 Subject: USB: musb: fix compilation warning in host only mode Fixes below compilation warning when host only configuration is selected. drivers/usb/musb/musb_core.c: In function 'musb_stage0_irq': drivers/usb/musb/musb_core.c:711: warning: unused variable 'mbase' Signed-off-by: Ajay Kumar Gupta Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 3b795c56221..540c766c4f8 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -704,7 +704,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, #ifdef CONFIG_USB_MUSB_HDRC_HCD if (int_usb & MUSB_INTR_CONNECT) { struct usb_hcd *hcd = musb_to_hcd(musb); - void __iomem *mbase = musb->mregs; handled = IRQ_HANDLED; musb->is_active = 1; @@ -717,9 +716,9 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (is_peripheral_active(musb)) { /* REVISIT HNP; just force disconnect */ } - musb_writew(mbase, MUSB_INTRTXE, musb->epmask); - musb_writew(mbase, MUSB_INTRRXE, musb->epmask & 0xfffe); - musb_writeb(mbase, MUSB_INTRUSBE, 0xf7); + musb_writew(musb->mregs, MUSB_INTRTXE, musb->epmask); + musb_writew(musb->mregs, MUSB_INTRRXE, musb->epmask & 0xfffe); + musb_writeb(musb->mregs, MUSB_INTRUSBE, 0xf7); #endif musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED |USB_PORT_STAT_HIGH_SPEED -- cgit v1.2.3-70-g09d2 From 5aa4af2ce6a0643f32d47f21614817792b85298d Mon Sep 17 00:00:00 2001 From: Ajay Kumar Gupta Date: Thu, 8 Jul 2010 14:03:02 +0530 Subject: USB: ehci_omap: fix device detect issue with modules Currently devices don't get detected automatically if the ehci module is inserted 2nd time onward. We need to disconnect and reconnect the device for it to get detected and enumerated. Resetting the USB PHY using PHY reset comamnd over ULPI fixes this issue. Tested on OMAP3EVM. Signed-off-by: Ajay Kumar Gupta Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-omap.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 5450e628157..116ae280053 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -38,6 +38,7 @@ #include #include #include +#include #include /* @@ -236,6 +237,35 @@ static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask) /*-------------------------------------------------------------------------*/ +static void omap_ehci_soft_phy_reset(struct ehci_hcd_omap *omap, u8 port) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + unsigned reg = 0; + + reg = ULPI_FUNC_CTRL_RESET + /* FUNCTION_CTRL_SET register */ + | (ULPI_SET(ULPI_FUNC_CTRL) << EHCI_INSNREG05_ULPI_REGADD_SHIFT) + /* Write */ + | (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) + /* PORTn */ + | ((port + 1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) + /* start ULPI access*/ + | (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT); + + ehci_omap_writel(omap->ehci_base, EHCI_INSNREG05_ULPI, reg); + + /* Wait for ULPI access completion */ + while ((ehci_omap_readl(omap->ehci_base, EHCI_INSNREG05_ULPI) + & (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) { + dev_dbg(omap->dev, "phy reset operation timed out\n"); + break; + } + } +} + /* omap_start_ehc * - Start the TI USBHOST controller */ @@ -425,6 +455,12 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) gpio_set_value(omap->reset_gpio_port[1], 1); } + /* Soft reset the PHY using PHY reset command over ULPI */ + if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) + omap_ehci_soft_phy_reset(omap, 0); + if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) + omap_ehci_soft_phy_reset(omap, 1); + return 0; err_sys_status: -- cgit v1.2.3-70-g09d2 From 3f1a9696aed6814601078f3c430a8e432b625c66 Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Thu, 8 Jul 2010 16:34:54 +0530 Subject: USB: musb: Kill board specific pinmux from driver file This pin-muxing is best done in the board files. The driver should not do this explicitly. Also, this code causes a warning to be thrown when OMAP2430 and OMAP3/4 support are enabled in the same kernel. Signed-off-by: Anand Gadiyar Acked-by: Felipe Balbi Signed-off-by: Ajay Kumar Gupta Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/omap2430.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index e06d65e36bf..2111a241dd0 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -32,8 +32,6 @@ #include #include -#include - #include "musb_core.h" #include "omap2430.h" @@ -194,10 +192,6 @@ int __init musb_platform_init(struct musb *musb, void *board_data) u32 l; struct omap_musb_board_data *data = board_data; -#if defined(CONFIG_ARCH_OMAP2430) - omap_cfg_reg(AE5_2430_USB0HS_STP); -#endif - /* We require some kind of external transceiver, hooked * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. -- cgit v1.2.3-70-g09d2 From 8ca47c8a7621835914c053caaec74e66147dd7dc Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Thu, 8 Jul 2010 16:34:55 +0530 Subject: USB: musb: do not override DMA mode in channel program There is no reason for the DMA channel program to override the DMA mode passed down by its caller. Use the passed parameter directly, and let the caller handle the decision on which mode is to be used. Signed-off-by: Anand Gadiyar Acked-by: Felipe Balbi Signed-off-by: Ajay Kumar Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musbhsdma.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index dc66e4376d4..6dc107f2524 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -173,10 +173,7 @@ static int dma_channel_program(struct dma_channel *channel, musb_channel->max_packet_sz = packet_sz; channel->status = MUSB_DMA_STATUS_BUSY; - if ((mode == 1) && (len >= packet_sz)) - configure_channel(channel, packet_sz, 1, dma_addr, len); - else - configure_channel(channel, packet_sz, 0, dma_addr, len); + configure_channel(channel, packet_sz, mode, dma_addr, len); return true; } -- cgit v1.2.3-70-g09d2 From 7c7e2d00435bd8129c4bacb73fe4a2d4db4e7d7c Mon Sep 17 00:00:00 2001 From: Martin Enderleit Date: Sat, 10 Jul 2010 16:50:12 +0200 Subject: usb: storage: freecom: Fixed several coding style issues. Fixed several coding style issues in freecom.c. Signed-off-by: Martin Enderleit Cc: Matthew Dharm Cc: Daniel Mack Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/freecom.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index 54cc94277ac..6542ca40d50 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -269,7 +269,7 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) /* The firmware will time-out commands after 20 seconds. Some commands * can legitimately take longer than this, so we use a different * command that only waits for the interrupt and then sends status, - * without having to send a new ATAPI command to the device. + * without having to send a new ATAPI command to the device. * * NOTE: There is some indication that a data transfer after a timeout * may not work, but that is a condition that should never happen. @@ -324,14 +324,14 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) /* Find the length we desire to read. */ switch (srb->cmnd[0]) { - case INQUIRY: - case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */ - case MODE_SENSE: - case MODE_SENSE_10: - length = le16_to_cpu(fst->Count); - break; - default: - length = scsi_bufflen(srb); + case INQUIRY: + case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */ + case MODE_SENSE: + case MODE_SENSE_10: + length = le16_to_cpu(fst->Count); + break; + default: + length = scsi_bufflen(srb); } /* verify that this amount is legal */ @@ -414,7 +414,7 @@ static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) /* should never hit here -- filtered in usb.c */ US_DEBUGP ("freecom unimplemented direction: %d\n", us->srb->sc_data_direction); - // Return fail, SCSI seems to handle this better. + /* Return fail, SCSI seems to handle this better. */ return USB_STOR_TRANSPORT_FAILED; break; } @@ -494,8 +494,7 @@ static void pdump (void *ibuffer, int length) offset = 0; } offset += sprintf (line+offset, "%08x:", i); - } - else if ((i & 7) == 0) { + } else if ((i & 7) == 0) { offset += sprintf (line+offset, " -"); } offset += sprintf (line+offset, " %02x", buffer[i] & 0xff); -- cgit v1.2.3-70-g09d2 From 925ce689bb31960c839804c19ef38d676f1939b9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 11 Jul 2010 23:18:56 +0200 Subject: USB: autoconvert trivial BKL users to private mutex All these files use the big kernel lock in a trivial way to serialize their private file operations, typically resulting from an earlier semi-automatic pushdown from VFS. None of these drivers appears to want to lock against other code, and they all use the BKL as the top-level lock in their file operations, meaning that there is no lock-order inversion problem. Consequently, we can remove the BKL completely, replacing it with a per-file mutex in every case. Using a scripted approach means we can avoid typos. file=$1 name=$2 if grep -q lock_kernel ${file} ; then if grep -q 'include.*linux.mutex.h' ${file} ; then sed -i '/include.*/d' ${file} else sed -i 's/include.*.*$/include /g' ${file} fi sed -i ${file} \ -e "/^#include.*linux.mutex.h/,$ { 1,/^\(static\|int\|long\)/ { /^\(static\|int\|long\)/istatic DEFINE_MUTEX(${name}_mutex); } }" \ -e "s/\(un\)*lock_kernel\>[ ]*()/mutex_\1lock(\&${name}_mutex)/g" \ -e '/[ ]*cycle_kernel_lock();/d' else sed -i -e '/include.*\/d' ${file} \ -e '/cycle_kernel_lock()/d' fi Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/printer.c | 7 ++++--- drivers/usb/misc/iowarrior.c | 15 ++++++++------- drivers/usb/misc/rio500.c | 15 ++++++++------- drivers/usb/misc/usblcd.c | 16 ++++++++-------- 4 files changed, 28 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 88f05996aa0..cf241c371a7 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -70,6 +70,7 @@ #define DRIVER_DESC "Printer Gadget" #define DRIVER_VERSION "2007 OCT 06" +static DEFINE_MUTEX(printer_mutex); static const char shortname [] = "printer"; static const char driver_desc [] = DRIVER_DESC; @@ -476,7 +477,7 @@ printer_open(struct inode *inode, struct file *fd) unsigned long flags; int ret = -EBUSY; - lock_kernel(); + mutex_lock(&printer_mutex); dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev); spin_lock_irqsave(&dev->lock, flags); @@ -492,7 +493,7 @@ printer_open(struct inode *inode, struct file *fd) spin_unlock_irqrestore(&dev->lock, flags); DBG(dev, "printer_open returned %x\n", ret); - unlock_kernel(); + mutex_unlock(&printer_mutex); return ret; } diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 7dc9d3c6998..82966458a00 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include @@ -61,6 +61,7 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* Module parameters */ +static DEFINE_MUTEX(iowarrior_mutex); static int debug = 0; module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "debug=1 enables debugging messages"); @@ -493,7 +494,7 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, return -ENOMEM; /* lock this object */ - lock_kernel(); + mutex_lock(&iowarrior_mutex); mutex_lock(&dev->mutex); /* verify that the device wasn't unplugged */ @@ -585,7 +586,7 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, error_out: /* unlock the device */ mutex_unlock(&dev->mutex); - unlock_kernel(); + mutex_unlock(&iowarrior_mutex); kfree(buffer); return retval; } @@ -602,12 +603,12 @@ static int iowarrior_open(struct inode *inode, struct file *file) dbg("%s", __func__); - lock_kernel(); + mutex_lock(&iowarrior_mutex); subminor = iminor(inode); interface = usb_find_interface(&iowarrior_driver, subminor); if (!interface) { - unlock_kernel(); + mutex_unlock(&iowarrior_mutex); err("%s - error, can't find device for minor %d", __func__, subminor); return -ENODEV; @@ -617,7 +618,7 @@ static int iowarrior_open(struct inode *inode, struct file *file) dev = usb_get_intfdata(interface); if (!dev) { mutex_unlock(&iowarrior_open_disc_lock); - unlock_kernel(); + mutex_unlock(&iowarrior_mutex); return -ENODEV; } @@ -644,7 +645,7 @@ static int iowarrior_open(struct inode *inode, struct file *file) out: mutex_unlock(&dev->mutex); - unlock_kernel(); + mutex_unlock(&iowarrior_mutex); return retval; } diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index a85771b1563..cc13ae61712 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -72,6 +72,7 @@ struct rio_usb_data { struct mutex lock; /* general race avoidance */ }; +static DEFINE_MUTEX(rio500_mutex); static struct rio_usb_data rio_instance; static int open_rio(struct inode *inode, struct file *file) @@ -79,12 +80,12 @@ static int open_rio(struct inode *inode, struct file *file) struct rio_usb_data *rio = &rio_instance; /* against disconnect() */ - lock_kernel(); + mutex_lock(&rio500_mutex); mutex_lock(&(rio->lock)); if (rio->isopen || !rio->present) { mutex_unlock(&(rio->lock)); - unlock_kernel(); + mutex_unlock(&rio500_mutex); return -EBUSY; } rio->isopen = 1; @@ -94,7 +95,7 @@ static int open_rio(struct inode *inode, struct file *file) mutex_unlock(&(rio->lock)); dev_info(&rio->rio_dev->dev, "Rio opened.\n"); - unlock_kernel(); + mutex_unlock(&rio500_mutex); return 0; } @@ -491,7 +492,7 @@ static void disconnect_rio(struct usb_interface *intf) struct rio_usb_data *rio = usb_get_intfdata (intf); usb_set_intfdata (intf, NULL); - lock_kernel(); + mutex_lock(&rio500_mutex); if (rio) { usb_deregister_dev(intf, &usb_rio_class); @@ -501,7 +502,7 @@ static void disconnect_rio(struct usb_interface *intf) /* better let it finish - the release will do whats needed */ rio->rio_dev = NULL; mutex_unlock(&(rio->lock)); - unlock_kernel(); + mutex_unlock(&rio500_mutex); return; } kfree(rio->ibuf); @@ -512,7 +513,7 @@ static void disconnect_rio(struct usb_interface *intf) rio->present = 0; mutex_unlock(&(rio->lock)); } - unlock_kernel(); + mutex_unlock(&rio500_mutex); } static const struct usb_device_id rio_table[] = { diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 7828c764b32..6ae39e3e504 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -30,6 +29,7 @@ #define IOCTL_GET_DRV_VERSION 2 +static DEFINE_MUTEX(lcd_mutex); static const struct usb_device_id id_table[] = { { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, }, { }, @@ -74,12 +74,12 @@ static int lcd_open(struct inode *inode, struct file *file) struct usb_interface *interface; int subminor, r; - lock_kernel(); + mutex_lock(&lcd_mutex); subminor = iminor(inode); interface = usb_find_interface(&lcd_driver, subminor); if (!interface) { - unlock_kernel(); + mutex_unlock(&lcd_mutex); err ("USBLCD: %s - error, can't find device for minor %d", __func__, subminor); return -ENODEV; @@ -89,7 +89,7 @@ static int lcd_open(struct inode *inode, struct file *file) dev = usb_get_intfdata(interface); if (!dev) { mutex_unlock(&open_disc_mutex); - unlock_kernel(); + mutex_unlock(&lcd_mutex); return -ENODEV; } @@ -101,13 +101,13 @@ static int lcd_open(struct inode *inode, struct file *file) r = usb_autopm_get_interface(interface); if (r < 0) { kref_put(&dev->kref, lcd_delete); - unlock_kernel(); + mutex_unlock(&lcd_mutex); return r; } /* save our object in the file's private structure */ file->private_data = dev; - unlock_kernel(); + mutex_unlock(&lcd_mutex); return 0; } @@ -164,14 +164,14 @@ static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case IOCTL_GET_HARD_VERSION: - lock_kernel(); + mutex_lock(&lcd_mutex); bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice); sprintf(buf,"%1d%1d.%1d%1d", (bcdDevice & 0xF000)>>12, (bcdDevice & 0xF00)>>8, (bcdDevice & 0xF0)>>4, (bcdDevice & 0xF)); - unlock_kernel(); + mutex_unlock(&lcd_mutex); if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0) return -EFAULT; break; -- cgit v1.2.3-70-g09d2 From e53e841d451a2d0da094b8fea4a7f22b296234f6 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 12 Jul 2010 13:50:13 -0700 Subject: USB: usb-skeleton: Remove unnecessary casts of private_data Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usb-skeleton.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index d110588b56f..552679b8dbd 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -142,7 +142,7 @@ static int skel_release(struct inode *inode, struct file *file) { struct usb_skel *dev; - dev = (struct usb_skel *)file->private_data; + dev = file->private_data; if (dev == NULL) return -ENODEV; @@ -162,7 +162,7 @@ static int skel_flush(struct file *file, fl_owner_t id) struct usb_skel *dev; int res; - dev = (struct usb_skel *)file->private_data; + dev = file->private_data; if (dev == NULL) return -ENODEV; @@ -246,7 +246,7 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, int rv; bool ongoing_io; - dev = (struct usb_skel *)file->private_data; + dev = file->private_data; /* if we cannot read at all, return EOF */ if (!dev->bulk_in_urb || !count) @@ -401,7 +401,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, char *buf = NULL; size_t writesize = min(count, (size_t)MAX_TRANSFER); - dev = (struct usb_skel *)file->private_data; + dev = file->private_data; /* verify that we actually have some data to write */ if (count == 0) -- cgit v1.2.3-70-g09d2 From 5bd6e8b3fb787b7337b681aaa601e5c7bdc67c55 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 12 Jul 2010 13:50:12 -0700 Subject: USB: misc: Remove unnecessary casts of private_data Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ftdi-elan.c | 4 ++-- drivers/usb/misc/iowarrior.c | 8 ++++---- drivers/usb/misc/legousbtower.c | 6 +++--- drivers/usb/misc/sisusbvga/sisusb.c | 10 +++++----- drivers/usb/misc/usblcd.c | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index 82e16630a78..aecf380f6ec 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -650,7 +650,7 @@ static int ftdi_elan_open(struct inode *inode, struct file *file) static int ftdi_elan_release(struct inode *inode, struct file *file) { - struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data; + struct usb_ftdi *ftdi = file->private_data; if (ftdi == NULL) return -ENODEV; up(&ftdi->sw_lock); /* decrement the count on our device */ @@ -673,7 +673,7 @@ static ssize_t ftdi_elan_read(struct file *file, char __user *buffer, int bytes_read = 0; int retry_on_empty = 10; int retry_on_timeout = 5; - struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data; + struct usb_ftdi *ftdi = file->private_data; if (ftdi->disconnected > 0) { return -ENODEV; } diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 82966458a00..2de49c8887c 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -283,7 +283,7 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer, int read_idx; int offset; - dev = (struct iowarrior *)file->private_data; + dev = file->private_data; /* verify that the device wasn't unplugged */ if (dev == NULL || !dev->present) @@ -349,7 +349,7 @@ static ssize_t iowarrior_write(struct file *file, char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */ struct urb *int_out_urb = NULL; - dev = (struct iowarrior *)file->private_data; + dev = file->private_data; mutex_lock(&dev->mutex); /* verify that the device wasn't unplugged */ @@ -484,7 +484,7 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, int retval; int io_res; /* checks for bytes read/written and copy_to/from_user results */ - dev = (struct iowarrior *)file->private_data; + dev = file->private_data; if (dev == NULL) { return -ENODEV; } @@ -657,7 +657,7 @@ static int iowarrior_release(struct inode *inode, struct file *file) struct iowarrior *dev; int retval = 0; - dev = (struct iowarrior *)file->private_data; + dev = file->private_data; if (dev == NULL) { return -ENODEV; } diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 8547bf9e317..6482c6e2e6b 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -448,7 +448,7 @@ static int tower_release (struct inode *inode, struct file *file) dbg(2, "%s: enter", __func__); - dev = (struct lego_usb_tower *)file->private_data; + dev = file->private_data; if (dev == NULL) { dbg(1, "%s: object is NULL", __func__); @@ -597,7 +597,7 @@ static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, dbg(2, "%s: enter, count = %Zd", __func__, count); - dev = (struct lego_usb_tower *)file->private_data; + dev = file->private_data; /* lock this object */ if (mutex_lock_interruptible(&dev->lock)) { @@ -686,7 +686,7 @@ static ssize_t tower_write (struct file *file, const char __user *buffer, size_t dbg(2, "%s: enter, count = %Zd", __func__, count); - dev = (struct lego_usb_tower *)file->private_data; + dev = file->private_data; /* lock this object */ if (mutex_lock_interruptible(&dev->lock)) { diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index d25814c172b..70d00e99a4b 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -2487,7 +2487,7 @@ sisusb_release(struct inode *inode, struct file *file) { struct sisusb_usb_data *sisusb; - if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + if (!(sisusb = file->private_data)) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2519,7 +2519,7 @@ sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) u16 buf16; u32 buf32, address; - if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + if (!(sisusb = file->private_data)) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2661,7 +2661,7 @@ sisusb_write(struct file *file, const char __user *buffer, size_t count, u16 buf16; u32 buf32, address; - if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + if (!(sisusb = file->private_data)) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2804,7 +2804,7 @@ sisusb_lseek(struct file *file, loff_t offset, int orig) struct sisusb_usb_data *sisusb; loff_t ret; - if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + if (!(sisusb = file->private_data)) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2969,7 +2969,7 @@ sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) long retval = 0; u32 __user *argp = (u32 __user *)arg; - if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + if (!(sisusb = file->private_data)) return -ENODEV; mutex_lock(&sisusb->lock); diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 6ae39e3e504..d00dde19194 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -116,7 +116,7 @@ static int lcd_release(struct inode *inode, struct file *file) { struct usb_lcd *dev; - dev = (struct usb_lcd *)file->private_data; + dev = file->private_data; if (dev == NULL) return -ENODEV; @@ -132,7 +132,7 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, l int retval = 0; int bytes_read; - dev = (struct usb_lcd *)file->private_data; + dev = file->private_data; /* do a blocking bulk read to get data from the device */ retval = usb_bulk_msg(dev->udev, @@ -158,7 +158,7 @@ static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) u16 bcdDevice; char buf[30]; - dev = (struct usb_lcd *)file->private_data; + dev = file->private_data; if (dev == NULL) return -ENODEV; @@ -217,7 +217,7 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz struct urb *urb = NULL; char *buf = NULL; - dev = (struct usb_lcd *)file->private_data; + dev = file->private_data; /* verify that we actually have some data to write */ if (count == 0) -- cgit v1.2.3-70-g09d2 From fd63b10ba33f272308d9f976a40f2cd064d8b21b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 12 Jul 2010 13:50:11 -0700 Subject: USB: gadget: Remove unnecessary casts of private_data Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_hid.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index 1e00ff9866a..53e120208e9 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c @@ -142,7 +142,7 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = { static ssize_t f_hidg_read(struct file *file, char __user *buffer, size_t count, loff_t *ptr) { - struct f_hidg *hidg = (struct f_hidg *)file->private_data; + struct f_hidg *hidg = file->private_data; char *tmp_buff = NULL; unsigned long flags; @@ -200,7 +200,7 @@ static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req) static ssize_t f_hidg_write(struct file *file, const char __user *buffer, size_t count, loff_t *offp) { - struct f_hidg *hidg = (struct f_hidg *)file->private_data; + struct f_hidg *hidg = file->private_data; ssize_t status = -ENOMEM; if (!access_ok(VERIFY_READ, buffer, count)) @@ -257,7 +257,7 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, static unsigned int f_hidg_poll(struct file *file, poll_table *wait) { - struct f_hidg *hidg = (struct f_hidg *)file->private_data; + struct f_hidg *hidg = file->private_data; unsigned int ret = 0; poll_wait(file, &hidg->read_queue, wait); -- cgit v1.2.3-70-g09d2 From f6c826a90055dd05905982f7a3f60e0bcaa0434e Mon Sep 17 00:00:00 2001 From: stephane duverger Date: Mon, 12 Jul 2010 18:37:53 +0200 Subject: USB: EHCI Debug Port Device Gadget This is a patch that implements an USB EHCI Debug Device using the Gadget API. This patch applies to a 2.6.35-rc3 kernel. The gadget needs a compliant usb controller that forwards the USB_DEVICE_DEBUG_MODE feature to its gadget. The gadget provides two configuration modes, one that only printk() the received data, and one that exposes a serial device to userland (/dev/ttyGSxxx). The gadget has been tested on an IGEPv2 board running a 2.6.35-rc1 kernel. The debug port was fed on the host side by a 2.6.34 kernel. Signed-off-by: Stephane Duverger Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Kconfig | 28 +++ drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/dbgp.c | 434 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 464 insertions(+) create mode 100644 drivers/usb/gadget/dbgp.c (limited to 'drivers') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index dd3b2510185..cd27f9bde2c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -915,6 +915,34 @@ config USB_G_HID Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_hid". +config USB_G_DBGP + tristate "EHCI Debug Device Gadget" + help + This gadget emulates an EHCI Debug device. This is useful when you want + to interact with an EHCI Debug Port. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_dbgp". + +if USB_G_DBGP +choice + prompt "EHCI Debug Device mode" + default USB_G_DBGP_SERIAL + +config USB_G_DBGP_PRINTK + depends on USB_G_DBGP + bool "printk" + help + Directly printk() received data. No interaction. + +config USB_G_DBGP_SERIAL + depends on USB_G_DBGP + bool "serial" + help + Userland can interact using /dev/ttyGSxxx. +endchoice +endif + # put drivers that need isochronous transfer support (for audio # or video class gadget drivers), or specific hardware, here. config USB_G_WEBCAM diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 9bcde110feb..397b892e90e 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -44,6 +44,7 @@ g_printer-objs := printer.o g_cdc-objs := cdc2.o g_multi-objs := multi.o g_hid-objs := hid.o +g_dbgp-objs := dbgp.o g_nokia-objs := nokia.o g_webcam-objs := webcam.o @@ -60,6 +61,7 @@ obj-$(CONFIG_USB_G_PRINTER) += g_printer.o obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o obj-$(CONFIG_USB_G_HID) += g_hid.o +obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o obj-$(CONFIG_USB_G_MULTI) += g_multi.o obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c new file mode 100644 index 00000000000..0ed50a2c0a3 --- /dev/null +++ b/drivers/usb/gadget/dbgp.c @@ -0,0 +1,434 @@ +/* + * dbgp.c -- EHCI Debug Port device gadget + * + * Copyright (C) 2010 Stephane Duverger + * + * Released under the GPLv2. + * + */ + +/* verbose messages */ +#include +#include +#include +#include + +/* See comments in "zero.c" */ +#include "epautoconf.c" + +#ifdef CONFIG_USB_G_DBGP_SERIAL +#include "u_serial.c" +#endif + +#define DRIVER_VENDOR_ID 0x0525 /* NetChip */ +#define DRIVER_PRODUCT_ID 0xc0de /* undefined */ + +#define USB_DEBUG_MAX_PACKET_SIZE 8 +#define DBGP_REQ_EP0_LEN 128 +#define DBGP_REQ_LEN 512 + +static struct dbgp { + struct usb_gadget *gadget; + struct usb_request *req; + struct usb_ep *i_ep; + struct usb_ep *o_ep; +#ifdef CONFIG_USB_G_DBGP_SERIAL + struct gserial *serial; +#endif +} dbgp; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID), + .bNumConfigurations = 1, +}; + +static struct usb_debug_descriptor dbg_desc = { + .bLength = sizeof dbg_desc, + .bDescriptorType = USB_DT_DEBUG, +}; + +static struct usb_endpoint_descriptor i_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .bEndpointAddress = USB_DIR_IN, +}; + +static struct usb_endpoint_descriptor o_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .bEndpointAddress = USB_DIR_OUT, +}; + +#ifdef CONFIG_USB_G_DBGP_PRINTK +static int dbgp_consume(char *buf, unsigned len) +{ + char c; + + if (!len) + return 0; + + c = buf[len-1]; + if (c != 0) + buf[len-1] = 0; + + printk(KERN_NOTICE "%s%c", buf, c); + return 0; +} + +static void __disable_ep(struct usb_ep *ep) +{ + if (ep && ep->driver_data == dbgp.gadget) { + usb_ep_disable(ep); + ep->driver_data = NULL; + } +} + +static void dbgp_disable_ep(void) +{ + __disable_ep(dbgp.i_ep); + __disable_ep(dbgp.o_ep); +} + +static void dbgp_complete(struct usb_ep *ep, struct usb_request *req) +{ + int stp; + int err = 0; + int status = req->status; + + if (ep == dbgp.i_ep) { + stp = 1; + goto fail; + } + + if (status != 0) { + stp = 2; + goto release_req; + } + + dbgp_consume(req->buf, req->actual); + + req->length = DBGP_REQ_LEN; + err = usb_ep_queue(ep, req, GFP_ATOMIC); + if (err < 0) { + stp = 3; + goto release_req; + } + + return; + +release_req: + kfree(req->buf); + usb_ep_free_request(dbgp.o_ep, req); + dbgp_disable_ep(); +fail: + dev_dbg(&dbgp.gadget->dev, + "complete: failure (%d:%d) ==> %d\n", stp, err, status); +} + +static int dbgp_enable_ep_req(struct usb_ep *ep) +{ + int err, stp; + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) { + err = -ENOMEM; + stp = 1; + goto fail_1; + } + + req->buf = kmalloc(DBGP_REQ_LEN, GFP_KERNEL); + if (!req->buf) { + err = -ENOMEM; + stp = 2; + goto fail_2; + } + + req->complete = dbgp_complete; + req->length = DBGP_REQ_LEN; + err = usb_ep_queue(ep, req, GFP_ATOMIC); + if (err < 0) { + stp = 3; + goto fail_3; + } + + return 0; + +fail_3: + kfree(req->buf); +fail_2: + usb_ep_free_request(dbgp.o_ep, req); +fail_1: + dev_dbg(&dbgp.gadget->dev, + "enable ep req: failure (%d:%d)\n", stp, err); + return err; +} + +static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc) +{ + int err = usb_ep_enable(ep, desc); + ep->driver_data = dbgp.gadget; + return err; +} + +static int dbgp_enable_ep(void) +{ + int err, stp; + + err = __enable_ep(dbgp.i_ep, &i_desc); + if (err < 0) { + stp = 1; + goto fail_1; + } + + err = __enable_ep(dbgp.o_ep, &o_desc); + if (err < 0) { + stp = 2; + goto fail_2; + } + + err = dbgp_enable_ep_req(dbgp.o_ep); + if (err < 0) { + stp = 3; + goto fail_3; + } + + return 0; + +fail_3: + __disable_ep(dbgp.o_ep); +fail_2: + __disable_ep(dbgp.i_ep); +fail_1: + dev_dbg(&dbgp.gadget->dev, "enable ep: failure (%d:%d)\n", stp, err); + return err; +} +#endif + +static void dbgp_disconnect(struct usb_gadget *gadget) +{ +#ifdef CONFIG_USB_G_DBGP_PRINTK + dbgp_disable_ep(); +#else + gserial_disconnect(dbgp.serial); +#endif +} + +static void dbgp_unbind(struct usb_gadget *gadget) +{ +#ifdef CONFIG_USB_G_DBGP_SERIAL + kfree(dbgp.serial); +#endif + if (dbgp.req) { + kfree(dbgp.req->buf); + usb_ep_free_request(gadget->ep0, dbgp.req); + } + + gadget->ep0->driver_data = NULL; +} + +static int __init dbgp_configure_endpoints(struct usb_gadget *gadget) +{ + int stp; + + usb_ep_autoconfig_reset(gadget); + + dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc); + if (!dbgp.i_ep) { + stp = 1; + goto fail_1; + } + + dbgp.i_ep->driver_data = gadget; + i_desc.wMaxPacketSize = + __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); + + dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc); + if (!dbgp.o_ep) { + dbgp.i_ep->driver_data = NULL; + stp = 2; + goto fail_2; + } + + dbgp.o_ep->driver_data = gadget; + o_desc.wMaxPacketSize = + __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); + + dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress & 0x7f; + dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress & 0x7f; + +#ifdef CONFIG_USB_G_DBGP_SERIAL + dbgp.serial->in = dbgp.i_ep; + dbgp.serial->out = dbgp.o_ep; + + dbgp.serial->in_desc = &i_desc; + dbgp.serial->out_desc = &o_desc; + + if (gserial_setup(gadget, 1) < 0) { + stp = 3; + goto fail_3; + } + + return 0; + +fail_3: + dbgp.o_ep->driver_data = NULL; +#else + return 0; +#endif +fail_2: + dbgp.i_ep->driver_data = NULL; +fail_1: + dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp); + return -ENODEV; +} + +static int __init dbgp_bind(struct usb_gadget *gadget) +{ + int err, stp; + + dbgp.gadget = gadget; + + dbgp.req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!dbgp.req) { + err = -ENOMEM; + stp = 1; + goto fail; + } + + dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL); + if (!dbgp.req->buf) { + err = -ENOMEM; + stp = 2; + goto fail; + } + + dbgp.req->length = DBGP_REQ_EP0_LEN; + gadget->ep0->driver_data = gadget; + +#ifdef CONFIG_USB_G_DBGP_SERIAL + dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL); + if (!dbgp.serial) { + stp = 3; + err = -ENOMEM; + goto fail; + } +#endif + err = dbgp_configure_endpoints(gadget); + if (err < 0) { + stp = 4; + goto fail; + } + + dev_dbg(&dbgp.gadget->dev, "bind: success\n"); + return 0; + +fail: + dev_dbg(&gadget->dev, "bind: failure (%d:%d)\n", stp, err); + dbgp_unbind(gadget); + return err; +} + +static void dbgp_setup_complete(struct usb_ep *ep, + struct usb_request *req) +{ + dev_dbg(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n", + req->status, req->actual, req->length); +} + +static int dbgp_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_request *req = dbgp.req; + u8 request = ctrl->bRequest; + u16 value = le16_to_cpu(ctrl->wValue); + u16 length = le16_to_cpu(ctrl->wLength); + int err = 0; + void *data; + u16 len; + + gadget->ep0->driver_data = gadget; + + if (request == USB_REQ_GET_DESCRIPTOR) { + switch (value>>8) { + case USB_DT_DEVICE: + dev_dbg(&dbgp.gadget->dev, "setup: desc device\n"); + len = sizeof device_desc; + data = &device_desc; + break; + case USB_DT_DEBUG: + dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n"); + len = sizeof dbg_desc; + data = &dbg_desc; + break; + default: + goto fail; + } + } else if (request == USB_REQ_SET_FEATURE && + value == USB_DEVICE_DEBUG_MODE) { + len = 0; + data = NULL; + dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n"); +#ifdef CONFIG_USB_G_DBGP_PRINTK + err = dbgp_enable_ep(); +#else + err = gserial_connect(dbgp.serial, 0); +#endif + if (err < 0) + goto fail; + } else + goto fail; + + if (len >= 0) { + req->length = min(length, len); + req->zero = len < req->length; + if (data && req->length) + memcpy(req->buf, data, req->length); + + req->complete = dbgp_setup_complete; + return usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + } + +fail: + dev_dbg(&dbgp.gadget->dev, + "setup: failure req %x v %x\n", request, value); + return err; +} + +static struct usb_gadget_driver dbgp_driver = { + .function = "dbgp", + .speed = USB_SPEED_HIGH, + .bind = dbgp_bind, + .unbind = dbgp_unbind, + .setup = dbgp_setup, + .disconnect = dbgp_disconnect, + .driver = { + .owner = THIS_MODULE, + .name = "dbgp" + }, +}; + +static int __init dbgp_init(void) +{ + return usb_gadget_register_driver(&dbgp_driver); +} + +static void __exit dbgp_exit(void) +{ + usb_gadget_unregister_driver(&dbgp_driver); +#ifdef CONFIG_USB_G_DBGP_SERIAL + gserial_cleanup(); +#endif +} + +MODULE_AUTHOR("Stephane Duverger"); +MODULE_LICENSE("GPL"); +module_init(dbgp_init); +module_exit(dbgp_exit); -- cgit v1.2.3-70-g09d2 From d0390d92bf548a903a48e1a2b3a12eff8a9d838b Mon Sep 17 00:00:00 2001 From: stephane duverger Date: Mon, 12 Jul 2010 18:43:47 +0200 Subject: USB: musb: forward debug mode feature to gadget This is a patch for the musb usb controller. It allows forwarding of the debug mode feature to its gadget in order to be able to act as an ehci debug device. This patch has been tested on an IGEPv2 board running a 2.6.35-rc1 kernel. Signed-off-by: Stephane Duverger Cc: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget_ep0.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 21b9788d024..59bef8f3a35 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -402,6 +402,9 @@ __acquires(musb->lock) musb->g.a_alt_hnp_support = 1; break; #endif + case USB_DEVICE_DEBUG_MODE: + handled = 0; + break; stall: default: handled = -EINVAL; -- cgit v1.2.3-70-g09d2 From a7a6b79bc5bd5dda9fdd1adb4a342a4c5ee789a8 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Tue, 13 Jul 2010 23:56:24 +0800 Subject: USB: serial: enable async suspend for usb serial port device Usb serial port device is child of its usb interface device, so we can enable async suspend of usb serial port device to speedup system suspend. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 443468e9d66..2a982e62963 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1071,6 +1071,8 @@ int usb_serial_probe(struct usb_interface *interface, dev_set_name(&port->dev, "ttyUSB%d", port->number); dbg ("%s - registering %s", __func__, dev_name(&port->dev)); port->dev_state = PORT_REGISTERING; + device_enable_async_suspend(&port->dev); + retval = device_add(&port->dev); if (retval) { dev_err(&port->dev, "Error registering port device, " -- cgit v1.2.3-70-g09d2 From 10d0ca024257c88b28235fcd58577c3eb01b0460 Mon Sep 17 00:00:00 2001 From: Christian Dietrich Date: Tue, 20 Jul 2010 09:08:01 +0200 Subject: USB: host: Remove dead CONFIG_ARCH_KARO CONFIG_ARCH_KARO doesn't exist in Kconfig and is never defined anywhere else, therefore removing all references for it from the source code. Signed-off-by: Christian Dietrich Acked-by: Ryan Mallon Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362.h | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h index d995351f9be..0f97820e65b 100644 --- a/drivers/usb/host/isp1362.h +++ b/drivers/usb/host/isp1362.h @@ -8,29 +8,7 @@ /* * Platform specific compile time options */ -#if defined(CONFIG_ARCH_KARO) -#include -#include -#include - -#define USE_32BIT 1 - - -/* These options are mutually eclusive */ -#define USE_PLATFORM_DELAY 1 -#define USE_NDELAY 0 -/* - * MAX_ROOT_PORTS: Number of downstream ports - * - * The chip has two USB ports, one of which can be configured as - * an USB device port, so the value of this constant is implementation - * specific. - */ -#define MAX_ROOT_PORTS 2 -#define DUMMY_DELAY_ACCESS do {} while (0) - -/* insert platform specific definitions for other machines here */ -#elif defined(CONFIG_BLACKFIN) +#if defined(CONFIG_BLACKFIN) #include #define USE_32BIT 0 -- cgit v1.2.3-70-g09d2 From 9800eb330df0c1a8ef6f4123705eea691000e374 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 20 Jul 2010 15:29:08 -0700 Subject: USB: io_ti.c: don't return 0 if writing the download record failed If the write download record failed we shouldn't return 0. Signed-off-by: Roel Kluin Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_ti.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 03696b91bd6..dc47f986df5 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1298,7 +1298,7 @@ static int download_fw(struct edgeport_serial *serial) kfree(header); kfree(rom_desc); kfree(ti_manuf_desc); - return status; + return -EINVAL; } /* verify the write -- must do this in order for -- cgit v1.2.3-70-g09d2 From fa345d0109ac8c8e388196b1db29d1a570239d3b Mon Sep 17 00:00:00 2001 From: Eric Bénard Date: Thu, 15 Jul 2010 09:20:19 +0200 Subject: USB: otg/ulpi.c : fix register write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ulpi_set_vbus and ulpi_set_flags are using ULPI_SET(register) to write to the PHY's registers, which means we can only set bits in the PHY's register and not clear them. By directly using the address of the register without any offset, we now get the expected behaviour for these functions. Signed-off-by: Eric Bénard Cc: Daniel Mack Cc: linux-arm-kernel@lists.infradead.org Cc: Sascha Hauer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/ulpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c index d331b222ad2..10a1df628f8 100644 --- a/drivers/usb/otg/ulpi.c +++ b/drivers/usb/otg/ulpi.c @@ -54,7 +54,7 @@ static int ulpi_set_flags(struct otg_transceiver *otg) if (otg->flags & USB_OTG_EXT_VBUS_INDICATOR) flags |= ULPI_OTG_CTRL_EXTVBUSIND; - return otg_io_write(otg, flags, ULPI_SET(ULPI_OTG_CTRL)); + return otg_io_write(otg, flags, ULPI_OTG_CTRL); } static int ulpi_init(struct otg_transceiver *otg) @@ -95,7 +95,7 @@ static int ulpi_set_vbus(struct otg_transceiver *otg, bool on) flags |= ULPI_OTG_CTRL_DRVVBUS_EXT; } - return otg_io_write(otg, flags, ULPI_SET(ULPI_OTG_CTRL)); + return otg_io_write(otg, flags, ULPI_OTG_CTRL); } struct otg_transceiver * -- cgit v1.2.3-70-g09d2 From 6a5a9a4b1791b49047267cba59a69a8861c9bb7c Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Thu, 15 Jul 2010 16:00:14 +0300 Subject: USB: otg/ulpi: remove unused macro Signed-off-by: Igor Grinberg Signed-off-by: Mike Rapoport Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/ulpi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c index 10a1df628f8..448d643e727 100644 --- a/drivers/usb/otg/ulpi.c +++ b/drivers/usb/otg/ulpi.c @@ -31,8 +31,6 @@ #define ULPI_ID(vendor, product) (((vendor) << 16) | (product)) -#define TR_FLAG(flags, a, b) (((flags) & a) ? b : 0) - /* ULPI hardcoded IDs, used for probing */ static unsigned int ulpi_ids[] = { ULPI_ID(0x04cc, 0x1504), /* NXP ISP1504 */ -- cgit v1.2.3-70-g09d2 From 51a91a5424cb94f40eb0c9d0b71d8df4e423742a Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Thu, 15 Jul 2010 16:00:15 +0300 Subject: USB: otg/ulpi: add support for SMSC USB3319 ulpi phy Signed-off-by: Igor Grinberg Signed-off-by: Mike Rapoport Signed-off-by: Greg Kroah-Hartman --- drivers/usb/otg/Kconfig | 2 -- drivers/usb/otg/ulpi.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 3d2d3e549bd..3b1289572d7 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -49,8 +49,6 @@ config USB_ULPI Enable this to support ULPI connected USB OTG transceivers which are likely found on embedded boards. - The only chip currently supported is NXP's ISP1504 - config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c index 448d643e727..ef7dbe40f11 100644 --- a/drivers/usb/otg/ulpi.c +++ b/drivers/usb/otg/ulpi.c @@ -34,6 +34,7 @@ /* ULPI hardcoded IDs, used for probing */ static unsigned int ulpi_ids[] = { ULPI_ID(0x04cc, 0x1504), /* NXP ISP1504 */ + ULPI_ID(0x0424, 0x0006), /* SMSC USB3319 */ }; static int ulpi_set_flags(struct otg_transceiver *otg) -- cgit v1.2.3-70-g09d2 From 13dd0c9767349b280cf131c34461f85e5effc42a Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Thu, 15 Jul 2010 16:00:16 +0300 Subject: USB: otg/ulpi: extend the generic ulpi driver. 1) Introduce ulpi specific flags for control of the ulpi phy 2) Extend the generic ulpi driver with support for Function and Interface control of upli phy 3) Update the platforms using the generic ulpi driver with new ulpi flags 4) Remove the otg control flags not in use Signed-off-by: Igor Grinberg Signed-off-by: Mike Rapoport Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-mx3/mach-armadillo5x0.c | 4 +- arch/arm/mach-mx3/mach-mx31lilly.c | 4 +- arch/arm/mach-mx3/mach-mx31lite.c | 2 +- arch/arm/mach-mx3/mach-mx31moboard.c | 2 +- arch/arm/mach-mx3/mach-pcm037.c | 4 +- arch/arm/mach-mx3/mach-pcm043.c | 2 +- arch/arm/mach-mx3/mx31moboard-smartbot.c | 2 +- drivers/usb/otg/ulpi.c | 127 ++++++++++++++++++++++++++++--- include/linux/usb/otg.h | 7 -- include/linux/usb/ulpi.h | 39 ++++++++++ 10 files changed, 166 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-mx3/mach-armadillo5x0.c b/arch/arm/mach-mx3/mach-armadillo5x0.c index 96aadcadb4f..68879c996a5 100644 --- a/arch/arm/mach-mx3/mach-armadillo5x0.c +++ b/arch/arm/mach-mx3/mach-armadillo5x0.c @@ -551,9 +551,9 @@ static void __init armadillo5x0_init(void) /* USB */ #if defined(CONFIG_USB_ULPI) usbotg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); mxc_register_device(&mxc_otg_host, &usbotg_pdata); mxc_register_device(&mxc_usbh2, &usbh2_pdata); diff --git a/arch/arm/mach-mx3/mach-mx31lilly.c b/arch/arm/mach-mx3/mach-mx31lilly.c index 8f66f65e80e..7c37daabb75 100644 --- a/arch/arm/mach-mx3/mach-mx31lilly.c +++ b/arch/arm/mach-mx3/mach-mx31lilly.c @@ -245,9 +245,9 @@ static struct mxc_usbh_platform_data usbh2_pdata = { static void lilly1131_usb_init(void) { usbotg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); mxc_register_device(&mxc_usbh1, &usbh1_pdata); mxc_register_device(&mxc_usbh2, &usbh2_pdata); diff --git a/arch/arm/mach-mx3/mach-mx31lite.c b/arch/arm/mach-mx3/mach-mx31lite.c index da236c497d2..f66a9576d8c 100644 --- a/arch/arm/mach-mx3/mach-mx31lite.c +++ b/arch/arm/mach-mx3/mach-mx31lite.c @@ -256,7 +256,7 @@ static void __init mxc_board_init(void) #if defined(CONFIG_USB_ULPI) /* USB */ usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); mxc_register_device(&mxc_usbh2, &usbh2_pdata); #endif diff --git a/arch/arm/mach-mx3/mach-mx31moboard.c b/arch/arm/mach-mx3/mach-mx31moboard.c index 67776bc61c3..7a075e8bf2d 100644 --- a/arch/arm/mach-mx3/mach-mx31moboard.c +++ b/arch/arm/mach-mx3/mach-mx31moboard.c @@ -412,7 +412,7 @@ static struct mxc_usbh_platform_data usbh2_pdata = { static int __init moboard_usbh2_init(void) { usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); return mxc_register_device(&mxc_usbh2, &usbh2_pdata); } diff --git a/arch/arm/mach-mx3/mach-pcm037.c b/arch/arm/mach-mx3/mach-pcm037.c index 8a292dd1a71..214de11b20b 100644 --- a/arch/arm/mach-mx3/mach-pcm037.c +++ b/arch/arm/mach-mx3/mach-pcm037.c @@ -654,13 +654,13 @@ static void __init mxc_board_init(void) #if defined(CONFIG_USB_ULPI) if (otg_mode_host) { otg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); mxc_register_device(&mxc_otg_host, &otg_pdata); } usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); mxc_register_device(&mxc_usbh2, &usbh2_pdata); #endif diff --git a/arch/arm/mach-mx3/mach-pcm043.c b/arch/arm/mach-mx3/mach-pcm043.c index 47f5311b301..28886f0e62f 100644 --- a/arch/arm/mach-mx3/mach-pcm043.c +++ b/arch/arm/mach-mx3/mach-pcm043.c @@ -378,7 +378,7 @@ static void __init mxc_board_init(void) #if defined(CONFIG_USB_ULPI) if (otg_mode_host) { otg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); mxc_register_device(&mxc_otg_host, &otg_pdata); } diff --git a/arch/arm/mach-mx3/mx31moboard-smartbot.c b/arch/arm/mach-mx3/mx31moboard-smartbot.c index 40c3e7564cb..417757e78c6 100644 --- a/arch/arm/mach-mx3/mx31moboard-smartbot.c +++ b/arch/arm/mach-mx3/mx31moboard-smartbot.c @@ -134,7 +134,7 @@ static struct mxc_usbh_platform_data otg_host_pdata = { static int __init smartbot_otg_host_init(void) { otg_host_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops, - USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT); + ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); return mxc_register_device(&mxc_otg_host, &otg_host_pdata); } diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c index ef7dbe40f11..ccc81950822 100644 --- a/drivers/usb/otg/ulpi.c +++ b/drivers/usb/otg/ulpi.c @@ -37,25 +37,106 @@ static unsigned int ulpi_ids[] = { ULPI_ID(0x0424, 0x0006), /* SMSC USB3319 */ }; -static int ulpi_set_flags(struct otg_transceiver *otg) +static int ulpi_set_otg_flags(struct otg_transceiver *otg) { - unsigned int flags = 0; + unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN | + ULPI_OTG_CTRL_DM_PULLDOWN; - if (otg->flags & USB_OTG_PULLUP_ID) + if (otg->flags & ULPI_OTG_ID_PULLUP) flags |= ULPI_OTG_CTRL_ID_PULLUP; - if (otg->flags & USB_OTG_PULLDOWN_DM) - flags |= ULPI_OTG_CTRL_DM_PULLDOWN; + /* + * ULPI Specification rev.1.1 default + * for Dp/DmPulldown is enabled. + */ + if (otg->flags & ULPI_OTG_DP_PULLDOWN_DIS) + flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN; - if (otg->flags & USB_OTG_PULLDOWN_DP) - flags |= ULPI_OTG_CTRL_DP_PULLDOWN; + if (otg->flags & ULPI_OTG_DM_PULLDOWN_DIS) + flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN; - if (otg->flags & USB_OTG_EXT_VBUS_INDICATOR) + if (otg->flags & ULPI_OTG_EXTVBUSIND) flags |= ULPI_OTG_CTRL_EXTVBUSIND; return otg_io_write(otg, flags, ULPI_OTG_CTRL); } +static int ulpi_set_fc_flags(struct otg_transceiver *otg) +{ + unsigned int flags = 0; + + /* + * ULPI Specification rev.1.1 default + * for XcvrSelect is Full Speed. + */ + if (otg->flags & ULPI_FC_HS) + flags |= ULPI_FUNC_CTRL_HIGH_SPEED; + else if (otg->flags & ULPI_FC_LS) + flags |= ULPI_FUNC_CTRL_LOW_SPEED; + else if (otg->flags & ULPI_FC_FS4LS) + flags |= ULPI_FUNC_CTRL_FS4LS; + else + flags |= ULPI_FUNC_CTRL_FULL_SPEED; + + if (otg->flags & ULPI_FC_TERMSEL) + flags |= ULPI_FUNC_CTRL_TERMSELECT; + + /* + * ULPI Specification rev.1.1 default + * for OpMode is Normal Operation. + */ + if (otg->flags & ULPI_FC_OP_NODRV) + flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; + else if (otg->flags & ULPI_FC_OP_DIS_NRZI) + flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI; + else if (otg->flags & ULPI_FC_OP_NSYNC_NEOP) + flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP; + else + flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + + /* + * ULPI Specification rev.1.1 default + * for SuspendM is Powered. + */ + flags |= ULPI_FUNC_CTRL_SUSPENDM; + + return otg_io_write(otg, flags, ULPI_FUNC_CTRL); +} + +static int ulpi_set_ic_flags(struct otg_transceiver *otg) +{ + unsigned int flags = 0; + + if (otg->flags & ULPI_IC_AUTORESUME) + flags |= ULPI_IFC_CTRL_AUTORESUME; + + if (otg->flags & ULPI_IC_EXTVBUS_INDINV) + flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS; + + if (otg->flags & ULPI_IC_IND_PASSTHRU) + flags |= ULPI_IFC_CTRL_PASSTHRU; + + if (otg->flags & ULPI_IC_PROTECT_DIS) + flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE; + + return otg_io_write(otg, flags, ULPI_IFC_CTRL); +} + +static int ulpi_set_flags(struct otg_transceiver *otg) +{ + int ret; + + ret = ulpi_set_otg_flags(otg); + if (ret) + return ret; + + ret = ulpi_set_ic_flags(otg); + if (ret) + return ret; + + return ulpi_set_fc_flags(otg); +} + static int ulpi_init(struct otg_transceiver *otg) { int i, vid, pid, ret; @@ -80,6 +161,31 @@ static int ulpi_init(struct otg_transceiver *otg) return -ENODEV; } +static int ulpi_set_host(struct otg_transceiver *otg, struct usb_bus *host) +{ + unsigned int flags = otg_io_read(otg, ULPI_IFC_CTRL); + + if (!host) { + otg->host = NULL; + return 0; + } + + otg->host = host; + + flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE | + ULPI_IFC_CTRL_3_PIN_SERIAL_MODE | + ULPI_IFC_CTRL_CARKITMODE); + + if (otg->flags & ULPI_IC_6PIN_SERIAL) + flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE; + else if (otg->flags & ULPI_IC_3PIN_SERIAL) + flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE; + else if (otg->flags & ULPI_IC_CARKIT) + flags |= ULPI_IFC_CTRL_CARKITMODE; + + return otg_io_write(otg, flags, ULPI_IFC_CTRL); +} + static int ulpi_set_vbus(struct otg_transceiver *otg, bool on) { unsigned int flags = otg_io_read(otg, ULPI_OTG_CTRL); @@ -87,10 +193,10 @@ static int ulpi_set_vbus(struct otg_transceiver *otg, bool on) flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT); if (on) { - if (otg->flags & USB_OTG_DRV_VBUS) + if (otg->flags & ULPI_OTG_DRVVBUS) flags |= ULPI_OTG_CTRL_DRVVBUS; - if (otg->flags & USB_OTG_DRV_VBUS_EXT) + if (otg->flags & ULPI_OTG_DRVVBUS_EXT) flags |= ULPI_OTG_CTRL_DRVVBUS_EXT; } @@ -111,6 +217,7 @@ otg_ulpi_create(struct otg_io_access_ops *ops, otg->flags = flags; otg->io_ops = ops; otg->init = ulpi_init; + otg->set_host = ulpi_set_host; otg->set_vbus = ulpi_set_vbus; return otg; diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 54b2c5e48b9..545cba73cca 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -43,13 +43,6 @@ enum usb_xceiv_events { USB_EVENT_ENUMERATED, /* gadget driver enumerated */ }; -#define USB_OTG_PULLUP_ID (1 << 0) -#define USB_OTG_PULLDOWN_DP (1 << 1) -#define USB_OTG_PULLDOWN_DM (1 << 2) -#define USB_OTG_EXT_VBUS_INDICATOR (1 << 3) -#define USB_OTG_DRV_VBUS (1 << 4) -#define USB_OTG_DRV_VBUS_EXT (1 << 5) - struct otg_transceiver; /* for transceivers connected thru an ULPI interface, the user must diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h index 900d97b7096..82b1507f473 100644 --- a/include/linux/usb/ulpi.h +++ b/include/linux/usb/ulpi.h @@ -14,6 +14,41 @@ #include /*-------------------------------------------------------------------------*/ +/* + * ULPI Flags + */ +#define ULPI_OTG_ID_PULLUP (1 << 0) +#define ULPI_OTG_DP_PULLDOWN_DIS (1 << 1) +#define ULPI_OTG_DM_PULLDOWN_DIS (1 << 2) +#define ULPI_OTG_DISCHRGVBUS (1 << 3) +#define ULPI_OTG_CHRGVBUS (1 << 4) +#define ULPI_OTG_DRVVBUS (1 << 5) +#define ULPI_OTG_DRVVBUS_EXT (1 << 6) +#define ULPI_OTG_EXTVBUSIND (1 << 7) + +#define ULPI_IC_6PIN_SERIAL (1 << 8) +#define ULPI_IC_3PIN_SERIAL (1 << 9) +#define ULPI_IC_CARKIT (1 << 10) +#define ULPI_IC_CLKSUSPM (1 << 11) +#define ULPI_IC_AUTORESUME (1 << 12) +#define ULPI_IC_EXTVBUS_INDINV (1 << 13) +#define ULPI_IC_IND_PASSTHRU (1 << 14) +#define ULPI_IC_PROTECT_DIS (1 << 15) + +#define ULPI_FC_HS (1 << 16) +#define ULPI_FC_FS (1 << 17) +#define ULPI_FC_LS (1 << 18) +#define ULPI_FC_FS4LS (1 << 19) +#define ULPI_FC_TERMSEL (1 << 20) +#define ULPI_FC_OP_NORM (1 << 21) +#define ULPI_FC_OP_NODRV (1 << 22) +#define ULPI_FC_OP_DIS_NRZI (1 << 23) +#define ULPI_FC_OP_NSYNC_NEOP (1 << 24) +#define ULPI_FC_RST (1 << 25) +#define ULPI_FC_SUSPM (1 << 26) + +/*-------------------------------------------------------------------------*/ + /* * Macros for Set and Clear * See ULPI 1.1 specification to find the registers with Set and Clear offsets @@ -59,6 +94,10 @@ /*-------------------------------------------------------------------------*/ +/* + * Register Bits + */ + /* Function Control */ #define ULPI_FUNC_CTRL_XCVRSEL (1 << 0) #define ULPI_FUNC_CTRL_XCVRSEL_MASK (3 << 0) -- cgit v1.2.3-70-g09d2 From 43b86af83da7db8b2c6d85ca970203950e5bad88 Mon Sep 17 00:00:00 2001 From: Dong Nguyen Date: Wed, 21 Jul 2010 16:56:08 -0700 Subject: USB: xHCI: Supporting MSI/MSI-X Enable MSI/MSI-X supporting in xhci driver. Provide the mechanism to fall back using MSI and Legacy IRQs if MSI-X IRQs register failed. Signed-off-by: Dong Nguyen Signed-off-by: Sarah Sharp , Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 1 + drivers/usb/host/xhci.c | 166 ++++++++++++++++++++++++++++++++++++------------ drivers/usb/host/xhci.h | 2 +- 3 files changed, 128 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index c5753c79773..5cca00a6d09 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2085,6 +2085,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) local_irq_restore(flags); return rc; } +EXPORT_SYMBOL_GPL(usb_hcd_irq); /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 5e73386b389..3106d22ae05 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -20,6 +20,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include @@ -171,22 +172,95 @@ int xhci_reset(struct xhci_hcd *xhci) return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000); } +static irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd) +{ + irqreturn_t ret; -#if 0 -/* Set up MSI-X table for entry 0 (may claim other entries later) */ -static int xhci_setup_msix(struct xhci_hcd *xhci) + set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + + ret = xhci_irq(hcd); + + return ret; +} + +/* + * Free IRQs + * free all IRQs request + */ +static void xhci_free_irq(struct xhci_hcd *xhci) +{ + int i; + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + + /* return if using legacy interrupt */ + if (xhci_to_hcd(xhci)->irq >= 0) + return; + + if (xhci->msix_entries) { + for (i = 0; i < xhci->msix_count; i++) + if (xhci->msix_entries[i].vector) + free_irq(xhci->msix_entries[i].vector, + xhci_to_hcd(xhci)); + } else if (pdev->irq >= 0) + free_irq(pdev->irq, xhci_to_hcd(xhci)); + + return; +} + +/* + * Set up MSI + */ +static int xhci_setup_msi(struct xhci_hcd *xhci) { int ret; + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + + ret = pci_enable_msi(pdev); + if (ret) { + xhci_err(xhci, "failed to allocate MSI entry\n"); + return ret; + } + + ret = request_irq(pdev->irq, (irq_handler_t)xhci_msi_irq, + 0, "xhci_hcd", xhci_to_hcd(xhci)); + if (ret) { + xhci_err(xhci, "disable MSI interrupt\n"); + pci_disable_msi(pdev); + } + + return ret; +} + +/* + * Set up MSI-X + */ +static int xhci_setup_msix(struct xhci_hcd *xhci) +{ + int i, ret = 0; struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - xhci->msix_count = 0; - /* XXX: did I do this right? ixgbe does kcalloc for more than one */ - xhci->msix_entries = kmalloc(sizeof(struct msix_entry), GFP_KERNEL); + /* + * calculate number of msi-x vectors supported. + * - HCS_MAX_INTRS: the max number of interrupts the host can handle, + * with max number of interrupters based on the xhci HCSPARAMS1. + * - num_online_cpus: maximum msi-x vectors per CPUs core. + * Add additional 1 vector to ensure always available interrupt. + */ + xhci->msix_count = min(num_online_cpus() + 1, + HCS_MAX_INTRS(xhci->hcs_params1)); + + xhci->msix_entries = + kmalloc((sizeof(struct msix_entry))*xhci->msix_count, + GFP_KERNEL); if (!xhci->msix_entries) { xhci_err(xhci, "Failed to allocate MSI-X entries\n"); return -ENOMEM; } - xhci->msix_entries[0].entry = 0; + + for (i = 0; i < xhci->msix_count; i++) { + xhci->msix_entries[i].entry = i; + xhci->msix_entries[i].vector = 0; + } ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count); if (ret) { @@ -194,20 +268,19 @@ static int xhci_setup_msix(struct xhci_hcd *xhci) goto free_entries; } - /* - * Pass the xhci pointer value as the request_irq "cookie". - * If more irqs are added, this will need to be unique for each one. - */ - ret = request_irq(xhci->msix_entries[0].vector, &xhci_irq, 0, - "xHCI", xhci_to_hcd(xhci)); - if (ret) { - xhci_err(xhci, "Failed to allocate MSI-X interrupt\n"); - goto disable_msix; + for (i = 0; i < xhci->msix_count; i++) { + ret = request_irq(xhci->msix_entries[i].vector, + (irq_handler_t)xhci_msi_irq, + 0, "xhci_hcd", xhci_to_hcd(xhci)); + if (ret) + goto disable_msix; } - xhci_dbg(xhci, "Finished setting up MSI-X\n"); - return 0; + + return ret; disable_msix: + xhci_err(xhci, "disable MSI-X interrupt\n"); + xhci_free_irq(xhci); pci_disable_msix(pdev); free_entries: kfree(xhci->msix_entries); @@ -215,21 +288,23 @@ free_entries: return ret; } -/* XXX: code duplication; can xhci_setup_msix call this? */ /* Free any IRQs and disable MSI-X */ static void xhci_cleanup_msix(struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - if (!xhci->msix_entries) - return; - free_irq(xhci->msix_entries[0].vector, xhci); - pci_disable_msix(pdev); - kfree(xhci->msix_entries); - xhci->msix_entries = NULL; - xhci_dbg(xhci, "Finished cleaning up MSI-X\n"); + xhci_free_irq(xhci); + + if (xhci->msix_entries) { + pci_disable_msix(pdev); + kfree(xhci->msix_entries); + xhci->msix_entries = NULL; + } else { + pci_disable_msi(pdev); + } + + return; } -#endif /* * Initialize memory for HCD and xHC (one-time init). @@ -423,20 +498,36 @@ int xhci_run(struct usb_hcd *hcd) { u32 temp; u64 temp_64; + u32 ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); void (*doorbell)(struct xhci_hcd *) = NULL; hcd->uses_new_polling = 1; xhci_dbg(xhci, "xhci_run\n"); -#if 0 /* FIXME: MSI not setup yet */ - /* Do this at the very last minute */ + /* unregister the legacy interrupt */ + if (hcd->irq) + free_irq(hcd->irq, hcd); + hcd->irq = -1; + ret = xhci_setup_msix(xhci); - if (!ret) - return ret; + if (ret) + /* fall back to msi*/ + ret = xhci_setup_msi(xhci); + + if (ret) { + /* fall back to legacy interrupt*/ + ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, + hcd->irq_descr, hcd); + if (ret) { + xhci_err(xhci, "request interrupt %d failed\n", + pdev->irq); + return ret; + } + hcd->irq = pdev->irq; + } - return -ENOSYS; -#endif #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING init_timer(&xhci->event_ring_timer); xhci->event_ring_timer.data = (unsigned long) xhci; @@ -520,11 +611,9 @@ void xhci_stop(struct usb_hcd *hcd) spin_lock_irq(&xhci->lock); xhci_halt(xhci); xhci_reset(xhci); + xhci_cleanup_msix(xhci); spin_unlock_irq(&xhci->lock); -#if 0 /* No MSI yet */ - xhci_cleanup_msix(xhci); -#endif #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING /* Tell the event ring poll function not to reschedule */ xhci->zombie = 1; @@ -558,11 +647,8 @@ void xhci_shutdown(struct usb_hcd *hcd) spin_lock_irq(&xhci->lock); xhci_halt(xhci); - spin_unlock_irq(&xhci->lock); - -#if 0 xhci_cleanup_msix(xhci); -#endif + spin_unlock_irq(&xhci->lock); xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n", xhci_readl(xhci, &xhci->op_regs->status)); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 6c7e3430ec9..5bc03d1c2be 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1130,7 +1130,7 @@ struct xhci_hcd { int page_size; /* Valid values are 12 to 20, inclusive */ int page_shift; - /* only one MSI vector for now, but might need more later */ + /* msi-x vectors */ int msix_count; struct msix_entry *msix_entries; /* data structures */ -- cgit v1.2.3-70-g09d2 From ae68a83bdc1971cb02fefc7a686ba6d077065e71 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 14 Jul 2010 11:03:23 -0400 Subject: USB: EHCI: remove PCI assumption This patch (as1405) fixes a small bug in ehci-hcd's isochronous scheduler. Not all EHCI controllers are PCI, and the code shouldn't assume that they are. Instead, introduce a special flag for controllers which need to delay iso scheduling for full-speed devices beyond the scheduling threshold. Signed-off-by: Alan Stern CC: Sarah Sharp CC: David Brownell CC: stable Acked-by: Sarah Sharp --- drivers/usb/host/ehci-pci.c | 1 + drivers/usb/host/ehci-sched.c | 10 ++++------ drivers/usb/host/ehci.h | 1 + 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index f555e4f35a0..58b72d741d9 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -114,6 +114,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) break; case PCI_VENDOR_ID_INTEL: ehci->need_io_watchdog = 0; + ehci->fs_i_thresh = 1; if (pdev->device == 0x27cc) { ehci->broken_periodic = 1; ehci_info(ehci, "using broken periodic workaround\n"); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index d640346f9b5..efadced4ae6 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1399,7 +1399,6 @@ iso_stream_schedule ( int status; unsigned mod = ehci->periodic_size << 3; struct ehci_iso_sched *sched = urb->hcpriv; - struct pci_dev *pdev; if (sched->span > (mod - SCHEDULE_SLOP)) { ehci_dbg (ehci, "iso request %p too long\n", urb); @@ -1426,15 +1425,14 @@ iso_stream_schedule ( * slot in the schedule, implicitly assuming URB_ISO_ASAP. */ if (likely (!list_empty (&stream->td_list))) { - pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller); start = stream->next_uframe; /* For high speed devices, allow scheduling within the - * isochronous scheduling threshold. For full speed devices, - * don't. (Work around for Intel ICH9 bug.) + * isochronous scheduling threshold. For full speed devices + * and Intel PCI-based controllers, don't (work around for + * Intel ICH9 bug). */ - if (!stream->highspeed && - pdev->vendor == PCI_VENDOR_ID_INTEL) + if (!stream->highspeed && ehci->fs_i_thresh) next = now + ehci->i_thresh; else next = now; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index a4a63ce290e..2c050efd1de 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -130,6 +130,7 @@ struct ehci_hcd { /* one per controller */ unsigned has_amcc_usb23:1; unsigned need_io_watchdog:1; unsigned broken_periodic:1; + unsigned fs_i_thresh:1; /* Intel iso scheduling */ /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) -- cgit v1.2.3-70-g09d2 From bccbefaae050186bed3bcc74b1fd1a9b8c6710b2 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 14 Jul 2010 11:03:36 -0400 Subject: USB: EHCI: simplify remainder computations This patch (as1406) adds a micro-optimization to ehci-hcd's scheduling code. Instead of computing remainders with respect to the schedule length, use bitwise-and (which is quicker). We know that the schedule length will always be a power of two, but the compiler doesn't have this information. Signed-off-by: Alan Stern CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sched.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index efadced4ae6..27dd841b9aa 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1417,7 +1417,7 @@ iso_stream_schedule ( if (!stream->highspeed) period <<= 3; - now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; + now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1); /* Typical case: reuse current schedule, stream is still active. * Hopefully there are no gaps from the host falling behind @@ -1461,7 +1461,7 @@ iso_stream_schedule ( * jump until after the queue is primed. */ start = SCHEDULE_SLOP + (now & ~0x07); - start %= mod; + start &= mod - 1; stream->next_uframe = start; /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ @@ -1483,7 +1483,7 @@ iso_stream_schedule ( /* schedule it here if there's enough bandwidth */ if (enough_space) { - stream->next_uframe = start % mod; + stream->next_uframe = start & (mod - 1); goto ready; } } @@ -1599,7 +1599,7 @@ itd_link_urb ( struct ehci_iso_sched *iso_sched = urb->hcpriv; struct ehci_itd *itd; - next_uframe = stream->next_uframe % mod; + next_uframe = stream->next_uframe & (mod - 1); if (unlikely (list_empty(&stream->td_list))) { ehci_to_hcd(ehci)->self.bandwidth_allocated @@ -1637,13 +1637,13 @@ itd_link_urb ( next_uframe += stream->interval; stream->depth += stream->interval; - next_uframe %= mod; + next_uframe &= mod - 1; packet++; /* link completed itds into the schedule */ if (((next_uframe >> 3) != frame) || packet == urb->number_of_packets) { - itd_link (ehci, frame % ehci->periodic_size, itd); + itd_link(ehci, frame & (ehci->periodic_size - 1), itd); itd = NULL; } } @@ -2020,7 +2020,7 @@ sitd_link_urb ( "sched devp %s ep%d%s-iso [%d] %dms/%04x\n", urb->dev->devpath, stream->bEndpointAddress & 0x0f, (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", - (next_uframe >> 3) % ehci->periodic_size, + (next_uframe >> 3) & (ehci->periodic_size - 1), stream->interval, hc32_to_cpu(ehci, stream->splits)); stream->start = jiffies; } @@ -2043,13 +2043,13 @@ sitd_link_urb ( sitd->urb = urb; sitd_patch(ehci, stream, sitd, sched, packet); - sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size, + sitd_link(ehci, (next_uframe >> 3) & (ehci->periodic_size - 1), sitd); next_uframe += stream->interval << 3; stream->depth += stream->interval << 3; } - stream->next_uframe = next_uframe % mod; + stream->next_uframe = next_uframe & (mod - 1); /* don't need that schedule data any more */ iso_sched_free (stream, sched); @@ -2258,7 +2258,7 @@ scan_periodic (struct ehci_hcd *ehci) now_uframe = ehci->next_uframe; if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { clock = ehci_readl(ehci, &ehci->regs->frame_index); - clock_frame = (clock >> 3) % ehci->periodic_size; + clock_frame = (clock >> 3) & (ehci->periodic_size - 1); } else { clock = now_uframe + mod - 1; clock_frame = -1; @@ -2267,7 +2267,7 @@ scan_periodic (struct ehci_hcd *ehci) free_cached_lists(ehci); ehci->clock_frame = clock_frame; } - clock %= mod; + clock &= mod - 1; clock_frame = clock >> 3; for (;;) { @@ -2356,7 +2356,7 @@ restart: * frame is current. */ if (((frame == clock_frame) || - (((frame + 1) % ehci->periodic_size) + (((frame + 1) & (ehci->periodic_size - 1)) == clock_frame)) && live && (q.sitd->hw_results & @@ -2423,7 +2423,8 @@ restart: || ehci->periodic_sched == 0) break; ehci->next_uframe = now_uframe; - now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; + now = ehci_readl(ehci, &ehci->regs->frame_index) & + (mod - 1); if (now_uframe == now) break; @@ -2436,7 +2437,7 @@ restart: } } else { now_uframe++; - now_uframe %= mod; + now_uframe &= mod - 1; } } } -- cgit v1.2.3-70-g09d2 From ffda080353979273e8aa69fc1e6134f20643ae56 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 14 Jul 2010 11:03:46 -0400 Subject: USB: EHCI: add missing frame -> microframe conversion This patch (as1407) fixes a bug in ehci-hcd's isochronous scheduler. All its calculations should be done in terms of microframes, but for full-speed devices, sched->span is stored in frames. It needs to be converted. This fix is liable to expose problems in other drivers. The old code would accept URBs that should not have been accepted, so drivers have had no reason to avoid submitting URBs that exceeded the maximum schedule length. In an attempt to partially compensate for this, the patch also adjusts the schedule length from a minimum of 256 frames up to a minimum of 512 frames. Signed-off-by: Alan Stern CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 8 +++++++- drivers/usb/host/ehci-sched.c | 21 ++++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2e704fa3ced..34a928d3b7d 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -79,7 +79,13 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_TUNE_RL_TT 0 #define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ #define EHCI_TUNE_MULT_TT 1 -#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ +/* + * Some drivers think it's safe to schedule isochronous transfers more than + * 256 ms into the future (partly as a result of an old bug in the scheduling + * code). In an attempt to avoid trouble, we will use a minimum scheduling + * length of 512 frames instead of 256. + */ +#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */ #define EHCI_IAA_MSECS 10 /* arbitrary */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 27dd841b9aa..dd37350170b 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1395,28 +1395,31 @@ iso_stream_schedule ( struct ehci_iso_stream *stream ) { - u32 now, next, start, period; + u32 now, next, start, period, span; int status; unsigned mod = ehci->periodic_size << 3; struct ehci_iso_sched *sched = urb->hcpriv; - if (sched->span > (mod - SCHEDULE_SLOP)) { + period = urb->interval; + span = sched->span; + if (!stream->highspeed) { + period <<= 3; + span <<= 3; + } + + if (span > mod - SCHEDULE_SLOP) { ehci_dbg (ehci, "iso request %p too long\n", urb); status = -EFBIG; goto fail; } - if ((stream->depth + sched->span) > mod) { + if (stream->depth + span > mod) { ehci_dbg (ehci, "request %p would overflow (%d+%d>%d)\n", - urb, stream->depth, sched->span, mod); + urb, stream->depth, span, mod); status = -EFBIG; goto fail; } - period = urb->interval; - if (!stream->highspeed) - period <<= 3; - now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1); /* Typical case: reuse current schedule, stream is still active. @@ -1445,7 +1448,7 @@ iso_stream_schedule ( period); /* Tried to schedule too far into the future? */ - if (unlikely(((start - now) & (mod - 1)) + sched->span + if (unlikely(((start - now) & (mod - 1)) + span >= mod - 2 * SCHEDULE_SLOP)) { status = -EFBIG; goto fail; -- cgit v1.2.3-70-g09d2 From 1fb2e0558781b07d2ecaabf94c81c17ac820d8f0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 14 Jul 2010 11:03:53 -0400 Subject: USB: EHCI: reorganize isochronous scheduler routine This patch (as1408) rearranges the scheduling code in ehci-hcd, partly to improve its structure, but mainly to change the way it works. Whether or not a transfer exceeds the hardware schedule length will now be determined by looking at the last frame the transfer would use, instead of the first available frame following the end of the transfer. The benefit of this change is that it allows the driver to accept valid URBs which would otherwise be rejected. For example, suppose the schedule length is 1024 frames, the endpoint period is 256 frames, and a four-packet URB is submitted. The four transfers would occupy slots that are 0, 256, 512, and 768 frames past the current frame (plus an extra slop factor). These don't exceed the 1024-frame limit, so the URB should be accepted. But the current code notices that the next available slot would be 1024 frames (plus slop) in the future, which is beyond the limit, and so the URB is rejected unnecessarily. Signed-off-by: Alan Stern CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sched.c | 110 ++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index dd37350170b..3381319a2b3 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1413,13 +1413,6 @@ iso_stream_schedule ( goto fail; } - if (stream->depth + span > mod) { - ehci_dbg (ehci, "request %p would overflow (%d+%d>%d)\n", - urb, stream->depth, span, mod); - status = -EFBIG; - goto fail; - } - now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1); /* Typical case: reuse current schedule, stream is still active. @@ -1428,7 +1421,7 @@ iso_stream_schedule ( * slot in the schedule, implicitly assuming URB_ISO_ASAP. */ if (likely (!list_empty (&stream->td_list))) { - start = stream->next_uframe; + u32 excess; /* For high speed devices, allow scheduling within the * isochronous scheduling threshold. For full speed devices @@ -1440,21 +1433,23 @@ iso_stream_schedule ( else next = now; - /* Fell behind (by up to twice the slop amount)? */ - if (((start - next) & (mod - 1)) >= - mod - 2 * SCHEDULE_SLOP) - start += period * DIV_ROUND_UP( - (next - start) & (mod - 1), - period); - - /* Tried to schedule too far into the future? */ - if (unlikely(((start - now) & (mod - 1)) + span - >= mod - 2 * SCHEDULE_SLOP)) { + /* Fell behind (by up to twice the slop amount)? + * We decide based on the time of the last currently-scheduled + * slot, not the time of the next available slot. + */ + excess = (stream->next_uframe - period - next) & (mod - 1); + if (excess >= mod - 2 * SCHEDULE_SLOP) + start = next + excess - mod + period * + DIV_ROUND_UP(mod - excess, period); + else + start = next + excess + period; + if (start - now >= mod) { + ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n", + urb, start - now - period, period, + mod); status = -EFBIG; goto fail; } - stream->next_uframe = start; - goto ready; } /* need to schedule; when's the next (u)frame we could start? @@ -1463,51 +1458,60 @@ iso_stream_schedule ( * can also help high bandwidth if the dma and irq loads don't * jump until after the queue is primed. */ - start = SCHEDULE_SLOP + (now & ~0x07); - start &= mod - 1; - stream->next_uframe = start; - - /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ - - /* find a uframe slot with enough bandwidth */ - for (; start < (stream->next_uframe + period); start++) { - int enough_space; - - /* check schedule: enough space? */ - if (stream->highspeed) - enough_space = itd_slot_ok (ehci, mod, start, - stream->usecs, period); - else { - if ((start % 8) >= 6) - continue; - enough_space = sitd_slot_ok (ehci, mod, stream, - start, sched, period); + else { + start = SCHEDULE_SLOP + (now & ~0x07); + + /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ + + /* find a uframe slot with enough bandwidth */ + next = start + period; + for (; start < next; start++) { + + /* check schedule: enough space? */ + if (stream->highspeed) { + if (itd_slot_ok(ehci, mod, start, + stream->usecs, period)) + break; + } else { + if ((start % 8) >= 6) + continue; + if (sitd_slot_ok(ehci, mod, stream, + start, sched, period)) + break; + } } - /* schedule it here if there's enough bandwidth */ - if (enough_space) { - stream->next_uframe = start & (mod - 1); - goto ready; + /* no room in the schedule */ + if (start == next) { + ehci_dbg(ehci, "iso resched full %p (now %d max %d)\n", + urb, now, now + mod); + status = -ENOSPC; + goto fail; } } - /* no room in the schedule */ - ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n", - list_empty (&stream->td_list) ? "" : "re", - urb, now, now + mod); - status = -ENOSPC; + /* Tried to schedule too far into the future? */ + if (unlikely(start - now + span - period + >= mod - 2 * SCHEDULE_SLOP)) { + ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n", + urb, start - now, span - period, + mod - 2 * SCHEDULE_SLOP); + status = -EFBIG; + goto fail; + } -fail: - iso_sched_free (stream, sched); - urb->hcpriv = NULL; - return status; + stream->next_uframe = start & (mod - 1); -ready: /* report high speed start in uframes; full speed, in frames */ urb->start_frame = stream->next_uframe; if (!stream->highspeed) urb->start_frame >>= 3; return 0; + + fail: + iso_sched_free(stream, sched); + urb->hcpriv = NULL; + return status; } /*-------------------------------------------------------------------------*/ -- cgit v1.2.3-70-g09d2 From 88d8aa462b8a2128a75b96a0134b22f724ca45d1 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 14 Jul 2010 11:03:57 -0400 Subject: USB: EHCI: remove dead code in the periodic scheduler This patch (as1409) removes some dead code from the ehci-hcd scheduler. Thanks to the previous patch in this series, stream->depth is no longer used. And stream->start and stream->rescheduled apparently have not been used for quite a while, except in some statistics-reporting code that never gets invoked. Signed-off-by: Alan Stern CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sched.c | 15 --------------- drivers/usb/host/ehci.h | 3 --- 2 files changed, 18 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 3381319a2b3..a92526d6e5a 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1074,15 +1074,6 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) if (stream->ep) stream->ep->hcpriv = NULL; - if (stream->rescheduled) { - ehci_info (ehci, "ep%d%s-iso rescheduled " - "%lu times in %lu seconds\n", - stream->bEndpointAddress, is_in ? "in" : "out", - stream->rescheduled, - ((jiffies - stream->start)/HZ) - ); - } - kfree(stream); } } @@ -1617,7 +1608,6 @@ itd_link_urb ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", urb->interval, next_uframe >> 3, next_uframe & 0x7); - stream->start = jiffies; } ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; @@ -1643,7 +1633,6 @@ itd_link_urb ( itd_patch(ehci, itd, iso_sched, packet, uframe); next_uframe += stream->interval; - stream->depth += stream->interval; next_uframe &= mod - 1; packet++; @@ -1699,7 +1688,6 @@ itd_complete ( t = hc32_to_cpup(ehci, &itd->hw_transaction [uframe]); itd->hw_transaction [uframe] = 0; - stream->depth -= stream->interval; /* report transfer status */ if (unlikely (t & ISO_ERRS)) { @@ -2029,7 +2017,6 @@ sitd_link_urb ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", (next_uframe >> 3) & (ehci->periodic_size - 1), stream->interval, hc32_to_cpu(ehci, stream->splits)); - stream->start = jiffies; } ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; @@ -2054,7 +2041,6 @@ sitd_link_urb ( sitd); next_uframe += stream->interval << 3; - stream->depth += stream->interval << 3; } stream->next_uframe = next_uframe & (mod - 1); @@ -2114,7 +2100,6 @@ sitd_complete ( desc->actual_length = desc->length - SITD_LENGTH(t); urb->actual_length += desc->actual_length; } - stream->depth -= stream->interval << 3; /* handle completion now? */ if ((urb_index + 1) != urb->number_of_packets) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 2c050efd1de..e5b9ece8a07 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -404,15 +404,12 @@ struct ehci_iso_stream { u32 refcount; u8 bEndpointAddress; u8 highspeed; - u16 depth; /* depth in uframes */ struct list_head td_list; /* queued itds/sitds */ struct list_head free_list; /* list of unused itds/sitds */ struct usb_device *udev; struct usb_host_endpoint *ep; /* output of (re)scheduling */ - unsigned long start; /* jiffies */ - unsigned long rescheduled; int next_uframe; __hc32 splits; -- cgit v1.2.3-70-g09d2 From 92eb2a5e533f39e8afc831acbbfdabe91cd0c20a Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Mon, 19 Jul 2010 11:47:04 +0200 Subject: USB: gadget: functionfs: stale Makefile entry removed Removed entry referencing g_eth_ffs.c file from Makefile. The file never existed and the line was a leftover from a developing process. Signed-off-by: Michal Nazarewicz Reported-by: Robert P. J. Day Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/Makefile | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 397b892e90e..27283df37d0 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -53,7 +53,6 @@ obj-$(CONFIG_USB_AUDIO) += g_audio.o obj-$(CONFIG_USB_ETH) += g_ether.o obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o -obj-$(CONFIG_USB_ETH_FUNCTIONFS) += g_eth_ffs.o obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o obj-$(CONFIG_USB_G_SERIAL) += g_serial.o -- cgit v1.2.3-70-g09d2 From 33d973ad88ceb83ed1449592b7574b5b5bb33ac6 Mon Sep 17 00:00:00 2001 From: Steven Robertson Date: Wed, 21 Jul 2010 16:38:44 -0400 Subject: USB: resizing usbmon binary interface buffer causes protection faults Enlarging the buffer size via the MON_IOCT_RING_SIZE ioctl causes general protection faults. It appears the culprit is an incorrect argument to mon_free_buff: instead of passing the size of the current buffer being freed, the size of the new buffer is passed. Use the correct size argument to mon_free_buff when changing the size of the buffer. Signed-off-by: Steven Robertson Acked-by: Pete Zaitcev Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_bin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 1be0b9f9336..44cb37b5a4d 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1004,7 +1004,7 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg mutex_lock(&rp->fetch_lock); spin_lock_irqsave(&rp->b_lock, flags); - mon_free_buff(rp->b_vec, size/CHUNK_SIZE); + mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE); kfree(rp->b_vec); rp->b_vec = vec; rp->b_size = size; -- cgit v1.2.3-70-g09d2 From 93362a875fc69881ae69299efaf19a55a1f57db0 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Thu, 22 Jul 2010 00:05:01 +0200 Subject: USB delay init quirk for logitech Harmony 700-series devices The Logitech Harmony 700 series needs an extra delay during initialization. This patch adds a USB quirk which enables such a delay and adds the device to the quirks list. Signed-off-by: Phil Dibowitz Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 6 +++++- drivers/usb/core/quirks.c | 3 +++ include/linux/usb/quirks.h | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d337ef80bf4..84c1897188d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1802,7 +1803,6 @@ int usb_new_device(struct usb_device *udev) pm_runtime_set_active(&udev->dev); pm_runtime_enable(&udev->dev); - usb_detect_quirks(udev); err = usb_enumerate_device(udev); /* Read descriptors */ if (err < 0) goto fail; @@ -3114,6 +3114,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, if (status < 0) goto loop; + usb_detect_quirks(udev); + if (udev->quirks & USB_QUIRK_DELAY_INIT) + msleep(1000); + /* consecutive bus-powered hubs aren't reliable; they can * violate the voltage drop budget. if the new child has * a "powered" LED, users should notice we didn't enable it diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index db99c084df9..25719da45e3 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -38,6 +38,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Creative SB Audigy 2 NX */ { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Harmony 700-series */ + { USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT }, + /* Philips PSC805 audio device */ { USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index 16b7f334754..3e93de7ecbc 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -26,4 +26,8 @@ and can't handle talking to these interfaces */ #define USB_QUIRK_HONOR_BNUMINTERFACES 0x00000020 +/* device needs a pause during initialization, after we read the device + descriptor */ +#define USB_QUIRK_DELAY_INIT 0x00000040 + #endif /* __LINUX_USB_QUIRKS_H */ -- cgit v1.2.3-70-g09d2 From b972302b0a13aaddc9e90da2b4b52722e5d0e776 Mon Sep 17 00:00:00 2001 From: Pavel Kazlou Date: Thu, 22 Jul 2010 03:22:20 +0300 Subject: USB: option: Huawei ETS 1220 support added The patch adds Huawei ETS 1220 product id into the list of supported devices in 'option' usb serial driver. Signed-off-by: Pavel Kazlou Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index a8c54b2116a..051c00ce5d2 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -146,6 +146,7 @@ static void option_instat_callback(struct urb *urb); #define HUAWEI_PRODUCT_E143E 0x143E #define HUAWEI_PRODUCT_E143F 0x143F #define HUAWEI_PRODUCT_E14AC 0x14AC +#define HUAWEI_PRODUCT_ETS1220 0x1803 #define QUANTA_VENDOR_ID 0x0408 #define QUANTA_PRODUCT_Q101 0xEA02 @@ -479,6 +480,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, /* Novatel Merlin V640/XV620 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, /* Novatel Merlin V620/S620 */ -- cgit v1.2.3-70-g09d2 From 4422da61550b2fe5089c5cdc374ceea33e581773 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 22 Jul 2010 15:22:55 -0700 Subject: USB: xHCI: handle_tx_event() refactor: finish_td This patch moves the td universal processing part in handle_tx_event() into a separate function finish_td(). if finish_td() returns 1, it indicates the urb can be given back. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 185 ++++++++++++++++++++++++++----------------- 1 file changed, 112 insertions(+), 73 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index bfc99a93945..691a108295e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1257,6 +1257,104 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code) return 0; } +/* + * Finish the td processing, remove the td from td list; + * Return 1 if the urb can be given back. + */ +static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, + union xhci_trb *event_trb, struct xhci_transfer_event *event, + struct xhci_virt_ep *ep, int *status, bool skip) +{ + struct xhci_virt_device *xdev; + struct xhci_ring *ep_ring; + unsigned int slot_id; + int ep_index; + struct urb *urb = NULL; + struct xhci_ep_ctx *ep_ctx; + int ret = 0; + u32 trb_comp_code; + + slot_id = TRB_TO_SLOT_ID(event->flags); + xdev = xhci->devs[slot_id]; + ep_index = TRB_TO_EP_ID(event->flags) - 1; + ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); + trb_comp_code = GET_COMP_CODE(event->transfer_len); + + if (skip) + goto td_cleanup; + + if (trb_comp_code == COMP_STOP_INVAL || + trb_comp_code == COMP_STOP) { + /* The Endpoint Stop Command completion will take care of any + * stopped TDs. A stopped TD may be restarted, so don't update + * the ring dequeue pointer or take this TD off any lists yet. + */ + ep->stopped_td = td; + ep->stopped_trb = event_trb; + return 0; + } else { + if (trb_comp_code == COMP_STALL) { + /* The transfer is completed from the driver's + * perspective, but we need to issue a set dequeue + * command for this stalled endpoint to move the dequeue + * pointer past the TD. We can't do that here because + * the halt condition must be cleared first. Let the + * USB class driver clear the stall later. + */ + ep->stopped_td = td; + ep->stopped_trb = event_trb; + ep->stopped_stream = ep_ring->stream_id; + } else if (xhci_requires_manual_halt_cleanup(xhci, + ep_ctx, trb_comp_code)) { + /* Other types of errors halt the endpoint, but the + * class driver doesn't call usb_reset_endpoint() unless + * the error is -EPIPE. Clear the halted status in the + * xHCI hardware manually. + */ + xhci_cleanup_halted_endpoint(xhci, + slot_id, ep_index, ep_ring->stream_id, + td, event_trb); + } else { + /* Update ring dequeue pointer */ + while (ep_ring->dequeue != td->last_trb) + inc_deq(xhci, ep_ring, false); + inc_deq(xhci, ep_ring, false); + } + +td_cleanup: + /* Clean up the endpoint's TD list */ + urb = td->urb; + + /* Do one last check of the actual transfer length. + * If the host controller said we transferred more data than + * the buffer length, urb->actual_length will be a very big + * number (since it's unsigned). Play it safe and say we didn't + * transfer anything. + */ + if (urb->actual_length > urb->transfer_buffer_length) { + xhci_warn(xhci, "URB transfer length is wrong, " + "xHC issue? req. len = %u, " + "act. len = %u\n", + urb->transfer_buffer_length, + urb->actual_length); + urb->actual_length = 0; + if (td->urb->transfer_flags & URB_SHORT_NOT_OK) + *status = -EREMOTEIO; + else + *status = 0; + } + list_del(&td->td_list); + /* Was this TD slated to be cancelled but completed anyway? */ + if (!list_empty(&td->cancelled_td_list)) + list_del(&td->cancelled_td_list); + + ret = 1; + } + + return ret; +} + /* * If this function returns an error condition, it means it got a Transfer * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. @@ -1278,6 +1376,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, int status = -EINPROGRESS; struct xhci_ep_ctx *ep_ctx; u32 trb_comp_code; + int ret = 0; xhci_dbg(xhci, "In %s\n", __func__); slot_id = TRB_TO_SLOT_ID(event->flags); @@ -1308,7 +1407,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); xhci_print_trb_offsets(xhci, (union xhci_trb *) event); - urb = NULL; goto cleanup; } xhci_dbg(xhci, "%s - getting list entry\n", __func__); @@ -1379,7 +1477,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, break; } xhci_warn(xhci, "ERROR Unknown event condition, HC probably busted\n"); - urb = NULL; goto cleanup; } /* Now update the urb's actual_length and give back to the core */ @@ -1427,7 +1524,10 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, 0, td, event_trb); - goto td_cleanup; + + ret = finish_td(xhci, td, event_trb, event, ep, + &status, true); + goto cleanup; } /* * Did we transfer any data, despite the errors that might have @@ -1456,7 +1556,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, td->urb->transfer_buffer_length - TRB_LEN(event->transfer_len); xhci_dbg(xhci, "Waiting for status stage event\n"); - urb = NULL; goto cleanup; } } @@ -1558,68 +1657,16 @@ static int handle_tx_event(struct xhci_hcd *xhci, TRB_LEN(event->transfer_len); } } - if (trb_comp_code == COMP_STOP_INVAL || - trb_comp_code == COMP_STOP) { - /* The Endpoint Stop Command completion will take care of any - * stopped TDs. A stopped TD may be restarted, so don't update - * the ring dequeue pointer or take this TD off any lists yet. - */ - ep->stopped_td = td; - ep->stopped_trb = event_trb; - } else { - if (trb_comp_code == COMP_STALL) { - /* The transfer is completed from the driver's - * perspective, but we need to issue a set dequeue - * command for this stalled endpoint to move the dequeue - * pointer past the TD. We can't do that here because - * the halt condition must be cleared first. Let the - * USB class driver clear the stall later. - */ - ep->stopped_td = td; - ep->stopped_trb = event_trb; - ep->stopped_stream = ep_ring->stream_id; - } else if (xhci_requires_manual_halt_cleanup(xhci, - ep_ctx, trb_comp_code)) { - /* Other types of errors halt the endpoint, but the - * class driver doesn't call usb_reset_endpoint() unless - * the error is -EPIPE. Clear the halted status in the - * xHCI hardware manually. - */ - xhci_cleanup_halted_endpoint(xhci, - slot_id, ep_index, ep_ring->stream_id, td, event_trb); - } else { - /* Update ring dequeue pointer */ - while (ep_ring->dequeue != td->last_trb) - inc_deq(xhci, ep_ring, false); - inc_deq(xhci, ep_ring, false); - } -td_cleanup: - /* Clean up the endpoint's TD list */ - urb = td->urb; - /* Do one last check of the actual transfer length. - * If the host controller said we transferred more data than - * the buffer length, urb->actual_length will be a very big - * number (since it's unsigned). Play it safe and say we didn't - * transfer anything. - */ - if (urb->actual_length > urb->transfer_buffer_length) { - xhci_warn(xhci, "URB transfer length is wrong, " - "xHC issue? req. len = %u, " - "act. len = %u\n", - urb->transfer_buffer_length, - urb->actual_length); - urb->actual_length = 0; - if (td->urb->transfer_flags & URB_SHORT_NOT_OK) - status = -EREMOTEIO; - else - status = 0; - } - list_del(&td->td_list); - /* Was this TD slated to be cancelled but completed anyway? */ - if (!list_empty(&td->cancelled_td_list)) - list_del(&td->cancelled_td_list); + ret = finish_td(xhci, td, event_trb, event, ep, &status, false); +cleanup: + inc_deq(xhci, xhci->event_ring, true); + xhci_set_hc_event_deq(xhci); + + /* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */ + if (ret) { + urb = td->urb; /* Leave the TD around for the reset endpoint function to use * (but only if it's not a control endpoint, since we already * queued the Set TR dequeue pointer command for stalled @@ -1627,17 +1674,9 @@ td_cleanup: */ if (usb_endpoint_xfer_control(&urb->ep->desc) || (trb_comp_code != COMP_STALL && - trb_comp_code != COMP_BABBLE)) { + trb_comp_code != COMP_BABBLE)) kfree(td); - } - urb->hcpriv = NULL; - } -cleanup: - inc_deq(xhci, xhci->event_ring, true); - xhci_set_hc_event_deq(xhci); - /* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */ - if (urb) { usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); xhci_dbg(xhci, "Giveback URB %p, len = %d, status = %d\n", urb, urb->actual_length, status); -- cgit v1.2.3-70-g09d2 From 8af56be185a94e39c80ce90fc9f83f3058a3ce80 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 22 Jul 2010 15:23:03 -0700 Subject: USB: xHCI: handle_tx_event() refactor: process_ctrl_td This patch moves the ctrl td processing part in handle_tx_event() into a separate function process_ctrl_td(). Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 184 +++++++++++++++++++++++++------------------ 1 file changed, 106 insertions(+), 78 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 691a108295e..e28cfd15e1a 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1355,6 +1355,109 @@ td_cleanup: return ret; } +/* + * Process control tds, update urb status and actual_length. + */ +static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, + union xhci_trb *event_trb, struct xhci_transfer_event *event, + struct xhci_virt_ep *ep, int *status) +{ + struct xhci_virt_device *xdev; + struct xhci_ring *ep_ring; + unsigned int slot_id; + int ep_index; + struct xhci_ep_ctx *ep_ctx; + u32 trb_comp_code; + + slot_id = TRB_TO_SLOT_ID(event->flags); + xdev = xhci->devs[slot_id]; + ep_index = TRB_TO_EP_ID(event->flags) - 1; + ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); + trb_comp_code = GET_COMP_CODE(event->transfer_len); + + xhci_debug_trb(xhci, xhci->event_ring->dequeue); + switch (trb_comp_code) { + case COMP_SUCCESS: + if (event_trb == ep_ring->dequeue) { + xhci_warn(xhci, "WARN: Success on ctrl setup TRB " + "without IOC set??\n"); + *status = -ESHUTDOWN; + } else if (event_trb != td->last_trb) { + xhci_warn(xhci, "WARN: Success on ctrl data TRB " + "without IOC set??\n"); + *status = -ESHUTDOWN; + } else { + xhci_dbg(xhci, "Successful control transfer!\n"); + *status = 0; + } + break; + case COMP_SHORT_TX: + xhci_warn(xhci, "WARN: short transfer on control ep\n"); + if (td->urb->transfer_flags & URB_SHORT_NOT_OK) + *status = -EREMOTEIO; + else + *status = 0; + break; + default: + if (!xhci_requires_manual_halt_cleanup(xhci, + ep_ctx, trb_comp_code)) + break; + xhci_dbg(xhci, "TRB error code %u, " + "halted endpoint index = %u\n", + trb_comp_code, ep_index); + /* else fall through */ + case COMP_STALL: + /* Did we transfer part of the data (middle) phase? */ + if (event_trb != ep_ring->dequeue && + event_trb != td->last_trb) + td->urb->actual_length = + td->urb->transfer_buffer_length + - TRB_LEN(event->transfer_len); + else + td->urb->actual_length = 0; + + xhci_cleanup_halted_endpoint(xhci, + slot_id, ep_index, 0, td, event_trb); + return finish_td(xhci, td, event_trb, event, ep, status, true); + } + /* + * Did we transfer any data, despite the errors that might have + * happened? I.e. did we get past the setup stage? + */ + if (event_trb != ep_ring->dequeue) { + /* The event was for the status stage */ + if (event_trb == td->last_trb) { + if (td->urb->actual_length != 0) { + /* Don't overwrite a previously set error code + */ + if ((*status == -EINPROGRESS || *status == 0) && + (td->urb->transfer_flags + & URB_SHORT_NOT_OK)) + /* Did we already see a short data + * stage? */ + *status = -EREMOTEIO; + } else { + td->urb->actual_length = + td->urb->transfer_buffer_length; + } + } else { + /* Maybe the event was for the data stage? */ + if (trb_comp_code != COMP_STOP_INVAL) { + /* We didn't stop on a link TRB in the middle */ + td->urb->actual_length = + td->urb->transfer_buffer_length - + TRB_LEN(event->transfer_len); + xhci_dbg(xhci, "Waiting for status " + "stage event\n"); + return 0; + } + } + } + + return finish_td(xhci, td, event_trb, event, ep, status, false); +} + /* * If this function returns an error condition, it means it got a Transfer * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. @@ -1482,84 +1585,9 @@ static int handle_tx_event(struct xhci_hcd *xhci, /* Now update the urb's actual_length and give back to the core */ /* Was this a control transfer? */ if (usb_endpoint_xfer_control(&td->urb->ep->desc)) { - xhci_debug_trb(xhci, xhci->event_ring->dequeue); - switch (trb_comp_code) { - case COMP_SUCCESS: - if (event_trb == ep_ring->dequeue) { - xhci_warn(xhci, "WARN: Success on ctrl setup TRB without IOC set??\n"); - status = -ESHUTDOWN; - } else if (event_trb != td->last_trb) { - xhci_warn(xhci, "WARN: Success on ctrl data TRB without IOC set??\n"); - status = -ESHUTDOWN; - } else { - xhci_dbg(xhci, "Successful control transfer!\n"); - status = 0; - } - break; - case COMP_SHORT_TX: - xhci_warn(xhci, "WARN: short transfer on control ep\n"); - if (td->urb->transfer_flags & URB_SHORT_NOT_OK) - status = -EREMOTEIO; - else - status = 0; - break; - - default: - if (!xhci_requires_manual_halt_cleanup(xhci, - ep_ctx, trb_comp_code)) - break; - xhci_dbg(xhci, "TRB error code %u, " - "halted endpoint index = %u\n", - trb_comp_code, ep_index); - /* else fall through */ - case COMP_STALL: - /* Did we transfer part of the data (middle) phase? */ - if (event_trb != ep_ring->dequeue && - event_trb != td->last_trb) - td->urb->actual_length = - td->urb->transfer_buffer_length - - TRB_LEN(event->transfer_len); - else - td->urb->actual_length = 0; - - xhci_cleanup_halted_endpoint(xhci, - slot_id, ep_index, 0, td, event_trb); - - ret = finish_td(xhci, td, event_trb, event, ep, - &status, true); - goto cleanup; - } - /* - * Did we transfer any data, despite the errors that might have - * happened? I.e. did we get past the setup stage? - */ - if (event_trb != ep_ring->dequeue) { - /* The event was for the status stage */ - if (event_trb == td->last_trb) { - if (td->urb->actual_length != 0) { - /* Don't overwrite a previously set error code */ - if ((status == -EINPROGRESS || - status == 0) && - (td->urb->transfer_flags - & URB_SHORT_NOT_OK)) - /* Did we already see a short data stage? */ - status = -EREMOTEIO; - } else { - td->urb->actual_length = - td->urb->transfer_buffer_length; - } - } else { - /* Maybe the event was for the data stage? */ - if (trb_comp_code != COMP_STOP_INVAL) { - /* We didn't stop on a link TRB in the middle */ - td->urb->actual_length = - td->urb->transfer_buffer_length - - TRB_LEN(event->transfer_len); - xhci_dbg(xhci, "Waiting for status stage event\n"); - goto cleanup; - } - } - } + ret = process_ctrl_td(xhci, td, event_trb, event, ep, + &status); + goto cleanup; } else { switch (trb_comp_code) { case COMP_SUCCESS: -- cgit v1.2.3-70-g09d2 From 22405ed2e1bd8d2676cb45c578bdd05527ce25b9 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 22 Jul 2010 15:23:08 -0700 Subject: USB xHCI: handle_tx_event() refactor: process_bulk_intr_td This patch moves the bulk and interrupt td processing part in handle_tx_event() into a separate function process_bulk_intr_td(). Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 216 +++++++++++++++++++++++-------------------- 1 file changed, 115 insertions(+), 101 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e28cfd15e1a..83580cf794d 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1458,6 +1458,117 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, return finish_td(xhci, td, event_trb, event, ep, status, false); } +/* + * Process bulk and interrupt tds, update urb status and actual_length. + */ +static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, + union xhci_trb *event_trb, struct xhci_transfer_event *event, + struct xhci_virt_ep *ep, int *status) +{ + struct xhci_ring *ep_ring; + union xhci_trb *cur_trb; + struct xhci_segment *cur_seg; + u32 trb_comp_code; + + ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + trb_comp_code = GET_COMP_CODE(event->transfer_len); + + switch (trb_comp_code) { + case COMP_SUCCESS: + /* Double check that the HW transferred everything. */ + if (event_trb != td->last_trb) { + xhci_warn(xhci, "WARN Successful completion " + "on short TX\n"); + if (td->urb->transfer_flags & URB_SHORT_NOT_OK) + *status = -EREMOTEIO; + else + *status = 0; + } else { + if (usb_endpoint_xfer_bulk(&td->urb->ep->desc)) + xhci_dbg(xhci, "Successful bulk " + "transfer!\n"); + else + xhci_dbg(xhci, "Successful interrupt " + "transfer!\n"); + *status = 0; + } + break; + case COMP_SHORT_TX: + if (td->urb->transfer_flags & URB_SHORT_NOT_OK) + *status = -EREMOTEIO; + else + *status = 0; + break; + default: + /* Others already handled above */ + break; + } + dev_dbg(&td->urb->dev->dev, + "ep %#x - asked for %d bytes, " + "%d bytes untransferred\n", + td->urb->ep->desc.bEndpointAddress, + td->urb->transfer_buffer_length, + TRB_LEN(event->transfer_len)); + /* Fast path - was this the last TRB in the TD for this URB? */ + if (event_trb == td->last_trb) { + if (TRB_LEN(event->transfer_len) != 0) { + td->urb->actual_length = + td->urb->transfer_buffer_length - + TRB_LEN(event->transfer_len); + if (td->urb->transfer_buffer_length < + td->urb->actual_length) { + xhci_warn(xhci, "HC gave bad length " + "of %d bytes left\n", + TRB_LEN(event->transfer_len)); + td->urb->actual_length = 0; + if (td->urb->transfer_flags & URB_SHORT_NOT_OK) + *status = -EREMOTEIO; + else + *status = 0; + } + /* Don't overwrite a previously set error code */ + if (*status == -EINPROGRESS) { + if (td->urb->transfer_flags & URB_SHORT_NOT_OK) + *status = -EREMOTEIO; + else + *status = 0; + } + } else { + td->urb->actual_length = + td->urb->transfer_buffer_length; + /* Ignore a short packet completion if the + * untransferred length was zero. + */ + if (*status == -EREMOTEIO) + *status = 0; + } + } else { + /* Slow path - walk the list, starting from the dequeue + * pointer, to get the actual length transferred. + */ + td->urb->actual_length = 0; + for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg; + cur_trb != event_trb; + next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { + if ((cur_trb->generic.field[3] & + TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && + (cur_trb->generic.field[3] & + TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) + td->urb->actual_length += + TRB_LEN(cur_trb->generic.field[2]); + } + /* If the ring didn't stop on a Link or No-op TRB, add + * in the actual bytes transferred from the Normal TRB + */ + if (trb_comp_code != COMP_STOP_INVAL) + td->urb->actual_length += + TRB_LEN(cur_trb->generic.field[2]) - + TRB_LEN(event->transfer_len); + } + + return finish_td(xhci, td, event_trb, event, ep, status, false); +} + /* * If this function returns an error condition, it means it got a Transfer * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. @@ -1584,109 +1695,12 @@ static int handle_tx_event(struct xhci_hcd *xhci, } /* Now update the urb's actual_length and give back to the core */ /* Was this a control transfer? */ - if (usb_endpoint_xfer_control(&td->urb->ep->desc)) { + if (usb_endpoint_xfer_control(&td->urb->ep->desc)) ret = process_ctrl_td(xhci, td, event_trb, event, ep, &status); - goto cleanup; - } else { - switch (trb_comp_code) { - case COMP_SUCCESS: - /* Double check that the HW transferred everything. */ - if (event_trb != td->last_trb) { - xhci_warn(xhci, "WARN Successful completion " - "on short TX\n"); - if (td->urb->transfer_flags & URB_SHORT_NOT_OK) - status = -EREMOTEIO; - else - status = 0; - } else { - if (usb_endpoint_xfer_bulk(&td->urb->ep->desc)) - xhci_dbg(xhci, "Successful bulk " - "transfer!\n"); - else - xhci_dbg(xhci, "Successful interrupt " - "transfer!\n"); - status = 0; - } - break; - case COMP_SHORT_TX: - if (td->urb->transfer_flags & URB_SHORT_NOT_OK) - status = -EREMOTEIO; - else - status = 0; - break; - default: - /* Others already handled above */ - break; - } - dev_dbg(&td->urb->dev->dev, - "ep %#x - asked for %d bytes, " - "%d bytes untransferred\n", - td->urb->ep->desc.bEndpointAddress, - td->urb->transfer_buffer_length, - TRB_LEN(event->transfer_len)); - /* Fast path - was this the last TRB in the TD for this URB? */ - if (event_trb == td->last_trb) { - if (TRB_LEN(event->transfer_len) != 0) { - td->urb->actual_length = - td->urb->transfer_buffer_length - - TRB_LEN(event->transfer_len); - if (td->urb->transfer_buffer_length < - td->urb->actual_length) { - xhci_warn(xhci, "HC gave bad length " - "of %d bytes left\n", - TRB_LEN(event->transfer_len)); - td->urb->actual_length = 0; - if (td->urb->transfer_flags & - URB_SHORT_NOT_OK) - status = -EREMOTEIO; - else - status = 0; - } - /* Don't overwrite a previously set error code */ - if (status == -EINPROGRESS) { - if (td->urb->transfer_flags & URB_SHORT_NOT_OK) - status = -EREMOTEIO; - else - status = 0; - } - } else { - td->urb->actual_length = td->urb->transfer_buffer_length; - /* Ignore a short packet completion if the - * untransferred length was zero. - */ - if (status == -EREMOTEIO) - status = 0; - } - } else { - /* Slow path - walk the list, starting from the dequeue - * pointer, to get the actual length transferred. - */ - union xhci_trb *cur_trb; - struct xhci_segment *cur_seg; - - td->urb->actual_length = 0; - for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg; - cur_trb != event_trb; - next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { - if ((cur_trb->generic.field[3] & - TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && - (cur_trb->generic.field[3] & - TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) - td->urb->actual_length += - TRB_LEN(cur_trb->generic.field[2]); - } - /* If the ring didn't stop on a Link or No-op TRB, add - * in the actual bytes transferred from the Normal TRB - */ - if (trb_comp_code != COMP_STOP_INVAL) - td->urb->actual_length += - TRB_LEN(cur_trb->generic.field[2]) - - TRB_LEN(event->transfer_len); - } - } - - ret = finish_td(xhci, td, event_trb, event, ep, &status, false); + else + ret = process_bulk_intr_td(xhci, td, event_trb, event, ep, + &status); cleanup: inc_deq(xhci, xhci->event_ring, true); -- cgit v1.2.3-70-g09d2 From 7fec3253edeb62ab7fc1a82d246196e72e9afdac Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 22 Jul 2010 15:23:15 -0700 Subject: USB: xHCI: remove redundant print messages Remove redundant print messages in the interrupt context. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 83580cf794d..44730eca5fd 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1592,7 +1592,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, u32 trb_comp_code; int ret = 0; - xhci_dbg(xhci, "In %s\n", __func__); slot_id = TRB_TO_SLOT_ID(event->flags); xdev = xhci->devs[slot_id]; if (!xdev) { @@ -1614,7 +1613,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, event_dma = event->buffer; /* This TRB should be in the TD at the head of this ring's TD list */ - xhci_dbg(xhci, "%s - checking for list empty\n", __func__); if (list_empty(&ep_ring->td_list)) { xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", TRB_TO_SLOT_ID(event->flags), ep_index); @@ -1623,30 +1621,17 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_print_trb_offsets(xhci, (union xhci_trb *) event); goto cleanup; } - xhci_dbg(xhci, "%s - getting list entry\n", __func__); td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); /* Is this a TRB in the currently executing TD? */ - xhci_dbg(xhci, "%s - looking for TD\n", __func__); event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, td->last_trb, event_dma); - xhci_dbg(xhci, "%s - found event_seg = %p\n", __func__, event_seg); if (!event_seg) { /* HC is busted, give up! */ xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n"); return -ESHUTDOWN; } event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)]; - xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", - (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); - xhci_dbg(xhci, "Offset 0x00 (buffer lo) = 0x%x\n", - lower_32_bits(event->buffer)); - xhci_dbg(xhci, "Offset 0x04 (buffer hi) = 0x%x\n", - upper_32_bits(event->buffer)); - xhci_dbg(xhci, "Offset 0x08 (transfer length) = 0x%x\n", - (unsigned int) event->transfer_len); - xhci_dbg(xhci, "Offset 0x0C (flags) = 0x%x\n", - (unsigned int) event->flags); /* Look for common error cases */ trb_comp_code = GET_COMP_CODE(event->transfer_len); -- cgit v1.2.3-70-g09d2 From 986a92d44810cad915279fdc942e2fd2c2857499 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 22 Jul 2010 15:23:20 -0700 Subject: USB: xHCI: adds new cases to trb_comp_code switch This patch adds new cases to trb_comp_code switch, and moves the switch judgment ahead of fetching td. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 76 ++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 44730eca5fd..5bb12fed9d2 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1605,36 +1605,16 @@ static int handle_tx_event(struct xhci_hcd *xhci, ep = &xdev->eps[ep_index]; ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); - if (!ep_ring || (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) { + if (!ep_ring || + (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) { xhci_err(xhci, "ERROR Transfer event for disabled endpoint " "or incorrect stream ring\n"); return -ENODEV; } event_dma = event->buffer; - /* This TRB should be in the TD at the head of this ring's TD list */ - if (list_empty(&ep_ring->td_list)) { - xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", - TRB_TO_SLOT_ID(event->flags), ep_index); - xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", - (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); - xhci_print_trb_offsets(xhci, (union xhci_trb *) event); - goto cleanup; - } - td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); - - /* Is this a TRB in the currently executing TD? */ - event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, - td->last_trb, event_dma); - if (!event_seg) { - /* HC is busted, give up! */ - xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n"); - return -ESHUTDOWN; - } - event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)]; - - /* Look for common error cases */ trb_comp_code = GET_COMP_CODE(event->transfer_len); + /* Look for common error cases */ switch (trb_comp_code) { /* Skip codes that require special handling depending on * transfer type @@ -1670,14 +1650,62 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_warn(xhci, "WARN: HC couldn't access mem fast enough\n"); status = -ENOSR; break; + case COMP_BW_OVER: + xhci_warn(xhci, "WARN: bandwidth overrun event on endpoint\n"); + break; + case COMP_BUFF_OVER: + xhci_warn(xhci, "WARN: buffer overrun event on endpoint\n"); + break; + case COMP_UNDERRUN: + /* + * When the Isoch ring is empty, the xHC will generate + * a Ring Overrun Event for IN Isoch endpoint or Ring + * Underrun Event for OUT Isoch endpoint. + */ + xhci_dbg(xhci, "underrun event on endpoint\n"); + if (!list_empty(&ep_ring->td_list)) + xhci_dbg(xhci, "Underrun Event for slot %d ep %d " + "still with TDs queued?\n", + TRB_TO_SLOT_ID(event->flags), ep_index); + goto cleanup; + case COMP_OVERRUN: + xhci_dbg(xhci, "overrun event on endpoint\n"); + if (!list_empty(&ep_ring->td_list)) + xhci_dbg(xhci, "Overrun Event for slot %d ep %d " + "still with TDs queued?\n", + TRB_TO_SLOT_ID(event->flags), ep_index); + goto cleanup; default: if (xhci_is_vendor_info_code(xhci, trb_comp_code)) { status = 0; break; } - xhci_warn(xhci, "ERROR Unknown event condition, HC probably busted\n"); + xhci_warn(xhci, "ERROR Unknown event condition, HC probably " + "busted\n"); + goto cleanup; + } + + /* This TRB should be in the TD at the head of this ring's TD list */ + if (list_empty(&ep_ring->td_list)) { + xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", + TRB_TO_SLOT_ID(event->flags), ep_index); + xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", + (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); + xhci_print_trb_offsets(xhci, (union xhci_trb *) event); goto cleanup; } + td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); + + /* Is this a TRB in the currently executing TD? */ + event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, + td->last_trb, event_dma); + if (!event_seg) { + /* HC is busted, give up! */ + xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n"); + return -ESHUTDOWN; + } + event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)]; + /* Now update the urb's actual_length and give back to the core */ /* Was this a control transfer? */ if (usb_endpoint_xfer_control(&td->urb->ep->desc)) -- cgit v1.2.3-70-g09d2 From d18240db797ed749b511b8dc910c5dcf08be46d6 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 22 Jul 2010 15:23:25 -0700 Subject: USB: xHCI: Missed Service Error Event process This patch adds mechanism to process Missed Service Error Event. Sometimes the xHC is unable to process the isoc TDs in time, it will generate Missed Service Error Event. In this case some TDs on the ring are not processed and missed. When encounter a Missed Servce Error Event, set the skip flag of the ep, and process the missed TDs until reach the next processed TD, then clear the skip flag. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 1 + drivers/usb/host/xhci-ring.c | 158 +++++++++++++++++++++++++++++-------------- drivers/usb/host/xhci.h | 8 +++ 3 files changed, 117 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 6d8f7e32932..64d03680471 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1124,6 +1124,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, virt_dev->num_rings_cached--; xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring); } + virt_dev->eps[ep_index].skip = false; ep_ring = virt_dev->eps[ep_index].new_ring; ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 5bb12fed9d2..4c3501003b8 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1675,6 +1675,16 @@ static int handle_tx_event(struct xhci_hcd *xhci, "still with TDs queued?\n", TRB_TO_SLOT_ID(event->flags), ep_index); goto cleanup; + case COMP_MISSED_INT: + /* + * When encounter missed service error, one or more isoc tds + * may be missed by xHC. + * Set skip flag of the ep_ring; Complete the missed tds as + * short transfer when process the ep_ring next time. + */ + ep->skip = true; + xhci_dbg(xhci, "Miss service interval error, set skip flag\n"); + goto cleanup; default: if (xhci_is_vendor_info_code(xhci, trb_comp_code)) { status = 0; @@ -1685,60 +1695,108 @@ static int handle_tx_event(struct xhci_hcd *xhci, goto cleanup; } - /* This TRB should be in the TD at the head of this ring's TD list */ - if (list_empty(&ep_ring->td_list)) { - xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n", - TRB_TO_SLOT_ID(event->flags), ep_index); - xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", - (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); - xhci_print_trb_offsets(xhci, (union xhci_trb *) event); - goto cleanup; - } - td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); - - /* Is this a TRB in the currently executing TD? */ - event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, - td->last_trb, event_dma); - if (!event_seg) { - /* HC is busted, give up! */ - xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n"); - return -ESHUTDOWN; - } - event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)]; + do { + /* This TRB should be in the TD at the head of this ring's + * TD list. + */ + if (list_empty(&ep_ring->td_list)) { + xhci_warn(xhci, "WARN Event TRB for slot %d ep %d " + "with no TDs queued?\n", + TRB_TO_SLOT_ID(event->flags), ep_index); + xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", + (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); + xhci_print_trb_offsets(xhci, (union xhci_trb *) event); + if (ep->skip) { + ep->skip = false; + xhci_dbg(xhci, "td_list is empty while skip " + "flag set. Clear skip flag.\n"); + } + ret = 0; + goto cleanup; + } - /* Now update the urb's actual_length and give back to the core */ - /* Was this a control transfer? */ - if (usb_endpoint_xfer_control(&td->urb->ep->desc)) - ret = process_ctrl_td(xhci, td, event_trb, event, ep, - &status); - else - ret = process_bulk_intr_td(xhci, td, event_trb, event, ep, - &status); + td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); + /* Is this a TRB in the currently executing TD? */ + event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, + td->last_trb, event_dma); + if (event_seg && ep->skip) { + xhci_dbg(xhci, "Found td. Clear skip flag.\n"); + ep->skip = false; + } + if (!event_seg && + (!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) { + /* HC is busted, give up! */ + xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not " + "part of current TD\n"); + return -ESHUTDOWN; + } -cleanup: - inc_deq(xhci, xhci->event_ring, true); - xhci_set_hc_event_deq(xhci); + if (event_seg) { + event_trb = &event_seg->trbs[(event_dma - + event_seg->dma) / sizeof(*event_trb)]; + /* + * No-op TRB should not trigger interrupts. + * If event_trb is a no-op TRB, it means the + * corresponding TD has been cancelled. Just ignore + * the TD. + */ + if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK) + == TRB_TYPE(TRB_TR_NOOP)) { + xhci_dbg(xhci, "event_trb is a no-op TRB. " + "Skip it\n"); + goto cleanup; + } + } - /* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */ - if (ret) { - urb = td->urb; - /* Leave the TD around for the reset endpoint function to use - * (but only if it's not a control endpoint, since we already - * queued the Set TR dequeue pointer command for stalled - * control endpoints). + /* Now update the urb's actual_length and give back to + * the core */ - if (usb_endpoint_xfer_control(&urb->ep->desc) || - (trb_comp_code != COMP_STALL && - trb_comp_code != COMP_BABBLE)) - kfree(td); - - usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); - xhci_dbg(xhci, "Giveback URB %p, len = %d, status = %d\n", - urb, urb->actual_length, status); - spin_unlock(&xhci->lock); - usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status); - spin_lock(&xhci->lock); - } + if (usb_endpoint_xfer_control(&td->urb->ep->desc)) + ret = process_ctrl_td(xhci, td, event_trb, event, ep, + &status); + else + ret = process_bulk_intr_td(xhci, td, event_trb, event, + ep, &status); + +cleanup: + /* + * Do not update event ring dequeue pointer if ep->skip is set. + * Will roll back to continue process missed tds. + */ + if (trb_comp_code == COMP_MISSED_INT || !ep->skip) { + inc_deq(xhci, xhci->event_ring, true); + xhci_set_hc_event_deq(xhci); + } + + if (ret) { + urb = td->urb; + /* Leave the TD around for the reset endpoint function + * to use(but only if it's not a control endpoint, + * since we already queued the Set TR dequeue pointer + * command for stalled control endpoints). + */ + if (usb_endpoint_xfer_control(&urb->ep->desc) || + (trb_comp_code != COMP_STALL && + trb_comp_code != COMP_BABBLE)) + kfree(td); + + usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); + xhci_dbg(xhci, "Giveback URB %p, len = %d, " + "status = %d\n", + urb, urb->actual_length, status); + spin_unlock(&xhci->lock); + usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status); + spin_lock(&xhci->lock); + } + + /* + * If ep->skip is set, it means there are missed tds on the + * endpoint ring need to take care of. + * Process them as short transfer until reach the td pointed by + * the event. + */ + } while (ep->skip && trb_comp_code != COMP_MISSED_INT); + return 0; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 5bc03d1c2be..f4dfb26a65a 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -720,6 +720,14 @@ struct xhci_virt_ep { struct timer_list stop_cmd_timer; int stop_cmds_pending; struct xhci_hcd *xhci; + /* + * Sometimes the xHC can not process isochronous endpoint ring quickly + * enough, and it will miss some isoc tds on the ring and generate + * a Missed Service Error Event. + * Set skip flag when receive a Missed Service Error Event and + * process the missed tds on the endpoint ring. + */ + bool skip; }; struct xhci_virt_device { -- cgit v1.2.3-70-g09d2 From 8e51adccd4c4b9ffcd509d7f2afce0a906139f75 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 22 Jul 2010 15:23:31 -0700 Subject: USB: xHCI: Introduce urb_priv structure Add urb_priv data structure to xHCI driver. This structure allows multiple xhci TDs to be linked to one urb, which is essential for isochronous transfer. For non-isochronous urb, only one TD is needed for one urb; for isochronous urb, the TD number for the urb is equal to urb->number_of_packets. The length field of urb_priv indicates the number of TDs in the urb. The td_cnt field indicates the number of TDs already processed by xHC. When td_cnt matches length, the urb can be given back to usbcore. When an urb is dequeued or cancelled, add all the unprocessed TDs to the endpoint's cancelled_td_list. When process a cancelled TD, increase td_cnt field. When td_cnt equals urb_priv->length, giveback the cancelled urb. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 16 ++++++++ drivers/usb/host/xhci-ring.c | 91 ++++++++++++++++++++++++++++++-------------- drivers/usb/host/xhci.c | 45 +++++++++++++++++++--- drivers/usb/host/xhci.h | 7 ++++ 4 files changed, 125 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 64d03680471..44eeaa016f1 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1390,6 +1390,22 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, return command; } +void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv) +{ + int last; + + if (!urb_priv) + return; + + last = urb_priv->length - 1; + if (last >= 0) { + int i; + for (i = 0; i <= last; i++) + kfree(urb_priv->td[i]); + } + kfree(urb_priv); +} + void xhci_free_command(struct xhci_hcd *xhci, struct xhci_command *command) { diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 4c3501003b8..fa8c9355913 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -578,16 +578,24 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, struct xhci_td *cur_td, int status, char *adjective) { struct usb_hcd *hcd = xhci_to_hcd(xhci); + struct urb *urb; + struct urb_priv *urb_priv; - cur_td->urb->hcpriv = NULL; - usb_hcd_unlink_urb_from_ep(hcd, cur_td->urb); - xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, cur_td->urb); + urb = cur_td->urb; + urb_priv = urb->hcpriv; + urb_priv->td_cnt++; - spin_unlock(&xhci->lock); - usb_hcd_giveback_urb(hcd, cur_td->urb, status); - kfree(cur_td); - spin_lock(&xhci->lock); - xhci_dbg(xhci, "%s URB given back\n", adjective); + /* Only giveback urb when this is the last td in urb */ + if (urb_priv->td_cnt == urb_priv->length) { + usb_hcd_unlink_urb_from_ep(hcd, urb); + xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, urb); + + spin_unlock(&xhci->lock); + usb_hcd_giveback_urb(hcd, urb, status); + xhci_urb_free_priv(xhci, urb_priv); + spin_lock(&xhci->lock); + xhci_dbg(xhci, "%s URB given back\n", adjective); + } } /* @@ -1272,6 +1280,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, struct urb *urb = NULL; struct xhci_ep_ctx *ep_ctx; int ret = 0; + struct urb_priv *urb_priv; u32 trb_comp_code; slot_id = TRB_TO_SLOT_ID(event->flags); @@ -1325,6 +1334,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, td_cleanup: /* Clean up the endpoint's TD list */ urb = td->urb; + urb_priv = urb->hcpriv; /* Do one last check of the actual transfer length. * If the host controller said we transferred more data than @@ -1349,7 +1359,10 @@ td_cleanup: if (!list_empty(&td->cancelled_td_list)) list_del(&td->cancelled_td_list); - ret = 1; + urb_priv->td_cnt++; + /* Giveback the urb when all the tds are completed */ + if (urb_priv->td_cnt == urb_priv->length) + ret = 1; } return ret; @@ -1588,6 +1601,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, union xhci_trb *event_trb; struct urb *urb = NULL; int status = -EINPROGRESS; + struct urb_priv *urb_priv; struct xhci_ep_ctx *ep_ctx; u32 trb_comp_code; int ret = 0; @@ -1770,6 +1784,7 @@ cleanup: if (ret) { urb = td->urb; + urb_priv = urb->hcpriv; /* Leave the TD around for the reset endpoint function * to use(but only if it's not a control endpoint, * since we already queued the Set TR dequeue pointer @@ -1778,7 +1793,7 @@ cleanup: if (usb_endpoint_xfer_control(&urb->ep->desc) || (trb_comp_code != COMP_STALL && trb_comp_code != COMP_BABBLE)) - kfree(td); + xhci_urb_free_priv(xhci, urb_priv); usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb); xhci_dbg(xhci, "Giveback URB %p, len = %d, " @@ -1979,10 +1994,12 @@ static int prepare_transfer(struct xhci_hcd *xhci, unsigned int stream_id, unsigned int num_trbs, struct urb *urb, - struct xhci_td **td, + unsigned int td_index, gfp_t mem_flags) { int ret; + struct urb_priv *urb_priv; + struct xhci_td *td; struct xhci_ring *ep_ring; struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); @@ -1998,24 +2015,29 @@ static int prepare_transfer(struct xhci_hcd *xhci, num_trbs, mem_flags); if (ret) return ret; - *td = kzalloc(sizeof(struct xhci_td), mem_flags); - if (!*td) - return -ENOMEM; - INIT_LIST_HEAD(&(*td)->td_list); - INIT_LIST_HEAD(&(*td)->cancelled_td_list); - ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb); - if (unlikely(ret)) { - kfree(*td); - return ret; + urb_priv = urb->hcpriv; + td = urb_priv->td[td_index]; + + INIT_LIST_HEAD(&td->td_list); + INIT_LIST_HEAD(&td->cancelled_td_list); + + if (td_index == 0) { + ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb); + if (unlikely(ret)) { + xhci_urb_free_priv(xhci, urb_priv); + urb->hcpriv = NULL; + return ret; + } } - (*td)->urb = urb; - urb->hcpriv = (void *) (*td); + td->urb = urb; /* Add this TD to the tail of the endpoint ring's TD list */ - list_add_tail(&(*td)->td_list, &ep_ring->td_list); - (*td)->start_seg = ep_ring->enq_seg; - (*td)->first_trb = ep_ring->enqueue; + list_add_tail(&td->td_list, &ep_ring->td_list); + td->start_seg = ep_ring->enq_seg; + td->first_trb = ep_ring->enqueue; + + urb_priv->td[td_index] = td; return 0; } @@ -2154,6 +2176,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, { struct xhci_ring *ep_ring; unsigned int num_trbs; + struct urb_priv *urb_priv; struct xhci_td *td; struct scatterlist *sg; int num_sgs; @@ -2174,9 +2197,13 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, &td, mem_flags); + num_trbs, urb, 0, mem_flags); if (trb_buff_len < 0) return trb_buff_len; + + urb_priv = urb->hcpriv; + td = urb_priv->td[0]; + /* * Don't give the first TRB to the hardware (by toggling the cycle bit) * until we've finished creating all the other TRBs. The ring's cycle @@ -2297,6 +2324,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index) { struct xhci_ring *ep_ring; + struct urb_priv *urb_priv; struct xhci_td *td; int num_trbs; struct xhci_generic_trb *start_trb; @@ -2342,10 +2370,13 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, &td, mem_flags); + num_trbs, urb, 0, mem_flags); if (ret < 0) return ret; + urb_priv = urb->hcpriv; + td = urb_priv->td[0]; + /* * Don't give the first TRB to the hardware (by toggling the cycle bit) * until we've finished creating all the other TRBs. The ring's cycle @@ -2431,6 +2462,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct xhci_generic_trb *start_trb; int start_cycle; u32 field, length_field; + struct urb_priv *urb_priv; struct xhci_td *td; ep_ring = xhci_urb_to_transfer_ring(xhci, urb); @@ -2458,10 +2490,13 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, num_trbs++; ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, &td, mem_flags); + num_trbs, urb, 0, mem_flags); if (ret < 0) return ret; + urb_priv = urb->hcpriv; + td = urb_priv->td[0]; + /* * Don't give the first TRB to the hardware (by toggling the cycle bit) * until we've finished creating all the other TRBs. The ring's cycle diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 3106d22ae05..295a0a2063a 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -804,7 +804,8 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) unsigned long flags; int ret = 0; unsigned int slot_id, ep_index; - + struct urb_priv *urb_priv; + int size, i; if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0) return -EINVAL; @@ -824,6 +825,30 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) ret = -ESHUTDOWN; goto exit; } + + if (usb_endpoint_xfer_isoc(&urb->ep->desc)) + size = urb->number_of_packets; + else + size = 1; + + urb_priv = kzalloc(sizeof(struct urb_priv) + + size * sizeof(struct xhci_td *), mem_flags); + if (!urb_priv) + return -ENOMEM; + + for (i = 0; i < size; i++) { + urb_priv->td[i] = kzalloc(sizeof(struct xhci_td), mem_flags); + if (!urb_priv->td[i]) { + urb_priv->length = i; + xhci_urb_free_priv(xhci, urb_priv); + return -ENOMEM; + } + } + + urb_priv->length = size; + urb_priv->td_cnt = 0; + urb->hcpriv = urb_priv; + if (usb_endpoint_xfer_control(&urb->ep->desc)) { /* Check to see if the max packet size for the default control * endpoint changed during FS device enumeration @@ -877,6 +902,8 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) exit: return ret; dying: + xhci_urb_free_priv(xhci, urb_priv); + urb->hcpriv = NULL; xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for " "non-responsive xHCI host.\n", urb->ep->desc.bEndpointAddress, urb); @@ -918,9 +945,10 @@ dying: int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { unsigned long flags; - int ret; + int ret, i; u32 temp; struct xhci_hcd *xhci; + struct urb_priv *urb_priv; struct xhci_td *td; unsigned int ep_index; struct xhci_ring *ep_ring; @@ -935,12 +963,12 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) temp = xhci_readl(xhci, &xhci->op_regs->status); if (temp == 0xffffffff) { xhci_dbg(xhci, "HW died, freeing TD.\n"); - td = (struct xhci_td *) urb->hcpriv; + urb_priv = urb->hcpriv; usb_hcd_unlink_urb_from_ep(hcd, urb); spin_unlock_irqrestore(&xhci->lock, flags); usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, -ESHUTDOWN); - kfree(td); + xhci_urb_free_priv(xhci, urb_priv); return ret; } if (xhci->xhc_state & XHCI_STATE_DYING) { @@ -968,9 +996,14 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) xhci_dbg(xhci, "Endpoint ring:\n"); xhci_debug_ring(xhci, ep_ring); - td = (struct xhci_td *) urb->hcpriv; - list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list); + urb_priv = urb->hcpriv; + + for (i = urb_priv->td_cnt; i < urb_priv->length; i++) { + td = urb_priv->td[i]; + list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list); + } + /* Queue a stop endpoint command, but only if this is * the first cancellation to be handled. */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index f4dfb26a65a..ebf62082950 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1090,6 +1090,12 @@ struct xhci_scratchpad { dma_addr_t *sp_dma_buffers; }; +struct urb_priv { + int length; + int td_cnt; + struct xhci_td *td[0]; +}; + /* * Each segment table entry is 4*32bits long. 1K seems like an ok size: * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table, @@ -1347,6 +1353,7 @@ struct xhci_ring *xhci_stream_id_to_ring( struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, bool allocate_in_ctx, bool allocate_completion, gfp_t mem_flags); +void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv); void xhci_free_command(struct xhci_hcd *xhci, struct xhci_command *command); -- cgit v1.2.3-70-g09d2 From 04e51901dd44f40a5a385ced897f6bca87d5f40a Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 22 Jul 2010 15:23:39 -0700 Subject: USB: xHCI: Isochronous transfer implementation This patch implements isochronous urb enqueue and interrupt handler part. When an isochronous urb is passed to xHCI driver, first check the transfer ring to guarantee there is enough room for the whole urb. Then update the start_frame and interval field of the urb. Always assume URB_ISO_ASAP is set, and never use urb->start_frame as input. The number of isoc TDs is equal to urb->number_of_packets. One isoc TD is consumed every Interval. Each isoc TD consists of an Isoch TRB chained to zero or more Normal TRBs. Call prepare_transfer for each TD to do initialization; then calculate the number of TRBs needed for each TD. If the data required by an isoc TD is physically contiguous (not crosses a page boundary), then only one isoc TRB is needed; otherwise one or more additional normal TRB shall be chained to the isoc TRB by the host. Set TRB_IOC to the last TRB of each isoc TD. Do not ring endpoint doorbell to start xHC procession until all the TDs are inserted to the endpoint transer ring. In irq handler, update urb status and actual_length, increase urb_priv->td_cnt. When all the TDs are completed(td_cnt is equal to urb_priv->length), giveback the urb to usbcore. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 319 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.h | 5 + 2 files changed, 324 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index fa8c9355913..da3519e76e2 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1471,6 +1471,104 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, return finish_td(xhci, td, event_trb, event, ep, status, false); } +/* + * Process isochronous tds, update urb packet status and actual_length. + */ +static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, + union xhci_trb *event_trb, struct xhci_transfer_event *event, + struct xhci_virt_ep *ep, int *status) +{ + struct xhci_ring *ep_ring; + struct urb_priv *urb_priv; + int idx; + int len = 0; + int skip_td = 0; + union xhci_trb *cur_trb; + struct xhci_segment *cur_seg; + u32 trb_comp_code; + + ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + trb_comp_code = GET_COMP_CODE(event->transfer_len); + urb_priv = td->urb->hcpriv; + idx = urb_priv->td_cnt; + + if (ep->skip) { + /* The transfer is partly done */ + *status = -EXDEV; + td->urb->iso_frame_desc[idx].status = -EXDEV; + } else { + /* handle completion code */ + switch (trb_comp_code) { + case COMP_SUCCESS: + td->urb->iso_frame_desc[idx].status = 0; + xhci_dbg(xhci, "Successful isoc transfer!\n"); + break; + case COMP_SHORT_TX: + if (td->urb->transfer_flags & URB_SHORT_NOT_OK) + td->urb->iso_frame_desc[idx].status = + -EREMOTEIO; + else + td->urb->iso_frame_desc[idx].status = 0; + break; + case COMP_BW_OVER: + td->urb->iso_frame_desc[idx].status = -ECOMM; + skip_td = 1; + break; + case COMP_BUFF_OVER: + case COMP_BABBLE: + td->urb->iso_frame_desc[idx].status = -EOVERFLOW; + skip_td = 1; + break; + case COMP_STALL: + td->urb->iso_frame_desc[idx].status = -EPROTO; + skip_td = 1; + break; + case COMP_STOP: + case COMP_STOP_INVAL: + break; + default: + td->urb->iso_frame_desc[idx].status = -1; + break; + } + } + + /* calc actual length */ + if (ep->skip) { + td->urb->iso_frame_desc[idx].actual_length = 0; + return finish_td(xhci, td, event_trb, event, ep, status, true); + } + + if (trb_comp_code == COMP_SUCCESS || skip_td == 1) { + td->urb->iso_frame_desc[idx].actual_length = + td->urb->iso_frame_desc[idx].length; + td->urb->actual_length += + td->urb->iso_frame_desc[idx].length; + } else { + for (cur_trb = ep_ring->dequeue, + cur_seg = ep_ring->deq_seg; cur_trb != event_trb; + next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { + if ((cur_trb->generic.field[3] & + TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && + (cur_trb->generic.field[3] & + TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) + len += + TRB_LEN(cur_trb->generic.field[2]); + } + len += TRB_LEN(cur_trb->generic.field[2]) - + TRB_LEN(event->transfer_len); + + if (trb_comp_code != COMP_STOP_INVAL) { + td->urb->iso_frame_desc[idx].actual_length = len; + td->urb->actual_length += len; + } + } + + if ((idx == urb_priv->length - 1) && *status == -EINPROGRESS) + *status = 0; + + return finish_td(xhci, td, event_trb, event, ep, status, false); +} + /* * Process bulk and interrupt tds, update urb status and actual_length. */ @@ -1768,6 +1866,9 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (usb_endpoint_xfer_control(&td->urb->ep->desc)) ret = process_ctrl_td(xhci, td, event_trb, event, ep, &status); + else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc)) + ret = process_isoc_td(xhci, td, event_trb, event, ep, + &status); else ret = process_bulk_intr_td(xhci, td, event_trb, event, ep, &status); @@ -2553,6 +2654,224 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, return 0; } +static int count_isoc_trbs_needed(struct xhci_hcd *xhci, + struct urb *urb, int i) +{ + int num_trbs = 0; + u64 addr, td_len, running_total; + + addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset); + td_len = urb->iso_frame_desc[i].length; + + running_total = TRB_MAX_BUFF_SIZE - + (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1)); + if (running_total != 0) + num_trbs++; + + while (running_total < td_len) { + num_trbs++; + running_total += TRB_MAX_BUFF_SIZE; + } + + return num_trbs; +} + +/* This is for isoc transfer */ +static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + struct urb *urb, int slot_id, unsigned int ep_index) +{ + struct xhci_ring *ep_ring; + struct urb_priv *urb_priv; + struct xhci_td *td; + int num_tds, trbs_per_td; + struct xhci_generic_trb *start_trb; + bool first_trb; + int start_cycle; + u32 field, length_field; + int running_total, trb_buff_len, td_len, td_remain_len, ret; + u64 start_addr, addr; + int i, j; + + ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; + + num_tds = urb->number_of_packets; + if (num_tds < 1) { + xhci_dbg(xhci, "Isoc URB with zero packets?\n"); + return -EINVAL; + } + + if (!in_interrupt()) + dev_dbg(&urb->dev->dev, "ep %#x - urb len = %#x (%d)," + " addr = %#llx, num_tds = %d\n", + urb->ep->desc.bEndpointAddress, + urb->transfer_buffer_length, + urb->transfer_buffer_length, + (unsigned long long)urb->transfer_dma, + num_tds); + + start_addr = (u64) urb->transfer_dma; + start_trb = &ep_ring->enqueue->generic; + start_cycle = ep_ring->cycle_state; + + /* Queue the first TRB, even if it's zero-length */ + for (i = 0; i < num_tds; i++) { + first_trb = true; + + running_total = 0; + addr = start_addr + urb->iso_frame_desc[i].offset; + td_len = urb->iso_frame_desc[i].length; + td_remain_len = td_len; + + trbs_per_td = count_isoc_trbs_needed(xhci, urb, i); + + ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, + urb->stream_id, trbs_per_td, urb, i, mem_flags); + if (ret < 0) + return ret; + + urb_priv = urb->hcpriv; + td = urb_priv->td[i]; + + for (j = 0; j < trbs_per_td; j++) { + u32 remainder = 0; + field = 0; + + if (first_trb) { + /* Queue the isoc TRB */ + field |= TRB_TYPE(TRB_ISOC); + /* Assume URB_ISO_ASAP is set */ + field |= TRB_SIA; + if (i > 0) + field |= ep_ring->cycle_state; + first_trb = false; + } else { + /* Queue other normal TRBs */ + field |= TRB_TYPE(TRB_NORMAL); + field |= ep_ring->cycle_state; + } + + /* Chain all the TRBs together; clear the chain bit in + * the last TRB to indicate it's the last TRB in the + * chain. + */ + if (j < trbs_per_td - 1) { + field |= TRB_CHAIN; + } else { + td->last_trb = ep_ring->enqueue; + field |= TRB_IOC; + } + + /* Calculate TRB length */ + trb_buff_len = TRB_MAX_BUFF_SIZE - + (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1)); + if (trb_buff_len > td_remain_len) + trb_buff_len = td_remain_len; + + remainder = xhci_td_remainder(td_len - running_total); + length_field = TRB_LEN(trb_buff_len) | + remainder | + TRB_INTR_TARGET(0); + queue_trb(xhci, ep_ring, false, false, + lower_32_bits(addr), + upper_32_bits(addr), + length_field, + /* We always want to know if the TRB was short, + * or we won't get an event when it completes. + * (Unless we use event data TRBs, which are a + * waste of space and HC resources.) + */ + field | TRB_ISP); + running_total += trb_buff_len; + + addr += trb_buff_len; + td_remain_len -= trb_buff_len; + } + + /* Check TD length */ + if (running_total != td_len) { + xhci_err(xhci, "ISOC TD length unmatch\n"); + return -EINVAL; + } + } + + wmb(); + start_trb->field[3] |= start_cycle; + + ring_ep_doorbell(xhci, slot_id, ep_index, urb->stream_id); + return 0; +} + +/* + * Check transfer ring to guarantee there is enough room for the urb. + * Update ISO URB start_frame and interval. + * Update interval as xhci_queue_intr_tx does. Just use xhci frame_index to + * update the urb->start_frame by now. + * Always assume URB_ISO_ASAP set, and NEVER use urb->start_frame as input. + */ +int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, + struct urb *urb, int slot_id, unsigned int ep_index) +{ + struct xhci_virt_device *xdev; + struct xhci_ring *ep_ring; + struct xhci_ep_ctx *ep_ctx; + int start_frame; + int xhci_interval; + int ep_interval; + int num_tds, num_trbs, i; + int ret; + + xdev = xhci->devs[slot_id]; + ep_ring = xdev->eps[ep_index].ring; + ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); + + num_trbs = 0; + num_tds = urb->number_of_packets; + for (i = 0; i < num_tds; i++) + num_trbs += count_isoc_trbs_needed(xhci, urb, i); + + /* Check the ring to guarantee there is enough room for the whole urb. + * Do not insert any td of the urb to the ring if the check failed. + */ + ret = prepare_ring(xhci, ep_ring, ep_ctx->ep_info & EP_STATE_MASK, + num_trbs, mem_flags); + if (ret) + return ret; + + start_frame = xhci_readl(xhci, &xhci->run_regs->microframe_index); + start_frame &= 0x3fff; + + urb->start_frame = start_frame; + if (urb->dev->speed == USB_SPEED_LOW || + urb->dev->speed == USB_SPEED_FULL) + urb->start_frame >>= 3; + + xhci_interval = EP_INTERVAL_TO_UFRAMES(ep_ctx->ep_info); + ep_interval = urb->interval; + /* Convert to microframes */ + if (urb->dev->speed == USB_SPEED_LOW || + urb->dev->speed == USB_SPEED_FULL) + ep_interval *= 8; + /* FIXME change this to a warning and a suggestion to use the new API + * to set the polling interval (once the API is added). + */ + if (xhci_interval != ep_interval) { + if (!printk_ratelimit()) + dev_dbg(&urb->dev->dev, "Driver uses different interval" + " (%d microframe%s) than xHCI " + "(%d microframe%s)\n", + ep_interval, + ep_interval == 1 ? "" : "s", + xhci_interval, + xhci_interval == 1 ? "" : "s"); + urb->interval = xhci_interval; + /* Convert back to frames for LS/FS devices */ + if (urb->dev->speed == USB_SPEED_LOW || + urb->dev->speed == USB_SPEED_FULL) + urb->interval /= 8; + } + return xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); +} + /**** Command Ring Operations ****/ /* Generic function for queueing a command TRB on the command ring. diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ebf62082950..e1383d91468 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -919,6 +919,9 @@ struct xhci_event_cmd { /* Control transfer TRB specific fields */ #define TRB_DIR_IN (1<<16) +/* Isochronous TRB specific fields */ +#define TRB_SIA (1<<31) + struct xhci_generic_trb { u32 field[4]; }; @@ -1416,6 +1419,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index); int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index); +int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, + struct urb *urb, int slot_id, unsigned int ep_index); int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed); int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, -- cgit v1.2.3-70-g09d2 From a061a5a0b816de3b4711a2e96764bb3cd8df861e Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 22 Jul 2010 15:23:47 -0700 Subject: USB: xHCI: allocate bigger ring for isochronous endpoint Isochronous endpoint needs a bigger size of transfer ring. Isochronous URB consists of multiple packets, each packet needs a isoc td to carry, and there will be multiple trbs inserted to the ring at one time. One segment is too small for isochronous endpoints, and it will result in room_on_ring() check failure and the URB is failed to enqueue. Allocate bigger ring for isochronous endpoint. 8 segments should be enough. This will be replaced with dynamic ring expansion in the future. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 44eeaa016f1..7d60d1f4deb 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1112,8 +1112,18 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); /* Set up the endpoint ring */ - virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 1, true, mem_flags); + /* + * Isochronous endpoint ring needs bigger size because one isoc URB + * carries multiple packets and it will insert multiple tds to the + * ring. + * This should be replaced with dynamic ring resizing in the future. + */ + if (usb_endpoint_xfer_isoc(&ep->desc)) + virt_dev->eps[ep_index].new_ring = + xhci_ring_alloc(xhci, 8, true, mem_flags); + else + virt_dev->eps[ep_index].new_ring = + xhci_ring_alloc(xhci, 1, true, mem_flags); if (!virt_dev->eps[ep_index].new_ring) { /* Attempt to use the ring cache */ if (virt_dev->num_rings_cached == 0) -- cgit v1.2.3-70-g09d2 From 787f4e5adaabba01becd646818dbace2d7e6b386 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Thu, 22 Jul 2010 15:23:52 -0700 Subject: USB: xHCI: Isoc urb enqueue Enable isochronous urb enqueue. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 295a0a2063a..89ac4853409 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -897,7 +897,12 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) slot_id, ep_index); spin_unlock_irqrestore(&xhci->lock, flags); } else { - ret = -EINVAL; + spin_lock_irqsave(&xhci->lock, flags); + if (xhci->xhc_state & XHCI_STATE_DYING) + goto dying; + ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb, + slot_id, ep_index); + spin_unlock_irqrestore(&xhci->lock, flags); } exit: return ret; -- cgit v1.2.3-70-g09d2 From 8156d158efa6370a8183f47327f122edbb4f2cb6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 22 Jul 2010 11:58:47 +0300 Subject: usb: gadget: storage: strict coversion of 'ro' parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring a strict way to get the 'ro' parameter from the user. The patch followed by this one adds another boolean parameter. To be consistent Michał Nazarewicz proposed to use simple_strtol() in both cases (correspondend discussion in LKML [1]). Due to simple_strtol() doesn't return error in a good way and we have a boolean parameter the strict_strtoul() is used. [1] http://lkml.org/lkml/2010/7/14/169 Signed-off-by: Andy Shevchenko Acked-by: Alan Stern Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/storage_common.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index a10faecfabc..3bbddab72e5 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -748,9 +748,9 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, ssize_t rc = count; struct fsg_lun *curlun = fsg_lun_from_dev(dev); struct rw_semaphore *filesem = dev_get_drvdata(dev); - int i; + unsigned long ro; - if (sscanf(buf, "%d", &i) != 1) + if (strict_strtoul(buf, 2, &ro)) return -EINVAL; /* @@ -762,8 +762,8 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, LDBG(curlun, "read-only status change prevented\n"); rc = -EBUSY; } else { - curlun->ro = !!i; - curlun->initially_ro = !!i; + curlun->ro = ro; + curlun->initially_ro = ro; LDBG(curlun, "read-only status set to %d\n", curlun->ro); } up_read(filesem); -- cgit v1.2.3-70-g09d2 From a93917d39fc388c4761d2530af82513e2d3bf9f6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 22 Jul 2010 17:53:56 +0300 Subject: USB: gadget: storage: optional SCSI WRITE FUA bit MS Windows mounts removable storage in "Removal optimized mode" by default. All the writes to the media are synchronous which is achieved by setting FUA (Force Unit Access) bit in SCSI WRITE(10,12) commands. This prevents I/O requests aggregation in block layer dramatically decreasing performance. This patch brings an option to accept or ignore mentioned bit a) via specifying module parameter "nofua", or b) through sysfs entry /sys/devices/platform/_UDC_/gadget/gadget-lunX/nofua (_UDC_ is the name of the USB Device Controller driver) Patch is based on the work that was done by Denis Karpov for Maemo 5 platform. Signed-off-by: Andy Shevchenko Acked-by: Alan Stern Cc: Denis Karpov Cc: Adrian Hunter Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- .../testing/sysfs-devices-platform-_UDC_-gadget | 12 +++++++++ drivers/usb/gadget/file_storage.c | 31 +++++++++++++++++----- drivers/usb/gadget/storage_common.c | 28 +++++++++++++++++++ 3 files changed, 64 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget b/Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget index 34034027b13..d548eaac230 100644 --- a/Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget +++ b/Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget @@ -7,3 +7,15 @@ Description: 0 -> resumed (_UDC_ is the name of the USB Device Controller driver) + +What: /sys/devices/platform/_UDC_/gadget/gadget-lunX/nofua +Date: July 2010 +Contact: Andy Shevchenko +Description: + Show or set the reaction on the FUA (Force Unit Access) bit in + the SCSI WRITE(10,12) commands when a gadget in USB Mass + Storage mode. + + Possible values are: + 1 -> ignore the FUA flag + 0 -> obey the FUA flag diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index d57c09f764d..88e5ad2bc71 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -93,6 +93,8 @@ * removable Default false, boolean for removable media * luns=N Default N = number of filenames, number of * LUNs to support + * nofua=b[,b...] Default false, booleans for ignore FUA flag + * in SCSI WRITE(10,12) commands * stall Default determined according to the type of * USB device controller (usually true), * boolean to permit the driver to halt @@ -112,12 +114,12 @@ * PAGE_CACHE_SIZE) * * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro", - * "removable", "luns", "stall", and "cdrom" options are available; default - * values are used for everything else. + * "removable", "luns", "nofua", "stall", and "cdrom" options are available; + * default values are used for everything else. * * The pathnames of the backing files and the ro settings are available in - * the attribute files "file" and "ro" in the lun subdirectory of the - * gadget's sysfs directory. If the "removable" option is set, writing to + * the attribute files "file", "nofua", and "ro" in the lun subdirectory of + * the gadget's sysfs directory. If the "removable" option is set, writing to * these files will simulate ejecting/loading the medium (writing an empty * line means eject) and adjusting a write-enable tab. Changes to the ro * setting are not allowed when the medium is loaded or if CD-ROM emulation @@ -304,8 +306,10 @@ MODULE_LICENSE("Dual BSD/GPL"); static struct { char *file[FSG_MAX_LUNS]; int ro[FSG_MAX_LUNS]; + int nofua[FSG_MAX_LUNS]; unsigned int num_filenames; unsigned int num_ros; + unsigned int num_nofuas; unsigned int nluns; int removable; @@ -345,6 +349,10 @@ MODULE_PARM_DESC(file, "names of backing files or devices"); module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO); MODULE_PARM_DESC(ro, "true to force read-only"); +module_param_array_named(nofua, mod_data.nofua, bool, &mod_data.num_nofuas, + S_IRUGO); +MODULE_PARM_DESC(nofua, "true to ignore SCSI WRITE(10,12) FUA bit"); + module_param_named(luns, mod_data.nluns, uint, S_IRUGO); MODULE_PARM_DESC(luns, "number of LUNs"); @@ -1279,7 +1287,8 @@ static int do_write(struct fsg_dev *fsg) curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - if (fsg->cmnd[1] & 0x08) { // FUA + /* FUA */ + if (!curlun->nofua && (fsg->cmnd[1] & 0x08)) { spin_lock(&curlun->filp->f_lock); curlun->filp->f_flags |= O_DSYNC; spin_unlock(&curlun->filp->f_lock); @@ -3133,6 +3142,7 @@ static int fsg_main_thread(void *fsg_) /* The write permissions and store_xxx pointers are set in fsg_bind() */ static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL); +static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, NULL); static DEVICE_ATTR(file, 0444, fsg_show_file, NULL); @@ -3362,6 +3372,10 @@ static int __ref fsg_bind(struct usb_gadget *gadget) } } + /* Only for removable media? */ + dev_attr_nofua.attr.mode = 0644; + dev_attr_nofua.store = fsg_store_nofua; + /* Find out how many LUNs there should be */ i = mod_data.nluns; if (i == 0) @@ -3387,6 +3401,7 @@ static int __ref fsg_bind(struct usb_gadget *gadget) curlun->ro = mod_data.cdrom || mod_data.ro[i]; curlun->initially_ro = curlun->ro; curlun->removable = mod_data.removable; + curlun->nofua = mod_data.nofua[i]; curlun->dev.release = lun_release; curlun->dev.parent = &gadget->dev; curlun->dev.driver = &fsg_driver.driver; @@ -3400,6 +3415,8 @@ static int __ref fsg_bind(struct usb_gadget *gadget) } if ((rc = device_create_file(&curlun->dev, &dev_attr_ro)) != 0 || + (rc = device_create_file(&curlun->dev, + &dev_attr_nofua)) != 0 || (rc = device_create_file(&curlun->dev, &dev_attr_file)) != 0) { device_unregister(&curlun->dev); @@ -3525,8 +3542,8 @@ static int __ref fsg_bind(struct usb_gadget *gadget) if (IS_ERR(p)) p = NULL; } - LINFO(curlun, "ro=%d, file: %s\n", - curlun->ro, (p ? p : "(error)")); + LINFO(curlun, "ro=%d, nofua=%d, file: %s\n", + curlun->ro, curlun->nofua, (p ? p : "(error)")); } } kfree(pathbuf); diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 3bbddab72e5..484acfb1a7c 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -284,6 +284,7 @@ struct fsg_lun { unsigned int prevent_medium_removal:1; unsigned int registered:1; unsigned int info_valid:1; + unsigned int nofua:1; u32 sense_data; u32 sense_data_info; @@ -714,6 +715,14 @@ static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr, : curlun->initially_ro); } +static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return sprintf(buf, "%u\n", curlun->nofua); +} + static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, char *buf) { @@ -770,6 +779,25 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, return rc; } +static ssize_t fsg_store_nofua(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + unsigned long nofua; + + if (strict_strtoul(buf, 2, &nofua)) + return -EINVAL; + + /* Sync data when switching from async mode to sync */ + if (!nofua && curlun->nofua) + fsg_lun_fsync_sub(curlun); + + curlun->nofua = nofua; + + return count; +} + static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { -- cgit v1.2.3-70-g09d2 From 76be932af17ddebac1b4e3dd2565a0519eaa29ce Mon Sep 17 00:00:00 2001 From: Kulikov Vasiliy Date: Fri, 16 Jul 2010 20:15:06 +0400 Subject: usb: host: sl811-hcd: check kzalloc() result If kzalloc() fails exit with -ENOMEM. Signed-off-by: Kulikov Vasiliy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/sl811-hcd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index bcf9f0e809d..990f06b89ea 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -813,8 +813,11 @@ static int sl811h_urb_enqueue( #endif /* avoid all allocations within spinlocks */ - if (!hep->hcpriv) + if (!hep->hcpriv) { ep = kzalloc(sizeof *ep, mem_flags); + if (ep == NULL) + return -ENOMEM; + } spin_lock_irqsave(&sl811->lock, flags); -- cgit v1.2.3-70-g09d2 From 6d091ee761992f804a1dcd411836ad0abb71be10 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 19 Jul 2010 09:40:40 +0100 Subject: USB: s3c-hsotg: Increase TX fifo limit Up the FIFO size for the TX to 1024 entries, as this now seems to work with all the cores. This fixes a problem when using large packets on a core with MPS set to 512 can hang due to insufficient space for the writes. The hang arises due to getting the non-periodic FIFO empty IRQ but not being able to satisfy any requests since there is never enough space to write 512 bytes into the buffer. This means we end up with a stream of interrupt requests. It is easier to up the TX FIFO to fill the space we left for it than to try and fix the positions in the code where we should have limited the max-packet size to < TXFIFOSIZE, since the TXFIFOSIZE depends on how the TX FIFOs have been setup. Signed-off-by: Ben Dooks Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 26193eceb32..81f62da26f3 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -310,11 +310,11 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg) hsotg->regs + S3C_GNPTXFSIZ); */ - /* set FIFO sizes to 2048/0x1C0 */ + /* set FIFO sizes to 2048/1024 */ writel(2048, hsotg->regs + S3C_GRXFSIZ); writel(S3C_GNPTXFSIZ_NPTxFStAddr(2048) | - S3C_GNPTXFSIZ_NPTxFDep(0x1C0), + S3C_GNPTXFSIZ_NPTxFDep(1024), hsotg->regs + S3C_GNPTXFSIZ); /* arange all the rest of the TX FIFOs, as some versions of this -- cgit v1.2.3-70-g09d2 From 679f9b7c7c7d3c746792138e9d7d76578ef52c41 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 19 Jul 2010 09:40:41 +0100 Subject: USB: s3c-hsotg: The NPTX/PTX FIFO sizes are in words, not bytes Fix a problem where we have been underestimating the space available in the IN PTX/NPTX FIFOs by assuming that they where simply word aligned instead of in number-of-words. This means all length calculations need to be multiplied-by-4. Note, we do not change the information about fifo size or start addresses available to userspace as we assume the user can multiply by four easily and is already knows these values are in words. Signed-off-by: Ben Dooks Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 81f62da26f3..4a251458c61 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -505,6 +505,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, } can_write = S3C_GNPTXSTS_NPTxFSpcAvail_GET(gnptxsts); + can_write *= 4; /* fifo size is in 32bit quantities. */ } dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n", @@ -2732,7 +2733,7 @@ static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg, */ ptxfifo = readl(hsotg->regs + S3C_DPTXFSIZn(epnum)); - hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo); + hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo) * 4; /* if we're using dma, we need to set the next-endpoint pointer * to be something valid. -- cgit v1.2.3-70-g09d2 From e7a9ff54271bf0ddbf641e5a0bde3ebda35808be Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 19 Jul 2010 09:40:42 +0100 Subject: USB: s3c-hsotg: Avoid overwriting contents of perodic in 'fifo' In shared fifo mode (used on older SoCs) the periodic in fifo beahves much more like a packet buffer, discarding old data when writing new data. Avoid this by ensuring that we do not load new transactions in when there is data sitting already in the FIFO. Note, this may not be an observed bug, we are fixing the case that this may happen. Signed-off-by: Ben Dooks Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 4a251458c61..354fd456f8c 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -91,7 +91,9 @@ struct s3c_hsotg_req; * For periodic IN endpoints, we have fifo_size and fifo_load to try * and keep track of the amount of data in the periodic FIFO for each * of these as we don't have a status register that tells us how much - * is in each of them. + * is in each of them. (note, this may actually be useless information + * as in shared-fifo mode periodic in acts like a single-frame packet + * buffer than a fifo) */ struct s3c_hsotg_ep { struct usb_ep ep; @@ -474,6 +476,14 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, size_left = S3C_DxEPTSIZ_XferSize_GET(epsize); + /* if shared fifo, we cannot write anything until the + * previous data has been completely sent. + */ + if (hs_ep->fifo_load != 0) { + s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp); + return -ENOSPC; + } + dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n", __func__, size_left, hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size); -- cgit v1.2.3-70-g09d2 From b3864cedfb576e11d2f9274f14a24840d8b569c3 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 19 Jul 2010 09:40:43 +0100 Subject: USB: s3c-hsotg: Re-initialise all FIFOs on USB bus reset The USB documentation suggest that the FIFOs should be reset when a bus reset event happens. Use the s3c_hsotg_init_fifo() to ensure that the FIFO layout is correct and that the FIFOs are flushed before acknowledging the reset. Signed-off-by: Ben Dooks Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 354fd456f8c..9d32c9ff737 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2082,17 +2082,12 @@ irq_retry: kill_all_requests(hsotg, &hsotg->eps[0], -ECONNRESET, true); /* it seems after a reset we can end up with a situation - * where the TXFIFO still has data in it... try flushing - * it to remove anything that may still be in it. + * where the TXFIFO still has data in it... the docs + * suggest resetting all the fifos, so use the init_fifo + * code to relayout and flush the fifos. */ - if (1) { - writel(S3C_GRSTCTL_TxFNum(0) | S3C_GRSTCTL_TxFFlsh, - hsotg->regs + S3C_GRSTCTL); - - dev_info(hsotg->dev, "GNPTXSTS=%08x\n", - readl(hsotg->regs + S3C_GNPTXSTS)); - } + s3c_hsotg_init_fifo(hsotg); s3c_hsotg_enqueue_setup(hsotg); -- cgit v1.2.3-70-g09d2 From 10aebc772a10c95e30dff0779cb0f879b8f1554f Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 19 Jul 2010 09:40:44 +0100 Subject: USB: s3c-hsotg: Add initial detection and setup for dedicated FIFO mode Add support for the dedicated FIFO mode on newer SoCs such as the S5PV210 partly to improve support and to fix the bug where any non-EP0 IN endpoint requires its own FIFO allocation. To fix this, we ensure that any non-zero IN endpoint is given a TXFIFO using the same allocation method as the periodic case (all our current hardware has enough FIFOs and FIFO memory for a 1:1 mapping) and ensure that the necessary transmission done interrupt is enabled. The default settings from reset for the core point all EPs at FIFO0, used for the control endpoint. However, the controller documentation states that all IN endpoints _must_ have a unique FIFO to avoid any contention during transmission. Note, this leaves us with a large IN FIFO for EP0 (which re-uses the old NPTXFIFO) for an endpoint which cannot shift more than a pair of packets at a time... this is a waste, but it looks like we cannot re-allocate space to the individual IN FIFOs as they are already maxed out (to be confirmed). Signed-off-by: Ben Dooks Signed-off-by: Greg Kroah-Hartman --- .../arm/plat-samsung/include/plat/regs-usb-hsotg.h | 2 ++ drivers/usb/gadget/s3c-hsotg.c | 40 ++++++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/arch/arm/plat-samsung/include/plat/regs-usb-hsotg.h b/arch/arm/plat-samsung/include/plat/regs-usb-hsotg.h index 8d18d9d4d14..dc90f5ede88 100644 --- a/arch/arm/plat-samsung/include/plat/regs-usb-hsotg.h +++ b/arch/arm/plat-samsung/include/plat/regs-usb-hsotg.h @@ -226,6 +226,7 @@ #define S3C_DIEPMSK S3C_HSOTG_REG(0x810) +#define S3C_DIEPMSK_TxFIFOEmpty (1 << 7) #define S3C_DIEPMSK_INEPNakEffMsk (1 << 6) #define S3C_DIEPMSK_INTknEPMisMsk (1 << 5) #define S3C_DIEPMSK_INTknTXFEmpMsk (1 << 4) @@ -371,6 +372,7 @@ #define S3C_DIEPDMA(_a) S3C_HSOTG_REG(0x914 + ((_a) * 0x20)) #define S3C_DOEPDMA(_a) S3C_HSOTG_REG(0xB14 + ((_a) * 0x20)) +#define S3C_DTXFSTS(_a) S3C_HSOTG_REG(0x918 + ((_a) * 0x20)) #define S3C_EPFIFO(_a) S3C_HSOTG_REG(0x1000 + ((_a) * 0x1000)) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 9d32c9ff737..4196e376a34 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -12,6 +12,8 @@ * published by the Free Software Foundation. */ +#define DEBUG + #include #include #include @@ -130,6 +132,7 @@ struct s3c_hsotg_ep { * @regs: The memory area mapped for accessing registers. * @regs_res: The resource that was allocated when claiming register space. * @irq: The IRQ number we are using + * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos. * @debug_root: root directrory for debugfs. * @debug_file: main status file for debugfs. * @debug_fifo: FIFO status file for debugfs. @@ -148,6 +151,8 @@ struct s3c_hsotg { struct resource *regs_res; int irq; + unsigned int dedicated_fifos:1; + struct dentry *debug_root; struct dentry *debug_file; struct dentry *debug_fifo; @@ -466,7 +471,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, if (to_write == 0) return 0; - if (periodic) { + if (periodic && !hsotg->dedicated_fifos) { u32 epsize = readl(hsotg->regs + S3C_DIEPTSIZ(hs_ep->index)); int size_left; int size_done; @@ -504,6 +509,11 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp); return -ENOSPC; } + } else if (hsotg->dedicated_fifos && hs_ep->index != 0) { + can_write = readl(hsotg->regs + S3C_DTXFSTS(hs_ep->index)); + + can_write &= 0xffff; + can_write *= 4; } else { if (S3C_GNPTXSTS_NPTxQSpcAvail_GET(gnptxsts) == 0) { dev_dbg(hsotg->dev, @@ -1829,6 +1839,15 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, __func__, idx); clear |= S3C_DIEPMSK_INTknEPMisMsk; } + + /* FIFO has space or is empty (see GAHBCFG) */ + if (hsotg->dedicated_fifos && + ints & S3C_DIEPMSK_TxFIFOEmpty) { + dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n", + __func__, idx); + s3c_hsotg_trytx(hsotg, hs_ep); + clear |= S3C_DIEPMSK_TxFIFOEmpty; + } } writel(clear, hsotg->regs + epint_reg); @@ -2280,6 +2299,12 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, break; } + /* if the hardware has dedicated fifos, we must give each IN EP + * a unique tx-fifo even if it is non-periodic. + */ + if (dir_in && hsotg->dedicated_fifos) + epctrl |= S3C_DxEPCTL_TxFNum(index); + /* for non control endpoints, set PID to D0 */ if (index) epctrl |= S3C_DxEPCTL_SetD0PID; @@ -2569,7 +2594,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk | S3C_DIEPMSK_INTknEPMisMsk | - S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk, + S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk | + ((hsotg->dedicated_fifos) ? S3C_DIEPMSK_TxFIFOEmpty : 0), hsotg->regs + S3C_DIEPMSK); /* don't need XferCompl, we get that from RXFIFO in slave mode. In @@ -2778,6 +2804,8 @@ static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg) static void s3c_hsotg_init(struct s3c_hsotg *hsotg) { + u32 cfg4; + /* unmask subset of endpoint interrupts */ writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk | @@ -2813,6 +2841,14 @@ static void s3c_hsotg_init(struct s3c_hsotg *hsotg) writel(using_dma(hsotg) ? S3C_GAHBCFG_DMAEn : 0x0, hsotg->regs + S3C_GAHBCFG); + + /* check hardware configuration */ + + cfg4 = readl(hsotg->regs + 0x50); + hsotg->dedicated_fifos = (cfg4 >> 25) & 1; + + dev_info(hsotg->dev, "%s fifos\n", + hsotg->dedicated_fifos ? "dedicated" : "shared"); } static void s3c_hsotg_dump(struct s3c_hsotg *hsotg) -- cgit v1.2.3-70-g09d2 From 03e10e5ab5ba6511ddaf80085cf08c62e9336fa5 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 19 Jul 2010 09:40:45 +0100 Subject: USB: s3c-hsotg: Only load packet per fifo write Limit the IN FIFO write to a single packet per attempt at writing, as per the specifications and ensure that we don't return fifo-full so that we can continue writing packets if we have the space. Signed-off-by: Ben Dooks Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 4196e376a34..df6a39d6270 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -538,6 +538,17 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, if (can_write > 512) can_write = 512; + /* limit the write to one max-packet size worth of data, but allow + * the transfer to return that it did not run out of fifo space + * doing it. */ + if (to_write > hs_ep->ep.maxpacket) { + to_write = hs_ep->ep.maxpacket; + + s3c_hsotg_en_gsint(hsotg, + periodic ? S3C_GINTSTS_PTxFEmp : + S3C_GINTSTS_NPTxFEmp); + } + /* see if we can write data */ if (to_write > can_write) { -- cgit v1.2.3-70-g09d2 From c9a64ea884b8b40d70077ffe1e93081f2190f072 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 19 Jul 2010 09:40:46 +0100 Subject: USB: s3c-hsotg: Check for new request before enqueing new setup Before trying a new setup transaction after getting an EP0 in complete interrupt, check that the driver did not try and send more EP0 IN data before enqueing a new setup transaction. This fixes a bug where we cannot send all of the IN data in one go so split the transfer, but then fail to send all the data as we start waiting for a new OUT transaction Signed-off-by: Ben Dooks Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index df6a39d6270..10aeee145ee 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -1790,7 +1790,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, if (dir_in) { s3c_hsotg_complete_in(hsotg, hs_ep); - if (idx == 0) + if (idx == 0 && !hs_ep->req) s3c_hsotg_enqueue_setup(hsotg); } else if (using_dma(hsotg)) { /* We're using DMA, we need to fire an OutDone here -- cgit v1.2.3-70-g09d2 From 6cd68de741d5b05482cab369978663fdf78c407c Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 19 Jul 2010 09:40:47 +0100 Subject: USB: s3c-hsotg: Fix max EP0 IN request length The maximum length for any EP0 IN request on EP0 is 127 bytes, not 128 as the driver currently has it. Signed-off-by: Ben Dooks Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 10aeee145ee..1020006ecfc 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -612,8 +612,7 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep) maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1; } else { if (hs_ep->dir_in) { - /* maxsize = S3C_DIEPTSIZ0_XferSize_LIMIT + 1; */ - maxsize = 64+64+1; + maxsize = 64+64; maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1; } else { maxsize = 0x3f; -- cgit v1.2.3-70-g09d2 From b05ca580c39314c8527e2e1c36a823970cc01683 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 19 Jul 2010 09:40:48 +0100 Subject: USB: s3c-hsotg: Fix the OUT EP0 limit The EP0 out limit is the same as the IN limit, so make them the same. Signed-off-by: Ben Dooks Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 1020006ecfc..552ec899500 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -611,11 +611,10 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep) maxsize = S3C_DxEPTSIZ_XferSize_LIMIT + 1; maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1; } else { + maxsize = 64+64; if (hs_ep->dir_in) { - maxsize = 64+64; maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1; } else { - maxsize = 0x3f; maxpkt = 2; } } -- cgit v1.2.3-70-g09d2 From a33e7136e9652374f7d54ded3cff8062d8e1e84f Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 19 Jul 2010 09:40:49 +0100 Subject: USB: s3c-hsotg: Fix OUT packet request retry If there is more data in the request than we could fit into a single hardware request, then check when the OutDone event is received if we have more data, and if so, schedule the new data instead of trying to complete the request (and in the case of EP0, sending a 0 packet in the middle of a transfer). Also, move the debug message about the current transfer state before the warning about a bad transfer. Signed-off-by: Ben Dooks Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 552ec899500..825b6ca6294 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -1383,6 +1383,9 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) read_ptr = hs_req->req.actual; max_req = hs_req->req.length - read_ptr; + dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n", + __func__, to_read, max_req, read_ptr, hs_req->req.length); + if (to_read > max_req) { /* more data appeared than we where willing * to deal with in this request. @@ -1392,9 +1395,6 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) WARN_ON_ONCE(1); } - dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n", - __func__, to_read, max_req, read_ptr, hs_req->req.length); - hs_ep->total_data += to_read; hs_req->req.actual += to_read; to_read = DIV_ROUND_UP(to_read, 4); @@ -1463,9 +1463,11 @@ static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg, static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg, int epnum, bool was_setup) { + u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum)); struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum]; struct s3c_hsotg_req *hs_req = hs_ep->req; struct usb_request *req = &hs_req->req; + unsigned size_left = S3C_DxEPTSIZ_XferSize_GET(epsize); int result = 0; if (!hs_req) { @@ -1474,9 +1476,7 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg, } if (using_dma(hsotg)) { - u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum)); unsigned size_done; - unsigned size_left; /* Calculate the size of the transfer by checking how much * is left in the endpoint size register and then working it @@ -1486,14 +1486,18 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg, * so may overshoot/undershoot the transfer. */ - size_left = S3C_DxEPTSIZ_XferSize_GET(epsize); - size_done = hs_ep->size_loaded - size_left; size_done += hs_ep->last_load; req->actual = size_done; } + /* if there is more request to do, schedule new transfer */ + if (req->actual < req->length && size_left == 0) { + s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true); + return; + } + if (req->actual < req->length && req->short_not_ok) { dev_dbg(hsotg->dev, "%s: got %d/%d (short not ok) => error\n", __func__, req->actual, req->length); -- cgit v1.2.3-70-g09d2 From e50bf385bfadeaacfb8af07b9b78dcfdef339981 Mon Sep 17 00:00:00 2001 From: Maurus Cuelenaere Date: Mon, 19 Jul 2010 09:40:50 +0100 Subject: USB: s3c-hsotg: Add support for external USB clock The PLL that drives the USB clock supports 3 input clocks: 12, 24 and 48Mhz. This patch adds support to the USB driver for setting the correct register bit according to the given clock. This depends on the following patch: [PATCH] ARM: S3C64XX: Add USB external clock definition Signed-off-by: Maurus Cuelenaere Signed-off-by: Ben Dooks Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 825b6ca6294..a4e0b0fa019 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -2798,6 +2799,7 @@ static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg, */ static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg) { + struct clk *xusbxti; u32 osc; writel(0, S3C_PHYPWR); @@ -2805,6 +2807,23 @@ static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg) osc = hsotg->plat->is_osc ? S3C_PHYCLK_EXT_OSC : 0; + xusbxti = clk_get(hsotg->dev, "xusbxti"); + if (xusbxti && !IS_ERR(xusbxti)) { + switch (clk_get_rate(xusbxti)) { + case 12*MHZ: + osc |= S3C_PHYCLK_CLKSEL_12M; + break; + case 24*MHZ: + osc |= S3C_PHYCLK_CLKSEL_24M; + break; + default: + case 48*MHZ: + /* default reference clock */ + break; + } + clk_put(xusbxti); + } + writel(osc | 0x10, S3C_PHYCLK); /* issue a full set of resets to the otg and core */ -- cgit v1.2.3-70-g09d2 From 4d47166c975382f5e95086e8a88d4a39d27b34b5 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 19 Jul 2010 16:01:40 +0200 Subject: USB: s3c-hsotg: fix compilation problem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/usb/gadget/s3c-hsotg.c: In function ‘s3c_hsotg_otgreset’: drivers/usb/gadget/s3c-hsotg.c:2816: error: ‘MHZ’ undeclared (first use in this function) drivers/usb/gadget/s3c-hsotg.c:2816: error: (Each undeclared identifier is reported only once drivers/usb/gadget/s3c-hsotg.c:2816: error: for each function it appears in.) Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index a4e0b0fa019..ce272b4d79c 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -36,6 +36,7 @@ #include #include #include +#include #define DMA_ADDR_INVALID (~((dma_addr_t)0)) -- cgit v1.2.3-70-g09d2 From 1eb838d3e2a473acbb9b21278e75b79640fb2c7b Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 19 Jul 2010 16:01:41 +0200 Subject: USB: s3c-hsotg: modify only selected bits in S3C_PHYPWR register S5PV210 SoCs has 2 USB PHY interfaces, both enabled by writing zero to S3C_PHYPWR register. HS/OTG driver uses only PHY0, so do not touch bits related to PHY1. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index ce272b4d79c..258ca01ac67 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2801,9 +2801,11 @@ static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg, static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg) { struct clk *xusbxti; - u32 osc; + u32 pwr, osc; - writel(0, S3C_PHYPWR); + pwr = readl(S3C_PHYPWR); + pwr &= ~0x19; + writel(pwr, S3C_PHYPWR); mdelay(1); osc = hsotg->plat->is_osc ? S3C_PHYCLK_EXT_OSC : 0; -- cgit v1.2.3-70-g09d2 From 31ee04de7e670de4199572595cce4aaa7f7f6351 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 19 Jul 2010 16:01:42 +0200 Subject: USB: s3c-hsotg: add support for clock gating This patch adds support for clock gating of the HS/OTG block. On S5PV210 otg gating clock is initally disabled so the driver needs to get and enable it before it can access its registers. Signed-off-by: Marek Szyprowski Signed-off-by: Kyungmin Park Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/s3c-hsotg.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 258ca01ac67..521ebed0118 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -152,6 +152,7 @@ struct s3c_hsotg { void __iomem *regs; struct resource *regs_res; int irq; + struct clk *clk; unsigned int dedicated_fifos:1; @@ -3258,13 +3259,20 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) hsotg->dev = dev; hsotg->plat = plat; + hsotg->clk = clk_get(&pdev->dev, "otg"); + if (IS_ERR(hsotg->clk)) { + dev_err(dev, "cannot get otg clock\n"); + ret = -EINVAL; + goto err_mem; + } + platform_set_drvdata(pdev, hsotg); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "cannot find register resource 0\n"); ret = -EINVAL; - goto err_mem; + goto err_clk; } hsotg->regs_res = request_mem_region(res->start, resource_size(res), @@ -3272,7 +3280,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) if (!hsotg->regs_res) { dev_err(dev, "cannot reserve registers\n"); ret = -ENOENT; - goto err_mem; + goto err_clk; } hsotg->regs = ioremap(res->start, resource_size(res)); @@ -3325,6 +3333,8 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) /* reset the system */ + clk_enable(hsotg->clk); + s3c_hsotg_gate(pdev, true); s3c_hsotg_otgreset(hsotg); @@ -3348,7 +3358,8 @@ err_regs: err_regs_res: release_resource(hsotg->regs_res); kfree(hsotg->regs_res); - +err_clk: + clk_put(hsotg->clk); err_mem: kfree(hsotg); return ret; @@ -3370,6 +3381,9 @@ static int __devexit s3c_hsotg_remove(struct platform_device *pdev) s3c_hsotg_gate(pdev, false); + clk_disable(hsotg->clk); + clk_put(hsotg->clk); + kfree(hsotg); return 0; } -- cgit v1.2.3-70-g09d2 From afad19648f70c6493193e0a774bd754b7790b4a0 Mon Sep 17 00:00:00 2001 From: "John G. Rogers" Date: Sat, 24 Jul 2010 09:50:52 -0400 Subject: USB: serial: enabling support for Segway RMP in ftdi_sio I have added the ProductID=0xe729 VendorID=FTDI_VID=0x0403 which will enable support for the Segway Robotic Mobility Platform (RMP200) in the ftdi_sio kernel module. Currently, users of the Segway RMP200 must use a RUN+="/sbin/modprobe -q ftdi-sio product=0xe729 vendor=0x0403 in a udev rule to get the ftdi_sio module to handle the usb interface and mount it on /dev/ttyXXX. This is not a good solution because some users will have multiple USB to Serial converters which will use the ftdi_sio module. Signed-off-by: John Rogers Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio_ids.h | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index e298dc4baed..a3671dbcdd4 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -746,6 +746,7 @@ static struct usb_device_id id_table_combined [] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH4_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, SEGWAY_RMP200_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index d01946db8fa..74f094273f7 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -1032,3 +1032,8 @@ #define XVERVE_SIGNALYZER_SH2_PID 0xBCA2 #define XVERVE_SIGNALYZER_SH4_PID 0xBCA4 +/* + * Segway Robotic Mobility Platform USB interface (using VID 0x0403) + * Submitted by John G. Rogers + */ +#define SEGWAY_RMP200_PID 0xe729 -- cgit v1.2.3-70-g09d2 From 9a4b7c3b14905a191da09980b9da966be5fc7fa2 Mon Sep 17 00:00:00 2001 From: Kulikov Vasiliy Date: Mon, 26 Jul 2010 12:26:22 +0400 Subject: usb: imx21-hcd: set task state with schedule_timeout_uninterruptible() imx21_hc_reset() uses schedule_timeout() without setting state to STATE_(UN)INTERRUPTIBLE. As it is called in cycle without checking of pending signals, use schedule_timeout_uninterruptible(). Signed-off-by: Kulikov Vasiliy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/imx21-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index ca0e98d8e1f..3e5630369c3 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -1521,7 +1521,7 @@ static int imx21_hc_reset(struct usb_hcd *hcd) return -ETIMEDOUT; } spin_unlock_irq(&imx21->lock); - schedule_timeout(1); + schedule_timeout_uninterruptible(1); spin_lock_irq(&imx21->lock); } spin_unlock_irqrestore(&imx21->lock, flags); -- cgit v1.2.3-70-g09d2 From 567064916e4fe32af81b89e473a1e4f40c64b27b Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Thu, 22 Jul 2010 14:16:37 +0200 Subject: USB: gadget: file_storage: serial parameter even if not test mode Moved the serial parameter handling code out of "#ifdef CONFIG_USB_FILE_STORAGE_TEST". This modifies Yann Cantin's commit "USB: Add a serial number parameter to g_file_storage" module as per Alan Stern's request. Signed-off-by: Michal Nazarewicz Signed-off-by: Kyungmin Park Acked-by: Alan Stern Tested-by: Anand Gadiyar Cc: David Brownell Cc: Yann Cantin --- drivers/usb/gadget/file_storage.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 88e5ad2bc71..a857b7ac238 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -321,7 +321,7 @@ static struct { unsigned short vendor; unsigned short product; unsigned short release; - char *serial_parm; + char *serial; unsigned int buflen; int transport_type; @@ -365,6 +365,8 @@ MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO); MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); +module_param_named(serial, mod_data.serial, charp, S_IRUGO); +MODULE_PARM_DESC(serial, "USB serial number"); /* In the non-TEST version, only the module parameters listed above * are available. */ @@ -386,9 +388,6 @@ MODULE_PARM_DESC(product, "USB Product ID"); module_param_named(release, mod_data.release, ushort, S_IRUGO); MODULE_PARM_DESC(release, "USB release number"); -module_param_named(serial, mod_data.serial_parm, charp, S_IRUGO); -MODULE_PARM_DESC(serial, "USB serial number"); - module_param_named(buflen, mod_data.buflen, uint, S_IRUGO); MODULE_PARM_DESC(buflen, "I/O buffer size"); @@ -3291,10 +3290,12 @@ static int __init check_parameters(struct fsg_dev *fsg) return -ETOOSMALL; } +#endif /* CONFIG_USB_FILE_STORAGE_TEST */ + /* Serial string handling. * On a real device, the serial string would be loaded * from permanent storage. */ - if (mod_data.serial_parm) { + if (mod_data.serial) { const char *ch; unsigned len = 0; @@ -3303,7 +3304,7 @@ static int __init check_parameters(struct fsg_dev *fsg) * 12 uppercase hexadecimal characters. * BBB need at least 12 uppercase hexadecimal characters, * with a maximum of 126. */ - for (ch = mod_data.serial_parm; *ch; ++ch) { + for (ch = mod_data.serial; *ch; ++ch) { ++len; if ((*ch < '0' || *ch > '9') && (*ch < 'A' || *ch > 'F')) { /* not uppercase hex */ @@ -3322,8 +3323,11 @@ static int __init check_parameters(struct fsg_dev *fsg) "Failing back to default\n"); goto fill_serial; } - fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial_parm; + fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial; } else { + WARNING(fsg, + "Userspace failed to provide serial number; " + "Failing back to default\n"); fill_serial: /* Serial number not specified or invalid, make our own. * We just encode it from the driver version string, @@ -3339,8 +3343,6 @@ fill_serial: } } -#endif /* CONFIG_USB_FILE_STORAGE_TEST */ - return 0; } -- cgit v1.2.3-70-g09d2 From 0372a754be9aa43e19fd86c9bc04796d43b55e38 Mon Sep 17 00:00:00 2001 From: Andrew Bird Date: Fri, 23 Jul 2010 16:04:41 +0100 Subject: USB: option: add huawei k3765 k4505 devices to work properly This patch adds the product IDs of Huawei's K3765 and K4505 mobile broadband usb modems to option.c. It also adds a quirk to the option probe function so that binding to the device's network interface(class 0xff) is avoided. This is necessary to allow another driver to bind to that, and to avoid programs like wvdial opening a nonfunctioning tty during modem discovery. Signed-off-by: Andrew Bird Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 051c00ce5d2..9fc6ea2c681 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -145,6 +145,8 @@ static void option_instat_callback(struct urb *urb); #define HUAWEI_PRODUCT_E143D 0x143D #define HUAWEI_PRODUCT_E143E 0x143E #define HUAWEI_PRODUCT_E143F 0x143F +#define HUAWEI_PRODUCT_K4505 0x1464 +#define HUAWEI_PRODUCT_K3765 0x1465 #define HUAWEI_PRODUCT_E14AC 0x14AC #define HUAWEI_PRODUCT_ETS1220 0x1803 @@ -480,6 +482,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, /* Novatel Merlin V640/XV620 */ @@ -1015,6 +1019,13 @@ static int option_probe(struct usb_serial *serial, serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff) return -ENODEV; + /* Don't bind network interfaces on Huawei K3765 & K4505 */ + if (serial->dev->descriptor.idVendor == HUAWEI_VENDOR_ID && + (serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K3765 || + serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4505) && + serial->interface->cur_altsetting->desc.bInterfaceNumber == 1) + return -ENODEV; + data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) -- cgit v1.2.3-70-g09d2 From c4e0b508bcdd1af6b1b3c317042336936173591f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 27 Jul 2010 11:28:42 -0400 Subject: USB: accept RNDIS configs if there's no alternative This patch (as1410) makes a slight change to the strategy used for choosing a default configuration. Currently we skip configs whose first interface is RNDIS, if the kernel wasn't built with the corresponding driver. This risks losing access to the other interfaces in those configs. In addition, if there is only one config then we will end up not configuring the device at all. This changes the logic; now such configurations will be skipped only if there is at least one other config. Signed-off-by: Alan Stern Tested-by: Adam Kropelin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/generic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 9a34ccb0a1c..69ecd3c9231 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -105,8 +105,10 @@ int usb_choose_configuration(struct usb_device *udev) /* When the first config's first interface is one of Microsoft's * pet nonstandard Ethernet-over-USB protocols, ignore it unless * this kernel has enabled the necessary host side driver. + * But: Don't ignore it if it's the only config. */ - if (i == 0 && desc && (is_rndis(desc) || is_activesync(desc))) { + if (i == 0 && num_configs > 1 && desc && + (is_rndis(desc) || is_activesync(desc))) { #if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE) continue; #else -- cgit v1.2.3-70-g09d2 From b6180ef7c99574c3350bbffa2a3a9d675321543d Mon Sep 17 00:00:00 2001 From: "dranch@trinnet.net" Date: Mon, 26 Jul 2010 19:44:33 -0700 Subject: USB: ftdi_sio: device id for Navitator This patch is to add a US Interface, Inc. "Navigator" USB device. Specifically, it's a HAM Radio USB sound modem that also incorporates three pairs of unique FTDI serial ports. The standard Linux FTDI serial driver will only recognize the first two serial ports of an unknown FDTI derived device and this patch adds in recognition to these specific new IDs. Signed-off-by: David A. Ranch Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 3 +++ drivers/usb/serial/ftdi_sio_ids.h | 5 +++++ 2 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index a3671dbcdd4..eb12d9b096b 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -157,6 +157,9 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_5_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_6_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_7_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_USINT_CAT_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_USINT_WKEY_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_USINT_RS232_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ACTZWAVE_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 74f094273f7..6e612c52e76 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -40,6 +40,11 @@ #define FTDI_NXTCAM_PID 0xABB8 /* NXTCam for Mindstorms NXT */ +/* US Interface Navigator (http://www.usinterface.com/) */ +#define FTDI_USINT_CAT_PID 0xb810 /* Navigator CAT and 2nd PTT lines */ +#define FTDI_USINT_WKEY_PID 0xb811 /* Navigator WKEY and FSK lines */ +#define FTDI_USINT_RS232_PID 0xb812 /* Navigator RS232 and CONFIG lines */ + /* OOCDlink by Joern Kaipf * (http://www.joernonline.de/dw/doku.php?id=start&idx=projects:oocdlink) */ #define FTDI_OOCDLINK_PID 0xbaf8 /* Amontec JTAGkey */ -- cgit v1.2.3-70-g09d2 From 356c5a4834a74c621715f7a7f16ded914eecbd3c Mon Sep 17 00:00:00 2001 From: Alessio Igor Bogani Date: Tue, 27 Jul 2010 23:05:14 +0200 Subject: USB: cp210x: Add four new device IDs Signed-off-by: Alessio Igor Bogani Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 8b8c7976b4c..2bef4415c19 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -126,6 +126,10 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ + { USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */ + { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */ + { USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */ + { USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */ { } /* Terminating Entry */ }; -- cgit v1.2.3-70-g09d2 From f2402f21caba2aceebbf637458b777300669020f Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 28 Jul 2010 22:11:35 +0200 Subject: USB: Add USB 2.0 to ssb ohci driver This adds USB 2.0 support to ssb ohci driver. This patch was used in OpenWRT for a long time now. CC: Steve Brown Signed-off-by: Hauke Mehrtens Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-ssb.c | 52 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c index 23fd6a886bd..48ee6943bf3 100644 --- a/drivers/usb/host/ohci-ssb.c +++ b/drivers/usb/host/ohci-ssb.c @@ -93,8 +93,11 @@ static void ssb_ohci_detach(struct ssb_device *dev) { struct usb_hcd *hcd = ssb_get_drvdata(dev); + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); usb_remove_hcd(hcd); iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); ssb_device_disable(dev, 0); } @@ -106,10 +109,52 @@ static int ssb_ohci_attach(struct ssb_device *dev) int err = -ENOMEM; u32 tmp, flags = 0; - if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) - flags |= SSB_OHCI_TMSLOW_HOSTMODE; + if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) || + dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32))) + return -EOPNOTSUPP; - ssb_device_enable(dev, flags); + if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) { + /* Put the device into host-mode. */ + flags |= SSB_OHCI_TMSLOW_HOSTMODE; + ssb_device_enable(dev, flags); + } else if (dev->id.coreid == SSB_DEV_USB20_HOST) { + /* + * USB 2.0 special considerations: + * + * In addition to the standard SSB reset sequence, the Host + * Control Register must be programmed to bring the USB core + * and various phy components out of reset. + */ + ssb_device_enable(dev, 0); + ssb_write32(dev, 0x200, 0x7ff); + + /* Change Flush control reg */ + tmp = ssb_read32(dev, 0x400); + tmp &= ~8; + ssb_write32(dev, 0x400, tmp); + tmp = ssb_read32(dev, 0x400); + + /* Change Shim control reg */ + tmp = ssb_read32(dev, 0x304); + tmp &= ~0x100; + ssb_write32(dev, 0x304, tmp); + tmp = ssb_read32(dev, 0x304); + + udelay(1); + + /* Work around for 5354 failures */ + if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) { + /* Change syn01 reg */ + tmp = 0x00fe00fe; + ssb_write32(dev, 0x894, tmp); + + /* Change syn03 reg */ + tmp = ssb_read32(dev, 0x89c); + tmp |= 0x1; + ssb_write32(dev, 0x89c, tmp); + } + } else + ssb_device_enable(dev, 0); hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev, dev_name(dev->dev)); @@ -200,6 +245,7 @@ static int ssb_ohci_resume(struct ssb_device *dev) static const struct ssb_device_id ssb_ohci_table[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV), SSB_DEVTABLE_END }; MODULE_DEVICE_TABLE(ssb, ssb_ohci_table); -- cgit v1.2.3-70-g09d2 From 185c9bcfde628f1d71aefd34517252cce4c4a57a Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 28 Jul 2010 22:33:28 +0800 Subject: USB: ehci: fix remove of ehci debugfs dir The patch below on gregkh tree only creates 'lpm' file under ehci->debug_dir, but not removes it when unloading module, USB: EHCI: EHCI 1.1 addendum: preparation which can make loading of ehci-hcd module failed after unloading it. This patch replaces debugfs_remove with debugfs_remove_recursive to remove ehci debugfs dir and files. It does fix the bug above, and may simplify the removing procedure. Also, remove the debug_registers, debug_async and debug_periodic field from ehci_hcd struct since they are useless now. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-dbg.c | 58 ++++++++++++++++----------------------------- drivers/usb/host/ehci.h | 4 ---- 2 files changed, 21 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 4498efb49b9..76b7fd2d838 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -1049,49 +1049,33 @@ static inline void create_debug_files (struct ehci_hcd *ehci) ehci->debug_dir = debugfs_create_dir(bus->bus_name, ehci_debug_root); if (!ehci->debug_dir) - goto dir_error; - - ehci->debug_async = debugfs_create_file("async", S_IRUGO, - ehci->debug_dir, bus, - &debug_async_fops); - if (!ehci->debug_async) - goto async_error; - - ehci->debug_periodic = debugfs_create_file("periodic", S_IRUGO, - ehci->debug_dir, bus, - &debug_periodic_fops); - if (!ehci->debug_periodic) - goto periodic_error; - - ehci->debug_registers = debugfs_create_file("registers", S_IRUGO, - ehci->debug_dir, bus, - &debug_registers_fops); - - ehci->debug_registers = debugfs_create_file("lpm", S_IRUGO|S_IWUGO, - ehci->debug_dir, bus, - &debug_lpm_fops); - if (!ehci->debug_registers) - goto registers_error; + return; + + if (!debugfs_create_file("async", S_IRUGO, ehci->debug_dir, bus, + &debug_async_fops)) + goto file_error; + + if (!debugfs_create_file("periodic", S_IRUGO, ehci->debug_dir, bus, + &debug_periodic_fops)) + goto file_error; + + if (!debugfs_create_file("registers", S_IRUGO, ehci->debug_dir, bus, + &debug_registers_fops)) + goto file_error; + + if (!debugfs_create_file("lpm", S_IRUGO|S_IWUGO, ehci->debug_dir, bus, + &debug_lpm_fops)) + goto file_error; + return; -registers_error: - debugfs_remove(ehci->debug_periodic); -periodic_error: - debugfs_remove(ehci->debug_async); -async_error: - debugfs_remove(ehci->debug_dir); -dir_error: - ehci->debug_periodic = NULL; - ehci->debug_async = NULL; - ehci->debug_dir = NULL; +file_error: + debugfs_remove_recursive(ehci->debug_dir); } static inline void remove_debug_files (struct ehci_hcd *ehci) { - debugfs_remove(ehci->debug_registers); - debugfs_remove(ehci->debug_periodic); - debugfs_remove(ehci->debug_async); - debugfs_remove(ehci->debug_dir); + debugfs_remove_recursive(ehci->debug_dir); } #endif /* STUB_DEBUG_FILES */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index e5b9ece8a07..bde823f704e 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -156,10 +156,6 @@ struct ehci_hcd { /* one per controller */ /* debug files */ #ifdef DEBUG struct dentry *debug_dir; - struct dentry *debug_async; - struct dentry *debug_periodic; - struct dentry *debug_registers; - struct dentry *debug_lpm; #endif }; -- cgit v1.2.3-70-g09d2 From f283925fe9ef4ee75dc43e4c2bfbbd6b8a70bd0a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 28 Jul 2010 17:12:39 -0400 Subject: USB: usb-storage: implement autosuspend This patch (as1400) adds runtime-PM support to usb-storage. It utilizes the SCSI layer's runtime-PM implementation, so its scope is limited. Currently the only effect is that disk-like devices (such as card readers or flash drives) will be autosuspended if they aren't mounted and their device files aren't open. This would apply, for example, to card readers that don't contain a memory card. Unfortunately this won't interact very well with the removable-media polling normally carried out by hal or DeviceKit. Maybe those programs can be changed to use a longer polling interval, or maybe the default autosuspend time for usb-storage should be set to something below 1 second. Signed-off-by: Alan Stern Cc: James Bottomley Cc: Matthew Dharm Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/usb.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index a7d0bf9d92a..90bb0175a15 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -336,6 +336,7 @@ static int usb_stor_control_thread(void * __us) else { US_DEBUG(usb_stor_show_command(us->srb)); us->proto_handler(us->srb, us); + usb_mark_last_busy(us->pusb_dev); } /* lock access to the state */ @@ -845,6 +846,7 @@ static int usb_stor_scan_thread(void * __us) /* Should we unbind if no devices were detected? */ } + usb_autopm_put_interface(us->pusb_intf); complete_and_exit(&us->scanning_done, 0); } @@ -968,6 +970,7 @@ int usb_stor_probe2(struct us_data *us) goto BadDevice; } + usb_autopm_get_interface_no_resume(us->pusb_intf); wake_up_process(th); return 0; @@ -1040,6 +1043,7 @@ static struct usb_driver usb_storage_driver = { .pre_reset = usb_stor_pre_reset, .post_reset = usb_stor_post_reset, .id_table = usb_storage_usb_ids, + .supports_autosuspend = 1, .soft_unbind = 1, }; -- cgit v1.2.3-70-g09d2 From 52af954599396e5945a895035525c703f2761b20 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Thu, 29 Jul 2010 11:05:41 -0400 Subject: USB: add USB serial ssu100 driver Add support for the Quatech SSU-100 single port usb to serial device. This driver is based on the ftdi_sio.c driver and the original serqt_usb driver from Quatech. Signed-off-by: Bill Pemberton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/Kconfig | 9 + drivers/usb/serial/Makefile | 1 + drivers/usb/serial/ssu100.c | 698 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 708 insertions(+) create mode 100644 drivers/usb/serial/ssu100.c (limited to 'drivers') diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index bd8aab0ef1c..916b2b6d765 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -642,6 +642,15 @@ config USB_SERIAL_ZIO To compile this driver as a module, choose M here: the module will be called zio. +config USB_SERIAL_SSU100 + tristate "USB Quatech SSU-100 Single Port Serial Driver" + help + Say Y here if you want to use the Quatech SSU-100 single + port usb to serial adapter. + + To compile this driver as a module, choose M here: the + module will be called ssu100. + config USB_SERIAL_DEBUG tristate "USB Debugging Device" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index e54c728c016..40ebe17b6ea 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o +obj-$(CONFIG_USB_SERIAL_SSU100) += ssu100.o obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c new file mode 100644 index 00000000000..6e82d4f54bc --- /dev/null +++ b/drivers/usb/serial/ssu100.c @@ -0,0 +1,698 @@ +/* + * usb-serial driver for Quatech SSU-100 + * + * based on ftdi_sio.c and the original serqt_usb.c from Quatech + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QT_OPEN_CLOSE_CHANNEL 0xca +#define QT_SET_GET_DEVICE 0xc2 +#define QT_SET_GET_REGISTER 0xc0 +#define QT_GET_SET_PREBUF_TRIG_LVL 0xcc +#define QT_SET_ATF 0xcd +#define QT_GET_SET_UART 0xc1 +#define QT_TRANSFER_IN 0xc0 +#define QT_HW_FLOW_CONTROL_MASK 0xc5 +#define QT_SW_FLOW_CONTROL_MASK 0xc6 + +#define MODEM_CTL_REGISTER 0x04 +#define MODEM_STATUS_REGISTER 0x06 + + +#define SERIAL_LSR_OE 0x02 +#define SERIAL_LSR_PE 0x04 +#define SERIAL_LSR_FE 0x08 +#define SERIAL_LSR_BI 0x10 + +#define SERIAL_LSR_TEMT 0x40 + +#define SERIAL_MCR_DTR 0x01 +#define SERIAL_MCR_RTS 0x02 +#define SERIAL_MCR_LOOP 0x10 + +#define SERIAL_MSR_CTS 0x10 +#define SERIAL_MSR_CD 0x80 +#define SERIAL_MSR_RI 0x40 +#define SERIAL_MSR_DSR 0x20 +#define SERIAL_MSR_MASK 0xf0 + +#define SERIAL_CRTSCTS ((SERIAL_MCR_RTS << 8) | SERIAL_MSR_CTS) + +#define SERIAL_8_DATA 0x03 +#define SERIAL_7_DATA 0x02 +#define SERIAL_6_DATA 0x01 +#define SERIAL_5_DATA 0x00 + +#define SERIAL_ODD_PARITY 0X08 +#define SERIAL_EVEN_PARITY 0X18 + +#define MAX_BAUD_RATE 460800 + +#define ATC_DISABLED 0x00 +#define DUPMODE_BITS 0xc0 +#define RR_BITS 0x03 +#define LOOPMODE_BITS 0x41 +#define RS232_MODE 0x00 +#define RTSCTS_TO_CONNECTOR 0x40 +#define CLKS_X4 0x02 +#define FULLPWRBIT 0x00000080 +#define NEXT_BOARD_POWER_BIT 0x00000004 + +static int debug = 1; + +/* Version Information */ +#define DRIVER_VERSION "v0.1" +#define DRIVER_DESC "Quatech SSU-100 USB to Serial Driver" + +#define USB_VENDOR_ID_QUATECH 0x061d /* Quatech VID */ +#define QUATECH_SSU100 0xC020 /* SSU100 */ + +static const struct usb_device_id id_table[] = { + {USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_SSU100)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, id_table); + + +static struct usb_driver ssu100_driver = { + .name = "ssu100", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, + .suspend = usb_serial_suspend, + .resume = usb_serial_resume, + .no_dynamic_id = 1, + .supports_autosuspend = 1, +}; + +struct ssu100_port_private { + u8 shadowLSR; + u8 shadowMSR; + wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ + unsigned short max_packet_size; +}; + +static void ssu100_release(struct usb_serial *serial) +{ + struct ssu100_port_private *priv = usb_get_serial_port_data(*serial->port); + + dbg("%s", __func__); + kfree(priv); +} + +static inline int ssu100_control_msg(struct usb_device *dev, + u8 request, u16 data, u16 index) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + request, 0x40, data, index, + NULL, 0, 300); +} + +static inline int ssu100_setdevice(struct usb_device *dev, u8 *data) +{ + u16 x = ((u16)(data[1] << 8) | (u16)(data[0])); + + return ssu100_control_msg(dev, QT_SET_GET_DEVICE, x, 0); +} + + +static inline int ssu100_getdevice(struct usb_device *dev, u8 *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + QT_SET_GET_DEVICE, 0xc0, 0, 0, + data, 3, 300); +} + +static inline int ssu100_getregister(struct usb_device *dev, + unsigned short uart, + unsigned short reg, + u8 *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + QT_SET_GET_REGISTER, 0xc0, reg, + uart, data, sizeof(*data), 300); + +} + + +static inline int ssu100_setregister(struct usb_device *dev, + unsigned short uart, + u16 data) +{ + u16 value = (data << 8) | MODEM_CTL_REGISTER; + + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + QT_SET_GET_REGISTER, 0x40, value, uart, + NULL, 0, 300); + +} + +#define set_mctrl(dev, set) update_mctrl((dev), (set), 0) +#define clear_mctrl(dev, clear) update_mctrl((dev), 0, (clear)) + +/* these do not deal with device that have more than 1 port */ +static inline int update_mctrl(struct usb_device *dev, unsigned int set, + unsigned int clear) +{ + unsigned urb_value; + int result; + + if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) { + dbg("%s - DTR|RTS not being set|cleared", __func__); + return 0; /* no change */ + } + + clear &= ~set; /* 'set' takes precedence over 'clear' */ + urb_value = 0; + if (set & TIOCM_DTR) + urb_value |= SERIAL_MCR_DTR; + if (set & TIOCM_RTS) + urb_value |= SERIAL_MCR_RTS; + + result = ssu100_setregister(dev, 0, urb_value); + if (result < 0) + dbg("%s Error from MODEM_CTRL urb", __func__); + + return result; +} + +static int ssu100_initdevice(struct usb_device *dev) +{ + u8 *data; + int result = 0; + + dbg("%s", __func__); + + data = kzalloc(3, GFP_KERNEL); + if (!data) + return -ENOMEM; + + result = ssu100_getdevice(dev, data); + if (result < 0) { + dbg("%s - get_device failed %i", __func__, result); + goto out; + } + + data[1] &= ~FULLPWRBIT; + + result = ssu100_setdevice(dev, data); + if (result < 0) { + dbg("%s - setdevice failed %i", __func__, result); + goto out; + } + + result = ssu100_control_msg(dev, QT_GET_SET_PREBUF_TRIG_LVL, 128, 0); + if (result < 0) { + dbg("%s - set prebuffer level failed %i", __func__, result); + goto out; + } + + result = ssu100_control_msg(dev, QT_SET_ATF, ATC_DISABLED, 0); + if (result < 0) { + dbg("%s - set ATFprebuffer level failed %i", __func__, result); + goto out; + } + + result = ssu100_getdevice(dev, data); + if (result < 0) { + dbg("%s - get_device failed %i", __func__, result); + goto out; + } + + data[0] &= ~(RR_BITS | DUPMODE_BITS); + data[0] |= CLKS_X4; + data[1] &= ~(LOOPMODE_BITS); + data[1] |= RS232_MODE; + + result = ssu100_setdevice(dev, data); + if (result < 0) { + dbg("%s - setdevice failed %i", __func__, result); + goto out; + } + +out: kfree(data); + return result; + +} + + +static void ssu100_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios) +{ + struct usb_device *dev = port->serial->dev; + struct ktermios *termios = tty->termios; + u16 baud, divisor, remainder; + unsigned int cflag = termios->c_cflag; + u16 urb_value = 0; /* will hold the new flags */ + int result; + + dbg("%s", __func__); + + if (cflag & PARENB) { + if (cflag & PARODD) + urb_value |= SERIAL_ODD_PARITY; + else + urb_value |= SERIAL_EVEN_PARITY; + } + + switch (cflag & CSIZE) { + case CS5: + urb_value |= SERIAL_5_DATA; + break; + case CS6: + urb_value |= SERIAL_6_DATA; + break; + case CS7: + urb_value |= SERIAL_7_DATA; + break; + default: + case CS8: + urb_value |= SERIAL_8_DATA; + break; + } + + baud = tty_get_baud_rate(tty); + if (!baud) + baud = 9600; + + dbg("%s - got baud = %d\n", __func__, baud); + + + divisor = MAX_BAUD_RATE / baud; + remainder = MAX_BAUD_RATE % baud; + if (((remainder * 2) >= baud) && (baud != 110)) + divisor++; + + urb_value = urb_value << 8; + + result = ssu100_control_msg(dev, QT_GET_SET_UART, divisor, urb_value); + if (result < 0) + dbg("%s - set uart failed", __func__); + + if (cflag & CRTSCTS) + result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK, + SERIAL_CRTSCTS, 0); + else + result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK, + 0, 0); + if (result < 0) + dbg("%s - set HW flow control failed", __func__); + + if (I_IXOFF(tty) || I_IXON(tty)) { + u16 x = ((u16)(START_CHAR(tty) << 8) | (u16)(STOP_CHAR(tty))); + + result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK, + x, 0); + } else + result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK, + 0, 0); + + if (result < 0) + dbg("%s - set SW flow control failed", __func__); + +} + + +static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port) +{ + struct usb_device *dev = port->serial->dev; + struct ssu100_port_private *priv = usb_get_serial_port_data(port); + u8 *data; + int result; + + dbg("%s - port %d", __func__, port->number); + + data = kzalloc(2, GFP_KERNEL); + if (!data) + return -ENOMEM; + + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + QT_OPEN_CLOSE_CHANNEL, + QT_TRANSFER_IN, 0x01, + 0, data, 2, 300); + if (result < 0) { + dbg("%s - open failed %i", __func__, result); + kfree(data); + return result; + } + + priv->shadowLSR = data[0] & (SERIAL_LSR_OE | SERIAL_LSR_PE | + SERIAL_LSR_FE | SERIAL_LSR_BI); + + priv->shadowMSR = data[1] & (SERIAL_MSR_CTS | SERIAL_MSR_DSR | + SERIAL_MSR_RI | SERIAL_MSR_CD); + + kfree(data); + +/* set to 9600 */ + result = ssu100_control_msg(dev, QT_GET_SET_UART, 0x30, 0x0300); + if (result < 0) + dbg("%s - set uart failed", __func__); + + if (tty) + ssu100_set_termios(tty, port, tty->termios); + + return usb_serial_generic_open(tty, port); +} + +static void ssu100_close(struct usb_serial_port *port) +{ + dbg("%s", __func__); + usb_serial_generic_close(port); +} + +static int get_serial_info(struct usb_serial_port *port, + struct serial_struct __user *retinfo) +{ + struct serial_struct tmp; + + if (!retinfo) + return -EFAULT; + + memset(&tmp, 0, sizeof(tmp)); + tmp.line = port->serial->minor; + tmp.port = 0; + tmp.irq = 0; + tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; + tmp.xmit_fifo_size = port->bulk_out_size; + tmp.baud_base = 9600; + tmp.close_delay = 5*HZ; + tmp.closing_wait = 30*HZ; + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int ssu100_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct usb_serial_port *port = tty->driver_data; + struct ssu100_port_private *priv = usb_get_serial_port_data(port); + + dbg("%s cmd 0x%04x", __func__, cmd); + + switch (cmd) { + case TIOCGSERIAL: + return get_serial_info(port, + (struct serial_struct __user *) arg); + + case TIOCMIWAIT: + while (priv != NULL) { + u8 prevMSR = priv->shadowMSR & SERIAL_MSR_MASK; + interruptible_sleep_on(&priv->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + else { + u8 diff = (priv->shadowMSR & SERIAL_MSR_MASK) ^ prevMSR; + if (!diff) + return -EIO; /* no change => error */ + + /* Return 0 if caller wanted to know about + these bits */ + + if (((arg & TIOCM_RNG) && (diff & SERIAL_MSR_RI)) || + ((arg & TIOCM_DSR) && (diff & SERIAL_MSR_DSR)) || + ((arg & TIOCM_CD) && (diff & SERIAL_MSR_CD)) || + ((arg & TIOCM_CTS) && (diff & SERIAL_MSR_CTS))) + return 0; + } + } + return 0; + + default: + break; + } + + dbg("%s arg not supported", __func__); + + return -ENOIOCTLCMD; +} + +static void ssu100_set_max_packet_size(struct usb_serial_port *port) +{ + struct ssu100_port_private *priv = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + struct usb_device *udev = serial->dev; + + struct usb_interface *interface = serial->interface; + struct usb_endpoint_descriptor *ep_desc = &interface->cur_altsetting->endpoint[1].desc; + + unsigned num_endpoints; + int i; + + num_endpoints = interface->cur_altsetting->desc.bNumEndpoints; + dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints); + + for (i = 0; i < num_endpoints; i++) { + dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1, + interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize); + ep_desc = &interface->cur_altsetting->endpoint[i].desc; + } + + /* set max packet size based on descriptor */ + priv->max_packet_size = ep_desc->wMaxPacketSize; + + dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size); +} + +static int ssu100_attach(struct usb_serial *serial) +{ + struct ssu100_port_private *priv; + struct usb_serial_port *port = *serial->port; + + dbg("%s", __func__); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__, + sizeof(*priv)); + return -ENOMEM; + } + + init_waitqueue_head(&priv->delta_msr_wait); + usb_set_serial_port_data(port, priv); + + ssu100_set_max_packet_size(port); + + return ssu100_initdevice(serial->dev); +} + +static int ssu100_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct usb_serial_port *port = tty->driver_data; + struct usb_device *dev = port->serial->dev; + u8 *d; + int r; + + dbg("%s\n", __func__); + + d = kzalloc(2, GFP_KERNEL); + if (!d) + return -ENOMEM; + + r = ssu100_getregister(dev, 0, MODEM_CTL_REGISTER, d); + if (r < 0) + goto mget_out; + + r = ssu100_getregister(dev, 0, MODEM_STATUS_REGISTER, d+1); + if (r < 0) + goto mget_out; + + r = (d[0] & SERIAL_MCR_DTR ? TIOCM_DTR : 0) | + (d[0] & SERIAL_MCR_RTS ? TIOCM_RTS : 0) | + (d[1] & SERIAL_MSR_CTS ? TIOCM_CTS : 0) | + (d[1] & SERIAL_MSR_CD ? TIOCM_CAR : 0) | + (d[1] & SERIAL_MSR_RI ? TIOCM_RI : 0) | + (d[1] & SERIAL_MSR_DSR ? TIOCM_DSR : 0); + +mget_out: + kfree(d); + return r; +} + +static int ssu100_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct usb_serial_port *port = tty->driver_data; + struct usb_device *dev = port->serial->dev; + + dbg("%s\n", __func__); + return update_mctrl(dev, set, clear); +} + +static void ssu100_dtr_rts(struct usb_serial_port *port, int on) +{ + struct usb_device *dev = port->serial->dev; + + dbg("%s\n", __func__); + + mutex_lock(&port->serial->disc_mutex); + if (!port->serial->disconnected) { + /* Disable flow control */ + if (!on && + ssu100_setregister(dev, 0, 0) < 0) + dev_err(&port->dev, "error from flowcontrol urb\n"); + /* drop RTS and DTR */ + if (on) + set_mctrl(dev, TIOCM_DTR | TIOCM_RTS); + else + clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS); + } + mutex_unlock(&port->serial->disc_mutex); +} + +static int ssu100_process_packet(struct tty_struct *tty, + struct usb_serial_port *port, + struct ssu100_port_private *priv, + char *packet, int len) +{ + int i; + char flag; + char *ch; + + dbg("%s - port %d", __func__, port->number); + + if (len < 4) { + dbg("%s - malformed packet", __func__); + return 0; + } + + if ((packet[0] == 0x1b) && (packet[1] == 0x1b) && + ((packet[2] == 0x00) || (packet[2] == 0x01))) { + if (packet[2] == 0x00) + priv->shadowLSR = packet[3] & (SERIAL_LSR_OE | + SERIAL_LSR_PE | + SERIAL_LSR_FE | + SERIAL_LSR_BI); + + if (packet[2] == 0x01) { + priv->shadowMSR = packet[3]; + wake_up_interruptible(&priv->delta_msr_wait); + } + + len -= 4; + ch = packet + 4; + } else + ch = packet; + + if (!len) + return 0; /* status only */ + + if (port->port.console && port->sysrq) { + for (i = 0; i < len; i++, ch++) { + if (!usb_serial_handle_sysrq_char(tty, port, *ch)) + tty_insert_flip_char(tty, *ch, flag); + } + } else + tty_insert_flip_string_fixed_flag(tty, ch, flag, len); + + return len; +} + +static void ssu100_process_read_urb(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + struct ssu100_port_private *priv = usb_get_serial_port_data(port); + char *data = (char *)urb->transfer_buffer; + struct tty_struct *tty; + int count = 0; + int i; + int len; + + dbg("%s", __func__); + + tty = tty_port_tty_get(&port->port); + if (!tty) + return; + + for (i = 0; i < urb->actual_length; i += priv->max_packet_size) { + len = min_t(int, urb->actual_length - i, priv->max_packet_size); + count += ssu100_process_packet(tty, port, priv, &data[i], len); + } + + if (count) + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + + +static struct usb_serial_driver ssu100_device = { + .driver = { + .owner = THIS_MODULE, + .name = "ssu100", + }, + .description = DRIVER_DESC, + .id_table = id_table, + .usb_driver = &ssu100_driver, + .num_ports = 1, + .bulk_in_size = 256, + .bulk_out_size = 256, + .open = ssu100_open, + .close = ssu100_close, + .attach = ssu100_attach, + .release = ssu100_release, + .dtr_rts = ssu100_dtr_rts, + .process_read_urb = ssu100_process_read_urb, + .tiocmget = ssu100_tiocmget, + .tiocmset = ssu100_tiocmset, + .ioctl = ssu100_ioctl, + .set_termios = ssu100_set_termios, +}; + +static int __init ssu100_init(void) +{ + int retval; + + dbg("%s", __func__); + + /* register with usb-serial */ + retval = usb_serial_register(&ssu100_device); + + if (retval) + goto failed_usb_sio_register; + + retval = usb_register(&ssu100_driver); + if (retval) + goto failed_usb_register; + + printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" + DRIVER_DESC "\n"); + + return 0; + +failed_usb_register: + usb_serial_deregister(&ssu100_device); +failed_usb_sio_register: + return retval; +} + +static void __exit ssu100_exit(void) +{ + usb_deregister(&ssu100_driver); + usb_serial_deregister(&ssu100_device); +} + +module_init(ssu100_init); +module_exit(ssu100_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); -- cgit v1.2.3-70-g09d2 From 021bff9179c2d19c26599dc3e9134d04cf1c8a3a Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 29 Jul 2010 22:12:20 -0700 Subject: USB: xhci: Performance - move functions that find ep ring. I've been using perf to measure the top symbols while transferring 1GB of data on a USB 3.0 drive with dd. This is using the raw disk with /dev/sdb, with a block size of 1K. During performance testing, the top symbol was xhci_triad_to_transfer_ring(), a function that should return immediately if streams are not enabled for an endpoint. It turned out that the functions to find the endpoint ring was defined in xhci-mem.c and used in xhci-ring.c and xhci-hcd.c. I moved a copy of xhci_triad_to_transfer_ring() and xhci_urb_to_transfer_ring() into xhci-ring.c and declared them static. I also made a static version of xhci_urb_to_transfer_ring() in xhci.c. This improved throughput on a 1GB read of the raw disk with dd from 186MB/s to 195MB/s, and perf reported sampling the xhci_triad_to_transfer_ring() 0.06% of the time, rather than 9.26% of the time. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 43 ------------------------------------------- drivers/usb/host/xhci-ring.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.c | 41 +++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.h | 5 ----- 4 files changed, 85 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 7d60d1f4deb..ac57f538f95 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -391,49 +391,6 @@ struct xhci_ring *xhci_stream_id_to_ring( return ep->stream_info->stream_rings[stream_id]; } -struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci, - unsigned int slot_id, unsigned int ep_index, - unsigned int stream_id) -{ - struct xhci_virt_ep *ep; - - ep = &xhci->devs[slot_id]->eps[ep_index]; - /* Common case: no streams */ - if (!(ep->ep_state & EP_HAS_STREAMS)) - return ep->ring; - - if (stream_id == 0) { - xhci_warn(xhci, - "WARN: Slot ID %u, ep index %u has streams, " - "but URB has no stream ID.\n", - slot_id, ep_index); - return NULL; - } - - if (stream_id < ep->stream_info->num_streams) - return ep->stream_info->stream_rings[stream_id]; - - xhci_warn(xhci, - "WARN: Slot ID %u, ep index %u has " - "stream IDs 1 to %u allocated, " - "but stream ID %u is requested.\n", - slot_id, ep_index, - ep->stream_info->num_streams - 1, - stream_id); - return NULL; -} - -/* Get the right ring for the given URB. - * If the endpoint supports streams, boundary check the URB's stream ID. - * If the endpoint doesn't support streams, return the singular endpoint ring. - */ -struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci, - struct urb *urb) -{ - return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id, - xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id); -} - #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING static int xhci_test_radix_tree(struct xhci_hcd *xhci, unsigned int num_streams, diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index da3519e76e2..7f1c54585b6 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -419,6 +419,50 @@ static struct xhci_segment *find_trb_seg( return cur_seg; } + +static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci, + unsigned int slot_id, unsigned int ep_index, + unsigned int stream_id) +{ + struct xhci_virt_ep *ep; + + ep = &xhci->devs[slot_id]->eps[ep_index]; + /* Common case: no streams */ + if (!(ep->ep_state & EP_HAS_STREAMS)) + return ep->ring; + + if (stream_id == 0) { + xhci_warn(xhci, + "WARN: Slot ID %u, ep index %u has streams, " + "but URB has no stream ID.\n", + slot_id, ep_index); + return NULL; + } + + if (stream_id < ep->stream_info->num_streams) + return ep->stream_info->stream_rings[stream_id]; + + xhci_warn(xhci, + "WARN: Slot ID %u, ep index %u has " + "stream IDs 1 to %u allocated, " + "but stream ID %u is requested.\n", + slot_id, ep_index, + ep->stream_info->num_streams - 1, + stream_id); + return NULL; +} + +/* Get the right ring for the given URB. + * If the endpoint supports streams, boundary check the URB's stream ID. + * If the endpoint doesn't support streams, return the singular endpoint ring. + */ +static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci, + struct urb *urb) +{ + return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id, + xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id); +} + /* * Move the xHC's endpoint ring dequeue pointer past cur_td. * Record the new state of the xHC's endpoint ring dequeue segment, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 89ac4853409..f5e0b00cbb8 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -916,6 +916,47 @@ dying: return -ESHUTDOWN; } +/* Get the right ring for the given URB. + * If the endpoint supports streams, boundary check the URB's stream ID. + * If the endpoint doesn't support streams, return the singular endpoint ring. + */ +static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci, + struct urb *urb) +{ + unsigned int slot_id; + unsigned int ep_index; + unsigned int stream_id; + struct xhci_virt_ep *ep; + + slot_id = urb->dev->slot_id; + ep_index = xhci_get_endpoint_index(&urb->ep->desc); + stream_id = urb->stream_id; + ep = &xhci->devs[slot_id]->eps[ep_index]; + /* Common case: no streams */ + if (!(ep->ep_state & EP_HAS_STREAMS)) + return ep->ring; + + if (stream_id == 0) { + xhci_warn(xhci, + "WARN: Slot ID %u, ep index %u has streams, " + "but URB has no stream ID.\n", + slot_id, ep_index); + return NULL; + } + + if (stream_id < ep->stream_info->num_streams) + return ep->stream_info->stream_rings[stream_id]; + + xhci_warn(xhci, + "WARN: Slot ID %u, ep index %u has " + "stream IDs 1 to %u allocated, " + "but stream ID %u is requested.\n", + slot_id, ep_index, + ep->stream_info->num_streams - 1, + stream_id); + return NULL; +} + /* * Remove the URB's TD from the endpoint ring. This may cause the HC to stop * USB transfers, potentially stopping in the middle of a TRB buffer. The HC diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index e1383d91468..9fe95c4e2a5 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1344,11 +1344,6 @@ void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci, struct xhci_ring *xhci_dma_to_transfer_ring( struct xhci_virt_ep *ep, u64 address); -struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci, - struct urb *urb); -struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci, - unsigned int slot_id, unsigned int ep_index, - unsigned int stream_id); struct xhci_ring *xhci_stream_id_to_ring( struct xhci_virt_device *dev, unsigned int ep_index, -- cgit v1.2.3-70-g09d2 From 9032cd52798daf4cd6314ffea5030b37b3eb34af Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 29 Jul 2010 22:12:29 -0700 Subject: USB: xhci: Performance - move interrupt handlers into xhci-ring.c Most of the work for interrupt handling is done in xhci-ring.c, so it makes sense to move the functions that are first called when an interrupt happens (xhci_irq() or xhci_msi_irq()) into xhci-ring.c, so that the compiler can better optimize them. Shorten some lines to make it pass checkpatch. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 103 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.c | 103 ------------------------------------------- drivers/usb/host/xhci.h | 1 + 3 files changed, 104 insertions(+), 103 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7f1c54585b6..4b50a02ba11 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2030,6 +2030,109 @@ void xhci_handle_event(struct xhci_hcd *xhci) /* Are there more items on the event ring? */ xhci_handle_event(xhci); } +/* + * Called in interrupt context when there might be work + * queued on the event ring + * + * xhci->lock must be held by caller. + */ +static void xhci_work(struct xhci_hcd *xhci) +{ + u32 temp; + u64 temp_64; + + /* + * Clear the op reg interrupt status first, + * so we can receive interrupts from other MSI-X interrupters. + * Write 1 to clear the interrupt status. + */ + temp = xhci_readl(xhci, &xhci->op_regs->status); + temp |= STS_EINT; + xhci_writel(xhci, temp, &xhci->op_regs->status); + /* FIXME when MSI-X is supported and there are multiple vectors */ + /* Clear the MSI-X event interrupt status */ + + /* Acknowledge the interrupt */ + temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); + temp |= 0x3; + xhci_writel(xhci, temp, &xhci->ir_set->irq_pending); + + if (xhci->xhc_state & XHCI_STATE_DYING) + xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " + "Shouldn't IRQs be disabled?\n"); + else + /* FIXME this should be a delayed service routine + * that clears the EHB. + */ + xhci_handle_event(xhci); + + /* Clear the event handler busy flag (RW1C); event ring is empty. */ + temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue); + /* Flush posted writes -- FIXME is this necessary? */ + xhci_readl(xhci, &xhci->ir_set->irq_pending); +} + +/* + * xHCI spec says we can get an interrupt, and if the HC has an error condition, + * we might get bad data out of the event ring. Section 4.10.2.7 has a list of + * indicators of an event TRB error, but we check the status *first* to be safe. + */ +irqreturn_t xhci_irq(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + u32 temp, temp2; + union xhci_trb *trb; + + spin_lock(&xhci->lock); + trb = xhci->event_ring->dequeue; + /* Check if the xHC generated the interrupt, or the irq is shared */ + temp = xhci_readl(xhci, &xhci->op_regs->status); + temp2 = xhci_readl(xhci, &xhci->ir_set->irq_pending); + if (temp == 0xffffffff && temp2 == 0xffffffff) + goto hw_died; + + if (!(temp & STS_EINT) && !ER_IRQ_PENDING(temp2)) { + spin_unlock(&xhci->lock); + xhci_warn(xhci, "Spurious interrupt.\n"); + return IRQ_NONE; + } + xhci_dbg(xhci, "op reg status = %08x\n", temp); + xhci_dbg(xhci, "ir set irq_pending = %08x\n", temp2); + xhci_dbg(xhci, "Event ring dequeue ptr:\n"); + xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n", + (unsigned long long) + xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb), + lower_32_bits(trb->link.segment_ptr), + upper_32_bits(trb->link.segment_ptr), + (unsigned int) trb->link.intr_target, + (unsigned int) trb->link.control); + + if (temp & STS_FATAL) { + xhci_warn(xhci, "WARNING: Host System Error\n"); + xhci_halt(xhci); +hw_died: + xhci_to_hcd(xhci)->state = HC_STATE_HALT; + spin_unlock(&xhci->lock); + return -ESHUTDOWN; + } + + xhci_work(xhci); + spin_unlock(&xhci->lock); + + return IRQ_HANDLED; +} + +irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd) +{ + irqreturn_t ret; + + set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + + ret = xhci_irq(hcd); + + return ret; +} /**** Endpoint Ring Operations ****/ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f5e0b00cbb8..d5c550ea3e6 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -172,17 +172,6 @@ int xhci_reset(struct xhci_hcd *xhci) return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000); } -static irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd) -{ - irqreturn_t ret; - - set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); - - ret = xhci_irq(hcd); - - return ret; -} - /* * Free IRQs * free all IRQs request @@ -332,100 +321,8 @@ int xhci_init(struct usb_hcd *hcd) return retval; } -/* - * Called in interrupt context when there might be work - * queued on the event ring - * - * xhci->lock must be held by caller. - */ -static void xhci_work(struct xhci_hcd *xhci) -{ - u32 temp; - u64 temp_64; - - /* - * Clear the op reg interrupt status first, - * so we can receive interrupts from other MSI-X interrupters. - * Write 1 to clear the interrupt status. - */ - temp = xhci_readl(xhci, &xhci->op_regs->status); - temp |= STS_EINT; - xhci_writel(xhci, temp, &xhci->op_regs->status); - /* FIXME when MSI-X is supported and there are multiple vectors */ - /* Clear the MSI-X event interrupt status */ - - /* Acknowledge the interrupt */ - temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); - temp |= 0x3; - xhci_writel(xhci, temp, &xhci->ir_set->irq_pending); - /* Flush posted writes */ - xhci_readl(xhci, &xhci->ir_set->irq_pending); - - if (xhci->xhc_state & XHCI_STATE_DYING) - xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " - "Shouldn't IRQs be disabled?\n"); - else - /* FIXME this should be a delayed service routine - * that clears the EHB. - */ - xhci_handle_event(xhci); - - /* Clear the event handler busy flag (RW1C); the event ring should be empty. */ - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue); - /* Flush posted writes -- FIXME is this necessary? */ - xhci_readl(xhci, &xhci->ir_set->irq_pending); -} - /*-------------------------------------------------------------------------*/ -/* - * xHCI spec says we can get an interrupt, and if the HC has an error condition, - * we might get bad data out of the event ring. Section 4.10.2.7 has a list of - * indicators of an event TRB error, but we check the status *first* to be safe. - */ -irqreturn_t xhci_irq(struct usb_hcd *hcd) -{ - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - u32 temp, temp2; - union xhci_trb *trb; - - spin_lock(&xhci->lock); - trb = xhci->event_ring->dequeue; - /* Check if the xHC generated the interrupt, or the irq is shared */ - temp = xhci_readl(xhci, &xhci->op_regs->status); - temp2 = xhci_readl(xhci, &xhci->ir_set->irq_pending); - if (temp == 0xffffffff && temp2 == 0xffffffff) - goto hw_died; - - if (!(temp & STS_EINT) && !ER_IRQ_PENDING(temp2)) { - spin_unlock(&xhci->lock); - return IRQ_NONE; - } - xhci_dbg(xhci, "op reg status = %08x\n", temp); - xhci_dbg(xhci, "ir set irq_pending = %08x\n", temp2); - xhci_dbg(xhci, "Event ring dequeue ptr:\n"); - xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n", - (unsigned long long)xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb), - lower_32_bits(trb->link.segment_ptr), - upper_32_bits(trb->link.segment_ptr), - (unsigned int) trb->link.intr_target, - (unsigned int) trb->link.control); - - if (temp & STS_FATAL) { - xhci_warn(xhci, "WARNING: Host System Error\n"); - xhci_halt(xhci); -hw_died: - xhci_to_hcd(xhci)->state = HC_STATE_HALT; - spin_unlock(&xhci->lock); - return -ESHUTDOWN; - } - - xhci_work(xhci); - spin_unlock(&xhci->lock); - - return IRQ_HANDLED; -} #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING void xhci_event_ring_work(unsigned long arg) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 9fe95c4e2a5..6d829c41d96 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1371,6 +1371,7 @@ void xhci_stop(struct usb_hcd *hcd); void xhci_shutdown(struct usb_hcd *hcd); int xhci_get_frame(struct usb_hcd *hcd); irqreturn_t xhci_irq(struct usb_hcd *hcd); +irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd); int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev); void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev); int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, -- cgit v1.2.3-70-g09d2 From bda531452c143b0bafe3dd6567dbfe9274009345 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 29 Jul 2010 22:12:38 -0700 Subject: USB: xhci: Performance - move xhci_work() into xhci_irq() When we move xhci_work() into xhci_irq(), we don't need to read the operational register status field twice. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 73 ++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 4b50a02ba11..f9c08d2fe33 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2030,48 +2030,6 @@ void xhci_handle_event(struct xhci_hcd *xhci) /* Are there more items on the event ring? */ xhci_handle_event(xhci); } -/* - * Called in interrupt context when there might be work - * queued on the event ring - * - * xhci->lock must be held by caller. - */ -static void xhci_work(struct xhci_hcd *xhci) -{ - u32 temp; - u64 temp_64; - - /* - * Clear the op reg interrupt status first, - * so we can receive interrupts from other MSI-X interrupters. - * Write 1 to clear the interrupt status. - */ - temp = xhci_readl(xhci, &xhci->op_regs->status); - temp |= STS_EINT; - xhci_writel(xhci, temp, &xhci->op_regs->status); - /* FIXME when MSI-X is supported and there are multiple vectors */ - /* Clear the MSI-X event interrupt status */ - - /* Acknowledge the interrupt */ - temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); - temp |= 0x3; - xhci_writel(xhci, temp, &xhci->ir_set->irq_pending); - - if (xhci->xhc_state & XHCI_STATE_DYING) - xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " - "Shouldn't IRQs be disabled?\n"); - else - /* FIXME this should be a delayed service routine - * that clears the EHB. - */ - xhci_handle_event(xhci); - - /* Clear the event handler busy flag (RW1C); event ring is empty. */ - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue); - /* Flush posted writes -- FIXME is this necessary? */ - xhci_readl(xhci, &xhci->ir_set->irq_pending); -} /* * xHCI spec says we can get an interrupt, and if the HC has an error condition, @@ -2083,6 +2041,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) struct xhci_hcd *xhci = hcd_to_xhci(hcd); u32 temp, temp2; union xhci_trb *trb; + u64 temp_64; spin_lock(&xhci->lock); trb = xhci->event_ring->dequeue; @@ -2117,7 +2076,35 @@ hw_died: return -ESHUTDOWN; } - xhci_work(xhci); + /* + * Clear the op reg interrupt status first, + * so we can receive interrupts from other MSI-X interrupters. + * Write 1 to clear the interrupt status. + */ + temp |= STS_EINT; + xhci_writel(xhci, temp, &xhci->op_regs->status); + /* FIXME when MSI-X is supported and there are multiple vectors */ + /* Clear the MSI-X event interrupt status */ + + /* Acknowledge the interrupt */ + temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); + temp |= 0x3; + xhci_writel(xhci, temp, &xhci->ir_set->irq_pending); + + if (xhci->xhc_state & XHCI_STATE_DYING) + xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " + "Shouldn't IRQs be disabled?\n"); + else + /* FIXME this should be a delayed service routine + * that clears the EHB. + */ + xhci_handle_event(xhci); + + /* Clear the event handler busy flag (RW1C); event ring is empty. */ + temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue); + /* Flush posted writes -- FIXME is this necessary? */ + xhci_readl(xhci, &xhci->ir_set->irq_pending); spin_unlock(&xhci->lock); return IRQ_HANDLED; -- cgit v1.2.3-70-g09d2 From 27e0dd4d7ccc3e8e2a79600c3608031022a2298c Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 29 Jul 2010 22:12:43 -0700 Subject: USB: xhci: Remove unnecessary reads of IRQ_PENDING register. Remove a duplicate register read of the interrupt pending register from xhci_irq(). Also, remove waiting on the posted write of that register. The host will see it eventually. It will probably read the register itself before deciding whether to interrupt the system again, forcing the posted write to complete. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f9c08d2fe33..eb4b10229a0 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2039,25 +2039,25 @@ void xhci_handle_event(struct xhci_hcd *xhci) irqreturn_t xhci_irq(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - u32 temp, temp2; + u32 status, irq_pending; union xhci_trb *trb; u64 temp_64; spin_lock(&xhci->lock); trb = xhci->event_ring->dequeue; /* Check if the xHC generated the interrupt, or the irq is shared */ - temp = xhci_readl(xhci, &xhci->op_regs->status); - temp2 = xhci_readl(xhci, &xhci->ir_set->irq_pending); - if (temp == 0xffffffff && temp2 == 0xffffffff) + status = xhci_readl(xhci, &xhci->op_regs->status); + irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending); + if (status == 0xffffffff && irq_pending == 0xffffffff) goto hw_died; - if (!(temp & STS_EINT) && !ER_IRQ_PENDING(temp2)) { + if (!(status & STS_EINT) && !ER_IRQ_PENDING(irq_pending)) { spin_unlock(&xhci->lock); xhci_warn(xhci, "Spurious interrupt.\n"); return IRQ_NONE; } - xhci_dbg(xhci, "op reg status = %08x\n", temp); - xhci_dbg(xhci, "ir set irq_pending = %08x\n", temp2); + xhci_dbg(xhci, "op reg status = %08x\n", status); + xhci_dbg(xhci, "ir set irq_pending = %08x\n", irq_pending); xhci_dbg(xhci, "Event ring dequeue ptr:\n"); xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n", (unsigned long long) @@ -2067,7 +2067,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) (unsigned int) trb->link.intr_target, (unsigned int) trb->link.control); - if (temp & STS_FATAL) { + if (status & STS_FATAL) { xhci_warn(xhci, "WARNING: Host System Error\n"); xhci_halt(xhci); hw_died: @@ -2081,15 +2081,14 @@ hw_died: * so we can receive interrupts from other MSI-X interrupters. * Write 1 to clear the interrupt status. */ - temp |= STS_EINT; - xhci_writel(xhci, temp, &xhci->op_regs->status); + status |= STS_EINT; + xhci_writel(xhci, status, &xhci->op_regs->status); /* FIXME when MSI-X is supported and there are multiple vectors */ /* Clear the MSI-X event interrupt status */ /* Acknowledge the interrupt */ - temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); - temp |= 0x3; - xhci_writel(xhci, temp, &xhci->ir_set->irq_pending); + irq_pending |= 0x3; + xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending); if (xhci->xhc_state & XHCI_STATE_DYING) xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " @@ -2103,8 +2102,6 @@ hw_died: /* Clear the event handler busy flag (RW1C); event ring is empty. */ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue); - /* Flush posted writes -- FIXME is this necessary? */ - xhci_readl(xhci, &xhci->ir_set->irq_pending); spin_unlock(&xhci->lock); return IRQ_HANDLED; -- cgit v1.2.3-70-g09d2 From d6d98a4d8d2411bca7e15d9c0796bf3bc30c3f21 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 29 Jul 2010 22:12:46 -0700 Subject: USB: xhci: Make xhci_handle_event() static. xhci_handle_event() is now only called from within xhci-ring.c, so make it static. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index eb4b10229a0..3b962ba5ef0 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1964,7 +1964,7 @@ cleanup: * This function handles all OS-owned events on the event ring. It may drop * xhci->lock between event processing (e.g. to pass up port status changes). */ -void xhci_handle_event(struct xhci_hcd *xhci) +static void xhci_handle_event(struct xhci_hcd *xhci) { union xhci_trb *event; int update_ptrs = 1; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 6d829c41d96..25e108e981f 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1400,7 +1400,6 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg, int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code); void xhci_ring_cmd_db(struct xhci_hcd *xhci); void *xhci_setup_one_noop(struct xhci_hcd *xhci); -void xhci_handle_event(struct xhci_hcd *xhci); void xhci_set_hc_event_deq(struct xhci_hcd *xhci); int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id); int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, -- cgit v1.2.3-70-g09d2 From c06d68b814d556cff5a4dc589215f5ed9f0b7fd5 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 29 Jul 2010 22:12:49 -0700 Subject: USB: xhci: Minimize HW event ring dequeue pointer writes. The xHCI specification suggests that writing the hardware event ring dequeue pointer register too often can be an expensive operation for the xHCI hardware to manage. It suggests minimizing the number of writes to that register. Originally, the driver wrote the event ring dequeue pointer after each event was processed. Depending on how the event ring moderation register is set up and how fast the transfers are completing, there may be several events processed for each interrupt. This patch makes the hardware event ring dequeue pointer be written only once per interrupt. Make the transfer event handler and port status event handler only write the software event ring dequeue pointer. Move the updating of the hardware event ring dequeue pointer into the interrupt function. Move the contents of xhci_set_hc_event_deq() into the interrupt handler. The interrupt handler must clear the event handler busy flag, so it might as well also write the dequeue pointer to the same register. This eliminates two 32-bit PCI reads and two 32-bit PCI writes. Reported-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 50 ++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3b962ba5ef0..7dfd17707d5 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1184,7 +1184,6 @@ static void handle_port_status(struct xhci_hcd *xhci, /* Update event ring dequeue pointer before dropping the lock */ inc_deq(xhci, xhci->event_ring, true); - xhci_set_hc_event_deq(xhci); spin_unlock(&xhci->lock); /* Pass this up to the core */ @@ -1924,7 +1923,6 @@ cleanup: */ if (trb_comp_code == COMP_MISSED_INT || !ep->skip) { inc_deq(xhci, xhci->event_ring, true); - xhci_set_hc_event_deq(xhci); } if (ret) { @@ -2022,11 +2020,10 @@ static void xhci_handle_event(struct xhci_hcd *xhci) return; } - if (update_ptrs) { - /* Update SW and HC event ring dequeue pointer */ + if (update_ptrs) + /* Update SW event ring dequeue pointer */ inc_deq(xhci, xhci->event_ring, true); - xhci_set_hc_event_deq(xhci); - } + /* Are there more items on the event ring? */ xhci_handle_event(xhci); } @@ -2042,6 +2039,8 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) u32 status, irq_pending; union xhci_trb *trb; u64 temp_64; + union xhci_trb *event_ring_deq; + dma_addr_t deq; spin_lock(&xhci->lock); trb = xhci->event_ring->dequeue; @@ -2090,18 +2089,43 @@ hw_died: irq_pending |= 0x3; xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending); - if (xhci->xhc_state & XHCI_STATE_DYING) + if (xhci->xhc_state & XHCI_STATE_DYING) { xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " "Shouldn't IRQs be disabled?\n"); - else - /* FIXME this should be a delayed service routine - * that clears the EHB. + /* Clear the event handler busy flag (RW1C); + * the event ring should be empty. */ - xhci_handle_event(xhci); + temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + xhci_write_64(xhci, temp_64 | ERST_EHB, + &xhci->ir_set->erst_dequeue); + spin_unlock(&xhci->lock); + + return IRQ_HANDLED; + } + + event_ring_deq = xhci->event_ring->dequeue; + /* FIXME this should be a delayed service routine + * that clears the EHB. + */ + xhci_handle_event(xhci); - /* Clear the event handler busy flag (RW1C); event ring is empty. */ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue); + /* If necessary, update the HW's version of the event ring deq ptr. */ + if (event_ring_deq != xhci->event_ring->dequeue) { + deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, + xhci->event_ring->dequeue); + if (deq == 0) + xhci_warn(xhci, "WARN something wrong with SW event " + "ring dequeue ptr.\n"); + /* Update HC event ring dequeue pointer */ + temp_64 &= ERST_PTR_MASK; + temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); + } + + /* Clear the event handler busy flag (RW1C); event ring is empty. */ + temp_64 |= ERST_EHB; + xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); + spin_unlock(&xhci->lock); return IRQ_HANDLED; -- cgit v1.2.3-70-g09d2 From 257d585aaec469ded6ec15d8a8e7ebada21d7277 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 29 Jul 2010 22:12:56 -0700 Subject: USB: xhci: Make xhci_set_hc_event_deq() static. Now that the event handler functions no longer use xhci_set_hc_event_deq() to update the event ring dequeue pointer, that function is not used by anything in xhci-ring.c. Move that function into xhci-mem.c and make it static. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 23 +++++++++++++++++++++++ drivers/usb/host/xhci-ring.c | 22 ---------------------- drivers/usb/host/xhci.h | 1 - 3 files changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index ac57f538f95..4e51343ddff 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1601,6 +1601,29 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags) return 0; } +static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) +{ + u64 temp; + dma_addr_t deq; + + deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, + xhci->event_ring->dequeue); + if (deq == 0 && !in_interrupt()) + xhci_warn(xhci, "WARN something wrong with SW event ring " + "dequeue ptr.\n"); + /* Update HC event ring dequeue pointer */ + temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp &= ERST_PTR_MASK; + /* Don't clear the EHB bit (which is RW1C) because + * there might be more events to service. + */ + temp &= ~ERST_EHB; + xhci_dbg(xhci, "// Write event ring dequeue pointer, " + "preserving EHB bit\n"); + xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, + &xhci->ir_set->erst_dequeue); +} + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7dfd17707d5..f479f73711a 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -301,28 +301,6 @@ static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring, return 1; } -void xhci_set_hc_event_deq(struct xhci_hcd *xhci) -{ - u64 temp; - dma_addr_t deq; - - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); - if (deq == 0 && !in_interrupt()) - xhci_warn(xhci, "WARN something wrong with SW event ring " - "dequeue ptr.\n"); - /* Update HC event ring dequeue pointer */ - temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - temp &= ERST_PTR_MASK; - /* Don't clear the EHB bit (which is RW1C) because - * there might be more events to service. - */ - temp &= ~ERST_EHB; - xhci_dbg(xhci, "// Write event ring dequeue pointer, preserving EHB bit\n"); - xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, - &xhci->ir_set->erst_dequeue); -} - /* Ring the host controller doorbell after placing a command on the ring */ void xhci_ring_cmd_db(struct xhci_hcd *xhci) { diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 25e108e981f..34a60d9f056 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1400,7 +1400,6 @@ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg, int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code); void xhci_ring_cmd_db(struct xhci_hcd *xhci); void *xhci_setup_one_noop(struct xhci_hcd *xhci); -void xhci_set_hc_event_deq(struct xhci_hcd *xhci); int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id); int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr, u32 slot_id); -- cgit v1.2.3-70-g09d2 From c21599a36165dbc78b380846b254017a548b9de5 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 29 Jul 2010 22:13:00 -0700 Subject: USB: xhci: Reduce reads and writes of interrupter registers. The interrupter register set includes a register that says whether interrupts are pending for each event ring (the IP bit). Each MSI-X vector will get its own interrupter set with separate IP bits. The status register includes an "Event Interrupt (EINT)" bit that is set when an IP bit is set in any of the interrupters. When PCI interrupts are used, the EINT bit exactly mirrors the IP bit in the single interrupter set, and it is a waste of time to check both registers when trying to figure out if the xHC interrupted or another device on the shared IRQ line interrupted. Only check the IP bit to reduce register reads. The IP bit is automatically cleared by the xHC when MSI or MSI-X is enabled. It doesn't make sense to read that register to check for shared interrupts (since MSI and MSI-X aren't shared). It also doesn't make sense to write to that register to clear the IP bit, since it is cleared by the hardware. We can tell whether MSI or MSI-X is enabled by looking at the irq number in hcd->irq. If it's -1, we know MSI or MSI-X is enabled. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f479f73711a..6860e9f097b 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2014,7 +2014,7 @@ static void xhci_handle_event(struct xhci_hcd *xhci) irqreturn_t xhci_irq(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - u32 status, irq_pending; + u32 status; union xhci_trb *trb; u64 temp_64; union xhci_trb *event_ring_deq; @@ -2024,17 +2024,15 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) trb = xhci->event_ring->dequeue; /* Check if the xHC generated the interrupt, or the irq is shared */ status = xhci_readl(xhci, &xhci->op_regs->status); - irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending); - if (status == 0xffffffff && irq_pending == 0xffffffff) + if (status == 0xffffffff) goto hw_died; - if (!(status & STS_EINT) && !ER_IRQ_PENDING(irq_pending)) { + if (!(status & STS_EINT)) { spin_unlock(&xhci->lock); xhci_warn(xhci, "Spurious interrupt.\n"); return IRQ_NONE; } xhci_dbg(xhci, "op reg status = %08x\n", status); - xhci_dbg(xhci, "ir set irq_pending = %08x\n", irq_pending); xhci_dbg(xhci, "Event ring dequeue ptr:\n"); xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n", (unsigned long long) @@ -2063,9 +2061,13 @@ hw_died: /* FIXME when MSI-X is supported and there are multiple vectors */ /* Clear the MSI-X event interrupt status */ - /* Acknowledge the interrupt */ - irq_pending |= 0x3; - xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending); + if (hcd->irq != -1) { + u32 irq_pending; + /* Acknowledge the PCI interrupt */ + irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending); + irq_pending |= 0x3; + xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending); + } if (xhci->xhc_state & XHCI_STATE_DYING) { xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " -- cgit v1.2.3-70-g09d2 From ed3f24539209826235debaec66ca0da708b5bb89 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 29 Jul 2010 22:13:17 -0700 Subject: USB: xhci: Don't flush doorbell writes. To tell the host controller that there are transfers on the endpoint rings, we need to ring the endpoint doorbell. This is a PCI MMIO write, which can be delayed until another register read is queued. The previous code would flush the doorbell write by reading the doorbell register after the write. This may take time, and it's not necessary to force the host controller to know about the transfers right away. Don't flush the doorbell register writes. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 6860e9f097b..bc3f4f42706 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -337,11 +337,6 @@ static void ring_ep_doorbell(struct xhci_hcd *xhci, field = xhci_readl(xhci, db_addr) & DB_MASK; field |= EPI_TO_DB(ep_index) | STREAM_ID_TO_DB(stream_id); xhci_writel(xhci, field, db_addr); - /* Flush PCI posted writes - FIXME Matthew Wilcox says this - * isn't time-critical and we shouldn't make the CPU wait for - * the flush. - */ - xhci_readl(xhci, db_addr); } } -- cgit v1.2.3-70-g09d2 From 006d5820b41b298328e6b6085e4d58129fbc78f0 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 29 Jul 2010 22:13:22 -0700 Subject: USB: xhci: Set DMA mask for host. Tell the USB core that we can do DMA directly (instead of needing it to memory-map the buffers for PIO). If the xHCI host supports 64-bit addresses, set the DMA mask accordingly. Otherwise indicate the host can handle 32-bit DMA addresses. This improves performance because the USB core doesn't have to spend time remapping buffers in high memory into the 32-bit address range. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 11482b6b938..f7efe025bed 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -53,6 +53,7 @@ static int xhci_pci_setup(struct usb_hcd *hcd) struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); int retval; + u32 temp; hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2; @@ -93,6 +94,14 @@ static int xhci_pci_setup(struct usb_hcd *hcd) return retval; xhci_dbg(xhci, "Reset complete\n"); + temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + if (HCC_64BIT_ADDR(temp)) { + xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); + } else { + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); + } + xhci_dbg(xhci, "Calling HCD init\n"); /* Initialize HCD and host controller data structures. */ retval = xhci_init(hcd); -- cgit v1.2.3-70-g09d2 From e10e1bec8e6654de4591ef45ddd6a6d1e5b2591c Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 2 Aug 2010 22:09:01 +0800 Subject: USB: usbtest: avoid to free coherent buffer in atomic context This patch fixes the warning below: [30753.755998] ------------[ cut here ]------------ [30753.755998] WARNING: at /home/tom/git/linux-2.6/linux-2.6-next/arch/x86/include/asm/dma-mapping.h:155 hcd_buffer_free+0xb1/0xd4 [usbcore]() [30753.755998] Hardware name: 6475EK2 [30753.755998] Modules linked in: uvcvideo ehci_hcd usbtest cdc_ether usbnet vfat fat usb_storage nfsd lockd nfs_acl auth_rpcgss exportfs mii tun videodev v4l1_compat v4l2_compat_ioctl32 fuse bridge stp llc sunrpc ipv6 cpufreq_ondemand acpi_cpufreq freq_table mperf kvm_intel kvm arc4 ecb ath5k usbhid mac80211 snd_hda_codec_conexant ch341 usbserial ath cfg80211 thinkpad_acpi snd_hda_intel pcspkr wmi hwmon yenta_socket iTCO_wdt iTCO_vendor_support i2c_i801 e1000e snd_hda_codec snd_hwdep snd_pcm snd_timer snd soundcore snd_page_alloc pata_acpi uhci_hcd ohci_hcd usbcore i915 drm_kms_helper drm i2c_algo_bit i2c_core video output [last unloaded: uvcvideo] [30753.755998] Pid: 0, comm: swapper Tainted: G W 2.6.35-rc6-gkh-wl+ #49 [30753.755998] Call Trace: [30753.755998] [] warn_slowpath_common+0x80/0x98 [30753.755998] [] warn_slowpath_null+0x15/0x17 [30753.755998] [] hcd_buffer_free+0xb1/0xd4 [usbcore] [30753.755998] [] usb_free_coherent+0x1c/0x1e [usbcore] [30753.755998] [] simple_free_urb+0x23/0x2f [usbtest] [30753.755998] [] iso_callback+0xbb/0x10f [usbtest] [30753.755998] [] usb_hcd_giveback_urb+0x8c/0xc0 [usbcore] [30753.755998] [] ehci_urb_done+0x84/0x95 [ehci_hcd] [30753.755998] [] ehci_work+0x41a/0x7dd [ehci_hcd] [30753.755998] [] ehci_irq+0x33b/0x370 [ehci_hcd] [30753.755998] [] ? sched_clock+0x9/0xd [30753.755998] [] ? sched_clock_local+0x1c/0x82 [30753.755998] [] ? sched_clock_cpu+0xc3/0xce [30753.755998] [] ? trace_hardirqs_off+0xd/0xf [30753.755998] [] ? cpu_clock+0x43/0x5e [30753.755998] [] usb_hcd_irq+0x45/0xa1 [usbcore] [30753.755998] [] handle_IRQ_event+0x20/0xa5 [30753.755998] [] handle_fasteoi_irq+0x92/0xd2 [30753.755998] [] handle_irq+0x1f/0x2a [30753.755998] [] do_IRQ+0x57/0xbe [30753.755998] [] ret_from_intr+0x0/0x16 [30753.755998] [] ? acpi_idle_enter_bm+0x231/0x269 [30753.755998] [] ? acpi_idle_enter_bm+0x22a/0x269 [30753.755998] [] cpuidle_idle_call+0x99/0xce [30753.755998] [] cpu_idle+0x61/0xaa [30753.755998] [] start_secondary+0x1c2/0x1c6 [30753.755998] ---[ end trace 904cfaf7ab4cb1a2 ]--- Signed-off-by: Ming Lei Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 0cfbd789ddf..d92b7ec9a23 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1378,7 +1378,6 @@ static void iso_callback (struct urb *urb) break; } } - simple_free_urb (urb); ctx->pending--; if (ctx->pending == 0) { @@ -1495,6 +1494,7 @@ test_iso_queue (struct usbtest_dev *dev, struct usbtest_param *param, } simple_free_urb (urbs [i]); + urbs[i] = NULL; context.pending--; context.submit_error = 1; break; @@ -1504,6 +1504,10 @@ test_iso_queue (struct usbtest_dev *dev, struct usbtest_param *param, wait_for_completion (&context.done); + for (i = 0; i < param->sglen; i++) { + if (urbs[i]) + simple_free_urb(urbs[i]); + } /* * Isochronous transfers are expected to fail sometimes. As an * arbitrary limit, we will report an error if any submissions -- cgit v1.2.3-70-g09d2 From 951fd8ee6a9493f80bc5bccf2c8cfa1535c6ce15 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 2 Aug 2010 22:09:17 +0800 Subject: USB: usbtest: support test device with only one iso-in or iso-out endpoint It is very common that one altsetting may include only one iso-in or iso-out single endpoint, especially for high bandwidth endpoint, so support it. Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index d92b7ec9a23..eef370eb7a5 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -136,7 +136,7 @@ try_iso: iso_out = e; } } - if ((in && out) || (iso_in && iso_out)) + if ((in && out) || iso_in || iso_out) goto found; } return -EINVAL; @@ -162,6 +162,9 @@ found: dev->in_iso_pipe = usb_rcvisocpipe (udev, iso_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + } + + if (iso_out) { dev->iso_out = &iso_out->desc; dev->out_iso_pipe = usb_sndisocpipe (udev, iso_out->desc.bEndpointAddress -- cgit v1.2.3-70-g09d2 From b3e670443b7fb8a2d29831b62b44a039c283e351 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Tue, 3 Aug 2010 02:32:28 +0200 Subject: USB: fix thread-unsafe anchor utiliy routines This patch fixes a race condition in two utility routines related to the removal/unlinking of urbs from an anchor. If two threads are concurrently accessing the same anchor, both could end up with the same urb - thinking they are the exclusive owner. Alan Stern pointed out a related issue in usb_unlink_anchored_urbs: "The URB isn't removed from the anchor until it completes (as a by-product of completion, in fact), which might not be for quite some time after the unlink call returns. In the meantime, the subroutine will keep trying to unlink it, over and over again." Cc: stable Cc: Oliver Neukum Cc: Greg Kroah-Hartman Acked-by: Alan Stern Signed-off-by: Christian Lamparter Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/urb.c | 50 +++++++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 7c0555548ac..419e6b34e2f 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -137,6 +137,16 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor) } EXPORT_SYMBOL_GPL(usb_anchor_urb); +/* Callers must hold anchor->lock */ +static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor) +{ + urb->anchor = NULL; + list_del(&urb->anchor_list); + usb_put_urb(urb); + if (list_empty(&anchor->urb_list)) + wake_up(&anchor->wait); +} + /** * usb_unanchor_urb - unanchors an URB * @urb: pointer to the urb to anchor @@ -156,17 +166,14 @@ void usb_unanchor_urb(struct urb *urb) return; spin_lock_irqsave(&anchor->lock, flags); - if (unlikely(anchor != urb->anchor)) { - /* we've lost the race to another thread */ - spin_unlock_irqrestore(&anchor->lock, flags); - return; - } - urb->anchor = NULL; - list_del(&urb->anchor_list); + /* + * At this point, we could be competing with another thread which + * has the same intention. To protect the urb from being unanchored + * twice, only the winner of the race gets the job. + */ + if (likely(anchor == urb->anchor)) + __usb_unanchor_urb(urb, anchor); spin_unlock_irqrestore(&anchor->lock, flags); - usb_put_urb(urb); - if (list_empty(&anchor->urb_list)) - wake_up(&anchor->wait); } EXPORT_SYMBOL_GPL(usb_unanchor_urb); @@ -749,20 +756,11 @@ EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs); void usb_unlink_anchored_urbs(struct usb_anchor *anchor) { struct urb *victim; - unsigned long flags; - spin_lock_irqsave(&anchor->lock, flags); - while (!list_empty(&anchor->urb_list)) { - victim = list_entry(anchor->urb_list.prev, struct urb, - anchor_list); - usb_get_urb(victim); - spin_unlock_irqrestore(&anchor->lock, flags); - /* this will unanchor the URB */ + while ((victim = usb_get_from_anchor(anchor)) != NULL) { usb_unlink_urb(victim); usb_put_urb(victim); - spin_lock_irqsave(&anchor->lock, flags); } - spin_unlock_irqrestore(&anchor->lock, flags); } EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs); @@ -799,12 +797,11 @@ struct urb *usb_get_from_anchor(struct usb_anchor *anchor) victim = list_entry(anchor->urb_list.next, struct urb, anchor_list); usb_get_urb(victim); - spin_unlock_irqrestore(&anchor->lock, flags); - usb_unanchor_urb(victim); + __usb_unanchor_urb(victim, anchor); } else { - spin_unlock_irqrestore(&anchor->lock, flags); victim = NULL; } + spin_unlock_irqrestore(&anchor->lock, flags); return victim; } @@ -826,12 +823,7 @@ void usb_scuttle_anchored_urbs(struct usb_anchor *anchor) while (!list_empty(&anchor->urb_list)) { victim = list_entry(anchor->urb_list.prev, struct urb, anchor_list); - usb_get_urb(victim); - spin_unlock_irqrestore(&anchor->lock, flags); - /* this may free the URB */ - usb_unanchor_urb(victim); - usb_put_urb(victim); - spin_lock_irqsave(&anchor->lock, flags); + __usb_unanchor_urb(victim, anchor); } spin_unlock_irqrestore(&anchor->lock, flags); } -- cgit v1.2.3-70-g09d2 From b409214c683ed06c26e2cdad0be546ad11463354 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 5 Aug 2010 13:12:14 -0400 Subject: USB: remove fake "address-of" expressions Fake "address-of" expressions that evaluate to NULL generally confuse readers and can provoke compiler warnings. This patch (as1412) removes three such fake expressions, using "#ifdef"s in their place. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 6 ++---- drivers/usb/core/usb.c | 6 ++---- drivers/usb/host/uhci-debug.c | 3 +-- drivers/usb/host/uhci-hcd.c | 23 ++++++++++------------- 4 files changed, 15 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 880f65baf58..d7a4401ef01 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1791,15 +1791,13 @@ static const struct dev_pm_ops usb_bus_pm_ops = { .runtime_idle = usb_runtime_idle, }; -#else - -#define usb_bus_pm_ops (*(const struct dev_pm_ops *) NULL) - #endif /* CONFIG_USB_SUSPEND */ struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, .uevent = usb_uevent, +#ifdef CONFIG_USB_SUSPEND .pm = &usb_bus_pm_ops, +#endif }; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 5ae14f6c1e7..fdd4130fbb7 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -317,10 +317,6 @@ static const struct dev_pm_ops usb_device_pm_ops = { .restore = usb_dev_restore, }; -#else - -#define usb_device_pm_ops (*(struct dev_pm_ops *) NULL) - #endif /* CONFIG_PM */ @@ -338,7 +334,9 @@ struct device_type usb_device_type = { .release = usb_release_dev, .uevent = usb_dev_uevent, .devnode = usb_devnode, +#ifdef CONFIG_PM .pm = &usb_device_pm_ops, +#endif }; diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index c168999722d..6e7fb5f38db 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -17,7 +17,6 @@ #include "uhci-hcd.h" -#define uhci_debug_operations (* (const struct file_operations *) NULL) static struct dentry *uhci_debugfs_root; #ifdef DEBUG @@ -558,7 +557,6 @@ static int uhci_debug_release(struct inode *inode, struct file *file) return 0; } -#undef uhci_debug_operations static const struct file_operations uhci_debug_operations = { .owner = THIS_MODULE, .open = uhci_debug_open, @@ -566,6 +564,7 @@ static const struct file_operations uhci_debug_operations = { .read = uhci_debug_read, .release = uhci_debug_release, }; +#define UHCI_DEBUG_OPS #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index b04506036b6..f52d04db28f 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -597,7 +597,7 @@ static int uhci_start(struct usb_hcd *hcd) struct uhci_hcd *uhci = hcd_to_uhci(hcd); int retval = -EBUSY; int i; - struct dentry *dentry; + struct dentry __maybe_unused *dentry; hcd->uses_new_polling = 1; @@ -607,18 +607,16 @@ static int uhci_start(struct usb_hcd *hcd) INIT_LIST_HEAD(&uhci->idle_qh_list); init_waitqueue_head(&uhci->waitqh); - if (DEBUG_CONFIGURED) { - dentry = debugfs_create_file(hcd->self.bus_name, - S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, - uhci, &uhci_debug_operations); - if (!dentry) { - dev_err(uhci_dev(uhci), "couldn't create uhci " - "debugfs entry\n"); - retval = -ENOMEM; - goto err_create_debug_entry; - } - uhci->dentry = dentry; +#ifdef UHCI_DEBUG_OPS + dentry = debugfs_create_file(hcd->self.bus_name, + S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, + uhci, &uhci_debug_operations); + if (!dentry) { + dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n"); + return -ENOMEM; } + uhci->dentry = dentry; +#endif uhci->frame = dma_alloc_coherent(uhci_dev(uhci), UHCI_NUMFRAMES * sizeof(*uhci->frame), @@ -732,7 +730,6 @@ err_alloc_frame_cpu: err_alloc_frame: debugfs_remove(uhci->dentry); -err_create_debug_entry: return retval; } -- cgit v1.2.3-70-g09d2 From b58af4066d240b18b43f202e07b9ec7461d90b17 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 4 Aug 2010 15:45:57 +0200 Subject: USB: serial: fix stalled writes As David VomLehn points out, it was possible to receive an interrupt before clearing the free-urb flag which could lead to the urb being incorrectly marked as busy. For the same reason, move tx_bytes accounting so that it will never be negative. Note that the free-flags set and clear operations do not need any additional locking as they are manipulated while USB_SERIAL_WRITE_BUSY is set. Reported-by: David VomLehn Tested-by: David VomLehn Signed-off-by: Johan Hovold Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/generic.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index a817ced8283..ca92f67747c 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -208,18 +208,23 @@ retry: urb->transfer_buffer_length = count; usb_serial_debug_data(debug, &port->dev, __func__, count, urb->transfer_buffer); + spin_lock_irqsave(&port->lock, flags); + port->tx_bytes += count; + spin_unlock_irqrestore(&port->lock, flags); + + clear_bit(i, &port->write_urbs_free); result = usb_submit_urb(urb, GFP_ATOMIC); if (result) { dev_err(&port->dev, "%s - error submitting urb: %d\n", __func__, result); + set_bit(i, &port->write_urbs_free); + spin_lock_irqsave(&port->lock, flags); + port->tx_bytes -= count; + spin_unlock_irqrestore(&port->lock, flags); + clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags); return result; } - clear_bit(i, &port->write_urbs_free); - - spin_lock_irqsave(&port->lock, flags); - port->tx_bytes += count; - spin_unlock_irqrestore(&port->lock, flags); /* Try sending off another urb, unless in irq context (in which case * there will be no free urb). */ -- cgit v1.2.3-70-g09d2