<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 189,comments - 115,trackbacks - 0

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

    字號:    
    這段代碼中比較繁鎖的就是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,                   \
    }
    由此可見.上面這三個屬性對應(yīng)的名稱為別為uevent, drivers_probe, drivers_autoprobe.也就是說,會在bus_types目錄下生成三個文件,分別為uevent,probe,autoprobe.
    根據(jù)之前的分析,我們知道在sysfs文件系統(tǒng)中,對普通屬性文件的讀寫都會回溯到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;
    }
    從代碼可以看出.讀寫操作又會回溯到bus_attribute中的show和store中.在自定義結(jié)構(gòu)里嵌入struct attribute,.然后再操作回溯到自定義結(jié)構(gòu)中,這是一種比較高明的架構(gòu)設(shè)計手法.
    閑言少敘.我們對應(yīng)看一下上面三個文件對應(yīng)的最終操作:
    Uevent對應(yīng)的讀寫操作為:NULL, bus_uevent_store.對于這個文件沒有讀操作,只有寫操作.用cat 命令去查看這個文件的時候,可能會返回”設(shè)備不存在”的錯誤.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就會產(chǎn)生一個add的事件,
    Probe文件對應(yīng)的讀寫操作為:NULL store_drivers_probe.
    store_drivers_probe()這個函數(shù)的代碼涉及到struct device.等分析完struct device可以自行回過來看下這個函數(shù)的實現(xiàn).實際上,這個函數(shù)是將用戶輸和的設(shè)備名稱對應(yīng)的設(shè)備與驅(qū)動匹配一次.
     
    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的值輸出到用戶空間,這個值為1時,自動將驅(qū)動與設(shè)備進行匹配.否則,反之.
    寫操作的代碼如下:
    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就可以控制總線是否要進行自動匹配了.
    從這里也可以看出.內(nèi)核開發(fā)者的思維是何等的靈活.
    我們從sysfs中找個例子來印證一下:
    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()中有幾個很重要的操作,如下:
    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()的實現(xiàn).這個函數(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的時候,沒有指定父結(jié)點,在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部份,這部份不會影響本函數(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è)備號,則建立屬性為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()會在對應(yīng)總線代表目錄的device目錄下創(chuàng)建幾個到device的鏈接.然后產(chǎn)生一個add事件,再調(diào)用bus_attach_device()去匹配已經(jīng)注冊到總線的驅(qū)動程序.全部做完之后,將設(shè)備掛到父結(jié)點的子鏈表.
     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()是一個很重要的函數(shù)。它將設(shè)備自動與掛在總線上面的驅(qū)動進行匹配。代碼如下:
    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 文件的作用.然后,將設(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ū)動的情況,只需要將其直接和驅(qū)動綁定即可。如果沒有指定驅(qū)動。就匹配總線之上的驅(qū)動。這是在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;
    }
    很明顯,這個函數(shù)就是遍歷總線之上的驅(qū)動。每遍歷一個驅(qū)動就調(diào)用一次回調(diào)函數(shù)進行判斷。如果回調(diào)函數(shù)返回不為0。就說明匹配已經(jīng)成功了。不需要再匹配剩余的。退出。在這里調(diào)用的回調(diào)函數(shù)是__device_attach().在這里。完全了設(shè)備與驅(qū)動匹配的最核心的動作。代碼如下:
    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ù)進行匹配。如果match()函數(shù)返回0.說明匹配失敗。那退出此函數(shù)。如果match函數(shù)返回1.說明初步的檢查已經(jīng)通過了??梢赃M入really_probe()再進行細致的檢查。如果匹配成功,這個函數(shù)會返回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ū)動和設(shè)備是匹配的。為設(shè)備結(jié)構(gòu)設(shè)置驅(qū)動成員。使其指向匹配的驅(qū)動。然后再調(diào)用driver_sysfs_add()建立幾個符號鏈接。這幾個鏈接分別為:
    1:在驅(qū)動目錄下建立一個到設(shè)備的同名鏈接
    2:在設(shè)備目錄下建立一個名為driver。到驅(qū)動的鏈接
     
         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ù)不存在。就會調(diào)用驅(qū)動的probe函數(shù)。如果匹配成功,返回0.如果不成功,就會跳轉(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ū)動已經(jīng)匹配成功,調(diào)用driver_bound()將其關(guān)聯(lián)起來。在這個函數(shù)里:
    會將設(shè)備加至驅(qū)動的設(shè)備鏈表。這在我們之前分析bus,device driver中分析到的。相關(guān)的代碼如下示:
         klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices);
    至此,這個匹配過程已經(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;
    這里是匹配不成功的處理,在這里,刪除之前建立的幾個鏈接文件,然后將設(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;
    }
    從代碼可以看出。這里會顯示出由設(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;
    }
    從上面的代碼可以看出。這個文件的作用是輸入一個字符字串。如果字符不合法,就會默認產(chǎn)生一個add事件。
     
    devt_attr對應(yīng)的讀寫函數(shù)為show_dev NULL.寫函數(shù)為空,也就是說這個屬性文件不允許寫。只允許讀。讀操作的代碼如下示:
    static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
                  char *buf)
    {
         return print_dev_t(buf, dev->devt);
    }
    也就是說,會將設(shè)備號顯示出來.
     
    分析完了bus.device.再接著分析driver.這里我們要分析的最后一個元素了。耐著性子往下看,快要完了^_^
     
    驅(qū)動注冊的接口為: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中定義的.這一點我們在分析device注冊的時候已經(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ū)動的driver_private域。使其內(nèi)嵌的kobject的kset指bus中的drivers_kset.這樣,這個內(nèi)嵌的kobject所生成的目錄就會存在于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);
    如果總線允許自動進行匹配。就會調(diào)用driver_attach()進行這個自己匹配過程。這個函數(shù)跟我們在上面分析的device自動匹配過程是一樣的。請自行分析.最后,將驅(qū)動掛到bus對應(yīng)的驅(qū)動鏈表
     
         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;
    }
    總的來說,這個函數(shù)比較簡單。其中涉及到的子函數(shù)大部份都在之前分析過。我們接下來分析一下。它所創(chuàng)建的幾個屬性文件的含義。
    如上所述。在這里會創(chuàng)建三個屬性文件,對應(yīng)屬性分別為: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.它的讀寫函數(shù)分別為: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;
    }
    很明顯,這是一個手動產(chǎn)生事件的過程。用戶可間可以寫事件到這個文件來產(chǎn)生事件。
    對于driver_unbind.它的讀寫函數(shù)分別為: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;
    }
    從上面的代碼可以看出。寫入文件的是一個設(shè)備名稱。這個函數(shù)對應(yīng)操作是將這個設(shè)備與驅(qū)動的綁定分離開來。
     
    driver_attr_bind屬性對應(yīng)的讀寫函數(shù)分別為NULL。driver_attr_bind 即也是不允許寫的。從字面意思和上面分析的driver_attr_unbind操作代碼來看,這個屬性對應(yīng)的寫函數(shù)應(yīng)該是將寫入的設(shè)備文件與此驅(qū)動綁定起來。我們來看下代碼。以證實我們的猜測。代碼如下:
    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)系。也分析了它們建立的幾個屬性文件的含義。到這里,我們已經(jīng)可以自己寫驅(qū)動架構(gòu)代碼了.^_^
    posted on 2009-06-11 21:31 MEYE 閱讀(616) 評論(0)  編輯  收藏

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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 美女羞羞免费视频网站| 亚洲线精品一区二区三区| 免费人妻无码不卡中文字幕18禁| 国产免费看插插插视频| 中文字幕中韩乱码亚洲大片| 亚洲国产精品线在线观看| 亚洲精品中文字幕无乱码| 在线a亚洲老鸭窝天堂av高清| 国产成人+综合亚洲+天堂| 中文无码日韩欧免费视频| 久久免费看少妇高潮V片特黄| 九九精品免费视频| 又粗又大又硬又爽的免费视频| 在线观看亚洲精品福利片| 久久久久亚洲av无码专区喷水| 亚洲一日韩欧美中文字幕在线| 暖暖免费中文在线日本| 84pao强力永久免费高清| 午夜视频免费观看| 亚洲日韩中文无码久久| 亚洲免费观看网站| 无遮挡a级毛片免费看| 98精品全国免费观看视频| 日韩免费高清一级毛片在线| 亚洲精品无码永久中文字幕| 亚洲不卡中文字幕| 一级做α爱过程免费视频| 曰批全过程免费视频播放网站| 免费a级黄色毛片| 亚洲黄色在线观看| 鲁啊鲁在线视频免费播放| 中文字幕亚洲免费无线观看日本| 国产片免费在线观看| 亚洲AV成人精品网站在线播放| 欧洲亚洲国产精华液| 久别的草原电视剧免费观看| 四虎影视大全免费入口| 亚洲a在线视频视频| 国产午夜亚洲精品不卡免下载| 97公开免费视频| 91麻豆精品国产自产在线观看亚洲 |