Linux驱动修炼之道

http://blog.csdn.net/dahailinan/article/details/7280701

首先介绍一下DMA,S3C2440A支持位于系统总线和外围总线之间的4通道DMA控制器,每个通道都可以在系统总线或外围总线上的设备之间传输数据。每个通道可以对下面4种情况进行传输:1.源和目的都在系统总线上2.源在系统总线而目的在外围总线3.源在外围总线而目的在系统总线4.源和目的都在外围总线下图是请求源为硬件模式时的每个通道的请求源:

DMA使用3个状态的有限状态机:1.初始状态,DMA等待DMA请求,一旦请求到达DMA进入状态2,DMA ACK与INT REQ为0。2.在这个状态,DMA ACK置为1并且计数器CURR_TC的值被从DCON[19:0]载入,注意DMA ACK保持为1直到它被清除。3.在这个状态,处理DMA原子操作的子状态机被初始化。子状态机从源地址读取数据,然后写入目的地址。在这个操作中,要考虑数据的大小和传输的大小(单个/突发),这个操作重复执行直到计数器(CURR_TC)变成0在全服务模式,而只执行一次在单服务模式。当子状态机结束每一次原子操作的时候主状态机减少CURR_TC的值。另外,主状态机发出INT REQ信号当CURR_TC变成0并且DCON[29]位被置位1时。并且,清除DMA ACK,如果下面两个条件之一满足的话:1)在全服务模式下CURR_TC变成02)在单服务模式下结束原子操作注意在单服务模式下,主有限状态机的3个状态被执行然后停止,等待另一个DMA REQ。如果DMA REQ到来,所有的3个状态被重复执行。所以在每次原子操作中DMA ACK被置位,然后又被清除。与之对比,在全服务模式,主有限状态机等在状态3直到CURR_TC变成0。所以,DMA ACK在传输期间被置位,到TC为0时被清除。然而,不管哪个服务模式,INT REQ被发出只有当CURR_TC变成0时。如下图,是基本的DMA时序:

nXDREQ生效后等待至少2个时钟周期,nXDACK响应并开始生效,但要知道延时至少3个时钟周期,DMA控制器才可获得总线的控制权,进行读写操作一次。

下面来分析内核DMA驱动源码:首先来看一下DMA驱动是怎样注册的:这里使用了系统设备的概念,通过内核中源码的注释我们看看什么是系统设备。系统设备与驱动模型有一点不同,他们不需要动态的驱动绑定,也不能被探测,并且不属于任何类型的外围总线。对系统设备我们仍然有驱动的概念,因为我们仍想执行在这些设备上执行基本的操作。在arch/arm/plat-s3c24xx/s3c244x.c中,注册了系统设备的类:

[c-sharp]view plaincopyprint?

    structsysdev_classs3c2440_sysclass={.name="s3c2440-core",.suspend=s3c244x_suspend,.resume=s3c244x_resume};staticint__inits3c2440_core_init(void){returnsysdev_class_register(&s3c2440_sysclass);}

[c-sharp]view plaincopyprint?

    structsysdev_classs3c2440_sysclass={.name="s3c2440-core",.suspend=s3c244x_suspend,.resume=s3c244x_resume};staticint__inits3c2440_core_init(void){returnsysdev_class_register(&s3c2440_sysclass);}

在arch/arm/mach-s3c2410/dma.c中,注册了dma的驱动:

[c-sharp]view plaincopyprint?

    #ifdefined(CONFIG_CPU_S3C2410)staticstructsysdev_drivers3c2410_dma_driver={.add=s3c2410_dma_add,};

[c-sharp]view plaincopyprint?

    #ifdefined(CONFIG_CPU_S3C2410)staticstructsysdev_drivers3c2410_dma_driver={.add=s3c2410_dma_add,};

把dma驱动注册到设备类下:

[c-sharp]view plaincopyprint?

    staticint__inits3c2410_dma_drvinit(void){returnsysdev_driver_register(&s3c2410_sysclass,&s3c2410_dma_driver);}

[c-sharp]view plaincopyprint?

    staticint__inits3c2410_dma_drvinit(void){returnsysdev_driver_register(&s3c2410_sysclass,&s3c2410_dma_driver);}

先来看一下系统设备类:

[c-sharp]view plaincopyprint?

    structsysdev_class{constchar*name;structlist_headdrivers;/*Defaultoperationsforthesetypesofdevices*/int(*shutdown)(structsys_device*);int(*suspend)(structsys_device*,pm_message_tstate);int(*resume)(structsys_device*);structksetkset;};

[c-sharp]view plaincopyprint?

    structsysdev_class{constchar*name;structlist_headdrivers;/*Defaultoperationsforthesetypesofdevices*/int(*shutdown)(structsys_device*);int(*suspend)(structsys_device*,pm_message_tstate);int(*resume)(structsys_device*);structksetkset;};

这个结构体有一个drivers双向循环链表,注册到这个类的驱动都挂在这里。下面分析一下dma驱动是怎样注册的:

[c-sharp]view plaincopyprint?

    intsysdev_driver_register(structsysdev_class*cls,structsysdev_driver*drv){。。。。。。。。。。。if(cls&&kset_get(&cls->kset)){/*这里把这个驱动添加到了设备类的驱动链表上*/list_add_tail(&drv->entry,&cls->drivers);/*如果驱动定义了add方法,则为类下的每个设备调用驱动的add方法*/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;}

[c-sharp]view plaincopyprint?

    intsysdev_driver_register(structsysdev_class*cls,structsysdev_driver*drv){。。。。。。。。。。。if(cls&&kset_get(&cls->kset)){/*这里把这个驱动添加到了设备类的驱动链表上*/list_add_tail(&drv->entry,&cls->drivers);/*如果驱动定义了add方法,则为类下的每个设备调用驱动的add方法*/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_sysdev,

[c-sharp]view plaincopyprint?

    staticstructsys_devices3c2440_sysdev={.cls=&s3c2440_sysclass,};int__inits3c2440_init(void){。。。。。。。。returnsysdev_register(&s3c2440_sysdev);}

[c-sharp]view plaincopyprint?

    staticstructsys_devices3c2440_sysdev={.cls=&s3c2440_sysclass,};int__inits3c2440_init(void){。。。。。。。。returnsysdev_register(&s3c2440_sysdev);}

注意系统设备这个结构体,里边封装了一个系统设备类。

[c-sharp]view plaincopyprint?

    structsys_device{u32id;structsysdev_class*cls;structkobjectkobj;};

[c-sharp]view plaincopyprint?

    structsys_device{u32id;structsysdev_class*cls;structkobjectkobj;};

下面来看一下系统设备的注册,系统设备是一个虚拟设备,这里的目的就是为了调用driver的add函数。

[c-sharp]view plaincopyprint?

    intsysdev_register(structsys_device*sysdev){。。。。。。。。。。。。/*Notifyclassauxillarydrivers*/list_for_each_entry(drv,&cls->drivers,entry){/*为这个设备调用了设备类下所有驱动的add函数*/if(drv->add)drv->add(sysdev);}。。。。。。。。。。。。。}

[c-sharp]view plaincopyprint?

    intsysdev_register(structsys_device*sysdev){。。。。。。。。。。。。/*Notifyclassauxillarydrivers*/list_for_each_entry(drv,&cls->drivers,entry){/*为这个设备调用了设备类下所有驱动的add函数*/if(drv->add)drv->add(sysdev);}。。。。。。。。。。。。。}

下面来分析一下这个add函数。看上边的那个dma驱动的结构体,指明了add函数为s3c2410_dma_add:

[c-sharp]view plaincopyprint?

    staticint__inits3c2410_dma_add(structsys_device*sysdev){s3c2410_dma_init();(一)s3c24xx_dma_order_set(&s3c2410_dma_order);(二)returns3c24xx_dma_init_map(&s3c2410_dma_sel);(三)}

[c-sharp]view plaincopyprint?

    staticint__inits3c2410_dma_add(structsys_device*sysdev){s3c2410_dma_init();(一)s3c24xx_dma_order_set(&s3c2410_dma_order);(二)returns3c24xx_dma_init_map(&s3c2410_dma_sel);(三)}

分别对s3c2410_dma_add中的3个函数进行分析:

(一)

[c-sharp]view plaincopyprint?

    int__inits3c2410_dma_init(void){/*4个通道,中断号为IRQ_DMA0,每一个通道的寄存器覆盖的地址范围为0x40*/returns3c24xx_dma_init(4,IRQ_DMA0,0x40);}

[c-sharp]view plaincopyprint?

    int__inits3c2410_dma_init(void){/*4个通道,中断号为IRQ_DMA0,每一个通道的寄存器覆盖的地址范围为0x40*/returns3c24xx_dma_init(4,IRQ_DMA0,0x40);}

[c-sharp]view plaincopyprint?

    int__inits3c24xx_dma_init(unsignedintchannels,unsignedintirq,unsignedintstride){/*每一个通道用一个s3c2410_dma_chan结构体描述*/structs3c2410_dma_chan*cp;intchannel;intret;printk("S3C24XXDMADriver,(c)2003-2004,2006SimtecElectronics/n");/*dma_channels是一个全局变量,用来存放通道数量*/dma_channels=channels;/*获得DMA寄存器的虚拟起始地址*/dma_base=ioremap(S3C24XX_PA_DMA,stride*channels);if(dma_base==NULL){printk(KERN_ERR"dmafailedtoremapregisterblock/n");return-ENOMEM;}/*分配一个高速缓冲区,以后用来分配s3c2410_dma_buf*/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<channels;channel++){cp=&s3c2410_chans[channel];memset(cp,0,sizeof(structs3c2410_dma_chan));/*对通道的结构体进行初始化*/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;}

[c-sharp]view plaincopyprint?

    int__inits3c24xx_dma_init(unsignedintchannels,unsignedintirq,unsignedintstride){/*每一个通道用一个s3c2410_dma_chan结构体描述*/structs3c2410_dma_chan*cp;intchannel;intret;printk("S3C24XXDMADriver,(c)2003-2004,2006SimtecElectronics/n");/*dma_channels是一个全局变量,用来存放通道数量*/dma_channels=channels;/*获得DMA寄存器的虚拟起始地址*/dma_base=ioremap(S3C24XX_PA_DMA,stride*channels);if(dma_base==NULL){printk(KERN_ERR"dmafailedtoremapregisterblock/n");return-ENOMEM;}/*分配一个高速缓冲区,以后用来分配s3c2410_dma_buf*/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<channels;channel++){cp=&s3c2410_chans[channel];memset(cp,0,sizeof(structs3c2410_dma_chan));/*对通道的结构体进行初始化*/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;}

这里使用到了一个s3c2410_dma_chan结构体,struct s3c2410_dma_chan记录dma通道信息,内容如下:

[c-sharp]view plaincopyprint?

    151structs3c2410_dma_chan{152/*channelstateflagsandinformation*/153unsignedcharnumber;//dma通道号,154unsignedcharin_use;//当前通道是否已经使用155unsignedcharirq_claimed;//有无dma中断156unsignedcharirq_enabled;//是否使能了dma中断157unsignedcharxfer_unit;//传输块大小158159/*channelstate*/160161enums3c2410_dma_statestate;162enums3c2410_dma_loadstload_state;163structs3c2410_dma_client*client;164165/*channelconfiguration*/166enums3c2410_dmasrcsource;167enumdma_chreq_ch;168unsignedlongdev_addr;169unsignedlongload_timeout;170unsignedintflags;/*channelflags*/171172structs3c24xx_dma_map*map;/*channelhwmaps*/173174/*channel’shardwarepositionandconfiguration*/175void__iomem*regs;/*channelsregisters*/176void__iomem*addr_reg;/*dataaddressregister*/177unsignedintirq;中断号178unsignedlongdcon;/默认控制寄存器的值179180/*driverhandles*/181s3c2410_dma_cbfn_tcallback_fn;传输完成回调函数182s3c2410_dma_opfn_top_fn;操作完成回调函数*/183184/*statsgathering*/185structs3c2410_dma_stats*stats;186structs3c2410_dma_statsstats_store;187188/*bufferlistandinformation*/189structs3c2410_dma_buf*curr;/*currentdmabuffer*/190structs3c2410_dma_buf*next;/*nextbuffertoload*/191structs3c2410_dma_buf*end;/*endofqueue*/dma缓冲区链表192193/*systemdevice*/194structsys_devicedev;195};

[c-sharp]view plaincopyprint?

    151structs3c2410_dma_chan{152/*channelstateflagsandinformation*/153unsignedcharnumber;//dma通道号,154unsignedcharin_use;//当前通道是否已经使用155unsignedcharirq_claimed;//有无dma中断156unsignedcharirq_enabled;//是否使能了dma中断157unsignedcharxfer_unit;//传输块大小158159/*channelstate*/160161enums3c2410_dma_statestate;162enums3c2410_dma_loadstload_state;163structs3c2410_dma_client*client;164165/*channelconfiguration*/166enums3c2410_dmasrcsource;167enumdma_chreq_ch;168unsignedlongdev_addr;169unsignedlongload_timeout;170unsignedintflags;/*channelflags*/171172structs3c24xx_dma_map*map;/*channelhwmaps*/173174/*channel’shardwarepositionandconfiguration*/175void__iomem*regs;/*channelsregisters*/176void__iomem*addr_reg;/*dataaddressregister*/177unsignedintirq;中断号178unsignedlongdcon;/默认控制寄存器的值179180/*driverhandles*/181s3c2410_dma_cbfn_tcallback_fn;传输完成回调函数182s3c2410_dma_opfn_top_fn;操作完成回调函数*/183184/*statsgathering*/185structs3c2410_dma_stats*stats;186structs3c2410_dma_statsstats_store;187188/*bufferlistandinformation*/189structs3c2410_dma_buf*curr;/*currentdmabuffer*/190structs3c2410_dma_buf*next;/*nextbuffertoload*/191structs3c2410_dma_buf*end;/*endofqueue*/dma缓冲区链表192193/*systemdevice*/194structsys_devicedev;195};

(二)先看下边一个结构体,s3c2410_dma_order。这个是建立目标板dma源与硬件的dma通道的关联。

[c-sharp]view plaincopyprint?

    staticstructs3c24xx_dma_order__initdatas3c2410_dma_order={.channels={[DMACH_SDI]={.list={[0]=3|DMA_CH_VALID,[1]=2|DMA_CH_VALID,[2]=0|DMA_CH_VALID,},},[DMACH_I2S_IN]={.list={[0]=1|DMA_CH_VALID,[1]=2|DMA_CH_VALID,},},},};

[c-sharp]view plaincopyprint?

    staticstructs3c24xx_dma_order__initdatas3c2410_dma_order={.channels={[DMACH_SDI]={.list={[0]=3|DMA_CH_VALID,[1]=2|DMA_CH_VALID,[2]=0|DMA_CH_VALID,},},[DMACH_I2S_IN]={.list={[0]=1|DMA_CH_VALID,[1]=2|DMA_CH_VALID,},},},};

分析这里SDI可以是使用通道3,2,0,为什么从大到小排列,是因为某些dma请求只能使用dma0,dma1等较小的通道号,比如外部总线dma只能只用dma0,为了避免sdi占用,这里就采用的这种排列。

[c-sharp]view plaincopyprint?

    [DMACH_SDI]={.list={[0]=3|DMA_CH_VALID,[1]=2|DMA_CH_VALID,[2]=0|DMA_CH_VALID,},},

[c-sharp]view plaincopyprint?

    [DMACH_SDI]={.list={[0]=3|DMA_CH_VALID,[1]=2|DMA_CH_VALID,[2]=0|DMA_CH_VALID,},},

注意这个结构体是用__initdata修饰的,所以在初始化后会被释放掉。下边这个函数重新分配内存保存这个结构体就是这个原因。

[c-sharp]view plaincopyprint?

    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;}

[c-sharp]view plaincopyprint?

    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;}

(三)

[c-sharp]view plaincopyprint?

    structs3c24xx_dma_map{constchar*name;//DMA源的名structs3c24xx_dma_addrhw_addr;//源的物理地址unsignedlongchannels[S3C2410_DMA_CHANNELS];//DMA通道信息unsignedlongchannels_rx[S3C2410_DMA_CHANNELS];};structs3c24xx_dma_selection{structs3c24xx_dma_map*map;//记录了structs3c24xx_dma_map数组的首地址unsignedlongmap_size;//structs3c24xx_dma_map数组的成员个数unsignedlongdcon_mask;//dma控制器掩码void(*select)(structs3c2410_dma_chan*chan,structs3c24xx_dma_map*map);//源选择函数void(*direction)(structs3c2410_dma_chan*chan,//dma方向structs3c24xx_dma_map*map,enums3c2410_dmasrcdir);};

[c-sharp]view plaincopyprint?

    structs3c24xx_dma_map{constchar*name;//DMA源的名structs3c24xx_dma_addrhw_addr;//源的物理地址unsignedlongchannels[S3C2410_DMA_CHANNELS];//DMA通道信息unsignedlongchannels_rx[S3C2410_DMA_CHANNELS];};structs3c24xx_dma_selection{structs3c24xx_dma_map*map;//记录了structs3c24xx_dma_map数组的首地址unsignedlongmap_size;//structs3c24xx_dma_map数组的成员个数unsignedlongdcon_mask;//dma控制器掩码void(*select)(structs3c2410_dma_chan*chan,structs3c24xx_dma_map*map);//源选择函数void(*direction)(structs3c2410_dma_chan*chan,//dma方向structs3c24xx_dma_map*map,enums3c2410_dmasrcdir);};

建立芯片本身的dma源与硬件dma通道的视图。

[c-sharp]view plaincopyprint?

    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<sel->map_size;ptr++)s3c24xx_dma_check_entry(nmap+ptr,ptr);return0;}staticstructs3c24xx_dma_selection__initdatas3c2410_dma_sel={.select=s3c2410_dma_select,//通道选择函数.dcon_mask=7<<24,//屏蔽DMA控制寄存器中用于选择请求源的位.map=s3c2410_dma_mappings,//dma源与硬件dma通道的视图.map_size=ARRAY_SIZE(s3c2410_dma_mappings),//虚拟通道的数目};staticvoids3c2410_dma_select(structs3c2410_dma_chan*chan,structs3c24xx_dma_map*map){chan->dcon=map->channels[chan->number]&~DMA_CH_VALID;//选择通道,并设置成请求模式}staticstructs3c24xx_dma_map__initdatas3c2410_dma_mappings[]={[DMACH_XD0]={.name="xdreq0",.channels[0]=S3C2410_DCON_CH0_XDREQ0|DMA_CH_VALID,},[DMACH_XD1]={.name="xdreq1",.channels[1]=S3C2410_DCON_CH1_XDREQ1|DMA_CH_VALID,},[DMACH_SDI]={.name="sdi",.channels[0]=S3C2410_DCON_CH0_SDI|DMA_CH_VALID,.channels[2]=S3C2410_DCON_CH2_SDI|DMA_CH_VALID,.channels[3]=S3C2410_DCON_CH3_SDI|DMA_CH_VALID,.hw_addr.to=S3C2410_PA_IIS+S3C2410_IISFIFO,.hw_addr.from=S3C2410_PA_IIS+S3C2410_IISFIFO,},[DMACH_SPI0]={.name="spi0",.channels[1]=S3C2410_DCON_CH1_SPI|DMA_CH_VALID,.hw_addr.to=S3C2410_PA_SPI+S3C2410_SPTDAT,.hw_addr.from=S3C2410_PA_SPI+S3C2410_SPRDAT,},[DMACH_SPI1]={.name="spi1",.channels[3]=S3C2410_DCON_CH3_SPI|DMA_CH_VALID,.hw_addr.to=S3C2410_PA_SPI+0x20+S3C2410_SPTDAT,.hw_addr.from=S3C2410_PA_SPI+0x20+S3C2410_SPRDAT,},[DMACH_UART0]={.name="uart0",.channels[0]=S3C2410_DCON_CH0_UART0|DMA_CH_VALID,.hw_addr.to=S3C2410_PA_UART0+S3C2410_UTXH,.hw_addr.from=S3C2410_PA_UART0+S3C2410_URXH,},[DMACH_UART1]={.name="uart1",.channels[1]=S3C2410_DCON_CH1_UART1|DMA_CH_VALID,.hw_addr.to=S3C2410_PA_UART1+S3C2410_UTXH,.hw_addr.from=S3C2410_PA_UART1+S3C2410_URXH,},[DMACH_UART2]={.name="uart2",.channels[3]=S3C2410_DCON_CH3_UART2|DMA_CH_VALID,.hw_addr.to=S3C2410_PA_UART2+S3C2410_UTXH,.hw_addr.from=S3C2410_PA_UART2+S3C2410_URXH,},[DMACH_TIMER]={.name="timer",.channels[0]=S3C2410_DCON_CH0_TIMER|DMA_CH_VALID,.channels[2]=S3C2410_DCON_CH2_TIMER|DMA_CH_VALID,.channels[3]=S3C2410_DCON_CH3_TIMER|DMA_CH_VALID,},[DMACH_I2S_IN]={.name="i2s-sdi",.channels[1]=S3C2410_DCON_CH1_I2SSDI|DMA_CH_VALID,.channels[2]=S3C2410_DCON_CH2_I2SSDI|DMA_CH_VALID,.hw_addr.from=S3C2410_PA_IIS+S3C2410_IISFIFO,},[DMACH_I2S_OUT]={.name="i2s-sdo",.channels[2]=S3C2410_DCON_CH2_I2SSDO|DMA_CH_VALID,.hw_addr.to=S3C2410_PA_IIS+S3C2410_IISFIFO,},[DMACH_USB_EP1]={.name="usb-ep1",.channels[0]=S3C2410_DCON_CH0_USBEP1|DMA_CH_VALID,},[DMACH_USB_EP2]={.name="usb-ep2",.channels[1]=S3C2410_DCON_CH1_USBEP2|DMA_CH_VALID,},[DMACH_USB_EP3]={.name="usb-ep3",.channels[2]=S3C2410_DCON_CH2_USBEP3|DMA_CH_VALID,},[DMACH_USB_EP4]={.name="usb-ep4",.channels[3]=S3C2410_DCON_CH3_USBEP4|DMA_CH_VALID,},};

DMA通道的使用:申请通道,申请中断,设置寄存器,安装回调函数,设置标志,将数据放入队列,最后就是调用static int s3c2410_dma_start(struct s3c2410_dma_chan *chan)来开始DMA的传输了。首先看通道的申请:

[c-sharp]view plaincopyprint?

    ints3c2410_dma_request(unsignedintchannel,structs3c2410_dma_client*client,void*dev){structs3c2410_dma_chan*chan;unsignedlongflags;interr;pr_debug("dma%d:s3c2410_request_dma:client=%s,dev=%p/n",channel,client->name,dev);local_irq_save(flags);//关中断/*找到一个有效的物理通道*/chan=s3c2410_dma_map_channel(channel);if(chan==NULL){local_irq_restore(flags);return-EBUSY;}dbg_showchan(chan);/*设置通道的名字*/chan->client=client;/*设置通道的使用标志*/chan->in_use=1;if(!chan->irq_claimed){//该中断没有被注册pr_debug("dma%d:%s:requestingirq%d/n",channel,__func__,chan->irq);chan->irq_claimed=1;//标记该中断被注册local_irq_restore(flags);//开中断err=request_irq(chan->irq,s3c2410_dma_irq,IRQF_DISABLED,//注册中断处理程序client->name,(void*)chan);local_irq_save(flags);if(err){chan->in_use=0;chan->irq_claimed=0;local_irq_restore(flags);printk(KERN_ERR"%s:cannotgetIRQ%dforDMA%d/n",client->name,chan->irq,chan->number);returnerr;}chan->irq_enabled=1;}local_irq_restore(flags);/*needtosetup*/pr_debug("%s:channelinitialised,%p/n",__func__,chan);returnchan->number|DMACH_LOW_LEVEL;}

[c-sharp]view plaincopyprint?

    ints3c2410_dma_request(unsignedintchannel,structs3c2410_dma_client*client,void*dev){structs3c2410_dma_chan*chan;unsignedlongflags;interr;pr_debug("dma%d:s3c2410_request_dma:client=%s,dev=%p/n",channel,client->name,dev);local_irq_save(flags);//关中断/*找到一个有效的物理通道*/chan=s3c2410_dma_map_channel(channel);if(chan==NULL){local_irq_restore(flags);return-EBUSY;}dbg_showchan(chan);/*设置通道的名字*/chan->client=client;/*设置通道的使用标志*/chan->in_use=1;if(!chan->irq_claimed){//该中断没有被注册pr_debug("dma%d:%s:requestingirq%d/n",channel,__func__,chan->irq);chan->irq_claimed=1;//标记该中断被注册local_irq_restore(flags);//开中断err=request_irq(chan->irq,s3c2410_dma_irq,IRQF_DISABLED,//注册中断处理程序client->name,(void*)chan);local_irq_save(flags);if(err){chan->in_use=0;chan->irq_claimed=0;local_irq_restore(flags);printk(KERN_ERR"%s:cannotgetIRQ%dforDMA%d/n",client->name,chan->irq,chan->number);returnerr;}chan->irq_enabled=1;}local_irq_restore(flags);/*needtosetup*/pr_debug("%s:channelinitialised,%p/n",__func__,chan);returnchan->number|DMACH_LOW_LEVEL;}

下面的函数是找通道好,先在板子通道映射中找,再在芯片通道映射中找。

[c-sharp]view plaincopyprint?

    staticstructs3c2410_dma_chan*s3c2410_dma_map_channel(intchannel){structs3c24xx_dma_order_ch*ord=NULL;structs3c24xx_dma_map*ch_map;structs3c2410_dma_chan*dmach;intch;if(dma_sel.map==NULL||channel>dma_sel.map_size)returnNULL;/*获得芯片的虚拟通道与真实通道映射的结构*/ch_map=dma_sel.map+channel;/*first,trytheboardmapping*//*如果有板子通道映射*/if(dma_order){/*得到对应虚拟通道的所有真实通道的结构*/ord=&dma_order->channels[channel];/*找这个虚拟通道对应的每一个真实通道,看有没有有效并且未被使用的*/for(ch=0;ch<dma_channels;ch++){if(!is_channel_valid(ord->list[ch]))continue;if(s3c2410_chans[ord->list[ch]].in_use==0){ch=ord->list[ch]&~DMA_CH_VALID;gotofound;}}if(ord->flags&DMA_CH_NEVER)returnNULL;}/*检查芯片虚拟通道与真实通道的映射,看有没有有效且未被使用的真实通道*/for(ch=0;ch<dma_channels;ch++){if(!is_channel_valid(ch_map->channels[ch]))continue;if(s3c2410_chans[ch].in_use==0){printk("mappedchannel%dto%d/n",channel,ch);break;}}if(ch>=dma_channels)returnNULL;/*updateourchannelmapping*/found:/*将找到的通道保存在dmach中,并返回*/dmach=&s3c2410_chans[ch];dmach->map=ch_map;dma_chan_map[channel]=dmach;/*selectthechannel*//*调用选择通道的函数*/(dma_sel.select)(dmach,ch_map);returndmach;}

[c-sharp]view plaincopyprint?

    staticstructs3c2410_dma_chan*s3c2410_dma_map_channel(intchannel){structs3c24xx_dma_order_ch*ord=NULL;structs3c24xx_dma_map*ch_map;structs3c2410_dma_chan*dmach;intch;if(dma_sel.map==NULL||channel>dma_sel.map_size)returnNULL;/*获得芯片的虚拟通道与真实通道映射的结构*/ch_map=dma_sel.map+channel;/*first,trytheboardmapping*//*如果有板子通道映射*/if(dma_order){/*得到对应虚拟通道的所有真实通道的结构*/ord=&dma_order->channels[channel];/*找这个虚拟通道对应的每一个真实通道,看有没有有效并且未被使用的*/for(ch=0;ch<dma_channels;ch++){if(!is_channel_valid(ord->list[ch]))continue;if(s3c2410_chans[ord->list[ch]].in_use==0){ch=ord->list[ch]&~DMA_CH_VALID;gotofound;}}if(ord->flags&DMA_CH_NEVER)returnNULL;}/*检查芯片虚拟通道与真实通道的映射,看有没有有效且未被使用的真实通道*/for(ch=0;ch<dma_channels;ch++){if(!is_channel_valid(ch_map->channels[ch]))continue;if(s3c2410_chans[ch].in_use==0){printk("mappedchannel%dto%d/n",channel,ch);break;}}if(ch>=dma_channels)returnNULL;/*updateourchannelmapping*/found:/*将找到的通道保存在dmach中,并返回*/dmach=&s3c2410_chans[ch];dmach->map=ch_map;dma_chan_map[channel]=dmach;/*selectthechannel*//*调用选择通道的函数*/(dma_sel.select)(dmach,ch_map);returndmach;}

设置寄存器,设置寄存器的工作由s3c2410_dma_devconfig和s3c2410_dma_config完成:

[c-sharp]view plaincopyprint?

    ints3c2410_dma_devconfig(intchannel,enums3c2410_dmasrcsource,inthwcfg,unsignedlongdevaddr){/*根据虚拟通道号找到真实通道*/structs3c2410_dma_chan*chan=lookup_dma_channel(channel);if(chan==NULL)return-EINVAL;pr_debug("%s:source=%d,hwcfg=%08x,devaddr=%08lx/n",__func__,(int)source,hwcfg,devaddr);chan->source=source;//保存dma源chan->dev_addr=devaddr;//保存dma源地址chan->hw_cfg=hwcfg;//保存dma源的控制信息switch(source){caseS3C2410_DMASRC_HW://源是外设,从外设读数据到内存,源的地址是固定的/*sourceishardware*/pr_debug("%s:hwsource,devaddr=%08lx,hwcfg=%d/n",__func__,devaddr,hwcfg);dma_wrreg(chan,S3C2410_DMA_DISRCC,hwcfg&3);//初始化源控制寄存器dma_wrreg(chan,S3C2410_DMA_DISRC,devaddr);//将源地址写入初始源寄存器dma_wrreg(chan,S3C2410_DMA_DIDSTC,(0<<1)|(0<<0));//目的地在AHB总线上chan->addr_reg=dma_regaddr(chan,S3C2410_DMA_DIDST);break;caseS3C2410_DMASRC_MEM://源是内存,从内存读数据到外设上,目的地址是固定的/*sourceismemory*/pr_debug("%s:memsource,devaddr=%08lx,hwcfg=%d/n",__func__,devaddr,hwcfg);dma_wrreg(chan,S3C2410_DMA_DISRCC,(0<<1)|(0<<0));//目的地址在AHB总线上dma_wrreg(chan,S3C2410_DMA_DIDST,devaddr);//把目的地址写到初始目的寄存器dma_wrreg(chan,S3C2410_DMA_DIDSTC,hwcfg&3);//初始化目的控制寄存器chan->addr_reg=dma_regaddr(chan,S3C2410_DMA_DISRC);break;/*无论内存是源还是目的,这个地址始终是保存在chan->addr_reg*/default:printk(KERN_ERR"dma%d:invalidsourcetype(%d)/n",channel,source);return-EINVAL;}if(dma_sel.direction!=NULL)(dma_sel.direction)(chan,chan->map,source);return0;}

[c-sharp]view plaincopyprint?

    ints3c2410_dma_devconfig(intchannel,enums3c2410_dmasrcsource,inthwcfg,unsignedlongdevaddr){/*根据虚拟通道号找到真实通道*/structs3c2410_dma_chan*chan=lookup_dma_channel(channel);if(chan==NULL)return-EINVAL;pr_debug("%s:source=%d,hwcfg=%08x,devaddr=%08lx/n",__func__,(int)source,hwcfg,devaddr);chan->source=source;//保存dma源chan->dev_addr=devaddr;//保存dma源地址chan->hw_cfg=hwcfg;//保存dma源的控制信息switch(source){caseS3C2410_DMASRC_HW://源是外设,从外设读数据到内存,源的地址是固定的/*sourceishardware*/pr_debug("%s:hwsource,devaddr=%08lx,hwcfg=%d/n",__func__,devaddr,hwcfg);dma_wrreg(chan,S3C2410_DMA_DISRCC,hwcfg&3);//初始化源控制寄存器dma_wrreg(chan,S3C2410_DMA_DISRC,devaddr);//将源地址写入初始源寄存器dma_wrreg(chan,S3C2410_DMA_DIDSTC,(0<<1)|(0<<0));//目的地在AHB总线上chan->addr_reg=dma_regaddr(chan,S3C2410_DMA_DIDST);break;caseS3C2410_DMASRC_MEM://源是内存,从内存读数据到外设上,目的地址是固定的/*sourceismemory*/pr_debug("%s:memsource,devaddr=%08lx,hwcfg=%d/n",__func__,devaddr,hwcfg);dma_wrreg(chan,S3C2410_DMA_DISRCC,(0<<1)|(0<<0));//目的地址在AHB总线上dma_wrreg(chan,S3C2410_DMA_DIDST,devaddr);//把目的地址写到初始目的寄存器dma_wrreg(chan,S3C2410_DMA_DIDSTC,hwcfg&3);//初始化目的控制寄存器chan->addr_reg=dma_regaddr(chan,S3C2410_DMA_DISRC);break;/*无论内存是源还是目的,这个地址始终是保存在chan->addr_reg*/default:printk(KERN_ERR"dma%d:invalidsourcetype(%d)/n",channel,source);return-EINVAL;}if(dma_sel.direction!=NULL)(dma_sel.direction)(chan,chan->map,source);return0;}

[c-sharp]view plaincopyprint?

    ints3c2410_dma_config(unsignedintchannel,intxferunit,intdcon){/*找到虚拟通道对应的实际通道*/structs3c2410_dma_chan*chan=lookup_dma_channel(channel);pr_debug("%s:chan=%d,xfer_unit=%d,dcon=%08x/n",__func__,channel,xferunit,dcon);if(chan==NULL)return-EINVAL;pr_debug("%s:Initialdconis%08x/n",__func__,dcon);/*清除DMA源的选择位*/dcon|=chan->dcon&dma_sel.dcon_mask;pr_debug("%s:Newdconis%08x/n",__func__,dcon);/*传输数据的大小*/switch(xferunit){case1:dcon|=S3C2410_DCON_BYTE;break;case2:dcon|=S3C2410_DCON_HALFWORD;break;case4:dcon|=S3C2410_DCON_WORD;break;default:pr_debug("%s:badtransfersize%d/n",__func__,xferunit);return-EINVAL;}dcon|=S3C2410_DCON_HWTRIG;//DMA源是硬件dcon|=S3C2410_DCON_INTREQ;//中断使能pr_debug("%s:dconnow%08x/n",__func__,dcon);/*将通道控制寄存器和传输大小存于chan中*/chan->dcon=dcon;chan->xfer_unit=xferunit;return0;}

[c-sharp]view plaincopyprint?

    ints3c2410_dma_config(unsignedintchannel,intxferunit,intdcon){/*找到虚拟通道对应的实际通道*/structs3c2410_dma_chan*chan=lookup_dma_channel(channel);pr_debug("%s:chan=%d,xfer_unit=%d,dcon=%08x/n",__func__,channel,xferunit,dcon);if(chan==NULL)return-EINVAL;pr_debug("%s:Initialdconis%08x/n",__func__,dcon);/*清除DMA源的选择位*/dcon|=chan->dcon&dma_sel.dcon_mask;pr_debug("%s:Newdconis%08x/n",__func__,dcon);/*传输数据的大小*/switch(xferunit){case1:dcon|=S3C2410_DCON_BYTE;break;case2:dcon|=S3C2410_DCON_HALFWORD;break;case4:dcon|=S3C2410_DCON_WORD;break;default:pr_debug("%s:badtransfersize%d/n",__func__,xferunit);return-EINVAL;}dcon|=S3C2410_DCON_HWTRIG;//DMA源是硬件dcon|=S3C2410_DCON_INTREQ;//中断使能pr_debug("%s:dconnow%08x/n",__func__,dcon);/*将通道控制寄存器和传输大小存于chan中*/chan->dcon=dcon;chan->xfer_unit=xferunit;return0;}

设置回调函数:

[c-sharp]view plaincopyprint?

    ints3c2410_dma_set_buffdone_fn(unsignedintchannel,s3c2410_dma_cbfn_trtn){。。。。。。。chan->callback_fn=rtn;return0;}

[c-sharp]view plaincopyprint?

    ints3c2410_dma_set_buffdone_fn(unsignedintchannel,s3c2410_dma_cbfn_trtn){。。。。。。。chan->callback_fn=rtn;return0;}

设置标志:

[c-sharp]view plaincopyprint?

    ints3c2410_dma_setflags(unsignedintchannel,unsignedintflags){。。。。。。。。。。。。。。chan->flags=flags;return0;}

[c-sharp]view plaincopyprint?

    ints3c2410_dma_setflags(unsignedintchannel,unsignedintflags){。。。。。。。。。。。。。。chan->flags=flags;return0;}

将数据放入队列,先看一下一个结构:

[c-sharp]view plaincopyprint?

    structs3c2410_dma_buf{structs3c2410_dma_buf*next;intmagic;/*magic*/intsize;/*buffersizeinbytes*/dma_addr_tdata;/*startofDMAdata*/dma_addr_tptr;/*wheretheDMAgotto[1]*/void*id;/*client’sid*/};

[c-sharp]view plaincopyprint?

    structs3c2410_dma_buf{structs3c2410_dma_buf*next;intmagic;/*magic*/intsize;/*buffersizeinbytes*/dma_addr_tdata;/*startofDMAdata*/dma_addr_tptr;/*wheretheDMAgotto[1]*/void*id;/*client’sid*/};

每个struct s3c2410_dma_chan维护了一个缓冲区队列,每个缓冲区用上边的结构表示。在struct s3c2410_dma_chan中的结构是:

[c-sharp]view plaincopyprint?

    /*bufferlistandinformation*/structs3c2410_dma_buf*curr;/*currentdmabuffer*/structs3c2410_dma_buf*next;/*nextbuffertoload*/structs3c2410_dma_buf*end;/*endofqueue*/

[c-sharp]view plaincopyprint?

    /*bufferlistandinformation*/structs3c2410_dma_buf*curr;/*currentdmabuffer*/structs3c2410_dma_buf*next;/*nextbuffertoload*/structs3c2410_dma_buf*end;/*endofqueue*/

下边这个函数就是完成将s3c2410_dma_buf放入这个队列中排队:

[c-sharp]view plaincopyprint?

    ints3c2410_dma_enqueue(unsignedintchannel,void*id,dma_addr_tdata,intsize){/*找到虚拟通道对应的实际通道*/structs3c2410_dma_chan*chan=lookup_dma_channel(channel);structs3c2410_dma_buf*buf;unsignedlongflags;if(chan==NULL)return-EINVAL;pr_debug("%s:id=%p,data=%08x,size=%d/n",__func__,id,(unsignedint)data,size);/*分配s3c2410_dma_chan结构的buffer*/buf=kmem_cache_alloc(dma_kmem,GFP_ATOMIC);if(buf==NULL){pr_debug("%s:outofmemory(%ldalloc)/n",__func__,(long)sizeof(*buf));return-ENOMEM;}//pr_debug("%s:newbuffer%p/n",__func__,buf);//dbg_showchan(chan);/*设置这个buffer*/buf->next=NULL;buf->data=buf->ptr=data;//指向要传输数据的地址buf->size=size;//该段buffer的大小buf->id=id;buf->magic=BUF_MAGIC;local_irq_save(flags);/*加载的是该通道的第一段buf*/if(chan->curr==NULL){/*we’vegotnothingloaded…*/pr_debug("%s:buffer%pqueuedontoemptychannel/n",__func__,buf);chan->curr=buf;//curr指向现在生成的bufchan->end=buf;chan->next=NULL;}else{pr_debug("dma%d:%s:buffer%pqueuedontonon-emptychannel/n",chan->number,__func__,buf);if(chan->end==NULL)pr_debug("dma%d:%s:%pnotempty,andchan->end==NULL?/n",chan->number,__func__,chan);/*从链表尾加入链表*/chan->end->next=buf;chan->end=buf;}/*ifnecessary,updatethenextbufferfield*/if(chan->next==NULL)chan->next=buf;if(chan->state==S3C2410_DMA_RUNNING){//该channel正在运行if(chan->load_state==S3C2410_DMALOAD_1LOADED&&1){//已有bufload了if(s3c2410_dma_waitforload(chan,__LINE__)==0){//等待loadprintk(KERN_ERR"dma%d:loadbuffer:""timeoutloadingbuffer/n",chan->number);dbg_showchan(chan);local_irq_restore(flags);return-EINVAL;}}while(s3c2410_dma_canload(chan)&&chan->next!=NULL){//检查能否loads3c2410_dma_loadbuffer(chan,chan->next);//loadbuffer}}elseif(chan->state==S3C2410_DMA_IDLE){//该channel空闲着if(chan->flags&S3C2410_DMAF_AUTOSTART){//如果设了自动启动标记,则直接启动该次传输s3c2410_dma_ctrl(chan->number|DMACH_LOW_LEVEL,//启动传输S3C2410_DMAOP_START);}}local_irq_restore(flags);return0;}

[c-sharp]view plaincopyprint?

    ints3c2410_dma_enqueue(unsignedintchannel,void*id,dma_addr_tdata,intsize){/*找到虚拟通道对应的实际通道*/structs3c2410_dma_chan*chan=lookup_dma_channel(channel);structs3c2410_dma_buf*buf;unsignedlongflags;if(chan==NULL)return-EINVAL;pr_debug("%s:id=%p,data=%08x,size=%d/n",__func__,id,(unsignedint)data,size);/*分配s3c2410_dma_chan结构的buffer*/buf=kmem_cache_alloc(dma_kmem,GFP_ATOMIC);if(buf==NULL){pr_debug("%s:outofmemory(%ldalloc)/n",__func__,(long)sizeof(*buf));return-ENOMEM;}//pr_debug("%s:newbuffer%p/n",__func__,buf);//dbg_showchan(chan);/*设置这个buffer*/buf->next=NULL;buf->data=buf->ptr=data;//指向要传输数据的地址buf->size=size;//该段buffer的大小buf->id=id;buf->magic=BUF_MAGIC;local_irq_save(flags);/*加载的是该通道的第一段buf*/if(chan->curr==NULL){/*we’vegotnothingloaded…*/pr_debug("%s:buffer%pqueuedontoemptychannel/n",__func__,buf);chan->curr=buf;//curr指向现在生成的bufchan->end=buf;chan->next=NULL;}else{pr_debug("dma%d:%s:buffer%pqueuedontonon-emptychannel/n",chan->number,__func__,buf);if(chan->end==NULL)pr_debug("dma%d:%s:%pnotempty,andchan->end==NULL?/n",chan->number,__func__,chan);/*从链表尾加入链表*/chan->end->next=buf;chan->end=buf;}/*ifnecessary,updatethenextbufferfield*/if(chan->next==NULL)chan->next=buf;if(chan->state==S3C2410_DMA_RUNNING){//该channel正在运行if(chan->load_state==S3C2410_DMALOAD_1LOADED&&1){//已有bufload了if(s3c2410_dma_waitforload(chan,__LINE__)==0){//等待loadprintk(KERN_ERR"dma%d:loadbuffer:""timeoutloadingbuffer/n",chan->number);dbg_showchan(chan);local_irq_restore(flags);return-EINVAL;}}while(s3c2410_dma_canload(chan)&&chan->next!=NULL){//检查能否loads3c2410_dma_loadbuffer(chan,chan->next);//loadbuffer}}elseif(chan->state==S3C2410_DMA_IDLE){//该channel空闲着if(chan->flags&S3C2410_DMAF_AUTOSTART){//如果设了自动启动标记,则直接启动该次传输s3c2410_dma_ctrl(chan->number|DMACH_LOW_LEVEL,//启动传输S3C2410_DMAOP_START);}}local_irq_restore(flags);return0;}

channel在运行的时候会有很多状态,在arch/arm/mach-s3c2410/include/mach/dma.h,注意已经很清楚了,我就不多解释了。

[c-sharp]view plaincopyprint?

    /*enums3c2410_dma_loadst**ThisrepresentsthestateoftheDMAengine,wrttotheloaded/running*transfers.Sincewedon’thaveanywayofknowingexactlythestateof*theDMAtransfers,weneedtoknowthestatetomakedecisionsonwether*wecan**S3C2410_DMA_NONE**Therearenobuffersloaded(thechannelshouldbeinactive)**S3C2410_DMA_1LOADED**Thereisonebufferloaded,howeverithasnotbeenconfirmedtobe*loadedbytheDMAengine.Thismaybebecausethechannelisnot*yetrunning,ortheDMAdriverdecidedthatitwastoocostlyto*sitandwaitforittohappen.**S3C2410_DMA_1RUNNING**Thebufferhasbeenconfirmedrunning,andnotfinisged**S3C2410_DMA_1LOADED_1RUNNING**ThereisabufferwaitingtobeloadedbytheDMAengine,andone*currentlyrunning.*/enums3c2410_dma_loadst{S3C2410_DMALOAD_NONE,S3C2410_DMALOAD_1LOADED,S3C2410_DMALOAD_1RUNNING,S3C2410_DMALOAD_1LOADED_1RUNNING,};

[c-sharp]view plaincopyprint?

    /*enums3c2410_dma_loadst**ThisrepresentsthestateoftheDMAengine,wrttotheloaded/running*transfers.Sincewedon’thaveanywayofknowingexactlythestateof*theDMAtransfers,weneedtoknowthestatetomakedecisionsonwether*wecan**S3C2410_DMA_NONE**Therearenobuffersloaded(thechannelshouldbeinactive)**S3C2410_DMA_1LOADED**Thereisonebufferloaded,howeverithasnotbeenconfirmedtobe*loadedbytheDMAengine.Thismaybebecausethechannelisnot*yetrunning,ortheDMAdriverdecidedthatitwastoocostlyto*sitandwaitforittohappen.**S3C2410_DMA_1RUNNING**Thebufferhasbeenconfirmedrunning,andnotfinisged**S3C2410_DMA_1LOADED_1RUNNING**ThereisabufferwaitingtobeloadedbytheDMAengine,andone*currentlyrunning.*/enums3c2410_dma_loadst{S3C2410_DMALOAD_NONE,S3C2410_DMALOAD_1LOADED,S3C2410_DMALOAD_1RUNNING,S3C2410_DMALOAD_1LOADED_1RUNNING,};

中断处理函数:

[c-sharp]view plaincopyprint?

    staticirqreturn_ts3c2410_dma_irq(intirq,void*devpw){structs3c2410_dma_chan*chan=(structs3c2410_dma_chan*)devpw;structs3c2410_dma_buf*buf;buf=chan->curr;//当前传输完毕的bufdbg_showchan(chan);/*modifythechannelstate*/switch(chan->load_state){//改变状态,如果对上边那4个状态理解了很容易看懂的caseS3C2410_DMALOAD_1RUNNING:/*TODO-ifwearerunningonlyonebuffer,weprobably*wanttoreloadhere,andthenworryaboutthebuffer*callback*/chan->load_state=S3C2410_DMALOAD_NONE;break;caseS3C2410_DMALOAD_1LOADED:/*iirc,weshouldgobacktoNONEloadedhere,we*hadabuffer,anditwasneververifiedasbeing*loaded.*/chan->load_state=S3C2410_DMALOAD_NONE;break;caseS3C2410_DMALOAD_1LOADED_1RUNNING:/*we’llworryaboutcheckingtoseeifanotherbufferis*readyafterwe’vecalledbacktheowner.Thisshould*ensurewedonotwaitaroundtoolongfortheDMA*enginetostartthenexttransfer*/chan->load_state=S3C2410_DMALOAD_1LOADED;break;caseS3C2410_DMALOAD_NONE:printk(KERN_ERR"dma%d:IRQwithnoloadedbuffer?/n",chan->number);break;default:printk(KERN_ERR"dma%d:IRQininvalidload_state%d/n",chan->number,chan->load_state);break;}if(buf!=NULL){//如果不为空/*updatethechaintomakesurethatifweloadanymore*bufferswhenwecallthecallbackfunction,thingsshould*workproperly*/chan->curr=buf->next;//指向下一个bufbuf->next=NULL;if(buf->magic!=BUF_MAGIC){printk(KERN_ERR"dma%d:%s:buf%pincorrectmagic/n",chan->number,__func__,buf);returnIRQ_HANDLED;}s3c2410_dma_buffdone(chan,buf,S3C2410_RES_OK);//buf传输完成后的操作/*freeresouces*/s3c2410_dma_freebuf(buf);//释放buf}else{}/*onlyreloadifthechannelisstillrunning…ourbufferdone*routinemayhavealteredthestatebyrequestingthedmachannel*tostoporshutdown…*//*todo:checkthatwhenthechannelisshut-downfrominsidethis*function,wecopewithunsettingreload,etc*/if(chan->next!=NULL&&chan->state!=S3C2410_DMA_IDLE){//还有要传输的buf,则继续传输unsignedlongflags;switch(chan->load_state){caseS3C2410_DMALOAD_1RUNNING:/*don’tneedtodoanythingforthisstate*/break;caseS3C2410_DMALOAD_NONE:/*canloadbufferimmediately*/break;caseS3C2410_DMALOAD_1LOADED:if(s3c2410_dma_waitforload(chan,__LINE__)==0){//如果已经有载入的,则等待被载入/*flagerror?*/printk(KERN_ERR"dma%d:timeoutwaitingforload(%s)/n",chan->number,__func__);returnIRQ_HANDLED;}break;caseS3C2410_DMALOAD_1LOADED_1RUNNING:gotono_load;default:printk(KERN_ERR"dma%d:unknownload_stateinirq,%d/n",chan->number,chan->load_state);returnIRQ_HANDLED;}local_irq_save(flags);s3c2410_dma_loadbuffer(chan,chan->next);//载入buflocal_irq_restore(flags);}else{//所有传输完成s3c2410_dma_lastxfer(chan);//完成处理工作/*seeifwecanstopthischannel..*/if(chan->load_state==S3C2410_DMALOAD_NONE){pr_debug("dma%d:endoftransfer,stoppingchannel(%ld)/n",chan->number,jiffies);s3c2410_dma_ctrl(chan->number|DMACH_LOW_LEVEL,//停止DMA传输S3C2410_DMAOP_STOP);}}no_load:returnIRQ_HANDLED;}

[c-sharp]view plaincopyprint?

    staticirqreturn_ts3c2410_dma_irq(intirq,void*devpw){structs3c2410_dma_chan*chan=(structs3c2410_dma_chan*)devpw;structs3c2410_dma_buf*buf;buf=chan->curr;//当前传输完毕的bufdbg_showchan(chan);/*modifythechannelstate*/switch(chan->load_state){//改变状态,如果对上边那4个状态理解了很容易看懂的caseS3C2410_DMALOAD_1RUNNING:/*TODO-ifwearerunningonlyonebuffer,weprobably*wanttoreloadhere,andthenworryaboutthebuffer*callback*/chan->load_state=S3C2410_DMALOAD_NONE;break;caseS3C2410_DMALOAD_1LOADED:/*iirc,weshouldgobacktoNONEloadedhere,we*hadabuffer,anditwasneververifiedasbeing*loaded.*/chan->load_state=S3C2410_DMALOAD_NONE;break;caseS3C2410_DMALOAD_1LOADED_1RUNNING:/*we’llworryaboutcheckingtoseeifanotherbufferis*readyafterwe’vecalledbacktheowner.Thisshould*ensurewedonotwaitaroundtoolongfortheDMA*enginetostartthenexttransfer*/chan->load_state=S3C2410_DMALOAD_1LOADED;break;caseS3C2410_DMALOAD_NONE:printk(KERN_ERR"dma%d:IRQwithnoloadedbuffer?/n",chan->number);break;default:printk(KERN_ERR"dma%d:IRQininvalidload_state%d/n",chan->number,chan->load_state);break;}if(buf!=NULL){//如果不为空/*updatethechaintomakesurethatifweloadanymore*bufferswhenwecallthecallbackfunction,thingsshould*workproperly*/chan->curr=buf->next;//指向下一个bufbuf->next=NULL;if(buf->magic!=BUF_MAGIC){printk(KERN_ERR"dma%d:%s:buf%pincorrectmagic/n",chan->number,__func__,buf);returnIRQ_HANDLED;}s3c2410_dma_buffdone(chan,buf,S3C2410_RES_OK);//buf传输完成后的操作/*freeresouces*/s3c2410_dma_freebuf(buf);//释放buf}else{}/*onlyreloadifthechannelisstillrunning…ourbufferdone*routinemayhavealteredthestatebyrequestingthedmachannel*tostoporshutdown…*//*todo:checkthatwhenthechannelisshut-downfrominsidethis*function,wecopewithunsettingreload,etc*/if(chan->next!=NULL&&chan->state!=S3C2410_DMA_IDLE){//还有要传输的buf,则继续传输unsignedlongflags;switch(chan->load_state){caseS3C2410_DMALOAD_1RUNNING:/*don’tneedtodoanythingforthisstate*/break;caseS3C2410_DMALOAD_NONE:/*canloadbufferimmediately*/break;caseS3C2410_DMALOAD_1LOADED:if(s3c2410_dma_waitforload(chan,__LINE__)==0){//如果已经有载入的,则等待被载入/*flagerror?*/printk(KERN_ERR"dma%d:timeoutwaitingforload(%s)/n",chan->number,__func__);returnIRQ_HANDLED;}break;caseS3C2410_DMALOAD_1LOADED_1RUNNING:gotono_load;default:printk(KERN_ERR"dma%d:unknownload_stateinirq,%d/n",chan->number,chan->load_state);returnIRQ_HANDLED;}local_irq_save(flags);s3c2410_dma_loadbuffer(chan,chan->next);//载入buflocal_irq_restore(flags);}else{//所有传输完成s3c2410_dma_lastxfer(chan);//完成处理工作/*seeifwecanstopthischannel..*/if(chan->load_state==S3C2410_DMALOAD_NONE){pr_debug("dma%d:endoftransfer,stoppingchannel(%ld)/n",chan->number,jiffies);s3c2410_dma_ctrl(chan->number|DMACH_LOW_LEVEL,//停止DMA传输S3C2410_DMAOP_STOP);}}no_load:returnIRQ_HANDLED;}

可以选择不同的dma操作:

[c-sharp]view plaincopyprint?

    ints3c2410_dma_ctrl(unsignedintchannel,enums3c2410_chan_opop){structs3c2410_dma_chan*chan=lookup_dma_channel(channel);if(chan==NULL)return-EINVAL;switch(op){caseS3C2410_DMAOP_START:returns3c2410_dma_start(chan);caseS3C2410_DMAOP_STOP:returns3c2410_dma_dostop(chan);caseS3C2410_DMAOP_PAUSE:caseS3C2410_DMAOP_RESUME:return-ENOENT;caseS3C2410_DMAOP_FLUSH:returns3c2410_dma_flush(chan);caseS3C2410_DMAOP_STARTED:returns3c2410_dma_started(chan);caseS3C2410_DMAOP_TIMEOUT:return0;}return-ENOENT;/*unknown,don’tbother*/}

只需勇敢前行,梦想自会引路,有多远,走多远,把足迹连成生命线。

Linux驱动修炼之道

相关文章:

你感兴趣的文章:

标签云: