下面是一个使用 dma_alloc_coherent
接口的简单 Linux 驱动示例,该驱动实现了基本的读写功能,能够通过字符设备接口与用户空间进行交互。该示例展示了如何在驱动中分配 DMA 一致性内存,并提供给用户空间读写操作。
示例:DMA 读写内存的字符设备驱动
1. 驱动头文件和模块信息
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#define DEVICE_NAME "my_dma_device"
#define BUFFER_SIZE 1024
static int major_number;
static void *dma_buffer;
static dma_addr_t dma_handle;
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple DMA coherent memory example with read/write support");
2. 打开、关闭、读取和写入函数
static int my_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "Device opened\n");
return 0;
}
static int my_release(struct inode *inode, struct file *file) {
printk(KERN_INFO "Device closed\n");
return 0;
}
ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *offset) {
if (*offset >= BUFFER_SIZE)
return 0; // EOF
if (*offset + count > BUFFER_SIZE)
count = BUFFER_SIZE - *offset;
if (copy_to_user(buf, dma_buffer + *offset, count))
return -EFAULT;
*offset += count;
return count; // 返回实际读取的字节数
}
ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) {
if (*offset >= BUFFER_SIZE)
return -ENOSPC; // No space left on device
if (*offset + count > BUFFER_SIZE)
count = BUFFER_SIZE - *offset;
if (copy_from_user(dma_buffer + *offset, buf, count))
return -EFAULT;
*offset += count;
return count; // 返回实际写入的字节数
}
3. 文件操作结构体
static struct file_operations fops = {
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
};
4. 初始化和清理模块
static int __init my_init(void) {
// 注册字符设备
major_number = register_chrdev(0, DEVICE_NAME, &fops);
if (major_number < 0) {
printk(KERN_ALERT "Failed to register device: %d\n", major_number);
return major_number;
}
// 分配DMA一致性内存
dma_buffer = dma_alloc_coherent(NULL, BUFFER_SIZE, &dma_handle, GFP_KERNEL);
if (!dma_buffer) {
unregister_chrdev(major_number, DEVICE_NAME);
printk(KERN_ALERT "Failed to allocate DMA buffer\n");
return -ENOMEM;
}
memset(dma_buffer, 0, BUFFER_SIZE); // 清空缓冲区
printk(KERN_INFO "DMA buffer allocated at %p with handle %pad\n", dma_buffer, &dma_handle);
return 0;
}
static void __exit my_exit(void) {
// 释放DMA一致性内存
if (dma_buffer)
dma_free_coherent(NULL, BUFFER_SIZE, dma_buffer, dma_handle);
// 注销字符设备
unregister_chrdev(major_number, DEVICE_NAME);
printk(KERN_INFO "Device unregistered and DMA buffer freed\n");
}
5. 模块入口和出口函数
module_init(my_init);
module_exit(my_exit);
编译与加载模块
创建一个 Makefile 来编译这个模块:
obj-m += my_dma_device.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
使用以下命令编译并加载模块:
make # 编译模块
sudo insmod my_dma_device.ko # 加载模块
dmesg # 查看内核日志以确认成功加载以及分配情况
sudo rmmod my_dma_device # 卸载模块
make clean # 清理生成文件
测试驱动程序的读写功能
可以编写一个简单的用户空间程序来测试该驱动。下面是一个 C 程序示例,用于打开设备,向其写入数据,然后读取数据。
用户空间测试代码示例:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define DEVICE_PATH "/dev/my_dma_device"
int main() {
int fd = open(DEVICE_PATH, O_RDWR);
if (fd < 0) {
perror("Failed to open the device");
return -1;
}
const char* write_data = "Hello from user space!";
write(fd , write_data , strlen(write_data)); // 写数据到设备
char read_data[1024] = {0};
lseek(fd , 0 , SEEK_SET); // 移动到文件开始位置,准备读取数据
read(fd , read_data , sizeof(read_data)); // 从设备读取数据
printf("Read from device: %s\n", read_data);
close(fd);
return 0;
}
总结
上述代码演示了如何使用 dma_alloc_coherent
在 Linux 驱动程序中分配 DMA 一致性内存,并实现简单的读写功能。请根据具体需求进行必要的错误处理和调整。在实际应用中,还需要考虑线程安全、同步等问题。
内容由零声教学AI助手提供,问题来源于学员提问