Asynchronous bulk transfers in usb2 ?

Hans Petter Selasky hselasky at c2i.net
Thu Dec 4 08:02:18 PST 2008


On Wednesday 03 December 2008, Thierry Herbelot wrote:
> Hello,
>
> I've been looking at the usb2 code, and from what I've understood, only
> synchronous transfers are possible : a read (for exemple) is only scheduled
> when the userland program calls usb_bulk_read().
>
> Furthermore, only fixed size buffers are used : only one buffer of 32 kbyte
> per transfer. This seems sub-optimal when reading (or writing) from
> userland blocks bigger than 32 kbytes, as multiple kernel-to-userland
> switches are necessary, and there is a latency window where no buffer
> exists to accept data between the arrival of one block and the posting of
> the next transfer.
>
> One way to work around this kind of limitations is to provide in advance a
> certain number of transfers, arranged in a ring, where the callback
> function at the end of a transfer schedules the next, without any further
> userland intervention.
>
> Is there any project for adding asynchronous bulk reads and/or writes to
> the fine usb2 stack ?

Hi Thierry,

There are two USB API's in the USB2 library. See man libusb20.

The other API supports all of what you want to do. I.E. all of the USB 
functionality which is present in the kernel.

--HPS

Example code for the other USB API:

        struct libusb20_backend *pbe =
          libusb20_be_alloc_default();
        struct libusb20_device *pdev = NULL;
        while ((pdev = libusb20_be_device_foreach(pbe, pdev))) {
                if (strstr(libusb20_dev_get_desc(pdev), argv[1]))
                  {
                    libusb20_be_dequeue_device(pbe, pdev);
                    break;
                  }
        }

        /* release data */
        libusb20_be_free(pbe);
        if (pdev == NULL) {
            printf("No such device\n");
            return (0);
        }

        printf("Trying to attach ...\n");

        if (libusb20_dev_open(pdev, 2)) {
                err(1, "could not open device");
        }
        xfer_in = libusb20_tr_get_pointer(pdev, 0);
        ep = 0x81;
        error = libusb20_tr_open(xfer_in, 65536 /* max block size */, 1 /* # 
of transfers */, ep /* endpoint */);
        if (error) {
                err(1, "could not open endpoint %u, %d", ep, error);
        }

        xfer_out = libusb20_tr_get_pointer(pdev, 1);
        ep = 0x01;
        error = libusb20_tr_open(xfer_out, 65536, 1, ep);
        if (error) {
                err(1, "could not open endpoint %u, %d", ep, error);
        }

        usb_pdev = pdev;

        libusb20_tr_clear_stall_sync(xfer_in);
        libusb20_tr_clear_stall_sync(xfer_out);

do_io(struct libusb20_transfer *xfer, void *buf, uint32_t len, uint32_t 
timeout)
{
        struct libusb20_device *pdev = usb_pdev;
        uint32_t max;
        uint32_t alen;
        uint32_t slen = 0;

        if (libusb20_tr_pending(xfer)) {
          return (-1); /* error */
        }

 repeat:
        max = libusb20_tr_get_max_total_length(xfer);
        if (max > len)
          max = len;

//There is also a function to setup multiple bulk transfers at the 
//same time. See man libusb20

        libusb20_tr_setup_bulk(xfer, buf, max, timeout);
        libusb20_tr_start(xfer);

        while (libusb20_dev_process(pdev) == 0) {
          if (libusb20_tr_pending(xfer) == 0) {
            break;
          }
// there is also an FD that you can poll!
// See the libusb20_dev_wait_process() code.
          libusb20_dev_wait_process(pdev, -1);
        }
        if (libusb20_tr_get_status(xfer))
          return (-1);

        alen = libusb20_tr_get_actual_length(xfer);

        slen += alen;

        if (alen == max) {
          len -= alen;
          if (len)
            goto repeat;
        }

        return (slen);
}


More information about the freebsd-usb mailing list