一、platform总线、设备与驱动相关概念
platform机制是在linux 2.6版本中引入的。我们知道通常大部分的设备以及驱动需要挂在一条具体的总线上,如i2c、spi、usb等。但是对于现在的嵌入式系统而言,出现了一种时髦的东西-soc,就是片内系统的意思。这个soc系统中所集成的一些独立的外设控制器、挂在soc内存空间的外设等却不依附于我们上面说的总线。基于这种现象,linux中出现了一种称为platform的虚拟的总线,我们称之为platform总线,当然了,它也有自己对应的设备以及驱动,分别是platform_device以及platform_driver。但是这里的platform_device以及platform_driver与我们的字符、块、网络设备不是同一个概念。这里我们将platform_device以及platform_driver理解为一种linux系统所提供的附加手段。但这并不冲突。
二、platform总线、设备与驱动的使用
在说明使用之前,先理清三者的关系。在设备模型中,我们一般关心总线、设备以及驱动这三个实体。我们platform也提供了同样的实体。这里总线将设备与驱动进行了绑定。在注册设备时会去总线上寻找与之匹配的驱动,同样驱动被注册时也去它所属的总线上找寻设备。若找到了,并且匹配成功,则就会进行具体的操作。下面看看具体的使用情况。
1、platform_device
platform_device结构:
-
struct platform_device {
-
const char *name;
-
u32 id;
-
struct device dev;
-
u32 num_resources;
-
struct resource *resource;
-
}
platform设备有个特点,就是cpu总线可以直接寻址。我们对platform_device的定义通常在bsp的板级文件中进行,一般将platform归纳为一个数组,再通过platform_add_devices()函数统一注册。如下(arch/xxx/mach-xxx/xx):
-
static struct i2c_board_info dkb_i2c_camera[] = {
-
#if defined(CONFIG_SOC_CAMERA_OV5642)
-
{
-
I2C_BOARD_INFO("ov5642", 0x3c),
-
},
-
};
-
-
static struct soc_camera_link iclink_ov5642_dvp = {
-
.bus_id = 0, /* Must match with the camera ID */
-
.power = camera_sensor_power,
-
.board_info = &dkb_i2c_camera[0],
-
.i2c_adapter_id = 0,
-
/* .flags = SOCAM_MIPI, */
-
.module_name = "ov5642",
-
.priv = "pxa910-dvp",
-
};
-
-
static struct platform_device dkb_ov5642_dvp = {
-
.name = "soc-camera-pdrv",
-
.id = 0,
-
.dev = {
-
.platform_data = &iclink_ov5642_dvp,
-
},
-
};
-
static struct platform_device *ttc_dkb_devices[] = {
-
&ttc_dkb_device_onenand,
-
&pxa910_device_rtc,
-
&dkb_ov5642_dvp,
-
};
-
。
-
。
-
。
-
platform_add_devices(ARRAY_AND_SIZE(ttc_dkb_devices));
这里关于platform_data要说下,我们知道设备除了可以在bsp中定义资源外,还可以附加一些数据信息,因为对设备的硬件描述除了中断、内存、DMA通道外,可能还会有一些其它的依赖于板的配置,并且它们不适宜放置在driver中,因此这里的platform就提供了platform_data来容纳这些信息。platform_add_devices()将设备加到platform中去了。跟踪下看看如何增加的。
-
/**
-
* platform_add_devices - add a numbers of platform devices
-
* @devs: array of platform devices to add
-
* @num: number of platform devices in array
-
*/
-
int platform_add_devices(struct platform_device **devs, int num)
-
{
-
int i, ret = 0;
-
-
for (i = 0; i < num; i++) {
-
ret = platform_device_register(devs[i]);
-
if (ret) {
-
while (--i >= 0)
-
platform_device_unregister(devs[i]);
-
break;
-
}
-
}
-
-
return ret;
-
}
-
EXPORT_SYMBOL_GPL(platform_add_devices);
没啥好说的,再看platform_devcie_register():
-
/**
-
* platform_device_register - add a platform-level device
-
* @pdev: platform device we're adding
-
*/
-
int platform_device_register(struct platform_device *pdev)
-
{
-
device_initialize(&pdev->dev);
-
return platform_device_add(pdev);
-
}
-
EXPORT_SYMBOL_GPL(platform_device_register);
我们看到这里有一个初始化的函数以及增加platform设备的函数。其中device_initialize()函数主要是对pdev嵌入的struct device结构的初始化,这里将platform设备与struct device进行了绑定。我们再看platform_device_add
()函数,此函数比较长,我们截取重要部分进行分析。
-
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;
-
-
pdev->dev.bus = &platform_bus_type;
-
。
-
。
-
。
-
ret = device_add(&pdev->dev);
-
if (ret == 0)
-
return ret;
-
。
-
。
-
。
-
return ret;
-
}
-
EXPORT_SYMBOL_GPL(platform_device_add);
我们看到我们的platform挂在platform_bus上(第9行),这里的platform_bus在具体的驱动中已经被注册了。作为我们现在设备的父目录。而device_add()函数中主要是调用很多的例程完成dev的添加,其中我们需要关注的是bus_probe_device(dev),如下:
-
/**
-
* bus_probe_device - probe drivers for a new device
-
* @dev: device to probe
-
*
-
* - Automatically probe for a driver if the bus allows it.
-
*/
-
void bus_probe_device(struct device *dev)
-
{
-
struct bus_type *bus = dev->bus;
-
int ret;
-
-
if (bus && bus->p->drivers_autoprobe) {
-
ret = device_attach(dev);
-
WARN_ON(ret < 0);
-
}
-
}
这个函数实现对我们新加的设备的探测,再看device_attach()函数:
-
/**
-
* device_attach - try to attach device to a driver.
-
* @dev: device.
-
*
-
* Walk the list of drivers that the bus has and call
-
* driver_probe_device() for each pair. If a compatible
-
* pair is found, break out and return.
-
*
-
* Returns 1 if the device was bound to a driver;
-
* 0 if no matching driver was found;
-
* -ENODEV if the device is not registered.
-
*
-
* When called for a USB interface, @dev->parent lock must be held.
-
*/
-
int device_attach(struct device *dev)
-
{
-
int ret = 0;
-
-
device_lock(dev);
-
if (dev->driver) {
-
if (klist_node_attached(&dev->p->knode_driver)) {
-
ret = 1;
-
goto out_unlock;
-
}
-
ret = device_bind_driver(dev);
-
if (ret == 0)
-
ret = 1;
-
else {
-
dev->driver = NULL;
-
ret = 0;
-
}
-
} else {
-
pm_runtime_get_noresume(dev);
-
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
-
pm_runtime_put_sync(dev);
-
}
-
out_unlock:
-
device_unlock(dev);
-
return ret;
-
}
-
EXPORT_SYMBOL_GPL(device_attach);
一个bus_for_each_drv()揭开了谜底,它要实现的就是查找与我们device匹对的driver。如果找到就调用__device_attach()
这个回调函数。看下__device_attach():
-
static int __device_attach(struct device_driver *drv, void *data)
-
{
-
struct device *dev = data;
-
-
if (!driver_match_device(drv, dev))
-
return 0;
-
-
return driver_probe_device(drv, dev);
-
}
如果device与driver匹对成功,则调用driver_probe_device()函数,此函数中使用really_probe()完成最后的调用,看下:
-
static int really_probe(struct device *dev, struct device_driver *drv)
-
{
-
.
-
.
-
.
-
if (dev->bus->probe) {
-
ret = dev->bus->probe(dev);
-
if (ret)
-
goto probe_failed;
-
} else if (drv->probe) {
-
ret = drv->probe(dev);
-
if (ret)
-
goto probe_failed;
-
}
-
.
-
.
-
.
-
}
6-14行:如果设置了
dev
->bus
->probe,则调用之,但在platform_bus_type 没有设置,所以我们这里就调用platform驱动注册在device_driver的probe,但是这里暂时还未实现,关于此probe就要看下面了。
2、platform_driver
粗略浅析了platform_device,我们再看看platform_driver又是怎样工作的呢。一般我们通过platform_driver_probe()或者platform_driver_register()完成driver的注册,这两者功能基本相同,它们的区别就是如果使用前者,则此driver不能被以后其他的device probe了,也就是说此driver只能和一个device进行绑定。下面我们看看具体的driver是怎么跑起来的:
-
static struct platform_driver __refdata soc_camera_pdrv = {
-
.remove = __devexit_p(soc_camera_pdrv_remove),
-
.driver = {
-
.name = "soc-camera-pdrv",
-
.owner = THIS_MODULE,
-
},
-
};
-
-
platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);
这边的name跟上面的name一模一样,为了日后的绑定埋下伏笔。在platform_driver_probe()函数里调用了platform_driver_register()函数,看下它:
-
/**
-
* platform_driver_register - register a driver for platform-level devices
-
* @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;
-
-
return driver_register(&drv->driver);
-
}
-
EXPORT_SYMBOL_GPL(platform_driver_register);
这里将我们的driver设置成了platform_bus_type,我们说过driver和device是通过bus联系在一起的,这里就是通过platform_bus_type中注册的回调例程和属性来实现的,上面说的device与这里的driver的匹配就是通过platform_bus_type注册的回调例程mach()函数来完成的。platform_bus_type哪里来的?看下它是怎么被注册的:
-
struct bus_type platform_bus_type = {
-
.name = "platform",
-
.dev_attrs = platform_dev_attrs,
-
.match = platform_match,
-
.uevent = platform_uevent,
-
.pm = &platform_dev_pm_ops,
-
};
-
。
-
。
-
。
-
bus_register(&platform_bus_type);
这里注册了一个名为”platform“的bus,在syfsfs显示为sys/bus/platform。好了,再看driver_register(),此函数中主要调用了bus_add_driver()函数,顾名思义,就是add driver to bus的意思。bus_add_driver()函数中又调用了driver_attach()函数,跟上面device_attach()很像,driver_attach()就是找寻跟此driver相匹配的device。
-
/**
-
* driver_attach - try to bind driver to devices.
-
* @drv: driver.
-
*
-
* Walk the list of devices that the bus has on it and try to
-
* match the driver with each one. If driver_probe_device()
-
* returns 0 and the @dev->driver is set, we've found a
-
* compatible pair.
-
*/
-
int driver_attach(struct device_driver *drv)
-
{
-
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
-
}
-
EXPORT_SYMBOL_GPL(driver_attach);
第11行:遍历bus,对每个device使用回调函数__driver_attach()来鉴别是否和driver匹配。这里__driver_attach()与上面__device_attach()很相似,看下__driver_attach():
-
static int __driver_attach(struct device *dev, void *data)
-
{
-
struct device_driver *drv = data;
-
-
/*
-
* Lock device and try to bind to it. We drop the error
-
* here and always return 0, because we need to keep trying
-
* to bind to devices and some drivers will return an error
-
* simply if it didn't support the device.
-
*
-
* driver_probe_device() will spit a warning if there
-
* is an error.
-
*/
-
-
if (!driver_match_device(drv, dev))
-
return 0;
-
-
if (dev->parent) /* Needed for USB */
-
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);
-
-
return 0;
-
}
真是犹抱琵琶半遮面,我们要得结果快要出来了。对dev->driver进行判断,显然我们的dev还未有driver,所以会执行driver_probe_device
()函数,来看看它:
-
/**
-
* driver_probe_device - attempt to bind device & driver together
-
* @drv: driver to bind a device to
-
* @dev: device to try to bind to the driver
-
*
-
* This function returns -ENODEV if the device is not registered,
-
* 1 if the device is bound successfully and 0 otherwise.
-
*
-
* This function must be called with @dev lock held. When called for a
-
* USB interface, @dev->parent lock must be held as well.
-
*/
-
int driver_probe_device(struct device_driver *drv, struct device *dev)
-
{
-
int ret = 0;
-
-
if (!device_is_registered(dev))
-
return -ENODEV;
-
-
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
-
drv->bus->name, __func__, dev_name(dev), drv->name);
-
-
pm_runtime_get_noresume(dev);
-
pm_runtime_barrier(dev);
-
ret = really_probe(dev, drv);
-
pm_runtime_put_sync(dev);
-
-
return ret;
-
}
根据platform_device部分知晓,really_probe()完成最后的调用,我们在platform_device部分really_probe()时未能进行,因为driver不存在,而经过本driver的注册后就可以顺利进行了。最终会调用我们driver中绑定的probe,我们这里就是soc_camera_pdrv_probe。好了,platform_device与platform_driver浅析至此告一段落。上面结合了相关的例子对platform部分进行了简单的跟踪分析,其中牵扯到很多的东西,这里也不能具体罗列了,如果想深入了解的话,可以看下ldd3部分十四章linux设备模型,这样更有助于理解。
阅读(2040) | 评论(0) | 转发(0) |