分类: LINUX
2012-04-23 20:42:12
设备模型之三 platform
2012-04-21
Platform 设备模型实际上也是总线设备驱动,只是为什么要把它独立开来呢?从 linux 2.6以后,为了更好的管理内核模块,从而引入了设备模型。而大家有没有发现每个模块都是独立的,比如说电源管理、热插拔的事件等都是具有自己的功能和特点,其实 platform 也是一样啊,它主要管理内核片上资源相关的,大家可以从它的定义中看出#include
struct platform_device {
const char * name; /*设备名*/
int id; /*设备号,配合设备名使用*/
struct device dev; /*设备<总线>*/
u32 num_resources;
struct resource * resource; /*设备资源
struct platform_device_id *id_entry;
/* arch specific additions */
struct pdev_archdata archdata;
};
其中就有一个 struct resource 结构专门定义片上资源的。这样的目的为了更好管理片上资源和为了以后的移植工作更方便和简单。大家也发现该结构中也有 struct device ,大家会不会这样想,这就是前面我们所说的嵌入的struct device,正是如此啊。因此认真研究 platform 的源代码是非常有必要的哦。
从 platform 设备模型使用方法中:
1)定义平台设备
2)注册平台设备
3)定义平台驱动
4)注册平台驱动
中可以看出我们并没有定义和注册平台总线。为什么呢?而我们经常说总线是处理和设备(一个或者多个)之间的通道,难道 platform 设备模型不需要总线,还是内核帮我们处理好了呢?若处理好了,在哪里处理吗?何时处理的呢?
在系统中platform对应的文件drivers/base/platform.c,它不是作为一个模块注册到内核的,关键的注册总线的函数由系统初始化部分,对应/init/main.c中的do_basic_setup函数间接调用。从而看出,platform 有多么的重要,也就是内核注册板级驱动时处理的。而在 do_basic_setup 间接调用
而 driver_init();而在 /drivers/base/init.c 中 driver_init()
struct device platform_bus = {
.init_name = "platform", /*为何只初始化设备名字呢?*/
};
struct bus_type platform_bus_type = {
.name = "platform", /*为何与 上面名字相同呢?*/
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
/*总线也是设备,因而也要注册设备*/
error = device_register(&platform_bus);
if (error)
return error;
/*注册platform 总线,所有的 platform 设备模型的总线都挂载该总线上*/
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev); /*初始化platform_device中的struct device==>这是内核帮忙做的初始化,那我们就看看哪些需要手动初始化*/
return platform_device_add(pdev); /*这跟device_add类似,是寻找挂载到哪跟总线上*/
}
/**
* device_initialize - init device structure.
* @dev: device.
*
* This prepares the device for use by other layers by initializing
* its fields.
* It is the first half of device_register(), if called by
* that function, though it can also be called separately, so one
* may use @dev's fields. In particular, get_device()/put_device()
* may be used for reference counting of @dev after calling this
* function.
*
* NOTE: Use put_device() to give up your reference instead of freeing
* @dev directly once you have called this function.
*/
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
device_pm_init(dev);
set_dev_node(dev, -1);
}
/**
* platform_device_add - add a platform device to device hierarchy
* @pdev: platform device we're adding
*
* This is part 2 of platform_device_register(), though may be called
* separately _iff_ pdev was allocated by platform_device_alloc().
*/
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; /*初始化平台设备的父类
==>platform_bus(platform)*/
pdev->dev.bus = &platform_bus_type; /*初始化平台设备的总线
==>platform_bus_type(platform)*/
/*该名字在/sys/device/platform 出现*/
if (pdev->id != -1) /*id 处理,一般采用 id = -1 */
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
else
dev_set_name(&pdev->dev, "%s", 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;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
dev_name(&pdev->dev), dev_name(pdev->dev.parent));
ret = device_add(&pdev->dev); /*平台设备寻找挂载的总线*/
if (ret == 0)
return ret;
failed:
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
release_resource(r);
}
return ret;
}
/**
* device_add - add device to device hierarchy.
* @dev: device.
*
* This is part 2 of device_register(), though may be called
* separately _iff_ device_initialize() has been called separately.
*
* This adds @dev to the kobject hierarchy via kobject_add(), adds it
* to the global and sibling lists for the device, then
* adds it to the other relevant subsystems of the driver model.
*
* NOTE: _Never_ directly free @dev after calling this function, even
* if it returned an error! Always use put_device() to give up your
* reference instead.
*/
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev);
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL; /*为何重新赋空值*/
}
if (!dev_name(dev))
goto name_error;
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent);
setup_parent(dev, parent);
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
devtmpfs_create_node(dev);
}
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
if (error)
goto AttrsError;
error = bus_add_device(dev);
if (error)
goto BusError;
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
/* Notify clients of device addition. This call must come
* after dpm_sysf_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev);
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->class_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->class_mutex);
}
done:
put_device(dev);
return error;
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
device_remove_sys_dev_entry(dev);
devtattrError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
}
到了这里差不多把 platform 设备讲完,而 platform 设备驱动大家可以按照此方法自己去寻找,若碰到不懂的、不明白的你把它搞明白,就是你自己的了。