• Linux 设备模型【1】-PLATFORM(平台)总线详解


    系列文章目录

    Linux platform子系统【1】-PLATFORM(平台)总线详解
    Linux platform子系统【2】-PLATFORM注册(struct device)platform_bus
    Linux 设备模型【2】- kobject
    Linux 设备模型【3】- uevnet
    Linux 设备模型【8】- bus
    Linux 设备模型【9】- CLASS
    Linux 设备模型【10】- Bus, Class, Device和Device Driver的概念



    前言

    platform虚拟平台总线驱动分析总结
    https://blog.csdn.net/weixin_40237571/article/details/110877523


    一、什么是platform(平台)总线?

    相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。

    • 那为什么需要platform总线呢?
      其实是Linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线。因为对于usb设备i2c设备pci设备spi设备等等,他们与cpu的通信都是直接挂在相应的总线下面与我们的cpu进行数据交互的,但是在我们的嵌入式系统当中,并不是所有的设备都能够归属于这些常见的总线,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。所以Linux驱动模型为了保持完整性,将这些设备挂在一条虚拟的总线上(platform总线),而不至于使得有些设备挂在总线上,另一些设备没有挂在总线上。

    platform总线相关代码:driver\base\platform.c 文件

    相关结构体定义:include\linux\platform_device.h 文件中

    二、platform总线管理下的2员大将

    2.1. 两个结构体platform_deviceplatform_driver

    对于任何一种Linux设备驱动模型下的总线都由两个部分组成:描述设备相关的结构体和描述驱动相关的结构体在platform总线下就是platform_deviceplatform_driver,下面是对两个结构体的各个元素进行分析:

    1. platform_device结构体:(include\linux\platform_device.h)

      struct platform_device {           //  platform总线设备
      	const char    * name;          //  平台设备的名字
      	int        id;                 //   ID 是用来区分如果设备名字相同的时候(通过在后面添加一个数字来代表不同的设备,因为有时候有这种需求)
      	struct device    dev;          //   内置的device结构体
      	u32        num_resources;      //   资源结构体数量
      	struct resource    * resource; //   指向一个资源结构体数组
      			 
      	const struct platform_device_id    *id_entry; //  用来进行与设备驱动匹配用的id_table表
      			  
      	/* arch specific additions */
      	struct pdev_archdata    archdata;             //  自留地    添加自己的东西
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    2. platform_device结构体中的struct resource结构体分析:

      struct resource {      // 资源结构体
      	resource_size_t start;      // 资源的起始值,如果是地址,那么是物理地址,不是虚拟地址
      	resource_size_t end;        // 资源的结束值,如果是地址,那么是物理地址,不是虚拟地址
      	const char *name;           // 资源名
      	unsigned long flags;        // 资源的标示,用来识别不同的资源
      	struct resource *parent, *sibling, *child;   // 资源指针,可以构成链表
      };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    3. platform_driver结构体:(include\linux\platform_device.h)

      	 struct platform_driver {
      	     int (*probe)(struct platform_device *);     //  这个probe函数其实和  device_driver中的是一样的功能,但是一般是使用device_driver中的那个
      	     int (*remove)(struct platform_device *);    //  卸载平台设备驱动的时候会调用这个函数,但是device_driver下面也有,具体调用的是谁这个就得分析了
      	     void (*shutdown)(struct platform_device *);
      	     int (*suspend)(struct platform_device *, pm_message_t state);
      	     int (*resume)(struct platform_device *);
      	     struct device_driver driver;                //   内置的device_driver 结构体 
      	     const struct platform_device_id *id_table;  //  该设备驱动支持的设备的列表  他是通过这个指针去指向  platform_device_id 类型的数组
      	 };
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

    2.2. 两组接口函数(driver\base\platform.c)

    int platform_driver_register(struct platform_driver *); // 用来注册我们的设备驱动

    void platform_driver_unregister(struct platform_driver *); // 用来卸载我们的设备驱动

    int platform_device_register(struct platform_device *); // 用来注册我们的设备

    void platform_device_unregister(struct platform_device *); // 用来卸载我们的设备

    三、platform平台总线的初始化

    3.1 platform平台总线的注册初始化: platform_bus_init

    	b	start_kernel		//head-common.S
    	start_kernel(void){
    		rest_init(void)		// /init/mai.c
    	}
    	rest_init(void)		// /init/mai.c
    	kernel_init			// /init/mai.c
    	kernel_init_freeable  // /init/main.c
    	do_basic_setup // /init/main.c
        driver_init	// /init/init.c
    	platform_bus_init
        early_platform_cleanup       //  进行一些早期的平台清理   
        device_register              //  注册设备 (在/sys/devices/目录下建立 platform目录对应的设备对象  /sys/devices/platform/) 
        bus_register                //  总线注册
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.2 注册总线设计的模型以及结构体

    这个问题起是就是在解决

    1. platform_bus 这个子系统是怎么产生的?

    platform_bus_type 是系统开启就会分配好的数据结构

    	struct bus_type platform_bus_type = {
    		.name		= "platform",
    		.dev_groups	= platform_dev_groups,
    		.match		= platform_match,
    		.uevent		= platform_uevent,
    		.pm		= &platform_dev_pm_ops,
    	};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2. platform_bus 注册到哪里去?(bus_kset->list 中)

    int __init buses_init(void)
    	{
    		static struct kset *bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
    	}
    		
    platform_bus_init(void)
    	-->bus_register(&platform_bus_type);
    		struct subsys_private {
    			struct kset subsys;
    		}
    		struct bus_type {
    			struct subsys_private *p;
    		}
    		priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
    		priv->bus = &platform_bus_type;
    		bus->p = (struct subsys_private*)priv;
    		priv->subsys.kobj.kset = bus_kset;   /*设置本子系统对象所属集合*/
    		priv->subsys.kobj.ktype = &bus_ktype;/*所属集合的操作方式和属性*/
    		-->kset_register(&priv->subsys); /*注册子系统,注册到那里去了*/
    			/*kobject_add_internal(&(priv->subsys)->kobj)*/
    		-->err = kobject_add_internal(&k->kobj)(struct kobject *kobj); /*以内核对象的方式注册*/
    		{
    			/*如果所属集合不为空,且无父对象,所属集合(/sys/bus)为父对象*/
    			if (kobj->kset) {	/*priv->subsys.kobj.kset = bus_kset*/
    				if (!parent)
    					parent = kobject_get(&kobj->kset->kobj); /*kobject_get(bus_kset->kobj)*/
    				kobj_kset_join(kobj); /*kobj_kset_join(&priv->subsys->kobj)*/
    				{
    					/* add the kobject to its kset's list */
    					/*list_add_tail(&(&priv->subsys)->entry, &(priv->subsys->kset)->list);*/
    					list_add_tail(&kobj->entry, &kobj->kset->list);
    					/*
    						struct kobject {
    							const char		*name;
    							struct list_head	entry;
    						}				
    					*/
    				}
    				kobj->parent = parent;
    			}
    		}
    
    • 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

    3. platform_bus 以怎样的数据结构驻留在内存中。

    最终以 priv的形式贮存在bus_ksetlist

    (struct subsys_private) priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
    -->priv->bus = (struct bus_type *)platform_bus_type
    
    • 1
    • 2

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    1. 卸载platfom_bus挂载在其上的设备和驱动会一起被释放吗?
  • 相关阅读:
    计算机网络协议栈:物理层 | 信道通信
    基于flask写的一个小商城mall项目
    念一句咒语 AI 就帮我写一个应用,我人麻了...
    flink cdc多种数据源安装、配置与验证
    linux系统、kylin麒麟系统 添加samba 安装和配置
    Ubuntu 22.10 (Kinetic Kudu) 发布
    GateWay网关
    HTML5七夕情人节表白网页制作【飘落蒲公英动画超酷炫的HTML5页面】HTML+CSS+JavaScript
    【MAX7800实现KWS20 demo演示】
    日常工作中程序员最讨厌哪些工作事项?
  • 原文地址:https://blog.csdn.net/m0_46535940/article/details/126149849