USB Gadget总结
USB Gadget 流程
USB Gadget设备一般是通过SHELL脚本通过configfs文件系统来创建的。其大致分为三部:
- 第一步:创建function目录下的USB设备模板,例如
mkdir functions/Loopback.0
,就会调用其对应的loopback_alloc_instance。 - 第二步:将创建的实例与配置目录下进行“链接”,例如:
ln -s functions/Loopback.0 configs/c.1
,则会调用loopback_alloc,即以loopback_alloc_instance的实例usb_function_instance创建usb_function。 - 第三步:就是启用UDC.这时会硬件触发设备的枚举。会执行 function.bind,function.set_alt函数。
以上的操作详细可见https://www.usbzh.com/article/detail-1579.html
function.bind
在function.bind函数为设备枚举前的数据准备,即根据提供的USB设备描述符信息:
- 分配相应的接口描述符id,函数 usb_interface_id
- 从硬件中选择相应的端点,并修改端点描述符地址,函数usb_ep_autoconfig
ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); if (!ep) goto fail; audio->out_ep = ep; audio->out_ep->desc = &as_out_ep_desc;
这是我们在gadget function中提交的软件信息与硬件信息绑定适配的过程,即UDC控制器硬件根据软件提交的接口和端点信息,分配相应的硬件信息。
当然,做了这么多的分配信息,其主要还是生成配置描述符,这里根据linux版本和UDC控制器的情况,分有USB3.0超高速配置描述符,USB2.0高速配置描述符、全速配置描述符。
所以function.bind的功能就是硬件根据软件提交的需求信息,确定配置描述符的过程。
关于usb_interface_id中设置是最大USB_MAXINTERFACES,最小从0开始
#define USB_MAXINTERFACES 32
表明这个数值是一个人为设定的上限,并非USB协议本身的强制规定。之所以这样设定,是因为在实际应用中,极少有USB设备的一个配置会包含超过32个接口,这个值被认定为足以满足几乎所有情况。
usb_assign_descriptors实质是对指定的描述符进行复制备份
int usb_assign_descriptors(struct usb_function *f,
struct usb_descriptor_header **fs,
struct usb_descriptor_header **hs,
struct usb_descriptor_header **ss,
struct usb_descriptor_header **ssp)
{
struct usb_gadget *g = f->config->cdev->gadget;
if (fs) {
f->fs_descriptors = usb_copy_descriptors(fs);
if (!f->fs_descriptors)
goto err;
}
if (hs && gadget_is_dualspeed(g)) {
f->hs_descriptors = usb_copy_descriptors(hs);
if (!f->hs_descriptors)
goto err;
}
if (ss && gadget_is_superspeed(g)) {
f->ss_descriptors = usb_copy_descriptors(ss);
if (!f->ss_descriptors)
goto err;
}
if (ssp && gadget_is_superspeed_plus(g)) {
f->ssp_descriptors = usb_copy_descriptors(ssp);
if (!f->ssp_descriptors)
goto err;
}
return 0;
err:
usb_free_all_descriptors(f);
return -ENOMEM;
}
function.set_alt
set_alt对应的USB标准请求应是SET_INTERFACE,SET_INTERFACE应启用其接口描述符下的各个端点。所以这里主要涉及的是对端点进行数据传输前的配置和启用,当然,对于OUT端点,需要准备UDC端的接收队列。
- config_ep_by_speed:对usb_ep_autoconfig选择出的端点的maxpacket等参数进行配置。
2.usb_ep_enable 使能端点.这里在看相关的参考代码时发现很奇怪,详见https://www.usbzh.com/article/detail-1587.html 一文中的疑惑。
static inline int usb_ep_enable(struct usb_ep *ep)
{
return ep->ops->enable(ep, ep->desc);
}
关于使能与禁用端点,我们后面再议。
3.分端点分配请求。对于IN端点,按需分配,对于OUT端点,提前分配好并加入到其端点队列中,这样就可以随时接收数据了。
struct usb_request *req = usb_ep_alloc_request(out_ep, GFP_ATOMIC);
if (req) {
req->buf = kzalloc(req_buf_size,GFP_ATOMIC);
if (req->buf) {
req->length = req_buf_size;
req->context = audio;
req->complete = f_audio_complete;
err = usb_ep_queue(out_ep, req, GFP_ATOMIC);
}
}
`
结构体声名为:
struct usb_request {
void *buf;//数据指针
unsigned length;//数据指针,buf长度
dma_addr_t dma;
struct scatterlist *sg;
unsigned num_sgs;
unsigned num_mapped_sgs;
unsigned stream_id:16;
unsigned no_interrupt:1;
unsigned zero:1;
unsigned short_not_ok:1;
void (*complete)(struct usb_ep *ep,struct usb_request *req);//完成例程
void *context; //端点自定义上下文
struct list_head list;
int status;
unsigned actual; //实际发送或者接收长度
};
当一个请求完成时,会返回其状态。
static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_audio *audio = req->context;
int status = req->status;
u32 data = 0;
struct usb_ep *out_ep = audio->out_ep;
switch (status) {
case 0: /* normal completion? */
if (ep == out_ep)
f_audio_out_ep_complete(ep, req);
else if (audio->set_con) {
memcpy(&data, req->buf, req->length);
audio->set_con->set(audio->set_con, audio->set_cmd, le16_to_cpu(data));
audio->set_con = NULL;
}
break;
default:
break;
}
}
本文链接为:http://www.usbzh.com/article/detail-1589.html ,欢迎转载,转载请附上本文链接。