OFN鼠标驱动(十一) -- I2C驱动的总结

 

在分析完drivers\base\文件夹下的内容之后,我们对驱动的基本操作已经有了一个大概的印象,现在,我们再重新复习一遍IIC设备的注册过程,用一个实例来将这条线融会贯通,同样的,用我们熟悉的ds1337为例来说说明,这一章的内容就比较散了,将会跳转在几大文件之间,所以要看明白这一章的内容,那么前面的东西就得先消化消化了。

 

其实驱动的注册过程,在网上以probe函数的调用过程为关键字做搜索,可以找到很多资料,将其过程描述得很清楚,所以我这里的很多内容,会和网上一些资料大部分雷同。

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

流程小记:

 

1、  I2C设备的注册(DS13337),注册的驱动为ds1337_driver

2、  驱动的注册是在i2c-core.c中调用i2c_register_driver函数实现的,这里要特别注意的是,设置驱动所属的总线为i2c_bus_type,该总线的函数实现都在本文件中找。设置完成后,调用driver_register对设备驱动进行注册,注意传递给注册函数时的驱动结构成员已经变成了如下格式:

       .driver = {

              .name      = "ds1337",

              .owner    = THIS_MODULE

              .bus        = &i2c_bus_type

       },

3、  driver_register中的工作很简单,只是检查一下驱动和总线是否重复设置了probe等函数,如果重复设置了,则打印一个警告信息提示使用者,将使用总线上的操作函数。然后将驱动关联的设备链表挂空(这个概念请参考代码分析文件drivers\base\dd.c)。最后调用bus_add_driver函数将驱动添加到总线上

4、  在bus_add_driver函数的实现中,核心的操作有:

Ø         驱动drv->kobj.kset的设置——&bus->drivers,以及kobj的注册

Ø         如果总线有autoprobe属性,则执行driver_attach(drv),由于I2C驱动不是用probe探测的,所以不会probe函数,更不会有这个属性,所以以下(5、6可以跳过)

Ø         klist_add_tail(&drv->knode_bus, &bus->klist_drivers);将驱动节点挂到总线的驱动链表中,完成驱动注册的实际操作

Ø         为驱动创建一些属性操作文件

5、  driver_attach的具体实现是:遍历总线bus的设备链表klist_devices,使每一个设备都执行一次__driver_attach(dev, data),这里的dev是当前设备,data则是驱动地址drv(因为一个驱动可以关联上很多设备,所以这里将遍历总线上的所有设备进行探测,而不是探测到一个后就中断循环)。而这个函数的具体实现只有一句,即先判断dev->driver是否指定,如果没有关联上,则执行driver_probe_device(drv, dev)关联设备和驱动。

6、  在driver_probe_device中,就有对总线关联函数的调用了

Ø         bus->match函数的调用,i2c_device_match

Ø         bus->probe或drv->probe(really_probe函数中),i2c_device_probe

Ø         以上两个函数调用成功,则认为该设备可以绑定该驱动,则最终调用klist_add_tail(&dev->knode_driver,        &dev->driver->klist_devices);将设备关联到驱动的设备链表中

 

 

 

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

一切一切的入口,都从ds1337.c的驱动注册开始:

static int        __init     ds1337_init(void)

{

       return     i2c_add_driver(&ds1337_driver);

}

 

关联一下驱动:

static struct    i2c_driver      ds1337_driver = {

       .driver = {

              .name      = "ds1337",

       },

       .attach_adapter       = ds1337_attach_adapter,

       .detach_client         = ds1337_detach_client,

       .command             = ds1337_command,

};

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

我们关联一下注册函数的实现(include\linux\i2c.h):

static inline int      i2c_add_driver(struct i2c_driver *driver)

{

       return     i2c_register_driver(THIS_MODULE, driver);

}

 

//drivers\i2c\i2c-core.c

int   i2c_register_driver(struct module *owner,   struct i2c_driver *driver)

{

       int res;

 

       if (is_newstyle_driver(driver)) {   //这个宏的代码很简单,就不COPY了

              //如果driver设置了probe或remove函数,则不能再设置下面三个函数

              if (driver->attach_adapter || driver->detach_adapter    || driver->detach_client) {

                     return     -EINVAL;

              }

       }

 

       //设置驱动所属模块,设置驱动所属的总线

       driver->driver.owner = owner;

       driver->driver.bus = &i2c_bus_type;           //这里我们要特别记住这个总线,因为base中的操作会直接调用这个总线设置的函数

 

       //调用base中的函数注册驱动,因为下面的函数比较简单,所以我们先看一下后面的函数,然后在到这个函数里面看他具体的实现过程

       //这里要注意的是,传入的参数是driver->driver,加上上面两行代码设置的参数,也就只是:

       .driver = {

              .name      = "ds1337",

              .owner    = THIS_MODULE

              .bus        = &i2c_bus_type

       },

       res = driver_register(&driver->driver);

       if (res)    return res;

 

       //加锁

       mutex_lock(&core_lists);

 

       list_add_tail(&driver->list,&drivers);          //将驱动的节点加入到静态链表drivers中

      

       //如果驱动关联了attach_adapter函数,进该if

       //本驱动中有设置这个函数:.attach_adapter     = ds1337_attach_adapter,所以将取出适配器adapters中所有的adapter,并执行驱动的attach_adapter函数(适配器绑定)

       if (driver->attach_adapter) {

              struct i2c_adapter *adapter;

 

              list_for_each_entry(adapter, &adapters, list) {

                     driver->attach_adapter(adapter);

                     //在本范例中,这里调用的是ds1337.c中的ds1337_attach_adapter函数

              }

       }

 

       mutex_unlock(&core_lists);

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

我们大概了解了i2c驱动注册的过程,然后我们将具体的范例代入代码中,再分析一次驱动的注册函数driver_register(drivers\base\driver.c)

 

int   driver_register(struct device_driver * drv)

{

       if ((drv->bus->probe && drv->probe) ||

           (drv->bus->remove && drv->remove) ||

           (drv->bus->shutdown && drv->shutdown)) {

              //打印警告:将执行总线的probe,remove,shutdown函数

       }

       klist_init(&drv->klist_devices, NULL, NULL);          //驱动上没有关联设备

       return     bus_add_driver(drv);

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

继续,进drivers\base\bus.c中看bus_add_driver的具体实现

再复习一次,这里的参数drv的数据内容只是如下:

.driver = {

              .name      = "ds1337",

              .owner    = THIS_MODULE

              .bus        = &i2c_bus_type

       },

 

int   bus_add_driver(struct device_driver    *drv)

{

       //驱动的总线引用加1,这里得到的是i2c_bus_type,其关联的代码在drivers\i2c\i2c-core.c

       struct bus_type * bus = bus_get(drv->bus);

       int error = 0;

 

       if (!bus)         return -EINVAL;                  //驱动没有所属的总线,错误返回

       error = kobject_set_name(&drv->kobj, "%s", drv->name); //设置kobj为”ds1337”

       drv->kobj.kset = &bus->drivers;                 //驱动kobj的set宿主为总线的驱动链表

       error = kobject_register(&drv->kobj);         //注册kobj

 

       //如果驱动所属的总线有自动探测属性,则这里会执行自动探测函数

       //虽然i2c总线上没有设置这个属性,但是为了了解驱动的一般过程,所以我们也会看进这个函数中

       //由于后面的函数比较简单,为了保持函数分析的完成,我们先看后面的代码

       if (drv->bus->drivers_autoprobe) {

              error = driver_attach(drv);

              if (error)        goto out_unregister;

       }

 

       //将驱动的节点添加到总线的驱动链表中

       //注1:这里的总线是I2C的总线——i2c_bus_type

       //注2:这一步操作,实际也就是注册的实质——链表的挂接

       //注3:虽然在驱动中并没有设置knode_bus,但因为取驱动实际只是取结构体中的一个成员,然后根据其成员反取整个结构体(container_of的操作对玩LINUX的人来说应该不陌生吧),所以knode_bus成员是否设置有值其实并无意义,有意义的只是该地址是属于哪个结构体。

       klist_add_tail(&drv->knode_bus, &bus->klist_drivers);

 

//这个函数在编译到内核中时候为空函数,所以我们认为他不是关键的部分,暂时不做研究

       module_add_driver(drv->owner, drv);

 

       //这三个属性操作文件的创建在分析drivers\base\bus.c时已经分析过了

       error = driver_create_file(drv, &driver_attr_uevent);  //为驱动关联属性操作函数

       error = driver_add_attrs(bus, drv);              //将总线的属性继承给驱动

       error = add_bind_files(drv);                //给驱动绑定bind和unbind函数

       return     error;

 

out_unregister:

       kobject_unregister(&drv->kobj);

out_put_bus:

       bus_put(bus);

       return error;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

我们看看driver_attach的实现,drivers\base\dd.c中:

 

int   driver_attach(struct device_driver * drv)

{

       return     bus_for_each_dev(drv->bus,        NULL,   drv,        __driver_attach);

}

 

@@@@@

drivers\base\bus.c中

int   bus_for_each_dev(

       struct bus_type      * bus,                          // i2c_bus_type

       struct device          * start,                         //NULL

       void                     * data,                         //驱动

       int (*fn)(struct device *, void *))                //__driver_attach

{

       struct klist_iter      i;

       struct device          * dev;

       int                        error = 0;

 

       if (!bus)  return -EINVAL;                         //没有所属总线,错误返回

 

       //初始化klist的遍历,i->cur = NULL,即从头开始

       klist_iter_init_node(&bus->klist_devices,    &i,

                          (start ? &start->knode_bus : NULL));

 

       //遍历总线上的每一个设备,执行fn函数,这里的fn就是:__driver_attach

       while ((dev = next_device(&i)) && !error)

              error = fn(dev, data);

       klist_iter_exit(&i);

       return error;

}

 

@@@@@

我们现在回过头来看看__driver_attach的具体实现,也是在drivers\base\bus.c中

 

//这里的参数,dev为总线上提取出来的设备,data为驱动,也就是ds1337的drv

//可见,关键的函数就一句:如果设备的驱动没有关联,则执行driver_probe_device函数

static int __driver_attach(struct device * dev,      void * data)

{

       struct device_driver      * drv = data;

 

       if (dev->parent)     down(&dev->parent->sem);

       down(&dev->sem);

       if (!dev->driver)    driver_probe_device(drv, dev);

       up(&dev->sem);

       if (dev->parent)     up(&dev->parent->sem);

 

       return 0;

}

 

@@@@@

继续探进去,在drivers\base\dd.c中

 

int   driver_probe_device(struct device_driver * drv, struct device * dev)

{

       int ret = 0;

 

       if (!device_is_registered(dev))      return -ENODEV;         //设备未注册,错误返回

 

       //执行总线的match函数对设备和驱动进行匹配

       if (drv->bus->match && !drv->bus->match(dev, drv))

              goto done;

 

       ret = really_probe(dev, drv);

 

done:

       return ret;

}

 

@@@@@

继续,同一文件中

 

static int        really_probe(struct device *dev,   struct device_driver *drv)

{

       int ret = 0;

 

       atomic_inc(&probe_count);                 //有probe工作正在进行

       WARN_ON(!list_empty(&dev->devres_head));   //设备的资源链表有资源,出警告

 

       dev->driver = drv;                              //试探性的绑上驱动

       if (driver_sysfs_add(dev)) {                //添加驱动和设备的link文件

              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;

       }

 

       //成功,设备和驱动完成绑定

       //其核心实现代码:klist_add_tail(&dev->knode_driver,   &dev->driver->klist_devices);

       driver_bound(dev);

       ret = 1;

       goto done;

 

probe_failed:

       //探测失败的代码就不复制了,无非就是之前创建的文件的删除

       ret = 0;

done:

       atomic_dec(&probe_count);

       wake_up(&probe_waitqueue);

       return ret;

}

 

至此,驱动的注册工作便全部完成。

至于ds1337_attach_adapter的具体实现,之前的代码已经有过分析了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值