自娱自乐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有用。

你可能感兴趣的:(自娱自乐4之Linux gadget驱动3(bind过程))