您的位置:首页 > 运维架构 > 网站架构

USB Gadget设备驱动开发(二) USB Gadget UVC 软件流程分析

2017-03-22 22:58 1431 查看
一、驱动模块注册方法:module_platform_driver_probe

module_platform_driver_probe(ambarella_udc_driver,ambarella_udc_probe);

#definemodule_platform_driver_probe(__platform_driver, __platform_probe) \

static int __init__platform_driver##_init(void) \                       

{ \

         returnplatform_driver_probe(&(__platform_driver), \

                                          __platform_probe);    \

} \

module_init(__platform_driver##_init);\                
                  

static void __exit__platform_driver##_exit(void) \                   

{ \

         platform_driver_unregister(&(__platform_driver));\

} \

module_exit(__platform_driver##_exit);                                    

 

变形为(驱动一般写法)

static int __init ambarella_udc_driver_init(void)                       

{

         return platform_driver_probe(&(ambarella_udc_driver),

                                          ambarella_udc_probe);   

}

module_init(ambarella_udc_driver_init);                        

 

static void __exit ambarella_udc_driver _exit(void)                   

{

         platform_driver_unregister(&(ambarella_udc_driver));

}

module_exit(ambarella_udc_driver _exit);

 

二、继续看platform_driver_probe(&(ambarella_udc_driver),ambarella_udc_probe);

 

int __init_or_moduleplatform_driver_probe(struct platform_driver *drv,

                   int(*probe)(struct platform_device *))

{

         intretval, code;

 

         /*make sure driver won't have bind/unbind attributes */

         drv->driver.suppress_bind_attrs= true;

 

         /*temporary section violation during probe() */

         drv->probe = probe;

         retval= code = platform_driver_register(drv);

 

         /*

          * Fixup that section violation, being paranoidabout code scanning

          * the list of drivers in order to probe newdevices.  Check to see

          * if the probe was successful, and make sureany forced probes of

          * new devices fail.

          */

         spin_lock(&drv->driver.bus->p->klist_drivers.k_lock);

         drv->probe= NULL;

         if(code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))

                   retval= -ENODEV;

         drv->driver.probe= platform_drv_probe_fail;

         spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock);

 

         if(code != retval)

                   platform_driver_unregister(drv);

         returnretval;

}

这里将drv->probe = probe;然后调用platform_driver_register(drv);也就是

ambarella_udc_driver.probe= ambarella_udc_probe;

 

三、probe函数的调用:当platform驱动和设备匹配时调用

 

流程:

                                                        à  
 drv->driver.bus =&platform_bus_type;(match函数在里面)//注意

platform_driver_register(drv);  à driver_register(&drv->driver);  à  bus_add_driver(drv);  àdriver_attach(drv);   à    bus_for_each_dev(drv->bus,NULL, drv, __driver_attach);  à__driver_attach       àdriver_match_device(在这里判断驱动和设备匹配)

 

 

四、__driver_attach函数

1 调用driver_match_device,查找platform总线上和驱动匹配的设备

static inline int driver_match_device(structdevice_driver *drv,

                                           struct device *dev)

{

         returndrv->bus->match ?drv->bus->match(dev, drv) : 1; //match函数在(三、)里有介绍,后面会讲

}

2  找到后调用driver_probe_device(drv, dev);

流程:

 

driver_probe_device(drv, dev); à  really_probe(dev, drv)  à  drv->probe(dev);

也就是ambarella_udc_probe函数

 

五、match函数

platform_driver_register(drv);                à    drv->driver.bus =&platform_bus_type;

 

match函数就是platform_match

struct bus_type platform_bus_type = {

         .name                  = "platform",

         .dev_attrs  = platform_dev_attrs,

         .match                 =platform_match,

         .uevent                = platform_uevent,

         .pm            = &platform_dev_pm_ops,

};

 

 

六、一直在讲驱动和设备匹配,然后调用probe函数,设备在哪?

 

static const struct of_device_idambarella_udc_dt_ids[] = {

         {.compatible = "ambarella,udc" },

         {/* sentinel */ }

};

 

MODULE_DEVICE_TABLE(of,ambarella_udc_dt_ids);

和一般设备驱动通过name字段来匹配不同,此处驱动程序要匹配的实际是of_device_id的.compatible字段,而platform_match在此处调用的的of_driver_match_device(dev, drv)。

注of: open firmware,

现在内核采用了设备树来传递板级的信息,如果设备树中也存在.compatible字段相同的节点,就加载驱动,设备树源文件位置在Z:\y16619\UPDATE\AC_repo\RSM_SDK_005\ambalink_sdk_3_10\linux\arch\arm\boot\dts目录下的ambarella-a12.dtsi文件,可以看到

                   udc@e0006000{

                            compatible= "ambarella,udc";

                            reg= <0xe0006000 0x2000 0xec1702cc 0x4>;

                            interrupts= <4 0x4>;

                            amb,usbphy= <&usbphy>;

                  };

 

设备树引进:由于过去的ARM LINUX中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resourse、i2c_board_info、spi_board_info以及各种硬件的platform_data,于是社区参考了PowerPC等其他体系架构下已经使用的Flattedned Device Tree(FDT), Device Tree 是一种描述硬件的数据结构,它起源于OF(OpenFirmware),采用Device
Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。

 

platform_match在此处调用的的of_driver_match_device(dev, drv)函数,流程如下

of_driver_match_device(dev, drv) à  of_match_device(drv->of_match_table, dev)  à __of_match_node  à strcmp(matches->name, node->name);  在此处才真正进行匹配

 

 

七、USB设备枚举的过程(参考抓包数据)

 

1 设备插入主机,主机检测到设备,复位设备(USB_DEV_RESET)

2 主机向设备控制端点发送Get_Descriptor来了解设备默认管道的大小

3 主机指定一个地址,发送Set_Address标准请求设置设备的地址

4 主机使用新的地址,再次发送Get_Descriptor获得各种描述符

4 主机加载一个USB设备驱动

6 USB设备驱动再发送Set_Confuration表示设备请求配置设备

 

设备中断:读USB_dev_intRegister
ambarella_udc_irq –> udc_device_interrupt(udc,value)-> ret = udc->driver->setup(&udc->gadget, &crq) –>value= usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);–> composite_setup_complete(gadget->ep0,req); –>ep->ops->queue(ep,req, gfp_flags);

 
端点中断:读USB_end_intRegister
ambarella_udc_irq –>udc_epout_interrupt(udc, i); –>ambarella_handle_request_packet(udc); –>ret= udc->driver->setup(&udc->gadget, crq); –>value =usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);–> composite_setup_complete(gadget->ep0,req);
–>ep->ops->queue(ep,req, gfp_flags);

 

ambarella_udc_irq –> udc_epin_interrupt(udc, i); –>ambarella_udc_done(ep,req, 0);–> req->memcpy(req->req.buf, req->buf_aux,req->req.actual);-->

req.complete(&ep->ep, &req->req); –>ep->ops->queue(ep,req, gfp_flags);

 

 

八、描述符的绑定

webcam_bind() àusb_add_config(cdev,&webcam_config_driver,webcam_config_bind)) àwebcam_config_bind()àuvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls,

                   uvc_fs_streaming_cls,uvc_hs_streaming_cls,

                   uvc_ss_streaming_cls);

 

八、UVC功能的绑定

         Uvc功能函数:

uvc->func.bind = uvc_function_bind;

         uvc->func.unbind= uvc_function_unbind;

         uvc->func.get_alt= uvc_function_get_alt;

         uvc->func.set_alt= uvc_function_set_alt;

         uvc->func.disable= uvc_function_disable;

         uvc->func.setup= uvc_function_setup;

uvc->func.strings= uvc_function_strings;

 

Uvc功能函数绑定

uvc_bind_config-à ret = usb_add_function(c,&uvc->func); 进行uvc功能的绑定

usb_add_function调用function->bind(config,function);即uvc_function_bind,此函数会对uvc进行一系列的初始化工作,并调用uvc_video_init(&uvc->video);初始化uvc video stream,然后调用ret = uvc_register_video(uvc);来注册一个V4l2设备。

 

 

九、V4l2操作接口的注册

uvc_register_video(uvc);会注册v4l2设备操作接口,主要用来处理视频流,video->fops = &uvc_v4l2_fops;

 

static struct v4l2_file_operationsuvc_v4l2_fops = {

         .owner                 = THIS_MODULE,

         .open                   =uvc_v4l2_open,

         .release      = uvc_v4l2_release,

         .ioctl           =uvc_v4l2_ioctl,

         .mmap                = uvc_v4l2_mmap,

         .poll            = uvc_v4l2_poll,

#ifndef CONFIG_MMU

         .get_unmapped_area= uvc_v4l2_get_unmapped_area,

#endif

};

 

我们写app 主要就操作这些接口,在处理v4l2 cmd方法可参考uvc_v4l2_ioctl,

static long

uvc_v4l2_ioctl(struct file *file, unsignedint cmd, unsigned long arg)

{

         returnvideo_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);

}

 

我们可通过

uvc_v4l2_do_ioctl(struct file *file,unsigned int cmd, void *arg) 的cmd来实现视频传输,如VIDIOC_QBUF、 VIDIOC_DQBUF命令等。

 

至此,主要流程分析完毕,大家可做进一步更细致分析

 

十、驱动编译及App编写
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息