读书人

自娱自乐四之Linux gadget驱动3(bind

发布时间: 2013-09-05 16:02:07 作者: rapoo

自娱自乐4之Linux gadget驱动3(bind过程)

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有用。

读书人网 >UNIXLINUX

热点推荐