前一段时间弄了2个礼拜的OTG驱动调试,感觉精神疲惫啊。主要原因还是自己对OTG功能不了解造成的。现在终于完成但是对实质原理还有些模糊。所以自己重新总结一下。因为自己是菜鸟,所以用菜鸟的白话方式分析。高手滤过吧。 所谓OTG功能就是具备该功能的设备即可当主设备(host)去轮询别人,也可以当从设备(device)去被别人轮~~(双性人?)。正所谓所有的产品和功能都是因为需求存在的,举个最简单的需求,原来MP3想传送一个歌曲都得通过电脑。现在只要两个MP3链接,其中一个MP3有OTG功能作为主设备(相当于电脑主机),然后另外一个是从设备就可以实现数据的传送了。 那么话说回来,具有OTG功能的设备如何确定自己是主还是从设备那。原来原来USB接口上有4个管脚,OTG功能有5个。原来4个分别是电 D+ D- 地。 现在增加了一个ID。这个ID线就决定了自己做主设备还是从设备。如果ID线是高则自己是从设备,反之是主设备。
下面开始分析代码。
定义平台设备私有数据,以后驱动要使用 static inline void dr_register_otg(void) { dr_otg_device.dev.platform_data = PDATA; //该设备的私有数据赋值,就是上面定义的dr_utmi_config if (platform_device_register(&dr_otg_device)) |
上面几个过程主要是完成了设备的注册。这个过程是:
1.定义platform_device结构。
2.定义platform_device下的struct resource设备资源结构
3.定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
4.调用platform_device_register将platform_device结构
注册上面4个过程调用结束后,设备的信息就被注册到系统中,等待驱动的使用
下面分析驱动和设备的链接过程
定义platform_driver结构
|
调用fsl_otg_probe函数,函数参数platform_device *pdev,就是我们上面注册进系统的platform_device结构,现在由系统赋值调用fsl_otg_probe
static int __init fsl_otg_probe(struct platform_device *pdev)
|
上面函数中调用了fsl_otg_conf,我们来看看他干了什么。
static int fsl_otg_conf(struct platform_device *pdev)
/* allocate space to fsl otg device */ INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event); INIT_LIST_HEAD(&active_timers); /* Set OTG state machine operations */
/* initialize the otg structure */ fsl_otg_dev = fsl_otg_tc; /* Store the otg transceiver */
return 0; |
int usb_otg_start(struct platform_device *pdev)
/* Initialize the state machine structure with default values */ /* We don't require predefined MEM/IRQ resource index */
/* We don't request_mem_region here to enable resource sharing
/* request irq */
if (pdata->platform_init && pdata->platform_init(pdev) != 0) /* Export DR controller resources */
/* reset the controller */ /* wait reset completed */ /* configure the VBUSHS as IDLE(both host and device) */ /* configure PHY interface */ if (pdata->have_sysif_regs) { /* disable all interrupt and clear all OTGSC status */ /* DBG("initial ID pin=%d\n", p_otg->fsm.id); /* enable OTG ID pin interrupt */ return 0; |
下面分析下 中断例程函数
该函数就是判断ID的高低,也就是自己做主设备还是从设备
/* Only clear otg interrupts */ /*FIXME: ID change not generate when init to 0 */ /* process OTG interrupts */ if (otg->host) if (fsm->id) { /* switch to gadget *///从设备
return IRQ_HANDLED; return IRQ_NONE; |
int fsl_otg_start_host(struct otg_fsm *fsm, int on)
/* Update a_vbus_vld state as a_vbus_vld int is disabled otg_dev->host_working = 1;
|
int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
|
上面部分就是 OTG功能的 OTG驱动部分。 OTG功能还要有做主设备使用的主设备驱动和做从设备的从设备驱动。
从上面代码分析我们归纳出流程:
分两个大部分:
一 设备的注册 其中包括
1.定义platform_device结构。
2.定义platform_device下的struct resource设备资源结构
3.定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
4.调用platform_device_register将platform_device结构
二 OTG驱动的注册 其中包括
1.struct platform_driver fsl_otg_driver 结构的注册
2.匹配到有设备存在时调用的PORE函数,对设备进行初始化设置和功能函数的绑定
3.完成中断函数的绑定和中断例程的注册。
经过上面的处理后,只要OTG ID的变化就会触发中断,调用中断例程函数,决定是调用主设备还是从设备驱动。 而主设备和从设备驱动和OTG调用的链接是分别在主从设备驱动中完成的。后面我们介绍主从设备驱动中会介绍到。
在文章的最后想起来这次调OTG遇见的问题,分享给大家希望大家有帮助。我调试OTG时,开始将OTG编译到内核中。(Y)。结果插入U盘没有反应。后来发现原来我加入内核后,主设备驱动的先OTG设备驱动被执行,造成主设备函数和OTG功能的链接出现问题。(应该是OTG先初始化 然后从和主设备驱动链接。)后来我使用模块方式编译OTG功能。按照先载入OTG后载入从和主设备。(insmod方式),结果OTG就可以使用了。 后来通过降低主设备的优先级方式,把OTG编译进内核,然后因为主设备优先级低所以最后被调用。 也就是在主设备注册那使用
late_initcall(ehci_hcd_init);代替//module_init(ehci_hcd_init);。这样主设备的优先级就低于设备驱动的优先级就在驱动加载完加载了。 但是总感觉这样不是很合理的方式,如果有朋友有更好的办法请指教。