platform总线、设备、驱动模型


前言

设备驱动模型中,关心的是总线,设备和驱动这3个实体,总线将设备与驱动绑定。
在系统总线注册(挂接)一个设备的时候,程序会自动寻找与之匹配的驱动;相反,在系统总线注册(挂接)一个驱动的时候,也会寻找与之匹配的设备。这个匹配动作是有总线完成的。
一个现实的LINUX设备和驱动,通常都需要挂接在一种总线上,对于本身依附于PCI,USB,IIC,SPI总线等的设备而言,这自然不是问题。
但是,在嵌入式系统里,soc(现在大多数处理器芯片都是soc)系统中集成的独立的外设控制器(如串口控制器)却不依附于此类总线。
基于这一背景,LINUX发明了一种虚拟的总线,称为platform总线。相应的设备称为platform_device,而驱动称为platform_driver
注意,所谓的platform_device并不是与字符设备,块设备和网络设备并列的概念,而是LINUX系统提供的一种附加手段。例如,在s3c2440处理器中,把内部集成的iic,rtc,spi,lcd,uart等控制器都归纳为platform_device,而它们本身就是字符设备。


一、总线

1、设备结构:

struct device platform_bus = {
	.init_name	= "platform",
};
platform总线本身就是是一种设备

2、总线结构体:

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};
其中,成员说明:
platform_dev_attrs:   设备属性
platform_match: match函数,当属于platform的设备或者驱动注册到              
内核时就会被调用,完成设备与驱动的匹配工作。
platform_uevent:       热插拔操作函数

3、内核总线初始化

int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();

	error = device_register(&platform_bus);
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);
	if (error)
		device_unregister(&platform_bus);
	return error;
}
这个函数向内核注册了一种总线。他首先由/drivers/base/init.c中的
driver_init函数调用,driver_init函数由/init/main.c中的do_basic_setup
函数调用,do_basic_setup这个函数由kernel_init调用,所以platform总线是
在内核初始化的时候就注册进了内核。

二、platform设备

1、platform_device结构体

struct platform_device {
	const char	* name;
	int		id;
	struct device	dev;
	u32		num_resources;
	struct resource	* resource;
	const struct platform_device_id	*id_entry;
	/* arch specific additions */
	struct pdev_archdata	archdata;
};
platform_device结构体中有一个struct resource结构,
是设备占用系统的资源
struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	struct resource *parent, *sibling, *child;
};
num_resources 占用系统资源的数目,一般设备都占用两种资源,
io内存(寄存器的物理地址都被映射为虚拟地址,IO空间了)和中断信号线。

2、platform_device_register设备注册

int platform_device_register(struct platform_device *pdev)
{
	device_initialize(&pdev->dev);
	return platform_device_add(pdev);
}
首先初始化了platform_device的device结构,然后调用
platform_device_add,这个是注册函数的关键。

mxs_add_device(pdev, 3);是platform_device_register的简单封装

3、自己注册设备流程

单设备:
在这里插入图片描述
通过定义一个platform_device实体,并进初始化,再将实体赋值给looup数组,最后通过mxs_get_device得到设备实体的指针 再进行注册。

多设备:
在这里插入图片描述
定义实体数组,并初始化,再将实体赋值给looup数组,在通过mxs_get_devices 获得lookup数组的指针,再遍历注册设备

三、platform驱动

1.platform_driver结构体

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;
};
如果要将所写的驱动程序注册成platform驱动,那么所做的工作就是初始化一个
platform_driver,然后调用platform_driver_register进行注册。

上面主要实现probe和remove,当驱动与设备匹配到就调用probe函数进行驱动的
初始配置,类似于驱动初始化init。remove同exit函数

2、platform_driver_register驱动注册

int platform_driver_register(struct platform_driver *drv)
{
	drv->driver.bus = &platform_bus_type;
	if (drv->probe)
		drv->driver.probe = platform_drv_probe;
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}

3、设备与驱动如何联系起来?

每当注册一个platform驱动的时候就会调用driver_register,这个函数的调用会遍历设备驱动所属总线上的所有设备,并对每个设备调用总线的match函数。
platform驱动是属于platform_bus_type总线,所以调用platform_match函数。
如下所示:
platform_driver_register -> driver_register ->bus_add_driver ->
driver_attach -> __driver_attach ->
driver_match_device(){return drv->bus->match ? drv->bus->match(dev, drv) : 1;}

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

	/* match against the id table first */
	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);
}

可以看出,platform_device与platform_driver是通过platform_match联系起
来的。进一步说,是通过相同的name来匹配的。

4、驱动模板

当设备与驱动通过name匹配到后调用探测函数(probe)

static int mxs_key_probe(struct platform_device *pdev)
{
	.....
	return 0;
}
static int mxs_rtc_remove(struct platform_device *dev)
{
	.....
	return 0;
}
static struct platform_driver mxs_keydrv = {
	.probe		= mxs_rtc_probe,
	.remove		= mxs_rtc_remove,
	.driver		= {
		.name	= "mxs-key",
		.owner	= THIS_MODULE,
	},
};

static int __init mxs_key_init(void)
{
	printk("mxs_key_init \n");
	return platform_driver_register(&mxs_keydrv);
}

static void __exit mxs_key_exit(void)
{
	printk("mxs_key_exit \n");
	platform_driver_unregister(&mxs_keydrv);
}

module_exit(mxs_key_exit);
module_init(mxs_key_init);

那如果先调用platform_driver_register进行驱动注册,再调用platform_device_register进行设备注册,是否可以找到相应的驱动呢?
答案是肯定的!
platform_device_register -> platform_device_add -> device_add -> bus_probe_device -> device_attach -> __device_attach

static int __device_attach(struct device_driver *drv, void *data)
{
	struct device *dev = data;

	if (!driver_match_device(drv, dev))
		return 0;

	return driver_probe_device(drv, dev);
}

可以看出,通过如上流程,设备找到了相应的驱动,并调用了相应的probe探测函数。

总结

1、设备注册: 在arch/arm/plat-mxs/device.c里通过platform_device_register进行设备注册,
2、驱动注册:在drivers/key/key.c里通过platform_driver_register进行驱动注册。
3、然后设备与驱动通过相同的名字”mxs-key”匹配成功后,最后会调用驱动探测函数mxs_key_probe。
4、mxs_key_probe做的是一些针对lcd的初始化工作。其中也包括,注册输入子系统、中断,等等。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值