linux驱动学习笔记(七)platform

前言

在linux内核中我们找不到类似之前demo类型的代码,对于linux来说,代码重用性很重要,否则内核中就会出现很多垃圾代码导致内核文件数相当的大。就比如相同的模块在不同的平台就有不同的驱动程序,那么以此来说的话一个硬件就对应多个驱动文件,显然在linux内核中是不允许存在的。所以在linux内核中提出了,总线,驱动,设备的概念。
linux驱动学习笔记(七)platform
当我们向系统注册一个设备(驱动)总线就去看有没有与之匹配的驱动(设备)如果有的话就把彼此建立起来联系。
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函数不运行。

上一篇:swiper的用法


下一篇:u-boot dm驱动模型-udevice和driver