驱动进化之路:总线设备驱动模型

总线设备驱动模型

总线设备驱动模型是基于驱动设计思想分离而实现的。其主要包括了三个部分:总线(bus)、设备(dev)、驱动(drv); 主要功能就是将dev与drv分离开来。

设备(dev)

在dev部分中会注册一个platform_device(设备平台)。可以在platform_device结构体里面定义使用哪个引脚也可以用来表示所有设备的资源。

struct platform_device {
	const char	*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;
};

驱动(drv)

在drv部分中会注册一个platform_driver(驱动平台)。platform_driver结构体中的probe函数可以做任何你想做的事情,例如:寄存器硬件操作,实现引脚的寄存器操作,/分配/设置/注册file_operations,使用ioremap映射寄存器都能在该结构体中实现。

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;
	bool prevent_deferred_probe;
};

总线

那怎么将这两个平台联系起来呢?

内核里面有一个总线,这个总线是bus_type,是一个虚拟总线。bus有两个链表,一个是设备链表,一个是驱动链表。当我们注册一个platform_device 时,就会放入设备链表;注册platform_driver时就会放入驱动链表。比如说,将platform_device注册进内核里面了,同时就会去对面的驱动链表里面,将里面的成员一个个取出来进行对比,如果对比成功,就会调用platform_driver的probe函数来处理device。

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);

	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;
}

怎么知道是否匹配成功然后进行配对呢?

总线bus_type结构体里面有个match函数,其函数就用来比较dev和drv是否匹配。

进行匹配的先后顺序:

1.如果platform_device结构体定义了driver_override,就首先通过driver_override定义的字符串与platform_driver里面的名字是否相等,如果相等,就说明匹配成功。
2.如果平台设备并没有定义driver_override的话,就通过第二种方法进行比较。如果数组id_table不是空的,那么设备平台中的名字就会与数组里面的每一项进行匹配。
3.如果id_table为空的话,或者设备平台的名字与id_table匹配失败,那么就会将设备平台的名字与驱动平台的名字进行匹配。
在这里插入图片描述

match函数demo:

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);

	/* 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);
}

编写LED总线设备驱动模型

1、实现platform_device结构体
board_A.c 作为一个可加载模块,里面也有入口函数、出口函数。在入口函数中注册 platform_device结构体,在 platform_device 结构体中指定使用哪个 GPIO 引脚。
首先看入口函数,它调用 platform_device_register 函数,向内核注册 board_A_led_dev 结构体:

demo

static void led_dev_release(struct device *dev)
{

}

static struct resource	resource[] = {   //用来存放引脚资源
	{
		.start = GROUP_PIN(5, 3),
		.name = "led2_pin",
		.flags = IORESOURCE_IRQ,
	},

	{
		.start = GROUP_PIN(3, 1),
		.name = "led2_pin",
		.flags = IORESOURCE_IRQ,
	},
};

static struct platform_device board_A_led_device = {
	.name = "imx6ull_led",	//平台设备(platform_device)名字
	.num_resources	= ARRAY_SIZE(resource),  //ARRAY_SIZE计算资源数组的个数
	.resource = resource,
	.dev = {
		.release = led_dev_release,
	},
};

static int __init led_dev_init(void)
{
	int err;

	err = platform_device_register(&board_A_led_device);
	
	return 0;
}

static void __exit led_dev_exit(void)
{
	platform_device_unregister(&board_A_led_device);
}

module_init(led_dev_init);  
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");

注意:提供了一个空函数 led_dev_release,它被赋给 board_A_led_device结构体,这个函数在卸载 platform_device 时会被调用,如果不提供的话内核会打印警告信息。

2、实现 platform_driver 结构体
chip_demo_gpio.c 中注 册 platform_driver 结构 体 , 它 使用 Bus/Dev/Drv 模型 , 当有 匹 配的platform_device 时,它的 probe 函数就会被调用。
在 probe 函数中做你想做的事情,例如分配、设置、注册file_operation、操作寄存器等。
在probe函数中调用platform_get_resource获取设备平台中的resource。

platform_get_resource函数原型

/**
 * platform_get_resource - get a resource for a device
 * @dev: platform device
 * @type: resource type
 * @num: resource index
 */
struct resource *platform_get_resource(struct platform_device *dev,
				       unsigned int type, unsigned int num)

获取资源之后就需要将引脚信息记录下来。
并需要创建设备节点。由于device_create函数中led_class变量在上层文件中,不能够直接调用device_create。可以在上层文件中将device_create封装进函数中。

demo

/* 创建设备 */
static int chip_demo_gpio_probe(struct platform_device *pdev)
{
	struct resource *res;
	int i = 0;
	
	while(1){
		/* platform_get_resource - get a resource for a device */
		res = platform_get_resource(pdev, IORESOURCE_IRQ, i++); //搜索设备链表pdev中IORESOURCE_IRQ类型的资源
		if(!res)
			break;

		/* 记录设备资源中的引脚信息 */
		g_ledpins[g_ledcnt] = res->start;

		/* device_create 
		 * 由于device_create函数中led_class变量在上层文件中,不能够直接调用device_create
		 * 可以将device_create封装进函数中
		 */
		led_class_create_device(i);
		g_ledcnt++;
	}
	return 0;
}

/* 销毁设备 */
static int chip_demo_gpio_remove(struct platform_device *pdev)
{
	struct resource *res;
	int i = 0;

	while(1){
		res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
		if(!res)
			break;
		led_class_destroy_device(i);
		i++;
		g_ledcnt--;
	}
	return 0;
}


static struct led_operations board_demo_led_opr = {
	.init = board_demo_led_init,
	.ctl = board_demo_led_ctl
};

struct led_operations *get_board_led_opr(void)
{
	return &board_demo_led_opr;
}



static struct platform_driver chip_demo_gpio_driver = {
	/* 当设备平台与驱动平台匹配成功之后,会调用probe函数 */
	.probe = chip_demo_gpio_probe,
	/* 当移除设备的时候会调用remove函数,与probe函数对应 */
	.remove = chip_demo_gpio_remove,
	.driver = {
		.name = "imx6ull_led",/* 需要与platform_device .name一致,这样才能够匹配 */
	},
};

static int __init chip_demo_gpio_init(void)
{
	int err;

	err = platform_driver_register(&chip_demo_gpio_driver);
	register_led_operations(&board_demo_led_opr);
	
	return 0;
}

static void __exit chip_demo_gpio_exit(void)
{
	platform_driver_unregister(&chip_demo_gpio_driver);
}

module_init(chip_demo_gpio_init);  
module_exit(chip_demo_gpio_exit);
MODULE_LICENSE("GPL");

程序现象:
在这里插入图片描述

其他

如果想要打印内核信息
输入指令echo 7 4 1 7 > /proc/sys/kernel/printk
在这里插入图片描述
关闭内核打印信息指令

# echo 1       4       1      7 > /proc/sys/kernel/printk
或者
# echo 0       4       0      7 > /proc/sys/kernel/printk
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值