Linux-2.6驱动开发 6 内存映射,DMA

本文深入探讨了内存映射的基础概念,包括虚拟地址、页结构及其转换方式,并详细介绍了DMA的操作原理及其实现方法。此外,还讲解了DMA池的概念、DMA映射的流程以及DMA使用的具体步骤。

6 内存映射,DMA

6.1 虚拟地址

6.1.1 (page)

页的重要成员:

atomic_t count; //引用的数量

void *virtual; //页的虚拟地址

unsigned long flags; //当前页的状态

 

虚拟地址与页的转换:

struct page *virt_to_page(void *kaddr); //虚拟地址转换成页

struct page *pfn_to_page(int pfn);  //页帧索引转换成页

void *page_address(struct page *page); //返回页的虚拟地址

 

页映射到虚拟地址

#include <linux/highmem.h>

void *kmap(struct page *page);

void kunmap(struct page *page);

 

#include <linux/highmem.h>

#include <asm/kmap_types.h>

void *kmap_atomic(struct page *page, enum km_type type);

void kunmap_atomic(void *addr, enum km_type type);

6.2 映射

mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
int (*mmap) (struct file *filp, struct vm_area_struct *vma);
6.2.1 指定映射范围
int remap_pfn_range(struct vm_area_struct *vma, 
                     unsigned long virt_addr, unsigned long pfn,
                     unsigned long size, pgprot_t prot);
int io_remap_page_range(struct vm_area_struct *vma, 
                        unsigned long virt_addr, unsigned long phys_addr,
                        unsigned long size, pgprot_t prot);

6.3直接I/O操作

一般的I/O操作都是使用内核空间的内存,当涉及大量数据传输时,使用用户空间的内存会使速度更快。

申请用户空间内存:

#include <linux/mm.h>
int get_user_pages(struct task_struct *tsk, 
                   struct mm_struct *mm, 
                   unsigned long start,
                   int len, 
                   int write, 
                   int force, 
                   struct page **pages, 
                   struct vm_area_struct **vmas);

为了使获取到的地址能在内核代码中运行,需将pages通过kmap函数映射成虚拟地址。

释放用户空间内存:

if (! PageReserved(page))
    SetPageDirty(page);

释放前先标识为dirty

void page_cache_release(struct page *page);

6.4 异步I/O操作

允许该操作未完成的时候去作其他操作。

#include <linux/aio.h>
ssize_t (*aio_read) (struct kiocb *iocb, char *buffer, 
                     size_t count, loff_t offset);
ssize_t (*aio_write) (struct kiocb *iocb, const char *buffer, 
                      size_t count, loff_t offset);
int (*aio_fsync) (struct kiocb *iocb, int datasync);

6.5直接访问内存(DMA

DMA的数据传输中触发的条件:

1 软件请求数据,流程如下:

       1)进程调用read,然后驱动申请DMA缓冲区,并请求硬件将数据传输到该缓冲区,同时进程进入休眠,等待缓冲区可读。

       2)硬件将数据写到缓冲区,完成后产生中断

       3) 中断处理程序获取输入数据,应答中断,唤醒进程,进程就可以读取数据了。

2 硬件不时的写入数据,流程如下:

       1)硬件产生中断,通知新数据已经到来。

       2)中断处理程序申请缓冲区,并告诉硬件将数据送往哪里

       3)硬件将数据送往指定区域,完成时再次产生中断

       4)中断处理程序分配新数据,并唤醒相关进程,然后继续等待新数据。

6.5.1 申请DMA缓冲区

Kmalloc, get_free_pages等在申请DMA缓冲区时flag须为GFP_DMA,以免申请的区间不适合DMA操作。

ioremap可分配指定区域的缓冲区,如

dmabuf = ioremap (0xFF00000 /* 255M */, 0x100000 /* 1M */);

有的设备受限于24位寻址。可以用dma_set_mask()函数解决

int dma_set_mask(struct device *dev, u64 mask);

dma_set_mask (dev, 0xffffff)

6.5.2 DMA映射

DMA映射由申请DMA缓冲区和产生设备可访问该缓冲区的地址两部分组成。

申请连贯的DMA缓冲区:

void *dma_alloc_coherent(struct device *dev, size_t size,
                         dma_addr_t *dma_handle, int flag);
void dma_free_coherent(struct device *dev, size_t size,
                        void *vaddr, dma_addr_t dma_handle);

申请流DMA缓冲区:

dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, 
                          enum dma_data_direction direction);
void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, 
                      enum dma_data_direction direction);
dma_addr_t dma_map_page(struct device *dev, struct page *page,
                        unsigned long offset, size_t size,
                        enum dma_data_direction direction);
 
void dma_unmap_page(struct device *dev, dma_addr_t dma_address, 
                    size_t size, enum dma_data_direction direction);

single 适用于单缓冲区,page适用于页结构的缓冲区

 6.5.3 DMA

DMA池是一种小型、连贯的DMA映射的申请机制

#include <linux/dmapool.h>
struct dma_pool *dma_pool_create(const char *name, struct device *dev, 
                                 size_t size, size_t align, 
                                 size_t allocation);
void *dma_pool_alloc(struct dma_pool *pool, int mem_flags, 
                     dma_addr_t *handle);
void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr);
void dma_pool_destroy(struct dma_pool *pool);

dma_pool_create 创建一个大池,alloction是它的总量,dma_pool_alloc分配不大于池总量的空间。

6.5.4 DMA使用

1 注册

#include <asm/dma.h>
int request_dma(unsigned int channel, const char *name); 
void free_dma(unsigned int channel);

2 自旋锁

unsigned long claim_dma_lock( );

void release_dma_lock(unsigned long flags);

3 模式

void set_dma_mode(unsigned int channel, char mode);

4 缓冲区地址

void set_dma_addr(unsigned int channel, unsigned int addr);

5 数据量

void set_dma_count(unsigned int channel, unsigned int count);

6 使能

void disable_dma(unsigned int channel);

void enable_dma(unsigned int channel);

7 查询剩余数据量

int get_dma_residue(unsigned int channel);

8 控制访问寄存器的数据位

void clear_dma_ff(unsigned int channel);

ff = flip-flop ,其功能是8位和16位的切换,flip-flop 在传输8位数据时会自动切换,在访问DMA寄存器前须先清flip-flop

9 应用

int dad_dma_prepare(int channel, int mode, unsigned int buf,
                    unsigned int count)
{
    unsigned long flags;
 
    flags = claim_dma_lock(  );
    disable_dma(channel);
    clear_dma_ff(channel);
    set_dma_mode(channel, mode);
    set_dma_addr(channel, virt_to_bus(buf));
    set_dma_count(channel, count);
    enable_dma(channel);
    release_dma_lock(flags);
 
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值