硬件平台: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设备的注册和初始化,函数中有很多细节有待进一步研究。