设备的名字和设备类

#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

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. 编译和测试

编译驱动

bash
 
复制
 
下载
make

加载驱动

bash
 
复制
 
下载
sudo insmod mydevice.ko

检查设备文件

bash
 
复制
 
下载
ls -l /dev/mydevice

你应该会看到类似这样的输出:

text
 
复制
 
下载
crw------- 1 root root 245, 0 Jun 15 15:30 /dev/mydevice

查看内核日志

bash
 
复制
 
下载
dmesg | tail

测试设备

bash
 
复制
 
下载
# 写入数据
echo "Hello, Device Driver!" | sudo tee /dev/mydevice

# 读取数据
sudo cat /dev/mydevice

卸载驱动

bash
 
复制
 
下载
sudo rmmod mydevice

4. 代码说明

  1. 设备注册流程:

    • alloc_chrdev_region() - 动态分配设备号

    • class_create() - 创建设备类

    • cdev_init() 和 cdev_add() - 初始化和添加字符设备

    • device_create() - 在 /dev 下创建设备节点

  2. 文件操作:

    • open - 打开设备

    • release - 关闭设备

    • read - 从设备读取数据

    • write - 向设备写入数据

  3. 内存管理:

    • kmalloc 分配内核内存

    • copy_to_user 和 copy_from_user 用于用户空间和内核空间的数据交换

  4. 错误处理:

    • 每个步骤都有适当的错误检查和资源释放

5. 扩展功能

你可以扩展这个驱动来支持更多功能:

  • 添加 ioctl 接口实现设备控制

  • 支持多个次设备号

  • 实现更复杂的数据结构

  • 添加同步机制(如互斥锁)保护共享数据

这个示例提供了一个完整的框架,可以帮助你理解Linux字符设备驱动的基本结构和实现方法。

 

posted @ 2025-06-14 00:18  MaxBruce  阅读(22)  评论(0)    收藏  举报