Linux-2.6驱动开发 6 内存映射,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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核版本2.6是一个较早的版本,对于SPI总线的支持相对较为简单。SPI(Serial Peripheral Interface)是一种同步的全双工串行通信总线,可用于连接微控制器与外围设备,例如传感器、存储器等。 在Linux 2.6版本中,对于SPI总线的驱动代码主要涉及以下几个方面: 1. 初始化SPI硬件:SPI总线的初始化包括选择SPI设备、配置SPI寄存器、设置传输速率、设置SPI模式等。这通常在驱动模块的probe函数中完成。 2. 定义SPI设备:在驱动代码中,需要定义SPI设备的一些基本信息,如设备名称、ID、SPI模式、位宽等。这些信息在驱动模块中进行定义,并通过spi_register_driver函数进行注册。 3. 数据传输:SPI驱动代码需要实现与SPI设备的数据传输功能,包括发送数据和接收数据。对于发送数据,驱动代码通过spi_write函数将数据发送给SPI设备;对于接收数据,驱动代码通过spi_read函数从SPI设备接收数据。 4. 中断处理:SPI驱动代码可以通过中断处理函数来处理SPI设备发生的中断事件,以便实时响应设备的数据传输情况。中断处理函数通常与硬件平台相关,需要根据具体的硬件平台进行编写。 5. 设备节点的创建和注册:驱动代码需要创建设备节点,并通过sysfs接口将设备节点注册到/sys/class/spi目录下,以提供给用户空间进行访问和控制。 需要注意的是,以上是对于Linux 2.6版本中SPI驱动代码大致的介绍,实际的驱动代码会根据具体的硬件平台和需求进行适配和实现。对于较新的Linux内核版本,SPI驱动代码的实现可能会有所不同,更加灵活和强大。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值