一、Platform概述
从Linux2.6起,内核引入了一套新的驱动管理和注册机制:Platform_device和 Platform_driver。现在Linux中大部分的设备驱动都可以使用这套机制,总线为platform_bus,设备用platform_device表示,驱动用platform_driver进行注册。
Linux的这种platform driver机制和传统的device_driver机制相比,一个十分明显的优势在于platform机制将本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform_device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性。下面是SPI驱动层次示意图,Linux中的SPI总线可理解为SPI控制器引出的总线:
和传统的驱动一样,platform机制也分为三个步骤:
1、总线注册阶段:
内核启动初始化时linux3.12/init/main.c文件中的: kernel_init()→kernel_init_freeable()->do_basic_setup()→driver_init()→platform_bus_init()→bus_register(&platform_bus_type), 注册了一条platform总线(虚拟总线,platform_bus)。
分析流程:
platform_bus_init ->bus_register,调用bus_register注册总线platform_bus_type,其名字为"platform",并且在platform目录下创建devices和drivers目录.
int __init platform_bus_init(void)
{
...
early_platform_cleanup();
error = device_register(&platform_bus);
error = bus_register(&platform_bus_type);
return error;
}
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv; //重点
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
priv->bus = bus; //指定 priv中的bus_type;
bus->p = priv; //指定bus中的priv;
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //将名字"platform"赋值给kobject
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype; //初始化为bus_ktype
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys); //注册kset
retval = bus_create_file(bus, &bus_attr_uevent);
priv->devices_kset = kset_create_and_add("devices", NULL, //在sys/bus/platform目录创建devices
&priv->subsys.kobj);
priv->drivers_kset = kset_create_and_add("drivers", NULL,
//在sys/bus/platform目录创建drivers&priv->subsys.kobj);
klist_init (& priv -> klist_devices , klist_devices_get , klist_devices_put );
klist_init(&priv->klist_drivers, NULL, NULL);
retval = add_probe_files(bus);