• Linux平台设备框架驱动


    Linux平台设备框架驱动

      平台设备框架(platform)是将一个驱动分为设备层驱动层两个部分,通过总线模型将设备和驱动进行绑定。在系统中每注册一个设备,都会与之匹配一个驱动,同样的,每注册一个驱动也会与之匹配一个设备。
       通常 Linux 设备和驱动通常都需要挂接在一种总线上,对于本身依附于 PCI、 USB、 I2 C、 SPI 等的设备而言,这自然不是问题,但是在嵌入式系统里面, SOC 系统中集成的独立的外设控制器、挂接在 SOC 内存空间的外设等却不依附于此类总线。
       基于总线框架的模型结构,Linux下就衍生了平台设备框架模型(platform),平台设备总线是一种虚拟总线,称为platform总线。对应的设备层称为platform_device;驱动层称为platform_driver。设备层和驱动层通过平台设备总线进行匹配管理。

    1.平台设备框架特性

    1. 平台模型采用了分层结构,把一个设备驱动程序分成了两个部分:
        平台设备( platform_device)平台驱动( platform_driver)
    2. 平台设备将设备本身的资源注册进内核,可以由内核统一管理。
    3. 将硬件资源和驱动接口分离,编译代码的维护与移植。
      在这里插入图片描述

    2.平台设备总线相关接口函数

    2.1 设备层接口函数

      每个设备的系统通过设备结构体struct platform_device保存。结构体原型在 include/linux/platform_devcie.h 中定义。

    struct platform_device {
    	const char	* name; //设备名字,驱动层和设备层匹配标志
    	int		id;//通常填-1
    	struct device	dev;//设备结构体信息
    	u32		num_resources;//资源个数
    	struct resource	* resource;//资源内容
    	const struct platform_device_id	*id_entry;
    	/* MFD cell pointer */
    	struct mfd_cell *mfd_cell;
    	/* arch specific additions */
    	struct pdev_archdata	archdata;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • struct device dev结构信息

        struct device dev 结构体是用来实现设备模型的。这个结构体中成员比较多,结构体原型在 include/linux/devcie.h 中定义。我们常关心的是其中的两个成员:
        平台数据指针: void *platform_data;
        资源释放函数: void (*release)(struct device *dev);
        平台数据指针platform_data是一个void *类型指针,可以向驱动层传递任意数据,release资源释放函数接口在设备层一定要自己实现,否则在设备层释放资源时会报错。

      以下列举其中几个成员结构:

    struct device {
    	const char *init_name; /*逻辑设备的名字*/
    	struct device_type *type; /* 设备类型 */
    	struct bus_type *bus; /* 设备所属的总线类型 */
    	struct device_driver *driver;/* 指向开辟 struct device 结构 driver 指针*/
    	void		*platform_data;	/* 平台设备指针 */
    	dev_t devt;  /* 存放设备号 dev_t,creates the sysfs"dev" */
    	struct class *class;  /* 设备所属类*/
    	void	(*release)(struct device *dev);/*设备资源释放函数*/
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • struct resource * resource结构信息

        struct resource * resource 结构体是用来保存设备资源内容信息。该结构体定义位置: include/linux/ioport.h
    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

      在资源结构体中的资源类型flags,资源类型的相关宏定义位置: include/linux/ioport.h ,常用的资源类型如下:

    #define IORESOURCE_TYPE_BITS	0x00001f00	/* Resource type */
    #define IORESOURCE_IO		0x00000100 //IO 空间, 一般在 X86 框架中存在, ARM 一般没有
    #define IORESOURCE_MEM		0x00000200 //内存空间,占用的是 CPU 4G 统一编址空间
    #define IORESOURCE_IRQ		0x00000400 //中断号
    #define IORESOURCE_DMA		0x00000800 //DMA
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 设备层注册函数

    int platform_device_register(struct platform_device *pdev)
    函数功能: 注册平台设备;
    形参: pdev --设备结构体;
    返回值: 成功返回0,失败返回其它值;

    • 设备层注销函数

    void platform_device_unregister(struct platform_device *pdev)
    函数功能: 注册平台设备;
    形参: pdev --设备结构体;

    • 向内核添加多个设备

    int platform_add_devices(struct platform_device **devs, int num)
    函数功能: 向内核注册多个设备;
    形参: pdev --设备结构体;
       num – 注册的设备个数
    返回值: 成功返回0,失败返回其它值;

    2.2 驱动层接口函数

      驱动层通过struct platform_driver 结构体保存相关信息,结构体定义位置: include/linux/devcie.h
      在该结构体中必须要实现接口函数:

    • 资源匹配函数:int (*probe)(struct platform_device *)
    • 资源释放函数:int (*remove)(struct platform_device *);
    • 驱动资源结构体:struct device_driver driver;

      一个驱动层可匹配多个设备层,若想同时匹配多个设备层,则通过id_table 指针完成匹配。

    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;//匹配多多设备时需要填写
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • struct device_driver driver 结构信息

      struct device_driver driver 结构体中有个成员name必须要填写,当没有实现id_table指针时,设备层和驱动层就是通过该成员完成资源匹配。

    struct device_driver {
    	const char		*name; //资源匹配参数
    	struct bus_type		*bus;
    
    	struct module		*owner;
    	const char		*mod_name;	/* used for built-in modules */
    
    	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
    
    	const struct of_device_id	*of_match_table;
    
    	int (*probe) (struct device *dev);
    	int (*remove) (struct device *dev);
    	void (*shutdown) (struct device *dev);
    	int (*suspend) (struct device *dev, pm_message_t state);
    	int (*resume) (struct device *dev);
    	const struct attribute_group **groups;
    
    	const struct dev_pm_ops *pm;
    
    	struct driver_private *p;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • const struct platform_device_id *id_table 结构信息

      该结构也是用于匹配设备资源,一个驱动层可以同时匹配多个设备层。

    struct platform_device_id {
    	char name[PLATFORM_NAME_SIZE]; //资源匹配参数
    	kernel_ulong_t driver_data
    			__attribute__((aligned(sizeof(kernel_ulong_t)))); //匹配设备层的 void *platform_data数据
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 驱动层注册和注销函数

    //驱动注册函数
    int platform_driver_register(struct platform_driver *drv)
    //驱动注销函数
    void platform_driver_unregister(struct platform_driver *drv)

    3.平台设备框架应用示例

    3.1 设备层注册示例

      设备层注册步骤:

    1. 填充struct device结构体,填写设备资源信息struct resource * resource;
    2. 调用设备层注册函数platform_device_register();
    3. 注销时调用注销函数platform_device_unregister;
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    #include 
    
    static void platform_release(struct device *dev)
    {
    	printk("资源释放完成\n");
    }
    static struct resource	resource[]=
    {
    	[0]={
    			.start=EXYNOS4X12_GPM4(0),
    			.end=EXYNOS4X12_GPM4(0),
    			.name="led1",
    			.flags=IORESOURCE_MEM
    		},
    	[1]={
    			.start=EXYNOS4X12_GPM4(1),
    			.end=EXYNOS4X12_GPM4(1),
    			.name="led2",
    			.flags=IORESOURCE_MEM
    		},		
    		
    };
    
    struct platform_device pdev=
    {
    	.name="led_dev",
    	.id=-1,
    	.dev=
    	{
    		.release=platform_release,//资源释放函数
    	},
    	.num_resources=sizeof(resource)/sizeof(resource[0]),
    	.resource=resource,	
    };
    
    static int __init wbyq_platform_dev_init(void)
    {
    	platform_device_register(&pdev);
        return 0;
    }
    /*驱动释放*/
    static void __exit wbyq_platform_dev_cleanup(void)
    {
    	/*注销设备层*/
    	platform_device_unregister(&pdev);
    
    }
    module_init(wbyq_platform_dev_init);//驱动入口函数
    module_exit(wbyq_platform_dev_cleanup);//驱动出口函数
    
    MODULE_LICENSE("GPL");//驱动注册协议
    MODULE_AUTHOR("it_ashui");
    MODULE_DESCRIPTION("Exynos4 platform_dev Driver");
    
    • 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

    3.2 驱动层注册示例

      驱动层注册步骤:

    1. 填充 struct platform_driver结构体,实现资源匹配函数和资源释放函数;
    2. 调用设备层注册函数platform_driver_register();
    3. 注销时调用注销函数platform_driver_unregister;
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    #include 
    
    static int platform_probe(struct platform_device *dev)
    {
    	printk("资源匹配成功\n");
    	printk("资源个数:%d\n",dev->num_resources);
    	struct resource * resource=platform_get_resource(dev,IORESOURCE_MEM,0);
    	if(resource)
    	{
    		printk("资源名:%s\tstart=%x\tend=%x\n",resource->name,resource->start,resource->end);
    	}
    	return 0;
    	
    }
    static int platform_remove(struct platform_device *dev)
    {
    	printk("资源释放成功\n");
    	return 0;
    }
    static struct platform_device_id id_table[]=
    {
    	[0]=
    	{
    		.name="led_dev"
    	},
    	[1]=
    	{
    		.name="tiny4412_dev"
    	},
    	
    };
    static struct platform_driver drv=
    {
    	.probe=platform_probe,
    	.remove=platform_remove,
    	.driver=
    	{
    		.name="platform_drv",
    	},
    	.id_table=id_table,
    };
    static int __init wbyq_platform_drv_init(void)
    {
    	platform_driver_register(&drv);
    	printk("驱动层平台设备注册成功\n");
        return 0;
    }
    /*驱动释放*/
    static void __exit wbyq_platform_drv_cleanup(void)
    {
    	/*注销设备层*/
    	platform_driver_unregister(&drv);
    	printk("驱动层平台设备注销成功\n");
    
    }
    module_init(wbyq_platform_drv_init);//驱动入口函数
    module_exit(wbyq_platform_drv_cleanup);//驱动出口函数
    
    MODULE_LICENSE("GPL");//驱动注册协议
    MODULE_AUTHOR("it_ashui");
    MODULE_DESCRIPTION("Exynos4 platform_drv Driver");
    
    • 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

    3.3 Makefile文件

    KER_ADD=/home/wbyq/src_pack/linux-3.5
    all:
    	make -C $(KER_ADD) M=`pwd` modules
    	#arm-linux-gcc main.c -o app 
    	cp ./*.ko  /home/wbyq/src_pack/rootfs/code 
    	make -C $(KER_ADD) M=`pwd` modules clean
    	rm app -f
    obj-m +=platform_drv.o platform_dev.o platform_dev2.o
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4运行效效果

    在这里插入图片描述

  • 相关阅读:
    C++PrimerPlus 第六章 分支语句和逻辑运算符(复习题含答案)
    CSS详解(二)
    一键重装win7系统详细教程
    机器人初识 —— 电机传动系统
    寄存器(汇编语言)
    谈谈mysql中的各个关键字
    微信小程序 实现手写签名(横屏签名板)
    开发微信小程序的下载安装及入门
    Oracle网络原理及配置
    客户端发现pod并与之通信
  • 原文地址:https://blog.csdn.net/weixin_44453694/article/details/126868999