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)
用户态有对应测试程序, 请参考 http://www.linux-usb.org/usbtest/
由于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/skel0
cat /dev/skel0
(如果设备端配置了loopdefaut=1, 就能看到loopback的数据)
123456
* 基于libusb自己写个用户态程序, 如下:
#include <stdio.h>
#include <string.h>
#include <libusb.h>
#define TIMEOUT 2000
#define STR_LEN 20
int 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<ep_cnt; i++) {
ep_desc = &config_desc->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=gcc
CFLAGS=-g -Wall
CFLAGS+=`pkg-config --cflags libusb-1.0`
LDFLAGS=`pkg-config --libs libusb-1.0`
target=usbtest
objs=$(patsubst %.c, %.o, $(wildcard *.c))
all:$(target)
$(target):$(objs)
$(CC) $^ -o $@ $(LDFLAGS)
.c.o:
$(CC) -c $< $(CFLAGS)
.PHONY:
clean
clean:
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相关。