硬件渠道:FL2440 (s3c2440)
内核版别:2.6.35
主机渠道: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
- int__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;channel
- cp=&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;ptr
map_size;ptr++) - s3c24xx_dma_check_entry(nmap+ptr,ptr);
- return0;
- }
这儿的kmalloc函数的效果同上面的效果相同。
注:我们内核实在是太深了,这儿仅仅表面上按流程大体了解了子同设备的注册和体系设备驱动的注册以及DMA设备的注册和初始化,函数中有许多细节有待进一步研讨。