一、驱动模块注册方法: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编写