Am Mittwoch, 28. April 2004 22:27 schrieb Alan Stern:
> > > finished.  That's why usb_wait_for_urb() is needed.
> >
> > But is very poorly implemented.
>
> Can you come up with something better in terms of both space and time?

Good question. How do you like this draft in terms of approach?

--- include/linux/usb.h.alt     2004-04-29 14:54:17.000000000 +0200
+++ include/linux/usb.h 2004-04-29 15:33:37.000000000 +0200
@@ -537,6 +537,13 @@
 #define URB_ZERO_PACKET                0x0040  /* Finish bulk OUTs with short packet 
*/
 #define URB_NO_INTERRUPT       0x0080  /* HINT: no non-error interrupt needed */
 
+/*
+ * urb->phase
+ */
+#define PHASE_IDLE     0
+#define PHASE_DOING    1
+#define PHASE_DONE     2
+
 struct usb_iso_packet_descriptor {
        unsigned int offset;
        unsigned int length;            /* expected length */
@@ -722,6 +729,8 @@
        void *hcpriv;                   /* private data for host controller */
        struct list_head urb_list;      /* list pointer to all active urbs */
        int bandwidth;                  /* bandwidth for INT/ISO request */
+       wait_queue_head_t handler_queue;/* waiting for completion handlers */
+       int phase;
 
        /* public, documented fields in the urb that can be used by drivers */
        struct usb_device *dev;         /* (in) pointer to associated device */
--- drivers/usb/core/hcd.c.alt  2004-04-29 15:15:06.000000000 +0200
+++ drivers/usb/core/hcd.c      2004-04-29 17:34:02.422824168 +0200
@@ -1185,6 +1185,7 @@
         * (urb lock, then hcd_data_lock) in case some other CPU is now
         * unlinking it.
         */
+rekill:
        spin_lock_irqsave (&urb->lock, flags);
        spin_lock (&hcd_data_lock);
 
@@ -1198,7 +1199,10 @@
        hcd = urb->dev->bus->hcpriv;
        if (!dev || !hcd) {
                retval = -ENODEV;
-               goto done;
+               if (urb->phase == PHASE_IDLE)
+                       goto done;
+               else
+                       goto maybe_wait;
        }
 
        /* running ~= hc unlink handshake works (irq, timer, etc)
@@ -1214,7 +1218,10 @@
        }
        if (tmp != &urb->urb_list) {
                retval = -EIDRM;
-               goto done;
+               if (urb->phase == PHASE_IDLE)
+                       goto done;
+               else
+                       goto maybe_wait;
        }
 
        /* Any status except -EINPROGRESS means something already started to
@@ -1224,7 +1231,7 @@
         */
        if (urb->status != -EINPROGRESS) {
                retval = -EBUSY;
-               goto done;
+               goto maybe_wait;
        }
 
        /* PCI IRQ setup can easily be broken so that USB controllers
@@ -1279,7 +1286,10 @@
                                urb->context = splice.context;
                                spin_unlock_irqrestore (&urb->lock, flags);
                        }
-                       goto bye;
+                       if (urb->phase == PHASE_IDLE)
+                               goto bye;
+                       else
+                               goto wait;
                }
        }
 
@@ -1297,6 +1307,16 @@
        if (retval != -EIDRM && sys && sys->driver)
                dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);
        return retval;
+maybe_wait:
+        spin_unlock (&hcd_data_lock);
+        spin_unlock_irqrestore (&urb->lock, flags);
+wait:
+       wait_event(urb->handler_queue, urb->phase != PHASE_IDLE);
+       if (urb->phase == PHASE_DOING)
+               /* the mother has escaped
+                  we go for the child */
+               goto rekill;
+       return retval;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1474,6 +1494,7 @@
        // hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)
        // It would catch exit/unlink paths for all urbs.
 
+       urb->phase = PHASE_DONE;
        /* lower level hcd code should use *_dma exclusively */
        if (hcd->self.controller->dma_mask) {
                if (usb_pipecontrol (urb->pipe)
@@ -1493,6 +1514,9 @@
 
        /* pass ownership to the completion handler */
        urb->complete (urb, regs);
+       if (urb->phase != PHASE_DOING) /* we have not been resubmitted */
+               urb->phase = PHASE_IDLE;
+       wake_up(&urb->handler_queue);
        usb_put_urb (urb);
 }
 EXPORT_SYMBOL (usb_hcd_giveback_urb);
--- drivers/usb/core/urb.c.alt  2004-04-29 15:13:45.000000000 +0200
+++ drivers/usb/core/urb.c      2004-04-29 15:36:23.000000000 +0200
@@ -33,6 +33,7 @@
                memset(urb, 0, sizeof(*urb));
                urb->count = (atomic_t)ATOMIC_INIT(1);
                spin_lock_init(&urb->lock);
+               init_waitqueue_head(&urb->handler_queue);
        }
 }
 
@@ -383,7 +384,8 @@
                        temp >>= 1;
                urb->interval = temp;
        }
-
+       
+       urb->phase = PHASE_DOING;
        return op->submit_urb (urb, mem_flags);
 }
 



-------------------------------------------------------
This SF.Net email is sponsored by: Oracle 10g
Get certified on the hottest thing ever to hit the market... Oracle 10g. 
Take an Oracle 10g class now, and we'll give you the exam FREE. 
http://ads.osdn.com/?ad_id=3149&alloc_id=8166&op=click
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to