本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/z2007b/archive/2011/05/17/6428000.aspx
//*********************************************************************************
Linux內核大講堂 (一) 設備驅動的基石驅動模型(5)
上節我們已經領教了傳說中的bus_register,這節我們繼續領教同樣是神級的driver_register。
driver_register如果看懂了,device自行分析應該沒太大的問題。
照樣先給出一個小的例子代碼。
typedef struct __wwhs_device_driver{
char *name;
struct device_driver driver;
}wwhs_device_driver;
static wwhs_device_driver wwhs_driver = {
.name= "wwhs_driver",
};
static int wwhs_driver_register(wwhs_device_driver *wwhs_driver)
{
wwhs_driver->driver.name = wwhs_driver->name;
wwhs_driver->driver.bus=&wwhs_bus_type;
return driver_register(&wwhs_driver->driver);
}
就這麼幾行,在這裏給大家佈置個小作業,請把這個自行加到上一節我們給出的代碼中合適的位置,注意釋放哦.^_^
其實主要就是一個函數啦,這個函數不是我寫的,是高手寫的,高手寫的肯定得膜拜一下,我們接下來就一起膜拜一下高手的作品,先給出函數定義:
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);
other = driver_find(drv->name, drv->bus);
if (other) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting.../n", drv->name);
return -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;
}
首先我們可以看到上面
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);
這一堆都是嚇人的,可以不用理它們。
接下來
other = driver_find(drv->name, drv->bus);
if (other) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting.../n", drv->name);
return -EBUSY;
}
這個其實就是查找之前是否有被註冊過,我們現在顯然不會先對這王八蛋進行分析,我們要抓重點,節省精力,要不然晚上哪有時間陪MM約會,哪有時間玩遊戲,哪有時間看電影…
接下來就是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);
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 */
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:
kobject_put(&priv->kobj);
kfree(drv->p);
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
下面這一段:
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);
if (error)
goto out_unregister;
其實就是爲我們的帥哥driver同學的私有成員driver_private分配了空間並且做了一些初始化的動作,前面如果仔細看了文章的同志應該具備了自行分析的能力,看懂肯定是沒問題的。但是這中間有一個成員是大家都會感興趣的,這個就是我們device_driver帥哥爲device美女準備的一排金絲鳥籠,這個帥哥很花心的,只要合他自已口味的美女都會拉過來放到金絲鳥籠裏,說了這麼久,這藏美女的鳥籠是哪位呢?君請看:
klist_init(&priv->klist_devices, NULL, NULL);就是它了,只要合乎帥哥口味的美女,全都被帥哥養在這裏面。
接下來:
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
記得我們在bus_register中講到的會創建六個文件吧,在這裏我們有一個就派上用場了,這就是drivers_autoprobe,這個文件我們是可以讀寫的。如果我們往裏面寫1:
echo “1” > /sys/bus/wwhs_bus/drivers_autoprobe
那麼就會調用driver_attach(drv)。
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
在這裏要說一下klist,klist是在內核的雙向鏈表的基礎上封裝出來的,這段代碼的意思就是輪詢klist_devices鏈表中的節點並取節點內含的大美女device成員。然後將其與帥哥device_driver一同作爲參數傳給回調函數fn,而fn就是我們之前傳入的__driver_attach()。我們在這裏假設bus裏面已經有一位或N位美女( 如果一位美女都沒有,就會判斷直接返回了,也就沒有了下面的故事),
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
在這個回調函數裏,首先會調用driver_match_device(drv, dev):
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
這個函數的作用其實就是:如果總線的match函數存在,則調用總線的match函數,否則直接返回1。在liunx函數的返回值是很有講究的,大家一定不可忽視了返回值的存在。
接下來就是:
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
在這段代碼中driver_probe_device(drv, dev)是重中之重。
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
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(dev, drv);
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;
}
頂住各位,一起跟哥繼續分析。
atomic_inc(&probe_count);
這個用於給探測計數。
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;
}
把驅動賦值給dev->driver後我們調用了driver_sysfs_add(dev)。
static int driver_sysfs_add(struct device *dev)
{
int ret;
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BIND_DRIVER, dev);
ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
kobject_name(&dev->kobj));
if (ret == 0) {
ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
"driver");
if (ret)
sysfs_remove_link(&dev->driver->p->kobj,
kobject_name(&dev->kobj));
}
return ret;
}
在這段代碼中blocking_notifier_call_chain()函數涉及到一項叫內核通知鏈的技術,其實就是一個回調函數鏈,在call鏈的時候,就會從函數鏈上一個一個取下來並執行。我們先假設函數鏈是空的。
接下來就是利用sysfs_create_link() 在總線所屬的驅動下創建一個設備鏈接和在總線所屬的設備下創建一個驅動鏈接。
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函數中分配的。後面我會拿一個詳盡的例子來分析整個過程。
接下來就是調用driver_bound()了。
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);
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BOUND_DRIVER, dev);
}
在這個函數中:
if (klist_node_attached(&dev->p->knode_driver)) {
printk(KERN_WARNING "%s: device %s already bound/n",
__func__, kobject_name(&dev->kobj));
return;
}
用來判斷設備美女是否已經和驅動帥哥匹配了,如果已經被匹配的話,嘿嘿,我們的設備美女是很專一的,不會再繼續匹配,會直接大叫一聲,我已經嫁人了。
如果兩者都還是單身的,我們就用:
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices)來促成這樁好事。(Klist我已經開了個專題進行講解了:http://blog.csdn.net/z2007b/archive/2011/05/13/6417293.aspx)
然後再call一下內核通知鏈上掛的函數。
這一塊講完了,我們再回到bus_add_driver()函數中,繼續往下走。
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers)就是驅動帥哥正式加入帥哥陣營。
module_add_driver(drv->owner, drv)將在在/sys/module創建相應的目錄和文件,這個就自行分析一下吧,很簡單的。
再往下走:
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 */
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);
很熟悉吧,上節講bus_register的時候也有類似的東西。記得嗎?就是創建了幾個具有自定義屬性的文件,並且通知一下用戶空間。記不起來的話就看上一節。做人不能忘本,不能喜新厭舊,再一次高歌:結識新朋友,不忘老朋友…
至此,driver_register()就講完了。不要貪多,如果沒看懂,回過頭再看,直到看懂爲止。
下一節更精彩,不要錯過哦。^_^