• 7. Linux驱动 - Platform总线驱动模型


    Linux总线

    Platform:平台设备总线驱动模型

    将软件驱动和硬件隔离,减少移植成本。Linux使用 设备-总线-驱动 模型。

    硬件部分(设备)  A  B  C  D
    
    总线 ------------------------------------
    
    软件部分(软件)  X  Y  Z 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    platform

    在Linux内核中实现了一条虚拟总线,叫做 platform_bus_type:

    #inlcude <linux/platform_device.h>
    
    struct bus_type platform_bus_type = {
    	.name		= "platform",
    	.dev_groups	= platform_dev_groups,
    	.match		= platform_match,
    	.uevent		= platform_uevent,
    	.probe		= platform_probe,
    	.remove		= platform_remove,
    	.shutdown	= platform_shutdown,
    	.dma_configure	= platform_dma_configure,
    	.pm		= &platform_dev_pm_ops,
    };
    
    static int platform_match(struct device *dev, struct device_driver *drv)
    {
    	struct platform_device *pdev = to_platform_device(dev);
    	struct platform_driver *pdrv = to_platform_driver(drv);
    
    	/* When driver_override is set, only bind to the matching driver */
    	if (pdev->driver_override)
    		return !strcmp(pdev->driver_override, drv->name);
    
    	/* Attempt an OF style match first */
    	if (of_driver_match_device(dev, drv))
    		return 1;
    
    	/* Then try ACPI style match */
    	if (acpi_driver_match_device(dev, drv))
    		return 1;
    
    	/* Then try to match against the id table */
    	if (pdrv->id_table)
    		return platform_match_id(pdrv->id_table, pdev) != NULL;
    
    	/* fall-back to driver name match */
    	return (strcmp(pdev->name, drv->name) == 0);
    };
    
    struct platform_device {
    	const char	*name;
    	int		id;
    	bool		id_auto;
    	struct device	dev;
    	u64		platform_dma_mask;
    	struct device_dma_parameters dma_parms;
    	u32		num_resources;
    	struct resource	*resource;  // 硬件信息,通过start, end 判断类型 
    
    	const struct platform_device_id	*id_entry;
    	char *driver_override; /* Driver name to force a match */
    
    	/* MFD cell pointer */
    	struct mfd_cell *mfd_cell;
    
    	/* arch specific additions */
    	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; // 驱动信息,name
    	const struct platform_device_id *id_table; // id
    	bool prevent_deferred_probe;
    };
    
    
    • 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

    总线上维护两条链表:dev(设备)链表: platform_device,drv(驱动)链表:platform_driver。

    匹配流程

    匹配的动作发生在添加驱动软件或者硬件节点的时候。match函数匹配成功后,调用probe函数,进行驱动。

    match过程(优先级):

    1. 通过设备树中传递的硬件信息id和drv中id链表匹配
    2. 通过drv中id_table中name和dev中name进行匹配
    3. 通过drv中name和dev的name进行匹配

    编程实现

    编程只负责向内核添加节点就可以了,匹配过程内核已经设计好了。

    硬件节点

    // 1 配置硬件节点
    struct resource hdev_res[] = {
        [0] = {
            .start = 0,  // 硬件信息起始数据,起始IO端口号,中断号等
            .end = 0,  // 硬件信息结束数据,
            .flags = IORESOURCE_IO, // 硬件信息的类型,
        },
        ....
    };
    
    // 2 初始化硬件节点
    struct platform_device hdev_dev = {
        .name = "devName",  // 用于drv匹配的
        .id = -1, // 防止节点重名,没有重名可以给-1
        .resource = hdev_res, // 硬件资源数据
        .num_resource = ARRAY_SIZE(hdev_res); // 数组长度
        .dev = {
            .platform_data = "",  // 传递额外的数据信息
            .release = func_rel,  // 删除硬件节点时,自动调用
        },
    };
    
    // 3 添加注册
    int platform_device_register(struct platform_device *pdev)
    {
    	device_initialize(&pdev->dev);
    	setup_pdev_dma_masks(pdev);
    	return platform_device_add(pdev);
    }
    // platform_device_unregister
    
    • 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

    驱动节点

    // 1 初始化
    struct platform_driver hdev_drv = {
        .id_table = tab_list; // 名称数组,数组以null结束,用于名称匹配
        .dirver = {
            .name = "drvName", // 用于匹配,优先级低于id_table
        }
        .probe = func_pro; // 匹配成功自动调用
        .remove = func_rem; // 解除匹配自动调用
    };
    
    // 2 添加注册
    int platform_driver_register(struct platform_driver *drv);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Demo:

    test2_drv.c

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/platform_device.h>
    
    // rror: array type has incomplete element type ‘struct platform_device_id’
    // struct platform_device_id cdd_table[] = {
    // 	{"cdd"}, {"cdd1"}, {"cdd2"}, {""}
    // };
    
    int cdd_probe(struct platform_device *dev){
    	printk("cdd_probe called");
    	return 0;
    }
    int cdd_remove(struct platform_device *dev){
    	printk("cdd_remove called");
    	return 0;
    }
    
    struct platform_driver hdev_drv = {
    	//.id_table = cdd_table,
    	.probe = cdd_probe,
    	.remove = cdd_remove,
    	.driver = {
    		.name = "cdd",
    	}
    };
    
    int plat_init(void){
    	platform_driver_register(&hdev_drv);
    	return 0;
    }
    
    void plat_exit(void){
    	platform_driver_unregister(&hdev_drv);
    }
    
    module_init(plat_init);
    module_exit(plat_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Andy");
    MODULE_DESCRIPTION("Fist platform driver test Module!");
    MODULE_VERSION("1.0");
    
    • 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

    test2_dev.c

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/platform_device.h>
    
    struct resource hdev_arr[] = {
    	[0] = {
    		.start = 0x60000000,
    		.end = 0x60000004,
    		.flags = IORESOURCE_MEM,
    	},
    	[1] = {
    		.start = 0x60000004,
    		.end = 0x60000008,
    		.flags = IORESOURCE_MEM,
    	},
    };
    
    void cdd_release(struct device* dev){
    	printk("cdd_release in device.");
    }
    
    struct platform_device hdev_dev = {
    	.name = "cdd",
    	.id = -1,
    	.resource = hdev_arr,
    	.num_resources = ARRAY_SIZE(hdev_arr),
    	.dev = {
    		.release = cdd_release,
    	}
    };
    
    int plat_init(void){
    	platform_device_register(&hdev_dev);
    	return 0;
    }
    
    void plat_exit(void){
    	platform_device_unregister(&hdev_dev);
    }
    
    module_init(plat_init);
    module_exit(plat_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Andy");
    MODULE_DESCRIPTION("Fist platform device test Module!");
    MODULE_VERSION("1.0");
    
    • 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

    Makefile

    # ifneq ($(KERNELRELEASE),)
    # mymodule-bojs := hello.o
    PWD := $(shell pwd)
    KERNELDIR = /lib/modules/$(shell uname -r)/build
    
    obj-m := test2_drv.o test2_dev.o
    
    build: all
    
    all:
    	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    
    clean:
    	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    实际上,Ubuntu有内核保护,驱动没有正确加载运行起来,不过学习的目的达到了。

  • 相关阅读:
    服务器性能测试监控平台export+prometheus(普罗米修斯)+grafana搭建
    后端——egg.js是什么、egg.js安装、约定规则、路由Router、控制器Controller、跨域
    计算机毕业设计php_thinphp_vue的约课管理系统-课程预约(源码+系统+mysql数据库+Lw文档)
    Flink的自定义状态序列化器
    分享一个基于微信小程序开发的高校学生毕业设计选题小程序的源码 lw 调试
    flink系列(一)flink部署及架构简介
    方法的重载
    (JavaSE)继承和多态
    当他们在私域里,掌握了分寸感
    c语言extern关键字
  • 原文地址:https://blog.csdn.net/bbdxf/article/details/125498480