Greg,

sorry for not replying earlier but I am kind of busy at the moment.

Greg KH wrote:
> 
> On Tue, May 28, 2002 at 01:30:18AM +0200, Stephan Feder wrote:
> > Dear Greg,
> >
> > Greg KH wrote:
> > >
> > > Ok, now that 2.5.16 is out, we have a total of 4 different USB UHCI
> > > controller drivers in the kernel!  That's about 3 too many for me :)
> > ...
> >
> > 1. I tried uhci-hcd and usb-uhci-hcd with linux-2.5.18 and in both cases
> > I get "usb_control/bulk_msg: timeout" while reading from my scanner
> > (using the scanner module). Same with every kernel I tested from 2.4.0
> > up to 2.4.18 with uhci and usb-uhci. Reported on this list last august.
> >
> > 2. usb-uhci-hcd is up to 10% faster than uhci-hcd. This is critical for
> > me because I cannot have the scan process stop every other scanline
> > (hardware is cheap but not that cheap).
> 
> How did you measure this due to what happens in 1.?
...

>From my original posting (note: "transfer block size" relates to
IBUF_SIZE in scanner.h)

<quote>

After experimenting with different scanner parameters such as
resolution, color mode, transfer block sizes etc I can sum up my
findings as follows:

1. With some settings (mostly those that cause heavy traffic) I get bulk
transfer timeouts.
2. The timeouts with the above settings are reproducible.
3. This behaviour is not directly related to the specific setting; in
many cases choosing a larger or smaller transfer block size works (note:
the overall amount of transferred data and the transfer rate remain the
same as before).
4. After reading the logs of the usb-uhci driver I noticed that a
requested transfer block gets split in many small data packets of 64
bytes, i.e the maximum packet size of a bulk transfer which coincides
with my scanner's maximum packet size). These are all scheduled, and
after receiving about half of the requested packets the uhci driver logs
that there are no more outstanding packets. This results in a short read
count in scanner.o, and the scanner again responds only after a complete
unload of the uhci driver.
5. The behaviour described above does not occur if the transfer size of
calls to usb_bulk_msg is forcibly limited to 64 bytes without changing
any scanner specific parameters.

</quote>

As the method described in 5. lead to inacceptable transfer rates I
rewrote read_scanner() in scanner.c so that it schedules lots of URBs
with a payload of 64 bytes each, with every completed URB immediately
rescheduled to keep the transfer rate at maximum peak. I attached the
source code of this function together with some helper functions and
definitions; just replace the function in scanner.c with this piece of
code and it should compile and work with all kernel versions 2.4.x and
2.5.x.

Regards
        Stephan

#define SCANNER_URB_SIZE (64)
#define SCANNER_URB_COUNT (IBUF_SIZE / sizeof (struct scanner_urb))

struct scanner_urb
{
        struct urb          urb;
        char                buf[SCANNER_URB_SIZE];
};

struct scanner_context
{
        wait_queue_head_t wait;
        unsigned int      count;
};

static void
scanner_complete (struct urb* urb)
{
        struct scanner_context* context;
        unsigned long           count;

        context = urb->context;
        count = context->count + urb->transfer_buffer_length;
        dbg ("complete: %lu", (unsigned long) count);
        barrier ();
        context->count = count;
        wake_up_interruptible (&context->wait);
}

static inline void
scanner_dec (struct scanner_urb** urb_ptr,
             struct scanner_urb*  urbs)
{
        if (urbs == *urb_ptr) {
                *urb_ptr = urbs + (SCANNER_URB_COUNT - 1);
        }
        else {
                *urb_ptr -= 1;
        }
}

static ssize_t
read_scanner (struct file* file,
              char*        buffer,
              size_t       count,
              loff_t*      ppos)
{
        struct scn_usb_data*   scn;
        struct scanner_context context;
        struct scanner_urb*    urbs;
        struct scanner_urb*    in;
        struct scanner_urb*    out;
        unsigned int           rcv;
        unsigned int           snd;
        char*                  ptr;
        ssize_t                result;

        dbg ("reading: %lu", (unsigned long) count);

        /* early return */

        if (count == 0) {
                dbg ("nothing to do");
                return 0;
        }

        scn = file->private_data;

        /* locking */

        if (down_interruptible (&scn->sem)) {
                dbg ("interrupted");
                return -ERESTARTSYS;
        }

        /* Updating atime of the device node */

        file->f_dentry->d_inode->i_atime = CURRENT_TIME;

        /* initialising */

        ptr = buffer;
        urbs = (struct scanner_urb*) scn->ibuf;
        memset (urbs, 0, sizeof (struct scanner_urb) * SCANNER_URB_COUNT);
        in = out = urbs + (SCANNER_URB_COUNT - 1);
        context.count = snd = rcv = 0;
        init_waitqueue_head (&context.wait);

        /* looping */

        do {
                /* scheduling */
                while (((in != out) || (snd == rcv)) && (snd != count)) {
                        FILL_BULK_URB (&out->urb,
                                       scn->scn_dev,
                                       usb_rcvbulkpipe (scn->scn_dev,
                                                        scn->bulk_in_ep),
                                       &out->buf,
                                       min_t (size_t, 
                                              count - snd, 
                                              SCANNER_URB_SIZE),
                                       scanner_complete,
                                       &context);
                        out->urb.transfer_flags = USB_QUEUE_BULK;
                        result = usb_submit_urb (&out->urb, 0);
                        if (result != 0) {
                                err ("schedule failed: %li", (long) result);
                                goto scanner_read_cleanup;
                        }
                        snd += out->urb.transfer_buffer_length;
                        scanner_dec (&out, urbs);
                        dbg ("scheduled: %lu", (unsigned long) snd);
                }

                /* waiting */

                result = wait_event_interruptible (context.wait,
                                                   rcv != context.count);
                if (result != 0) {
                        dbg ("wait interrupted");
                        goto scanner_read_cleanup;
                }

                /* processing */

                result = in->urb.status;
                if ((result != 0) && (result != -EREMOTEIO)) {
                        warn ("read_scanner(%d): error:%d. "
                              "Consult Documentation/usb/scanner.txt.",
                              scn->scn_minor, result);
                        if (usb_endpoint_halted (
                                  scn->scn_dev,
                                  usb_rcvbulkpipe (scn->scn_dev,
                                                   scn->bulk_in_ep),
                                  0)) {
                                if (usb_clear_halt (scn->scn_dev,
                                                    scn->bulk_in_ep)) {
                                        err ("read_scanner(%d): Failure to "
                                             "clear endpoint halt condition "
                                             "(%Zd).",
                                             scn->scn_minor, result);
                                }
                        }
                        result = -EIO;
                        goto scanner_read_cleanup;
                }
                if (in->urb.actual_length == 0) {
                        result = ptr - buffer;
                        err ("no data");
                        goto scanner_read_cleanup;
                }
                copy_to_user (ptr, in->buf, in->urb.actual_length);
                ptr += in->urb.actual_length;
                rcv += in->urb.transfer_buffer_length;
                scanner_dec (&in, urbs);
                dbg ("received: %lu", (unsigned long) rcv);
        }
        while (rcv != count);

        result = ptr - buffer;

        /* cleaning up */

scanner_read_cleanup:

        if (rcv != snd) {
                dbg ("cleaning up");
                do {
                        usb_unlink_urb (&in->urb);
                        scanner_dec (&in, urbs);
                }
                while (in != out);
        }

        /* unlocking */

        up (&scn->sem);

        dbg ("returning %li", (signed long) result);

        return result;
}


Reply via email to