小工具知识网

当前位置: 首页 > 行业知识 > Linux 驱动开发基础知识——总线设备驱动模型(八)

行业知识

Linux 驱动开发基础知识——总线设备驱动模型(八)

2026-10-20 21:55:09 来源:互联网转载

��个人名片:

作者简介:学生

个人主页:妄北y

个人QQ:2061314755

个人邮箱:2061314755@qq.com

个人WeChat:Vir2021GKBS

本文由妄北y原创,首发CSDN

座右铭:大多数人想要改造这个世界,但却罕有人想改造自己。

专栏导航:

妄北y系列专栏导航:

C/C++的基础算法:C/C++是一种常用的编程语言,可以用于实现各种算法,这里我们对一些基础算法进行了详细的介绍与分享。

QT基础入门学习:对QT的基础图形化页面设计进行了一个简单的学习与认识,利用QT的基础知识进行了翻金币小游戏的制作

Linux基础编程:初步认识什么是Linux,为什么学Linux,安装环境,进行基础命令的学习,入门级的shell编程。

Linux应用开发基础开发:分享Linux的基本概念、命令行操作、文件系统、用户和权限管理等,网络编程相关知识,TCP/IP 协议、套接字(Socket)编程等,可以实现网络通信功能。

Linux项目开发:Linux基础知识的实践,做项目是最锻炼能力的一个学习方法,这里我们会学习到一些简单基础的项目开发与应用,而且都是毕业设计级别的哦。

非常期待和您一起在这个小小的互联网世界里共同探索、学习和成长。 ✨✨ 欢迎订阅本专栏 ✨✨ 

​

文章介绍:

本篇文章对Linux驱动基础学习的相关知识进行分享!

Linux驱动程序 = 驱动框架 + 硬件操作 = 驱动框架 + 单片机,我们需要掌握别人的驱动框架,了解框架的思想,才能更好的去修改和运用别人的框架

如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加油,一起奔跑,让我们顶峰相见!!!

感谢大家点赞收藏⭐评论✍️

目录: 

目录

一、LED 模板驱动程序的改造

1.1 原来的框架

1.2 要实现的框架 

二、代码分析:

2.1 board_A_led.c

2.2 chip_demo_gpio.c 

2.3 leddrv.c

三、注意事项

3.1  release 函数

3.2 EXPORT_SYMBOL

四、上机测试

4.1 编译

4.2 挂载到开发板 


一、LED 模板驱动程序的改造

1.1 原来的框架

1.2 要实现的框架 

二、代码分析:

2.1 board_A_led.c

        平台设备文件

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "led_resource.h"static void led_dev_release(struct device *dev){}static struct resource resources[] = {        {                .start = GROUP_PIN(3,1),                .flags = IORESOURCE_IRQ,                .name = "100ask_led_pin",        },        {                .start = GROUP_PIN(5,8),                .flags = IORESOURCE_IRQ,                .name = "100ask_led_pin",        },};static struct platform_device board_A_led_dev = {        .name = "100ask_led",        .num_resources = ARRAY_SIZE(resources),        .resource = resources,        .dev = {                .release = led_dev_release,         },};static int __init led_dev_init(void){    int err;        err = platform_device_register(&board_A_led_dev);           return 0;}static void __exit led_dev_exit(void){    platform_device_unregister(&board_A_led_dev);}module_init(led_dev_init);module_exit(led_dev_exit);MODULE_LICENSE("GPL");

第27~38行:

static struct resource resources[] = {

如果我们再想增加一盏灯的话,我们可以在这里的平台设备在这里的资源再增加一盏灯

    

  .start = GROUP_PIN(3,1),

指定第3组里面的第1个引脚

第41~48行:

static struct platform_device board_A_led_dev = {

注册一个board_A_led_dev

 将上面代码资源数组中的资源添加到platform_device中

 .name = "100ask_led":平台设备的名字,与chip_demo_gpio_driver进行匹配

 .num_resources = ARRAY_SIZE(resources):资源的个数

 .resource = resources:指向这个数组

第50~62行:

board_A.c 作为一个可加载模块,里面也有入口函数、出口函数。

static int __init led_dev_init(void)static void __exit led_dev_exit(void)

设置注册好board_A_led_dev的入口函数和出口函数

 第64~67行:

module_init(led_dev_init);module_exit(led_dev_exit);

修饰入口函数和出口函数

MODULE_LICENSE("GPL");

确定GPL协议

第50~57行:

        在入口函数中注册 platform_device 结构体,在 platform_device 结构体中指定使用哪个 GPIO 引脚。

static int __init led_dev_init(void){    int err;        err = platform_device_register(&board_A_led_dev);           return 0;}

第23~48行:board_A_led_dev 结构体定义如下:

static void led_dev_release(struct device *dev){}static struct resource resources[] = {        {                .start = GROUP_PIN(3,1),                .flags = IORESOURCE_IRQ,                .name = "100ask_led_pin",        },        {                .start = GROUP_PIN(5,8),                .flags = IORESOURCE_IRQ,                .name = "100ask_led_pin",        },};static struct platform_device board_A_led_dev = {        .name = "100ask_led",        .num_resources = ARRAY_SIZE(resources),        .resource = resources,        .dev = {                .release = led_dev_release,         },};

        在 resouces 数组中指定了 2 个引脚(第 27~38 行); 我们还提供了一个空函数         led_dev_release(第 23~25 行),它被赋给 board_A_led_dev 结构体(第 46 行),这个函数在卸载 platform_device 时会 被调用,如果不提供的话内核会打印警告信息。

第27~38行:

static struct resource resources[] = {

定义资源数组

2.2 chip_demo_gpio.c 

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "led_opr.h"#include "leddrv.h"#include "led_resource.h"static int g_ledpins[100];static int g_ledcnt = 0;static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       {       //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);        printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));    switch(GROUP(g_ledpins[which]))    {        case 0:        {            printk("init pin of group 0 ...\n");            break;        }        case 1:        {            printk("init pin of group 1 ...\n");            break;        }        case 2:        {            printk("init pin of group 2 ...\n");            break;        }        case 3:        {            printk("init pin of group 3 ...\n");            break;        }    }        return 0;}static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */{    //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");    printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));    switch(GROUP(g_ledpins[which]))    {        case 0:        {            printk("set pin of group 0 ...\n");            break;        }        case 1:        {            printk("set pin of group 1 ...\n");            break;        }        case 2:        {            printk("set pin of group 2 ...\n");            break;        }        case 3:        {            printk("set pin of group 3 ...\n");            break;        }    }    return 0;}static struct led_operations board_demo_led_opr = {    .init = board_demo_led_init,    .ctl  = board_demo_led_ctl,};struct led_operations *get_board_led_opr(void){    return &board_demo_led_opr;}static int chip_demo_gpio_probe(struct platform_device *pdev){    struct resource *res;    int i = 0;    while (1)    {        res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);        if (!res)            break;                g_ledpins[g_ledcnt] = res->start;        led_class_create_device(g_ledcnt);        g_ledcnt++;    }    return 0;    }static int chip_demo_gpio_remove(struct platform_device *pdev){    struct resource *res;    int i = 0;    while (1)    {        res = platform_get_resource(pdev, IORESOURCE_IRQ, i);        if (!res)            break;                led_class_destroy_device(i);        i++;        g_ledcnt--;    }    return 0;}static struct platform_driver chip_demo_gpio_driver = {    .probe      = chip_demo_gpio_probe,    .remove     = chip_demo_gpio_remove,    .driver     = {        .name   = "100ask_led",    },};static int __init chip_demo_gpio_drv_init(void){    int err;        err = platform_driver_register(&chip_demo_gpio_driver);     register_led_operations(&board_demo_led_opr);        return 0;}static void __exit lchip_demo_gpio_drv_exit(void){    platform_driver_unregister(&chip_demo_gpio_driver);}module_init(chip_demo_gpio_drv_init);module_exit(lchip_demo_gpio_drv_exit);MODULE_LICENSE("GPL");

 第138~144行:

chip_demo_gpio.c 中注册 platform_driver 结构体 , 它使用Bus/Dev/Drv 模型,当有匹配的 platform_device 时,它的 probe 函数就会被调用。 

在 probe 函数中所做的事情跟之前的代码没有差别。

138 static struct platform_driver chip_demo_gpio_driver = {139 .probe = chip_demo_gpio_probe,140 .remove = chip_demo_gpio_remove,141 .driver = {142 .name = "100ask_led",143 },144 };145146 static int __init chip_demo_gpio_drv_init(void)147 {148 int err;149150 err = platform_driver_register(&chip_demo_gpio_driver);151 register_led_operations(&board_demo_led_opr);152153 return 0;154 }

注册一个chip_demo_gpio_driver

.driver  = {        .name   = "100ask_led",   },

设备名称,与board_A_led_dev中设备名称进行对应

.probe      = chip_demo_gpio_probe:记录引脚

.remove     = chip_demo_gpio_remove:销毁设备

         第 150 行:向内核注册一个 platform_driver 结构体,这个结构体的核心在于第 100 行的 chip_demo_gpio_probe 函数。 chip_demo_gpio_probe 函数代码如下:

100 static int chip_demo_gpio_probe(struct platform_device *pdev)101 {102 struct resource *res;103 int i = 0;104105 while (1)106 {107 res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);108 if (!res)109 break;110111 g_ledpins[g_ledcnt] = res->start;112 led_class_create_device(g_ledcnt);113 g_ledcnt++;114 }115 return 0;116117 }

        第 107 行:从匹配的 platform_device 中获取资源,确定 GPIO 引脚。

        第 111 行:把引脚记录下来,在操作硬件时要用。

        第 112 行:新发现了一个 GPIO 引脚,就调用上层驱动的代码创建设备节点

第146~159行:

static int __init chip_demo_gpio_drv_init(void)static void __exit lchip_demo_gpio_drv_exit(void)

设置注册好chip_demo_gpio_driver的入口函数和出口函数

 第64~67行:

module_init(chip_demo_gpio_drv_init);module_exit(lchip_demo_gpio_drv_exit);

修饰入口函数和出口函数

MODULE_LICENSE("GPL");

确定GPL协议

第100~117行:

static int chip_demo_gpio_probe(struct platform_device *pdev)

为board_A_led_dev提供 .probe 

        第105~144行:从资源里确定引脚

                第107行:获得设备pdev中的第i个IORESOURCE_IRQ资源

                第111行:记录引脚

                第112行:创建device

第119~135行:

static int chip_demo_gpio_remove(struct platform_device *pdev)

为board_A_led_dev提供 .remove

        第126行:销毁设备pdev中的第i个IORESOURCE_IRQ资源

操作硬件的代码如下,第 31、63 行的代码里用到了数组 g_ledpins,里面的值来自 platform_device,在 probe 函数中根据 platform_device 的资源确定了引脚:

static int g_ledpins[100];static int g_ledcnt = 0;static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */       {       //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);        printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));    switch(GROUP(g_ledpins[which]))    {        case 0:        {            printk("init pin of group 0 ...\n");            break;        }        case 1:        {            printk("init pin of group 1 ...\n");            break;        }        case 2:        {            printk("init pin of group 2 ...\n");            break;        }        case 3:        {            printk("init pin of group 3 ...\n");            break;        }    }        return 0;}static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */{    //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");    printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));    switch(GROUP(g_ledpins[which]))    {        case 0:        {            printk("set pin of group 0 ...\n");            break;        }        case 1:        {            printk("set pin of group 1 ...\n");            break;        }        case 2:        {            printk("set pin of group 2 ...\n");            break;        }        case 3:        {            printk("set pin of group 3 ...\n");            break;        }    }    return 0;}static struct led_operations board_demo_led_opr = {    .init = board_demo_led_init,    .ctl  = board_demo_led_ctl,};struct led_operations *get_board_led_opr(void){    return &board_demo_led_opr;}

第30和61行: 打印出想操作的引脚

第32和63行:判断引脚

第151行:

register_led_operations(&board_demo_led_opr);

 底层向上层调用

2.3 leddrv.c

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "led_opr.h"/* 1. 确定主设备号                  */static int major = 0;static struct class *led_class;struct led_operations *p_led_opr;#define MIN(a, b) (a ctl(minor, status);return 1;}static int led_drv_open (struct inode *node, struct file *file){int minor = iminor(node);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/* 根据次设备号初始化LED */p_led_opr->init(minor);return 0;}static int led_drv_close (struct inode *node, struct file *file){printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;}/* 2. 定义自己的file_operations结构体                                              */static struct file_operations led_drv = {.owner = THIS_MODULE,.open    = led_drv_open,.read    = led_drv_read,.write   = led_drv_write,.release = led_drv_close,};/* 4. 把file_operations结构体告诉内核:注册驱动程序                                *//* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */static int __init led_init(void){int err;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);major = register_chrdev(0, "100ask_led", &led_drv);  /* /dev/led */led_class = class_create(THIS_MODULE, "100ask_led_class");err = PTR_ERR(led_class);if (IS_ERR(led_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "led");return -1;}return 0;}/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */static void __exit led_exit(void){printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);class_destroy(led_class);unregister_chrdev(major, "100ask_led");}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(led_init);module_exit(led_exit);MODULE_LICENSE("GPL");

 第30~33行:创建设备

       

void led_class_create_device(int minor)
EXPORT_SYMBOL(led_class_create_device);

产生依赖只有使用led_class_create_device才能产生led_class_create_device

第34~37行:销毁设备

void led_class_destroy_device(int minor)
EXPORT_SYMBOL(led_class_destroy_device);

产生依赖只有使用(led_class_destroy_device才能产生led_class_destroy_device

第38~41行:底层向上层注册函数

void register_led_operations(struct led_operations *opr)
EXPORT_SYMBOL(register_led_operations);

产生依赖只有底层调用才能产生register_led_operations

三、注意事项

3.1  release 函数

        如果 platform_device 中不提供 release 函数,如下图所示不提供红框部分的函数:

        则在调用 platform_device_unregister 时会出现警告,如下图所示:

        你可以提供一个 release 函数,如果实在无事可做,把这函数写为空。 

3.2 EXPORT_SYMBOL

        a.c 编译为 a.ko,里面定义了 func_a;如果它想让 b.ko 使用该函数,那么 a.c 里需要导出此函数(如果 a.c, b.c 都编进内核,则无需导出):

EXPORT_SYMBOL(led_device_create);

        并且,使用时要先加载 a.ko。如果先加载 b.ko,会有类似如下“Unknown symbol”的提示:

四、上机测试

4.1 编译

编译程序,把代码上传代服务器后执行 make 命令。

4.2 挂载到开发板 

在开发板上挂载 NFS 

[root@100ask:/mnt]# insmod board_A_led.ko[root@100ask:/mnt]# insmod  leddrv.ko[root@100ask:/mnt]# insmod chip_demo_gpio.ko[root@100ask:/mnt]# ls /dev/100ask_led*[root@100ask:/mnt]# ./ledtest /dev/100ask_led0 on[root@100ask:/mnt]# ./ledtest /dev/100ask_led0 off

 

大佬觉得有用的话点个赞 呗。

❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!

如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!

任务在无形中完成,价值在无形中升华,让我们一起加油吧!

​

​

上一篇:为什么华为官网手机贵?

下一篇:外地人去医院看病怎么样备案(外地看病如何备案)