3.6platform_device和platform_driver如何让匹配

目录

分析

平台设备

平台驱动 

2.platform_dev和platform_drv的注册过程

注册 platform_device 的过程:

注册 platform_driver 的过程:

3. 如何判断dev和drv匹配成功呢?

设备树匹配方式


分析

主要是在匹配函数里面的支持设备树。

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.dma_configure	= platform_dma_configure,
	.pm		= &platform_dev_pm_ops,
};

平台设备

struct platform_device {
    const char    *name;
    int        id;
    bool        id_auto;
    struct device    dev; //dev中含有一个of_node
    u32        num_resources;
    struct resource    *resource;

    const struct platform_device_id    *id_entry;
    char *driver_override; /* Driver name to force a match */ 

    /* MFD cell pointer */
    struct mfd_cell *mfd_cell;

    /* arch specific additions */
    struct pdev_archdata    archdata;
};

其中有一项成员变量是driver_override

平台驱动 

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;
    bool prevent_deferred_probe;
};

平台总线的左边挂有一个或多个platform_device,另一边挂有一个或多个platform_driver。所谓平台设备就是一个又一个的的platform_device结构体,平台驱动就是一个又一个的platform_driver的结构体。

platform_driver结构体中有一项成员id_table,

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

它还有一个结构成员为driver,

struct device_driver {
    const char        *name;
    struct bus_type        *bus;

    struct module        *owner;
    const char        *mod_name;    /* used for built-in modules */

    bool suppress_bind_attrs;    /* disables bind/unbind via sysfs */
    enum probe_type probe_type;

    const struct of_device_id    *of_match_table;
    const struct acpi_device_id    *acpi_match_table;

    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);
    const struct attribute_group **groups;

    const struct dev_pm_ops *pm;

    struct driver_private *p;
};

在driver中有一项成员of_match_table,它指向一个数组。

struct of_device_id {
    char    name[32];
    char    type[32];
    char    compatible[128];
    const void *data;
};

看下面的一个例子:

struct platform_driver led_drv = {
    .probe = led_probe,
    .remove = led_remove,
    .driver = {
        .name = "myled", 
        .of_match_table = of_match_leds, /能支持哪些来自于dts的platform_device/
    },
    .id_table = led_ids,  //表示它可以支持哪些设备

};

static const struct of_device_id of_match_leds[] = {

    {.compatible = "jz2440_led", .data = NULL},
    {}
};

static const platform_device_id led_ids[] = {
    {
        .name = "jz2440_v2",
        .driver_data = s3c2440_GPF(5),
    }, {
        .name = "jz2440_v3",
        .driver_data = s3c2440_GPF(6),
    },
    {}

};

2.platform_dev和platform_drv的注册过程

drivers/base/platform.c

注册 platform_device 的过程:

platform_device_register
    platform_device_add
        device_add
            bus_add_device
                klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); // 把 platform_device 放入 platform_bus_type的device链表中
            bus_probe_device(dev);
                device_initial_probe
                    __device_attach
                        ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); // // 对于plarform_bus_type下的每一个driver, 调用 __device_attach_driver
                                    __device_attach_driver
                                        ret = driver_match_device(drv, dev);
                                                    return drv->bus->match ? drv->bus->match(dev, drv) : 1;  // 调用platform_bus_type.match
                                        driver_probe_device

注册 platform_driver 的过程:

platform_driver_register
    __platform_driver_register
        drv->driver.probe = platform_drv_probe;
        driver_register
            bus_add_driver
                klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);    // 把 platform_driver 放入 platform_bus_type 的driver链表中
                driver_attach
                    bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);  // 对于plarform_bus_type下的每一个设备, 调用__driver_attach
                        __driver_attach
                            ret = driver_match_device(drv, dev);  // 判断dev和drv是否匹配成功
                                        return drv->bus->match ? drv->bus->match(dev, drv) : 1;  // 调用 platform_bus_type.match
                            driver_probe_device(drv, dev);
                                        really_probe
                                            drv->probe  // platform_drv_probe
                                                platform_drv_probe
                                                    struct platform_driver *drv = to_platform_driver(_dev->driver);
                                                    drv->probe

3. 如何判断dev和drv匹配成功呢?

使用platform_bus_type结构体中的函数platform_match来进行比较:

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);

    /* When driver_override is set, only bind to the matching driver */
    if (pdev->driver_override)//如果平台设备这边设置了driver_override,那么就比较平台设备的driver_override和drv的name
        return !strcmp(pdev->driver_override, drv->name);

    /* Attempt an OF style match first */ //利用设备树进行比较
    if (of_driver_match_device(dev, drv))--->of_match_device(drv->of_match_table, dev)
                                                    of_match_node(matches, dev->of_node)
                                                        __of_match_node(matches, node);
                                                             __of_device_is_compatible(node, matches->compatible,matches->type, matches->name);
                                                                 __of_find_property(device, "compatible", NULL);//of_match_table与dev of_node中的compatible属性进行比较
        return 1;

    /* Then try ACPI style match */  //在ARM系统中几乎用不到
    if (acpi_driver_match_device(dev, drv))
        return 1;

    /* Then try to match against the id table */  
    if (pdrv->id_table)   //如果drv这边存在id_table就用dev那边的名字进行比较
        return platform_match_id(pdrv->id_table, pdev) != NULL;------>strcmp(pdev->name, id->name) == 0

    /* fall-back to driver name match */  //如果实在没有办法,就比较平台设备的名字和drv的名字
    return (strcmp(pdev->name, drv->name) == 0);
}

匹配过程按优先顺序罗列如下:
a. 比较 platform_dev.driver_override 和 platform_driver.drv->name
b. 比较 platform_dev.dev.of_node的compatible属性 和 platform_driver.drv->of_match_table
c. 比较 platform_dev.name 和 platform_driver.id_table
d. 比较 platform_dev.name 和 platform_driver.drv->name

只要a.b.c.d中有一个比较成功,就表明平台设备和平台driver是匹配的。然后就会调用平台driver中的probe函数。

当然除了第一个之外,其它的只要没匹配到,后面的几个匹配还会继续执行的。

设备树匹配方式

/**
 * of_driver_match_device - Tell if a driver's of_match_table matches a device.
 * @drv: the device_driver structure to test
 * @dev: the device structure to match against
 */
static inline int of_driver_match_device(struct device *dev,
					 const struct device_driver *drv)
{
	return of_match_device(drv->of_match_table, dev) != NULL;
}
 
 
/**
 * of_match_device - Tell if a struct device matches an of_device_id list
 * @ids: array of of device match structures to search in
 * @dev: the of device structure to match against
 *
 * Used by a driver to check whether an platform_device present in the
 * system is in its list of supported devices.
 */
const struct of_device_id *of_match_device(const struct of_device_id *matches,
					   const struct device *dev)
{
	if ((!matches) || (!dev->of_node))
		return NULL;
	return of_match_node(matches, dev->of_node);
}
 
 
 
/**
 * of_match_node - Tell if a device_node has a matching of_match structure
 *	@matches:	array of of device match structures to search in
 *	@node:		the of device structure to match against
 *
 *	Low level utility function used by device matching.
 */
const struct of_device_id *of_match_node(const struct of_device_id *matches,
					 const struct device_node *node)
{
	const struct of_device_id *match;
	unsigned long flags;
 
	raw_spin_lock_irqsave(&devtree_lock, flags);
	match = __of_match_node(matches, node);
	raw_spin_unlock_irqrestore(&devtree_lock, flags);
	return match;
}
 
static
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
					   const struct device_node *node)
{
	const struct of_device_id *best_match = NULL;
	int score, best_score = 0;
 
	if (!matches)
		return NULL;
 
    //根据匹配的分数,选择最高分的是最佳匹配
	for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
		score = __of_device_is_compatible(node, matches->compatible,
						  matches->type, matches->name);
		if (score > best_score) {
			best_match = matches;
			best_score = score;
		}
	}
 
	return best_match;
}
 
 
 
/**
 * __of_device_is_compatible() - Check if the node matches given constraints
 * @device: pointer to node
 * @compat: required compatible string, NULL or "" for any match
 * @type: required device_type value, NULL or "" for any match
 * @name: required node name, NULL or "" for any match
 *
 * Checks if the given @compat, @type and @name strings match the
 * properties of the given @device. A constraints can be skipped by
 * passing NULL or an empty string as the constraint.
 *
 * Returns 0 for no match, and a positive integer on match. The return
 * value is a relative score with larger values indicating better
 * matches. The score is weighted for the most specific compatible value
 * to get the highest score. Matching type is next, followed by matching
 * name. Practically speaking, this results in the following priority
 * order for matches:
 *
 * 1. specific compatible && type && name
 * 2. specific compatible && type
 * 3. specific compatible && name
 * 4. specific compatible
 * 5. general compatible && type && name
 * 6. general compatible && type
 * 7. general compatible && name
 * 8. general compatible
 * 9. type && name
 * 10. type
 * 11. name
 */
static int __of_device_is_compatible(const struct device_node *device,
				     const char *compat, const char *type, const char *name)
{
	struct property *prop;
	const char *cp;
	int index = 0, score = 0;
 
	/* Compatible match has highest priority */
    /* compatible 匹配有限即最高,匹配到了,则给的分数相对较高 */
	if (compat && compat[0]) {
		prop = __of_find_property(device, "compatible", NULL);
		for (cp = of_prop_next_string(prop, NULL); cp;
		     cp = of_prop_next_string(prop, cp), index++) {
			if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
				score = INT_MAX/2 - (index << 2);
				break;
			}
		}
		if (!score)
			return 0;
	}
 
	/* Matching type is better than matching name,类型匹配会加2分 */
	if (type && type[0]) {
		if (!device->type || of_node_cmp(type, device->type))
			return 0;
		score += 2;
	}
 
	/* Matching name is a bit better than not,最后在确认名字匹配加1分 */
	if (name && name[0]) {
		if (!device->name || of_node_cmp(name, device->name))
			return 0;
		score++;
	}
 
	return score;
}

看这句 prop = __of_find_property(device, "compatible", NULL);
可以发先追溯到底,是利用"compatible"来匹配的,即设备树加载之后,内核会自动把设备树节点转换成 platform_device这种格式,同时把名字放到of_node这个地方。
 

id_tabel是根据id_table表中的每一个和设备名字进行匹配,这样一个驱动可以支持多个名称的设备。

static const struct platform_device_id *platform_match_id(
			const struct platform_device_id *id,
			struct platform_device *pdev)
{
	while (id->name[0]) {
		if (strcmp(pdev->name, id->name) == 0) {
			pdev->id_entry = id;
			return id;
		}
		id++;
	}
	return NULL;
}
 

举例:

1.ti的omap8250驱动可以支持好多个型号的芯片,其它芯片只要这个的驱动基础上做很小的改动就可通用。

其中的改动点,使用of_device_id 的date表示的。

static const struct platform_device_id *platform_match_id(
			const struct platform_device_id *id,
			struct platform_device *pdev)
{
	while (id->name[0]) {
		if (strcmp(pdev->name, id->name) == 0) {
			pdev->id_entry = id;
			return id;
		}
		id++;
	}
	return NULL;
}
 

举例:

1.ti的omap8250驱动可以支持好多个型号的芯片,其它芯片只要这个的驱动基础上做很小的改动就可通用。

其中的改动点,使用of_device_id 的date表示的。

 
/*
 * Struct used for matching a device
 */
struct of_device_id {
	char	name[32];
	char	type[32];
	char	compatible[128];
	const void *data;
};
 
 
 
static const u8 omap4_habit = UART_ERRATA_CLOCK_DISABLE;
static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE;
static const u8 dra742_habit = UART_ERRATA_CLOCK_DISABLE;
 
 
static const struct of_device_id omap8250_dt_ids[] = {
	{ .compatible = "ti,am654-uart" },
	{ .compatible = "ti,omap2-uart" },
	{ .compatible = "ti,omap3-uart" },
	{ .compatible = "ti,omap4-uart", .data = &omap4_habit, },
	{ .compatible = "ti,am3352-uart", .data = &am3352_habit, },
	{ .compatible = "ti,am4372-uart", .data = &am3352_habit, },
	{ .compatible = "ti,dra742-uart", .data = &dra742_habit, },
	{},
};
 
 
static struct platform_driver omap8250_platform_driver = {
	.driver = {
		.name		= "omap8250",
		.pm		= &omap8250_dev_pm_ops,
		.of_match_table = omap8250_dt_ids,
	},
	.probe			= omap8250_probe,
	.remove			= omap8250_remove,
};

2.ad5380有好多中类型,芯片使用完全兼容。可能就是版本差异。驱动可以完全兼容

 
static const struct spi_device_id ad5380_spi_ids[] = {
	{ "ad5380-3", ID_AD5380_3 },
	{ "ad5380-5", ID_AD5380_5 },
	{ "ad5381-3", ID_AD5381_3 },
	{ "ad5381-5", ID_AD5381_5 },
	{ "ad5382-3", ID_AD5382_3 },
	{ "ad5382-5", ID_AD5382_5 },
	{ "ad5383-3", ID_AD5383_3 },
	{ "ad5383-5", ID_AD5383_5 },
	{ "ad5384-3", ID_AD5380_3 },
	{ "ad5384-5", ID_AD5380_5 },
	{ "ad5390-3", ID_AD5390_3 },
	{ "ad5390-5", ID_AD5390_5 },
	{ "ad5391-3", ID_AD5391_3 },
	{ "ad5391-5", ID_AD5391_5 },
	{ "ad5392-3", ID_AD5392_3 },
	{ "ad5392-5", ID_AD5392_5 },
	{ }
};
MODULE_DEVICE_TABLE(spi, ad5380_spi_ids);
 
static struct spi_driver ad5380_spi_driver = {
	.driver = {
		   .name = "ad5380",
	},
	.probe = ad5380_spi_probe,
	.remove = ad5380_spi_remove,
	.id_table = ad5380_spi_ids,
};

最后总结一下有了设备树前后,设备驱动怎么写

有了设备树这样在dts中编写

samsung-beep{
         compatible = "samsung,beep";
         reg = <0x114000a0 0x4 0x139D0000 0x14>;
};

a -- samsung-beep 为节点名,符合咱们前面提到的节点命名规范;

      我们通过名字可以知道,该节点描述的设备是beep, 设备名是samsung-beep;

b -- compatible = "samsung,beep"; compatible 属性, 即一个字符串;

      前面提到,所有新的compatible值都应使用制造商的前缀,这里是samsung

c -- reg = <0x114000a0 0x4 0x139D0000 0x14>;

       reg属性来将地址信息编码进设备树,表示该设备的地址范围;这里是我们用到的寄存器及偏移量;

 

之前在mach-xxx.c中编写
 

static struct  resource beep_resource[] =
{
    [0] = {
        .start = 0x114000a0,
        .end = 0x114000a0+0x4,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = 0x139D0000,
        .end = 0x139D0000+0x14,
        .flags = IORESOURCE_MEM,
    },
};
static struct platform_device hello_device=
{
    .name = "beep",
    .id = -1,
    .dev.release = hello_release,
    .num_resources = ARRAY_SIZE(beep_resource ),
    .resource = beep_resource,
};

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值