本文共 6202 字,大约阅读时间需要 20 分钟。
gadget zero驱动可以用于usb通道测试, 也可以用于gadget 驱动参考。
他的功能如下:
1. 他是个双配置的usb设备
配置1: 有一个接口, 一个altsetting(即没有备选设置)
接口内有两个bulk端点, 分别对应in/out
配置2: 一个接口, 两个altsetting,即有两种设置
设置1: Bulk In + Bulk Out
设置2: Isoch In + Isoch Out
2. 如果内核参数 loopdefaut = 1, 实现loopback回环功能。
即out端点数据发送给in端点。
也即主机端通过OUT端点发送的数据又可以通过IN端点获取。
3. 如何编写gadget zero驱动, 以前直接注册usb_gadget_driver并实现相关回调函数,
利用gadget api实现descriptor, 控制端点功能及Bulk, Isoch端点功能即可。
最新Linux内核中更改了结构, 内部需要使用f_sourcesink及f_loopback模块。
故后续再去写gadget zero驱动...
4. 这里主要针对主机端如何进行usb gadget zero回环测试进行说明。
windows 自带驱动无法识别gadget zero驱动, 网上也未找到。
Linux主机自带usbtest.ko驱动(drivers/usb/misc/usbtest.c)
用户态有对应测试程序, 请参考
由于g_zero是个lagency驱动, 最新版本尝试几次遇到一些问题, 就不去使用usbtest.ko了。
直接基于usb_skeleton.ko和libusb实现两套可运行的程序。
* 基于usb_skeleton.ko, 很简单, 更改下id_table使加载即可....
#define USB_SKEL_VENDOR_ID› 0x0525 #define USB_SKEL_PRODUCT_ID› 0xa4a0拔出usb线,insmod usb_skeleton.ko插入usb线,ok... 主机端就会优先加载usb_skeleton驱动, 而不去使用usbtest驱动了。文件系统产生/dev/skel0节点echo 123456 > /dev/skel0cat /dev/skel0(如果设备端配置了loopdefaut=1, 就能看到loopback的数据)123456
* 基于libusb自己写个用户态程序, 如下:
#include#include #include #define TIMEOUT 2000#define STR_LEN 20int main(int argc, char *argv[]) { int ret = 0; libusb_device_handle *handle; libusb_device **list; libusb_device *usbdev; struct libusb_device_descriptor dev_desc; struct libusb_config_descriptor *config_desc; const struct libusb_endpoint_descriptor *ep_desc; unsigned char ep_bulkin = 0 , ep_bulkout = 0; int i = 0, is_match = 0; int ep_cnt; char send_data[STR_LEN] = {0,}; char recv_data[STR_LEN] = {0,}; int transfered = 0; int count = 0; ret = libusb_init(NULL); if (ret < 0) { fprintf(stderr, "libusb_init failed, ret(%d)\n", ret); return -1; } // get usb device list ret = libusb_get_device_list(NULL, &list); if (ret < 0) { fprintf(stderr, "libusb_get_device_list failed," "ret(%d)\n", ret); goto get_failed; } /* print/check the matched device */ while ((usbdev = list[i++]) != NULL) { libusb_get_device_descriptor(usbdev, &dev_desc);#if 0 printf("usb-%d: pid(0x%x), vid(0x%x)\n", i++, dev_desc.idVendor, dev_desc.idProduct);#endif if (dev_desc.idVendor == 0x0525 && dev_desc.idProduct == 0xa4a0) { printf("match, break!\n"); is_match = 1; break; } } if (!is_match) { fprintf(stderr, "no matched usb device...\n"); goto match_fail; } /* open usb device */ ret = libusb_open(usbdev, &handle); if (ret < 0) { fprintf(stderr, "libusb_open failed. ret(%d)\n", ret); goto open_failed; } printf("this usb device has %d configs, " "but still use default config 0\n", dev_desc.bNumConfigurations); /* get config descriptor */ ret = libusb_get_config_descriptor(usbdev, 1, &config_desc); if (ret < 0) { fprintf(stderr, "get config descriptor failed...\n"); goto configdesc_fail; } printf("the config has %d interface..." "just use the 1st interface this time\n", config_desc->bNumInterfaces); ep_cnt = config_desc->interface->altsetting[0].bNumEndpoints; printf("this interface has %d endpoint\n", ep_cnt); /* get bulk in/out ep */ for (i=0; i interface->altsetting[0].endpoint[i]; if ((ep_desc->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) & LIBUSB_TRANSFER_TYPE_BULK) { if ((ep_desc->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) & LIBUSB_ENDPOINT_IN) { if (!ep_bulkin) { ep_bulkin = ep_desc->bEndpointAddress; } } else { if (!ep_bulkout) { ep_bulkout = ep_desc->bEndpointAddress; } } printf("ep_addr = 0x%x\n", ep_desc->bEndpointAddress); } } if (ep_bulkin) { printf("yes, got 1 bulk in endppint!\n"); } if (ep_bulkout) { printf("yes, got 1 bulk out endppint!\n"); } /* claim the interface */ printf("claim usb interface\n"); ret = libusb_claim_interface(handle, 0); if (ret < 0) { printf("claim usb interface failed... ret(%d)\n", ret); goto claim_failed; } printf("start bulk transfer...\n"); /* use bulk trandfer data directly */ while (count++ < 20) { snprintf(send_data, STR_LEN, "hello,world - %d", count); ret = libusb_bulk_transfer(handle, ep_bulkout, (unsigned char*)send_data, sizeof(send_data), &transfered, TIMEOUT); if (ret == 0) { ret = libusb_bulk_transfer(handle, ep_bulkin, (unsigned char *)recv_data, sizeof(recv_data), &transfered, TIMEOUT); if (ret == 0) { printf("recv: %s\n", recv_data); } else { printf("bulk in failed, ret(%d), transfered(%d)\n", ret, transfered); } } else { printf("bulk out failed, ret(%d), transfered(%d)\n", ret, transfered); } } // libusb_open(dev, &handle); libusb_close(handle); libusb_free_device_list(list, 1); libusb_exit(NULL); return 0;configdesc_fail:claim_failed: libusb_close(handle);open_failed:match_fail: libusb_free_device_list(list, 1);get_failed: libusb_exit(NULL); return ret;}
CC=gccCFLAGS=-g -WallCFLAGS+=`pkg-config --cflags libusb-1.0`LDFLAGS=`pkg-config --libs libusb-1.0`target=usbtestobjs=$(patsubst %.c, %.o, $(wildcard *.c))all:$(target)$(target):$(objs) $(CC) $^ -o $@ $(LDFLAGS).c.o: $(CC) -c $< $(CFLAGS).PHONY: cleanclean: rm *.o $(target) -rf
ok... 程序相对简单, 记录下libusb的函数调用
- libusb_init
- libusb_get_device_list // 获取设备列表, 返回设备数组
- libusb_get_device_descriptor // 获取设备描述符, 该结构体下面能找到配置, 接口, 端点, 字符串等描述符
- libusb_open // 打开设备
- 查看描述信息, 选择自己要用的配置项, libusb_set_configuration
- libusb_claim_interface // claim(认领, 索取)一个接口, 这个函数是必须调用的, 否则会有一些warning
- libusb_bulk_transfer // bulk传输, 其他控制, 登时有对应接口
- ****释放各种资源的函数, 暂不列举了....
ok, 就写这么多, 后面尽快写个gadget 驱动程序... 以加深gadget api的理解。
转载地址:http://licrj.baihongyu.com/