设备的名字和设备类
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#define DEVICE_NAME "mydevice"
#define CLASS_NAME "myclass"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple character device driver example");
static int major_number;
static struct class* myclass = NULL;
static struct device* mydevice = NULL;
static struct cdev my_cdev;
// 设备缓冲区
static char *device_buffer;
static int buffer_size = 1024;
static int buffer_offset = 0;
// 文件操作结构体
static int mydevice_open(struct inode *inode, struct file *file);
static int mydevice_release(struct inode *inode, struct file *file);
static ssize_t mydevice_read(struct file *file, char __user *buf, size_t len, loff_t *offset);
static ssize_t mydevice_write(struct file *file, const char __user *buf, size_t len, loff_t *offset);
static struct file_operations fops = {
.open = mydevice_open,
.release = mydevice_release,
.read = mydevice_read,
.write = mydevice_write,
};
// 打开设备
static int mydevice_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "mydevice: Device opened\n");
return 0;
}
// 释放设备
static int mydevice_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "mydevice: Device closed\n");
return 0;
}
// 读取设备
static ssize_t mydevice_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
{
int bytes_to_read;
int bytes_read = 0;
if (*offset >= buffer_offset)
return 0;
bytes_to_read = min(len, (size_t)(buffer_offset - *offset));
if (copy_to_user(buf, device_buffer + *offset, bytes_to_read)) {
return -EFAULT;
}
*offset += bytes_to_read;
bytes_read = bytes_to_read;
printk(KERN_INFO "mydevice: Read %d bytes\n", bytes_read);
return bytes_read;
}
// 写入设备
static ssize_t mydevice_write(struct file *file, const char __user *buf, size_t len, loff_t *offset)
{
int bytes_to_write;
int bytes_written = 0;
bytes_to_write = min(len, (size_t)(buffer_size - buffer_offset));
if (copy_from_user(device_buffer + buffer_offset, buf, bytes_to_write)) {
return -EFAULT;
}
buffer_offset += bytes_to_write;
bytes_written = bytes_to_write;
printk(KERN_INFO "mydevice: Wrote %d bytes\n", bytes_written);
return bytes_written;
}
// 模块初始化
static int __init mydevice_init(void)
{
dev_t dev = 0; // 显式初始化设备号变量
// 分配设备号(修复此处)
if (alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME) < 0) {
printk(KERN_ALERT "Failed to allocate device number\n");
return -1;
}
major_number = MAJOR(dev); // 提取主设备号
printk(KERN_INFO "mydevice: Registered with major number %d\n", major_number);
// 创建设备类
myclass = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(myclass)) {
unregister_chrdev_region(major_number, 1);
printk(KERN_ALERT "Failed to create device class\n");
return PTR_ERR(myclass);
}
// 初始化字符设备
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
// 添加字符设备
if (cdev_add(&my_cdev, major_number, 1) < 0) {
class_destroy(myclass);
unregister_chrdev_region(major_number, 1);
printk(KERN_ALERT "Failed to add character device\n");
return -1;
}
// 创建设备节点
mydevice = device_create(myclass, NULL, MKDEV(major_number, 0), NULL, DEVICE_NAME);
if (IS_ERR(mydevice)) {
cdev_del(&my_cdev);
class_destroy(myclass);
unregister_chrdev_region(major_number, 1);
printk(KERN_ALERT "Failed to create device\n");
return PTR_ERR(mydevice);
}
// 分配设备缓冲区
device_buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!device_buffer) {
device_destroy(myclass, MKDEV(major_number, 0));
cdev_del(&my_cdev);
class_destroy(myclass);
unregister_chrdev_region(major_number, 1);
printk(KERN_ALERT "Failed to allocate device buffer\n");
return -ENOMEM;
}
printk(KERN_INFO "mydevice: Device driver loaded successfully\n");
return 0;
}
// 模块清理
static void __exit mydevice_exit(void)
{
// 释放资源
device_destroy(myclass, MKDEV(major_number, 0));
cdev_del(&my_cdev);
class_destroy(myclass);
unregister_chrdev_region(major_number, 1);
kfree(device_buffer);
printk(KERN_INFO "mydevice: Device driver unloaded\n");
}
module_init(mydevice_init);
module_exit(mydevice_exit);
2. Makefile
obj-m := mydevice.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
3. 编译和测试
编译驱动
make
加载驱动
sudo insmod mydevice.ko
检查设备文件
ls -l /dev/mydevice
你应该会看到类似这样的输出:
crw------- 1 root root 245, 0 Jun 15 15:30 /dev/mydevice
查看内核日志
dmesg | tail
测试设备
# 写入数据
echo "Hello, Device Driver!" | sudo tee /dev/mydevice
# 读取数据
sudo cat /dev/mydevice
卸载驱动
sudo rmmod mydevice
4. 代码说明
-
设备注册流程:
-
alloc_chrdev_region()- 动态分配设备号 -
class_create()- 创建设备类 -
cdev_init()和cdev_add()- 初始化和添加字符设备 -
device_create()- 在/dev下创建设备节点
-
-
文件操作:
-
open- 打开设备 -
release- 关闭设备 -
read- 从设备读取数据 -
write- 向设备写入数据
-
-
内存管理:
-
kmalloc分配内核内存 -
copy_to_user和copy_from_user用于用户空间和内核空间的数据交换
-
-
错误处理:
-
每个步骤都有适当的错误检查和资源释放
-
5. 扩展功能
你可以扩展这个驱动来支持更多功能:
-
添加
ioctl接口实现设备控制 -
支持多个次设备号
-
实现更复杂的数据结构
-
添加同步机制(如互斥锁)保护共享数据
这个示例提供了一个完整的框架,可以帮助你理解Linux字符设备驱动的基本结构和实现方法。

浙公网安备 33010602011771号