歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

driver

最近在看一個mpc8315 CPU上的驅動程序發現在使用spi_register注冊完成後沒有調用到相應的probe函數,分析後發現在driver_attach()函數執行時沒有找到匹配的device,在網上狗狗後找到關於這部分的分析,引用如下:

個淺析linux 2.6.23驅動自動匹配設備driver_attach()函數
文章來源:http://gliethttp.cublog.cn
int driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
調用該函數,那麼drv驅動程式會和drv所在總線上連接了的物理設備進行一一匹配,再來看看下面:
int bus_for_each_dev(struct bus_type * bus, struct device * start,
void * data, int (*fn)(struct device *, void *))
{
struct klist_iter i;//專門用於遍歷的鏈表結構體,其中i_cur是遍歷移動的關鍵
struct device * dev;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->klist_devices, &i,
(start ? &start->knode_bus : NULL));
//i->i_klist = &bus->klist_devices;
//i->i_head = &bus->klist_devices.k_list;
//i->i_cur = NULL;//表示從最前端開始遍歷掛接到bus總線上的整個設備鏈條.
while ((dev = next_device(&i)) && !error)
//dev為該bus總線鏈表上的一個設備,[就像一根籐條上的一朵小花gliethttp_20071025]
//這些device設備把自己的&device->knode_bus鏈表單元鏈接到了bus->klist_devices上
//這也說明名字為knode_bus的list單元將是要被掛接到bus->klist_devices的鏈表上
//同理&device->knode_driver將是這個device設備鏈接到drivers驅動上的list節點識別單元
//見driver_bound()->klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
error = fn(dev, data);//調用__driver_attach函數,進行匹配運算
klist_iter_exit(&i);
return error;//成功匹配返回0
}
struct klist_iter {
struct klist * i_klist;
struct list_head * i_head;
struct klist_node * i_cur;
};
void klist_iter_init_node(struct klist * k, struct klist_iter * i, struct klist_node * n)
{
i->i_klist = k; //需要被遍歷的klist
i->i_head = &k->k_list; //開始的鏈表頭
i->i_cur = n; //當前位置對應的klist_node節點,next_device()會從當前n開始一直搜索到
//鏈表的結尾,也就是i_head->prev處停止
if (n)
kref_get(&n->n_ref);//引用計數加1
}
static struct device * next_device(struct klist_iter * i)
{
struct klist_node * n = klist_next(i);
return n ? container_of(n, struct device, knode_bus) : NULL;
//因為n是device->knode_bus的指針,所以container_of將返回device的指針
}
struct klist_node * klist_next(struct klist_iter * i)
{
struct list_head * next;
struct klist_node * lnode = i->i_cur;
struct klist_node * knode = NULL;//賦0,當next == i->i_head時用於退出
void (*put)(struct klist_node *) = i->i_klist->put;
spin_lock(&i->i_klist->k_lock);
if (lnode) {
next = lnode->n_node.next;
if (!klist_dec_and_del(lnode))//釋放前一個i_cur對象的引用計數
put = NULL;//klist_dec_and_del成功的對引用計數做了減1操作,那麼失效用戶定義put
} else
next = i->i_head->next;//如果lnode=0,那麼從鏈表頭開始,所以head->next指向第1個實際對象
if (next != i->i_head) {//head並不鏈接設備,所以head無效
//當next == i->i_head時,說明已遍歷到了head牽頭的鏈表的末尾,回環到了head,
//所以knode將不會進行賦值,這時knode=0,while ((dev = next_device(&i)) && !error)因為0而退出
knode = to_klist_node(next);//調用container_of()獲取klist_node->n_node中klist_node地址
kref_get(&knode->n_ref);//對該node的引用計數加1
}
i->i_cur = knode;//記住當前遍歷到的對象,當next == i->i_head時,knode=0
spin_unlock(&i->i_klist->k_lock);
if (put && lnode)
put(lnode);
return knode;
}
static int klist_dec_and_del(struct klist_node * n)
{
return kref_put(&n->n_ref, klist_release);//對該node的引用計數減1,如果引用計數到達0,那麼調用klist_release
}
static void klist_release(struct kref * kref)
{
struct klist_node * n = container_of(kref, struct klist_node, n_ref);
list_del(&n->n_node);//從節點鏈表上摘掉該node節點
complete(&n->n_removed);//
n->n_klist = NULL;
}
void fastcall complete(struct completion *x)
{
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags);//關閉中斷,防止並發
x->done++;
//喚醒因為某些原因懸停在klist_node->n_removed等待隊列上的task們
//這種現象之一是:__device_release_driver()刪除掛接在設備上的driver時,會出現
//刪除task小憩在node的wait上
__wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,
1, 0, NULL);
spin_unlock_irqrestore(&x->wait.lock, flags);//恢復中斷
}
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, int sync, void *key)
{
struct list_head *tmp, *next;
list_for_each_safe(tmp, next, &q->task_list) {//遍歷以head牽頭的鏈表上的task們
wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list);
unsigned flags = curr->flags;
if (curr->func(curr, mode, sync, key) &&//調用wait上准備好了的回調函數func
(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
}
}
//拋開鏈表上的head,當最後一個post==head時,說明鏈表已遍歷結束(gliethttp_20071025)
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
void klist_iter_exit(struct klist_iter * i)
{
if (i->i_cur) {
//對於正常遍歷的退出,i->i_cur會等於0,如果找到了匹配對象,提前退出了,那麼就會在這裡對引用進行釋放
klist_del(i->i_cur);
i->i_cur = NULL;
}
}
static int __driver_attach(struct device * dev, void * data)
{
struct device_driver * drv = data;
//data就是打算把自己匹配到bus上掛接的合適設備上的driver驅動
if (dev->parent)
down(&dev->parent->sem);//使用信號量保護下面的操作
down(&dev->sem);
if (!dev->driver)//如果當前這個dev設備還沒有掛接一個driver驅動
driver_probe_device(drv, dev);//那麼嘗試該dev是否適合被該drv驅動管理
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
int ret = 0;
if (!device_is_registered(dev))//設備是否已被bus總線認可
return -ENODEV;
if (drv->bus->match && !drv->bus->match(dev, drv))
//調用該driver驅動自定義的match函數,如:usb_device_match(),查看
//這個設備是否符合自己,drv->bus->match()返回1,表示本drv認可該設備
//否則,goto done,繼續檢測下一個device設備是否和本drv匹配
goto done;
pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
//這下來真的了,
ret = really_probe(dev, drv);
done:
return ret;
}
static inline int device_is_registered(struct device *dev)
{
return dev->is_registered;//當調用bus_attach_device()之後,is_registered=1
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("%s: Probing driver %s with device %s\n",
drv->bus->name, drv->name, dev->bus_id);
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;//管理本dev的驅動指針指向drv
if (driver_sysfs_add(dev)) {//將driver和dev使用link,鏈接到一起,使他們真正相關
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__FUNCTION__, dev->bus_id);
goto probe_failed;
}
if (dev->bus->probe) {//總線提供了設備探測函數
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {//驅動自己提供了設備探測函數
//因為drv驅動自己也不想管理那些意外的非法設備
//所以一般drv都會提供這個功能,相反
//比如:usb_bus_type沒有提供probe,而usb驅動提供了usb_probe_interface
//來確認我這個driver軟件真的能夠管理這個device設備
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("%s: Bound Device %s to Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
if (ret != -ENODEV && ret != -ENXIO) {
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev->bus_id, ret);
}
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
static void driver_bound(struct device *dev)
{
if (klist_node_attached(&dev->knode_driver)) {
//本dev已掛到了某個driver驅動的klist_devices鏈條上了
//感覺不應該發生
printk(KERN_WARNING "%s: device %s already bound\n",
__FUNCTION__, kobject_name(&dev->kobj));
return;
}
pr_debug("bound device ’%s’ to driver ’%s’\n",
dev->bus_id, dev->driver->name);
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->bus_notifier,
BUS_NOTIFY_BOUND_DRIVER, dev);
//將本dev的knode_driver鏈表結構體節點掛接到該driver->klist_devices上
//這樣driver所管理的device設備又多了1個,
//也能說又多了1個device設備使用本driver驅動管理他自己(gilethttp_20071025).
klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
}

Copyright © Linux教程網 All Rights Reserved