Linux驱动的platform机制
2015-05-31 11:36
393 查看
一、Platform概述
从Linux2.6起,内核引入了一套新的驱动管理和注册机制:Platform_device和Platform_driver。现在Linux中大部分的设备驱动都可以使用这套机制,总线为platform_bus,设备用platform_device表示,驱动用platform_driver进行注册。Linux的这种platformdriver机制和传统的device_driver机制相比,一个十分明显的优势在于platform机制将本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform_device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性。下面是SPI驱动层次示意图,Linux中的SPI总线可理解为SPI控制器引出的总线:int__initplatform_bus_init(void)
{
...
early_platform_cleanup();
error=device_register(&platform_bus);
error=bus_register(&platform_bus_type);
returnerror;
}
structbus_typeplatform_bus_type={
.name="platform",
.dev_groups=platform_dev_groups,
.match=platform_match,
.uevent=platform_uevent,
.pm=&platform_dev_pm_ops,
};分析bus_register:
intbus_register(structbus_type*bus)
{
intretval;
structsubsys_private*priv;//重点
priv=kzalloc(sizeof(structsubsys_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);
retval=bus_add_attrs(bus);
retval=bus_add_groups(bus,bus->bus_groups);
return0;
}
staticstructkobj_typebus_ktype={
.sysfs_ops=&bus_sysfs_ops,
};总线注册流程图如下所示:
intplatform_device_register(structplatform_device*pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
returnplatform_device_add(pdev);
}首先调用device_initialize对device进行初始化,代码如下:
voiddevice_initialize(structdevice*dev)
{
dev->kobj.kset=devices_kset;
kobject_init(&dev->kobj,&device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev,-1);
}然后platform_device_add:
intplatform_device_add(structplatform_device*pdev)
{
inti,ret;
if(!pdev)
return-EINVAL;
if(!pdev->dev.parent)
pdev->dev.parent=&platform_bus;
pdev->dev.bus=&platform_bus_type;//初始化structdevice中的bus_type...
ret=device_add(&pdev->dev);
}
}
structdeviceplatform_bus={
.init_name="platform",//初始化device的name
};device_add:
intdevice_add(structdevice*dev)
{...
dev=get_device(dev);//调用container_ofcontainer_of(kobj,structdevice,kobj)得到dev...
/*first,registerwithgenericlayer.*/
/*werequirethenametobesetbefore,andpassNULL*/
error=kobject_add(&dev->kobj,dev->kobj.parent,NULL);
if(error)
gotoError;
/*notifyplatformofdeviceentry*/
if(platform_notify)
platform_notify(dev);
error=device_create_file(dev,&dev_attr_uevent);//创建设备属性文件
error=device_add_attrs(dev);
error=bus_add_device(dev);//添加设备到bus
kobject_uevent(&dev->kobj,KOBJ_ADD);//probedriversforanewdevice->device_attach->device_bind_driveror->__device_attach->driver_probe_device->really_probe->dev->bus->probeor->drv->probe
bus_probe_device(dev);...}[/code]
structplatform_device{
constchar*name;
intid;
boolid_auto;
structdevicedev;
u32num_resources;
structresource*resource;
conststructplatform_device_id*id_entry;
/*MFDcellpointer*/
structmfd_cell*mfd_cell;
/*archspecificadditions*/
structpdev_archdataarchdata;
};3、驱动注册阶段:__platform_driver_register()→driver_register()→bus_add_driver()→driver_attach()→bus_for_each_dev(),对在每个挂在虚拟的platformbus的设备作__driver_attach()→driver_probe_device(),判断drv→bus→match()是否执行成功,此时通过指针执行platform_match→strncmp(pdev→name,drv→name,BUS_ID_SIZE),如果相符就调用really_probe(实际就是执行相应设备的platform_driver→probe(platform_device)。)开始真正的探测,如果probe成功,则绑定设备到该驱动。
int__platform_driver_register(structplatform_driver*drv,
structmodule*owner)
{
drv->driver.owner=owner;
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;
returndriver_register(&drv->driver);
}driver_register:
intdriver_register(structdevice_driver*drv)
{
...
ret=bus_add_driver(drv);
kobject_uevent(&drv->p->kobj,KOBJ_ADD);
returnret;
}bus_add_driver:
intbus_add_driver(structdevice_driver*drv)
{
structbus_type*bus;
structdriver_private*priv;
interror=0;
bus=bus_get(drv->bus);
priv=kzalloc(sizeof(*priv),GFP_KERNEL);
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);
klist_add_tail(&priv->knode_bus,&bus->p->klist_drivers);
if(drv->bus->p->drivers_autoprobe){
error=driver_attach(drv);
}
return0;
}driver_attach->__driver_attach:
intdriver_attach(structdevice_driver*drv)
{
returnbus_for_each_dev(drv->bus,NULL,drv,__driver_attach);
}
staticint__driver_attach(structdevice*dev,void*data)
{
structdevice_driver*drv=data;
if(!driver_match_device(drv,dev))
return0;
if(dev->parent)/*NeededforUSB*/
device_lock(dev->parent);
device_lock(dev);
if(!dev->driver)
driver_probe_device(drv,dev);
device_unlock(dev);
if(dev->parent)
device_unlock(dev->parent);
return0;
}
intdriver_probe_device(structdevice_driver*drv,structdevice*dev)
{
intret=0;
if(!device_is_registered(dev))
return-ENODEV;
pr_debug("bus:'%s':%s:matcheddevice%swithdriver%s\n",
drv->bus->name,__func__,dev_name(dev),drv->name);
pm_runtime_barrier(dev);
ret=really_probe(dev,drv);
pm_request_idle(dev);
returnret;
}
staticintreally_probe(structdevice*dev,structdevice_driver*drv)
{...
if(dev->bus->probe){
ret=dev->bus->probe(dev);
if(ret)
gotoprobe_failed;
}elseif(drv->probe){
ret=drv->probe(dev);
if(ret)
gotoprobe_failed;
}
driver_bound(dev);...
}驱动注册流程图如下所示:
相关文章推荐
- Linux下执行py文件
- linux下的文件目录
- Linux环境变量以及文件查找
- linux命令行常用热键
- 安装vnc
- 6.CentOS常用命令小结
- linux2.6内核,如何指定内核启动参数
- Linux Ubuntu12.04 更新firefox
- Redhat6.2升级为6.3之后再降级为Redhat6.2 (linux内核降级)
- Redhat6.2升级为Redhat6.3 (linux内核升级)
- Linux下whereis和which的区别
- 鸟哥的linux私房菜学习笔记 ---第5章-2
- ARM+Linux 嵌入式开发环境搭建
- LEMP stack On CentOS 7
- 关于Linux下前后台程序的一点疑惑(& , (cmd &), setsid )--- (kafka-stop-server.sh不工作问题)
- centos 7 安装jdk java: /lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录
- Linux(centos)系统各个目录的作用详解
- Linux字符驱动中动态分配设备号与动态生成设备节点
- Foreign LINUX让你在Windows上运行Linux应用
- Linux vmstat命令实战详解