在 Linux 设备驱动中,使用 dma_map_single
来进行 DMA 流式映射的代码示例如下。这段代码展示了如何将内存缓冲区映射到设备可访问的地址,并进行读写操作。
示例代码
#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 4096
static int major;
static char *buffer;
static dma_addr_t dma_handle;
static int my_open(struct inode *inode, struct file *file) {
return 0; // 成功打开
}
static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *offset) {
if (*offset >= BUFFER_SIZE) {
return 0; // 到达文件末尾
}
if (*offset + count > BUFFER_SIZE) {
count = BUFFER_SIZE - *offset; // 调整读取大小
}
if (copy_to_user(buf, buffer + *offset, count)) {
return -EFAULT; // 用户空间拷贝出错
}
*offset += count;
return count; // 返回读取字节数
}
static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) {
if (*offset >= BUFFER_SIZE) {
return 0; // 到达文件末尾
}
if (*offset + count > BUFFER_SIZE) {
count = BUFFER_SIZE - *offset; // 调整写入大小
}
if (copy_from_user(buffer + *offset, buf, count)) {
return -EFAULT; // 用户空间拷贝出错
}
// 使用 DMA 映射,将数据传输到设备或从设备传输数据到这个缓冲区。
dma_addr_t addr = dma_map_single(NULL, buffer + *offset, count, DMA_TO_DEVICE);
if (dma_mapping_error(NULL, addr)) {
printk(KERN_ERR "DMA mapping error\n");
return -EIO; // DMA 映射错误处理
}
/*
在此处执行与硬件交互的操作,例如通过 DMA 将数据发送到硬件。
假设有一个函数 send_data_to_device(dma_addr),这里调用它:
*/
// send_data_to_device(addr);
dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE); // 解除映射
*offset += count;
return count; // 返回写入字节数
}
static int my_init(void) {
major = register_chrdev(0, DEVICE_NAME, &(struct file_operations){
.owner = THIS_MODULE,
.open = my_open,
.read = my_read,
.write = my_write,
});
if (major < 0) {
printk(KERN_ALERT "Failed to register device\n");
return major;
}
buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
if (!buffer) {
unregister_chrdev(major, DEVICE_NAME);
printk(KERN_ALERT "Failed to allocate buffer\n");
return -ENOMEM;
}
memset(buffer, 0xAA, BUFFER_SIZE); // 用一些数据填充缓冲区以供演示
printk(KERN_INFO "Device registered with major number %d\n", major);
return 0; // 成功初始化
}
static void my_exit(void) {
kfree(buffer); // 释放缓冲区内存
unregister_chrdev(major, DEVICE_NAME);
printk(KERN_INFO "Device unregistered\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple DMA Device Driver Example with dma_map_single");
MODULE_AUTHOR("Your Name");
简要说明:
- DMA 映射:
dma_map_single
被用于将主机内存中的数据映射为设备可以访问的物理地址。在写操作时,它将buffer
中的数据映射为一个适合于目标设备的物理地址。
- 错误处理:
- 如果发生 DMA 映射错误,使用
dma_mapping_error
检查并相应地返回错误代码。
- 如果发生 DMA 映射错误,使用
- 解除映射:
- 一旦完成与硬件交互后,需要调用
dma_unmap_single
来解除之前创建的映射,以防止内存泄漏。
- 一旦完成与硬件交互后,需要调用
- 读和写接口:
- 定义了字符设备的读和写接口,可以与用户空间程序进行交互。
注意事项:
- 请根据实际硬件情况修改代码,比如如何与具体设备进行交互等。
- 确保你的系统支持相应的 DMA 操作,并正确配置编译环境以生成内核模块。
内容由零声教学AI助手提供,问题来源于学员提问