
可以使用 get_free_pages, 然后使用dma_map_single, dma_map_pages, dma_map_sg将之前分配的内存空间映射, 但也不会太大。
dma 内存不光是申请就可以的,因为传输时不经cpu, 所以要对 cache 进行 clean 或 invalidate *** 作,上面的方式就不保证 cache 的一致性。
使用 dma_alloc_coherent 申请的保证一致性.
不管那种方式,申请过大内存都存在失败的可能性,不建议一次申请很大内存,申请的小点,加大传输次数。
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读数据到内存。
}
望采纳!!!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)