下面将介绍一个简单的Linux内核驱动程序(部分),用于处理一个假设的字符设备(鼠标、键盘、串口、LED...),具体介绍如下:
1 头文件导入
#include <linux/fs.h> // 用于声明 file_operations 结构体和其他文件系统相关函数。
#include <linux/module.h> // 用于声明 module_init 和 module_exit 宏,并定义模块相关的功能。
#include <linux/init.h> // 用于声明 __init 和 __exit 宏。
#include <linux/device.h> // 用于声明 class 和 device 相关的结构体和函数。
#include <linux/uaccess.h> // 用于处理用户空间和内核空间之间的数据传输(如 copy_from_user)。
#include <linux/types.h> // 用于定义 dev_t 类型,它表示设备号。
#include <asm/io.h> // 用于声明 ioremap 和 iounmap 函数。
这些头文件提供了内核编程所需的基础功能,例如设备文件操作、内核模块初始化和退出、与用户空间进行交互等。
2 全局变量和宏定义
1)'pin4'为假设的设备,首先创建设备类'pin4_class'和设备文件'pin4_class_dev',用于在'/dev'目录下创建设备节点。
static struct class *pin4_class;
static struct device *pin4_class_dev;
2)定义设备号,包括主设备号(标识设备类型)及次设备号(标识设备实例),同时定义设备名称。
static dev_t devno; // 设备号,用于标识内核中的设备。
static int major = 231; // 主设备号231是为这个驱动手动分配的。
static int minor = 0; // 次设备号。
static char *module_name = "pin4"; // 模块名称,用于标识和注册这个驱动模块。
3 设备操作函数
3.1 打开操作
// 打开设备文件时调用
static int pin4_open(struct inode *inode, struct file *file)
{
printk("pin4_open\n"); // 使用内核日志系统打印消息,类似于用户态的 printf。
return 0;
}
当用户空间程序通过'open()'调用打开设备文件时,内核会调用该函数。返回值0表示设备文件被正常打开,如果返回一个负值,则打开失败。
- 'struct inode *inode':指向设备文件的inode结构体,inode是文件在文件系统中的抽象表示,包含文件的元数据(权限、类型、大小...)。对于字符设备来说,该参数可以用来获取设备号;
- 'struct file *file':指向代表打开文件的结构体,包含文件的当前状态(文件指针、访问模式...)。
3.2 写入操作
// 写入设备文件时调用
static ssize_t pin4_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
printk("pin4_write\n"); // 打印写操作的日志消息。
return 0;
}
该函数在数据从用户空间传递到内核空间时被调用,通常用于处理写操作,将数据写入设备或存储在驱动程序中。
- 'struct file *file':指向代表打开文件的结构体;
- 'const char __user *buf':指向用户空间缓冲区的指针,缓冲区中包含了用户程序传递的数据;
- 'size_t count':表示用户空间缓冲区'buf'的大小(以字节为单位),即写入的数据的字节数,驱动程序需要根据这个值来处理要写入的数据长度;
- 'loff_t *ppos':指向文件位置指针的指针,通常用于标记文件当前的偏移量。
4 设备文件操作结构体
static struct file_operations pin4_fops = {
.owner = THIS_MODULE,
.open = pin4_open,
.write = pin4_write,
};
- THIS_MODULE:表示这个模块的所有者是当前模块,确保在模块被卸载前,不会被意外地引用或卸载;
- pin4_open:将 pin4_open 函数指针赋值给 open 成员,用于处理设备文件的打开操作;
- pin4_write:将 pin4_write 函数指针赋值给 write 成员,用于处理设备文件的写操作。
结构体'pin4_fops'包含了字符设备的各种操作,内核会通过这个结构体来调用相应的设备操作函数。
5 模块初始化函数
int __init pin4_drv_init(void)
{
int ret;
devno = MKDEV(major, minor); // 生成设备号,使用主设备号和次设备号。
ret = register_chrdev(major, module_name, &pin4_fops);
// 注册字符设备,将设备号和操作函数与内核关联起来,内核可以识别这个设备并知道如何处理它。
pin4_class = class_create(THIS_MODULE, "myfirstdemo");
// 创建一个类,在 `/sys/class` 目录下生成一个对应的设备类。
pin4_class_dev = device_create(pin4_class, NULL, devno, NULL, module_name);
// 创建设备文件,实际在 `/dev` 目录下创建对应的设备节点。
return 0; // 返回 0 表示初始化成功。
}
- 函数头部
int __init pin4_drv_init(void)
'_init'是一个内核宏,用于告诉编译器该函数仅在模块初始化时使用,'pin4_drv_init'是驱动的初始化函数名称。
- 变量声明
int ret;
用于存储函数调用的返回值,以检查操作是否成功,这里主要用于检测register_chrdev函数的返回状态。
- 生成设备号
devno = MKDEV(major, minor);
MKDEV(major, minor)是一个内核宏,用于生成一个设备号('dev_t'类型),设备号用于唯一标识内核中的某个设备。
- 注册字符设备
ret = register_chrdev(major, module_name, &pin4_fops);
该函数用于注册字符设备驱动程序,告诉内核如何处理与该设备相关的操作(例如打开、关闭、读写等)。major为主设备号,设备名称module_name用于标识这个设备,&pin4_fops是指向file_operations结构体的指针,结构体中包含了操作该字符设备的函数指针(如open、read、write...),内核将通过这些函数指针来操作设备。
- 创建设备类
pin4_class = class_create(THIS_MODULE, "myfirstdemo");
函数class_create用于创建一个设备类,在'/sys/class'目录下生成一个对应的设备类,这个类的主要作用是为设备文件创建一个逻辑分组,便于管理和分类。THIS_MODULE表示当前模块的指针,"myfirstdemo"为类的名称。
- 创建设备文件
pin4_class_dev = device_create(pin4_class, NULL, devno, NULL, module_name);
函数device_create用于创建设备文件,在'/dev'目录下生成一个设备节点,用户可以通过这个节点实现与设备的交互。第一个NULL参数为指向父设备的指针(此处没有父设备),第二个NULL参数为私有数据指针。
6 模块清理函数
void __exit pin4_drv_exit(void)
{
device_destroy(pin4_class, devno); // 删除设备文件。
class_destroy(pin4_class); // 删除设备类。
unregister_chrdev(major, module_name); // 取消字符设备的注册。
}
其中unregister_chrdev为取消字符设备的注册,释放设备号,允许其他模块使用这个设备号。
7 模块入口和出口声明
module_init(pin4_drv_init);
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");
- module_init定义模块的初始化入口,内核加载模块时会调用模块的初始化函数;
- module_exit定义模块的清理出口,内核卸载模块时会调用模块的清理函数函数;
- MODULE_LICENSE声明该模块使用 GPLv2 许可证,确保模块符合开源要求,并可以安全地被内核加载。
总结
该驱动程序是一个简单的字符设备驱动,主要用来展示如何在 Linux 内核中创建字符设备,并处理基本的文件操作(打开和写入)。程序在加载时会创建一个名为'/dev/pin4'的设备文件,用户可以通过文件操作访问这个设备。驱动程序还展示了如何注册和注销设备、创建和销毁设备文件,以及处理用户态和内核态之间的简单交互。
标签:文件,为例,--,内核,模块,驱动,pin4,class,设备 From: https://blog.csdn.net/2401_82904490/article/details/141184897