Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=8a3c1f573c771e60f67ef172d2392d1a28385b4a
Commit:     8a3c1f573c771e60f67ef172d2392d1a28385b4a
Parent:     e009f1b202219c62ea7e277adbb953d703dac983
Author:     David Brownell <[EMAIL PROTECTED]>
AuthorDate: Wed Mar 21 12:26:32 2007 -0700
Committer:  Greg Kroah-Hartman <[EMAIL PROTECTED]>
CommitDate: Mon Mar 26 14:17:48 2007 -0700

    USB: omap_udc: workaround dma_free_coherent() bogosity
    
    Various fixes to omap_udc, noted with some recent testing:
    
     - Cope with some SMP-induced braindamage in ARM's 
dma_{alloc,free}_coherent()
       implementation: alloc() can be called with IRQs blocked, but since late
       last year that's no longer true for free().  This resolves really NASTY
       problems with logspamming via WARN_ON(), indicating N-page leaks.
    
     - Be more correct in handling GET_STATUS request for RECIP_ENDPOINT ... the
       previous code only handled RECIP_INTERFACE, this version should be 
correct
       except for (sigh) bulk/interrupt endpoints.
    
     - Provide a better name for the function reporting whether the board has
       vbus sensing wired up.
    
    GET_STATUS requests for endpoint status still acts strangely though, at 
least
    given one flakey host doesn't always ack the first DATA packet, then the 
packet
    that gets retransmitted doesn't have data!
    
    Signed-off-by: David Brownell <[EMAIL PROTECTED]>
    Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>
---
 drivers/usb/gadget/omap_udc.c |  103 +++++++++++++++++++++++++++++++++++++----
 1 files changed, 93 insertions(+), 10 deletions(-)

diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index 8f9a2b6..b394e63 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -296,6 +296,15 @@ omap_free_request(struct usb_ep *ep, struct usb_request 
*_req)
 
 /*-------------------------------------------------------------------------*/
 
+/*
+ * dma-coherent memory allocation (for dma-capable endpoints)
+ *
+ * NOTE: the dma_*_coherent() API calls suck.  Most implementations are
+ * (a) page-oriented, so small buffers lose big; and (b) asymmetric with
+ * respect to calls with irqs disabled:  alloc is safe, free is not.
+ * We currently work around (b), but not (a).
+ */
+
 static void *
 omap_alloc_buffer(
        struct usb_ep   *_ep,
@@ -307,6 +316,9 @@ omap_alloc_buffer(
        void            *retval;
        struct omap_ep  *ep;
 
+       if (!_ep)
+               return NULL;
+
        ep = container_of(_ep, struct omap_ep, ep);
        if (use_dma && ep->has_dma) {
                static int      warned;
@@ -326,6 +338,35 @@ omap_alloc_buffer(
        return retval;
 }
 
+static DEFINE_SPINLOCK(buflock);
+static LIST_HEAD(buffers);
+
+struct free_record {
+       struct list_head        list;
+       struct device           *dev;
+       unsigned                bytes;
+       dma_addr_t              dma;
+};
+
+static void do_free(unsigned long ignored)
+{
+       spin_lock_irq(&buflock);
+       while (!list_empty(&buffers)) {
+               struct free_record      *buf;
+
+               buf = list_entry(buffers.next, struct free_record, list);
+               list_del(&buf->list);
+               spin_unlock_irq(&buflock);
+
+               dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma);
+
+               spin_lock_irq(&buflock);
+       }
+       spin_unlock_irq(&buflock);
+}
+
+static DECLARE_TASKLET(deferred_free, do_free, 0);
+
 static void omap_free_buffer(
        struct usb_ep   *_ep,
        void            *buf,
@@ -333,13 +374,29 @@ static void omap_free_buffer(
        unsigned        bytes
 )
 {
-       struct omap_ep  *ep;
+       if (!_ep) {
+               WARN_ON(1);
+               return;
+       }
 
-       ep = container_of(_ep, struct omap_ep, ep);
-       if (use_dma && _ep && ep->has_dma)
-               dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma);
-       else
-               kfree (buf);
+       /* free memory into the right allocator */
+       if (dma != DMA_ADDR_INVALID) {
+               struct omap_ep          *ep;
+               struct free_record      *rec = buf;
+               unsigned long           flags;
+
+               ep = container_of(_ep, struct omap_ep, ep);
+
+               rec->dev = ep->udc->gadget.dev.parent;
+               rec->bytes = bytes;
+               rec->dma = dma;
+
+               spin_lock_irqsave(&buflock, flags);
+               list_add_tail(&rec->list, &buffers);
+               tasklet_schedule(&deferred_free);
+               spin_unlock_irqrestore(&buflock, flags);
+       } else
+               kfree(buf);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1691,12 +1748,38 @@ ep0out_status_stage:
                        udc->ep0_pending = 0;
                        break;
                case USB_REQ_GET_STATUS:
+                       /* USB_ENDPOINT_HALT status? */
+                       if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
+                               goto intf_status;
+
+                       /* ep0 never stalls */
+                       if (!(w_index & 0xf))
+                               goto zero_status;
+
+                       /* only active endpoints count */
+                       ep = &udc->ep[w_index & 0xf];
+                       if (w_index & USB_DIR_IN)
+                               ep += 16;
+                       if (!ep->desc)
+                               goto do_stall;
+
+                       /* iso never stalls */
+                       if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
+                               goto zero_status;
+
+                       /* FIXME don't assume non-halted endpoints!! */
+                       ERR("%s status, can't report\n", ep->ep.name);
+                       goto do_stall;
+
+intf_status:
                        /* return interface status.  if we were pedantic,
                         * we'd detect non-existent interfaces, and stall.
                         */
                        if (u.r.bRequestType
                                        != (USB_DIR_IN|USB_RECIP_INTERFACE))
                                goto delegate;
+
+zero_status:
                        /* return two zero bytes */
                        UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;
                        UDC_DATA_REG = 0;
@@ -2068,7 +2151,7 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev)
 
 /*-------------------------------------------------------------------------*/
 
-static inline int machine_needs_vbus_session(void)
+static inline int machine_without_vbus_sense(void)
 {
        return (machine_is_omap_innovator()
                || machine_is_omap_osk()
@@ -2156,7 +2239,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver 
*driver)
        /* boards that don't have VBUS sensing can't autogate 48MHz;
         * can't enter deep sleep while a gadget driver is active.
         */
-       if (machine_needs_vbus_session())
+       if (machine_without_vbus_sense())
                omap_vbus_session(&udc->gadget, 1);
 
 done:
@@ -2179,7 +2262,7 @@ int usb_gadget_unregister_driver (struct 
usb_gadget_driver *driver)
        if (udc->dc_clk != NULL)
                omap_udc_enable_clock(1);
 
-       if (machine_needs_vbus_session())
+       if (machine_without_vbus_sense())
                omap_vbus_session(&udc->gadget, 0);
 
        if (udc->transceiver)
@@ -2822,7 +2905,7 @@ static int __init omap_udc_probe(struct platform_device 
*pdev)
                hmc = HMC_1510;
                type = "(unknown)";
 
-               if (machine_is_omap_innovator() || machine_is_sx1()) {
+               if (machine_without_vbus_sense()) {
                        /* just set up software VBUS detect, and then
                         * later rig it so we always report VBUS.
                         * FIXME without really sensing VBUS, we can't
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to