• Linux 设备驱动模型platform==Linux驱动开发5


    一、设备驱动模型简介

    1、什么是设备驱动模型

    • (1)类 class、总线 bus、设备 device、驱动 driver。
    • (2)kobject 和对象生命周期
    • (3)sysfs。
    • (4)udev。

    2、为什么需要设备驱动模型

    • (1)早期内核(2.4 之前)没有统一的设备驱动模型,但照样可以使用
    • (2)2.6 版本正式引入设备模型,目的是在设备越来越多,功耗要求等新特性要求的情况下 更易用、更优秀。
    • (3)设备驱动模型负责统一实现和维护一些特性,比如:电源管理、热插拔、对象生命周 期、用户空间和内核空间交互等基础设施。
    • (4)驱动模型的目的是简化驱动的编写,但客观上其本身的设计和实现很复杂。

    3、驱动开发的两个点

    • (1)驱动源码本身编写、调试。重点在于对硬件了解。
    • (2)驱动什么时候被安装、驱动中的函数什么时候被调用。跟硬件无关,与设备驱动模型 有关。

    二、设备驱动模型的底层架构

    1、kobject

    • (1)定义在 linux/kobject.h 中。
    • (2)各种对象最基本的单元,提供各种公共服务,如:对象引用计数、维护对象链表、对 象上锁、对用户空间表示。
    • (3)设备驱动模型中各种对象内部都会包含一个 kobject。
    • (4)地位相当于面向对象体系架构中的总基类。

    2、kobj_type

    • (1)很多书中简称 ktype,每一个 kobject 都需要绑定一个 ktype 来提供相应功能。
    • (2)关键点 1:sysfs_ops,提供该对象中 sysfs 中的操作方法(show 和 store)。
    • (3)关键点 2:attribute,提供中 sysfs 中以文件形式存在的属性,其实就是应用接口。

    3、kset

    • (1)主要作用是做顶层 kobject 的容器类。
    • (2)主要目的是将各个 kobject(代表着各个对象)组织出目录层次架构。
    • (3)可以认为 kset 就是为了在 sysfs 中弄出目录层次,从而让设备驱动模型中的多个对象能 够有层次有逻辑性地组织在一起

    三、总线式设备驱动组织方式

    1、总线(bus)

    • (1)物理上真实总线及其作用:如一栋楼的电线分布,方便连接和管理。
    • (2)驱动框架中的总线式设计:操作系统创建各种总线如 USB 总线,然后所有 USB 设备都归 USB 总线管理,USB总线中存放着各种USB设备(支线 1)和其对应驱动(支线 2),一旦有新的 USB 设备插入, 即可找到其对应的驱动。
    • (3)bus_type 结构体:总线模板,关键是match函数(用来匹配设备跟驱动)和 uevent(用来在用户空间自动创建设备节点)。

    2、设备

    • (1)struct device 是硬件设备在内核驱动框架中的抽象。
    • (2)device_register 用于向内核框架注册一个设备。
    • (3)通常 device 不会单独使用,而是被包含在一个具体设备结构体中,如 struct usb_device。

    3、驱动

    • (1)struct device_driver 是驱动程序在内核驱动框架中的抽象。
    • (2)关键元素 1:name,驱动程序的名字,很重要,经常作为设备和驱动的匹配依据。
    • (3)关键元素 2:probe,驱动程序探测函数,用来检测一个设备是否可以被该驱动所管理。

    4、类

    • (1)相关结构体:struct class 和 struct class_device。
    • (2)udev 使用离不开 class。
    • (3)class 的真正意义在于作为一个同类设备的容器。class 是一种人造概念,目的是为了对 各种设备进行分类管理。class 在分类的同时还对每个类贴上了一些标签,这也是我们 写驱动的基础设施。

    5、总结

    • (1)模型思想很重要,其实就是面向对象思想。
    • (2)全是结构体套结构体,对基本功(语言功底和大脑复杂度)要求很高。

    四、platform平台总线工作原理1

    1、何为平台总线

    • (1)总线(驱动模型)的一种,相对于 usb、pci、i2c 等物理总线来说,platform 总线是虚拟的,抽象出来的。
    • (2)回顾裸机中讲的,CPU 与外部通信的两种方式:地址总线式连接和专用接口式连接。 平台总线对应地址值总线式连接设备,也就是 SoC 内部集成的各种内部外设。
    • (3)思考:为什么要有平台总线?进一步思考为什么要有总线。:
      解决不带总线设备也拥有总线的功能,虚拟出来的总线平台platform,便于管理。

    2、平台总线下管理的两员大将

    • (1)platform 工作体系都定义在 driver/base/platform.c 中
    • (2)两个结构体:platform_device 和 platform_driver。
    • (3)两个接口函数:platform_device_register 和 platform_driver_register。
      struct platform_device
      {
      	const char* name;//平台总线下设备的名字
      	int id;
      	struct device dev;//所有设备通用的属性部分
      	u32 num_resources;//设备使用到的resource的个数
      	struct resource* resource;//设备使用到的资源数组的首地址
      	const struct platform_device_id* id_entry;//设备id表
      
      	/*arch specific addition*/
      	struct pdev_archdata archdata;//自留地,用来提供扩展性的
      }
      
      struct platform_driver
      {
      	int(*probe)(struct platform_device*);//驱动探测函数
      	int(*remove)(struct platform_device*);//去掉一个设备
      	void(*shutdown)(struct platform_device*);//关闭一个设备
      	int(*suspend)(struct platform_device*,pm_message_t state);
      	int(*resume)(struct platform_device*);
      	struct device_driver driver;//所有设备共用的一些属性
      	const struct platform_device_id* id_table;//设备ID 表
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23

    五、platform 平台总线工作原理 2

    1、平台总线体系工作流程

    • (1)第一步:系统启动时在 bus 系统中注册 platform。
    • (2)第二步:内核的人负责提供 platform_driver==提供主机驱动。
    • (3)第三步:写设备驱动的人负责提供 platform_driver==我们来做。
    • (4)platform 的 match 函数发现 driver 和 device 匹配后,调用 driver 的 probe 函数来完成 驱动的初始化和安装,然后设备就工作起来了。

    2、代码分析:platform本身注册

    • (1)每种总线都会带一个 match 方法,match 方法用来对线下的 device 和 driver 进行匹配。 理论上每种匹配算法是不同的,但实际上一般都是看 name 的。主要有四种
    • (2)platform_match 函数就是平台总线的匹配方法,该函数的工作中原理是:如果有 id_table 就说明驱动支持多个设备,所以这时候要拿 device 的 name 去对比 id_table 中所有的 name,只要找到一个相同的就匹配上了,如果没找到则匹配不上。如果没有 id_table 或匹配不上,那就直接对比 device 和 driver 的 name,若在匹配不上则匹配失败。

    六、platform 平台总线工作原理 3

    1、以 leds-s3c24xx.c 为例来分析 platform 设备和驱动的注册过程

    • (1)platform_driver_register。
    • (2)platform_device_register。

    2、platdata 简介

    • (1)platdata 是设备注册时提供的与设备有关的数据(譬如 gpio、中断号、设备名称),位 于 struct platform_device 的 struct device dev 中。
    • (2)这些数据在设备和驱动 match 之后,会由设备方转给驱动方,便于驱动操作设备
    • (3)好处:驱动源码中不携带数据,只负责算法,可移植性强。

    七、平台总线编程示例

    1.在驱动程序里添加 platform_driver 相关代码。
    2.测试 platform_device 和 platform_driver 相遇时会怎样。

    #include // module_init module_exit
    #include  // __init __exit
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define X210_LED_OFF 1 
    #define X210_LED_ON 0
    
    // 定义大结构体
    struct s5pv210_gpio_led{
    	struct led_classdev cdev;
    	struct s5pv210_led_platdata* pdata;
    };
    
    // 由 platform_device 指针得到 s5pv210_gpio_led 指针
    static inline struct s5pv210_gpio_led* pdev_to_gpio(struct platform_device* dev)
    {
    	return platform_get_drvdata(dev);
    }
    //通过led_classdev得到s5pv210_gpio_led指针
    static inline struct s5pv210_gpio_led* to_gpio(struct led_classdev* led_cdev)
    {
    	return container_of(led_cdev,struct s5pv210_gpio_led,cdev);
    }
    
    //这个函数就是要去完成具体的硬件读写任务的
    static void s5pv210_led_set(struct led_classdev* led_cdev,enum led_brightness value)
    {
    	struct s5pv210_gpio_led* p=to_gpio(led_cdev);//透过led_classdev得到s5pv210_gpio_led指针
    	printk(KERN_INFO "s5pv210_led_set\n");
    	if (value == LED_OFF)// 用户给了个 0,希望 LED 灭 
    		gpio_set_value(p->pdata->gpio, X210_LED_OFF); 
    	else// 用户给的是非 0,希望 LED 亮 
    		gpio_set_value(p->pdata->gpio, X210_LED_ON); 
    }
    
    //platform_device和platform_driver匹配后调用这函数
    static int s5pv210_led_probe(struct platform_device* dev)
    {
    	int ret=-1;
    	struct s5pv210_led_platdata* pdata=dev->dev.platform_data;//获取platform_data
    	struct s5pv210_gpio_led* led;//实例化
    	printk(KERN_INFO "----s5pv210_led_probe---\n");
    	led=kzalloc(sizeof(struct s5pv210_gpio_led),GFP_KERNEL);//申请所需内存空间
    	if(led==NULL){
    		dev_err(&dev->dev,"No memory for device\n");return -ENOMEM;
    	}
    	platform_set_drvdata(dev,led);
    
    	//在这里去申请驱动用到的各种资源,当前驱动中就是GPIO资源
    	if(gpio_request(pdata->gpio,pdata->name))
    		printk(KERN_ERR "gpio_request failed\n");
    	else// 设置为输出模式,并且默认输出 1 让 LED 灯灭
    		gpio_direction_output(pdata->gpio, 1);	
    	
    	//struct s5pv210_gpio_led的填充
    	led->cdev.name=pdata->name;
    	led->cdev.brightness=0;
    	led->cedv.brightness_set=s5pv210_led_set;
    	led->pdata=pdata;
    	
    	//注册led_classdev
    	ret=led_classdev_register(&dev->dev,&led->cdev);
    	if(ret<0){
    		printk(KERN_ERR"led_classdev_register failed\n");return ret;
    	}
    	return 0;
    }
    //卸载平台设备驱动时调用该函数
    static int s5pv210_led_remove(struct platform_device* dev)
    {
    	struct s5pv210_gpio_led* p=pdev_to_gpio(dev);//由platform_device指针得到s5pv210_gpio_led指针
    	led_classdev_unregister(&p->cdev);//注销led_classdev
    	gpio_free(p->pdata->gpio);//释放gpio资源
    	kfree(p);//kfree最后一步
    	return 0;
    }	
    
    //platform_driver实例化
    static struct platform_driver s5pv210_led_driver={
    	.probe=s5pv210_led_probe,
    	.remove=s5pv210_led_remove,
    	.driver={
    		.name="s5pv210_led",
    		,owner=THIS_MODULE,
    	},
    }
    
    static int __init s5pv210_led_init(void)
    {
    	return platform_driver_register(&s5pv210_led_driver);//注册设备驱动
    }
    static void __exit s5pv210_led_exit(void)
    {
    	platform_driver_unregister(&s5pv210_led_driver);//卸载设备驱动
    }
    module_init(s5pv210_led_init); 
    module_exit(s5pv210_led_exit);
    
    MODULE_LICENSE("GPL"); 
    MODULE_AUTHOR("aston <1264671872@qq.com>"); 
    MODULE_DESCRIPTION("s5pv210 led driver"); 
    MODULE_ALIAS("s5pv210_led"); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
  • 相关阅读:
    Web前端中级探索:技术进阶与困惑之旅
    C++ Primer Plus第三章编程练习答案
    2023年中国磷酸二铵市场发展概况分析:市场集中度较高[图]
    Linux项目自动化构建工具-make/Makefile
    英伟达算法岗面试,问的贼专业。。。
    Java急速转职GoLang工程师资料
    vue3后台管理系统
    Leetcode双指针刷题(一)
    C 语言学习笔记(三):C 语言开发环境搭建
    Mybatis-Plus【吐血详解 一篇掌握】
  • 原文地址:https://blog.csdn.net/weixin_47397155/article/details/125981471