bind和字符串描述符一样也是有多级关系,首先说一下usb很基础的关系
一个设备可能有多个配置
一个配置可能有多个接口
一个接口可能有多个端点或设置
还记得我在自娱自乐2中说的的结构体吧
我们先变通一下
struct usb_function 相当于接口
struct usb_configuration 相当于配置
struct usb_composite_dev 相当于设备
bind会建立他们的关系,当然bind不止是这层涵义
我们在看它们的bind之前先看看
int usb_composite_probe(struct usb_composite_driver *driver,
int(*bind)(struct usb_composite_dev *cdev))
这玩意也有个绑定
在zero中usb_composite_probe(&gadget_transfer_driver,gadget_transfer_bind);
gadget_transfer_bind()这个函数我在之前已经列出了它的一部分,只是没明说
主要就是分配厂商设备id,获取设备bcd码,生成厂商字符串。定时器初始化话。
还有重要一点就是调用sourcesink_add(loop我们不看)
int usb_composite_probe(structusb_composite_driver *driver,
int(*bind)(struct usb_composite_dev *cdev))
{
//一些判断赋值省去
composite = driver;
composite_gadget_bind = bind;//请记住zero提供的bind函数在此只是赋值
return usb_gadget_probe_driver(&composite_driver, composite_bind);
//composite_driver前面的文章已说过了,这个usb_gadget_probe_drvier不贴全代码了,看一点
/*
//这个udc是从udc_list中找到的
if (udc_is_newstyle(udc)) {//自娱自乐1有说udc_is_newstyle()
ret =bind(udc->gadget);//这个bind是composite_bind,不要搞错
if (ret)
goto err1;
//…
} else {
ret =usb_gadget_start(udc->gadget, driver, bind);//gadget->ops->start(driver, bind);不懂就看我之前写的udc模板
//…
}
不管怎么样都要调用composite_bind()函数,下面看到
*/
}
static int composite_bind(struct usb_gadget *gadget)//记住这个gadget是udc中的
{
structusb_composite_dev *cdev;
int status = -ENOMEM;
cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
if(!cdev)
returnstatus;
spin_lock_init(&cdev->lock);//初始化lock
cdev->gadget = gadget;//保存对应udc中的gadget
set_gadget_data(gadget, cdev);//相当于gadget->dev->driver_data= cdev;
//上面两句就是把gadget于udc驱动联系起来
INIT_LIST_HEAD(&cdev->configs);//初始化配置链表头
cdev->req =usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);//会调用udc中对应方法
if(!cdev->req)
gotofail;
cdev->req->buf =kmalloc(USB_BUFSIZ, GFP_KERNEL);
if(!cdev->req->buf)
gotofail;
cdev->req->complete =composite_setup_complete;//这个是请求完成函数,暂且不管
gadget->ep0->driver_data =cdev;
cdev->bufsiz = USB_BUFSIZ;
cdev->driver = composite;//还记得他是谁吧,就是zero中的gadget_transfer_driver
if(CONFIG_USB_GADGET_VBUS_DRAW/*内核配置*/ <= USB_SELF_POWER_VBUS_MAX_DRAW/*100*/)//电力不足
usb_gadget_set_selfpowered(gadget);//设为自供电
usb_ep_autoconfig_reset(cdev->gadget);
/*
void usb_ep_autoconfig_reset (structusb_gadget *gadget)
{
struct usb_ep *ep;
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
ep->driver_data = NULL;
}
#ifdef MANY_ENDPOINTS
in_epnum = 0;
#endif
epnum = 0;
}
没什么要解释吧
*/
status =composite_gadget_bind(cdev);//还记的它把,就是zero中的bind函数gadget_transfer_bind,这里面当然会调用sourcesink_add和loopback_add,我们下面看
if(status < 0)
gotofail;
cdev->desc =*composite->dev;//zero中的device_desc
//这里有一段就是我们之前说的判断装载是是否定义字符串的代码,
//当然还有厂商、产品id等。直接略过
//如果看过我之前写的设备模型,应该对device_create_file不陌生吧
status =device_create_file(&gadget->dev, &dev_attr_suspended);
if(status)
gotofail;
return0;
fail:
composite_unbind(gadget);
returnstatus;
}
//上面就相当于设备的绑定,设备过后当然就是配置
if (loopdefault) {
loopback_add(cdev, autoresume !=0);
sourcesink_add(cdev, autoresume!= 0);
} else {
sourcesink_add(cdev, autoresume!= 0);
loopback_add(cdev, autoresume!= 0);
}
不管loopdefault是什么,我们可以认为zero有两个配置
我只用f_sourcesink.c
sourcesink_add主要就是
usb_add_config(cdev,&sourcesink_driver, sourcesink_bind_config);
//为了节约纸张,我去掉了一下注释和DBG()
int usb_add_config(struct usb_composite_dev*cdev,
structusb_configuration *config,
int(*bind)(struct usb_configuration *))
{
int status = -EINVAL;
structusb_configuration *c;
if(!config->bConfigurationValue || !bind)
gotodone;
list_for_each_entry(c,&cdev->configs, list) {//这段很好理解就是判断是否已经注册过了
if(c->bConfigurationValue == config->bConfigurationValue) {
status = -EBUSY;
goto done;
}
}
config->cdev = cdev;
list_add_tail(&config->list,&cdev->configs);//加入设备的配置链表
INIT_LIST_HEAD(&config->functions);//初始化配置中的接口链表头
config->next_interface_id =0;//接口id初始化为0
status = bind(config);// 就是sourcesink_bind_config,这个就是把接口加入配置,下面看
if(status < 0) {
list_del(&config->list);
config->cdev = NULL;
} else{
unsigned i;
for(i = 0; i < MAX_CONFIG_INTERFACES; i++) {
struct usb_function *f =config->interface[i];
if (!f)
continue;
}
}
usb_ep_autoconfig_reset(cdev->gadget);//上面说过,不过上面的gadget是udc中的,这个是zero驱动中的
done:
returnstatus;
}
//到这我们可以说配置绑完了,下面看接口
static int __initsourcesink_bind_config(struct usb_configuration*c)
{
structf_sourcesink *ss;
int status;
ss = kzalloc(sizeof *ss, GFP_KERNEL);
if(!ss)
return-ENOMEM;
ss->function.name = "source/sink";
ss->function.descriptors =fs_source_sink_descs;
ss->function.bind =sourcesink_bind;
ss->function.unbind =sourcesink_unbind;
ss->function.set_alt =sourcesink_set_alt;
ss->function.disable =sourcesink_disable;
status = usb_add_function(c,&ss->function);//下面会看到
if(status)
kfree(ss);
returnstatus;
}
sink只用一个接口,在这我们要改变一下思维,上面说struct usb_function 相当于接口,在这
我改为struct f_sourcesink相当于接口,struct usb_function相当于接口的操作功能集合
struct f_sourcesink {
structusb_function function;
structusb_ep *in_ep;
structusb_ep *out_ep;
};
//为了节约纸张,我去掉了一下注释和DBG()
int usb_add_function(struct usb_configuration*config,
structusb_function *function)
{
int value = -EINVAL;
if(!function->set_alt || !function->disable)//set_alt和disable必须实现
gotodone;
function->config = config;//记住父亲是谁
list_add_tail(&function->list,&config->functions);
if(function->bind) {//下面看到的sourcesink_bind
value =function->bind(config, function);
if(value < 0) {
list_del(&function->list);
function->config =NULL;
}
} else
value = 0;
//设置速度标志
if(!config->fullspeed && function->descriptors)
config->fullspeed = true;
if(!config->highspeed && function->hs_descriptors)
config->highspeed = true;
if(!config->superspeed && function->ss_descriptors)
config->superspeed = true;
done:
returnvalue;
}
//到这我们看到接口也绑到配置了,只剩下端点了
static int __init
sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev*cdev = c->cdev;
struct f_sourcesink *ss = func_to_ss(f);
int id;
id = usb_interface_id(c, f);//和之前说的分配id没什么两样
if (id < 0)
return id;
source_sink_intf.bInterfaceNumber = id;//接口id赋值
ss->in_ep =usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
/*
struct usb_ep *usb_ep_autoconfig(
struct usb_gadget *gadget,
structusb_endpoint_descriptor *desc
)
{
returnusb_ep_autoconfig_ss(gadget, desc, NULL);
}
struct usb_ep *usb_ep_autoconfig_ss(
struct usb_gadget *gadget,
structusb_endpoint_descriptor *desc,
structusb_ss_ep_comp_descriptor *ep_comp
)
{
//没全贴,重点就是下面这个,就是从gadget的ep列表中找到合适的ep,主要通过ep_matches()
//这个不细说了,还要说一下就是是通过desc的信息查找的
list_for_each_entry (ep,&gadget->ep_list, ep_list) {
if(ep_matches(gadget, ep, desc, ep_comp))
return ep;
}
}
*/
if (!ss->in_ep) {
autoconf_fail:
return -ENODEV;
}
ss->in_ep->driver_data= cdev; /* claim */
ss->out_ep =usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
//和上面一样的,我们可以认为这个就是把端点绑定到接口
if (!ss->out_ep)
goto autoconf_fail;
ss->out_ep->driver_data = cdev; /* claim */
//高速设置
if(gadget_is_dualspeed(c->cdev->gadget)) {
hs_source_desc.bEndpointAddress =
fs_source_desc.bEndpointAddress;
hs_sink_desc.bEndpointAddress =
fs_sink_desc.bEndpointAddress;
f->hs_descriptors= hs_source_sink_descs;
}
//超速设置
if(gadget_is_superspeed(c->cdev->gadget)) {
ss_source_desc.bEndpointAddress =
fs_source_desc.bEndpointAddress;
ss_sink_desc.bEndpointAddress =
fs_sink_desc.bEndpointAddress;
f->ss_descriptors= ss_source_sink_descs;
}
return 0;
}
该绑的都绑了,我们来总结一下吧
看上面的过程,bind首先把端点、接口、配置、设备绑在一起
还有最关键是把gadget和udc绑在一起,这个有两个地方体现,一个是composite_bind()调用传入了udc的gadget,还有就是sourcesink_bind()调用usb_ep_autoconfig()找端点。
事实上绑定还有一个涵义就是把gadget驱动和其他驱动联系到一起,例如串口、网卡等。我在后面也尽量模拟一下这个过程,当然是简单的驱动。
最后还要提一下
if(!function->set_alt || !function->disable)//set_alt和disable必须实现
gotodone;
至少告诉我们基于composite的gadget驱动,必须要实现它们,这个对后面写gadget有用。