Chinaunix首页 | 论坛 | 博客
  • 博客访问: 35152
  • 博文数量: 42
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 70
  • 用 户 组: 普通用户
  • 注册时间: 2015-12-06 20:19
文章分类

全部博文(42)

文章存档

2016年(12)

2015年(30)

我的朋友

分类: LINUX

2015-12-06 20:23:41

原文地址:I2C设备与驱动的关联(2) 作者:xn_liu

2.6__device_release_driver()函数
/**
 *   device_release_driver - manually detach device from driver.
 *    @dev:   device.
 *
 *   Manually detach device from driver.
 *
 *   __device_release_driver() must be called with @dev->sem held.
 *   When called for a USB interface, @dev->parent->sem must be held
 *  as well.
 */

static void __device_release_driver(struct device * dev)
{
    struct device_driver * drv;

  drv = dev->driver;
    if (drv) {
       get_driver(drv);
     driver_sysfs_remove(dev);
        sysfs_remove_link(&dev->kobj, "driver");
      klist_remove(&dev->knode_driver);

     if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->bus_notifier,
                           BUS_NOTIFY_UNBIND_DRIVER,
                            dev);

       if (dev->bus && dev->bus->remove)
           dev->bus->remove(dev);
     else if (drv->remove)
        drv->remove(dev);
     devres_release_all(dev);
     dev->driver = NULL;
       put_driver(drv);
 }
}

2.7platform_drv_remove()函数
static int platform_drv_remove(struct device *_dev)
{
 struct platform_driver *drv = to_platform_driver(_dev->driver);
   struct platform_device *dev = to_platform_device(_dev);

  returndrv->remove(dev);
}

在此函数中,回调了我们在i2c-at91.c文件中实现的移除函数at91_i2c_remove(),至此,平台驱动的卸载过程结束。

在注册和卸载过程中,Linux采用了一些变量来保存相关的信息,比如引用计数、通知链等,感兴趣的人员,可以详细的阅读此部分内容。

设备注册过程

3.1at91_add_device_i2c()函数
在文件arch/arm/mach-at91/at91sam9263_devices.c中,对I2C设备进行了定义。
/* --------------------------------------------------------------------
 *  TWI (i2c)
 * -------------------------------------------------------------------- */

#if defined(CONFIG_I2C_AT91) || defined(CONFIG_I2C_AT91_MODULE)

static struct resource twi_resources[] = {
 [0] = {
      .start  = AT91SAM9263_BASE_TWI,
      .end    = AT91SAM9263_BASE_TWI + SZ_16K - 1,
     .flags  = IORESOURCE_MEM,
    },
   [1] = {
      .start  = AT91SAM9263_ID_TWI,
        .end    = AT91SAM9263_ID_TWI,
        .flags  = IORESOURCE_IRQ,
    },
};

static struct platform_deviceat91sam9263_twi_device = {
   //此名字与驱动信息中的名字"at91_i2c"是一致的,在match()函数调用中,会返回1
  .name       = "at91_i2c",
    .id     = -1,
    .resource   = twi_resources,
 .num_resources  = ARRAY_SIZE(twi_resources),
};

void __init at91_add_device_i2c(void)
{
    /* pins used for TWI interface */
    at91_set_A_periph(AT91_PIN_PB4, 0);     /* TWD */
    at91_set_multi_drive(AT91_PIN_PB4, 1);

   at91_set_A_periph(AT91_PIN_PB5, 0);     /* TWCK */
   at91_set_multi_drive(AT91_PIN_PB5, 1);

   //对设备进行注册
    platform_device_register(&at91sam9263_twi_device);
}
#else
void __init at91_add_device_i2c(void) {}
#endif
3.2platform.c文件
在文件drivers/base/platform.c中,实现了下面的函数。
3.2.1platform_device_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);
    returnplatform_device_add(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_register);
3.2.2platform_device_add()函数
/**
 *   platform_device_add - add a platform device to device hierarchy
 *    @pdev:  platform device we're adding
 *
 *  This is part 2 of platform_device_register(), though may be called
 * separately _iff_ pdev was allocated by platform_device_alloc().
 */
intplatform_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;

   if (pdev->id != -1)
       snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);
 else
     strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);

    for (i = 0; i < pdev->num_resources; i++) {
        struct resource *p, *r = &pdev->resource[i];

      if (r->name == NULL)
          r->name = pdev->dev.bus_id;

        p = r->parent;
        if (!p) {
            if (r->flags & IORESOURCE_MEM)
                p = &iomem_resource;
         else if (r->flags & IORESOURCE_IO)
                p = &ioport_resource;
        }

        if (p && insert_resource(p, r)) {
            printk(KERN_ERR
                 "%s: failed to claim resource %d\n",
                 pdev->dev.bus_id, i);
          ret = -EBUSY;
            goto failed;
     }
    }

    pr_debug("Registering platform device '%s'. Parent at %s\n",
      pdev->dev.bus_id, pdev->dev.parent->bus_id);

   ret =device_add(&pdev->dev);
 if (ret == 0)
        return ret;

 failed:
   while (--i >= 0)
      if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
         release_resource(&pdev->resource[i]);
 return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);

3.3device_add()函数
在文件drivers/base/core.c中实现了device_add()函数。
/**
 *  device_add - add device to device hierarchy.
 *   @dev:   device.
 *
 *   This is part 2 of device_register(), though may be called
 *  separately _iff_ device_initialize() has been called separately.
 *
 *  This adds it to the kobject hierarchy via kobject_add(), adds it
 *   to the global and sibling lists for the device, then
 *   adds it to the other relevant subsystems of the driver model.
 */
intdevice_add(struct device *dev)
{
 struct device *parent = NULL;
    char *class_name = NULL;
 struct class_interface *class_intf;
  int error = -EINVAL;

 dev = get_device(dev);
   if (!dev || !strlen(dev->bus_id))
     goto Error;

  pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);

    parent = get_device(dev->parent);

 error = setup_parent(dev, parent);
   if (error)
       goto Error;

  /* first, register with generic layer. */
    kobject_set_name(&dev->kobj, "%s", dev->bus_id);
   error = kobject_add(&dev->kobj);
  if (error)
       goto Error;

  /* notify platform of device entry */
    if (platform_notify)
     platform_notify(dev);

    /* notify clients of device entry (new way) */
   if (dev->bus)
     blocking_notifier_call_chain(&dev->bus->bus_notifier,
                       BUS_NOTIFY_ADD_DEVICE, dev);

    dev->uevent_attr.attr.name = "uevent";
    dev->uevent_attr.attr.mode = S_IWUSR;
 if (dev->driver)
      dev->uevent_attr.attr.owner = dev->driver->owner;
   dev->uevent_attr.store = store_uevent;
    error = device_create_file(dev, &dev->uevent_attr);
   if (error)
       goto attrError;

  if (MAJOR(dev->devt)) {
       struct device_attribute *attr;
       attr = kzalloc(sizeof(*attr), GFP_KERNEL);
       if (!attr) {
         error = -ENOMEM;
         goto ueventattrError;
        }
        attr->attr.name = "dev";
      attr->attr.mode = S_IRUGO;
        if (dev->driver)
          attr->attr.owner = dev->driver->owner;
      attr->show = show_dev;
        error = device_create_file(dev, attr);
       if (error) {
         kfree(attr);
         goto ueventattrError;
        }

        dev->devt_attr = attr;
    }

    if (dev->class) {
     sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj,
                  "subsystem");
      /* If this is not a "fake" compatible device, then create the
         * symlink from the class to the device. */
      if (dev->kobj.parent != &dev->class->subsys.kset.kobj)
          sysfs_create_link(&dev->class->subsys.kset.kobj,
                     &dev->kobj, dev->bus_id);
        if (parent) {
            sysfs_create_link(&dev->kobj, &dev->parent->kobj,
                           "device");
#ifdef CONFIG_SYSFS_DEPRECATED
          class_name = make_class_name(dev->class->name,
                         &dev->kobj);
          if (class_name)
              sysfs_create_link(&dev->parent->kobj,
                        &dev->kobj, class_name);
#endif
       }
    }

    if ((error = device_add_attrs(dev)))
     goto AttrsError;
 if ((error = device_add_groups(dev)))
        goto GroupError;
 if ((error = device_pm_add(dev)))
        goto PMError;
    if ((error = bus_add_device(dev)))
       goto BusError;
   if (!dev->uevent_suppress)
        kobject_uevent(&dev->kobj, KOBJ_ADD);
 if ((error =bus_attach_device(dev)))
        goto AttachError;
    if (parent)
      klist_add_tail(&dev->knode_parent, &parent->klist_children);

   if (dev->class) {
     down(&dev->class->sem);
        /* tie the class to the device */
        list_add_tail(&dev->node, &dev->class->devices);

        /* notify any interfaces that the device is here */
      list_for_each_entry(class_intf, &dev->class->interfaces, node)
         if (class_intf->add_dev)
              class_intf->add_dev(dev, class_intf);
     up(&dev->class->sem);
  }
 Done:
   kfree(class_name);
   put_device(dev);
 return error;
 AttachError:
    bus_remove_device(dev);
 BusError:
 device_pm_remove(dev);
 PMError:
   if (dev->bus)
     blocking_notifier_call_chain(&dev->bus->bus_notifier,
                       BUS_NOTIFY_DEL_DEVICE, dev);
    device_remove_groups(dev);
 GroupError:
    device_remove_attrs(dev);
 AttrsError:
 if (dev->devt_attr) {
     device_remove_file(dev, dev->devt_attr);
      kfree(dev->devt_attr);
    }

    if (dev->class) {
     sysfs_remove_link(&dev->kobj, "subsystem");
       /* If this is not a "fake" compatible device, remove the
      * symlink from the class to the device. */
      if (dev->kobj.parent != &dev->class->subsys.kset.kobj)
          sysfs_remove_link(&dev->class->subsys.kset.kobj,
                     dev->bus_id);
       if (parent) {
#ifdef CONFIG_SYSFS_DEPRECATED
           char *class_name = make_class_name(dev->class->name,
                              &dev->kobj);
           if (class_name)
              sysfs_remove_link(&dev->parent->kobj,
                        class_name);
           kfree(class_name);
#endif
          sysfs_remove_link(&dev->kobj, "device");
      }

        down(&dev->class->sem);
        /* notify any interfaces that the device is now gone */
      list_for_each_entry(class_intf, &dev->class->interfaces, node)
         if (class_intf->remove_dev)
               class_intf->remove_dev(dev, class_intf);
      /* remove the device from the class list */
      list_del_init(&dev->node);
        up(&dev->class->sem);
  }
 ueventattrError:
    device_remove_file(dev, &dev->uevent_attr);
 attrError:
 kobject_uevent(&dev->kobj, KOBJ_REMOVE);
  kobject_del(&dev->kobj);
 Error:
    if (parent)
      put_device(parent);
  goto Done;
}
3.4bus_attach_device()函数
在文件drivers/base/bus.c中实现了bus_attach_device()函数。

/**
 * bus_attach_device - add device to bus
 *   @dev:   device tried to attach to a driver
 *
 *  - Add device to bus's list of devices.
 *  - Try to attach to driver.
 */
int bus_attach_device(struct device * dev)
{
    struct bus_type *bus = dev->bus;
   int ret = 0;

  if (bus) {
        dev->is_registered = 1;
        ret =device_attach(dev);
     if (ret >= 0) {
//将设备信息存储在bus结构中。在驱动注册后,利用bus_for_each_dev()函数可以得到所有设备的
//信息,完成驱动与设备之间的绑定。          
           klist_add_tail(&dev->knode_bus, &bus->klist_devices);
       ret = 0;
     } else
            dev->is_registered = 0;
    }
 return ret;
}
3.5 dd.c文件
在文件drivers/base/dd.c中实现了device_attach()函数。
3.5.1device_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 device was found; error code otherwise.
 *
 *  When called for a USB interface, @dev->parent->sem must be held.
 */
int device_attach(struct device * dev)
{
    int ret = 0;
 
  down(&dev->sem);
  if (dev->driver) {
        ret =device_bind_driver(dev);
       if (ret == 0)
            ret = 1;
 } else
       ret =bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
 up(&dev->sem);
    return ret;
}

static int __device_attach(struct device_driver * drv, void * data)
{
    struct device * dev = data;
  //在此之后,利用驱动信息来探测设备,参考第一部分的介绍
 return driver_probe_device(drv, dev);
}
3.5.2device_bind_driver()函数
/**
 *   device_bind_driver - bind a driver to one device.
 *  @dev:   device.
 *
 *   Allow manual attachment of a driver to a device.
 *   Caller must have already set @dev->driver.
 *
 * Note that this does not modify the bus reference count
 * nor take the bus's rwsem. Please verify those are accounted
 *    for before calling this. (It is ok to call with no other effort
 *    from a driver's probe() method.)
 *
 *  This function must be called with @dev->sem held.
 */
int device_bind_driver(struct device *dev)
{
  int ret;

 ret = driver_sysfs_add(dev);
 if (!ret)
        driver_bound(dev); //通知链
 return ret;
}

到此,设备与驱动的两种绑定方式:在设备注册时进行绑定及在驱动注册时进行绑定就介绍完了。

阅读(186) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~