USB OTG驱动分析.docx
《USB OTG驱动分析.docx》由会员分享,可在线阅读,更多相关《USB OTG驱动分析.docx(24页珍藏版)》请在冰豆网上搜索。
USBOTG驱动分析
前一段时间弄了2个礼拜的OTG驱动调试,感觉精神疲惫啊。
主要原因还是自己对OTG功能不了解造成的。
现在终于完成但是对实质原理还有些模糊。
所以自己重新总结一下。
因为自己是菜鸟,所以用菜鸟的白话方式分析。
高手滤过吧。
所谓OTG功能就是具备该功能的设备即可当主设备(host)去轮询别人,也可以当从设备(device)去被别人轮~~(双性人?
)。
正所谓所有的产品和功能都是因为需求存在的,举个最简单的需求,原来MP3想传送一个歌曲都得通过电脑。
现在只要两个MP3链接,其中一个MP3有OTG功能作为主设备(相当于电脑主机),然后另外一个是从设备就可以实现数据的传送了。
那么话说回来,具有OTG功能的设备如何确定自己是主还是从设备那。
原来原来USB接口上有4个管脚,OTG功能有5个。
原来4个分别是电D+D-地。
现在增加了一个ID。
这个ID线就决定了自己做主设备还是从设备。
如果ID线是高则自己是从设备,反之是主设备。
下面开始分析代码。
向平时一样定义platform_device资源等信息。
定义platform_device结构
staticstructplatform_device__maybe_unuseddr_otg_device=
{.name="fsl-usb2-otg",//设备的名称日后匹配用
.id=-1,//只有一个这样的设备
.dev={.release=dr_otg_release,
.dma_mask=&dr_otg_dmamask,
.coherent_dma_mask=0xffffffff,
},
.resource=otg_resources,//设备的资源看下面
.num_resources=ARRAY_SIZE(otg_resources),
};
定义platform_device下的structresource设备资源结构
staticstructresourceotg_resources[]={
[0]={
.start=(u32)(USB_OTGREGS_BASE),//描述设备实体在cpu总线上的线性起始物理地址
.end=(u32)(USB_OTGREGS_BASE+0x1ff),//描述设备实体在cpu总线上的线性结尾物理地址
.flags=IORESOURCE_MEM,},
[1]={
.start=MXC_INT_USB_OTG,//中断号
.flags=IORESOURCE_IRQ,},
};
定义平台设备私有数据,以后驱动要使用
staticstructfsl_usb2_platform_data__maybe_unuseddr_utmi_config={
.name="DR",
.platform_init=usbotg_init,
.platform_uninit=usbotg_uninit,
.phy_mode=FSL_USB2_PHY_UTMI_WIDE,
.power_budget=500,/*viaRT9706*/
.gpio_usb_active=gpio_usbotg_utmi_active,
.gpio_usb_inactive=gpio_usbotg_utmi_inactive,
.transceiver="utmi",
.wake_up_enable=_wake_up_enable,
};
#definePDATA(&dr_utmi_config)定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
staticinlinevoiddr_register_otg(void){
PDATA->operating_mode=FSL_USB2_DR_OTG;//将模式更改(上面定义的时候定义的是FSL_USB2_PHY_UTMI_WIDE,不知道为什么开始不定义这个,可能是为了兼容)
dr_otg_device.dev.platform_data=PDATA;//该设备的私有数据赋值,就是上面定义的dr_utmi_config
if(platform_device_register(&dr_otg_device))
printk(KERN_ERR"usb:
can'tregisterotgdevice/n");
else
printk(KERN_INFO"usb:
DROTGregistered/n");
}
上面几个过程主要是完成了设备的注册。
这个过程是:
1.定义platform_device结构。
2.定义platform_device下的structresource设备资源结构
3.定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
4.调用platform_device_register将platform_device结构
注册上面4个过程调用结束后,设备的信息就被注册到系统中,等待驱动的使用
下面分析驱动和设备的链接过程
定义platform_driver结构
structplatform_driverfsl_otg_driver={
.probe=fsl_otg_probe,//定义处理函数,该函数在设备名字匹配到后调用,也就是发现该驱动对应的设备在系统中注册过。
.remove=fsl_otg_remove,
.driver={
.name="fsl-usb2-otg",//通过该名字匹配开始注册进系统的设备
.owner=THIS_MODULE,
},
};
将platform_driver结构注册进系统,系统通过注册名字匹配该设备是否已经在系统中,如果在调用注册的probe=fsl_otg_probe函数
staticint__initfsl_usb_otg_init(void)
{
printk(KERN_INFODRIVER_DESC"loaded,%s/n",DRIVER_VERSION);
returnplatform_driver_register(&fsl_otg_driver);
}
调用fsl_otg_probe函数,函数参数platform_device*pdev,就是我们上面注册进系统的platform_device结构,现在由系统赋值调用fsl_otg_probe
staticint__initfsl_otg_probe(structplatform_device*pdev)
{
intstatus;
structfsl_usb2_platform_data*pdata;
DBG("pdev=0x%p/n",pdev);
if(!
pdev)
return-ENODEV;
/*判断是否有设备自己的数据,就是检查我们上面定义的3的过程*/
if(!
pdev->dev.platform_data)
return-ENOMEM;
pdata=pdev->dev.platform_data;
/*configuretheOTG*/
status=fsl_otg_conf(pdev);
if(status){
printk(KERN_INFO"Couldn'tinitOTGmodule/n");
return-status;
}
/*startOTG*/
status=usb_otg_start(pdev);
if(register_chrdev(FSL_OTG_MAJOR,FSL_OTG_NAME,&otg_fops)){
printk(KERN_WARNINGFSL_OTG_NAME
":
unabletoregisterFSLOTGdevice/n");
return-EIO;
}
create_proc_file();
returnstatus;
}
上面函数中调用了fsl_otg_conf,我们来看看他干了什么。
staticintfsl_otg_conf(structplatform_device*pdev)
{
intstatus;
structfsl_otg*fsl_otg_tc;
structfsl_usb2_platform_data*pdata;
pdata=pdev->dev.platform_data;
DBG();
/**************************************************************/
structfsl_otg{
structotg_transceiverotg;
structotg_fsmfsm;
structusb_dr_mmap*dr_mem_map;
structdelayed_workotg_event;
/*usedforusbhost*/
structwork_structwork_wq;
u8 host_working;
intirq;
};
/**************************************************************/
if(fsl_otg_dev)
return0;
/*allocatespacetofslotgdevice*/
fsl_otg_tc=kzalloc(sizeof(structfsl_otg),GFP_KERNEL);
if(!
fsl_otg_tc)
return-ENODEV;
INIT_DELAYED_WORK(&fsl_otg_tc->otg_event,fsl_otg_event);
INIT_LIST_HEAD(&active_timers);
status=fsl_otg_init_timers(&fsl_otg_tc->fsm);
if(status){
printk(KERN_INFO"Couldn'tinitOTGtimers/n");
fsl_otg_uninit_timers();
kfree(fsl_otg_tc);
returnstatus;
}
spin_lock_init(&fsl_otg_tc->fsm.lock);
/*SetOTGstatemachineoperations*/
/**************************************************************/
staticstructotg_fsm_opsfsl_otg_ops={
.chrg_vbus=fsl_otg_chrg_vbus,
.drv_vbus=fsl_otg_drv_vbus,
.loc_conn=fsl_otg_loc_conn,
.loc_sof=fsl_otg_loc_sof,
.start_pulse=fsl_otg_start_pulse,
.add_timer=fsl_otg_add_timer,
.del_timer=fsl_otg_del_timer,
.start_host=fsl_otg_start_host,
.start_gadget=fsl_otg_start_gadget,
};
/**************************************************************/
fsl_otg_tc->fsm.ops=&fsl_otg_ops;
/*initializetheotgstructure*/
fsl_otg_tc->otg.label=DRIVER_DESC;
fsl_otg_tc->otg.set_host=fsl_otg_set_host;
fsl_otg_tc->otg.set_peripheral=fsl_otg_set_peripheral;
fsl_otg_tc->otg.set_power=fsl_otg_set_power;
fsl_otg_tc->otg.start_hnp=fsl_otg_start_hnp;
fsl_otg_tc->otg.start_srp=fsl_otg_start_srp;
fsl_otg_dev=fsl_otg_tc;
/*Storetheotgtransceiver*/
/***************************************************************/
intotg_set_transceiver(structotg_transceiver*x)
{
if(xceiv&&x)
return-EBUSY;
xceiv=x;
return0;
}
该函数就是将structotg_transceiver结构副给一个全局变量保存,供以后使用,以后会通过调用下面函数得到该结构
structotg_transceiver*otg_get_transceiver(void)
{
if(xceiv)
get_device(xceiv->dev);
returnxceiv;
}
/***************************************************************/
status=otg_set_transceiver(&fsl_otg_tc->otg);
if(status){
printk(KERN_WARNING":
unabletoregisterOTGtransceiver./n");
returnstatus;
}
return0;
}
intusb_otg_start(structplatform_device*pdev)
{
structfsl_otg*p_otg;
/*获得otg_transceiver结构*/
structotg_transceiver*otg_trans=otg_get_transceiver();
structotg_fsm*fsm;
volatileunsignedlong*p;
intstatus;
structresource*res;
u32temp;
/*获得设备的私有数据*/
structfsl_usb2_platform_data*pdata=pdev->dev.platform_data;
/*使用container_of宏定义可以通过结构中一个变量的指针获得该结构首地址*/
p_otg=container_of(otg_trans,structfsl_otg,otg);
fsm=&p_otg->fsm;
/*Initializethestatemachinestructurewithdefaultvalues*/
SET_OTG_STATE(otg_trans,OTG_STATE_UNDEFINED);
fsm->transceiver=&p_otg->otg;
/*Wedon'trequirepredefinedMEM/IRQresourceindex*/
/*获得设备的资源,是在设备注册时结构体里面的内容*/
res=platform_get_resource(pdev,IORESOURCE_MEM,0);
if(!
res)
return-ENXIO;
/*Wedon'trequest_mem_regionheretoenableresourcesharing
*withhost/device*/
/*通过资源中获得的物理地址映射一个可以被驱动访问的虚拟地址指针*/
usb_dr_regs=ioremap(res->start,sizeof(structusb_dr_mmap));
/*将该指针保存到p_otg->dr_mem_map中*/
p_otg->dr_mem_map=(structusb_dr_mmap*)usb_dr_regs;
pdata->regs=(void*)usb_dr_regs;
/*requestirq*/
/*获得设备注册时候的中断并注册,在OTGID发生变化时触发中断,然后调用注册的中断例程函数,函数后面分析*/
p_otg->irq=platform_get_irq(pdev,0);
status=request_irq(p_otg->irq,fsl_otg_isr,
IRQF_SHARED,driver_name,p_otg);
if(status){
dev_dbg(p_otg->otg.dev,"can'tgetIRQ%d,error%d/n",
p_otg->irq,status);
iounmap(p_otg->dr_mem_map);
kfree(p_otg);
returnstatus;
}
if(pdata->platform_init&&pdata->platform_init(pdev)!
=0)
return-EINVAL;
/*ExportDRcontrollerresources*/
/**************************************************/
intotg_set_resources(structresource*resources)
{
otg_resources=resources;
return0;
}
和otg_set_transceiver功能类似将设备资源保存到一个全局变量中
/**************************************************/
otg_set_resources(pdev->resource);
/*开始配置USB寄存器*/
/*stopthecontroller*/
temp=readl(&p_otg->dr_mem_map->usbcmd);
temp&=~USB_CMD_RUN_STOP;
writel(temp,&p_otg->dr_mem_map->usbcmd);
/*resetthecontroller*/
temp=readl(&p_otg->dr_mem_map->usbcmd);
temp|=USB_CMD_CTRL_RESET;
writel(temp,&p_otg->dr_mem_map->usbcmd);
/*waitresetcompleted*/
while(readl(&p_otg->dr_mem_map->usbcmd)&USB_CMD_CTRL_RESET);
/*configuretheVBUSHSasIDLE(bothhostanddevice)*/
temp=USB_MODE_STREAM_DISABLE|(pdata->es?
USB_MODE_ES:
0);
writel(temp,&p_otg->dr_mem_map->usbmode);
/*configurePHYinterface*/
temp=readl(&p_otg->dr_mem_map->portsc);
temp&=~(PORTSC_PHY_TYPE_SEL|PORTSC_PTW);
switch(pdata->phy_mode){
caseFSL_USB2_PHY_ULPI: