在内核中新增驱动程序的实战案例

发布于:2025-07-04 ⋅ 阅读:(12) ⋅ 点赞:(0)

一、加载内核源码

1. 安装系统内核开发包
sudo apt-get update
sudo apt-get install linux-headers-6.8.0-60-generic
2.加载版本内核(我用的6.8版本)
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.8.tar.xz
tar -xvf linux-6.8.tar.xz
3. 编译内核
cd linux-6.8
cp /boot/config-6.8.0-60-generic .config
make defconfig
make oldconfig  # 按回车接受默认值
make prepare && make modules_prepare
####当然可能会遇见的错误的解决办法:
sudo apt-get update
sudo apt-get install build-essential flex bison libssl-dev libncurses5-dev

sudo apt-get install libelf-dev

sudo apt-get install gcc-12 g++-12

二、实战案例

1.内核源码目录结构规划

        我们要在内核中添加一个简单的my_driver的驱动,目录结构如下:

drivers/
└── my_driver/                # 主驱动目录
    ├── include/              # 头文件子目录
    │   └── my_driver.h
    ├── src/                  # 源代码子目录
    │   ├── my_driver_core.c
    │   └── my_driver_device.c
    |   └── Makefile
    ├── Kconfig               # 驱动配置文件
    └── Makefile              # 驱动编译文件

2. 创建驱动代码与目录

步骤 1:进入内核源码目录
cd /home/jerry/Documents/test/linux-6.8  # 替换为你的内核源码路径
步骤 2:创建目录结构
mkdir -p drivers/my_driver/{include,src}
步骤 3:编写头文件: my_driver.h
// drivers/my_driver/include/my_driver.h
#ifndef MY_DRIVER_H
#define MY_DRIVER_H

#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/fs.h>

#define MY_DRIVER_NAME "my_driver"
#define MY_DRIVER_DEVICE_COUNT 1
#define MY_DRIVER_DEVICE_MINOR 0

int my_driver_init(void);
void my_driver_exit(void);
int my_driver_device_init(void);
void my_driver_device_exit(void);  // 新增声明

#endif /* MY_DRIVER_H */
步骤 4:编写核心驱动代码: my_driver_core.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include "../include/my_driver.h"
 
static dev_t my_driver_devt;  //设备号(主设备号 + 次设备号)
static struct cdev my_driver_cdev;  //字符设备结构,用于注册设备操作函数
static struct class *my_driver_class;  //设备类指针,用于自动创建设备节点

//设备打开函数
static int my_driver_open(struct inode *inode,struct file *file){
    printk(KERN_INFO "my_driver: device opened\n");
    return 0;
}

// 设备释放函数
static int my_driver_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO "my_driver: device released\n");
    return 0;
}

//文件操作表
static const struct file_operations my_driver_fops = {
    .open = my_driver_open,
    .release = my_driver_release,
};

//驱动初始化函数
int my_driver_init(void){
    int ret;

    //让内核中台分配一个主设备号分配设备号
    /*  
        输出参数,存储分配的设备号
        起始次设备号
        设备数量
        设备名
    */
    ret = alloc_chrdev_region(&my_driver_devt,MY_DRIVER_DEVICE_MINOR,MY_DRIVER_DEVICE_COUNT,MY_DRIVER_NAME);
    if (ret < 0) {
        printk(KERN_ERR "my_driver: failed to allocate device number\n");
        return ret;
    }

    //初始化字符设备
    cdev_init(&my_driver_cdev,&my_driver_fops);
    my_driver_cdev.owner = THIS_MODULE;

    //注册字符设备
    ret = cdev_add(&my_driver_cdev,my_driver_devt,MY_DRIVER_DEVICE_COUNT);
    if (ret < 0) {
        unregister_chrdev_region(my_driver_devt, MY_DRIVER_DEVICE_COUNT);
        printk(KERN_ERR "my_driver: failed to add cdev\n");
        return ret;
    }

    // 创建设备类(修改此处:移除 THIS_MODULE 参数)
    my_driver_class = class_create(MY_DRIVER_NAME);
    if (IS_ERR(my_driver_class)) {
        cdev_del(&my_driver_cdev);
        unregister_chrdev_region(my_driver_devt, MY_DRIVER_DEVICE_COUNT);
        printk(KERN_ERR "my_driver: failed to create class\n");
        return PTR_ERR(my_driver_class);
    }

    //创建设备节点
    device_create(my_driver_class,NULL,my_driver_devt,NULL,MY_DRIVER_NAME);

    printk(KERN_INFO "my_driver: initialized successfully\n");
    return 0;
}
EXPORT_SYMBOL(my_driver_init);

//驱动退出函数
void my_driver_exit(void){
    //销毁设备节点
    device_destroy(my_driver_class,my_driver_devt);

    //销毁设备类
    class_destroy(my_driver_class);

    //销毁字符设备
    cdev_del(&my_driver_cdev);

    //释放设备号
    unregister_chrdev_region(my_driver_devt,MY_DRIVER_DEVICE_COUNT);

    printk(KERN_INFO "my_driver: unloaded\n");
}
EXPORT_SYMBOL(my_driver_exit);


MODULE_LICENSE("GPL");

MODULE_AUTHOR("jerry");

MODULE_DESCRIPTION("My Driver Example");
步骤 5:编写设备相关代码: my_driver_device.c
// drivers/my_driver/src/my_driver_device.c
#include <linux/module.h>
#include "../include/my_driver.h"

// 设备初始化函数
int my_driver_device_init(void) {
    printk(KERN_INFO "my_driver: device module initialized\n");
    return my_driver_init();
}

// 设备退出函数
void my_driver_device_exit(void) {
    my_driver_exit();
    printk(KERN_INFO "my_driver: device module unloaded\n");
}

// 模块入口/出口
module_init(my_driver_device_init);
module_exit(my_driver_device_exit);

// 模块信息(严格遵循格式)
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("My Driver Device Module");
MODULE_AUTHOR("Your Name");
MODULE_SOFTDEP("pre: my_driver_core");  // Optional soft dependency

3. 修改 Kconfig 和 Makefile 文件

3.1 驱动目录下的 Kconfig 文件
# drivers/my_driver/Kconfig
config MY_DRIVER
    tristate "My Driver Example"
    help
      This is a simple example driver to demonstrate adding a new directory
      to the kernel source tree. It provides a basic character device.

config MY_DRIVER_DEBUG
    bool "My Driver Debug Mode"
    default n
    depends on MY_DRIVER
    help
      Enable debug messages for my driver.
3.2 驱动目录下的 Makefile 文件
# drivers/my_driver/Makefile
obj-$(CONFIG_MY_DRIVER) += src/

src-y += my_driver_core.o
src-y += my_driver_device.o

# 子目录编译规则
src-$(CONFIG_MY_DRIVER) += include/
3.3 上层目录的 Kconfig 和 Makefile 修改
修改 drivers/Kconfig(添加菜单入口)
# drivers/Kconfig
source "drivers/my_driver/Kconfig"

 

修改 drivers/Makefile(添加编译规则)
# drivers/Makefile
obj-y += my_driver/

4. 清除旧配置并重新生成
cd /home/jerry/Documents/test/linux-6.8
make mrproper  # 清除所有编译文件和配置
make defconfig  # 生成默认配置

5. 重新执行 menuconfig
make menuconfig

验证配置是否生效

menuconfig界面中,应能找到:

Device Drivers  --->
    [*] My Driver Example  --->
        [ ] My Driver Debug Mode  # 可选勾选

         光标移到新模块,然后选择:

  • <*>(内置):按 Y,驱动会编译进内核镜像,系统启动后自动加载。适合基础、必选的驱动。
  • <M>(模块):按 M,驱动编译为独立 .ko 文件,需手动 insmod 加载、rmmod 卸载。灵活度高,方便调试。
  • < >(不编译):按 N,驱动不会参与编译,适合暂时不用的功能。

        选好后,按 <Enter> 确认,若有子选项(比如之前定义的 MY_DRIVER_DEBUG),可进入子菜单进一步配置。

        根据编译方式,执行不同命令:

  • 驱动内置(Y:直接编译整个内核

    make -j$(nproc)  # 用所有 CPU 核心加速编译,nproc 显示核心数
    sudo make modules_install  # 安装内核模块(若有其他模块)
    sudo make install  # 安装内核、更新引导(不同系统可能需额外操作,如更新 grub)
    

    编译完成后,重启系统(sudo reboot),新内核启动后驱动随内核加载。

  • 驱动编译为模块(M:单独编译模块

    make -j$(nproc) modules  # 只编译模块,生成 .ko 文件
    

    编译出的 my_driver.ko(路径一般在 drivers/my_driver/src/ 或对应模块目录),可用以下命令管理:

    sudo insmod drivers/my_driver/src/my_driver.ko  # 加载模块
    lsmod | grep my_driver  # 查看是否加载成功
    sudo rmmod my_driver  # 卸载模块

 


网站公告

今日签到

点亮在社区的每一天
去签到