On 04/29/2010 12:40 PM, Dennis Muhlestein wrote:

One idea I've been toying with is to add a semaphore around submitting
the URBs.

In uvc_video.c, where the URBs are submitted, I'd acquire a semephore
for each device currently submitting URBs. The semaphore would limit the
number of devices to whatever number I decide can safely submit URBs
simultaneously on the bus without throwing out of space errors.

I did an initial test of this and it looks promising. I can configure
all the cameras. As long as I don't submit the URBs for the number of
devices beyond that which will work at the same time, the other cameras
simply drop the data.

I'm not sure the best places to control the locking and unlocking of the
semaphore are. Right now, I lock it before submitting URBs in
uvc_init_video. In uvc_video_complete, I unlock it and relock it if the
buffer is complete (allowing another camera to acquire it and capture a
frame).

Anyway, it isn't working perfectly yet but I think I can debug it and at
least get to a point where I know if it's worth pursuing. I'm curious if
anyone can provide thoughts or alternatives.


I have two solutions that I've come up with this so far.
1) Modify the uvc_video.c to queue urbs in a list in the urb completion handler. A driver level semaphore controls the number of currently submitting cameras. You can adjust the initial sem_count in uvc_driver.c. Ideally, that would be a module parameter but I'm just getting things to work.

I found capture speeds quite a bit slower than I'd like with this method though. I can capture with 8 cameras at 10 FPS without overwhelming the ISO bus but if I change to 15 FPS I can only capture with 3 cameras at a time. Adding the patch, I then can configure and capture from all 8 cameras running at 15 FPS but only submitting URBs for 3 at a time. Depending on how many frames I let those cameras capture I got captured frame rates from around 4 to 6.

I'm attaching the patch in case anyone wants to play with this or suggest ways to improve it. One thing I had a problem with is that it seems some of the capture images are corrupted. This patch was against 2.6.27.2. A little old I know but I'm working on an embedded device.

2) I modified ehci-sched.c to not raise the ENOSPC error. That solution actually lets me capture on all 8 cameras at 15 FPS. This has other implications though and I'm not sure it is a very good solution. It does tell me that perhaps the Linux ISO scheduler could perhaps use look through. One major discrepancy is that bandwidth is allocated based on the max packet size I think but for my compressed images (MJPG) each frame is only a fraction of the max allocated bandwidth.

-Dennis

diff --git a/uvc_driver.c b/uvc_driver.c
index 7e10203..6445ec1 100644
--- a/uvc_driver.c
+++ b/uvc_driver.c
@@ -1963,8 +1963,12 @@ static int __init uvc_init(void)
 
        INIT_LIST_HEAD(&uvc_driver.devices);
        INIT_LIST_HEAD(&uvc_driver.controls);
+    INIT_LIST_HEAD(&uvc_driver.wait_urbs);
        mutex_init(&uvc_driver.open_mutex);
        mutex_init(&uvc_driver.ctrl_mutex);
+    //mutex_init(&uvc_driver.urb_mutex);
+    uvc_driver.urb_lock = SPIN_LOCK_UNLOCKED;
+    uvc_driver.sem_count=3;
 
        uvc_ctrl_init();
 
diff --git a/uvc_v4l2.c b/uvc_v4l2.c
index b27cf5c..6376759 100644
--- a/uvc_v4l2.c
+++ b/uvc_v4l2.c
@@ -427,6 +427,10 @@ static int uvc_v4l2_open(struct inode *inode, struct file 
*file)
                goto done;
        }
 
+    video->has_sem=0;
+    video->sent_urbs=0;
+    video->release_on_no_urbs=0;
+    video->sem_frame =0;
        handle->device = video;
        handle->state = UVC_HANDLE_PASSIVE;
        file->private_data = handle;
diff --git a/uvc_video.c b/uvc_video.c
index 6854ac7..f1b9673 100644
--- a/uvc_video.c
+++ b/uvc_video.c
@@ -26,6 +26,9 @@
 
 #include "uvcvideo.h"
 
+// predef
+static void uvc_video_frame_complete(struct uvc_video_device *);
+
 /* ------------------------------------------------------------------------
  * UVC Controls
  */
@@ -415,6 +418,7 @@ static void uvc_video_decode_end(struct uvc_video_device 
*video,
                buf->state = UVC_BUF_STATE_DONE;
                if (video->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
                        video->last_fid ^= UVC_STREAM_FID;
+        uvc_video_frame_complete(video);
        }
 }
 
@@ -524,13 +528,161 @@ static void uvc_video_decode_bulk(struct urb *urb,
        }
 }
 
+
+/**
+ * Incs the resource count for allows video devices
+ * if the specified video device currently has a 
+ * resource locked.
+ **/
+static void uvc_video_frame_complete(struct uvc_video_device *video) {
+  
+ // mutex_lock ( &uvc_driver.urb_mutex ); 
+ unsigned long flags;
+ spin_lock_irqsave ( &uvc_driver.urb_lock, flags );
+  //uvc_printk ( KERN_WARNING, "Frame Complete: %s\n", 
video->dev->udev->serial );
+  video->sem_frame += 1;
+  if ( video->has_sem && video->sem_frame > 2 ) {
+    if (!video->sent_urbs) {
+        video->has_sem = 0;
+        uvc_driver.sem_count += 1;
+  //      uvc_printk ( KERN_WARNING, "Release Sem: %s\n", 
video->dev->udev->serial );
+    } else {
+        video->release_on_no_urbs=1;
+    }
+  }
+  //mutex_unlock (&uvc_driver.urb_mutex );
+ spin_unlock_irqrestore ( &uvc_driver.urb_lock, flags );
+
+}
+
+/** 
+ *  Call regardless of whether frame is complete or not.
+ **/
+static void uvc_video_urb_complete(struct uvc_video_device *video) {
+  
+ unsigned long flags;
+ spin_lock_irqsave ( &uvc_driver.urb_lock, flags );
+ 
+  //uvc_printk ( KERN_WARNING, "Frame Complete: %s\n", 
video->dev->udev->serial );
+  video->sent_urbs -= 1;
+  if (video->release_on_no_urbs && !video->sent_urbs) {
+    video->release_on_no_urbs=0;
+    if (!video->has_sem) {
+        uvc_printk ( KERN_WARNING, "Bad Logic in releasing urb semaphore.\n" );
+    } else {
+        video->has_sem = 0;
+        uvc_driver.sem_count += 1;
+ //       uvc_printk ( KERN_WARNING, "Release Sem: %s\n", 
video->dev->udev->serial );
+    }
+  }
+  
+ spin_unlock_irqrestore ( &uvc_driver.urb_lock, flags );
+
+}
+
+/**
+ * Add the URBs to the URB list and then process the list,
+ * submitting URBs for devices that can capture.
+ **/
+static int uvc_video_urb_submit (struct urb *urb) {
+
+ struct wait_urb *tmp;
+ struct uvc_video_device *video = urb->context;
+ struct list_head *p, *n; // for traversal
+ int submitted=0, ret=0;
+ unsigned long flags;
+
+ //mutex_lock (&uvc_driver.urb_mutex );
+ spin_lock_irqsave ( &uvc_driver.urb_lock, flags );
+
+// if ( video->has_sem ) {
+//    // in this case, go ahead and submit the urb.
+//     if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+//             uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d) 
(1st).\n",
+//                     ret);
+//       video->has_sem=0;
+//       uvc_driver.sem_count += 1;
+//       goto done;
+//     } else submitted += 1;
+// } else {
+
+     // add the urb to the queue
+     tmp = (struct wait_urb*)kmalloc ( sizeof (struct wait_urb), GFP_KERNEL );
+     if (!tmp) {
+        return -ENOMEM;
+     }
+     tmp->urb=urb;
+     list_add_tail (&tmp->list, &uvc_driver.wait_urbs);
+     //uvc_printk ( KERN_WARNING, "Added urb to queue: %s\n", 
video->dev->udev->serial );
+// }
+
+ // now, we take care of any URBs in the queue.
+ 
+  list_for_each_safe(p, n, &uvc_driver.wait_urbs ) {
+
+    tmp = list_entry ( p, struct wait_urb, list );
+    video = tmp->urb->context;
+    if (!video->has_sem) {
+        // see if we can acquire it 
+        if (uvc_driver.sem_count > 0) {
+            uvc_driver.sem_count -= 1;
+            video->has_sem = 1;
+            video->sem_frame = 0;
+            if (video->sent_urbs) {
+                uvc_printk ( KERN_ERR, "Bad logic in urb count.  Should be 0: 
%d\n", video->sent_urbs );
+            }
+     //       uvc_printk ( KERN_WARNING, "Acquired Sem: %s\n", 
video->dev->udev->serial );
+        }
+    } 
+
+    if (video->has_sem && !video->release_on_no_urbs) {
+        // great, submit the urb
+           ret = usb_submit_urb(tmp->urb, GFP_ATOMIC);
+        if (ret >= 0) { 
+            submitted += 1;
+            video->sent_urbs += 1;
+            //uvc_printk (KERN_WARNING, "Submitted URB from Queue %s\n" , 
video->dev->udev->serial );
+        } else {
+            // no need this item in the list any more.
+               uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d)\n",
+                       ret);
+            video->has_sem = 0;
+            uvc_driver.sem_count += 1;
+        }
+        list_del(p);
+        kfree(tmp);
+
+        if (ret<0)
+            goto done;
+        
+    }
+  }
+
+done: 
+ //mutex_unlock(&uvc_driver.urb_mutex );
+ //uvc_printk ( KERN_WARNING, "Submitted URBs: %d, list empty: %d\n", 
submitted, list_empty ( &uvc_driver.wait_urbs) );
+ //if (submitted != 1) { // || ret < 0) {
+ // video=urb->context;
+ // uvc_printk ( KERN_ERR, "Submitted %d URBs %s, ret: %d\n", submitted, 
video->dev->udev->serial, ret );
+ //}
+ spin_unlock_irqrestore ( &uvc_driver.urb_lock, flags );
+ return ret;
+ 
+}
+
+
+/**
+ * USB combletion handler for URBs coming back off the USB Bus.
+ **/
 static void uvc_video_complete(struct urb *urb)
 {
        struct uvc_video_device *video = urb->context;
        struct uvc_video_queue *queue = &video->queue;
        struct uvc_buffer *buf = NULL;
        unsigned long flags;
-       int ret;
+       //int ret;
+
+    uvc_video_urb_complete ( video );
 
        switch (urb->status) {
        case 0:
@@ -541,12 +693,15 @@ static void uvc_video_complete(struct urb *urb)
                        "completion handler.\n", urb->status);
 
        case -ENOENT:           /* usb_kill_urb() called. */
-               if (video->frozen)
+               if (video->frozen) {
+            uvc_video_frame_complete(video);
                        return;
+        }
 
        case -ECONNRESET:       /* usb_unlink_urb() called. */
        case -ESHUTDOWN:        /* The endpoint is being disabled. */
                uvc_queue_cancel(queue, urb->status == -ESHUTDOWN);
+        uvc_video_frame_complete(video);
                return;
        }
 
@@ -558,10 +713,11 @@ static void uvc_video_complete(struct urb *urb)
 
        video->decode(urb, video, buf);
 
-       if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-               uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
-                       ret);
-       }
+    uvc_video_urb_submit(urb);
+       //if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+       //      uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
+       //              ret);
+       //}
 }
 
 /*
@@ -806,12 +962,16 @@ static int uvc_init_video(struct uvc_video_device *video, 
gfp_t gfp_flags)
 
        /* Submit the URBs. */
        for (i = 0; i < UVC_URBS; ++i) {
-               if ((ret = usb_submit_urb(video->urb[i], gfp_flags)) < 0) {
-                       uvc_printk(KERN_ERR, "Failed to submit URB %u "
-                                       "(%d).\n", i, ret);
-                       uvc_uninit_video(video, 1);
-                       return ret;
-               }
+        if ((ret = uvc_video_urb_submit ( video->urb[i] ) ) < 0 ) {
+                   uvc_uninit_video(video, 1);
+            return ret;
+        }
+               //if ((ret = usb_submit_urb(video->urb[i], gfp_flags)) < 0) {
+               //      uvc_printk(KERN_ERR, "Failed to submit URB %u "
+               //                      "(%d).\n", i, ret);
+               //      uvc_uninit_video(video, 1);
+               //      return ret;
+               //}
        }
 
        return 0;
diff --git a/uvcvideo.h b/uvcvideo.h
index bafe340..4210e2c 100644
--- a/uvcvideo.h
+++ b/uvcvideo.h
@@ -582,6 +582,10 @@ struct uvc_video_device {
        struct uvc_entity *selector;
        struct list_head extensions;
        struct mutex ctrl_mutex;
+    int has_sem;
+    int sent_urbs;
+    int release_on_no_urbs;
+    int sem_frame;
 
        struct uvc_video_queue queue;
 
@@ -651,6 +655,11 @@ struct uvc_fh {
        enum uvc_handle_state state;
 };
 
+struct wait_urb {
+    struct list_head list;
+    struct urb* urb; // an urb, waiting to be submitted.
+};
+
 struct uvc_driver {
        struct usb_driver driver;
 
@@ -658,6 +667,10 @@ struct uvc_driver {
 
        struct list_head devices;       /* struct uvc_device list */
        struct list_head controls;      /* struct uvc_control_info list */
+    struct list_head wait_urbs; /* urbs waiting to be submitted */
+    int sem_count;
+    //struct mutex urb_mutex;     /* protects urb submit handler */
+    spinlock_t urb_lock;
        struct mutex ctrl_mutex;        /* protects controls and devices
                                           lists */
 };

_______________________________________________
Linux-uvc-devel mailing list
Linux-uvc-devel@lists.berlios.de
https://lists.berlios.de/mailman/listinfo/linux-uvc-devel

Reply via email to