首页 > 代码库 > 两年前实习时的文档——Platform学习总结
两年前实习时的文档——Platform学习总结
1 概述
驱动程序实际上是硬件与应用程序之间的中间层。在Linux操作系统中,设备驱动程序对各种不同的设备提供了一致的访问接口,把设备映射成一个特殊的设备文件,用户程序可以像其他文件一样对设备文件进行操作。
Linux2.6引入了新的设备管理机制kobject,通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管理,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密联系,每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。
在这些内核对象机制的基础上,Linux的设备模型包括设备结构device、驱动程序driver、总线bus和设备类结构class几个关键组件。
一个现实的linux设备和驱动通常都需要挂接在一种总线上,比较常见的总线有USB、PCI总线等。但是,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附于此类总线。基于这样的背景下,2.6内核加入了platform虚拟总线。Platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性。
Platform设备概念的引入是能够更好地描述设备的资源信息。Platform设备是系统中自治的实体,包括基于端口的设备、外围总线和集成入片上系统平台的大多数控制器,它们通常直接通过CPU的总线寻址。每个platform设备被赋予一个名称,并分配一定数量的资源。
Platform总线对加入到该总线的设备和驱动分别封装了结构体——platform_device和platform_driver并且提供了对应的注册函数。
Platform虚拟总线
由上图可知,在platform虚拟总线上我们分别对device和driver进行注册,这样我们能够更加方便的进行驱动设备的管理。这样当有总线或者设备注册到该虚拟总线上时,内核自动的调用platform_match函数将platform_device绑定到platform_driver上。
2 platform数据结构
Linux在启动的时候就注册了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,
};
可以看到,总线中定义了.match函数,当有总线或者设备注册到platform总线时,内核自动调用.match函数,判断device和driver的name是否一致。
platform_device的结构体定义如下:
struct platform_device {
constchar * name;//设备名字,这将代替device->dev_id,用作sys/device下显示目录名
int id;//设备ID,用于给插入该总线并且具有相同name的设备编号,如果只有一个设备的话填-1
structdevice dev;//结构体中内嵌的device结构体
u32 num_resources;//资源数
structresource * resource;//用于存放资源的数组
conststruct platform_device_id *id_entry;
/*arch specific additions */
structpdev_archdata archdata;
};
可以看出,在platform_device中定义了name,并且内嵌了structdevice结构体。另外,包含的structresource如下:
struct resource {
resource_size_tstart;
resource_size_tend;
constchar *name;
unsignedlong flags;
structresource *parent, *sibling, *child;
};
platform_driver的结构体定义如下:
struct platform_driver {
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(*resume)(struct platform_device *);
structdevice_driver driver;
conststruct platform_device_id *id_table;
};
可以看出,在platform_driver中内嵌了structdevice_driver,以及一些回调函数。structdevice_driver的具体定义如下:
struct device_driver {
constchar *name;
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);
conststruct attribute_group **groups;
conststruct dev_pm_ops *pm;
structdriver_private *p;
};
此处定义了name用于和platform_device匹配。因为platform_device和platform_driver的匹配就是通过内嵌的structdevice和structdevice_driver的name进行匹配的。
static int platform_match(struct device *dev,struct device_driver *drv)
{
structplatform_device *pdev = to_platform_device(dev);
structplatform_driver *pdrv = to_platform_driver(drv);
/*match against the id table first */
if(pdrv->id_table)
returnplatform_match_id(pdrv->id_table, pdev) != NULL;
/*fall-back to driver name match */
return(strcmp(pdev->name, drv->name) == 0);
}
3 platform device
3.1 register
注册一个platform层的设备。注册后,会在sys/device目录下创建一个以name命名的目录,并且创建软连接到/sys/bus/platform/device下。
int platform_device_register(structplatform_device *pdev)
{
device_initialize(&pdev->dev);
returnplatform_device_add(pdev);
}
其中,第一步device_initialize(在kernel文件夹下)用于初始化一个structdevice。函数的定义如下:
void device_initialize(struct device *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);
}
第二步,添加一个platformdevice到device层 。函数的定义如下:
int platform_device_add(struct platform_device*pdev)
{
inti, ret = 0;
if(!pdev)
return -EINVAL;
if(!pdev->dev.parent)
pdev->dev.parent= &platform_bus;//如果p->dev.parent不存在则赋值&platform_bus
pdev->dev.bus= &platform_bus_type;//设置pdev->dev.bus的bus类型
if(pdev->id != -1) //pdev->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;
elseif (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;
gotofailed;
}
}
pr_debug("Registeringplatform device ‘%s‘. Parent at %s\n",
dev_name(&pdev->dev),dev_name(pdev->dev.parent));
ret= device_add(&pdev->dev);//(在core/core.c中定义)
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);
}
returnret;
}
3.2 unregister
platform_device_unregister用于注销一个platform-leveldevice。函数的定义如下:
void platform_device_unregister(structplatform_device *pdev)
{
platform_device_del(pdev);
platform_device_put(pdev);
}
在注销一个设备时,第一步调用platform_device_del函数。此函数的定义如下:
void platform_device_del(structplatform_device *pdev)
{
inti;
if(pdev) {
device_del(&pdev->dev);
//遍历所有的resource如果是IORESOURCE_MEM或者IORESOURCE_IO类型则调用release_resource函数释放掉resource。
for (i = 0; i <pdev->num_resources; i++) {
structresource *r = &pdev->resource[i];
unsignedlong type = resource_type(r);
if (type == IORESOURCE_MEM || type== IORESOURCE_IO)
release_resource(r);
}
}
}
此函数的作用是:移除一个platform-leveldevice。其中的IOSOURCE_MEM和IORESOURCE_IR是CPU对外设I/O端口物理地址的两种编制方式。Resource是一个指向platform资源数组的指针,该数组有num_resource个资源,下面是资源结构体的定义(linux/ioport.h):
struct resource {
resource_size_tstart; //起始地址
resource_size_tend; //终止地址
constchar *name; //名称
unsignedlong flags; //标志
structresource *parent, *sibling, *child;
};
在structplatform_device中可以设置多种资源信息。资源的flags标志包括:
#define IORESOURCE_IO 0x00000100 //IO资源
#define IORESOURCE_MEM 0x00000200 //内存资源
#define IORESOURCE_IRQ 0x00000400 //中断资源
#define IORESOURCE_DMA 0x00000800 //DMA资源
第二步,调用platform_device_put函数。
void platform_device_put(structplatform_device *pdev)
{
if(pdev)
put_device(&pdev->dev);
}
销毁一个platformdevice,并且释放所有与这个platformdevice相关的内存。
4 platform driver
4.1 register
通过调用函数platform_driver_register实现为platformlevel的设备注册一个驱动。注册成功后,内核会在/sys/bus/platform/driver/目录下创建一个名字为driver->name的目录。具体的函数定义如下:
int platform_driver_register(structplatform_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;
returndriver_register(&drv->driver);
}
此函数首先对struct platform_driver变量的driver进行赋值。然后调用driver_register并且返回。driver_register函数的定义如下:
int driver_register(struct device_driver *drv)
{
intret;
structdevice_driver *other;
BUG_ON(!drv->bus->p);
//如果driver的方法和总线上的方法不能匹配则驱动的名称需要更新
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_typemethods\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 -EBUSY;
}
ret= bus_add_driver(drv);//将驱动加载到总线上,如果成功就返回
if(ret)
return ret;
ret= driver_add_groups(drv, drv->groups);
if(ret)
bus_remove_driver(drv);
returnret;
}
4.2 unregister
通过调用函数platform_driver_unregister函数实现platform_driver级设备的注销。
void platform_driver_unregister(structplatform_driver *drv)
{
driver_unregister(&drv->driver);
}
在此函数中调用了driver_unregister函数,将驱动从系统中移除。函数的具体定义如下:
void driver_unregister(struct device_driver*drv)
{
if(!drv || !drv->p) {
WARN(1, "Unexpected driverunregister!\n");
return;
}
driver_remove_groups(drv,drv->groups);
bus_remove_driver(drv);
}
分两步走,第一步调用driver_remove_groups。函数的具体定义如下:
static void driver_remove_groups(structdevice_driver *drv,
const struct attribute_group **groups)
{
inti;
if(groups)
for (i = 0; groups[i]; i++)
sysfs_remove_group(&drv->p->kobj,groups[i]);
}
其中,调用了
static inline void sysfs_remove_group(struct kobject *kobj,
const struct attribute_group *grp)
{
}
第二步从总线中将驱动删除。调用如下函数:
void bus_remove_driver(struct device_driver *drv)
{
if (!drv->bus)
return;
if(!drv->suppress_bind_attrs)
remove_bind_files(drv);
driver_remove_attrs(drv->bus,drv);
driver_remove_file(drv,&driver_attr_uevent);
klist_remove(&drv->p->knode_bus);
pr_debug("bus: ‘%s‘:remove driver %s\n", drv->bus->name, drv->name);
driver_detach(drv);
module_remove_driver(drv);
kobject_put(&drv->p->kobj);
bus_put(drv->bus);
}
此函数的作用是:将驱动从它控制的设备中卸载,并且从驱动的总线链表中将它移除。