您的位置:首页 > 运维架构 > 网站架构

Linux驱动下的platform总线架构(转)

2014-12-26 11:32 344 查看
作者: vitaminch 时间: 2010-04-23

从 Linux 2.6 内核起,引入一套新的驱动管理和注册机制:platform_device 和 platform_driver 。

Linux 中大部分的设备驱动,都可以使用这套机制,设备用 platform_device 表示;驱动用 platform_driver 进行注册。

Linux platform driver 机制和传统的 device driver 机制(即:通过 driver_register 函数进行注册)相比,一个十分明显的优势在于 platform 机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中用使用这些资源时,通过 platform device 提供的标准接口进行申请并使用。

platform 是一个虚拟的地址总线,相比 PCI、USB,它主要用于描述 SOC 上的片上资源。比如 S3C2410 上集成的控制器( LCD、Watchdog、RTC等),platform 所描述的资源有一个共同点:在 CPU 的总线上直接取址。

平台设备会分到一个名称(用在驱动绑定中)以及一系列诸如地址和中断请求号(IRQ)之类的资源。

struct platform_device { // include/linux/platform_device.h

const char * name;

int id;

struct device dev;

u32 num_resources;

struct resource * resource;

};

***************************************************************************

/*

* 主要用于定义具体设备占用的硬件资源(如:地址空间、中断号等)。

*/

struct resource {

resource_size_t start;

resource_size_t end;

const char *name;

unsigned long flags;

struct resource *parent, *sibling, *child;

};

***************************************************************************

platform 总线下驱动的开发步骤是:

1、设备

设备注册中,需要实现的机构体是:platform_device 。

1)初始化 resource 结构变量

2)初始化 platform_device 结构变量

3)向系统注册设备:platform_device_register。(以上三步,必须在 设备驱动加载前完成,所以,AT91RM9200 的内核下,该三步是放在 arch/arm/mach-at91/board-dk.c 中)

2、驱动

驱动注册中,需要实现的结构体是:platform_driver 。

struct platform_driver { // include/linux/platform_device.h

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 (*suspend_late)(struct platform_device *, pm_message_t state);

int (*resume_early)(struct platform_device *);

int (*resume)(struct platform_device *);

struct pm_ext_ops *pm;

struct device_driver driver;

};

在驱动程序的初始化函数中,调用了 platform_driver_register() 注册 platform_driver 。

需要注意的是:platform_driver 和 platform_device 中的 name 变量的值必须是相同的 。这样在

platform_driver_register() 注册时,会将当前注册的 platform_driver 中的 name 变量的值和已注册的所有 platform_device 中的 name 变量的值进行比较,只有找到具有相同名称的 platform_device 才能注册成功。当注册成功时,会调用 platform_driver 结构元素 probe 函数指针。

rtc-ds1302.c 中,驱动首先执行:

********************************************************************

static int __init ds1302_rtc_init(void)

{

return platform_driver_register(&ds1302_platform_driver);

}

********************************************************************

然后,进入:drivers/base/platform.c:platform_driver_register()

********************************************************************

/**

* platform_driver_register

* @drv: platform driver structure

*/

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;

if (drv->pm)

drv->driver.pm = &drv->pm->base;

return driver_register(&drv->driver);

}

此处,对drv->driver 结构体中的几个函数指针进行初始化设置。最后,调用 driver_register 注册 driver 成员。

drv->driver 的类型是:

struct platform_driver { // include/linux/platform_device.h

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 (*suspend_late)(struct platform_device *, pm_message_t state);

int (*resume_early)(struct platform_device *);

int (*resume)(struct platform_device *);

struct pm_ext_ops *pm;

struct device_driver driver; // drv->driver

};

struct device_driver { // include/linux/device.h

const char *name;

struct bus_type *bus;

struct module *owner;

const char *mod_name; /* used for built-in modules */

int (*probe) (struct device *dev);

int (*remove) (struct device *dev);

void (*shutdown) (struct device *dev);

int (*suspend) (struct device *dev, pm_message_t state);

int (*resume) (struct device *dev);

struct attribute_group **groups;

struct pm_ops *pm;

struct driver_private *p;

};

********************************************************************

然后,进入:drivers/base/driver.c:driver_register

********************************************************************

/**

* driver_register - register driver with bus

* @drv: driver to register

*

* We pass off most of the work to the bus_add_driver() call,

* since most of the things we have to do deal with the bus

* structures.

*/

int driver_register(struct device_driver *drv)

{

int ret;

struct device_driver *other;

if ((drv->bus->probe && drv->probe) ||

(drv->bus->remove && drv->remove) ||

(drv->bus->shutdown && drv->shutdown))

printk(KERN_WARNING "Driver '%s' needs updating - please use "

"bus_type methods\n", drv->name);

other = driver_find(drv->name, drv->bus);

if (other) {

put_driver(other);

printk(KERN_ERR "Error: Driver '%s' is already registered, "

"aborting...\n

", drv->name);

return -EEXIST;

}

ret = bus_add_driver(drv); // 调用:bus_add_driver(),执行到此处,drv->bus 指向的是 platform_bus_type 这一全局变量。

if (ret)

return ret;

ret = driver_add_groups(drv, drv->groups);

if (ret)

bus_remove_driver(drv);

return ret;

}

********************************************************************

然后,进入:drivers/base/bus.c:bus_add_driver

********************************************************************

/**

* bus_add_driver - Add a driver to the bus.

* @drv: driver.

*/

int bus_add_driver(struct device_driver *drv)

{

struct bus_type *bus;

struct driver_private *priv;

int error = 0;

bus = bus_get(drv->bus);

if (!bus)

return -EINVAL;

pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

priv = kzalloc(sizeof(*priv), GFP_KERNEL);

if (!priv) {

error = -ENOMEM;

goto out_put_bus;

}

klist_init(&priv->klist_devices, NULL, NULL);

priv->driver = drv;

drv->p = priv;

priv->kobj.kset = bus->p->drivers_kset;

error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,

"%s", drv->name);

if (error)

goto out_unregister;

if (drv->bus->p->drivers_autoprobe) {

error = driver_attach(drv);

if (error)

goto out_unregister;

}

klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

module_add_driver(drv->owner, drv);

error = driver_create_file(drv, &driver_attr_uevent);

if (error) {

printk(KERN_ERR "%s: uevent attr (%s) failed\n",

__func__, drv->name);

}

error = driver_add_attrs(bus, drv);

if (error) {

/* How the hell do we get out of this pickle? Give up */

printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",

__func__, drv->name);

}

error = add_bind_files(drv);

if (error) {

/* Ditto */

printk(KERN_ERR "%s: add_bind_files(%s) failed\n",

__func__, drv->name);

}

kobject_uevent(&priv->kobj, KOBJ_ADD);

return error;

out_unregister:

kobject_put(&priv->kobj);

out_put_bus:

bus_put(bus);

return error;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: