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;
}
相关文章推荐
- linux驱动学习--第二十天:第十一章:CPU 与内存和I/O(三):I/O 内存静态映射 和 DMA
- Linux-2.6驱动开发 4 内存分配
- Linux驱动mmap内存映射2
- Linux设备驱动之内存映射--mmap--转 .
- Linux驱动开发----块设备驱动(内存模拟)Tiny6410
- linux2.6内核驱动开发学习(1)
- 【内核】Linux 2.6 内存反向映射机制 Reverse Mapping
- Linux驱动开发-----内存存取
- Linux驱动开发常用调试工具---之内存读写工具devmem和devkmem
- Linux内核驱动之DDR3(二)内存映射
- Linux设备驱动之内存映射
- Linux s3c2410 DMA驱动开发
- Linux开发--mmap映射/dev/mem内存
- Linux-2.6驱动开发 7 设备命名
- Linux 驱动开发中常用的内存分配方法浅析
- linux驱动开发--内核空间中内存的申请与释放
- linux2.6 驱动开发入门:中断
- Linux-2.6驱动开发 5 时间,延时
- linux驱动开发--I/O内存的访问流程
- Linux驱动开发常用调试工具---之内存读写工具devmem和devkmem