前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >USB Gadget 应用实例之 zero

USB Gadget 应用实例之 zero

作者头像
Jasonangel
发布2023-08-22 13:57:59
7450
发布2023-08-22 13:57:59
举报
文章被收录于专栏:嵌入式Linux系统开发

1. 编写程序

1.1 编程思路

涉及的程序如下图所示:

PC 端基于 libusb 编写应用程序,开发板端直接使用 Linux 自带的 USB Gadget 驱动 zero.c【/drivers/usb/gadget/legacy/zero.c】。

应用程序编程框架如下:

  • 找到设备
  • 选择配置:zero.c 提供了两种配置,loopback、sourcesink
  • 得到端点:找到 interface 进而得到 endpoint
  • 读写数据:操作 endpoint

1.2 zero 设备的描述符

在 Ubuntu 里执行如下命令,根据 VID:PID 获取设备信息:

代码语言:javascript
复制
$ lsusb -v -d 0525:a4a0

可以列出 zero 设备的描述符:

代码语言:javascript
复制
Bus 001 Device 002: ID 0525:a4a0 Netchip Technology, Inc. Linux-USB "Gadget Zero"
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass          255 Vendor Specific Class
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor           0x0525 Netchip Technology, Inc.
  idProduct          0xa4a0 Linux-USB "Gadget Zero"
  bcdDevice            4.09
  iManufacturer           1
  iProduct                2
  iSerial                 3
  bNumConfigurations      2
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           69
    bNumInterfaces          1
    bConfigurationValue     3
    iConfiguration          4
    bmAttributes         0xc0
      Self Powered
    MaxPower                2mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       1
      bNumEndpoints           4
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0400  1x 1024 bytes
        bInterval               4
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0400  1x 1024 bytes
        bInterval               4
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           32
    bNumInterfaces          1
    bConfigurationValue     2
    iConfiguration          5
    bmAttributes         0xc0
      Self Powered
    MaxPower                2mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              6
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0

它有 2 个配置:

  • 第 1 个配置(bConfigurationValue = 2)对应 loopback 功能:里面有 1 个接口,接口有 1 个 setting,下面有 2 个 endpoint
  • 第 2 个配置(bConfigurationValue = 3)对应 SourceSink 功能:里面有 1 个接口,接口有 2 个 setting
    • 第 1 个 setting 下面有 2 个 endpoint:都是 bulk 端点
    • 第 2 个 setting 下面有 4 个 endpoint:2 个是 bulk 端点,另外 2 个是 Isochronous 端点

1.3 编程

参考 libusb 示例:libusb\examples\xusb.c

代码语言:javascript
复制
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libusb-1.0/libusb.h>

#define DRIVER_VENDOR_NUM 0x0525  /* NetChip */
#define DRIVER_PRODUCT_NUM 0xa4a0  /* Linux-USB "Gadget Zero" */

int get_bulk_endpoint(libusb_device *dev, int *in_ep, int *out_ep, int *in_ep_maxlen)
{
    struct libusb_config_descriptor *config;
    const struct libusb_endpoint_descriptor *ep;
    int r;
    int iface_idx;
    int found = 0;

    r = libusb_get_active_config_descriptor(dev, &config);
    if (r < 0) {
        printf("could not retrieve active config descriptor");
        return LIBUSB_ERROR_OTHER;
    }

 {
  const struct libusb_interface *iface = &config->interface[0];
  int altsetting_idx = 0;

  const struct libusb_interface_descriptor *altsetting
   = &iface->altsetting[altsetting_idx];
  int ep_idx;

  for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) {
   const struct libusb_endpoint_descriptor *ep = &altsetting->endpoint[ep_idx];

  if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK)
  {
       if (ep->bEndpointAddress & LIBUSB_ENDPOINT_IN)
       {
        *in_ep = ep->bEndpointAddress;
        *in_ep_maxlen = ep->wMaxPacketSize;
         found++;
       }
       else
       {
         *out_ep = ep->bEndpointAddress;
         found++;
        }
     }
  }
 }

    libusb_free_config_descriptor(config);
    return (found == 2) ? 0 : -1;
}

void PrintUsage(char *name)
{
    printf("Usage:\n");
    printf("%s -l : list bConfigurationValue of all configs\n", name);
    printf("%s -s <bConfigurationValue> : select config\n", name);
    printf("%s -wstr <string> : write string\n", name);
    printf("%s -rstr : read string\n", name);
    printf("%s -w <val1 val2 ....> : write bytes\n", name);
    printf("%s -r : read 32 bytes\n", name);
}

int main(int argc, char **argv)
{
    int err = 0;
    libusb_device *dev, **devs;
    int num_devices;
    int endpoint;
    int interface_num = 0;
    int found = 0;
    int transferred;
    int count = 0;
    unsigned char buffer[1024];
    struct libusb_config_descriptor *config_desc;
    struct libusb_device_handle *dev_handle = NULL;
    int i;
    int in_ep, out_ep;
    int in_ep_maxlen;

    if (argc == 1)
    {
        PrintUsage(argv[0]);
        return 0;
    }
    
    /* libusb_init */
    err = libusb_init(NULL);
    if (err < 0) {
        fprintf(stderr, "failed to initialise libusb %d - %s\n", err, libusb_strerror(err));
        exit(1);
    }

    /* open device */
    dev_handle = libusb_open_device_with_vid_pid(NULL, DRIVER_VENDOR_NUM, DRIVER_PRODUCT_NUM);
    if (!dev_handle) {
         printf("can not open zero device\n");
    return -1;
 }

 dev = libusb_get_device(dev_handle);
    /* 想选择某一个配置, 先知道它的bConfigurationValue */
    if (!strcmp(argv[1], "-l"))
    {
        for (i = 0; i < 255; i++)        
        {
            /* parse interface descriptor, find usb mouse */        
            err = libusb_get_config_descriptor(dev, i, &config_desc);
            if (err) {
                //fprintf(stderr, "could not get configuration descriptor\n");
                break;
            }
            printf("config %d: bConfigurationValue = %d\n", i, config_desc->bConfigurationValue);
            libusb_free_config_descriptor(config_desc);
        }
        return 0;
    }

    /* 想选择某一个配置 */
    if (!strcmp(argv[1], "-s") && (argc == 3))
    {
        i = strtoul(argv[2], NULL, 0);
        libusb_set_auto_detach_kernel_driver(dev_handle, 0);  
        libusb_detach_kernel_driver(dev_handle, 0);
        //libusb_release_interface(dev_handle, 0);
        err = libusb_set_configuration(dev_handle, i);
        if (err) {
            fprintf(stderr, "could not set configuration as %d, err = %d\n", i, err);
            return -1;
        }
        return 0;
    }

    err = libusb_get_configuration(dev_handle, &i);
    fprintf(stdout, "current config: %d\n", i);

    /* 想读写数据需要得到 endpoint */
    err = get_bulk_endpoint(dev, &in_ep, &out_ep, &in_ep_maxlen);
    if (err) {
        fprintf(stderr, "could not get bulk endpoints\n");
        goto exit;
    }
    fprintf(stdout, "in_ep = 0x%x, out_ep = 0x%x\n", in_ep, out_ep);

    /* claim interface */
    libusb_set_auto_detach_kernel_driver(dev_handle, 1);  
    err = libusb_claim_interface(dev_handle, interface_num);
    if (err)
    {
        fprintf(stderr, "failed to libusb_claim_interface\n");
        goto exit;
    }

    /* write string */
    if (!strcmp(argv[1], "-wstr") && (argc == 3))
    {
        memset(buffer, 0, 32);
        strncpy(buffer, argv[2], 32);
        err = libusb_bulk_transfer(dev_handle, out_ep,
           buffer, 32, &transferred, 1000);        
        if (err) {
            fprintf(stderr, "libusb_bulk_transfer err = %d\n", err);
            goto exit;
        }     
        if (transferred != 32)
        {
            fprintf(stderr, "transferred != 32\n");
        }        
        goto exit;
    }

    /* read string */
    if (!strcmp(argv[1], "-rstr"))
    {
        memset(buffer, 0, 32);
        err = libusb_bulk_transfer(dev_handle, in_ep,
           buffer, 32, &transferred, 1000);        
        if (err) {
            fprintf(stderr, "libusb_bulk_transfer err = %d\n", err);
            goto exit;
        }     
        if (transferred != 32)
        {
            fprintf(stderr, "transferred != 32\n");
        }
        printf("Read string: %s\n", buffer);      
        goto exit;
    }

    /* write datas */
    if (!strcmp(argv[1], "-w") && (argc >= 3))
    {
        memset(buffer, 0, 32);
        /* argv[2],... */
        for (i = 2; i < argc; i++)
            buffer[i-2] = strtoul(argv[i], NULL, 0);

        err = libusb_bulk_transfer(dev_handle, out_ep,
           buffer, argc - 2, &transferred, 1000);        
        if (err) {
            fprintf(stderr, "libusb_bulk_transfer err = %d\n", err);
            goto exit;
        }     
        if (transferred != argc - 2)
        {
            fprintf(stderr, "transferred != %d\n", argc - 2);
        }
        goto exit;
    }
    
    /* read datas */
    if (!strcmp(argv[1], "-r")) /* 读Source/Sink这个配置里的端点时, 它一次性返回512字节的数据 */
    {
        memset(buffer, 0, 1024);
        err = libusb_bulk_transfer(dev_handle, in_ep,
           buffer, in_ep_maxlen, &transferred, 1000);        
        if (err) {
            fprintf(stderr, "libusb_bulk_transfer err = %d\n", err);
            goto exit;
        }     
        if (transferred != in_ep_maxlen)
        {
            fprintf(stderr, "transferred != in_ep_maxlen\n");
        }
        printf("Read datas: \n");
        for (i = 0; i < transferred; i++)
        {
            printf("%02x ", buffer[i]);
            if ((i+1) % 16 == 0)
                printf("\n");
        }
        printf("\n");
        
        goto exit;
    }

exit:
    /* libusb_close */
    libusb_release_interface(dev_handle, interface_num);
    libusb_close(dev_handle);
    libusb_exit(NULL);
    return err;
}

2. 上机实验

实验步骤:

  • 先安装 g_zero 驱动程序:在开发板上执行modprobe g_zero
  • 然后连接 OTG 线到 PC
  • 在 Ubuntu 中识别出设备
  • 执行测试程序
    • 先编译:在 Ubuntu 里执行如下命令
代码语言:javascript
复制
apt-cache search libusb               # 查找 libusb 开发包
sudo apt install libusb-1.0-0-dev     # 安装 libusb 开发包
gcc -o zero_app zero_app.c -lusb-1.0  # 编译
  • 测试:在 Ubuntu 里执行如下命令
代码语言:javascript
复制
$ sudo ./zero_app -l    # 列出设备的配置值
config 0: bConfigurationValue = 3
config 1: bConfigurationValue = 2

# 测试loopback功能
$ sudo ./zero_app -s 2                  # 选择loopback的配置
$ sudo ./zero_app -wstr www.100ask.net  # 写入字符串
current config: 2
in_ep = 0x81, out_ep = 0x1
$ sudo ./zero_app -rstr                # 读出字符串
current config: 2
in_ep = 0x81, out_ep = 0x1
Read string: www.100ask.net

$ sudo ./zero_app -w 1 2 3 4 5 6 7 8   # 写入8个字节
current config: 2
in_ep = 0x81, out_ep = 0x1
sudo ./zero_app -r                     # 读到8个字节
current config: 2
in_ep = 0x81, out_ep = 0x1
transferred != in_ep_maxlen
Read datas:
01 02 03 04 05 06 07 08

#测试Source/Sink功能
$ sudo ./zero_app -s 3                   # 选择source/sink的配置         
book@100ask:~/nfs_rootfs/05_libusb_zero$ sudo ./zero_app -r  # 读数据
current config: 3
in_ep = 0x81, out_ep = 0x1
Read datas:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

sudo ./zero_app -w 0 0 0  # 写数据, 只能写入0, 
                          # 写入其他值将会导致开发板上的驱动认为是错误然后halt out端点
                          # 然后只能重新执行 ”sudo ./zero_app -s 3“ 才能恢复
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-06-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式Linux系统开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.1 编程思路
  • 1.2 zero 设备的描述符
  • 1.3 编程
  • 2. 上机实验
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档