ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

内存与IO访问

2021-11-22 19:58:11  阅读:172  来源: 互联网

标签:dma struct void unsigned long 访问 内存 IO


提 纲

  • LINUX内存管理
  • 内存存取
  • I/O内存访问接口
  • 将设备地址映射到用户空间
  • I/O内存的静态映射
  • DMA

1 LINUX内存管理

  1. 用户空间 0~3GB 内核空间
  2. 3~4GB 每个进程的用户空间是完全独立、互不相干的,用户进程各自有不同的页表。
  3. 内核空间是由内核负责映射,不会跟着进程改变。
  4. 用户程序需要通过系统调用才能访问内核空间。
  5. 当系统的物理内存大于4GB,则需要使用CPU的扩展分页(需要CPU支持)。

2 内存存取

2.1 用户空间内存动态申请

char *p = (char *)malloc(SIZE);
if(p==NULL)
	return 0;
function(p);
…
free(p);
p=NULL;

2.2 内核空间内存动态申请

1 void *kmalloc (size_t size,int flags);
其中flags主要有:
GFP_KERNEL 若暂时不存在空闲页可供申请,则休眠。所以不能在中断上下文或持有自旋锁时使用。
GFP_ATOMIC若暂时不存在空闲页可供申请,则返回。可在中断上下文或持有自旋锁时使用。
释放:kfree(void *p);
2 get_free_pages 系列,以页为单位分配内存,很少使用。
3 void *vmalloc(unsigned long size);
适用于分配较大的内存。
释放:void vfree(void *p);
4 内存池—操作系统自身使用。

2.3 虚地址与物理地址的关系

1 虚拟地址转物理地址

unsigned long virt_to_phys(volatile void *address);

2 物理地址转虚拟地址

unsigned long phys_to_virt (volatile void *address);

虚拟地址通常=物理地址-某个值+3GB (在内核空间)

unsigned long l=virt_to_phys((volatile void *)S3C2410_GPBDAT);
unsigned long l=phys_to_virt((volatile void *)0x56000010);

具体由任何一个寄存器一直追溯其虚拟地址。看mini2440驱动笔记_su .txt_7

3 I/O端口和I/O内存访问接口

3.1 I/O端口访问函数—ARM、PowerPC等不提供I/O空间。不适用。
3.2 I/O内存(自2.6.12后,一般采用下面2种方式读写寄存器)
方法一:使用系统定义好的API访问定义好的虚地址

 static inline u32 __raw_readl(const volatile void __iomem *addr)
 static inline void __raw_writel(u32 b, volatile void __iomem *addr);

如:__raw_readl(S3C2410_GPFCON);
       __raw_writel(0x12,S3C2410_GPFDAT);
    (函数定义在2.6.32 include/asm-generic/io.h)

方法二:采用ioremap()函数将设备的物理地址映射到虚拟地址再直接访问。如

#define rGPFCON (*((volatile unsigned  *)(ioremap((volatile unsigned *)0x56000050,4))))
regval = rGPFCON;  读
rGPFCON = regval;  写

使用ioremap()获得的虚地址应该被iounmap()释放。

3.3 检查将要申请的I/O内存是否可用。

 void request_mem_region(unsigned start,unsigned long len,char *name);
 void release_mem_region(unsigned start,unsigned long len);

申请成功,则标志为已经使用,其他驱动想再申请就会失败。
在这里插入图片描述
实际上对一般的特殊寄存器,大家都可访问,所以无需检查将要申请的I/O内存是否可用。只有专用来放资源的空间才需要。

4 将设备地址映射到用户空间

实现mmap()函数,使得用户空间能直接访问设备的物理地址。----用在显示适配器类的设备。
驱动中函数原型:

Int (*mmap) (struct file *,struct vm_area_struct*);

实现机制(想进一步了解,则请看代码清单11.8):
1 建立页表
2 填充vm_area_struct中的vm_operations_struct指针

I/O内存的静态映射

外设I/O内存物理地址到虚拟地址的静态映射。使用map_desc结构体。

struct map_desc {
	unsigned long virtual;  //虚拟地址
	unsigned long physical;//物理地址
	unsigned long length;//大小
	unsigned int type;//类型
};

请参考/arch/arm/mach-s3c2440/mach-smdk2440.c

DMA

DMA 一种无需CPU的参与就可以让外设与系统内存之间进行双向数据传输的硬件机制。
Cache 是CPU针对内存的缓存。
当DMA的目的地址与cache的对象存在重叠区域时,容易使cache的数据与主存中的数据不一致。所以应当禁止DMA的目的地址范围内的cache功能。
在这里插入图片描述
1 分配/释放一个写合并的DMA缓冲区

 void *dma_alloc_writecombine(struct device *dev,size_t size,dma_addr_t *handle,gfp_t gfp);

其中:函数通过参数handle返回DMA缓冲区的总线地址
struct device: 用于描述设备相关的信息设备之间的层次关系,以及设备与总线、驱动的关系。

gfp一般为GFP_KERNEL
 void *dma_free_writecombine(struct device *dev,size_t size,void *cpuaddr,dma_addr_t *);
如:fbi->screen_base = dma_alloc_writecombine(sfb->dev, size,						  &map_dma, GFP_KERNEL);

参数解释

A = dma_alloc_writecombine(B,C,D,GFP_KERNEL);
A: 内存的虚拟起始地址,在内核要用此地址来操作所分配的内存
B: struct device指针,可以平台初始化里指定,主要是dma_mask之类,可参考framebuffer
C: 实际分配大小,传入dma_map_size即可
D: 返回的内存物理地址,dma就可以用。
所以,A和D是一一对应的,只不过,A是虚拟地址,而D是物理地址。
2 申请/释放DMA通道

int request_dma(unsigned int dmanr, const char *device_id);
void free_dma(unsigned int dmanr);

如: err = request_dma(chn, deviceID)

Linux 中DMA使用流程

在这里插入图片描述

标签:dma,struct,void,unsigned,long,访问,内存,IO
来源: https://blog.csdn.net/qq_29890089/article/details/121478794

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有