s3c2410的dma操作的一般步骤
s3c2410的dma操作的一般步骤
一般的,在s3c2440中,要想进行dma传输,需要一下七个步骤:
一:
/* s3c2410_request_dma
*
* get control of an dma channel
*/
int s3c2410_dma_request(unsigned int channel,
struct s3c2410_dma_client * client,
void * dev)
s3c2410_dma_client的定义为:
struct s3c2410_dma_client {
char * name;
};
二:
/* DMA configuration for each channel
*
* DISRCC -> source of the DMA (AHB(系统总线),APB(外围总线))
* DISRC -> source address of the DMA
* DIDSTC -> destination of the DMA (AHB,APD)
* DIDST -> destination address of the DMA
*/
/* s3c2410_dma_config
*
* xfersize: size of unit in bytes (1,2,4)
* dcon: base value of the DCONx register
*/
int s3c2410_dma_config(dmach_t channel,
int xferunit,
int dcon)
根据xferunit以及dcon设置通道的控制寄存器DCONx
xferunit为每次传输的数据大小: 0 : byte 1 :half word 2 :word
每个 DMA 通道都有 9 个控制寄存器( 4 个通道 DMA 控制器共计 36 个寄存器)。
6 个寄存器用来控制 DMA 传输,其他三个监视 DMA 控制器的状态。这些寄存器的详细情况如下:
( 1 )DMA 初始源寄存器(DISRC)
( 2 )DMA初始源控制寄存器(DISRCC)
( 3 )DMA初始目的寄存器(DIDST)
( 4 )DMA初始目的控制寄存器(DIDSTC)
( 5 )DMA控制寄存器(DCON)
( 6 )DMA状态寄存器(DSTAT)
( 7 )DMA当前源寄存器(DCSRC)
( 8 )DMA当前目的寄存器(DCDST)
( 9 )DMA屏蔽触发寄存器(DMASKTRIG)
三:
int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn)
{
struct s3c2410_dma_chan * chan = lookup_dma_channel(channel);
if (chan == NULL)
return - EINVAL;
pr_debug( " %s: chan=%p, callback rtn=%p\n " , __func__, chan, rtn);
chan -> callback_fn = rtn;
return 0 ;
}
设置相应的dma通道完成一次dma传输后的回调函数,也即是s3c2410_dma_enqueue完成后会调用的函数
回调函数应具有一下格式:
/* s3c2410_dma_cbfn_t
*
* buffer callback routine type
*/
typedef void ( * s3c2410_dma_cbfn_t)( struct s3c2410_dma_chan * ,
void * buf, int size,
enum s3c2410_dma_buffresult result);
buf可以传递一些有用的数据,在uda1314的驱动中,传递的是audio_buf_t结构体
四:
int s3c2410_dma_setflags(dmach_t channel, unsigned int flags)
{
struct s3c2410_dma_chan * chan = lookup_dma_channel(channel);
if (chan == NULL)
return - EINVAL;
pr_debug( " %s: chan=%p, flags=%08x\n " , __func__, chan, flags);
chan -> flags = flags;
return 0 ;
}
五:
/* s3c2410_dma_devconfig
*
* configure the dma source/destination hardware type and address
*
* source: S3C2410_DMASRC_HW: source is hardware
* S3C2410_DMASRC_MEM: source is memory
*
* hwcfg: the value for xxxSTCn register,
* bit 0: 0=increment pointer, 1=leave pointer
* bit 1: 0=source is AHB, 1=source is APB
*
* devaddr: physical address of the source
*/
int s3c2410_dma_devconfig( int channel,
enum s3c2410_dmasrc source,
int hwcfg,
unsigned long devaddr)
六:
/*
* Allocate memory for a coherent(连续的) mapping.
*/
void * dma_alloc_coherent( struct device * dev,size_t size,dma_addr_t * dma_handle, int flag)
如:
void * pLogicAddr = NULL; /* 逻辑地址 */
dma_addr_t pBusAddr = NULL; /* 总线地址(实际物理地址) */
pLogicAddr = dma_alloc_coherent(NULL, 480 * 640 , & pBusAddr, GFP_KERNEL | GFP_DMA);
该函数实际获得两个地址,
1 、函数的返回值是一个void * ,代表缓冲区的内核虚拟地址
2 、相关的总线地址,保存在dma_handle中
dma关心的是总线地址。
物理地址与总线地址
1 ) 物理地址是与CPU相关的。在CPU的地址信号线上产生的就是物理地址。在程序指令中的虚拟地址经过段映射和页面映射后,就生成了物理地址,这个物理地址被放到CPU的地址线上。
2 ) 总线地址,顾名思义,是与总线相关的,就是总线的地址线或在地址周期上产生的信号。外设使用的是总线地址。
3 ) 物理地址与总线地址之间的关系由系统的设计决定的。在x86平台上,物理地址与PCI总线地址是相同的。在其他平台上,也许会有某种转换,通常是线性的转换。
七:
/* s3c2410_dma_enqueue
*
* queue an given buffer for dma transfer.
*
* id the device driver's id information for this buffer
* data the physical address of the buffer data
* size the size of the buffer in bytes
*
* If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART
* is checked, and if set, the channel is started. If this flag isn't set,
* then an error will be returned.
*
* It is possible to queue more than one DMA buffer onto a channel at
* once, and the code will deal with the re-loading of the next buffer
* when necessary.
*/
int s3c2410_dma_enqueue(unsigned int channel, void * id,dma_addr_t data, int size)
将dma_alloc_coherent中得到的dmaphys传递给s3c2410_dma_enqueue.
s3c2410_dma_enqueue提交一次dma请求,当dma通道可用的时候通过s3c2410_dma_loadbuffer开始一次传输,传输完成后会产生irq中断。其dma的中断服务函数中会继续启动dma请求队列中的请求,传输剩下的数据。
/* s3c2410_dma_buffdone
*
* small wrapper to check if callback routine needs to be called, and
* if so, call it
*/
static inline void s3c2410_dma_buffdone( struct s3c2410_dma_chan * chan, struct s3c2410_dma_buf * buf,
enum s3c2410_dma_buffresult result)
{
if (chan -> callback_fn != NULL) {
(chan -> callback_fn)(chan, buf -> id, buf -> size, result);
}
}
其中 s3c2410_dma_irq 会调用 s3c2410_dma_buffdone;
static irqreturn_t s3c2410_dma_irq( int irq, void * devpw)
/* s3c2410_request_dma
*
* get control of an dma channel
*/
int s3c2410_dma_request(unsigned int channel,
struct s3c2410_dma_client * client,
void * dev)
{
struct s3c2410_dma_chan * chan;
unsigned long flags;
int err;
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 : requesting irq %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: cannot get IRQ %d for DMA %d\n " ,
client -> name, chan -> irq, chan -> number);
return err;
}
chan -> irq_enabled = 1 ;
}
local_irq_restore(flags);
/* need to setup */
pr_debug( " %s: channel initialised, %p\n " , __func__, chan);
return chan -> number | DMACH_LOW_LEVEL;
}
/* *
* request_irq - allocate an interrupt line
* @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs
* @irqflags: Interrupt type flags
* @devname: An ascii name for the claiming device
* @dev_id: A cookie passed back to the handler function
*
* This call allocates interrupt resources and enables the
* interrupt line and IRQ handling. From the point this
* call is made your handler function may be invoked. Since
* your handler function must clear any interrupt the board
* raises, you must take care both to initialise your hardware
* and to set up the interrupt handler in the right order.
*
* Dev_id must be globally unique. Normally the address of the
* device data structure is used as the cookie. Since the handler
* receives this value it makes sense to use it.
*
* If your interrupt is shared you must pass a non NULL dev_id
* as this is required when freeing the interrupt.
*
* Flags:
*
* IRQF_SHARED Interrupt is shared
* IRQF_DISABLED Disable local interrupts while processing
* IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
*
*/
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char * devname, void * dev_id)
{
struct irqaction * action;
int retval;
action = kmalloc( sizeof ( struct irqaction), GFP_ATOMIC);
if ( ! action)
return - ENOMEM;
action -> handler = handler;
action -> flags = irqflags;
cpus_clear(action -> mask);
action -> name = devname;
action -> next = NULL;
action -> dev_id = dev_id;
select_smp_affinity(irq);
retval = setup_irq(irq, action);
if (retval)
kfree(action);
return retval;
}
/* s3c2410_dma_loadbuffer
*
* load a buffer, and update the channel state
*/
static inline int s3c2410_dma_loadbuffer( struct s3c2410_dma_chan * chan,
struct s3c2410_dma_buf * buf)
{
unsigned long reload;
pr_debug( " s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n " ,
buf, (unsigned long )buf -> data, buf -> size);
if (buf == NULL) {
dmawarn( " buffer is NULL\n " );
return - EINVAL;
}
/* check the state of the channel before we do anything */
if (chan -> load_state == S3C2410_DMALOAD_1LOADED) {
dmawarn( " load_state is S3C2410_DMALOAD_1LOADED\n " );
}
if (chan -> load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) {
dmawarn( " state is S3C2410_DMALOAD_1LOADED_1RUNNING\n " );
}
/* it would seem sensible if we are the last buffer to not bother
* with the auto-reload bit, so that the DMA engine will not try
* and load another transfer after this one has finished
*/
if (chan -> load_state == S3C2410_DMALOAD_NONE) {
pr_debug( " load_state is none, checking for noreload (next=%p)\n " ,
buf -> next);
reload = (buf -> next == NULL) ? S3C2410_DCON_NORELOAD : 0 ;
} else {
// pr_debug("load_state is %d => autoreload\n", chan->load_state);
reload = S3C2410_DCON_AUTORELOAD;
}
if ((buf -> data & 0xf0000000 ) != 0x30000000 ) {
dmawarn( " dmaload: buffer is %p\n " , ( void * )buf -> data);
}
writel(buf -> data, chan -> addr_reg);
dma_wrreg(chan, S3C2410_DMA_DCON,
chan -> dcon | reload | (buf -> size / chan -> xfer_unit));
chan -> next = buf -> next;
/* update the state of the channel */
switch (chan -> load_state) {
case S3C2410_DMALOAD_NONE:
chan -> load_state = S3C2410_DMALOAD_1LOADED;
break ;
case S3C2410_DMALOAD_1RUNNING:
chan -> load_state = S3C2410_DMALOAD_1LOADED_1RUNNING;
break ;
default :
dmawarn( " dmaload: unknown state %d in loadbuffer\n " ,
chan -> load_state);
break ;
}
return 0 ;
}
============================================
一些相关函数的说明。供自己日后参考。
--------------------------------------------------------------------------------------
int __init s3c24xx_dma_init_map( struct s3c24xx_dma_selection * sel)
初始化全局变量dma_sel结构,在此函数中大家可能疑惑的是
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;
这几条语句,为什么用memcpy初始化为了dma_sel,而又要用再分配的nmap结构去给dma_sel赋值。其答案是为了保险,因为dma_sel 中的map成员是个指针。对指针的操作是要很小心的。这里在此函数中重新定义一个指针变量,并指向它,就是为了防止别处定义的指针被撤消而造成越界访问。其实我认为这没有必要的。
dma_sel的定义为struct s3c24xx_dma_selection dma_sel;
而struct s3c24xx_dma_selection的定义为
struct s3c24xx_dma_selection {
struct s3c24xx_dma_map * map;
unsigned long map_size;
unsigned long dcon_mask;
void ( * select)( struct s3c2410_dma_chan * chan,
struct s3c24xx_dma_map * map);
};
--------------------------------------------------------------------------------------------------------
struct s3c2410_dma_chan * s3c2410_dma_map_channel( int channel)
此函数功能是: turn the virtual channel number into a real, and un - used hardware channel.完成DMA通道的虚实映射
在s3c2440_dma_mappings数组中查找相应channel对应的通道( 0 -- 4 ,有效未使用),并将映射关系记入dma_chan_map[channel]数组中。
---------------------------------------------------------------------------------------------------------
static int __init s3c2410_init_dma( void )
用于初始化s3c2410_dma_chan结构,并完成地址映射。
-----------------------------------------------------------------------------------------------------------
static struct s3c2410_dma_chan * lookup_dma_channel(unsigned int channel)
DMA通道虚实转换
-----------------------------------------------------------------------------------------------------------
int s3c2410_dma_request(unsigned int channel,
struct s3c2410_dma_client * client,
void * dev)
通过s3c2410_dma_map_channel函数找到channel对应的通道,并设置相应的通道状态,申请中断
-----------------------------------------------------------------------------------------------------------
int s3c2410_dma_started( struct s3c2410_dma_chan * chan)
/* if we've only loaded one buffer onto the channel, then chec
* to see if we have another, and if so, try and load it so when
* the first buffer is finished, the new one will be loaded onto
* the channel */
-----------------------------------------------------------------------------------------------------------
int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client * client)
/* s3c2410_dma_free
*
* release the given channel back to the system, will stop and flush
* any outstanding transfers, and ensure the channel is ready for the
* next claimant.
*
* Note, although a warning is currently printed if the freeing client
* info is not the same as the registrant's client info, the free is still
* allowed to go through.
*/
--------------------------------------------------------------------------------------------------------------
static int s3c2410_dma_flush( struct s3c2410_dma_chan * chan)
* stop the channel, and remove all current and pending transfers
--------------------------------------------------------------------------------------------------------------
int s3c2410_dma_config(dmach_t channel,
int xferunit,
int dcon)
根据xferunit设置通道的控制寄存器
--------------------------------------------------------------------------------------------------------------
int s3c2410_dma_setflags(dmach_t channel, unsigned int flags)
根据flags设置通道的flags
--------------------------------------------------------------------------------------------------------------
int s3c2410_dma_ctrl(dmach_t channel, enum s3c2410_chan_op op);
0
一般的,在s3c2440中,要想进行dma传输,需要一下七个步骤:
一:
/* s3c2410_request_dma
*
* get control of an dma channel
*/
int s3c2410_dma_request(unsigned int channel,
struct s3c2410_dma_client * client,
void * dev)
s3c2410_dma_client的定义为:
struct s3c2410_dma_client {
char * name;
};
二:
/* DMA configuration for each channel
*
* DISRCC -> source of the DMA (AHB(系统总线),APB(外围总线))
* DISRC -> source address of the DMA
* DIDSTC -> destination of the DMA (AHB,APD)
* DIDST -> destination address of the DMA
*/
/* s3c2410_dma_config
*
* xfersize: size of unit in bytes (1,2,4)
* dcon: base value of the DCONx register
*/
int s3c2410_dma_config(dmach_t channel,
int xferunit,
int dcon)
根据xferunit以及dcon设置通道的控制寄存器DCONx
xferunit为每次传输的数据大小: 0 : byte 1 :half word 2 :word
每个 DMA 通道都有 9 个控制寄存器( 4 个通道 DMA 控制器共计 36 个寄存器)。
6 个寄存器用来控制 DMA 传输,其他三个监视 DMA 控制器的状态。这些寄存器的详细情况如下:
( 1 )DMA 初始源寄存器(DISRC)
( 2 )DMA初始源控制寄存器(DISRCC)
( 3 )DMA初始目的寄存器(DIDST)
( 4 )DMA初始目的控制寄存器(DIDSTC)
( 5 )DMA控制寄存器(DCON)
( 6 )DMA状态寄存器(DSTAT)
( 7 )DMA当前源寄存器(DCSRC)
( 8 )DMA当前目的寄存器(DCDST)
( 9 )DMA屏蔽触发寄存器(DMASKTRIG)
三:
int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn)
{
struct s3c2410_dma_chan * chan = lookup_dma_channel(channel);
if (chan == NULL)
return - EINVAL;
pr_debug( " %s: chan=%p, callback rtn=%p\n " , __func__, chan, rtn);
chan -> callback_fn = rtn;
return 0 ;
}
设置相应的dma通道完成一次dma传输后的回调函数,也即是s3c2410_dma_enqueue完成后会调用的函数
回调函数应具有一下格式:
/* s3c2410_dma_cbfn_t
*
* buffer callback routine type
*/
typedef void ( * s3c2410_dma_cbfn_t)( struct s3c2410_dma_chan * ,
void * buf, int size,
enum s3c2410_dma_buffresult result);
buf可以传递一些有用的数据,在uda1314的驱动中,传递的是audio_buf_t结构体
四:
int s3c2410_dma_setflags(dmach_t channel, unsigned int flags)
{
struct s3c2410_dma_chan * chan = lookup_dma_channel(channel);
if (chan == NULL)
return - EINVAL;
pr_debug( " %s: chan=%p, flags=%08x\n " , __func__, chan, flags);
chan -> flags = flags;
return 0 ;
}
五:
/* s3c2410_dma_devconfig
*
* configure the dma source/destination hardware type and address
*
* source: S3C2410_DMASRC_HW: source is hardware
* S3C2410_DMASRC_MEM: source is memory
*
* hwcfg: the value for xxxSTCn register,
* bit 0: 0=increment pointer, 1=leave pointer
* bit 1: 0=source is AHB, 1=source is APB
*
* devaddr: physical address of the source
*/
int s3c2410_dma_devconfig( int channel,
enum s3c2410_dmasrc source,
int hwcfg,
unsigned long devaddr)
六:
/*
* Allocate memory for a coherent(连续的) mapping.
*/
void * dma_alloc_coherent( struct device * dev,size_t size,dma_addr_t * dma_handle, int flag)
如:
void * pLogicAddr = NULL; /* 逻辑地址 */
dma_addr_t pBusAddr = NULL; /* 总线地址(实际物理地址) */
pLogicAddr = dma_alloc_coherent(NULL, 480 * 640 , & pBusAddr, GFP_KERNEL | GFP_DMA);
该函数实际获得两个地址,
1 、函数的返回值是一个void * ,代表缓冲区的内核虚拟地址
2 、相关的总线地址,保存在dma_handle中
dma关心的是总线地址。
物理地址与总线地址
1 ) 物理地址是与CPU相关的。在CPU的地址信号线上产生的就是物理地址。在程序指令中的虚拟地址经过段映射和页面映射后,就生成了物理地址,这个物理地址被放到CPU的地址线上。
2 ) 总线地址,顾名思义,是与总线相关的,就是总线的地址线或在地址周期上产生的信号。外设使用的是总线地址。
3 ) 物理地址与总线地址之间的关系由系统的设计决定的。在x86平台上,物理地址与PCI总线地址是相同的。在其他平台上,也许会有某种转换,通常是线性的转换。
七:
/* s3c2410_dma_enqueue
*
* queue an given buffer for dma transfer.
*
* id the device driver's id information for this buffer
* data the physical address of the buffer data
* size the size of the buffer in bytes
*
* If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART
* is checked, and if set, the channel is started. If this flag isn't set,
* then an error will be returned.
*
* It is possible to queue more than one DMA buffer onto a channel at
* once, and the code will deal with the re-loading of the next buffer
* when necessary.
*/
int s3c2410_dma_enqueue(unsigned int channel, void * id,dma_addr_t data, int size)
将dma_alloc_coherent中得到的dmaphys传递给s3c2410_dma_enqueue.
s3c2410_dma_enqueue提交一次dma请求,当dma通道可用的时候通过s3c2410_dma_loadbuffer开始一次传输,传输完成后会产生irq中断。其dma的中断服务函数中会继续启动dma请求队列中的请求,传输剩下的数据。
/* s3c2410_dma_buffdone
*
* small wrapper to check if callback routine needs to be called, and
* if so, call it
*/
static inline void s3c2410_dma_buffdone( struct s3c2410_dma_chan * chan, struct s3c2410_dma_buf * buf,
enum s3c2410_dma_buffresult result)
{
if (chan -> callback_fn != NULL) {
(chan -> callback_fn)(chan, buf -> id, buf -> size, result);
}
}
其中 s3c2410_dma_irq 会调用 s3c2410_dma_buffdone;
static irqreturn_t s3c2410_dma_irq( int irq, void * devpw)
/* s3c2410_request_dma
*
* get control of an dma channel
*/
int s3c2410_dma_request(unsigned int channel,
struct s3c2410_dma_client * client,
void * dev)
{
struct s3c2410_dma_chan * chan;
unsigned long flags;
int err;
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 : requesting irq %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: cannot get IRQ %d for DMA %d\n " ,
client -> name, chan -> irq, chan -> number);
return err;
}
chan -> irq_enabled = 1 ;
}
local_irq_restore(flags);
/* need to setup */
pr_debug( " %s: channel initialised, %p\n " , __func__, chan);
return chan -> number | DMACH_LOW_LEVEL;
}
/* *
* request_irq - allocate an interrupt line
* @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs
* @irqflags: Interrupt type flags
* @devname: An ascii name for the claiming device
* @dev_id: A cookie passed back to the handler function
*
* This call allocates interrupt resources and enables the
* interrupt line and IRQ handling. From the point this
* call is made your handler function may be invoked. Since
* your handler function must clear any interrupt the board
* raises, you must take care both to initialise your hardware
* and to set up the interrupt handler in the right order.
*
* Dev_id must be globally unique. Normally the address of the
* device data structure is used as the cookie. Since the handler
* receives this value it makes sense to use it.
*
* If your interrupt is shared you must pass a non NULL dev_id
* as this is required when freeing the interrupt.
*
* Flags:
*
* IRQF_SHARED Interrupt is shared
* IRQF_DISABLED Disable local interrupts while processing
* IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
*
*/
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char * devname, void * dev_id)
{
struct irqaction * action;
int retval;
action = kmalloc( sizeof ( struct irqaction), GFP_ATOMIC);
if ( ! action)
return - ENOMEM;
action -> handler = handler;
action -> flags = irqflags;
cpus_clear(action -> mask);
action -> name = devname;
action -> next = NULL;
action -> dev_id = dev_id;
select_smp_affinity(irq);
retval = setup_irq(irq, action);
if (retval)
kfree(action);
return retval;
}
/* s3c2410_dma_loadbuffer
*
* load a buffer, and update the channel state
*/
static inline int s3c2410_dma_loadbuffer( struct s3c2410_dma_chan * chan,
struct s3c2410_dma_buf * buf)
{
unsigned long reload;
pr_debug( " s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n " ,
buf, (unsigned long )buf -> data, buf -> size);
if (buf == NULL) {
dmawarn( " buffer is NULL\n " );
return - EINVAL;
}
/* check the state of the channel before we do anything */
if (chan -> load_state == S3C2410_DMALOAD_1LOADED) {
dmawarn( " load_state is S3C2410_DMALOAD_1LOADED\n " );
}
if (chan -> load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) {
dmawarn( " state is S3C2410_DMALOAD_1LOADED_1RUNNING\n " );
}
/* it would seem sensible if we are the last buffer to not bother
* with the auto-reload bit, so that the DMA engine will not try
* and load another transfer after this one has finished
*/
if (chan -> load_state == S3C2410_DMALOAD_NONE) {
pr_debug( " load_state is none, checking for noreload (next=%p)\n " ,
buf -> next);
reload = (buf -> next == NULL) ? S3C2410_DCON_NORELOAD : 0 ;
} else {
// pr_debug("load_state is %d => autoreload\n", chan->load_state);
reload = S3C2410_DCON_AUTORELOAD;
}
if ((buf -> data & 0xf0000000 ) != 0x30000000 ) {
dmawarn( " dmaload: buffer is %p\n " , ( void * )buf -> data);
}
writel(buf -> data, chan -> addr_reg);
dma_wrreg(chan, S3C2410_DMA_DCON,
chan -> dcon | reload | (buf -> size / chan -> xfer_unit));
chan -> next = buf -> next;
/* update the state of the channel */
switch (chan -> load_state) {
case S3C2410_DMALOAD_NONE:
chan -> load_state = S3C2410_DMALOAD_1LOADED;
break ;
case S3C2410_DMALOAD_1RUNNING:
chan -> load_state = S3C2410_DMALOAD_1LOADED_1RUNNING;
break ;
default :
dmawarn( " dmaload: unknown state %d in loadbuffer\n " ,
chan -> load_state);
break ;
}
return 0 ;
}
============================================
一些相关函数的说明。供自己日后参考。
--------------------------------------------------------------------------------------
int __init s3c24xx_dma_init_map( struct s3c24xx_dma_selection * sel)
初始化全局变量dma_sel结构,在此函数中大家可能疑惑的是
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;
这几条语句,为什么用memcpy初始化为了dma_sel,而又要用再分配的nmap结构去给dma_sel赋值。其答案是为了保险,因为dma_sel 中的map成员是个指针。对指针的操作是要很小心的。这里在此函数中重新定义一个指针变量,并指向它,就是为了防止别处定义的指针被撤消而造成越界访问。其实我认为这没有必要的。
dma_sel的定义为struct s3c24xx_dma_selection dma_sel;
而struct s3c24xx_dma_selection的定义为
struct s3c24xx_dma_selection {
struct s3c24xx_dma_map * map;
unsigned long map_size;
unsigned long dcon_mask;
void ( * select)( struct s3c2410_dma_chan * chan,
struct s3c24xx_dma_map * map);
};
--------------------------------------------------------------------------------------------------------
struct s3c2410_dma_chan * s3c2410_dma_map_channel( int channel)
此函数功能是: turn the virtual channel number into a real, and un - used hardware channel.完成DMA通道的虚实映射
在s3c2440_dma_mappings数组中查找相应channel对应的通道( 0 -- 4 ,有效未使用),并将映射关系记入dma_chan_map[channel]数组中。
---------------------------------------------------------------------------------------------------------
static int __init s3c2410_init_dma( void )
用于初始化s3c2410_dma_chan结构,并完成地址映射。
-----------------------------------------------------------------------------------------------------------
static struct s3c2410_dma_chan * lookup_dma_channel(unsigned int channel)
DMA通道虚实转换
-----------------------------------------------------------------------------------------------------------
int s3c2410_dma_request(unsigned int channel,
struct s3c2410_dma_client * client,
void * dev)
通过s3c2410_dma_map_channel函数找到channel对应的通道,并设置相应的通道状态,申请中断
-----------------------------------------------------------------------------------------------------------
int s3c2410_dma_started( struct s3c2410_dma_chan * chan)
/* if we've only loaded one buffer onto the channel, then chec
* to see if we have another, and if so, try and load it so when
* the first buffer is finished, the new one will be loaded onto
* the channel */
-----------------------------------------------------------------------------------------------------------
int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client * client)
/* s3c2410_dma_free
*
* release the given channel back to the system, will stop and flush
* any outstanding transfers, and ensure the channel is ready for the
* next claimant.
*
* Note, although a warning is currently printed if the freeing client
* info is not the same as the registrant's client info, the free is still
* allowed to go through.
*/
--------------------------------------------------------------------------------------------------------------
static int s3c2410_dma_flush( struct s3c2410_dma_chan * chan)
* stop the channel, and remove all current and pending transfers
--------------------------------------------------------------------------------------------------------------
int s3c2410_dma_config(dmach_t channel,
int xferunit,
int dcon)
根据xferunit设置通道的控制寄存器
--------------------------------------------------------------------------------------------------------------
int s3c2410_dma_setflags(dmach_t channel, unsigned int flags)
根据flags设置通道的flags
--------------------------------------------------------------------------------------------------------------
int s3c2410_dma_ctrl(dmach_t channel, enum s3c2410_chan_op op);
0