DMA对于ISA设备,DMA只能在16M以下内存中进行
#define __get_dma_pages(gfp_mask, order \
__get_free_pages((gfp_mask)|GFP_DMA,(order))
static unsigned long dma_mem_alloc(int size)
{
int order = get_order(size);
return __get_dma_pages(GFP_KERNEL,order);
基于DMA的硬件使用总线地址而非物理地址(CPU角度看到的未经转换的地址)
虚拟地址/总线地址
unsigned long virt_bus(volatile void *address);
void *bus_to_virt(unsigned long address);
DMA地址掩码
int dma_set_mask(struct device *dev, u64 mask);
分配DMA一致性的内存区域
void *dma_alloc_coherent(struct device *dev,size_t size, dma_addr_t *handl,gfp_t gfp);
返回DMA缓冲的虚拟地址,handle返回总线地址
void *dma_free_coherent(struct device *dev,size_t size, dma_addr_t );
void *dma_alloc_writecombine(struct device *dev,size_t size, dma_addr_t *handl,gfp_t gfp);
流式DMA映射
dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t dize, enum
dma_data_direction direction);
获得DMA缓冲区控制权
void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr, size_t dize, enum
dma_data_direction direction);
direct DMA_TO_DEVICE DMA_FROM_DEVICE DMA_BIDIRIRECTIONAL DMA_NONE
dma_addr_t dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t dize, enum
dma_data_direction direction);
返还控制权
void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_addr, size_t dize, enum
dma_data_direction direction);
申请大缓冲区 SG模式下申请不连续小缓冲
int dma_map_sg(struct device *dev,struct scatterlist *sg,int nents, enum
dma_data_direction direction);
返回缓冲区数量 对每个项,函数为设备产生恰当的总线地址
struct scatterlist
{
struct page *page;
unsigned int offset;
dma_addr_t dma_adress;
unsigned int length;
}
执行dma_map_sg()后,sg_dma_address可返回scatterlist 对应缓冲区总线地址,sg_dma_len()
dma_addr_t sg_dma_address(struct scatterlist *sg);
unsigned int sg_dma_len(struct scatterlist *sg);
mem= 预留缓存作为IO内存使用,可以静态映射也可以ioremap();
=======================================================
申请DMA通道
int request_dma(unsigned int dmant, const char * device_id);
void free_dma(unsigned int dmanr)
流程
1open():request_dma()初始化DMAC 申请DMA缓冲区
2write()..:DMA传输
3中断处理:若能进行中断处理,进行中断处理
4release :释放缓冲区 free_dma()
===
使用8237DMA范例
typedef struct
{....
void *dma_buffer;
struct {
unsigned int direction;
unsigned int length;
void *target;
unsigned long start_time;
}current_dma;
unsigned char dma;
}xxx_device;
static int xxx_open()
{
....
//set up interrupt
if((retval= request_irq(dev->irq, &xxx_interrupt, 0, dev->name, dev)))
{ printk( KERN_ERR "%s:could not allocate IRQ %d\n", dev->name, dev->irq);
return retval;
}
//request dma
if((retval= request_dma(dev->dma, dev->name)))
{
free_irq(dev->irq, dev);
printk(KERN_ERR "%s :could not alloc DMA%d chann..);
return retval;
}
dev->dma_buffer = ((void *)dma_mem_alloc(DMA_BUFFER_SIZE);
if(!dev->dma_buffer)
{
printk(KERN_ERR );
free_dma(dev->dma);
free_irq(dev->irq,dev);
return -ENOMEM;
}
init_dma();
...
}
static int mem_to_xxx(const byte *buf, int len)
{
...
dev->current_dma.direction =1;
devo->current_dma.start_time = jiffies;
memcpy(dev->dma_buffer, buf, len);
target= isa_virt_to_bus(dev->dma_buffer)// ISA
//write
flags= claim_dma_lock();
disable_dma(dev->dma);
clear_dma_ff(dev->dma); //dma flip flop
set_dma_mode(dev->dma, 0x48); //dma ->io
set_dma_addr(dev->dma, target);//addr
set_dma_count(dev->dma,len);
outb_control(dev->x_ctrl|DMAE|TCEN,dev)//get device DMA
enable_dma(devo->dma);
release_dma_lock(flags);
printk(KERN_DEBUG "%s :dma transfer started \n" ,dev->name);
...
}
static int xxx_to_mem(const byte *buf, int len, char *target)
{
...
dev->current_dma.target = target;
dev->current_dma.direction =0;
devo->current_dma.start_time = jiffies;
devo->current_dma.length = len;
outb_control(dev->x_ctrl|DIR|TCEN|DMAE,dev)
flags= claim_dma_lock();
disable_dma(dev->dma);
clear_dma_ff(dev->dma); //dma flip flop
set_dma_mode(dev->dma, 0x04); //
set_dma_addr(dev->dma, isa_virt_to_bus(target));//addr
set_dma_count(dev->dma,len);
enable_dma(devo->dma);
release_dma_lock(flags);
...
}
static irqreturn_t xxx_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr)
{
....
do
{ ///complete??
if(int_type==DMA_DONE)
{ outb_control(dev->x_ctrl &~(DMAE|TCEN|DIR), dev);
if(dev->current_dma.direction)
{
...
}
else
{
memcpy(dev->current_dma.target, dev->dma_buffer,dev->current_dma.len);
}
else if{int_type=RECV_DATA)
{
xxx_to_mem(...);//通过DMA读数据到内存。
}
阅读(2424) | 评论(0) | 转发(0) |