From 7fe4e995ccc36f56acc51ee3a1c392ac23d2a122 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 7 Feb 2011 11:51:07 -0500 Subject: [PATCH 01/15] USB: gadget: f_mtp: Fix problems transferring files from device to host Exit from send_file_work immediately when a cancel request is received. Only busy status if there is a cancel pending that has not been repoorted to userspace. This avoids a race condition that can occur when mtp_read resets the state to STATE_BUSY before we report status OK back to the host. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_mtp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index e07224fd9f89..d560fbcf4f58 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -662,6 +662,10 @@ static void send_file_work(struct work_struct *data) { ret = wait_event_interruptible(dev->write_wq, (req = req_get(dev, &dev->tx_idle)) || dev->state != STATE_BUSY); + if (dev->state == STATE_CANCELED) { + r = -ECANCELED; + break; + } if (!req) { r = ret; break; @@ -1098,14 +1102,13 @@ static int mtp_function_setup(struct usb_function *f, /* device status is "busy" until we report * the cancelation to userspace */ - if (dev->state == STATE_BUSY - || dev->state == STATE_CANCELED) + if (dev->state == STATE_CANCELED) status->wCode = __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY); else status->wCode = __cpu_to_le16(MTP_RESPONSE_OK); - spin_unlock_irqrestore(&dev->lock, flags); + spin_unlock_irqrestore(&dev->lock, flags); value = sizeof(*status); } } @@ -1185,7 +1188,7 @@ static void mtp_function_disable(struct usb_function *f) static int mtp_bind_config(struct usb_configuration *c) { struct mtp_dev *dev; - int ret; + int ret = 0; printk(KERN_INFO "mtp_bind_config\n"); From 736268d863c33759fe51f640eaa175ccb1a5439c Mon Sep 17 00:00:00 2001 From: Robert Morell Date: Wed, 26 Jan 2011 19:06:47 -0800 Subject: [PATCH 02/15] USB: HCD: Add usb_hcd prefix to exported functions The convention is to prefix symbols exported from the USB HCD core with "usb_hcd". This change makes unmap_urb_setup_for_dma() and unmap_urb_for_dma() consistent with that. Signed-off-by: Robert Morell Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 9 +++++---- include/linux/usb/hcd.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 5cca00a6d09d..a161503acf15 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1263,7 +1263,7 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, *dma_handle = 0; } -static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) { enum dma_data_direction dir; @@ -1307,6 +1307,7 @@ static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE | URB_MAP_LOCAL); } +EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_for_dma); static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) @@ -1400,7 +1401,7 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, } if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL))) - unmap_urb_for_dma(hcd, urb); + usb_hcd_unmap_urb_for_dma(hcd, urb); } return ret; } @@ -1441,7 +1442,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) if (likely(status == 0)) { status = hcd->driver->urb_enqueue(hcd, urb, mem_flags); if (unlikely(status)) - unmap_urb_for_dma(hcd, urb); + usb_hcd_unmap_urb_for_dma(hcd, urb); } } @@ -1547,7 +1548,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) !status)) status = -EREMOTEIO; - unmap_urb_for_dma(hcd, urb); + usb_hcd_unmap_urb_for_dma(hcd, urb); usbmon_urb_complete(&hcd->self, urb, status); usb_unanchor_urb(urb); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 3b571f1ffbb3..cc8b93eb3302 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -329,6 +329,7 @@ extern int usb_hcd_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_hcd_unlink_urb(struct urb *urb, int status); extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status); +extern void usb_hcd_unmap_urb_for_dma(struct usb_hcd *, struct urb *); extern void usb_hcd_flush_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); extern void usb_hcd_disable_endpoint(struct usb_device *udev, From a0d7774b258a555750ced45ce9afe21aac63a9f0 Mon Sep 17 00:00:00 2001 From: Robert Morell Date: Wed, 26 Jan 2011 19:06:48 -0800 Subject: [PATCH 03/15] USB: HCD: Add driver hooks for (un)?map_urb_for_dma Provide optional hooks for the host controller driver to override the default DMA mapping and unmapping routines. In general, these shouldn't be necessary unless the host controller has special DMA requirements, such as alignment contraints. If these are not specified, the general usb_hcd_(un)?map_urb_for_dma functions will be used instead. Also, pass the status to unmap_urb_for_dma so it can know whether the DMA buffer has been overwritten. Finally, add a flag to be used by these implementations if they allocated a temporary buffer so it can be freed properly when unmapping. Signed-off-by: Robert Morell Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 22 ++++++++++++++++++++-- include/linux/usb.h | 1 + include/linux/usb/hcd.h | 15 +++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index a161503acf15..f24f40eba180 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1263,6 +1263,14 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, *dma_handle = 0; } +static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + if (hcd->driver->unmap_urb_for_dma) + hcd->driver->unmap_urb_for_dma(hcd, urb); + else + usb_hcd_unmap_urb_for_dma(hcd, urb); +} + void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) { enum dma_data_direction dir; @@ -1311,6 +1319,15 @@ EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_for_dma); static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) +{ + if (hcd->driver->map_urb_for_dma) + return hcd->driver->map_urb_for_dma(hcd, urb, mem_flags); + else + return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); +} + +int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) { enum dma_data_direction dir; int ret = 0; @@ -1405,6 +1422,7 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, } return ret; } +EXPORT_SYMBOL_GPL(usb_hcd_map_urb_for_dma); /*-------------------------------------------------------------------------*/ @@ -1442,7 +1460,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) if (likely(status == 0)) { status = hcd->driver->urb_enqueue(hcd, urb, mem_flags); if (unlikely(status)) - usb_hcd_unmap_urb_for_dma(hcd, urb); + unmap_urb_for_dma(hcd, urb); } } @@ -1548,7 +1566,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) !status)) status = -EREMOTEIO; - usb_hcd_unmap_urb_for_dma(hcd, urb); + unmap_urb_for_dma(hcd, urb); usbmon_urb_complete(&hcd->self, urb, status); usb_unanchor_urb(urb); diff --git a/include/linux/usb.h b/include/linux/usb.h index 35fe6ab222bb..6d4566ee34c9 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -975,6 +975,7 @@ extern int usb_disabled(void); #define URB_SETUP_MAP_SINGLE 0x00100000 /* Setup packet DMA mapped */ #define URB_SETUP_MAP_LOCAL 0x00200000 /* HCD-local setup packet */ #define URB_DMA_SG_COMBINED 0x00400000 /* S-G entries were combined */ +#define URB_ALIGNED_TEMP_BUFFER 0x00800000 /* Temp buffer was alloc'd */ struct usb_iso_packet_descriptor { unsigned int offset; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index cc8b93eb3302..6e1b7507e103 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -233,6 +233,19 @@ struct hc_driver { int (*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb, int status); + /* + * (optional) these hooks allow an HCD to override the default DMA + * mapping and unmapping routines. In general, they shouldn't be + * necessary unless the host controller has special DMA requirements, + * such as alignment contraints. If these are not specified, the + * general usb_hcd_(un)?map_urb_for_dma functions will be used instead + * (and it may be a good idea to call these functions in your HCD + * implementation) + */ + int (*map_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags); + void (*unmap_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb); + /* hw synch, freeing endpoint resources that urb_dequeue can't */ void (*endpoint_disable)(struct usb_hcd *hcd, struct usb_host_endpoint *ep); @@ -329,6 +342,8 @@ extern int usb_hcd_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_hcd_unlink_urb(struct urb *urb, int status); extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status); +extern int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags); extern void usb_hcd_unmap_urb_for_dma(struct usb_hcd *, struct urb *); extern void usb_hcd_flush_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); From da0fbc7f9b0141e7f8665b142509da681f60c9ea Mon Sep 17 00:00:00 2001 From: Robert Morell Date: Wed, 26 Jan 2011 19:06:49 -0800 Subject: [PATCH 04/15] USB: ehci: tegra: Align DMA transfers to 32 bytes The Tegra2 USB controller doesn't properly deal with misaligned DMA buffers, causing corruption. This is especially prevalent with USB network adapters, where skbuff alignment is often in the middle of a 4-byte dword. To avoid this, allocate a temporary buffer for the DMA if the provided buffer isn't sufficiently aligned. Signed-off-by: Robert Morell --- drivers/usb/host/ehci-tegra.c | 90 +++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 3c72ed4c6cdc..a4f297544a1c 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -32,6 +32,8 @@ #define TEGRA_USB_USBMODE_HOST (3 << 0) #define TEGRA_USB_PORTSC1_PTC(x) (((x) & 0xf) << 16) +#define TEGRA_USB_DMA_ALIGN 32 + struct tegra_ehci_context { bool valid; u32 command; @@ -468,6 +470,92 @@ static int tegra_ehci_bus_resume(struct usb_hcd *hcd) } #endif +struct temp_buffer { + void *kmalloc_ptr; + void *old_xfer_buffer; + u8 data[0]; +}; + +static void free_temp_buffer(struct urb *urb) +{ + enum dma_data_direction dir; + struct temp_buffer *temp; + + if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) + return; + + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + temp = container_of(urb->transfer_buffer, struct temp_buffer, + data); + + if (dir == DMA_FROM_DEVICE) + memcpy(temp->old_xfer_buffer, temp->data, + urb->transfer_buffer_length); + urb->transfer_buffer = temp->old_xfer_buffer; + kfree(temp->kmalloc_ptr); + + urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; +} + +static int alloc_temp_buffer(struct urb *urb, gfp_t mem_flags) +{ + enum dma_data_direction dir; + struct temp_buffer *temp, *kmalloc_ptr; + size_t kmalloc_size; + + if (urb->num_sgs || urb->sg || + urb->transfer_buffer_length == 0 || + !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1))) + return 0; + + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + /* Allocate a buffer with enough padding for alignment */ + kmalloc_size = urb->transfer_buffer_length + + sizeof(struct temp_buffer) + TEGRA_USB_DMA_ALIGN - 1; + + kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); + if (!kmalloc_ptr) + return -ENOMEM; + + /* Position our struct temp_buffer such that data is aligned */ + temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1; + + temp->kmalloc_ptr = kmalloc_ptr; + temp->old_xfer_buffer = urb->transfer_buffer; + if (dir == DMA_TO_DEVICE) + memcpy(temp->data, urb->transfer_buffer, + urb->transfer_buffer_length); + urb->transfer_buffer = temp->data; + + urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; + + return 0; +} + +static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + int ret; + + ret = alloc_temp_buffer(urb, mem_flags); + if (ret) + return ret; + + ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); + if (ret) + free_temp_buffer(urb); + + return ret; +} + +static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + usb_hcd_unmap_urb_for_dma(hcd, urb); + free_temp_buffer(urb); +} + static const struct hc_driver tegra_ehci_hc_driver = { .description = hcd_name, .product_desc = "Tegra EHCI Host Controller", @@ -483,6 +571,8 @@ static const struct hc_driver tegra_ehci_hc_driver = { .shutdown = tegra_ehci_shutdown, .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, + .map_urb_for_dma = tegra_ehci_map_urb_for_dma, + .unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma, .endpoint_disable = ehci_endpoint_disable, .endpoint_reset = ehci_endpoint_reset, .get_frame_number = ehci_get_frame, From 6b825e9aaa011f01e433c57db9f191cb99f30709 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 7 Feb 2011 13:42:34 -0800 Subject: [PATCH 05/15] ARM: tegra: Handle timers during LP2 idle ticks Timer ticks aren't properly serviced while a CPU is in LP2 idle. Although the Tegra LP2 idle code calls hrtimer_peek_ahead_timers, because no IRQ regs have been saved, update_process_times is not called, and thus the timer list is not serviced (and neither is SMP rebalancing, etc.) This can cause significant delays scheduling timer-based activity, especially on CPU 1 (which is not servicing most other IRQs). Colin Cross suggested a patch based on upstream review feedback that uses clock notifiers to switch to the "broadcast" clock event source ("timer0" Tegra timer 3) during LP2, which has a real interrupt handler defined that calls the clock event handler in IRQ context, allowing timers to be checked. Change-Id: Ifa3f4ec662f07dc9636e433f278358f75b65d10c Signed-off-by: Todd Poynor --- arch/arm/mach-tegra/cpuidle.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c index a063c34ecf60..23cb9acc588c 100644 --- a/arch/arm/mach-tegra/cpuidle.c +++ b/arch/arm/mach-tegra/cpuidle.c @@ -445,6 +445,7 @@ static int tegra_idle_enter_lp2(struct cpuidle_device *dev, return tegra_idle_enter_lp3(dev, state); local_irq_disable(); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); local_fiq_disable(); enter = ktime_get(); @@ -463,11 +464,9 @@ static int tegra_idle_enter_lp2(struct cpuidle_device *dev, us = ktime_to_us(exit); local_fiq_enable(); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); local_irq_enable(); - /* cpu clockevents may have been reset by powerdown */ - hrtimer_peek_ahead_timers(); - smp_rmb(); state->exit_latency = tegra_lp2_exit_latency; state->target_residency = tegra_lp2_exit_latency + From 99648ea95eaa167e3cb7bc37403fdc7d5da0e56c Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 9 Feb 2011 09:38:26 -0500 Subject: [PATCH 06/15] USB: gadget: android: Support switching vendor ID when configuration changes Based on the list of enabled USB functions, we can now switch the vendor ID as well as the product ID. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/android.c | 32 +++++++++++++++++++++------ include/linux/usb/android_composite.h | 7 +++++- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 213e8a2d7825..8e6cafc851cb 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -65,6 +65,7 @@ struct android_dev { int num_functions; char **functions; + int vendor_id; int product_id; int version; }; @@ -255,6 +256,22 @@ static int product_matches_functions(struct android_usb_product *p) return 1; } +static int get_vendor_id(struct android_dev *dev) +{ + struct android_usb_product *p = dev->products; + int count = dev->num_products; + int i; + + if (p) { + for (i = 0; i < count; i++, p++) { + if (p->vendor_id && product_matches_functions(p)) + return p->vendor_id; + } + } + /* use default vendor ID */ + return dev->vendor_id; +} + static int get_product_id(struct android_dev *dev) { struct android_usb_product *p = dev->products; @@ -275,7 +292,7 @@ static int android_bind(struct usb_composite_dev *cdev) { struct android_dev *dev = _android_dev; struct usb_gadget *gadget = cdev->gadget; - int gcnum, id, product_id, ret; + int gcnum, id, ret; printk(KERN_INFO "android_bind\n"); @@ -325,8 +342,8 @@ static int android_bind(struct usb_composite_dev *cdev) usb_gadget_set_selfpowered(gadget); dev->cdev = cdev; - product_id = get_product_id(dev); - device_desc.idProduct = __constant_cpu_to_le16(product_id); + device_desc.idVendor = __constant_cpu_to_le16(get_vendor_id(dev)); + device_desc.idProduct = __constant_cpu_to_le16(get_product_id(dev)); cdev->desc.idProduct = device_desc.idProduct; return 0; @@ -396,7 +413,6 @@ void android_enable_function(struct usb_function *f, int enable) { struct android_dev *dev = _android_dev; int disable = !enable; - int product_id; if (!!f->disabled != disable) { usb_function_set_enabled(f, !disable); @@ -418,8 +434,8 @@ void android_enable_function(struct usb_function *f, int enable) update_dev_desc(dev); - product_id = get_product_id(dev); - device_desc.idProduct = __constant_cpu_to_le16(product_id); + device_desc.idVendor = __constant_cpu_to_le16(get_vendor_id(dev)); + device_desc.idProduct = __constant_cpu_to_le16(get_product_id(dev)); if (dev->cdev) dev->cdev->desc.idProduct = device_desc.idProduct; usb_composite_force_reset(dev->cdev); @@ -438,9 +454,11 @@ static int android_probe(struct platform_device *pdev) dev->num_products = pdata->num_products; dev->functions = pdata->functions; dev->num_functions = pdata->num_functions; - if (pdata->vendor_id) + if (pdata->vendor_id) { + dev->vendor_id = pdata->vendor_id; device_desc.idVendor = __constant_cpu_to_le16(pdata->vendor_id); + } if (pdata->product_id) { dev->product_id = pdata->product_id; device_desc.idProduct = diff --git a/include/linux/usb/android_composite.h b/include/linux/usb/android_composite.h index 62e72e3bd2b6..7f9000711f4e 100644 --- a/include/linux/usb/android_composite.h +++ b/include/linux/usb/android_composite.h @@ -27,7 +27,12 @@ struct android_usb_function { }; struct android_usb_product { - /* Default product ID. */ + /* Vendor ID for this set of functions. + * Default vendor_id in platform data will be used if this is zero. + */ + __u16 vendor_id; + + /* Product ID for this set of functions. */ __u16 product_id; /* List of function names associated with this product. From f35c505ce33f5cb97dca9532c510740a666161e4 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 10 Feb 2011 11:54:53 -0500 Subject: [PATCH 07/15] USB: gadget: f_mtp: Don't block in mtp_send_event We used to wait for the previous interrupt packet to complete before sending the next packet. But unfortunately the previous packet will not complete until USB is disconnected if the host is not listening on the interrupt endpoint (which is the case with libmtp on Linux and Mac). To avoid hanging indefinitely in this case, we now simply return -EBUSY if the previous interrupt packet has not completed yet. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_mtp.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index d560fbcf4f58..8128b203e76f 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -92,7 +92,6 @@ struct mtp_dev { wait_queue_head_t read_wq; wait_queue_head_t write_wq; - wait_queue_head_t intr_wq; struct usb_request *rx_req[RX_REQ_MAX]; struct usb_request *intr_req; int rx_done; @@ -373,12 +372,11 @@ static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req) { struct mtp_dev *dev = _mtp_dev; - DBG(dev->cdev, "mtp_complete_intr status: %d actual: %d\n", req->status, req->actual); + DBG(dev->cdev, "mtp_complete_intr status: %d actual: %d\n", + req->status, req->actual); dev->intr_busy = 0; if (req->status != 0) dev->state = STATE_ERROR; - - wake_up(&dev->intr_wq); } static int __init create_bulk_endpoints(struct mtp_dev *dev, @@ -798,13 +796,15 @@ static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) if (length < 0 || length > INTR_BUFFER_SIZE) return -EINVAL; - - /* wait for a request to complete */ - ret = wait_event_interruptible(dev->intr_wq, !dev->intr_busy || dev->state == STATE_OFFLINE); - if (ret < 0) - return ret; if (dev->state == STATE_OFFLINE) return -ENODEV; + /* unfortunately an interrupt request might hang indefinitely if the host + * is not listening on the interrupt endpoint, so instead of waiting, + * we just fail if the endpoint is busy. + */ + if (dev->intr_busy) + return -EBUSY; + req = dev->intr_req; if (copy_from_user(req->buf, (void __user *)event->data, length)) return -EFAULT; @@ -1016,7 +1016,6 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) mtp_request_free(dev->intr_req, dev->ep_intr); dev->state = STATE_OFFLINE; spin_unlock_irq(&dev->lock); - wake_up(&dev->intr_wq); misc_deregister(&mtp_device); kfree(_mtp_dev); @@ -1180,7 +1179,6 @@ static void mtp_function_disable(struct usb_function *f) /* readers may be blocked waiting for us to go online */ wake_up(&dev->read_wq); - wake_up(&dev->intr_wq); VDBG(cdev, "%s disabled\n", dev->function.name); } @@ -1208,7 +1206,6 @@ static int mtp_bind_config(struct usb_configuration *c) spin_lock_init(&dev->lock); init_waitqueue_head(&dev->read_wq); init_waitqueue_head(&dev->write_wq); - init_waitqueue_head(&dev->intr_wq); atomic_set(&dev->open_excl, 0); atomic_set(&dev->ioctl_excl, 0); INIT_LIST_HEAD(&dev->tx_idle); From 5f2e1258ff35f700f5ca9df3047dc5fe19b99017 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 27 Jan 2011 16:12:55 -0800 Subject: [PATCH 08/15] Revert "mmc: fix non-arm build due to eMMC 4.3+ change" This reverts commit 1c119f2c1667ea2747693f7fd2166205e1a10b09. --- drivers/mmc/core/mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 45055c46d954..f5b2aaf2371f 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -255,8 +255,8 @@ static int mmc_read_ext_csd(struct mmc_card *card) /* Cards with density > 2GiB are sector addressed */ if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512) { unsigned boot_sectors; - /* size is in 256K chunks, i.e. 512 sectors each */ - boot_sectors = ext_csd[EXT_CSD_BOOT_SIZE_MULTI] * 512; + boot_sectors = ext_csd[EXT_CSD_BOOT_SIZE_MULTI]; + boot_sectors *= SZ_256K / 512; card->ext_csd.sectors -= boot_sectors; mmc_card_set_blockaddr(card); } From 6b53bad8ac54b3d748c4b0dbe6b0a4ed6e2e60f4 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 27 Jan 2011 16:12:58 -0800 Subject: [PATCH 09/15] Revert "mmc: subtract boot sectors from disk size for eMMC 4.3+ devices" This reverts commit 2cdc235bbeb09b2b78cd02a94ab79c265861aea9. The reverted commit incorrectly calculates the size of eMMC devices in some (all?) cases. This revert may cause problems in cases where the bootloader was bug-compatible and puts a GPT partition at the incorrect end of the eMMC device. Change-Id: Icc9ddb3d294aa2a1caeddfe8fedd2e12aa7691e1 --- drivers/mmc/core/mmc.c | 7 +------ include/linux/mmc/mmc.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f5b2aaf2371f..6909a54c39be 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -253,13 +253,8 @@ static int mmc_read_ext_csd(struct mmc_card *card) ext_csd[EXT_CSD_SEC_CNT + 3] << 24; /* Cards with density > 2GiB are sector addressed */ - if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512) { - unsigned boot_sectors; - boot_sectors = ext_csd[EXT_CSD_BOOT_SIZE_MULTI]; - boot_sectors *= SZ_256K / 512; - card->ext_csd.sectors -= boot_sectors; + if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512) mmc_card_set_blockaddr(card); - } } switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) { diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 5b35987e691b..dd11ae51fb68 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -262,7 +262,6 @@ struct _mmc_csd { #define EXT_CSD_S_A_TIMEOUT 217 /* RO */ #define EXT_CSD_ERASE_TIMEOUT_MULT 223 /* RO */ #define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */ -#define EXT_CSD_BOOT_SIZE_MULTI 226 #define EXT_CSD_SEC_TRIM_MULT 229 /* RO */ #define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ #define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ From fd4194e5c3ab51f0eacdc115eeead02dbf8f4d88 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 28 Jan 2011 13:45:06 -0800 Subject: [PATCH 10/15] fs: partitions: efi: Add force_gpt_sector parameter force_gpt_sector= causes the GPT partition search to look at the specified sector for a valid GPT header if the GPT is not found at the beginning or the end of the block device. Change-Id: I9b5f85ce24719c0538d42ec5a94344c7f6556b2b Signed-off-by: Colin Cross --- fs/partitions/efi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/partitions/efi.c b/fs/partitions/efi.c index 35bbbd279d73..f245c767ad4d 100644 --- a/fs/partitions/efi.c +++ b/fs/partitions/efi.c @@ -105,6 +105,7 @@ * the partition tables happens after init too. */ static int force_gpt; +static u64 force_gpt_sector; static int __init force_gpt_fn(char *str) { @@ -113,6 +114,13 @@ force_gpt_fn(char *str) } __setup("gpt", force_gpt_fn); +static int __init force_gpt_sector_fn(char *str) +{ + force_gpt_sector = simple_strtoull(str, NULL, 0); + return 1; +} +__setup("gpt_sector=", force_gpt_sector_fn); + /** * efi_crc32() - EFI version of crc32 function @@ -541,6 +549,9 @@ static int find_valid_gpt(struct parsed_partitions *state, gpt_header **gpt, if (!good_agpt && force_gpt) good_agpt = is_gpt_valid(state, lastlba, &agpt, &aptes); + if (!good_agpt && force_gpt && force_gpt_sector) + good_agpt = is_gpt_valid(state, force_gpt_sector, &agpt, &aptes); + /* The obviously unsuccessful case */ if (!good_pgpt && !good_agpt) goto fail; From 8ef8637eef2d56187d947e0db762c02988c0c5f6 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 7 Feb 2011 13:55:26 -0800 Subject: [PATCH 11/15] PM: Increase dpm suspend timeout usbhid devices have a 10s timeout waiting for the out queue to clear. Increased the watchdog to 12s. Change-Id: I96368fca6dff98e4eba8aedb09c23be964c8f4b4 Signed-off-by: Benoit Goby --- drivers/base/power/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 4ff491f49ee4..1ba3de8015b5 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -881,7 +881,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) data.dev = dev; data.tsk = get_current(); init_timer_on_stack(&timer); - timer.expires = jiffies + HZ * 3; + timer.expires = jiffies + HZ * 12; timer.function = dpm_drv_timeout; timer.data = (unsigned long)&data; add_timer(&timer); From a14be8e976554dc59c06d6d769a368601dff12af Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 11 Feb 2011 16:54:39 -0800 Subject: [PATCH 12/15] net: wireless: bcm4329: Add sdlock to firmware loading Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/dhd_linux.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index c7ef3edd908f..dc0ec1602cd0 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -2185,12 +2185,15 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_TRACE(("%s: \n", __FUNCTION__)); + dhd_os_sdlock(dhdp); + /* try to download image and nvram to the dongle */ if (dhd->pub.busstate == DHD_BUS_DOWN) { if (!(dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh, fw_path, nv_path))) { DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n", __FUNCTION__, fw_path, nv_path)); + dhd_os_sdunlock(dhdp); return -1; } } @@ -2200,8 +2203,9 @@ dhd_bus_start(dhd_pub_t *dhdp) dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms); /* Bring up the bus */ - if ((ret = dhd_bus_init(&dhd->pub, TRUE)) != 0) { + if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) { DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret)); + dhd_os_sdunlock(dhdp); return ret; } #if defined(OOB_INTR_ONLY) @@ -2210,6 +2214,7 @@ dhd_bus_start(dhd_pub_t *dhdp) dhd->wd_timer_valid = FALSE; del_timer_sync(&dhd->timer); DHD_ERROR(("%s Host failed to resgister for OOB\n", __FUNCTION__)); + dhd_os_sdunlock(dhdp); return -ENODEV; } @@ -2222,9 +2227,12 @@ dhd_bus_start(dhd_pub_t *dhdp) dhd->wd_timer_valid = FALSE; del_timer_sync(&dhd->timer); DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); + dhd_os_sdunlock(dhdp); return -ENODEV; } + dhd_os_sdunlock(dhdp); + #ifdef EMBEDDED_PLATFORM bcm_mkiovar("event_msgs", dhdp->eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); dhdcdc_query_ioctl(dhdp, 0, WLC_GET_VAR, iovbuf, sizeof(iovbuf)); From fc4d5028cab51af9c25a0b9afaadfe1383e0b2eb Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Fri, 11 Feb 2011 17:01:28 -0800 Subject: [PATCH 13/15] net: wireless: bcm4329: Fix memleak in dev->p Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/bcmsdh_linux.c | 12 +++++++----- drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c | 10 +++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/bcm4329/bcmsdh_linux.c b/drivers/net/wireless/bcm4329/bcmsdh_linux.c index 3b7da4263fd8..1e33555b0531 100644 --- a/drivers/net/wireless/bcm4329/bcmsdh_linux.c +++ b/drivers/net/wireless/bcm4329/bcmsdh_linux.c @@ -301,7 +301,7 @@ int bcmsdh_remove(struct device *dev) MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); osl_detach(osh); -#if !defined(BCMLXSDMMC) +#if !defined(BCMLXSDMMC) || defined(OOB_INTR_ONLY) dev_set_drvdata(dev, NULL); #endif /* !defined(BCMLXSDMMC) */ @@ -647,10 +647,12 @@ void bcmsdh_unregister_oob_intr(void) { SDLX_MSG(("%s: Enter\n", __FUNCTION__)); - set_irq_wake(sdhcinfo->oob_irq, 0); - disable_irq(sdhcinfo->oob_irq); /* just in case.. */ - free_irq(sdhcinfo->oob_irq, NULL); - sdhcinfo->oob_irq_registered = FALSE; + if (sdhcinfo->oob_irq_registered) { + set_irq_wake(sdhcinfo->oob_irq, 0); + disable_irq(sdhcinfo->oob_irq); /* just in case.. */ + free_irq(sdhcinfo->oob_irq, NULL); + sdhcinfo->oob_irq_registered = FALSE; + } } #endif /* defined(OOB_INTR_ONLY) */ /* Module parameters specific to each host-controller driver */ diff --git a/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c index 8992a4267f9f..5a1a46c93571 100644 --- a/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c @@ -82,7 +82,6 @@ PBCMSDH_SDMMC_INSTANCE gInstance; extern int bcmsdh_probe(struct device *dev); extern int bcmsdh_remove(struct device *dev); -struct device sdmmc_dev; static int bcmsdh_sdmmc_probe(struct sdio_func *func, const struct sdio_device_id *id) @@ -102,7 +101,7 @@ static int bcmsdh_sdmmc_probe(struct sdio_func *func, if(func->device == 0x4) { /* 4318 */ gInstance->func[2] = NULL; sd_trace(("NIC found, calling bcmsdh_probe...\n")); - ret = bcmsdh_probe(&sdmmc_dev); + ret = bcmsdh_probe(&func->dev); } } @@ -110,7 +109,7 @@ static int bcmsdh_sdmmc_probe(struct sdio_func *func, if (func->num == 2) { sd_trace(("F2 found, calling bcmsdh_probe...\n")); - ret = bcmsdh_probe(&sdmmc_dev); + ret = bcmsdh_probe(&func->dev); } return ret; @@ -126,7 +125,7 @@ static void bcmsdh_sdmmc_remove(struct sdio_func *func) if (func->num == 2) { sd_trace(("F2 found, calling bcmsdh_remove...\n")); - bcmsdh_remove(&sdmmc_dev); + bcmsdh_remove(&func->dev); } } @@ -250,10 +249,8 @@ int sdio_function_init(void) if (!gInstance) return -ENOMEM; - bzero(&sdmmc_dev, sizeof(sdmmc_dev)); error = sdio_register_driver(&bcmsdh_sdmmc_driver); - return error; } @@ -265,7 +262,6 @@ void sdio_function_cleanup(void) { sd_trace(("%s Enter\n", __FUNCTION__)); - sdio_unregister_driver(&bcmsdh_sdmmc_driver); if (gInstance) From 4c0d9cf8d550d5a2b4318f950127fdc086e5b023 Mon Sep 17 00:00:00 2001 From: Mike Corrigan Date: Fri, 11 Feb 2011 17:05:54 -0800 Subject: [PATCH 14/15] net: wireless: bcm4329: Fix mem leak in wl_iw_handle_scanresults_ies Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/wl_iw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index 8460804c945a..00222196f6e4 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -3686,6 +3686,7 @@ wl_iw_handle_scanresults_ies(char **event_p, char *end, wpa_snprintf_hex(buf + 10, 2+1, &(ie->len), 1); wpa_snprintf_hex(buf + 12, 2*ie->len+1, ie->data, ie->len); event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, buf); + kfree(buf); #endif break; } From 1cd64ef5c1becb1c70c432bdcae98fdb686a8173 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Sat, 12 Feb 2011 13:21:32 -0800 Subject: [PATCH 15/15] Revert "mmc: subtract boot sectors from disk size for eMMC 4.3+ devices" This reverts commit f0b0e4bec1e89014f3dcef4da8bcf95428cc771c. The reverted commit incorrectly calculates the size of eMMC devices in some (all?) cases. This revert may cause problems in cases where the bootloader was bug-compatible and puts a GPT partition at the incorrect end of the eMMC device. Change-Id: Ib006acf9e517b3b8f7570220c28e19c91e7b5f25 Signed-off-by: Colin Cross --- drivers/mmc/core/mmc.c | 7 +------ include/linux/mmc/mmc.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 45055c46d954..6909a54c39be 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -253,13 +253,8 @@ static int mmc_read_ext_csd(struct mmc_card *card) ext_csd[EXT_CSD_SEC_CNT + 3] << 24; /* Cards with density > 2GiB are sector addressed */ - if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512) { - unsigned boot_sectors; - /* size is in 256K chunks, i.e. 512 sectors each */ - boot_sectors = ext_csd[EXT_CSD_BOOT_SIZE_MULTI] * 512; - card->ext_csd.sectors -= boot_sectors; + if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512) mmc_card_set_blockaddr(card); - } } switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) { diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 1041cf5220f6..dd11ae51fb68 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -266,7 +266,6 @@ struct _mmc_csd { #define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ #define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ #define EXT_CSD_TRIM_MULT 232 /* RO */ -#define EXT_CSD_BOOT_SIZE_MULTI 226 /* RO */ /* * EXT_CSD field definitions