Linux 总线、设备、驱动模型的探究

那么在 GITCHAT 的驱动里需要定义 GITCHAT的基地址、中断号等信息。假设 GITCHAT 的地址为0x0001,中断号是 2,那么:

#define GITCHAT_BASE 0x0001
#define GITCHAT_INTERRUPT 2

int gitchat_send()
{
    writel(GITCHAT_BASE + REG, 1);
    ...
}

int gitchat_init()
{
    request_init(GITCHAT_INTERRUPT, ...);
    ...
}

比如一个板子上有一个 GITCHAT,首先向总线注册:

static struct resource gitchat_resource[] = {
    {
            .start = ...,
            .end = ...,
            .flags = IORESOURCE_MEM
    }...
};

static struct platform_device gitchat_device = {
    .name = "gitchat";
    .id = 0;
    .num_resources = ARRAY_SIZE(gitchat_resource);
    .resource = gitchat_resource,
};
static struct platform_device *ip0x_device __initdata = {
    &gitchat_device,
    ...
};
static ini __init ip0x_init(void)
{
    platform_add_devices(ip0x_device, ARRAY_SIZE(ip0x_device));
}

下面看下驱动注册总线的代码示例:

static int gitchat_probe(struct platform_device *pdev)
{
    ...
    db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
    ...
}

总线里有很多匹配方式,比如:

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) 
                return !strcmp(pdev->driver_override, drv->name);

        /* Attempt an OF style match first */
        if (of_driver_match_device(dev, drv))
                return 1;

        /* Then try ACPI style match */
        if (acpi_driver_match_device(dev, drv))
                return 1;

        /* Then try to match against the id table */
        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 总线下的设备和驱动是通过名字进行匹配的,先去匹配 platform_driver中的 id_table 表中的各个名字与 platform_device->name 名字是否相同,如果相同则匹配。
__platform_driver_register

/**
 * __platform_driver_register - register a driver for platform-level devices
 * @drv: platform driver structure
 * @owner: owning module/driver
 */
int __platform_driver_register(struct platform_driver *drv,
    struct module *owner)
{
 drv->driver.owner = owner;
 drv->driver.bus = &platform_bus_type;
 drv->driver.probe = platform_drv_probe;
 drv->driver.remove = platform_drv_remove;
 drv->driver.shutdown = platform_drv_shutdown;
 return driver_register(&drv->driver);
}

platform_drv_probe

static int platform_drv_probe(struct device *_dev)
{
 struct platform_driver *drv = to_platform_driver(_dev->driver);
 struct platform_device *dev = to_platform_device(_dev);
 int ret;
 ret = of_clk_set_defaults(_dev->of_node, false);
 if (ret < 0)
  return ret;
 ret = dev_pm_domain_attach(_dev, true);
 if (ret != -EPROBE_DEFER) {
  if (drv->probe) {
   ret = drv->probe(dev);
   if (ret)
    dev_pm_domain_detach(_dev, true);
  } else {
   /* don't fail if just dev_pm_domain_attach failed */
   ret = 0;
  }
 }
 if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
  dev_warn(_dev, "probe deferral not supported\n");
  ret = -ENXIO;
 }
 return ret;
}

platform_get_resource

/**
 * platform_get_resource - get a resource for a device
 * @dev: platform device
 * @type: resource type
 * @num: resource index
 */
struct resource *platform_get_resource(struct platform_device *dev,
           unsigned int type, unsigned int num)
{
 int i;
 for (i = 0; i < dev->num_resources; i++) {
  struct resource *r = &dev->resource[i];
  if (type == resource_type(r) && num-- == 0)
   return r;
 }
 return NULL;
}

smsc_lan9217_device

static struct platform_device smsc_lan9217_device = {
 .name = "smsc911x",
 .id = -1,
 .dev = {
  .platform_data = &smsc911x_config,
 },
 .num_resources = ARRAY_SIZE(smsc911x_resources),
 .resource = smsc911x_resources,
};

smsc911x_resources

static struct resource smsc911x_resources[] = {
 [0] = {
  .start  = 0x28000000,
  .end  = 0x280000ff,
  .flags  = IORESOURCE_MEM,
 },
 [1] = {
  .start  = 65,
  .end  = 65,
  .flags  = IORESOURCE_IRQ,
 },
};
__platform_driver_register
 drv->driver.probe = platform_drv_probe;
  ret = drv->probe(dev);
  .probe   = gitchat_probe
    db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    for (i = 0; i < dev->num_resources; i++) 
     struct resource *r = &dev->resource[i];
static struct platform_device smsc_lan9217_device = {
 .name = "smsc911x",
 .id = -1,
 .dev = {
  .platform_data = &smsc911x_config,
 },
 .num_resources = ARRAY_SIZE(smsc911x_resources),
 .resource = smsc911x_resources,
};
static struct resource smsc911x_resources[] = {
 [0] = {
  .start  = 0x28000000,
  .end  = 0x280000ff,
  .flags  = IORESOURCE_MEM,
 },
 [1] = {
  .start  = 65,
  .end  = 65,
  .flags  = IORESOURCE_IRQ,
 },
};

platform_match_id

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

a. 注册 platform_driver 的过程:

platform_driver_register
    __platform_driver_register
        drv->driver.probe = platform_drv_probe;
        driver_register
            bus_add_driver

// 把 platform_driver放入 platform_bus_typedriver链表中

               klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);    // 把 platform_driver 放入 platform_bus_type 的driver链表中

// 对于plarform_bus_type下的每一个设备, 调用__driver_attach

                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

b. 注册 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_bus_type.match, 即platform_match,
匹配过程按优先顺序罗列如下:

a. 比较platform_dev.driver_overrideplatform_driver.drv->name
b. 比较 platform_dev.dev.of_nodecompatible属性 和platform_driver.drv->of_match_table
c. 比较 platform_dev.nameplatform_driver.id_table
d. 比较platform_dev.nameplatform_driver.drv->name

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值