您的位置:首页 > 运维架构 > Linux

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

2010-02-23 11:49 513 查看

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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息