更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_vm_iomap.c 文件参考

DMA 更多...

浏览源代码.

函数

VOID * LOS_DmaMemAlloc (DMA_ADDR_T *dmaAddr, size_t size, size_t align, enum DmaMemType type)
 分配DMA空间 更多...
 
VOID LOS_DmaMemFree (VOID *vaddr)
 释放 DMA指针 更多...
 
DMA_ADDR_T LOS_DmaVaddrToPaddr (VOID *vaddr)
 将DMA虚拟地址转成物理地址 更多...
 

详细描述

DMA

直接内存访问
    直接内存访问(Direct Memory Access,DMA)是计算机科学中的一种内存访问技术。它允许某些电脑内部的
    硬件子系统(电脑外设),可以独立地直接读写系统内存,而不需中央处理器(CPU)介入处理
    在同等程度的处理器负担下,DMA是一种快速的数据传送方式。很多硬件的系统会使用DMA,包含硬盘控制器、
    绘图显卡、网卡和声卡。

    DMA是所有现代电脑的重要特色,它允许不同速度的硬件设备来沟通,而不需要依于中央处理器的大量中断负载。
    否则,中央处理器需要从来源把每一片段的资料复制到寄存器,然后把它们再次写回到新的地方。在这个时间中,
    中央处理器对于其他的工作来说就无法使用。

    DMA传输常使用在将一个内存区从一个设备复制到另外一个。当中央处理器初始化这个传输动作,传输动作本身是
    由DMA控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存去。像是这样的操作
    并没有让处理器工作拖延,使其可以被重新调度去处理其他的工作。DMA传输对于高性能嵌入式系统算法和网络是
    很重要的。 举个例子,个人电脑的ISA DMA控制器拥有8个DMA通道,其中的7个通道是可以让计算机的中央处理器所利用。
    每一个DMA通道有一个16位地址寄存器和一个16位计数寄存器。要初始化资料传输时,设备驱动程序一起设置DMA通道的
    地址和计数寄存器,以及资料传输的方向,读取或写入。然后指示DMA硬件开始这个传输动作。当传输结束的时候,
    设备就会以中断的方式通知中央处理器。

    "分散-收集"(Scatter-gather)DMA允许在一次单一的DMA处理中传输资料到多个内存区域。相当于把多个简单的DMA要求
    串在一起。同样,这样做的目的是要减轻中央处理器的多次输出输入中断和资料复制任务。 
    DRQ意为DMA要求;DACK意为DMA确认。这些符号一般在有DMA功能的电脑系统硬件概要上可以看到。
     它们表示了介于中央处理器和DMA控制器之间的电子信号传输线路。
    
缓存一致性问题
    DMA会导致缓存一致性问题。想像中央处理器带有缓存与外部内存的情况,DMA的运作则是去访问外部内存,
    当中央处理器访问外部内存某个地址的时候,暂时先将新的值写入缓存中,但并未将外部内存的资料更新,
    若在缓存中的资料尚未更新到外部内存前发生了DMA,则DMA过程将会读取到未更新的资料。
    相同的,如果外部设备写入新的值到外部内存内,则中央处理器若访问缓存时则会访问到尚未更新的资料。
    这些问题可以用两种方法来解决:

    缓存同调系统(Cache-coherent system):以硬件方法来完成,当外部设备写入内存时以一个信号来通知
        缓存控制器某内存地址的值已经过期或是应该更新资料。
    非同调系统(Non-coherent system):以软件方法来完成,操作系统必须确认缓存读取时,DMA程序已经
        开始或是禁止DMA发生。
    第二种的方法会造成DMA的系统负担。
版本
作者
weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
日期
2022-04-02

在文件 los_vm_iomap.c 中定义.

函数说明

◆ LOS_DmaMemAlloc()

VOID * LOS_DmaMemAlloc ( DMA_ADDR_T dmaAddr,
size_t  size,
size_t  align,
enum DmaMemType  type 
)

分配DMA空间

在文件 los_vm_iomap.c86 行定义.

87{
88 VOID *kVaddr = NULL;
89
90 if (size == 0) {
91 return NULL;
92 }
93
94 if ((type != DMA_CACHE) && (type != DMA_NOCACHE)) {
95 VM_ERR("The dma type = %d is not supported!", type);
96 return NULL;
97 }
98
99#ifdef LOSCFG_KERNEL_VM
100 kVaddr = LOS_KernelMallocAlign(size, align);//不走内存池方式, 直接申请物理页
101#else
102 kVaddr = LOS_MemAllocAlign(OS_SYS_MEM_ADDR, size, align);//从内存池中申请
103#endif
104 if (kVaddr == NULL) {
105 VM_ERR("failed, size = %u, align = %u", size, align);
106 return NULL;
107 }
108
109 if (dmaAddr != NULL) {
110 *dmaAddr = (DMA_ADDR_T)LOS_PaddrQuery(kVaddr);//查询物理地址, DMA直接将数据灌到物理地址
111 }
112
113 if (type == DMA_NOCACHE) {//无缓存模式 , 计算新的虚拟地址
114 kVaddr = (VOID *)VMM_TO_UNCACHED_ADDR((UINTPTR)kVaddr);
115 }
116
117 return kVaddr;
118}
VOID * LOS_MemAllocAlign(VOID *pool, UINT32 size, UINT32 boundary)
从指定内存池中申请size长度的内存且地址按boundary字节对齐的内存
Definition: los_memory.c:1150
unsigned long DMA_ADDR_T
Definition: los_typedef.h:210
unsigned long UINTPTR
Definition: los_typedef.h:68
@ DMA_CACHE
有缓存的DMA
Definition: los_vm_iomap.h:55
@ DMA_NOCACHE
无缓存的DMA
Definition: los_vm_iomap.h:56
PADDR_T LOS_PaddrQuery(VOID *vaddr)
通过虚拟地址查询映射的物理地址
Definition: los_vm_map.c:550
VOID * LOS_KernelMallocAlign(UINT32 size, UINT32 boundary)
函数调用图:

◆ LOS_DmaMemFree()

VOID LOS_DmaMemFree ( VOID *  vaddr)

释放 DMA指针

在文件 los_vm_iomap.c120 行定义.

121{
122 UINTPTR addr;
123
124 if (vaddr == NULL) {
125 return;
126 }
127 addr = (UINTPTR)vaddr;
128 // 未缓存区
129 if ((addr >= UNCACHED_VMM_BASE) && (addr < UNCACHED_VMM_BASE + UNCACHED_VMM_SIZE)) {
130 addr = UNCACHED_TO_VMM_ADDR(addr); //转换成未缓存区地址
131#ifdef LOSCFG_KERNEL_VM
132 LOS_KernelFree((VOID *)addr);//
133#else
134 LOS_MemFree(OS_SYS_MEM_ADDR, (VOID *)addr);//内存池方式释放
135#endif
136 } else if ((addr >= KERNEL_VMM_BASE) && (addr < KERNEL_VMM_BASE + KERNEL_VMM_SIZE)) {
137#ifdef LOSCFG_KERNEL_VM
138 LOS_KernelFree((VOID *)addr);
139#else
140 LOS_MemFree(OS_SYS_MEM_ADDR, (VOID *)addr);
141#endif
142 } else {
143 VM_ERR("addr %#x not in dma area!!!", vaddr);
144 }
145 return;
146}
UINT32 LOS_MemFree(VOID *pool, VOID *ptr)
释放从指定动态内存中申请的内存
Definition: los_memory.c:1369
VOID LOS_KernelFree(VOID *ptr)
函数调用图:

◆ LOS_DmaVaddrToPaddr()

DMA_ADDR_T LOS_DmaVaddrToPaddr ( VOID *  vaddr)

将DMA虚拟地址转成物理地址

在文件 los_vm_iomap.c148 行定义.

149{
150 return (DMA_ADDR_T)LOS_PaddrQuery(vaddr);
151}
函数调用图: