linux 视频驱动分析,ARM-Linux驱动--DMA驱动分析(一)

硬件平台:FL2440 (s3c2440)

内核版本:2.6.35本文引用地址:http://www.eepw.com.cn/article/201611/319010.htm

主机平台:Ubuntu 11.04

内核版本:2.6.39

1、DMA的功能和工作原理这里就不多说了,可以查看s3c2440的手册

2、在正式分析DMA驱动之前,我们先来看一下DMA的注册和初始化过程

系统设备:(翻译自源码注释)

系统设备和系统模型有点不同,它不需要动态绑定驱动,不能被探测(probe),不归结为任何的系统总线,所以要区分对待。对待系统设备我们仍然要有设备驱动的观念,因为我们需要对设备进行基本的操作。

定义系统设备,在./arch/arm/mach-s3c2440/s3c244x.c中

/*定义系统设备类*/

structsysdev_classs3c2440_sysclass={

.name="s3c2440-core",

.suspend=s3c244x_suspend,

.resume=s3c244x_resume

};注册系统设备类,在真正注册设备之前,确保已经注册了初始化了的系统设备类

staticint__inits3c2440_core_init(void)

{

returnsysdev_class_register(&s3c2440_sysclass);

}

下面就是系统设备类的注册函数,在./drivers/base/sys.c中

intsysdev_class_register(structsysdev_class*cls)

{

intretval;

pr_debug("Registeringsysdevclass%s\n",cls->name);

INIT_LIST_HEAD(&cls->drivers);

memset(&cls->kset.kobj,0x00,sizeof(structkobject));

cls->kset.kobj.parent=&system_kset->kobj;

cls->kset.kobj.ktype=&ktype_sysdev_class;

cls->kset.kobj.kset=system_kset;

retval=kobject_set_name(&cls->kset.kobj,"%s",cls->name);

if(retval)

returnretval;

retval=kset_register(&cls->kset);

if(!retval&&cls->attrs)

retval=sysfs_create_files(&cls->kset.kobj,

(conststructattribute**)cls->attrs);

returnretval;

}

/*定义DMA系统设备驱动*/

staticstructsysdev_drivers3c2440_dma_driver={

.add=s3c2440_dma_add,/*添加add函数*/

};下面是add函数,就是调用三个函数

staticint__inits3c2440_dma_add(structsys_device*sysdev)

{

s3c2410_dma_init();

s3c24xx_dma_order_set(&s3c2440_dma_order);

returns3c24xx_dma_init_map(&s3c2440_dma_sel);

}注册DMA驱动到系统设备

staticint__inits3c2440_dma_init(void)

{

returnsysdev_driver_register(&s3c2440_sysclass,&s3c2440_dma_driver);

}下面就是系统设备驱动的注册函数

/**

*sysdev_driver_register-Registerauxillarydriver

*@cls:Deviceclassdriverbelongsto.

*@drv:Driver.

*

*@drvisinsertedinto@cls->driverstobe

*calledoneachoperationondevicesofthatclass.Therefcount

*of@clsisincremented.

*/

intsysdev_driver_register(structsysdev_class*cls,structsysdev_driver*drv)

{

interr=0;

if(!cls){

WARN(1,KERN_WARNING"sysdev:invalidclasspassedto"

"sysdev_driver_register!\n");

return-EINVAL;

}

/*Checkwhetherthisdriverhasalreadybeenaddedtoaclass.*/

if(drv->entry.next&&!list_empty(&drv->entry))

WARN(1,KERN_WARNING"sysdev:class%s:driver(%p)hasalready"

"beenregisteredtoaclass,somethingiswrong,but"

"willforgeon!\n",cls->name,drv);

mutex_lock(&sysdev_drivers_lock);

if(cls&&kset_get(&cls->kset)){

list_add_tail(&drv->entry,&cls->drivers);/*将设备驱动添加到系统设备类的链表中*/

/*Ifdevicesofthisclassalreadyexist,tellthedriver*/

if(drv->add){

structsys_device*dev;

list_for_each_entry(dev,&cls->kset.list,kobj.entry)

drv->add(dev);

}

}else{

err=-EINVAL;

WARN(1,KERN_ERR"%s:invaliddeviceclass\n",__func__);

}

mutex_unlock(&sysdev_drivers_lock);

returnerr;

}在./arch/arm/mach-s3c2440/s3c2440.c中定义s3c2440的系统设备和注册

staticstructsys_devices3c2440_sysdev={

.cls=&s3c2440_sysclass,/*定义系统设备的所属系统设备类,用于系统设备注册到指定设备类*/

};

/*S3C2440初始化*/

int__inits3c2440_init(void)

{

printk("S3C2440:Initialisingarchitecture\n");

s3c24xx_gpiocfg_default.set_pull=s3c_gpio_setpull_1up;

s3c24xx_gpiocfg_default.get_pull=s3c_gpio_getpull_1up;

/*changeirqforwatchdog*/

s3c_device_wdt.resource[1].start=IRQ_S3C2440_WDT;

s3c_device_wdt.resource[1].end=IRQ_S3C2440_WDT;

/*registeroursystemdeviceforeverythingelse*/

returnsysdev_register(&s3c2440_sysdev);/*注册s3c2440的系统设备*/

}接下来是系统设备的注册函数

/**

*sysdev_register-addasystemdevicetothetree

*@sysdev:deviceinquestion

*

*/

/*系统设备的注册*/

intsysdev_register(structsys_device*sysdev)

{

interror;

structsysdev_class*cls=sysdev->cls;/*所属的系统设备类*/

if(!cls)

return-EINVAL;

pr_debug("Registeringsysdeviceofclass%s\n",

kobject_name(&cls->kset.kobj));

/*initializethekobjectto0,incaseithadpreviouslybeenused*/

memset(&sysdev->kobj,0x00,sizeof(structkobject));

/*Makesuretheksetisset*/

sysdev->kobj.kset=&cls->kset;

/*Registertheobject*/

error=kobject_init_and_add(&sysdev->kobj,&ktype_sysdev,NULL,

"%s%d",kobject_name(&cls->kset.kobj),

sysdev->id);

if(!error){

structsysdev_driver*drv;

pr_debug("Registeringsysdevice%s\n",

kobject_name(&sysdev->kobj));

mutex_lock(&sysdev_drivers_lock);

/*Genericnotificationisimplicit,becauseitsthat

*codethatshouldhavecalledus.

*/

/*Notifyclassauxillarydrivers*/

list_for_each_entry(drv,&cls->drivers,entry){

if(drv->add)

drv->add(sysdev);/*遍历该设备所属同一个设备类的所有设备,并执行相应的add函数*/

}

mutex_unlock(&sysdev_drivers_lock);

kobject_uevent(&sysdev->kobj,KOBJ_ADD);

}

returnerror;

}那DMA系统设备驱动中的add函数中到底是什么呢?

(1)首先看第一个函数int __init s3c2410_dma_init(void),在./arch/arm/plat-s3c24xx/dma.c

[cpp]view plaincopyint__inits3c2410_dma_init(void)

{

returns3c24xx_dma_init(4,IRQ_DMA0,0x40);

}实际上就是初始化DMA为4通道,设置中断号,设置寄存器的覆盖范围

下面是该函数的实现

int__inits3c24xx_dma_init(unsignedintchannels,unsignedintirq,

unsignedintstride)/*参数分别为通道个数、中断号、寄存器的覆盖范围*/

{

structs3c2410_dma_chan*cp;/*通道的结构体表示*/

intchannel;

intret;

printk("S3C24XXDMADriver,Copyright2003-2006SimtecElectronics\n");

dma_channels=channels;

dma_base=ioremap(S3C24XX_PA_DMA,stride*channels);

if(dma_base==NULL){

printk(KERN_ERR"dmafailedtoremapregisterblock\n");

return-ENOMEM;

}

/*分配DMA告诉缓冲区*/

dma_kmem=kmem_cache_create("dma_desc",

sizeof(structs3c2410_dma_buf),0,

SLAB_HWCACHE_ALIGN,

s3c2410_dma_cache_ctor);

if(dma_kmem==NULL){

printk(KERN_ERR"dmafailedtomakekmemcache\n");

ret=-ENOMEM;

gotoerr;

}

for(channel=0;channelcp=&s3c2410_chans[channel];

memset(cp,0,sizeof(structs3c2410_dma_chan));

/*dmachannelirqsareinorder..*/

cp->number=channel;

cp->irq=channel+irq;

cp->regs=dma_base+(channel*stride);

/*pointcurrentstatssomewhere*/

cp->stats=&cp->stats_store;

cp->stats_store.timeout_shortest=LONG_MAX;

/*basicchannelconfiguration*/

cp->load_timeout=1<<18;

printk("DMAchannel%dat%p,irq%d\n",

cp->number,cp->regs,cp->irq);

}

return0;

/*异常处理*/

err:

kmem_cache_destroy(dma_kmem);

iounmap(dma_base);

dma_base=NULL;

returnret;

}

(2)然后是函数s3c24xx_dma_order_set(&s3c2440_dma_order);

int__inits3c24xx_dma_order_set(structs3c24xx_dma_order*ord)

{

structs3c24xx_dma_order*nord=dma_order;

if(nord==NULL)

nord=kmalloc(sizeof(structs3c24xx_dma_order),GFP_KERNEL);

if(nord==NULL){

printk(KERN_ERR"nomemorytostoredmachannelorder\n");

return-ENOMEM;

}

dma_order=nord;

memcpy(nord,ord,sizeof(structs3c24xx_dma_order));

return0;

}我们注意到函数中使用了kmalloc给结构体重新分配了内存,这是由于__initdata修饰的变量表示初始化用的变量,初始化完毕后空间自动释放,所以需要将其存储起来。

(3)最后一个函数s3c24xx_dma_init_map(&s3c2440_dma_sel)

该函数功能是建立DMA源与硬件通道的映射图

int__inits3c24xx_dma_init_map(structs3c24xx_dma_selection*sel)

{

structs3c24xx_dma_map*nmap;

size_tmap_sz=sizeof(*nmap)*sel->map_size;

intptr;

nmap=kmalloc(map_sz,GFP_KERNEL);

if(nmap==NULL)

return-ENOMEM;

memcpy(nmap,sel->map,map_sz);

memcpy(&dma_sel,sel,sizeof(*sel));

dma_sel.map=nmap;

for(ptr=0;ptrmap_size;ptr++)

s3c24xx_dma_check_entry(nmap+ptr,ptr);

return0;

}这里的kmalloc函数的作用同上面的作用一样。

注:由于内核实在是太深了,这里只是表面上按流程大体了解了子同设备的注册和系统设备驱动的注册以及DMA设备的注册和初始化,函数中有很多细节有待进一步研究。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值