linux驱动编程--设备模型3--平台设备解析


按照前面理解的设备模型,今天又制作了一个。平台设备版本的 "hello world".

如果选用平台设备作为总线,那么在设备模型这个三角关系中缺少的就只有 devicedriver了。那么现在的问题有两个:

1).怎么把device挂到平台设备bus的设备链表上。

2).怎么把driver添加到总线的设备链表上。

1.添加device到bus上

要添加一个device到总线的设备列表上,使用的通用函数是 device_register() ,但对于平台设备而言,有一个在该函数上封装的更简单易用的注册函数platform_driver_register()。总之就是使用这两个函数来完成注册,而具体的注册有两种方法:

a). 利用板级文件中的设备初始化,将自己的device添加到对应的设备数值中,那么在系统加载时就会自动加载该设备。但这有一个问题就是需要重新编译内核。

b). 利用模块加载,既然模块式内核留给用户的内核门户。那么它除了用来加载驱动之外,就一定可以用来对内核进行修改。下面的程序就是采用了第二种方法。

下面是一个简单例子

// for test driver

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/platform_device.h>

#define HELLO_NAM	"hello_test"

static struct resource hello_res[] = {
	[0] = {
		.start = 0,
		.end = 0x1,
		.flags = IORESOURCE_MEM,
	},

	[1] = {
		.start = 0x2,
		.end = 0x3,
		.flags = IORESOURCE_MEM,
	},

};

static struct platform_device hello_device =  {

	.name = HELLO_NAM,
	.id = 0,
	.num_resources = ARRAY_SIZE(hello_res),
	.resource = hello_res,
};

static int __init hello_init( void)
{

	return platform_device_register( &hello_device);
}

static void __exit hello_exit()
{

	platform_device_unregister( &hello_device);
}

MODULE_LICENSE("GPL");

module_init( hello_init);
module_exit( hello_exit);

2.添加driver到bus上

在上面device已经成功添加到bus上去了之后,现在就剩下将driver加载上去了。在通用的驱动中使用的信息记录结构体是device_driver,但为了使用方便平台设备又有一个封装结构体platform_driver。在通用的驱动中使用的驱动注册函数是driver_register() ,在设备平台中的封装产品是platform_driver_register() 。那么制作一个完整的例子就是

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/platform_device.h>

#define HELLO_NAME	"hello_test"

static int hello_probe(struct platform_device *dev)
{

	struct resource *pRes = NULL;
	printk("hello_probe............\n");

	pRes = platform_get_resource( dev, IORESOURCE_MEM, 1);

	if( NULL!=pRes)	
	{
		printk(" resource : %d, %d, %s\n", pRes->start, pRes->end, pRes->name);
	}
	device_register(struct device * dev);
	return 0;	//返回0表示接受这次探测
}

static int hello_remove(struct platform_device *dev)
{
	printk(" hello_remove.........................\n");

	return 0;
}

static struct platform_driver hello_drv = {
	.probe = hello_probe,
	.remove = hello_remove,
	.driver = {
		.name = HELLO_NAME,
		.owner = THIS_MODULE,
	},
};


static int __init hello_init( void)
{
	driver_register(struct device_driver * drv);
	return platform_driver_register( &hello_drv);
}

static void __exit hello_exit( void)
{
	platform_driver_unregister( &hello_drv);
}

MODULE_LICENSE("GPL");

module_init( hello_init);
module_exit( hello_exit); 

3.platfrom是设备模型的一个实例

从platform源码可以看出其只是设备模型的一个实例。来看一下platforn_device。它的信息结构体是

struct platform_device {
	const char	* name;
	//设备名,会用来和驱动进行匹配
	int		id;
	//
	struct device	dev;
	//通用设备成员
	u32		num_resources;
	//资源数
	struct resource	* resource;
	//资源数组
	struct platform_device_id	*id_entry;
};

从信息结构体中看到,platform_device包含了一个通用设备结构体成员dev。dev就是最终在总线上工作的信息节点。再来看注册函数

int platform_device_add(struct platform_device *pdev)
{
	int i, ret = 0;

	if (!pdev)
		return -EINVAL;
	//
	if (!pdev->dev.parent)
		pdev->dev.parent = &platform_bus;

	pdev->dev.bus = &platform_bus_type;

	if (pdev->id != -1)
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
	else
		dev_set_name(&pdev->dev, pdev->name);
	//将资源加入一个全局的资源链表
	for (i = 0; i < pdev->num_resources; i++) {
		struct resource *p, *r = &pdev->resource[i];

		if (r->name == NULL)
			r->name = dev_name(&pdev->dev);

		p = r->parent;
		if (!p) {
			if (resource_type(r) == IORESOURCE_MEM)	//默认的资源链表
				p = &iomem_resource;
			else if (resource_type(r) == IORESOURCE_IO)
				p = &ioport_resource;
		}

		if (p && insert_resource(p, r)) {
			printk(KERN_ERR
			       "%s: failed to claim resource %d\n",
			       dev_name(&pdev->dev), i);
			ret = -EBUSY;
			goto failed;
		}
	}

	//在这里就看到了最熟悉的身影
	ret = device_add(&pdev->dev);
	......
}

最后也就是靠"device_add(&pdev->dev) ",完成了将设备添加到总线(platform总线)的任务。之后的操作就是一般的通用设备注册操作了。

那么现在再来看看,driver部分,其信息结构体为

struct platform_driver {
	int (*probe)(struct platform_device *);
	//作为探针函数存在,返回0表示选中
	int (*remove)(struct platform_device *);
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*suspend_late)(struct platform_device *, pm_message_t state);
	int (*resume_early)(struct platform_device *);
	int (*resume)(struct platform_device *);
	struct device_driver driver;
	//通用设备驱动成员
	struct platform_device_id *id_table;
};

这里除了包含了最核心的通用设备驱动成员外,还包含了一些回调函数接口。

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;
	if (drv->suspend)
		drv->driver.suspend = platform_drv_suspend;
	if (drv->resume)
		drv->driver.resume = platform_drv_resume;
	//熟悉的身影
	return driver_register(&drv->driver);
}

通过其注册代码,很容易又找到了"driver_register(&drv->driver) "。

通过对上面的device和driver两部分的分析,很容易就能看到设备模型与平台设备的关系。

相关内容