linux中DMA申请空间的疑问

linux中DMA申请空间的疑问,第1张

多次 kmalloc 是肯定不行的,dma 内存在物理上要连续.

可以使用 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读数据到内存。

}

望采纳!!!


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/7579061.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-07
下一篇2023-04-07

发表评论

登录后才能评论

评论列表(0条)

    保存