[sane-devel] [RFC] additional sanei_usb API to queue multiple URBs asynchronly

René Rebe rene at exactcode.de
Sat Mar 4 18:10:49 UTC 2006


Hi,

On Friday 03 March 2006 08:10, Gerhard Jaeger wrote:
> On Thursday 02 March 2006 13:06, René Rebe wrote:
> > Hi all - again,
> > 
> > On Wednesday 01 March 2006 22:53, René Rebe wrote:
> > > Hi all,
> > > 
> > > I currently review how to further improve the SANE/Avision backend to
> > > get to the scanners maximal thruput, e.g. what the Windows driver
> > > get's out of those devices.
> > > 
> > > The 16kB buffer of the Linux usbfs devio looks like a showstopper and
> > > the kernel people indicate it is not likely to be removed / resolved easily or
> > > soon.
> > > 
> > > Another option would be to queue more URBs and handle the completion
> > > asynchonously. Would anyone veto when I further extend the API with
> > > async flavours?
> > 
> > Further investigation reviels just improving libusb to queue multiple URBs
> > might be enough.
> > 
> > Comments welcome,
> > 
> Hi René,
> 
> FOA I'm curious about the transferrate you expect on your devices - guess
> these are USB2.0 high-speed ones.
> I currently could not estimate the impact on the backends I maintain, but
> I think improving libusb ain't not a bad idea. Esp. when keeping in mind,
> that newer devices provide higher resolutions and need the full cap
> of USB2.0...
> 
> Keep us (me) informed about your progress - I'll test that even on my
> slower devices.

Attached is my libusb patch. The error path needs some thinking. It speeds
up the latest, fasted Avision devices by a factor of two, making my backend
even slightly faster (~20%) than the windows driver ,-)!

The transfer rate is now in the 10MB/s range if I calculated correctly. (20
color pages ++ per minutes @300dpi). The rate for duplex might be even higher
but I tweaked my backend just today to support large enough buffers for
the deinterlacing in that case and need to meassure this again when I'm
in the office with the AV220C2, tomorrow or so.

An _async interface to queue the protocol pro- and epilogue would speed
it up even further ,-) (by some percent)

Queue multiple URBs for buffer >16kB to reduce time slots without USB
i/o.

  - Rene Rebe <rene at exactcode.de>

--- libusb-0.1.11/linux.c	2006-03-02 15:33:58.000000000 +0000
+++ libusb-0.1.11-improved/linux.c	2006-03-03 08:26:55.000000000 +0000
@@ -162,11 +162,14 @@
 static int usb_urb_transfer(usb_dev_handle *dev, int ep, int urbtype,
 	char *bytes, int size, int timeout)
 {
-  struct usb_urb urb;
+  int nurbs;
+  struct usb_urb* urbs;
   unsigned int bytesdone = 0, requested;
   struct timeval tv, tv_ref, tv_now;
   struct usb_urb *context;
-  int ret, waiting;
+  int rc = 0, ret = 0, waiting, i;
+
+  fd_set writefds;
 
   /*
    * HACK: The use of urb.usercontext is a hack to get threaded applications
@@ -190,36 +193,47 @@
     tv_ref.tv_sec++;
   }
 
-  do {
-    fd_set writefds;
+  /* allocate and fill all URBs */
+  nurbs = (size + MAX_READ_WRITE - 1) / MAX_READ_WRITE;
+  //fprintf (stderr, "nurbs: %d\n", nurbs);
+  urbs = malloc (sizeof(struct usb_urb) * nurbs);
+  for (i = 0; i < nurbs; ++i) {
 
-    requested = size - bytesdone;
+    requested = size - i * MAX_READ_WRITE;
     if (requested > MAX_READ_WRITE)
       requested = MAX_READ_WRITE;
 
-    urb.type = urbtype;
-    urb.endpoint = ep;
-    urb.flags = 0;
-    urb.buffer = bytes + bytesdone;
-    urb.buffer_length = requested;
-    urb.signr = 0;
-    urb.actual_length = 0;
-    urb.number_of_packets = 0;	/* don't do isochronous yet */
-    urb.usercontext = NULL;
+    urbs[i].type = urbtype;
+    urbs[i].endpoint = ep;
+    urbs[i].flags = 0;
+    urbs[i].buffer = bytes + i * MAX_READ_WRITE;
+    urbs[i].buffer_length = requested;
+    urbs[i].signr = 0;
+    urbs[i].actual_length = 0;
+    urbs[i].number_of_packets = 0;	/* don't do isochronous yet */
+    urbs[i].usercontext = NULL;
 
-    ret = ioctl(dev->fd, IOCTL_USB_SUBMITURB, &urb);
+    //fprintf (stderr, "submitting urb %d\n", i);
+    ret = ioctl(dev->fd, IOCTL_USB_SUBMITURB, &urbs[i]);
     if (ret < 0) {
       USB_ERROR_STR(-errno, "error submitting URB: %s", strerror(errno));
-      return ret;
+			rc = ret;
+      goto end;
     }
+  }
 
-    FD_ZERO(&writefds);
-    FD_SET(dev->fd, &writefds);
+  FD_ZERO(&writefds);
+  FD_SET(dev->fd, &writefds);
 
-restart:
+  /* wait 'till all URBs completed */
+  for (i = 0; i < nurbs; ) {
     waiting = 1;
     context = NULL;
-    while (!urb.usercontext && ((ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context)) == -1) && waiting) {
+    //fprintf (stderr, "waiting for URB %d\n", i);
+
+    while (!urbs[i].usercontext &&
+           ((ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context)) == -1) &&
+           waiting) {
       tv.tv_sec = 0;
       tv.tv_usec = 1000; // 1 msec
       select(dev->fd + 1, NULL, &writefds, NULL, &tv); //sub second wait
@@ -229,52 +243,66 @@
         gettimeofday(&tv_now, NULL);
 
         if ((tv_now.tv_sec > tv_ref.tv_sec) ||
-            ((tv_now.tv_sec == tv_ref.tv_sec) && (tv_now.tv_usec >= tv_ref.tv_usec)))
+           ((tv_now.tv_sec == tv_ref.tv_sec) && (tv_now.tv_usec >= tv_ref.tv_usec)))
           waiting = 0;
       }
     }
 
-    if (context && context != &urb) {
+    /* mark done */
+    if (context) {
+      //fprintf (stderr, "marking URB %p done.\n", context);
       context->usercontext = URB_USERCONTEXT_COOKIE;
-      /* We need to restart since we got a successful URB, but not ours */
-      goto restart;
+
+      /* is it ours? */
+      //if (context && context == &urbs[i])
+        //fprintf (stderr, "which was the one we have been waiting for.\n");
     }
 
+    /* done= */
+    if (urbs[i].usercontext) {
+      bytesdone += context->actual_length;
+      ++i; /* next URB */
+      continue;
+    }
+
+    fprintf (stderr, "here, error?\n");
+
     /*
      * If there was an error, that wasn't EAGAIN (no completion), then
      * something happened during the reaping and we should return that
      * error now
      */
-    if (ret < 0 && !urb.usercontext && errno != EAGAIN)
+    if (ret < 0 && !urbs[i].usercontext && errno != EAGAIN)
       USB_ERROR_STR(-errno, "error reaping URB: %s", strerror(errno));
 
-    bytesdone += urb.actual_length;
-  } while ((ret == 0 || urb.usercontext) && bytesdone < size && urb.actual_length == requested);
+    /* If the URB didn't complete in success or error, then let's unlink it */
+    if (ret < 0 && !urbs[i].usercontext) {
 
-  /* If the URB didn't complete in success or error, then let's unlink it */
-  if (ret < 0 && !urb.usercontext) {
-    int rc;
+      if (!waiting)
+        rc = -ETIMEDOUT;
+      else
+        rc = urbs[i].status;
+
+      ret = ioctl(dev->fd, IOCTL_USB_DISCARDURB, &urbs[i]);
+      if (ret < 0 && errno != EINVAL && usb_debug >= 1)
+        fprintf(stderr, "error discarding URB: %s", strerror(errno));
+
+      /*
+       * When the URB is unlinked, it gets moved to the completed list and
+       * then we need to reap it or else the next time we call this function,
+       * we'll get the previous completion and exit early
+       */
+      ioctl(dev->fd, IOCTL_USB_REAPURB, &context);
 
-    if (!waiting)
-      rc = -ETIMEDOUT;
-    else
-      rc = urb.status;
-
-    ret = ioctl(dev->fd, IOCTL_USB_DISCARDURB, &urb);
-    if (ret < 0 && errno != EINVAL && usb_debug >= 1)
-      fprintf(stderr, "error discarding URB: %s", strerror(errno));
-
-    /*
-     * When the URB is unlinked, it gets moved to the completed list and
-     * then we need to reap it or else the next time we call this function,
-     * we'll get the previous completion and exit early
-     */
-    ioctl(dev->fd, IOCTL_USB_REAPURB, &context);
-
-    return rc;
+      goto end;
+    }
   }
 
-  return bytesdone;
+	rc = bytesdone;
+
+ end:
+  free (urbs);
+  return rc;
 }
 
 int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size,


Yours,

-- 
René Rebe - Rubensstr. 64 - 12157 Berlin (Europe / Germany)
            http://www.exactcode.de | http://www.t2-project.org
            +49 (0)30  255 897 45



More information about the sane-devel mailing list