使用异步I / O的Libusb竞争条件

时间:2018-04-02 04:05:20

标签: c libusb

我正在使用libusb与配置为USB设备的飞利浦ISP1362进行通信。我能够使用同步I / O成功环回数据而没有任何问题。出于某种原因,使用异步I / O时,似乎存在竞争条件。

我正在使用背靠背OUT-IN传输传输64字节数据包。偶尔,当我运行程序时,libusb会抛出超时错误,并且某些环回数据会丢失。当使用我的Beagle 12分析USB总线时,我可以看到OUT-IN事务发生故障(即OUT-OUT-IN-TIMEOUT)应该是(OUT-IN-OUT-IN)。

更新回调函数中的传输出现乱序,这很奇怪,因为它们与总线分析器上的实际内容不一致。

示例1 :(输入输出)

main(): submitting transfer 0, endpoint 1
main(): submitting transfer 1, endpoint 82
main(): submitting transfer 2, endpoint 1
main(): submitting transfer 3, endpoint 82
xfr_cb(): count 0, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 1, status = 0, endpoint = 1, actual_length = 64, completed = 0
xfr_cb(): count 2, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 3, status = 0, endpoint = 1, actual_length = 64, completed = 0
completed

示例2 :( OUT-IN-IN-OUT)

main(): submitting transfer 0, endpoint 1
main(): submitting transfer 1, endpoint 82
main(): submitting transfer 2, endpoint 1
main(): submitting transfer 3, endpoint 82
xfr_cb(): count 0, status = 0, endpoint = 1, actual_length = 64, completed = 0
xfr_cb(): count 1, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 2, status = 0, endpoint = 82, actual_length = 64, completed = 0
xfr_cb(): count 3, status = 0, endpoint = 1, actual_length = 64, completed = 0
completed

以下是分析仪的截图:

Beagle 12 Capture

以下是代码:

#include <stdlib.h>
#include <stdio.h>

#include <libusb-1.0/libusb.h>

/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID   0x0471
#define PRODUCT_ID  0x3630

/* Define number of bytes to transfer */
#define EP_SIZE 64               // bytes
#define TRANSFERS 4              // number of transfers
#define BYTES EP_SIZE*TRANSFERS
#define TIMEOUT 3*1000           // milliseconds

/* Use a global variable to keep the device handle */
static struct libusb_device_handle *devh = NULL;

/* use a global variable to keep the context */
static struct libusb_context *usb_context = NULL;

/* count variable */
int count = 0;

/* The Endpoint addresses are hard-coded.  You should use libusb -v to find
 * the values corresponding to device
 */
static int ep_in  = 0x82;
static int ep_out = 0x01;

void xfr_cb(struct libusb_transfer *transfer )
{
  int *completed = transfer->user_data;

  /* callback - This is called after the transfer has been received by libusb */
  fprintf(stderr, "xfr_cb(): count %d, status = %d, endpoint = %x, actual_length = %d, completed = %d\n",
          count,
          transfer->status,
          transfer->endpoint,
          transfer->actual_length,
          *completed);
  if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
  {
    /* Error! */
    fprintf(stderr, "Error: %s\n", libusb_error_name((int)transfer->status));
  }

  if (count == TRANSFERS-1)
    *completed = 1;
  count++;
}

int main(int argc, char **argv)
{
  int ep_addr;
  int completed = 0;
  unsigned char *buf;
  size_t length = 64;
  int n;
  int i;
  int rc;

  /* Initialize libusb */
  rc = libusb_init(NULL);
  if (rc < 0)
  {
    fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
    exit(1);
  }

  /* Set debugging output to max level */
  libusb_set_debug(NULL, 3);

  /* Look for a specific device and open it */
  devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
  if (!devh)
  {
    fprintf(stderr, "Error finding USB device\n");
    goto out;
  }

  /* allocate memory */
  buf = malloc(length);

  /* start with OUT transfer */
  ep_addr = ep_out;

  /* queue up alternating OUT-IN transfers */
  for (i = 0; i < TRANSFERS; i++)
  {
    /* fill the buffer with incrementing data */
    for (n = 0; n < EP_SIZE; n++)
    {
      buf[n] = i+n;
    }

    /* Set up the transfer object */
    struct libusb_transfer *transfer;
    transfer = libusb_alloc_transfer(0);
    libusb_fill_bulk_transfer(transfer, devh, ep_addr, buf, EP_SIZE, xfr_cb, &completed, TIMEOUT); /* callback data = &completed */

    /* Submit the transfer object */
    libusb_submit_transfer(transfer);
    fprintf(stderr, "main(): submitting transfer %d, endpoint %x\n", i, ep_addr);

    /* alternate writing and reading for loopback */
    ep_addr = (ep_addr == ep_out) ? ep_in : ep_out;
  }

  /* Handle Events */
  while (!completed)
  {
    rc = libusb_handle_events_completed(NULL, &completed);
    if (rc < 0)
    {
      if (rc == LIBUSB_ERROR_INTERRUPTED)
        continue;
      fprintf(stderr, "Transfer Error: %s", libusb_error_name(rc));
      continue;
    }
  }

  fprintf(stderr, "completed\n");

  /* Release the interface */
  libusb_release_interface(devh, 0);


  /* Close the device handle */
  if (devh)
    libusb_close(devh);


out:
  if (devh)
  {
    libusb_close(devh);
  }
  libusb_exit(NULL);

  return rc;
}

更新2 我成功消除了超时。 libusb超时的原因是因为主机在总线上间歇性地发送了两个连续的OUT事务。

Analyzer截图:

enter image description here

以下是工作代码(无超时)。这几千次没有问题

static void LIBUSB_CALL xfr_cb(struct libusb_transfer *transfer )
{
  int *completed = transfer->user_data;
  unsigned char *wbuf, *rbuf;
  size_t length = 64;

  fprintf(stderr, "xfr_cb(): status = %d, endpoint = %x, actual_length = %d\n",
          transfer->status,
          transfer->endpoint,
          transfer->actual_length);

  *completed = 1;
}

int main(int argc, char **argv)
{
  const struct libusb_version *version;
  int ep_addr;
  int completed = 0;
  unsigned char *buf, *wbuf1, *wbuf2, *rbuf1, *rbuf2;
  size_t length = 64;
  int n;
  int m;
  int i;
  int rc;

  /* Get libusb version */
  version = libusb_get_version();
  fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);

  /* Initialize libusb */
  rc = libusb_init(NULL);
  if (rc < 0)
  {
    fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
    exit(1);
  }

  /* Set debugging output to max level */
  libusb_set_debug(NULL, 3);

  /* Look for a specific device and open it */
  handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
  if (!handle)
  {
    fprintf(stderr, "Error finding USB device\n");
    goto out;
  }

  /* claim interface */
  rc = libusb_claim_interface(handle, 0);
  if (rc < 0)
  {
    fprintf(stderr, "Error claiming interface.\n");
    goto out;
  }

  /* allocate memory */
  wbuf1 = malloc(length);
  wbuf2 = malloc(length);
  rbuf1 = malloc(length);
  rbuf2 = malloc(length);

  /* fill the buffer with incrementing data */
  for (n = 0; n < EP_SIZE; n++)
    wbuf1[n] = n;

  for (m = 0; m < EP_SIZE; m++)
    wbuf2[m] = m+1;

  struct libusb_transfer *transfer1;
  struct libusb_transfer *transfer2;
  struct libusb_transfer *transfer3;
  struct libusb_transfer *transfer4;

  /* Set up the transfer object */
  transfer1 = libusb_alloc_transfer(0);
  transfer2 = libusb_alloc_transfer(0);
  transfer3 = libusb_alloc_transfer(0);
  transfer4 = libusb_alloc_transfer(0);
  libusb_fill_bulk_transfer(transfer1, handle, ep_out, wbuf1, EP_SIZE, xfr_cb, NULL, TIMEOUT);
  libusb_fill_bulk_transfer(transfer2, handle, ep_in, rbuf1, EP_SIZE,  xfr_cb, NULL, TIMEOUT);
  libusb_fill_bulk_transfer(transfer3, handle, ep_out, wbuf2, EP_SIZE, xfr_cb, NULL, TIMEOUT);
  libusb_fill_bulk_transfer(transfer4, handle, ep_in, rbuf2, EP_SIZE,  xfr_cb, &completed, TIMEOUT); /* callback data = &completed */

  /* Submit the transfers */
  libusb_submit_transfer(transfer1);
  libusb_submit_transfer(transfer2);
  libusb_submit_transfer(transfer3);
  libusb_submit_transfer(transfer4);

  /* Handle Events */
  while (!completed)
  {
    rc = libusb_handle_events_completed(NULL, &completed);
    if (rc != LIBUSB_SUCCESS)
    {
      fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
      break;
    }
  }

  fprintf(stderr, "completed\n");

  //* Release the interface */
  libusb_release_interface(handle, 0);

  /* Close the device handle */
  if (handle)
    libusb_close(handle);

out:
  if (handle)
  {
    libusb_close(handle);
  }
  libusb_exit(NULL);

  return rc;
}

如下所示更改代码(即,对于传输1-3,回调= NULL)重新创建间歇性重复事务,如屏幕截图所示。

  libusb_fill_bulk_transfer(transfer1, handle, ep_out, wbuf1, EP_SIZE, NULL, NULL, TIMEOUT);
  libusb_fill_bulk_transfer(transfer2, handle, ep_in, rbuf1, EP_SIZE,  NULL, NULL, TIMEOUT);
  libusb_fill_bulk_transfer(transfer3, handle, ep_out, wbuf2, EP_SIZE, NULL, NULL, TIMEOUT);
  libusb_fill_bulk_transfer(transfer4, handle, ep_in, rbuf2, EP_SIZE,  xfr_cb, &completed, TIMEOUT); /* callback data = &completed */

老实说,我不明白为什么循环会根据他们的文档和示例引起竞争条件。实际上在其中一个libusb示例(sam3u_benchmark.c)中建议排队多次传输,并在以下.pdfs中演示(使用循环)。

请参阅异步I / O部分:

https://www.socallinuxexpo.org/sites/default/files/presentations/scale_2017_usb.pdf http://www.signal11.us/oss/elc2014/elc_2014_usb_0.pdf

根据我的理解,使用 libusb_handle_events_completed(NULL,&amp; completed)可以解决同步问题。我误会了什么吗? 请参阅多个线程中的 libusb_handle_events() http://libusb.sourceforge.net/api-1.0/libusb_mtasync.html

- “这就是为什么libusb-1.0.9引入了新的libusb_handle_events_timeout_completed()和libusb_handle_events_completed()函数,它们在获取锁之后处理完成检查:”

如果是这种情况,他们需要的是如何使用其API的清晰示例。

我可以添加更多的事件检查,但这里似乎没有。

更新3:查看已接受的答案。

2 个答案:

答案 0 :(得分:2)

我开始阅读libusb源代码中的文档,并了解发生了什么。

特别是关于libusb如何处理数据包大小的部分: http://libusb.sourceforge.net/api-1.0/libusb_packetoverflow.html

在阅读之后它点击了我,我找到了两种使用异步I / O完成大数据大小的环回测试的方法。

第一种方法是使用包含整个数据结构的transfer-&gt;缓冲区连续提交两个传输(即要发送和接收的总字节数)。第二种方式是使用包含wMaxPacketSize(例如64字节)的transfer-&gt;缓冲区提交两个传输,并使out和in回调函数提交额外的传输以收发其余数据。

对于第二种情况,需要添加额外的代码以跟踪传输次数并在完成时设置完成的信号。 OUT-IN数据包交错由libusb和OS处理 - 这是我没有意识到的部分。换句话说,并非每个OUT-IN传输都需要单独指定和排队。

以下是异步代码以及USB设备(ISP1362)的传输速率。我的USB设备控制器是用纯SystemVerilog编码的FPGA。

注意:关于传输速率,我只在BULK_EP_IN上启用了双缓冲。我假设IN-NAK(#POLL)并且如果在BULK_EP_OUT上启用双缓冲,则第二种方法的传输速率会提高。因此,由于设备配置,这可能不是一个公平的比较。

第一种方法:~1.161 MB / s(~9.288 Mb / s)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include </usr/include/libusb-1.0/libusb.h>

/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID   0x0471
#define PRODUCT_ID  0x3630

/* Define number of bytes to transfer */
#define EP_SIZE 64                       // bytes
#define TRANSFERS 1024*768*3/EP_SIZE     // number of transfers
#define TIMEOUT 10*1000                  // milliseconds

/* Use a global variable to keep the device handle */
static struct libusb_device_handle *handle = NULL;

/* count variables */
unsigned int count = 0;
unsigned int count_in = 0;
unsigned int count_out = 0;

/* The Endpoint addresses are hard-coded.  You should use libusb -v to find
 * the values corresponding to device
 */
static int ep_in  = 0x82;
static int ep_out = 0x01;

/* Write and Read buffers */
unsigned char wbuf[EP_SIZE*TRANSFERS];
unsigned char wbuf_tmp[EP_SIZE*TRANSFERS];
unsigned char rbuf[EP_SIZE*TRANSFERS];
unsigned char rbuf_tmp[EP_SIZE*TRANSFERS];

static void LIBUSB_CALL xfr_cb_out(struct libusb_transfer *transfer )
{
  memcpy(wbuf+count_out*EP_SIZE, transfer->buffer, EP_SIZE);
}

static void LIBUSB_CALL xfr_cb_in(struct libusb_transfer *transfer )
{
  int *completed = transfer->user_data;
  memcpy(rbuf+count_in*EP_SIZE, transfer->buffer, EP_SIZE);

  count_in++;  // one transfer complete
  if (count_in < TRANSFERS)
    *completed = 1;
}

int main(int argc, char **argv)
{
  const struct libusb_version *version;
  int completed = 0;
  size_t length = 64;
  int n;
  int m;
  int rc;

  /* Get libusb version */
  version = libusb_get_version();
  fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);

  /* Initialize libusb */
  rc = libusb_init(NULL);
  if (rc < 0)
  {
    fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
    exit(1);
  }

  /* Set debugging output to max level */
  libusb_set_debug(NULL, 3);

  /* Look for a specific device and open it */
  handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
  if (!handle)
  {
    fprintf(stderr, "Error finding USB device\n");
    goto out;
  }

  /* claim interface */
  rc = libusb_claim_interface(handle, 0);
  if (rc < 0)
  {
    fprintf(stderr, "Error claiming interface.\n");
    goto out;
  }

  /* fill the buffer with incrementing data */
  for (n = 0; n < TRANSFERS; n++)
  {
    for (m = 0; m < EP_SIZE; m++)
    {
      wbuf_tmp[m+n*EP_SIZE] = m+n;
    }
  }

  struct libusb_transfer *transfer;
  transfer = libusb_alloc_transfer(0);
  libusb_fill_bulk_transfer(transfer, handle, ep_out, wbuf_tmp, EP_SIZE*TRANSFERS, xfr_cb_out, NULL, TIMEOUT);
  libusb_submit_transfer(transfer);

  transfer = libusb_alloc_transfer(0);
  libusb_fill_bulk_transfer(transfer, handle, ep_in, rbuf_tmp, EP_SIZE*TRANSFERS, xfr_cb_in, &completed, TIMEOUT);
  libusb_submit_transfer(transfer);

  /* Handle Events */
  while (!completed)
  {
    rc = libusb_handle_events_completed(NULL, &completed);
    if (rc != LIBUSB_SUCCESS)
    {
      fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
      break;
    }
  }

  fprintf(stderr, "completed\n");

  int res;
  res = memcmp(rbuf, wbuf, sizeof(wbuf));
  if (res != 0)
    fprintf(stderr, "miscompare\n");
  else
    fprintf(stderr, "success\n");

  //* Release the interface */
  libusb_release_interface(handle, 0);

  /* Close the device handle */
  if (handle)
    libusb_close(handle);

out:
  if (handle)
  {
    libusb_close(handle);
  }
  libusb_exit(NULL);

  return rc;
}

enter image description here

第二种方法:~755.9 MB / s(~6.047 Mb / s)

include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include </usr/include/libusb-1.0/libusb.h>

/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID   0x0471
#define PRODUCT_ID  0x3630

/* Define number of bytes to transfer */
#define EP_SIZE 64                       // bytes
#define TRANSFERS 1024*768*3/EP_SIZE     // number of transfers
#define TIMEOUT 10*1000                  // milliseconds

/* Use a global variable to keep the device handle */
static struct libusb_device_handle *handle = NULL;

/* count variables */
unsigned int count = 0;
unsigned int count_in = 0;
unsigned int count_out = 0;

/* The Endpoint addresses are hard-coded.  You should use libusb -v to find
 * the values corresponding to device
 */
static int ep_in  = 0x82;
static int ep_out = 0x01;

/* Write and Read buffers */
unsigned char wbuf[EP_SIZE*TRANSFERS];
unsigned char *wbuf_tmp;
unsigned char rbuf[EP_SIZE*TRANSFERS];
unsigned char rbuf_tmp[EP_SIZE*TRANSFERS];

static void LIBUSB_CALL xfr_cb_out(struct libusb_transfer *transfer )
{
  memcpy(wbuf+count_out*EP_SIZE, transfer->buffer, EP_SIZE);

  count_out++;  // one transfer complete
  if (count_out < TRANSFERS)
  {
    transfer->buffer = ++wbuf_tmp;
    libusb_submit_transfer(transfer);
  }
}

static void LIBUSB_CALL xfr_cb_in(struct libusb_transfer *transfer )
{
  int *completed = transfer->user_data;
  memcpy(rbuf+count_in*EP_SIZE, transfer->buffer, EP_SIZE);

  count_in++;  // one transfer complete
  if (count_in < TRANSFERS)
    libusb_submit_transfer(transfer);
  else
    *completed = 1;
}

int main(int argc, char **argv)
{
  const struct libusb_version *version;
  int completed = 0;
  size_t length = 64;
  int n;
  int rc;

  /* Get libusb version */
  version = libusb_get_version();
  fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);

  /* Initialize libusb */
  rc = libusb_init(NULL);
  if (rc < 0)
  {
    fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
    exit(1);
  }

  /* Set debugging output to max level */
  libusb_set_debug(NULL, 3);

  /* Look for a specific device and open it */
  handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
  if (!handle)
  {
    fprintf(stderr, "Error finding USB device\n");
    goto out;
  }

  /* claim interface */
  rc = libusb_claim_interface(handle, 0);
  if (rc < 0)
  {
    fprintf(stderr, "Error claiming interface.\n");
    goto out;
  }

  /* allocate memory */
  wbuf_tmp = malloc(length*TRANSFERS);

  /* fill the buffer with incrementing data */
  for (n = 0; n < EP_SIZE*TRANSFERS; n++)
  {
    wbuf_tmp[n] = n;
  }

  struct libusb_transfer *transfer;
  transfer = libusb_alloc_transfer(0);
  libusb_fill_bulk_transfer(transfer, handle, ep_out, wbuf_tmp, EP_SIZE, xfr_cb_out, NULL, TIMEOUT);
  libusb_submit_transfer(transfer);

  transfer = libusb_alloc_transfer(0);
  libusb_fill_bulk_transfer(transfer, handle, ep_in, rbuf_tmp, EP_SIZE, xfr_cb_in, &completed, TIMEOUT);
  libusb_submit_transfer(transfer);

  /* Handle Events */
  while (!completed)
  {
    rc = libusb_handle_events_completed(NULL, &completed);
    if (rc != LIBUSB_SUCCESS)
    {
      fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
      break;
    }
  }

  fprintf(stderr, "completed\n");

  int res;
  res = memcmp(rbuf, wbuf, sizeof(wbuf));
  if (res != 0)
    fprintf(stderr, "miscompare\n");
  else
    fprintf(stderr, "success\n");

  //* Release the interface */
  libusb_release_interface(handle, 0);

  /* Close the device handle */
  if (handle)
    libusb_close(handle);

out:
  if (handle)
  {
    libusb_close(handle);
  }
  libusb_exit(NULL);

  return rc;
}

enter image description here

答案 1 :(得分:1)

更新:查看已接受的答案。

以下是使用同步I / O的示例。使用异步I / O使事务以预期的顺序出现时遇到了很多麻烦。我认为这是因为@Gene提到的转会比赛。

我对libusb API的主要抱怨是缺乏说明正确使用的示例。 API会导致某人认为异步事务按照“提交”的顺序放在总线上,而且从我收集的内容来看,这不是真的。此功能适用于提交具有所有相同数据包TOKEN(即OUT或IN)的事务。

以下代码适用于大量批量转移。

使用同步I / O

#include <stdlib.h>
#include <stdio.h>

#include <libusb-1.0/libusb.h>

/* Change VENDOR_ID and PRODUCT_ID depending on device */
#define VENDOR_ID   0x0471
#define PRODUCT_ID  0x3630

/* Define number of bytes to transfer */
#define BYTES 1024*768*3 // bytes
#define EP_SIZE 64       // bytes
#define TIMEOUT 5*1000   // milliseconds

/* Use a global variable to keep the device handle */
static struct libusb_device_handle *devh = NULL;

/* The Endpoint addresses are hard-coded.  You should use libusb -v to find
 * the values corresponding to device
 */
static int ep_in_addr  = 0x82;
static int ep_out_addr = 0x01;

int write_chars(unsigned char * data, int length)
{
  /* To send a char to the device simply initiate a bulk_transfer to the Endpoint
   * with the address ep_out_addr.
   */
  int actual_length;

  int rc = libusb_bulk_transfer(devh, ep_out_addr, data, length, &actual_length, TIMEOUT);

  if (rc < 0)
  {
    fprintf(stderr, "Error while sending char: %d\n", rc);
    return -1;
  }

  return actual_length;
}

int read_chars(unsigned char * data, int length)
{
  /* To receive characters from the device initiate a bulk_transfer to the Entpoint
   * with address ep_in_addr
   */
  int actual_length;

  int rc = libusb_bulk_transfer(devh, ep_in_addr, data, length, &actual_length, TIMEOUT);

  if (rc == LIBUSB_ERROR_TIMEOUT)
  {
    printf("timeout (%d)\n", actual_length);
    return -1;
  }
  else if (rc < 0)
  {
    fprintf(stderr, "Error while waiting for char: %d\n", rc);
    return -1;
  }

  return actual_length;
}

int main(int argc, char **argv)
{
  int rc;

  /* Initialize libusb */
  rc = libusb_init(NULL);

  if (rc < 0)
  {
    fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
    exit(1);
  }

  /* Set debugging output to max level */
  libusb_set_debug(NULL, 3);

  /* Look for a specific device and open it */
  devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
  if (!devh)
  {
    fprintf(stderr, "Error finding USB device\n");
    goto out;
  }

  /* We can now start sending or receiving data to the device */
  unsigned char buf[BYTES];
  unsigned char rbuf[EP_SIZE];
  int len;
  int n;
  int l;
  int res;

  // fill buffer
  for (n = 0; n < BYTES; n++)
  {
    buf[n] = 0x00+n;
  }

  // loopback data, write-read
  for (l = 0; l < BYTES/EP_SIZE; l++)
  {
    len = write_chars(buf+l*EP_SIZE, EP_SIZE);
    len = read_chars(rbuf, EP_SIZE);
    res = memcmp(rbuf, buf+l*EP_SIZE, sizeof(rbuf));
    if (res != 0)
      fprintf(stderr, "Miscompare: block %d\n", l);
  }

  libusb_release_interface(devh, 0);

out:
  if (devh)
  {
    libusb_close(devh);
  }
  libusb_exit(NULL);

  return rc;
}

同时使用异步和同步(即OUT是异步提交的,IN是同步的)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include </usr/include/libusb-1.0/libusb.h>

/* Specify VENDOR_ID and PRODUCT_ID for device */
#define VENDOR_ID   0x0471
#define PRODUCT_ID  0x3630

/* Define number of bytes to transfer */
#define EP_SIZE 64                       // bytes
#define TRANSFERS 1024*768*3/EP_SIZE     // number of transfers
#define BYTES EP_SIZE*TRANSFERS
#define TIMEOUT 15*1000                  // milliseconds

/* Use a global variable to keep the device handle */
static struct libusb_device_handle *handle = NULL;

/* count variable */
unsigned int count_out = 0;

/* The Endpoint addresses are hard-coded.  You should use libusb -v to find
 * the values corresponding to device
 */
static int ep_in  = 0x82;
static int ep_out = 0x01;

unsigned char rbuf[EP_SIZE*TRANSFERS];
unsigned char wbuf[EP_SIZE*TRANSFERS];

static void LIBUSB_CALL xfr_cb_out(struct libusb_transfer *transfer )
{
  memcpy(wbuf+count_out*EP_SIZE, transfer->buffer, EP_SIZE);
  count_out++;
}

int main(int argc, char **argv)
{
  const struct libusb_version *version;
  unsigned char *buf, *rbuf_tmp;
  size_t length = 64;
  int n;
  int i;
  int rc;

  /* Get libusb version */
  version = libusb_get_version();
  fprintf(stderr, "libusb version: %d.%d.%d.%d\n", version->major, version->minor, version->micro, version->nano);

  /* Initialize libusb */
  rc = libusb_init(NULL);
  if (rc < 0)
  {
    fprintf(stderr, "Error Initializing libusb: %s\n", libusb_error_name(rc));
    exit(1);
  }

  /* Set debugging output to max level */
  libusb_set_debug(NULL, 3);

  /* Look for a specific device and open it */
  handle = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
  if (!handle)
  {
    fprintf(stderr, "Error finding USB device\n");
    goto out;
  }

  /* claim interface */
  rc = libusb_claim_interface(handle, 0);
  if (rc < 0)
  {
    fprintf(stderr, "Error claiming interface.\n");
    goto out;
  }

  /* allocate memory */
  buf = malloc(length*TRANSFERS);

  /* fill the buffer with incrementing data */
  for (n = 0; n < EP_SIZE*TRANSFERS; n++)
  {
    buf[n] = n;
  }

  /* allocate memory */
  rbuf_tmp = malloc(length);

  /* set up alternating OUT-IN transfers */
  for (i = 0; i < TRANSFERS; i++)
  {
    struct libusb_transfer *transfer;
    transfer = libusb_alloc_transfer(0);
    libusb_fill_bulk_transfer(transfer, handle, ep_out, buf+i, EP_SIZE, xfr_cb_out, NULL, TIMEOUT);
    libusb_submit_transfer(transfer);

    int actual_length;
    int rc = libusb_bulk_transfer(handle, ep_in, rbuf_tmp, EP_SIZE, &actual_length, TIMEOUT);
    if (rc != LIBUSB_SUCCESS)
    {
      fprintf(stderr, "Transfer Error: %s\n", libusb_error_name(rc));
      break;
    }

    memcpy(rbuf+i*EP_SIZE, rbuf_tmp, EP_SIZE);
  }
  fprintf(stderr, "completed\n");

  int res;
  res = memcmp(rbuf, wbuf, sizeof(wbuf));
  if (res != 0)
    fprintf(stderr, "miscompare\n");

  //* Release the interface */
  libusb_release_interface(handle, 0);

  /* Close the device handle */
  if (handle)
    libusb_close(handle);

out:
  if (handle)
  {
    libusb_close(handle);
  }
  libusb_exit(NULL);

  return rc;
}

上面的代码是一个实验,看看性能是否提高了。有趣的是,两者之间的速度差异可以忽略不计。

libusb的版本是1.0.17.10830