Linux停止自动匹配,浅析linux 2.6.23驱动自动匹配设备driver_attach()函数

//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);

}

以上的理解还缺少各种知识的汇集性(因为自己还缺少很多知识),甚至会存在若干错误性,当然正如开花的植物一样,一根藤条上需要开的花分几种,有已经绽开的,有含苞待放的,有蓄势待发的,还有10天以后才能显露的,那就让时间来完善吧,只要植物不被若干因素kill掉,我相信花会慢慢的开将来(gliethttp_20071025小感).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值