接上一篇文章,繼續(xù)device_add()中的代碼:
error = bus_add_device(dev);
if (error)
goto BusError;
在對應總線目錄下的device目錄下創(chuàng)建幾個到device的鏈接文件。
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
添加power文件。
/* Notify clients of device addition. This call must come
* after dpm_sysf_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
>
調用bus的回調函數(shù)。
kobject_uevent(&dev->kobj, KOBJ_ADD);
產(chǎn)生一個add事件。
bus_probe_device(dev);
這個函數(shù)將匹配已經(jīng)注冊到總線的驅動程序,如下:
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
int ret;
if (bus && bus->p->drivers_autoprobe) {
ret = device_attach(dev);
WARN_ON(ret < 0);
}
}
只有bus->p->drivers_autoprobe設置為1是才會去匹配,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 {
pm_runtime_get_noresume(dev);
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
pm_runtime_put_sync(dev);
}
up(&dev->sem);
return ret;
}
如果已指定了dev->driver,則直接在相應的driver目錄下建立鏈接文件,將驅動和設備綁定。
int device_bind_driver(struct device *dev)
{
int ret;
ret = driver_sysfs_add(dev);
if (!ret)
driver_bound(dev);
return ret;
}
driver_bound()函數(shù)如下:
static void driver_bound(struct device *dev)
{
if (klist_node_attached(&dev->p->knode_driver)) {
printk(KERN_WARNING "%s: device %s already bound\n",
__func__, kobject_name(&dev->kobj));
; return;
}
pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
__func__, dev->driver->name);
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BOUND_DRIVER, dev);
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
}
通知總線綁定驅動,將設備添加到驅動的設備鏈表。
否則調用bus_for_each_drv()匹配總線上的驅動:
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);
http://msnpiki.msnfanatic.com/index.php/Main_Page-->
size: 10.5pt"> klist_iter_exit(&i);
return error;
}
歷遍總線上的驅動,每次都調用回調函數(shù)fn()(這里是__device_attach),如果fn()返回1則匹配成功,__device_attach()如下:
static int __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
if (!driver_match_device(drv, dev))
return 0;
return driver_probe_device(drv, dev);
}
driver_match_device()如下:
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus ->match(dev, drv) : 1;
}
用drv->bus的match方法進行匹配,如果成功就會繼續(xù)調用driver_probe_device():
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
http://msnpiki.msnfanatic.com/index.php/Main_Page-->
cm 0cm 0pt"> return -ENODEV; pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
pm_runtime_put_sync(dev);
return ret;
}
進行一下驗證和同步后調用really_probe()最終詳細的匹配:
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, __func__, drv->name, dev_name(dev));
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",
__func__, dev_name(dev));
goto probe_failed;
}
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;
}
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
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_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
先假定dev的驅動為drv,然后如果總線閃的probe存在,則調用它,否則調用drv的probe()函數(shù),返回0則匹配成功,調用driver_bound()進行設備和驅動的綁定。driver_bound()函數(shù)在上面已經(jīng)分析。
接著device_add()函數(shù)中的代碼:
if (dev->class) {
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->class_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
http://msnpiki.msnfanatic.com/index.php/Main_Page-->
b-count: 3"> if (class_intf->add_dev) class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->class_mutex);
}
Dev所屬class的一些操作。
done:
put_device(dev);
return error;
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
device_remove_sys_dev_entry(dev);
devtattrError:
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);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
最后是一下除錯撤銷處理。
}
至此,已經(jīng)詳細的分析了創(chuàng)建和注冊設備的函數(shù)。
內核撤銷設備注冊的函數(shù)為device_unregister(),這里不再分析。
內核提供注冊驅動的函數(shù)為driver_register():
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
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);
驗證driver的包含了需要的方法,并打印信息。
other = driver_find(drv->name, drv->bus);
if (other) {
&nb
sp; put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
如果驅動已注冊則返回-EBUSY。
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_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) {
&
o-hansi-font-family: 'Times New Roman'">同樣內核提供driver_unregister()函數(shù)撤銷驅動注冊,這里不再分析。
至此,已經(jīng)清楚了設備驅動模型的device、driver和bus,在此基礎上就可以輕松的去分析各個模塊的驅動程序了^_^!
nbsp; 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);
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);
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
http://msnpiki.msnfanatic.com/index.php/Main_Page-->
t"> printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", __func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
return 0;
out_unregister:
kfree(drv->p);
drv->p = NULL;
kobject_put(&priv->kobj);
out_put_bus:
bus_put(bus);
return error;
}
簡單的分析一下:為drv分配內存并初始化,創(chuàng)建文件系統(tǒng)中相應的目錄、屬性文件和鏈接,調用driver_attach()匹配總線上的設備,將驅動注冊到bus上,產(chǎn)生一個kobject_uevent() ADD事件。詳細的過程請參考內核源碼。
http://msnpiki.msnfanatic.com/index.php/Main_Page-->