posts - 189,comments - 115,trackbacks - 0

          linux設備模型深探(2) 2009-06-05 15:40

          字號:    
          這段代碼中比較繁鎖的就是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_notifier,
                                      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_notifier,
                                      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);
                   if (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 < env->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;
          }
          果然,和我們猜測的是一樣的。
           
          五:小結
          在這一節里,分析了設備模型中的最底層的元素和他們之間的關系。也分析了它們建立的幾個屬性文件的含義。到這里,我們已經可以自己寫驅動架構代碼了.^_^
          posted on 2009-06-11 21:31 MEYE 閱讀(617) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 滨州市| 自治县| 榆社县| 垫江县| 德清县| 丰台区| 甘洛县| 汝南县| 会昌县| 天长市| 维西| 兰坪| 乐陵市| 鄂托克前旗| 南和县| 芒康县| 青田县| 略阳县| 祁东县| 崇文区| 龙门县| 疏附县| 彭泽县| 渝中区| 岳普湖县| 伊春市| 安陆市| 康平县| 乐至县| 青铜峡市| 巴林右旗| 佛学| 靖宇县| 如东县| 苏尼特左旗| 洛宁县| 新昌县| 庆阳市| 康马县| 金寨县| 云和县|