ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

linux驱动学习笔记(七)platform

2022-02-22 15:00:39  阅读:190  来源: 互联网

标签:struct demo driver 笔记 platform dev linux device


前言

在linux内核中我们找不到类似之前demo类型的代码,对于linux来说,代码重用性很重要,否则内核中就会出现很多垃圾代码导致内核文件数相当的大。就比如相同的模块在不同的平台就有不同的驱动程序,那么以此来说的话一个硬件就对应多个驱动文件,显然在linux内核中是不允许存在的。所以在linux内核中提出了,总线,驱动,设备的概念。
在这里插入图片描述
当我们向系统注册一个设备(驱动)总线就去看有没有与之匹配的驱动(设备)如果有的话就把彼此建立起来联系。
linux内核中大量的驱动都采用这个方式,驱动以标准途径拿到board_info,这样芯片原厂做了驱动工作,我们拿到芯片只需要按照原厂标准去写设备驱动。这种方式达到了驱动的分层隔离思想。

Platform设备驱动

基于上述方式,linux提供了一种虚拟总线Platform,相应设备为platform_device,相应驱动为platform_driver

总线

struct bus_type {
	const char		*name; //总线名字
	const char		*dev_name;
	struct device		*dev_root;
	struct device_attribute	*dev_attrs;	/* use dev_groups instead */
	const struct attribute_group **bus_groups;/* 总线属性 */
	const struct attribute_group **dev_groups;/* 设备属性 */
	const struct attribute_group **drv_groups;/* 驱动属性 */

	int (*match)(struct device *dev, struct device_driver *drv);/*匹配函数*/
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);/* 添加环境变量*/
	int (*probe)(struct device *dev);/*驱动匹配*/
	int (*remove)(struct device *dev);/*驱动卸载*/
	void (*shutdown)(struct device *dev);/*关机时执行*/

	int (*online)(struct device *dev);
	int (*offline)(struct device *dev);
	/*
	现在一般休眠唤醒都在dev_pm_ops 中执行
	*/
	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	const struct dev_pm_ops *pm;//电源管理

	const struct iommu_ops *iommu_ops;

	struct subsys_private *p;//私有数据
	struct lock_class_key lock_key;
};

总线就是通过match函数来根据注册的设备或驱动进行相应匹配,match函数指针的两个参数分别为 device 和 device_driver 类型,也就是设备和驱动。

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.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);

	/*设备树匹配方式*/
	if (of_driver_match_device(dev, drv))
		return 1;

	/*ACPI匹配方式*/
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/*id_table匹配方式*/
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/*name字段匹配*/
	return (strcmp(pdev->name, drv->name) == 0);
}

device_driver 中有of_match_table成员,它保存着驱动的compatible匹配表,设备树中的每个设备节点的 compatible 属性会和 of_match_table 表中的所有成员比较,查看是否有相同的条目,如果有的话就表示设备和此驱动匹配,设备和驱动匹配成功以后 probe 函数就会执行。

驱动


struct device_driver {
	const char		*name;
	struct bus_type		*bus;
	...
	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_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 *);
	/*
		device_driver中有成员of_device_id,acpi_device_id,name匹配方式
	*/
	struct device_driver driver;
	/*
		id_table匹配方式
	*/
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
};
int platform_driver_register (struct platform_driver *driver);//注册platform 驱动
void platform_driver_unregister(struct platform_driver *driver);//卸载platform 驱动

模板

/*
* platform 驱动的 probe 函数
* 驱动与设备匹配成功以后此函数就会执行
*/
static int xxx_probe(struct platform_device *dev){
	return 0;
}
static int xxx_remove(struct platform_device *dev){	
	return 0;
}
/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {
	{ .compatible = "xxx_xxx"},
	{ /* Sentinel */ }
};
static struct platform_driver xxx_driver = {
	.driver = {
		.name = "xxx",
		.of_match_table = xxx_of_match,
	},
	.probe = xxx_probe,
	.remove = xxx_remove,
};
static int __init xxx_init(void){
	return platform_driver_register(&xxx_driver);
}
static void __exit xxx_exit(void){
	platform_driver_unregister(&xxx_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin);

设备

struct platform_device {
	const char	*name;//设备名字,用于name匹配
	int		id;
	bool		id_auto;
	struct device	dev;
	u32		num_resources;//资源数量
	struct resource	*resource;//资源,外设寄存器等

	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 resource {
	resource_size_t start;//起始信息
	resource_size_t end;//终止信息
	const char *name;
	unsigned long flags;//资源类型
	struct resource *parent, *sibling, *child;
};
int platform_device_register(struct platform_device *pdev);
void platform_device_unregister(struct platform_device *pdev);

模板

/* 寄存器地址定义*/
#define REGI1_BASE (0X20000000) /* 外设 1 寄存器首地址 */
#define REGI2_BASE (0X02000000) /* 外设 2 寄存器首地址 */
#define REGISTER_LENGTH 4
/* 资源 */
static struct resource xxx_resources[] = {
	[0] = {
		.start = REGI1_BASE ,
		.end = (REGI1_BASE + REGISTER_LENGTH - 1),
		.flags = IORESOURCE_MEM,//内存类型
	},
	[1] = {
		.start = REGI2_BASE ,
		.end = (REGI2_BASE + REGISTER_LENGTH - 1),
		.flags = IORESOURCE_MEM,
	},
};
static struct platform_device xxx_device= {
	.name = "xxx",
	.id = -1,
	.num_resources = ARRAY_SIZE(xxx_resources),
	.resource = xxx_resources,
};
static int __init xxx_init(void){
return platform_device_register(&xxx_device);
}
static void __exit xxx_exit(void){
	platform_device_unregister(&xxx_device);
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");

demo

platform_device.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
/* 寄存器地址定义*/
#define REGI1_BASE (0X20000000) /* 外设 1 寄存器首地址 */
#define REGI2_BASE (0XC2000000) /* 外设 2 寄存器首地址 */
#define REGISTER_LENGTH 4
/* 资源 */
static struct resource demo_device_resources[] = {
	[0] = {
		.start = REGI1_BASE ,
		.end = (REGI1_BASE + REGISTER_LENGTH - 1),
		.flags = IORESOURCE_MEM,//内存类型
	},
	[1] = {
		.start = REGI2_BASE ,
		.end = (REGI2_BASE + REGISTER_LENGTH - 1),
		.flags = IORESOURCE_MEM,
	},
};
static void	demo_release(struct device *dev)
{
	printk("demo device released!\r\n");	
}
static struct platform_device demo_device= {
	.name = "demo-test",
	.id = -1,
    .dev = {
        .release = demo_release,
    },
	.num_resources = ARRAY_SIZE(demo_device_resources),
	.resource = demo_device_resources,
};
static int __init demo_device_init(void){
	return platform_device_register(&demo_device);
}
static void __exit demo_device_exit(void){
	platform_device_unregister(&demo_device);
}
module_init(demo_device_init);
module_exit(demo_device_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");

platform_driver.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#define DEMO_CNT			1		  	/* 设备号个数 */
#define DEMO_NAME			"demo"	/* 名字 */
#define MEM_BUFFER_SIZE 1000
/* demo设备结构体 */
struct demo_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class class;		/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
	unsigned char mem[MEM_BUFFER_SIZE];
};
struct demo_dev demo;	/* demo设备 */

static struct file_operations demo_fops = {
	.owner = THIS_MODULE,
};
char* strcpy(char* dest, const char* src) {
	char* tmp = dest;
	while ((*dest++ = *src++) != '0');
	return tmp;
}
static ssize_t std_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%s\n",demo.mem);
}
static ssize_t std_store(struct device *dev,  
	struct device_attribute *attr, const char *buf, size_t len)
{
	strcpy(demo.mem,buf);
	return len;
}
static DEVICE_ATTR_RW(std);


static struct attribute *demo_class_attrs[] = {
	&dev_attr_std.attr,
	NULL,
};

static const struct attribute_group demo_group = {
	.attrs = demo_class_attrs,
};

static const struct attribute_group *demo_groups[] = {
	&demo_group,
	NULL,
};
static int demo_probe(struct platform_device *dev)
{
    struct resource *source[2];
	int ressize[2];
    int i = 0;
	printk(" driver match device is success !!!\n");
    
    /* 1、获取资源 */
	for (i = 0; i < 2; i++) {
		source[i] = platform_get_resource(dev, IORESOURCE_MEM, i); /* 依次MEM类型资源 */
		if (!source[i]) {
			dev_err(&dev->dev, "No MEM resource for always on\n");
            /*
                正常这里是要返回的
            */
			//return -ENXIO;
		}else{
            ressize[i] = resource_size(source[i]);	
         printk(" souce [%#x]  ressize [%d]\n",source[i]->start,ressize[i]);
        }
		
	}
	/* 1、创建设备号 */
	if (demo.major) {		/*  定义了设备号 */
		demo.devid = MKDEV(demo.major, 0);
		register_chrdev_region(demo.devid, DEMO_CNT, DEMO_NAME);
	} else {						/* 没有定义设备号 */
		alloc_chrdev_region(&demo.devid, 0, DEMO_CNT, DEMO_NAME);	/* 申请设备号 */
		demo.major = MAJOR(demo.devid);	/* 获取分配号的主设备号 */
		demo.minor = MINOR(demo.devid);	/* 获取分配号的次设备号 */
	}
	printk("major=%d,minor=%d\r\n",demo.major, demo.minor);	
	
	/* 2、初始化cdev */
	demo.cdev.owner = THIS_MODULE;
	cdev_init(&demo.cdev, &demo_fops);
	
	/* 3、添加一个cdev */
	cdev_add(&demo.cdev, demo.devid, DEMO_CNT);

	/* 4、创建类 */
	demo.class.owner = THIS_MODULE;
	demo.class.name = DEMO_NAME;
	demo.class.dev_groups = demo_groups;
	class_register(&demo.class);
	/* 5、创建设备 */
	demo.device = device_create(&demo.class, NULL, demo.devid, NULL, DEMO_NAME);
	if (IS_ERR(demo.device)) {
		return PTR_ERR(demo.device);
	}
	
	return 0;
}
static int demo_remove(struct platform_device *dev)
{
	/* 注销字符设备驱动 */
	cdev_del(&demo.cdev);/*  删除cdev */
	unregister_chrdev_region(demo.devid, DEMO_CNT); /* 注销设备号 */

	device_destroy(&demo.class, demo.devid);
	class_unregister(&demo.class);
    return 0;
}
/* 设备树匹配列表 */
static const struct of_device_id demo_of_match[] = {
	{ .compatible = "demo-trees" },
	{ /* Sentinel */ }
};

/* platform驱动结构体 */
static struct platform_driver demo_driver= {
	.driver		= {
		.name	= "demo-test",			/* 驱动名字,用于和设备匹配 */
        .of_match_table	= demo_of_match, /* 设备树匹配表,用于和设备树匹配 */
	},
	.probe		= demo_probe,
	.remove		= demo_remove,
};
static int __init demo_init(void)
{
	return platform_driver_register(&demo_driver);
}

static void __exit demo_exit(void)
{
	platform_driver_unregister(&demo_driver);
}
module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");

dts文件添加

demo-tree {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "demo-trees";
		status = "okay";
	};

此实例先不在改设备树情况下,加载device 和driver 模块,用 device 和driver 的name进行匹配 匹配成功后probe函数可以读取device资源
在加上设备树代码后 可以只加载driver模块 ,也可以匹配成功,并运行driver模块的probe函数
在不修改设备树的情况下 只加载driver模块,probe函数不运行。

标签:struct,demo,driver,笔记,platform,dev,linux,device
来源: https://blog.csdn.net/weixin_35429431/article/details/123061153

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有