下面是一个使用 dma_alloc_coherent
接口的完整示例,演示如何在 Linux 驱动中分配和使用 DMA 可访问的内存。
示例代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/slab.h>
#define DEVICE_NAME "my_device"
#define CLASS_NAME "my_class"
static struct class* my_class = NULL;
static struct device* my_device = NULL;
struct my_dma_data {
void *cpu_addr; // CPU 可访问的虚拟地址
dma_addr_t dma_handle; // DMA 设备可访问的物理地址
size_t size; // 分配大小
};
static struct my_dma_data dma_data;
static int __init my_driver_init(void) {
// 创建类和设备
my_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(my_class)) {
printk(KERN_ALERT "Failed to register device class\n");
return PTR_ERR(my_class);
}
my_device = device_create(my_class, NULL, MKDEV(0, 0), NULL, DEVICE_NAME);
if (IS_ERR(my_device)) {
class_destroy(my_class);
printk(KERN_ALERT "Failed to create the device\n");
return PTR_ERR(my_device);
}
// 分配 DMA 内存
dma_data.size = PAGE_SIZE; // 假设我们需要分配一个页面大小的内存
dma_data.cpu_addr = dma_alloc_coherent(&my_device->dev,
dma_data.size,
&dma_data.dma_handle,
GFP_KERNEL);
if (!dma_data.cpu_addr) {
device_destroy(my_class, MKDEV(0, 0));
class_destroy(my_class);
printk(KERN_ALERT "Failed to allocate coherent DMA memory\n");
return -ENOMEM;
}
printk(KERN_INFO "Allocated %zu bytes of coherent DMA memory at CPU addr: %p, DMA addr: %pad\n",
dma_data.size,
dma_data.cpu_addr,
&dma_data.dma_handle);
return 0; // 初始化成功
}
static void __exit my_driver_exit(void) {
// 释放DMA内存
if (dma_data.cpu_addr) {
dma_free_coherent(&my_device->dev,
dma_data.size,
dma_data.cpu_addr,
dma_data.dma_handle);
printk(KERN_INFO "Freed coherent DMA memory\n");
}
// 销毁设备和类
device_destroy(my_class, MKDEV(0, 0));
class_destroy(my_class);
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple example of using dma_alloc_coherent in a kernel module.");
代码解析
模块初始化 (
my_driver_init
):- 创建字符设备类并注册设备。
- 使用
dma_alloc_coherent
函数分配一块可以被设备进行DMA操作的内存,并获取CPU可访问的虚拟地址和物理地址(DMA 地址)。 - 检查分配是否成功,如果失败,则清理资源并返回错误。
模块退出 (
my_driver_exit
):- 在模块卸载时,释放之前分配的DMA内存。
- 清理创建的设备和类。
宏定义:
MODULE_LICENSE
,MODULE_AUTHOR
,MODULE_DESCRIPTION
用于描述模块信息。
注意事项
- 确保在运行这个驱动程序之前,你有适当权限加载内核模块。
- 使用
GFP_KERNEL
标志来进行常规内存分配。如果你处于上下文中可能会阻塞,请选择合适的标志。 - 确保在使用完毕后及时释放所申请的资源,以避免内存泄漏。
内容由零声教学AI助手提供,问题来源于学员提问