`
rhj485hm
  • 浏览: 13891 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

linux设备模型深探(2)【转】

阅读更多

linux设备模型深探(2)【转】
2011年12月07日
  这段代码中比较繁锁的就是bus_type对应目录下的属性文件建立,为了直观的说明,将属性文件的建立统一放到一起分析
  从上面的代码中可以看,创建属性文件对应的属性分别为:
  bus_attr_uevent bus_attr_drivers_probe, bus_attr_drivers_autoprobe
  分别定义如下:
  static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
  static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
  static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
  show_drivers_autoprobe, store_drivers_autoprobe);
  BUS_ATTR定义如下:
  #define BUS_ATTR(_name, _mode, _show, _store)  \
  struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
  #define __ATTR(_name,_mode,_show,_store) { \
  .attr = {.name = __stringify(_name), .mode = _mode },   \
  .show    = _show,                    \
  .store   = _store,                   \
  }
  由此可见.上面这三个属性对应的名称为别为uevent, drivers_probe, drivers_autoprobe.也就是说,会在bus_types目录下生成三个文件,分别为uevent,probe,autoprobe.
  根据之前的分析,我们知道在sysfs文件系统中,对普通属性文件的读写都会回溯到kobject->ktype->sysfs_ops中.在这里,注意到有:
  priv->subsys.kobj.kset = bus_kset;
  priv->subsys.kobj.ktype = &bus_ktype;
  显然,读写操作就回溯到了bus_ktype中.定义如下:
  static struct kobj_type bus_ktype = {
  .sysfs_ops    = &bus_sysfs_ops,
  };
  static struct sysfs_ops bus_sysfs_ops = {
  .show    = bus_attr_show,
  .store   = bus_attr_store,
  };
  Show和store函数对应的代码为:
  static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
  char *buf)
  {
  struct bus_attribute *bus_attr = to_bus_attr(attr);
  struct bus_type_private *bus_priv = to_bus(kobj);
  ssize_t ret = 0;
  if (bus_attr->show)
  ret = bus_attr->show(bus_priv->bus, buf);
  return ret;
  }
  static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
  const char *buf, size_t count)
  {
  struct bus_attribute *bus_attr = to_bus_attr(attr);
  struct bus_type_private *bus_priv = to_bus(kobj);
  ssize_t ret = 0;
  if (bus_attr->store)
  ret = bus_attr->store(bus_priv->bus, buf, count);
  return ret;
  }
  从代码可以看出.读写操作又会回溯到bus_attribute中的show和store中.在自定义结构里嵌入struct attribute,.然后再操作回溯到自定义结构中,这是一种比较高明的架构设计手法.
  闲言少叙.我们对应看一下上面三个文件对应的最终操作:
  Uevent对应的读写操作为:NULL, bus_uevent_store.对于这个文件没有读操作,只有写操作.用cat 命令去查看这个文件的时候,可能会返回”设备不存在”的错误.bus_uevent_store()代码如下:
  static ssize_t bus_uevent_store(struct bus_type *bus,
  const char *buf, size_t count)
  {
  enum kobject_action action;
  if (kobject_action_type(buf, count, &action) == 0)
  kobject_uevent(&bus->p->subsys.kobj, action);
  return count;
  }
  从这里可以看到,可以在用户空间控制事件的发生,如echo add > event就会产生一个add的事件,
  Probe文件对应的读写操作为:NULL store_drivers_probe.
  store_drivers_probe()这个函数的代码涉及到struct device.等分析完struct device可以自行回过来看下这个函数的实现.实际上,这个函数是将用户输和的设备名称对应的设备与驱动匹配一次.
  Autoprobe文件对应的读写操作为show_drivers_autoprobe, store_drivers_autoprobe.对应读的代码为:
  static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
  {
  return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
  }
  它将总线对应的drivers_autoprobe的值输出到用户空间,这个值为1时,自动将驱动与设备进行匹配.否则,反之.
  写操作的代码如下:
  static ssize_t store_drivers_autoprobe(struct bus_type *bus,
  const char *buf, size_t count)
  {
  if (buf[0] == '0')
  bus->p->drivers_autoprobe = 0;
  else
  bus->p->drivers_autoprobe = 1;
  return count;
  }
  写操作就会改变bus->p->drivers_autoprobe的值.
  就这样,通过sysfs就可以控制总线是否要进行自动匹配了.
  从这里也可以看出.内核开发者的思维是何等的灵活.
  我们从sysfs中找个例子来印证一下:
  Cd  / sys/bus/usb
  用ls命令查看:
  devices  drivers  drivers_autoprobe  drivers_probe  uevent
  与上面分析的相吻合
  设备的注册接口为: device_register().
  int device_register(struct device *dev)
  {
  device_initialize(dev);
  return device_add(dev);
  }
  Device_initialize()中有几个很重要的操作,如下:
  void device_initialize(struct device *dev)
  {
  dev->kobj.kset = devices_kset;
  kobject_init(&dev->kobj, &device_ktype);
  klist_init(&dev->klist_children, klist_children_get,
  klist_children_put);
  INIT_LIST_HEAD(&dev->dma_pools);
  INIT_LIST_HEAD(&dev->node);
  init_MUTEX(&dev->sem);
  spin_lock_init(&dev->devres_lock);
  INIT_LIST_HEAD(&dev->devres_head);
  device_init_wakeup(dev, 0);
  set_dev_node(dev, -1);
  }
  在这里,它为device的内嵌kobject指定了ktype和kset.device_kset的值如下:
  devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
  即对应sysfs中的/sys/devices
  device_ktype 中对属性的读写操作同bus中的类似,被回溯到了struct device_attribute中的show 和store.
  接着往下看device_add()的实现.这个函数比较长,分段分析如下:
  int device_add(struct device *dev)
  {
  struct device *parent = NULL;
  struct class_interface *class_intf;
  int error;
  dev = get_device(dev);
  if (!dev || !strlen(dev->bus_id)) {
  error = -EINVAL;
  goto Done;
  }
  pr_debug("device: '%s': %s\n", dev->bus_id, __FUNCTION__);
  parent = get_device(dev->parent);
  setup_parent(dev, parent);
  /* first, register with generic layer. */
  error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
  if (error)
  goto Error;
  如果注册device的时候,没有指定父结点,在kobject_add将会在/sys/device/下建立相同名称的目录
  /* 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->p->bus_notifi er,
  BUS_NOTIFY_ADD_DEVICE, dev);
  忽略notify部份,这部份不会影响本函数的流程
  error = device_create_file(dev, &uevent_attr);
  if (error)
  goto attrError;
  if (MAJOR(dev->devt)) {
  error = device_create_file(dev, &devt_attr);
  if (error)
  goto ueventattrError;
  }
  建立属性为uevent_attr的属性文件,如果device中指定了设备号,则建立属性为devt_attr的属性文件
  error = device_add_class_symlinks(dev);
  if (error)
  goto SymlinkError;
  error = device_add_attrs(dev);
  if (error)
  goto AttrsError;
  error = dpm_sysfs_add(dev);
  if (error)
  goto PMError;
  device_pm_add(dev);
  在这里,不打算讨论class的部份,dpm pm是选择编译部份,不讨论. device_add_attrs中涉及到了group的部分,暂不讨论
  error = bus_add_device(dev);
  if (error)
  goto BusError;
  kobject_uevent(&dev->kobj, KOBJ_ADD);
  bus_attach_device(dev);
  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);
  }
  bus_add_device()会在对应总线代表目录的device目录下创建几个到device的链接.然后产生一个add事件,再调用bus_attach_device()去匹配已经注册到总线的驱动程序.全部做完之后,将设备挂到父结点的子链表.
  Done:
  put_device(dev);
  return error;
  BusError:
  device_pm_remove(dev);
  PMError:
  if (dev->bus)
  blocking_notifier_call_chain(&dev->bus->p->bus_notifi er,
  BUS_NOTIFY_DEL_DEVICE, dev);
  device_remove_attrs(dev);
  AttrsError:
  device_remove_class_symlinks(dev);
  SymlinkError:
  if (MAJOR(dev->devt))
  device_remove_file(dev, &devt_attr);
  ueventattrError:
  device_remove_file(dev, &uevent_attr);
  attrError:
  kobject_uevent(&dev->kobj, KOBJ_REMOVE);
  kobject_del(&dev->kobj);
  Error:
  cleanup_device_parent(dev);
  if (parent)
  put_device(parent);
  goto Done;
  }
  出错处理部份.
  bus_attach_device()是一个很重要的函数。它将设备自动与挂在总线上面的驱动进行匹配。代码如下:
  void bus_attach_device(struct device *dev)
  {
  struct bus_type *bus = dev->bus;
  int ret = 0;
  if (bus) {
  dev->is_registered = 1;
  if (bus->p->drivers_autoprobe)
  ret = device_attach(dev);
  WARN_ON(ret = 0)
  klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);
  else
  dev->is_registered = 0;
  }
  }
  从上面的代码我们可以看出。只有在bus->p->drivers_autoprobe为1的情况下,才会去自己匹配。这也就是bus目录下的drivers_probe 文件的作用.然后,将设备挂到总线的设备链表。
  Device_attach()代码如下:
  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 {
  dev->driver = NULL;
  ret = 0;
  }
  } else {
  ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
  }
  up(&dev->sem);
  return ret;
  }
  对于设备自己已经指定驱动的情况,只需要将其直接和驱动绑定即可。如果没有指定驱动。就匹配总线之上的驱动。这是在bus_for_each_drv(dev->bus, NULL, dev, __device_attach);完成的。代码如下:
  int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
  void *data, int (*fn)(struct device_driver *, void *))
  {
  struct klist_iter i;
  struct device_driver *drv;
  int error = 0;
  if (!bus)
  return -EINVAL;
  klist_iter_init_node(&bus->p->klist_drivers, &i,
  start ? &start->p->knode_bus : NULL);
  while ((drv = next_driver(&i)) && !error)
  error = fn(drv, data);
  klist_iter_exit(&i);
  return error;
  }
  很明显,这个函数就是遍历总线之上的驱动。每遍历一个驱动就调用一次回调函数进行判断。如果回调函数返回不为0。就说明匹配已经成功了。不需要再匹配剩余的。退出。在这里调用的回调函数是__device_attach().在这里。完全了设备与驱动匹配的最核心的动作。代码如下:
  static int __device_attach(struct device_driver *drv, void *data)
  {
  struct device *dev = data;
  return driver_probe_device(drv, dev);
  }
  转到driver_probe_device():
  int driver_probe_device(struct device_driver *drv, struct device *dev)
  {
  int ret = 0;
  if (!device_is_registered(dev))
  return -ENODEV;
  if (drv->bus->match && !drv->bus->match(dev, drv))
  goto done;
  pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
  drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);
  ret = really_probe(dev, drv);
  done:
  return ret;
  }
  如果设备没有注册到总线之上。即dev->is_registered不为1. 就直接返回。
  然后,再调用总线的match()函数进行匹配。如果match()函数返回0.说明匹配失败。那退出此函数。如果match函数返回1.说明初步的检查已经通过了。可以进入really_probe()再进行细致的检查。如果匹配成功,这个函数会返回1.此函数比较长而且比较重要,分段列出代码:
  static int really_probe(struct device *dev, struct device_driver *drv)
  {
  int ret = 0;
  atomic_inc(&probe_count);
  pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
  drv->bus->name, __FUNCTION__, drv->name, dev->bus_id);
  WARN_ON(!list_empty(&dev->devres_head));
  dev->driver = drv;
  if (driver_sysfs_add(dev)) {
  printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
  __FUNCTION__, dev->bus_id);
  goto probe_failed;
  }
  先假设驱动和设备是匹配的。为设备结构设置驱动成员。使其指向匹配的驱动。然后再调用driver_sysfs_add()建立几个符号链接。这几个链接分别为:
  1:在驱动目录下建立一个到设备的同名链接
  2:在设备目录下建立一个名为driver。到驱动的链接
  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;
  }
  然后,再调用总线的probe函数。如果总线的此函数不存在。就会调用驱动的probe函数。如果匹配成功,返回0.如果不成功,就会跳转到probe_failed
  driver_bound(dev);
  ret = 1;
  pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
  drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);
  goto done;
  到这里。设备和驱动已经匹配成功,调用driver_bound()将其关联起来。在这个函数里:
  会将设备加至驱动的设备链表。这在我们之前分析bus,device driver中分析到的。相关的代码如下示:
  klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices);
  至此,这个匹配过程已经圆满结束了。返回1
  probe_failed:
  devres_release_all(dev);
  driver_sysfs_remove(dev);
  dev->driver = NULL;
  if (ret != -ENODEV && ret != -ENXIO) {
  /* driver matched but the probe failed */
  printk(KERN_WARNING
  "%s: probe of %s failed with error %d\n",
  drv->name, dev->bus_id, ret);
  }
  /*
  * Ignore errors returned by ->probe so that the next driver can try
  * its luck.
  */
  ret = 0;
  这里是匹配不成功的处理,在这里,删除之前建立的几个链接文件,然后将设备的driver域置空。
  done:
  atomic_dec(&probe_count);
  wake_up(&probe_waitqueue);
  return ret;
  }
  从上面的分析可以看到,对应创建的属性文件分别为:uevent_attr devt_attr。它们的定义如下:
  static struct device_attribute uevent_attr =
  __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);
  static struct device_attribute devt_attr =
  __ATTR(dev, S_IRUGO, show_dev, NULL);
  uevent_attr对应的读写函数分别为:show_uevent store_uevent。先分析读操作。它的代码如下:
  static ssize_t show_uevent(struct device *dev, struct device_attribute *attr,
  char *buf)
  {
  struct kobject *top_kobj;
  struct kset *kset;
  struct kobj_uevent_env *env = NULL;
  int i;
  size_t count = 0;
  int retval;
  /* search the kset, the device belongs to */
  top_kobj = &dev->kobj;
  while (!top_kobj->kset && top_kobj->parent)
  top_kobj = top_kobj->parent;
  if (!top_kobj->kset)
  goto out;
  kset = top_kobj->kset;
  if (!kset->uevent_ops || !kset->uevent_ops->uevent)
  goto out;
  /* respect filter */
  if (kset->uevent_ops && kset->uevent_ops->filter)
  if (!kset->uevent_ops->filter(kset, &dev->kobj))
  goto out;
  env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
  if (!env)
  return -ENOMEM;
  /* let the kset specific function add its keys */
  retval = kset->uevent_ops->uevent(kset, &dev->kobj, env);
  if (retval)
  goto out;
  /* copy keys to file */
  for (i = 0; i envp_idx; i++)
  count += sprintf(&buf[count], "%s\n", env->envp[i]);
  out:
  kfree(env);
  return count;
  }
  从代码可以看出。这里会显示出由设备对应的kset.也就是由devices_kset所产生的环境变量。例如,在shell中输入如下指令:
  Cat /sys/devices/LNXSYSTM:00/ uevent
  输出结果如下:
  PHYSDEVBUS=acpi
  MODALIAS=acpi:LNXSYSTM:
  这就是由devices_kset所添加的环境变量
  写操作对应的代码如下:
  static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
  const char *buf, size_t count)
  {
  enum kobject_action action;
  if (kobject_action_type(buf, count, &action) == 0) {
  kobject_uevent(&dev->kobj, action);
  goto out;
  }
  dev_err(dev, "uevent: unsupported action-string; this will "
  "be ignored in a future kernel version\n");
  kobject_uevent(&dev->kobj, KOBJ_ADD);
  out:
  return count;
  }
  从上面的代码可以看出。这个文件的作用是输入一个字符字串。如果字符不合法,就会默认产生一个add事件。
  devt_attr对应的读写函数为show_dev NULL.写函数为空,也就是说这个属性文件不允许写。只允许读。读操作的代码如下示:
  static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
  char *buf)
  {
  return print_dev_t(buf, dev->devt);
  }
  也就是说,会将设备号显示出来.
  分析完了bus.device.再接着分析driver.这里我们要分析的最后一个元素了。耐着性子往下看,快要完了^_^
  驱动注册的接口为:driver_register().代码如下:
  int driver_register(struct device_driver *drv)
  {
  int ret;
  if ((drv->bus->probe && drv->probe) ||
  (drv->bus->remove && drv->remove) ||
  (drv->bus->shutdown && drv->shutdown))
  printk(KERN_WARNING "Driver '%s' needs updating - please use "
  "bus_type methods\n", drv->name);
  ret = bus_add_driver(drv);
  if (ret)
  return ret;
  ret = driver_add_groups(drv, drv->groups);
  if (ret)
  bus_remove_driver(drv);
  return ret;
  }
  如果设备与总线定义了相同的成员的函数。内核是优先使用bus中定义的.这一点我们在分析device注册的时候已经分析过。所以。这里打印出警告信息,用来提醒代码编写者。在这里,忽略有关group的东西。剩余的便只剩下bus_add_driver().代码如下:
  int bus_add_driver(struct device_driver *drv)
  {
  struct bus_type *bus;
  struct driver_private *priv;
  int error = 0;
  bus = bus_get(drv->bus);
  if (!bus)
  return -EINVAL;
  pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
  priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  if (!priv) {
  error = -ENOMEM;
  goto out_put_bus;
  }
  klist_init(&priv->klist_devices, NULL, NULL);
  priv->driver = drv;
  drv->p = priv;
  priv->kobj.kset = bus->p->drivers_kset;
  error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
  "%s", drv->name);
  初始化驱动的driver_private域。使其内嵌的kobject的kset指bus中的drivers_kset.这样,这个内嵌的kobject所生成的目录就会存在于bus对应目录的driver目录之下。这里还要注意的是,为内嵌kobject指定的ktype是driver_ktype.属性文件的读写操作都回回溯到struct driver_attribute中。这在之后再分析.
  if (error)
  goto out_unregister;
  if (drv->bus->p->drivers_autoprobe) {
  error = driver_attach(drv);
  if (error)
  goto out_unregister;
  }
  klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
  b
  module_add_driver(drv->owner, drv);
  如果总线允许自动进行匹配。就会调用driver_attach()进行这个自己匹配过程。这个函数跟我们在上面分析的device自动匹配过程是一样的。请自行分析.最后,将驱动挂到bus对应的驱动链表
  error = driver_create_file(drv, &driver_attr_uevent);
  if (error) {
  printk(KERN_ERR "%s: uevent attr (%s) failed\n",
  __FUNCTION__, drv->name);
  }
  生成一个属性为driver_attr_uevent的属性文件
  error = driver_add_attrs(bus, drv);
  if (error) {
  /* How the hell do we get out of this pickle? Give up */
  printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
  __FUNCTION__, drv->name);
  }
  为bus中的driver属性生成属性文件
  error = add_bind_files(drv);
  if (error) {
  /* Ditto */
  printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
  __FUNCTION__, drv->name);
  }
  生成属性为driver_attr_unbind和driver_attr_bind的属性文件
  kobject_uevent(&priv->kobj, KOBJ_ADD);
  生成一个add事件
  return error;
  out_unregister:
  kobject_put(&priv->kobj);
  out_put_bus:
  bus_put(bus);
  return error;
  }
  总的来说,这个函数比较简单。其中涉及到的子函数大部份都在之前分析过。我们接下来分析一下。它所创建的几个属性文件的含义。
  如上所述。在这里会创建三个属性文件,对应属性分别为:driver_attr_uevent,driver_attr_unbind,driver_attr_bind。这几个属性的定义如下:
  static DRIVER_ATTR(uevent, S_IWUSR, NULL, driver_uevent_store);
  static DRIVER_ATTR(unbind, S_IWUSR, NULL, driver_unbind);
  static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);
  DRIVER_ATTR宏的定义如下:
  #define DRIVER_ATTR(_name, _mode, _show, _store)   \
  struct driver_attribute driver_attr_##_name =      \
  __ATTR(_name, _mode, _show, _store)
  对于driver_attr_uevent.它的读写函数分别为:NULL。driver_uevent_store。也就是说这个文件只允许写,不允许读操作。写操作的代码如下示:
  static ssize_t driver_uevent_store(struct device_driver *drv,
  const char *buf, size_t count)
  {
  enum kobject_action action;
  if (kobject_action_type(buf, count, &action) == 0)
  kobject_uevent(&drv->p->kobj, action);
  return count;
  }
  很明显,这是一个手动产生事件的过程。用户可间可以写事件到这个文件来产生事件。
  对于driver_unbind.它的读写函数分别为:NULL driver_unbind。这个文件也是不允许读的。写操作代码如下:
  static ssize_t driver_unbind(struct device_driver *drv,
  const char *buf, size_t count)
  {
  struct bus_type *bus = bus_get(drv->bus);
  struct device *dev;
  int err = -ENODEV;
  dev = bus_find_device_by_name(bus, NULL, buf);
  if (dev && dev->driver == drv) {
  if (dev->parent)   /* Needed for USB */
  down(&dev->parent->sem);
  device_release_driver(dev);
  if (dev->parent)
  up(&dev->parent->sem);
  err = count;
  }
  put_device(dev);
  bus_put(bus);
  return err;
  }
  从上面的代码可以看出。写入文件的是一个设备名称。这个函数对应操作是将这个设备与驱动的绑定分离开来。
  driver_attr_bind属性对应的读写函数分别为NULL。driver_attr_bind 即也是不允许写的。从字面意思和上面分析的driver_attr_unbind操作代码来看,这个属性对应的写函数应该是将写入的设备文件与此驱动绑定起来。我们来看下代码。以证实我们的猜测。代码如下:
  static ssize_t driver_bind(struct device_driver *drv,
  const char *buf, size_t count)
  {
  struct bus_type *bus = bus_get(drv->bus);
  struct device *dev;
  int err = -ENODEV;
  dev = bus_find_device_by_name(bus, NULL, buf);
  if (dev && dev->driver == NULL) {
  if (dev->parent)   /* Needed for USB */
  down(&dev->parent->sem);
  down(&dev->sem);
  err = driver_probe_device(drv, dev);
  up(&dev->sem);
  if (dev->parent)
  up(&dev->parent->sem);
  if (err > 0) {
  /* success */
  err = count;
  } else if (err == 0) {
  /* driver didn't accept device */
  err = -ENODEV;
  }
  }
  put_device(dev);
  bus_put(bus);
  return err;
  }
  果然,和我们猜测的是一样的。
  五:小结
  在这一节里,分析了设备模型中的最底层的元素和他们之间的关系。也分析了它们建立的几个属性文件的含义。到这里,我们已经可以自己写驱动架构代码了.^_^
  
  
  
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics