posts - 189,comments - 115,trackbacks - 0

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

          字號(hào):    
          這段代碼中比較繁鎖的就是bus_type對應(yīng)目錄下的屬性文件建立,為了直觀的說明,將屬性文件的建立統(tǒng)一放到一起分析
          從上面的代碼中可以看,創(chuàng)建屬性文件對應(yīng)的屬性分別為:
          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,                   \
          }
          由此可見.上面這三個(gè)屬性對應(yīng)的名稱為別為uevent, drivers_probe, drivers_autoprobe.也就是說,會(huì)在bus_types目錄下生成三個(gè)文件,分別為uevent,probe,autoprobe.
          根據(jù)之前的分析,我們知道在sysfs文件系統(tǒng)中,對普通屬性文件的讀寫都會(huì)回溯到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函數(shù)對應(yīng)的代碼為:
          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;
          }
          從代碼可以看出.讀寫操作又會(huì)回溯到bus_attribute中的show和store中.在自定義結(jié)構(gòu)里嵌入struct attribute,.然后再操作回溯到自定義結(jié)構(gòu)中,這是一種比較高明的架構(gòu)設(shè)計(jì)手法.
          閑言少敘.我們對應(yīng)看一下上面三個(gè)文件對應(yīng)的最終操作:
          Uevent對應(yīng)的讀寫操作為:NULL, bus_uevent_store.對于這個(gè)文件沒有讀操作,只有寫操作.用cat 命令去查看這個(gè)文件的時(shí)候,可能會(huì)返回”設(shè)備不存在”的錯(cuò)誤.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;
          }
          從這里可以看到,可以在用戶空間控制事件的發(fā)生,如echo add > event就會(huì)產(chǎn)生一個(gè)add的事件,
          Probe文件對應(yīng)的讀寫操作為:NULL store_drivers_probe.
          store_drivers_probe()這個(gè)函數(shù)的代碼涉及到struct device.等分析完struct device可以自行回過來看下這個(gè)函數(shù)的實(shí)現(xiàn).實(shí)際上,這個(gè)函數(shù)是將用戶輸和的設(shè)備名稱對應(yīng)的設(shè)備與驅(qū)動(dòng)匹配一次.
           
          Autoprobe文件對應(yīng)的讀寫操作為show_drivers_autoprobe, store_drivers_autoprobe.對應(yīng)讀的代碼為:
          static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
          {
               return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
          }
          它將總線對應(yīng)的drivers_autoprobe的值輸出到用戶空間,這個(gè)值為1時(shí),自動(dòng)將驅(qū)動(dòng)與設(shè)備進(jìn)行匹配.否則,反之.
          寫操作的代碼如下:
          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;
          }
          寫操作就會(huì)改變bus->p->drivers_autoprobe的值.
          就這樣,通過sysfs就可以控制總線是否要進(jìn)行自動(dòng)匹配了.
          從這里也可以看出.內(nèi)核開發(fā)者的思維是何等的靈活.
          我們從sysfs中找個(gè)例子來印證一下:
          Cd  / sys/bus/usb
          用ls命令查看:
          devices  drivers  drivers_autoprobe  drivers_probe  uevent
          與上面分析的相吻合
           
          設(shè)備的注冊接口為: device_register().
          int device_register(struct device *dev)
          {
               device_initialize(dev);
               return device_add(dev);
          }
          Device_initialize()中有幾個(gè)很重要的操作,如下:
          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的內(nèi)嵌kobject指定了ktype和kset.device_kset的值如下:
          devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
          即對應(yīng)sysfs中的/sys/devices
          device_ktype 中對屬性的讀寫操作同bus中的類似,被回溯到了struct device_attribute中的show 和store.
          接著往下看device_add()的實(shí)現(xiàn).這個(gè)函數(shù)比較長,分段分析如下:
          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的時(shí)候,沒有指定父結(jié)點(diǎn),在kobject_add將會(huì)在/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部份,這部份不會(huì)影響本函數(shù)的流程
           
               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中指定了設(shè)備號(hào),則建立屬性為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()會(huì)在對應(yīng)總線代表目錄的device目錄下創(chuàng)建幾個(gè)到device的鏈接.然后產(chǎn)生一個(gè)add事件,再調(diào)用bus_attach_device()去匹配已經(jīng)注冊到總線的驅(qū)動(dòng)程序.全部做完之后,將設(shè)備掛到父結(jié)點(diǎn)的子鏈表.
           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;
          }
          出錯(cuò)處理部份.
           
          bus_attach_device()是一個(gè)很重要的函數(shù)。它將設(shè)備自動(dòng)與掛在總線上面的驅(qū)動(dòng)進(jìn)行匹配。代碼如下:
          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的情況下,才會(huì)去自己匹配。這也就是bus目錄下的drivers_probe 文件的作用.然后,將設(shè)備掛到總線的設(shè)備鏈表。
          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;
          }
          對于設(shè)備自己已經(jīng)指定驅(qū)動(dòng)的情況,只需要將其直接和驅(qū)動(dòng)綁定即可。如果沒有指定驅(qū)動(dòng)。就匹配總線之上的驅(qū)動(dòng)。這是在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;
          }
          很明顯,這個(gè)函數(shù)就是遍歷總線之上的驅(qū)動(dòng)。每遍歷一個(gè)驅(qū)動(dòng)就調(diào)用一次回調(diào)函數(shù)進(jìn)行判斷。如果回調(diào)函數(shù)返回不為0。就說明匹配已經(jīng)成功了。不需要再匹配剩余的。退出。在這里調(diào)用的回調(diào)函數(shù)是__device_attach().在這里。完全了設(shè)備與驅(qū)動(dòng)匹配的最核心的動(dòng)作。代碼如下:
          static int __device_attach(struct device_driver *drv, void *data)
          {
               struct device *dev = data;
               return driver_probe_device(drv, dev);
          }
          轉(zhuǎn)到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;
          }
          如果設(shè)備沒有注冊到總線之上。即dev->is_registered不為1. 就直接返回。
          然后,再調(diào)用總線的match()函數(shù)進(jìn)行匹配。如果match()函數(shù)返回0.說明匹配失敗。那退出此函數(shù)。如果match函數(shù)返回1.說明初步的檢查已經(jīng)通過了??梢赃M(jìn)入really_probe()再進(jìn)行細(xì)致的檢查。如果匹配成功,這個(gè)函數(shù)會(huì)返回1.此函數(shù)比較長而且比較重要,分段列出代碼:
          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;
               }
          先假設(shè)驅(qū)動(dòng)和設(shè)備是匹配的。為設(shè)備結(jié)構(gòu)設(shè)置驅(qū)動(dòng)成員。使其指向匹配的驅(qū)動(dòng)。然后再調(diào)用driver_sysfs_add()建立幾個(gè)符號(hào)鏈接。這幾個(gè)鏈接分別為:
          1:在驅(qū)動(dòng)目錄下建立一個(gè)到設(shè)備的同名鏈接
          2:在設(shè)備目錄下建立一個(gè)名為driver。到驅(qū)動(dòng)的鏈接
           
               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;
               }
          然后,再調(diào)用總線的probe函數(shù)。如果總線的此函數(shù)不存在。就會(huì)調(diào)用驅(qū)動(dòng)的probe函數(shù)。如果匹配成功,返回0.如果不成功,就會(huì)跳轉(zhuǎn)到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;
          到這里。設(shè)備和驅(qū)動(dòng)已經(jīng)匹配成功,調(diào)用driver_bound()將其關(guān)聯(lián)起來。在這個(gè)函數(shù)里:
          會(huì)將設(shè)備加至驅(qū)動(dòng)的設(shè)備鏈表。這在我們之前分析bus,device driver中分析到的。相關(guān)的代碼如下示:
               klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices);
          至此,這個(gè)匹配過程已經(jīng)圓滿結(jié)束了。返回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;
          這里是匹配不成功的處理,在這里,刪除之前建立的幾個(gè)鏈接文件,然后將設(shè)備的driver域置空。
          done:
               atomic_dec(&probe_count);
               wake_up(&probe_waitqueue);
               return ret;
          }
           
          從上面的分析可以看到,對應(yīng)創(chuàng)建的屬性文件分別為: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對應(yīng)的讀寫函數(shù)分別為: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;
          }
          從代碼可以看出。這里會(huì)顯示出由設(shè)備對應(yīng)的kset.也就是由devices_kset所產(chǎn)生的環(huán)境變量。例如,在shell中輸入如下指令:
          Cat /sys/devices/LNXSYSTM:00/ uevent
          輸出結(jié)果如下:
          PHYSDEVBUS=acpi
          MODALIAS=acpi:LNXSYSTM:
          這就是由devices_kset所添加的環(huán)境變量
           
          寫操作對應(yīng)的代碼如下:
          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;
          }
          從上面的代碼可以看出。這個(gè)文件的作用是輸入一個(gè)字符字串。如果字符不合法,就會(huì)默認(rèn)產(chǎn)生一個(gè)add事件。
           
          devt_attr對應(yīng)的讀寫函數(shù)為show_dev NULL.寫函數(shù)為空,也就是說這個(gè)屬性文件不允許寫。只允許讀。讀操作的代碼如下示:
          static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
                        char *buf)
          {
               return print_dev_t(buf, dev->devt);
          }
          也就是說,會(huì)將設(shè)備號(hào)顯示出來.
           
          分析完了bus.device.再接著分析driver.這里我們要分析的最后一個(gè)元素了。耐著性子往下看,快要完了^_^
           
          驅(qū)動(dòng)注冊的接口為: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;
          }
          如果設(shè)備與總線定義了相同的成員的函數(shù)。內(nèi)核是優(yōu)先使用bus中定義的.這一點(diǎn)我們在分析device注冊的時(shí)候已經(jīng)分析過。所以。這里打印出警告信息,用來提醒代碼編寫者。在這里,忽略有關(guān)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);
          初始化驅(qū)動(dòng)的driver_private域。使其內(nèi)嵌的kobject的kset指bus中的drivers_kset.這樣,這個(gè)內(nèi)嵌的kobject所生成的目錄就會(huì)存在于bus對應(yīng)目錄的driver目錄之下。這里還要注意的是,為內(nèi)嵌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);
          如果總線允許自動(dòng)進(jìn)行匹配。就會(huì)調(diào)用driver_attach()進(jìn)行這個(gè)自己匹配過程。這個(gè)函數(shù)跟我們在上面分析的device自動(dòng)匹配過程是一樣的。請自行分析.最后,將驅(qū)動(dòng)掛到bus對應(yīng)的驅(qū)動(dòng)鏈表
           
               error = driver_create_file(drv, &driver_attr_uevent);
               if (error) {
                   printk(KERN_ERR "%s: uevent attr (%s) failed\n",
                        __FUNCTION__, drv->name);
               }
          生成一個(gè)屬性為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);
          生成一個(gè)add事件
               return error;
          out_unregister:
               kobject_put(&priv->kobj);
          out_put_bus:
               bus_put(bus);
               return error;
          }
          總的來說,這個(gè)函數(shù)比較簡單。其中涉及到的子函數(shù)大部份都在之前分析過。我們接下來分析一下。它所創(chuàng)建的幾個(gè)屬性文件的含義。
          如上所述。在這里會(huì)創(chuàng)建三個(gè)屬性文件,對應(yīng)屬性分別為:driver_attr_uevent,driver_attr_unbind,driver_attr_bind。這幾個(gè)屬性的定義如下:
          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.它的讀寫函數(shù)分別為:NULL。driver_uevent_store。也就是說這個(gè)文件只允許寫,不允許讀操作。寫操作的代碼如下示:
          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;
          }
          很明顯,這是一個(gè)手動(dòng)產(chǎn)生事件的過程。用戶可間可以寫事件到這個(gè)文件來產(chǎn)生事件。
          對于driver_unbind.它的讀寫函數(shù)分別為:NULL driver_unbind。這個(gè)文件也是不允許讀的。寫操作代碼如下:
          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;
          }
          從上面的代碼可以看出。寫入文件的是一個(gè)設(shè)備名稱。這個(gè)函數(shù)對應(yīng)操作是將這個(gè)設(shè)備與驅(qū)動(dòng)的綁定分離開來。
           
          driver_attr_bind屬性對應(yīng)的讀寫函數(shù)分別為NULL。driver_attr_bind 即也是不允許寫的。從字面意思和上面分析的driver_attr_unbind操作代碼來看,這個(gè)屬性對應(yīng)的寫函數(shù)應(yīng)該是將寫入的設(shè)備文件與此驅(qū)動(dòng)綁定起來。我們來看下代碼。以證實(shí)我們的猜測。代碼如下:
          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;
          }
          果然,和我們猜測的是一樣的。
           
          五:小結(jié)
          在這一節(jié)里,分析了設(shè)備模型中的最底層的元素和他們之間的關(guān)系。也分析了它們建立的幾個(gè)屬性文件的含義。到這里,我們已經(jīng)可以自己寫驅(qū)動(dòng)架構(gòu)代碼了.^_^
          posted on 2009-06-11 21:31 MEYE 閱讀(617) 評論(0)  編輯  收藏

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 灵武市| 甘孜| 云安县| 闸北区| 寻甸| 兴和县| 宁化县| 丹棱县| 余庆县| 安乡县| 抚远县| 栾城县| 南宁市| 齐齐哈尔市| 平阴县| 女性| 绥阳县| 丹凤县| 文水县| 犍为县| 互助| 高密市| 会泽县| 白沙| 和平区| 临泉县| 嘉善县| 淄博市| 上栗县| 舞钢市| 宁阳县| 商丘市| 铅山县| 岳西县| 电白县| 连云港市| 东明县| 本溪市| 荃湾区| 新化县| 宝清县|