关于platform_device和platform_driver的匹配

说句老实话,我不太喜欢现在Linux 2.6这套bus, platform, device,device driver 的模式。我觉得这种模式破坏了Linux的“简单就是美”的哲学,原来那套驱动已经可以包容所有驱动,也可以直接注册驱动文件和管理,而且以前的驱动在现在的结构上也还可以使用,把它在注册到bus这棵树上又有什么用呢?虽然可以看到一点对于移植性和平台管理方面的优点,但是我认为现在这种驱动编程的风格越来越像Windows的风格,很不直观和简约,让人理解起来相当的困惑。
牢骚发完了,bus结构还得继续,说说platform_device和platform_driver的匹配吧!一般来说系统上来在init_init_machine的对应函数ap_init中注册一些这个平台的设备,如下:
static struct resource cbp_sdmmc_resource[]=
{
   [0]={
         .start=HWD_MMC_BASE,
         .end=HWD_MMC_BASE+0xff0,
         .flags=IORESOURCE_MEM
       },
   [1]={
         .start=IRQ_SDMMC,
         .end=IRQ_SDMMC,
         .flags=IORESOURCE_IRQ
       },
   [2]={
         .start=IRQ_SDMMC_CD,
         .end=IRQ_SDMMC_CD,
         .flags=IORESOURCE_IRQ
       }
};
struct platform_device cbp_device_sdmmc = {
 .name    = "cbp-sdmmc",
 .id    = -1,
 .num_resources   = ARRAY_SIZE(cbp_sdmmc_resource),
 .resource   = cbp_sdmmc_resource,
        .dev              = {
                .coherent_dma_mask = 0xffffffffUL
        }
};
static struct platform_device * cbp_devices[] __initdata = {
   &cbp_device_sdmmc
};
static void __init ap_init(void)
{
   platform_add_devices(cbp_devices,ARRAY_SIZE(cbp_devices));
}
说明这个平台使用的SD/MMC驱动的名字叫"cbp-sdmmc",然后在驱动中用platform_driver_register声明对应的platform_driver来使用上面声明的平台资源,如下:
static int __init cbpmci_init(void)
{
    return platform_driver_register(&cbpmci_driver);
}
platform_driver和platform_driver的匹配方式有两种:
1)直接根据名字来进行匹配,这种方式是比较常用的方式,比如如下申明cbpmci_driver:
static struct platform_driver cbpmci_driver = {
 .probe = cbpmci_probe,
 .driver = {
  .name = "cbp-sdmmc",
 },
};
2)通过id_talbe来实现,这种实现的最终还是通过名字对应来匹配,但是匹配的名字被列在一个表中,platform_device的name和这个表中的每一个值进行比较,知道找到相同的那一个,如下申明:
 static struct platform_device_id cbpmci_driver_ids[] = {
 {
  .name = "other-sdmmc",
  .driver_data = 0,
 }, 
 {
  .name = "cbp-sdmmc",
  .driver_data = 1,
 }, 
 { }
};
static struct platform_driver cbpmci_driver = {
 .driver = {
  .name = "vtc_sdmmc",
  .owner = THIS_MODULE,
  .pm = &cbpmci_pm_ops,
 },
 .id_table = cbpmci_driver_ids,
 .probe  = cbpmci_probe,
 .remove  = __devexit_p(cbpmci_remove),
 .shutdown = cbpmci_shutdown,
};
当id_table不为空的时候,.driver.name中的名字“vtc_sdmmc”就不管用了,会按照id_table中的内容进行匹配,同时匹配后的会把匹配上的platform_device_id保存在platform_device结构的id_entry中,在probe的时候就可以通过id_entry中的driver_data判断匹配的到底是cbpmci_driver_ids中的哪一组ID
比如上面例子中的"cbp-sdmmc"中匹配的是第二组,这样就可以再Probe的判断到底是什么平台了
if(platform_get_device_id(pdev)->driver_data==1) printk("cgp SD/MMC support\n");
这种匹配方式在三星的SD/MMC中有使用,由于2412和2440的地址是一样的,而2410不一样,所以通过driver_data 是否为1来区分。
static struct platform_device_id s3cmci_driver_ids[] = {
 {
  .name = "s3c2410-sdi",
  .driver_data = 0,
 }, {
  .name = "s3c2412-sdi",
  .driver_data = 1,
 }, {
  .name = "s3c2440-sdi",
  .driver_data = 1,
 },
 { }
};

一下是Linux匹配的源代码,在platform.c中,一看就一目了然了,当然要跟到这步,中间还有好多指针要走:
static int platform_match(struct device *dev, struct device_driver *drv)
{
 struct platform_device *pdev = to_platform_device(dev);
 struct platform_driver *pdrv = to_platform_driver(drv);
 /* match against the id table first */
 if (pdrv->id_table)
  return platform_match_id(pdrv->id_table, pdev) != NULL;
 /* fall-back to driver name match */
 return (strcmp(pdev->name, drv->name) == 0);
}
此外platform_add_devices最终会调用platform_device_register,而platform_device_register和platform_driver_register应该先调用哪一个理论上讲是先调用device然后driver,但其实是无所谓的,最后都会调用device_attach()来probe。

The platform device API

By  Jonathan Corbet
June 21, 2011
In the very early days, Linux users often had to tell the kernel where specific devices were to be found before their systems would work. In the absence of this information, the driver could not know which I/O ports and interrupt line(s) the device was configured to use. Happily, we now live in the days of busses like PCI which have discoverability built into them; any device sitting on a PCI bus can tell the system what sort of device it is and where its resources are. So the kernel can, at boot time, enumerate the devices available and everything Just Works.

Alas, life is not so simple; there are plenty of devices which are still not discoverable by the CPU. In the embedded and system-on-chip world, non-discoverable devices are, if anything, increasing in number. So the kernel still needs to provide ways to be told about the hardware that is actually present. "Platform devices" have long been used in this role in the kernel. This article will describe the interface for platform devices; it is meant as needed background material for a following article on integration with device trees.

Platform drivers

A platform device is represented by struct platform_device, which, like the rest of the relevant declarations, can be found in <linux/platform_device.h>. These devices are deemed to be connected to a virtual "platform bus"; drivers of platform devices must thus register themselves as such with the platform bus code. This registration is done by way of a platform_driver structure:

    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 *);
	struct device_driver driver;
	const struct platform_device_id *id_table;
    };

At a minimum, the probe() and remove() callbacks must be supplied; the other callbacks have to do with power management and should be provided if they are relevant.

The other thing the driver must provide is a way for the bus code to bind actual devices to the driver; there are two mechanisms which can be used for that purpose. The first is the id_table argument; the relevant structure is:

    struct platform_device_id {
	char name[PLATFORM_NAME_SIZE];
	kernel_ulong_t driver_data;
    };

If an ID table is present, the platform bus code will scan through it every time it has to find a driver for a new platform device. If the device's name matches the name in an ID table entry, the device will be given to the driver for management; a pointer to the matching ID table entry will be made available to the driver as well. As it happens, though, most platform drivers do not provide an ID table at all; they simply provide a name for the driver itself in the driver field. As an example, the i2c-gpio driver turns two GPIO lines into an i2c bus; it sets itself up as a platform device with:

    static struct platform_driver i2c_gpio_driver = {
	.driver		= {
		.name	= "i2c-gpio",
		.owner	= THIS_MODULE,
	},
	.probe		= i2c_gpio_probe,
	.remove		= __devexit_p(i2c_gpio_remove),
    };

With this setup, any device identifying itself as "i2c-gpio" will be bound to this driver; no ID table is needed.

Platform drivers make themselves known to the kernel with:

    int platform_driver_register(struct platform_driver *driver);

As soon as this call succeeds, the driver's probe() function can be called with new devices. That function gets as an argument a platform_device pointer describing the device to be instantiated:

    struct platform_device {
	const char	*name;
	int		id;
	struct device	dev;
	u32		num_resources;
	struct resource	*resource;
	const struct platform_device_id	*id_entry;
	/* Others omitted */
    };

The dev structure can be used in contexts where it is needed - the DMA mapping API, for example. If the device was matched using an ID table entry, id_entry will point to the specific entry matched. The resource array can be used to learn where various resources, including memory-mapped I/O registers and interrupt lines, can be found. There are a number of helper functions for getting data out of the resource array; these include:

    struct resource *platform_get_resource(struct platform_device *pdev, 
					   unsigned int type, unsigned int n);
    struct resource *platform_get_resource_byname(struct platform_device *pdev,
					   unsigned int type, const char *name);
    int platform_get_irq(struct platform_device *pdev, unsigned int n);

The "n" parameter says which resource of that type is desired, with zero indicating the first one. Thus, for example, a driver could find its second MMIO region with:

    r = platform_get_resource(pdev, IORESOURCE_MEM, 1);

Assuming the probe() function finds the information it needs, it should verify the device's existence to the extent possible, register the "real" devices associated with the platform device, and return zero.

Platform devices

So now we have a driver for a platform device, but no actual devices yet. As was noted at the beginning, platform devices are inherently not discoverable, so there must be another way to tell the kernel about their existence. That is typically done with the creation of a static platform_device structure providing, at a minimum, a name which is used to find the associated driver. So, for example, a simple (fictional) device might be set up this way:

    static struct resource foomatic_resources[] = {
	{
		.start	= 0x10000000,
		.end	= 0x10001000,
		.flags	= IORESOURCE_MEM,
		.name	= "io-memory"
	},
	{
		.start	= 20,
		.end	= 20,
		.flags	= IORESOURCE_IRQ,
		.name	= "irq",
	}
    };

    static struct platform_device my_foomatic = {
	.name 		= "foomatic",
	.resource	= foomatic_resources,
	.num_resources	= ARRAY_SIZE(foomatic_resources),
    };

These declarations describe a "foomatic" device with a one-page MMIO region starting at 0x10000000 and using IRQ 20. The device is made known to the system with:

    int platform_device_register(struct platform_device *pdev);

Once both a platform device and an associated driver have been registered, the driver's probe() function will be called and the device will be instantiated. Registration of device and driver are usually done in different places and can happen in either order. A call to platform_device_unregister() can be used to remove a platform device.

Platform data

The above information is adequate to instantiate a simple platform device, but many devices are more complex than that. Even the simple i2c-gpio driver described above needs two additional pieces of information: the numbers of the GPIO lines to be used as i2c clock and data lines. The mechanism used to pass this information is called "platform data"; in short, one defines a structure containing the specific information needed and passes it in the platform device's dev.platform_data field.

With the i2c-gpio example, a full configuration looks like this:

    #include <linux/i2c-gpio.h>

    static struct i2c_gpio_platform_data my_i2c_plat_data = {
	.scl_pin	= 100,
	.sda_pin	= 101,
    };

    static struct platform_device my_gpio_i2c = {
	.name		= "i2c-gpio",
	.id		= 0,
	.dev = {
		.platform_data = &my_i2c_plat_data,
	}
    };

When the driver's probe() function is called, it can fetch the platform_data pointer and use it to obtain the rest of the information it needs.

Not everybody in the kernel community is enamored with platform devices; they seem like a bit of a hack used to encode information about specific hardware platforms into the kernel. Additionally, the platform data mechanism lacks any sort of type checking; drivers must simply assume that they have been passed a structure of the expected type. Even so, platform devices are heavily used, and that's unlikely to change, though the means by which they are created and discovered is changing. The way of the future appears to be device trees, which will be described in the following article.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值