编写一个简单的Linux设备驱动程序可以是一个很好的学习实践项目。下面我将介绍如何创建一个非常基础的字符设备驱动程序,它可以接收用户从/dev目录下对应的设备节点读写的简单数据。
### 简单Linux字符设备驱动程序实例
假设我们要创建一个能存储一个字节数据并允许用户读写的设备驱动。以下是基本步骤和相关代码片段:
1. **声明设备结构体**:
在驱动程序中,首先需要定义一个字符设备结构体变量,并填充必要的字段。
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "mychardev" // 设备名
#define BUF_SIZE 1 // 缓冲区大小
static char buffer[BUF_SIZE]; // 内存缓冲区
static dev_t dev_num; // 设备号
static struct cdev my_cdev; // 字符设备结构体
static struct class *char_class; // 设备类
// 初始化和释放函数
static int __init my_char_driver_init(void);
static void __exit my_char_driver_exit(void);
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
// 文件操作结构体
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = device_open,
.read = device_read,
.write = device_write,
.release = device_release,
};
static int __init my_char_driver_init(void) {
int ret;
// 分配和注册设备号
ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME);
if (ret < 0) {
printk(KERN_ERR "Failed to allocate device number\n");
return ret;
}
// 初始化字符设备
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
// 添加字符设备到系统
ret = cdev_add(&my_cdev, dev_num, 1);
if (ret < 0) {
printk(KERN_ERR "Failed to add character device\n");
goto err_cdev_add;
}
// 创建设备类并注册
char_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(char_class)) {
printk(KERN_ERR "Failed to create device class\n");
ret = PTR_ERR(char_class);
goto err_class_create;
}
// 创建设备节点
device_create(char_class, NULL, dev_num, NULL, DEVICE_NAME);
return 0;
err_class_create:
cdev_del(&my_cdev);
err_cdev_add:
unregister_chrdev_region(dev_num, 1);
return ret;
}
static void __exit my_char_driver_exit(void) {
device_destroy(char_class, dev_num);
class_unregister(char_class);
class_destroy(char_class);
cdev_del(&my_cdev);
unregister_chrdev_region(dev_num, 1);
}
// 设备操作函数实现
static int device_open(struct inode *inode, struct file *file) {
return 0;
}
static int device_release(struct inode *inode, struct file *file) {
return 0;
}
static ssize_t device_read(struct file *filp, char *buf, size_t count, loff_t *offset) {
if (*offset >= BUF_SIZE)
return 0;
copy_to_user(buf, buffer + *offset, min(BUF_SIZE - *offset, count));
*offset += count;
return count;
}
static ssize_t device_write(struct file *filp, const char *buf, size_t count, loff_t *offset) {
if (*offset >= BUF_SIZE)
return -EINVAL;
copy_from_user(buffer + *offset, buf, min(BUF_SIZE - *offset, count));
*offset += count;
return count;
}
module_init(my_char_driver_init);
module_exit(my_char_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A Simple Character Device Driver Example");
这个例子中,我们创建了一个只有一字节的内存缓冲区作为设备的数据存储区域。`device_read`和`device_write`函数分别处理了对这个缓冲区的读写操作。当用户空间的应用程序打开/dev/mychardev节点并进行读写时,就会调用这些函数。
要编译此模块,需要将其保存为一个.c文件(比如`my_char_driver.c`),并创建相应的Makefile文件来编译生成内核模块。
请确保你的Linux内核开发环境已经正确设置,包括内核头文件和编译工具链等。在实际开发过程中,还需要考虑错误处理、并发访问控制等问题。