Gadget应用实例之serial

Gadget应用实例之serial


文章目录

  • Gadget应用实例之serial
  • 参考资料:
    • 一、编写程序
      • 1.1 编程思路
      • 1.2 zero设备的描述符
      • 1.3 编程
    • 二、 上机实验
  • 致谢



参考资料:

  • https://blog.csdn.net/embededswordman/article/details/6689593
  • Linux文档:Documentation\usb\gadget_serial.txt

一、编写程序

1.1 编程思路

涉及的程序如下图所示:
Gadget应用实例之serial_第1张图片
基于libusb编写程序:

  • 找到设备
  • 选择配置:loopback、sourcesink
  • 得到端点:找到interface进而得到endpoint
  • 读写数据:操作endpoint

1.2 zero设备的描述符

在Ubuntu里执行如下命令:

$ lsusb -v -d 0525:a4a0

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

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
zero_app.c

#include 
#include 
#include 
#include 
#include 

#include 

#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  : select config\n", name);
    printf("%s -wstr  : write string\n", name);
    printf("%s -rstr : read string\n", name);
    printf("%s -w  : 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;
}

二、 上机实验

实验步骤:

  • 先安装g_zero驱动程序:在开发板上执行modprobe g_zero

  • 然后连接OTG线到PC

  • 在Ubuntu中识别出设备

  • 执行测试程序

    • 先编译:在Ubuntu里执行如下命令

      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里执行如下命令

      $ 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的配置         
      [email protected]:/work/nfs_rootfs/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“ 才能恢复
      


致谢

以上笔记源自韦东山老师的视频课程,感谢韦老师,韦老师是嵌入式培训界一股清流,为嵌入式linux开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!

在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬!



你可能感兴趣的:(USB那些事儿,linux,arm开发,驱动开发,架构,嵌入式硬件)