這段代碼中比較繁鎖的就是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)代碼了.^_^