OFN鼠标驱动(四) -- i2c-core.c文件的分析

 

在IIC驱动(一)中,我们初步分析了需要移植部分的代码,当然还留下了不少未解之谜,为了把这些问题给弄清楚,所以本部分我们分析一下I2C-CORE.C的代码。

 

分析之前先看一下I2C驱动的结构图:

一条I2C线上可以挂很多个I2C设备,每一条I2C线对应一个适配器(Adapter),每一个I2C设备对应一个Client。

简单点理解,adapter就是一个集合,里面包含了多个client。

 

 

分析完这个文件之后,发现这里文件中又运用了以下几个新的知识点:

1、  completion

2、  IDR。

3、  Linux设备模型

 

分析完这份文件之后,又产出了一个新的问题:algo是怎么挂到适配器上的?

 

 

 

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

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

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

struct      i2c_devinfo {

       struct list_head              list;

       int                               busnum;

       struct i2c_board_info     board_info;

};

 

static      LIST_HEAD(adapters);                      //初始化链表adapters(I2C适配器)

static      LIST_HEAD(drivers);                        //初始化链表drivers(设备)

static      DEFINE_MUTEX(core_lists);             //初始化互斥锁core_lists

static      DEFINE_IDR(i2c_adapter_idr);          //IDR,暂时没有深入研究,先忽略,用的时候再说

 

//检查结构体中,probe和remove成员是否被设置(新风格这两个函数被设置)

#define   is_newstyle_driver(d)    ((d)->probe || (d)->remove)

 

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

先看两个宏:

#define   to_i2c_client(d)     container_of(d,      struct i2c_client,    dev)

 

static int               i2c_device_match(

       struct device   *dev,

       struct device_driver      *drv)

{

       //根据dev,获取该dev所在的i2c_client结构体

       //struct i2c_client {…    struct device dev;   …}

       struct i2c_client      *client = to_i2c_client(dev);        

       //根据drv获取i2c_driver结构体

       //struct i2c_driver {…    struct device_driver driver;    …}

       struct i2c_driver     *driver = to_i2c_driver(drv);

 

       //不是新的风格的驱动,直接返回

       if (!is_newstyle_driver(driver))

              return 0;

 

       //新风格的驱动要比较设备名

       return     strcmp(client->driver_name,        drv->name) == 0;

}

 

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

//热插拔检测

//这个函数分析有点不清楚

static int               i2c_device_uevent(

       struct device   *dev,

       struct kobj_uevent_env *env)

{

       struct i2c_client      *client = to_i2c_client(dev);

 

       //设备有驱动,但 无驱名,不需要检测

       if (dev->driver || !client->driver_name)

              return 0;

 

       if (add_uevent_var(env,        "MODALIAS=%s",      client->driver_name))

              return -ENOMEM;

       dev_dbg(dev, "uevent\n");

       return 0;

}

 

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

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

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

新风格中用到的探测函数

static int               i2c_device_probe(struct device *dev)

{

       //根据设备获取对应的结构体

       struct i2c_client      *client = to_i2c_client(dev);

       struct i2c_driver     *driver = to_i2c_driver(dev->driver);

 

       //如果没有probe函数,返回错误

       if (!driver->probe)

              return -ENODEV;

 

       //关联上驱动

       client->driver = driver;

       dev_dbg(dev, "probe\n");

       return     driver->probe(client);           //执行探测功能

}

 

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

新风格中用到的remove函数

static int               i2c_device_remove(struct device *dev)

{

       struct i2c_client      *client = to_i2c_client(dev);

       struct i2c_driver     *driver;

       int                        status;

 

       //设备没有关联上驱动

       if (!dev->driver)

              return 0;

 

       driver = to_i2c_driver(dev->driver);

       if (driver->remove) {    //如果驱动有指定remove函数,则执行

              dev_dbg(dev, "remove\n");

              status = driver->remove(client);

       } else {                        //如果驱动没有关联remove函数,则将设备的驱动直接挂空

              dev->driver = NULL;

              status = 0;

       }

 

       //status为0,client没有挂驱动

       if (status == 0)

              client->driver = NULL;

 

       return status;

}

 

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

static void      i2c_device_shutdown(struct device      *dev)

{

       struct i2c_driver *driver;

 

       if (!dev->driver)

              return;

       driver = to_i2c_driver(dev->driver);

       if (driver->shutdown)

              driver->shutdown(to_i2c_client(dev));

}

 

i2c_device_suspend

i2c_device_resume

 

以上几个函数的结构都是类似的,从传入参数中获取参数所在的结构体,然后执行具体设备指定的函数

 

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

static void      i2c_client_release(struct device *dev)

{

       struct i2c_client     *client = to_i2c_client(dev);

       complete(&client->released);

}

 

//释放设备

static void      i2c_client_dev_release(struct device *dev)

{

       kfree(to_i2c_client(dev));

}

 

显示设备的name

show_client_name

 

显示设备对应的驱动的name

show_modalias

 

//定义一个数组,用途不明,后面用到时再看

//看变量名应该是设备属性

static struct device_attribute i2c_dev_attrs[]

 

//最后,定义一个总线变量,将刚才的函数操作都归类进去

static struct bus_type     i2c_bus_type = {

       .name             = "i2c",

       .dev_attrs              = i2c_dev_attrs,

       .match            = i2c_device_match,

       .uevent           = i2c_device_uevent,

       .probe            = i2c_device_probe,

       .remove          = i2c_device_remove,

       .shutdown       = i2c_device_shutdown,

       .suspend         = i2c_device_suspend,

       .resume          = i2c_device_resume,

};

 

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

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

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

//新建一个I2C设备的client(一个client表示一个设备端)

struct i2c_client     *i2c_new_device(

       struct i2c_adapter *adap,                  //适配器

       struct i2c_board_info const   *info)     //板子属性

{

       struct i2c_client      *client;

       int                        status;

 

       //申请一块内存,用于保存一个I2C设备的client

       client = kzalloc(sizeof *client,      GFP_KERNEL);

       if (!client)

              return NULL;

 

       client->adapter = adap;         //关联上适配器

 

       client->dev.platform_data = info->platform_data;

       device_init_wakeup(&client->dev, info->flags & I2C_CLIENT_WAKE);

 

       client->flags = info->flags & ~I2C_CLIENT_WAKE;

       client->addr = info->addr;

       client->irq = info->irq;

       //上面这部分的代码是设置client的一些参数,当然,这些参数的来源目前我们还不知道,先放放

 

       strlcpy(client->driver_name, info->driver_name,

              sizeof(client->driver_name));

       strlcpy(client->name, info->type, sizeof(client->name));

 

       //探测设备client是否存在

       //这个函数在本文件后一点

       status = i2c_attach_client(client);

       if (status < 0) {             //设备不存在,释放内存

              kfree(client);

              client = NULL;

       }

       return client;

}

EXPORT_SYMBOL_GPL(i2c_new_device);

 

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

//卸载设备

void       i2c_unregister_device(struct i2c_client        *client)

{

       //从设备端中获取适配器和驱动

       struct i2c_adapter   *adapter = client->adapter;

       struct i2c_driver     *driver = client->driver;

 

       //驱动存在 但是旧风格的驱动,打印警告并返回

       if (driver && !is_newstyle_driver(driver)) {

              dev_err(&client->dev, "can't unregister devices "

                     "with legacy drivers\n");

              WARN_ON(1);

              return;

       }

 

       mutex_lock(&adapter->clist_lock);

       list_del(&client->list);                        //将本client从适配器链表中删除

       mutex_unlock(&adapter->clist_lock);

 

       device_unregister(&client->dev);         //设备注销

}

EXPORT_SYMBOL_GPL(i2c_unregister_device);

 

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

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

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

总线适配器操作

 

//调用适配器的dev_released函数

static void      i2c_adapter_dev_release(struct device *dev)

{

       struct i2c_adapter *adap = to_i2c_adapter(dev);

       complete(&adap->dev_released);        

       //唤醒complete(complete是一种同步机制)

       //这里应该是唤醒adap->dev_released函数

}

 

显示适配器名

static ssize_t    show_adapter_name(

       struct device   *dev,                    //设备

       struct device_attribute   *attr,      //设备属性

       char              *buf)                    //缓存

{

       struct i2c_adapter *adap = to_i2c_adapter(dev);

       return     sprintf(buf, "%s\n", adap->name);

}

 

static struct device_attribute i2c_adapter_attrs[] = {

       __ATTR(name, S_IRUGO, show_adapter_name, NULL),

       { },

};

 

//设置适配器关联函数

static struct class    i2c_adapter_class = {

       .owner                  = THIS_MODULE,

       .name                    = "i2c-adapter",

       .dev_attrs                     = i2c_adapter_attrs,

};

 

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

//扫描板子上静态的I2C设备

//这个函数应该是扫描板子上已知的I2C设备,所以是静态扫描

static void      i2c_scan_static_board_info(struct i2c_adapter     *adapter)

{

       struct i2c_devinfo   *devinfo;

 

       mutex_lock(&__i2c_board_lock);        //互斥锁

 

       //遍历链表头为__i2c_board_list的链表,取出devinfo成员

       //devinfo->list是该设备在__i2c_board_list中的成员

       list_for_each_entry(devinfo,        &__i2c_board_list,       list) {

              //注意,这里有一个C的基础知识,在这个if语句里面,只有第一个条件满足了,才会执行第二个条件的判断,也就是说,只有nr与busnum匹配了,才会执行i2c_new_device函数

              if (devinfo->busnum == adapter->nr           //适配器的nr和设备信息匹配

                     && !i2c_new_device(adapter, &devinfo->board_info))      //新建一个设备

                     //出错

                     printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",

                            i2c_adapter_id(adapter),

                            devinfo->board_info.addr);

       }

       mutex_unlock(&__i2c_board_lock);     //解锁

}

 

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

//注册适配器,同时探测目标板上静态注册的I2C设备和已经注册的I2C设备

static int        i2c_register_adapter(struct i2c_adapter        *adap)

{

       int res = 0;

       struct list_head   *item;

       struct i2c_driver  *driver;

 

       mutex_init(&adap->bus_lock);

       mutex_init(&adap->clist_lock);           //两个互斥锁

       INIT_LIST_HEAD(&adap->clients);    //初始化clients链表

 

       mutex_lock(&core_lists);                    //上锁(文件头初始化该锁)

       list_add_tail(&adap->list, &adapters);  //将adap->list加入到适配器链表中

 

       //如果适配器父节点为空

       if (adap->dev.parent == NULL) {

              //platform_bus定义在drivers/base/Platform.c中

              adap->dev.parent = &platform_bus;     //将适配器挂上设备总线

              pr_debug("I2C adapter driver [%s] forgot to specify "

                      "physical device\n", adap->name);

       }

       sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);   //显示适配器的nr(序号)

       //给适配器关联上操作对象:release函数和类(名字和属性,前文有分析)

       adap->dev.release = &i2c_adapter_dev_release;

       adap->dev.class = &i2c_adapter_class;               

 

       res = device_register(&adap->dev);      //将适配器设备注册进内核

       if (res)

              goto       out_list;                        //出错

 

       dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

 

       /* create pre-declared device nodes for new-style drivers */

       //__i2c_first_dynamic_bus_num是一个分界线,小于该值的,说明是目标板上的静态I2C设备(drivers\i2c\i2c-boardinfo.c中i2c_register_board_info函数下有对应的代码操作这个变量)

       if (adap->nr  <  __i2c_first_dynamic_bus_num)

              i2c_scan_static_board_info(adap);        //是板上的设备,用静态扫描的方法注册

 

       /* let legacy drivers scan this bus for matching devices */

       //这里是执行旧方法注册的I2C设备的探测函数

       //遍历drivers链表,依次取出他的成员

       list_for_each(item, &drivers) {

              //取出item所在的i2c_driver结构体指针

              driver = list_entry(item, struct i2c_driver, list);

              if (driver->attach_adapter)

                     driver->attach_adapter(adap);              //如果该驱动有指定attach_adapter,则执行他

       }

 

out_unlock:

       mutex_unlock(&core_lists);                 //解锁

       return res;

 

//适配器设备注册失败,直接到这里

out_list:

       list_del(&adap->list);            //从adapters链表中将本适配器删除

       idr_remove(&i2c_adapter_idr, adap->nr);    //在添加adapter函数中有申请一个IDR号,所以这里要释放(见i2c_add_adapter函数)

       goto       out_unlock;

}

 

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

//添加一个适配器

int   i2c_add_adapter(struct i2c_adapter      *adapter)

{

       int   id, res = 0;

 

retry:

       //获取一个IDR号(这也解释了为什么上一个函数注册adapter失败后要idr_remove了)

       //简单用SI关联了一下这个函数的实现,他是用kmem_cache_alloc申请了一块内存,失败则返回0

       if (idr_pre_get(&i2c_adapter_idr,        GFP_KERNEL) == 0)

              return      -ENOMEM;

      

//这里COPY网上的一段描述:在这里涉及到一个idr结构.idr结构本来是为了配合page cache中的radix tree而设计的.在这里我们只需要知道,它是一种高效的搜索树,且这个树预先存放了一些内存.避免在内存不够的时候出现问题.所在,在往idr中插入结构的时候,首先要调用idr_pre_get()为它预留足够的空闲内存,然后再调用idr_get_new_above()将结构插入idr中,该函数以参数的形式返回一个id.以后凭这个id就可以在idr中找到相对应的结构了

//注意一下idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id)的参数的含义,它是将adapter结构插入到i2c_adapter_idr中,存放位置的id必须要大于或者等于__i2c_first_dynamic_bus_num

       mutex_lock(&core_lists);

       /* "above" here means "above or equal to", sigh */

       res = idr_get_new_above(&i2c_adapter_idr,       adapter,

                            __i2c_first_dynamic_bus_num,    &id);

       mutex_unlock(&core_lists);

 

       //出错处理

       if (res < 0) {

              if (res == -EAGAIN)

                     goto retry;

              return res;

       }

 

       //将idr_get_new_above获得的ID号放入adapter->nr中

       //从这个函数的操作可以知道,这时候,nr>=__i2c_first_dynamic_bus_num

       adapter->nr = id;

       //注册适配器,有了上面的保证,这里注册的适配器不会去搜索板上的静态I2C设备

       return     i2c_register_adapter(adapter);      

}

EXPORT_SYMBOL(i2c_add_adapter);

 

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

添加一个适配器

这个函数和i2c_add_adapter的驱动在于,i2c_add_adapter保证了适配器的nr大于目标板的静态nr,从而不扫描目标板上的静态I2C设备,而这个函数是从输入adap的nr开始分配新的nr

int   i2c_add_numbered_adapter(struct i2c_adapter *adap)

{

       int   id;

       int   status;

 

       if (adap->nr & ~MAX_ID_MASK)

              return -EINVAL;

 

retry:

       //为IDR分配内存

       if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)

              return -ENOMEM;

 

       //我们先来看下idr_get_new_above的原型

       //int        idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id)

       // idp: 之前通过idr_init初始化的idr指针
       //id: 由内核自动分配的ID号
       //ptr: 和ID号相关联的指针
       //start_id: 起始ID号.内核在分配ID号时,会从start_id开始

       mutex_lock(&core_lists);

       status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);

       if (status == 0 && id != adap->nr) {

              status = -EBUSY;

              idr_remove(&i2c_adapter_idr, id);

       }

       mutex_unlock(&core_lists);

       if (status == -EAGAIN)

              goto retry;

 

       if (status == 0)

              status = i2c_register_adapter(adap);      //注册设备

       return status;

}

EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);

 

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

删除适配器

int   i2c_del_adapter(struct i2c_adapter       *adap)

{

       struct list_head     *item, *_n;

       struct i2c_adapter *adap_from_list;

       struct i2c_driver    *driver;

       struct i2c_client     *client;

       int                        res = 0;

 

       mutex_lock(&core_lists);

 

       /* First make sure that this adapter was ever added */

       //从adapters链表中找到指定的适配器

       list_for_each_entry(adap_from_list, &adapters, list) {

              if (adap_from_list == adap)

                     break;

       }

       //没有找到适配器

       if (adap_from_list != adap) {

              pr_debug("i2c-core: attempting to delete unregistered "

                      "adapter [%s]\n", adap->name);

              res = -EINVAL;

              goto       out_unlock;

       }

 

       //从drivers列表中提取每一个list_head成员(主要是执行驱动的detach_adapter函数)

       //而实际上,至少在ds1337上,是没指定这个函数的

       list_for_each(item, &drivers) {

              //取出item成员所在的宿主结构体

              driver = list_entry(item,       struct i2c_driver,   list);

              //如果驱动的detach_adapter成员有设置,则执行他

              if (driver->detach_adapter)   

                     if ((res = driver->detach_adapter(adap))) {

                            dev_err(&adap->dev, "detach_adapter failed "

                                   "for driver [%s]\n",

                                   driver->driver.name);

                            goto       out_unlock;

                     }

       }

 

       //_n = item->next

       //遍历挂在适配器adap上的所有clients

       //新风格的驱动,卸载设备

       //旧风格的驱动,执行驱动的detach_client函数

       list_for_each_safe(item, _n, &adap->clients) {

              struct i2c_driver     *driver;

 

              //获取item所在的i2c_client宿主

              client = list_entry(item, struct i2c_client, list);

              driver = client->driver;         //获取client的驱动

 

              //驱动不存在,或者是新风格的驱动

              if (!driver || is_newstyle_driver(driver)) {

                     //从适配器上卸载本client,函数的实现在前文有分析,主要步骤是断开client->list的链表连接,并卸载client->dev(device_unregister())

                     i2c_unregister_device(client);

                     continue;

              }

 

              //运行到这里,说明驱动存在,且为旧风格的驱动

              if ((res = driver->detach_client(client))) {    //执行驱动的detach_client函数

                     dev_err(&adap->dev, "detach_client failed for client "

                            "[%s] at address 0x%02x\n", client->name,

                            client->addr);

                     goto       out_unlock;

              }

       }

 

       /* clean up the sysfs representation */

       init_completion(&adap->dev_released);       //初始化completion同步机制

       device_unregister(&adap->dev);                 //将适配器设备卸载

       list_del(&adap->list);                                 //将本适配器从adapters链表中断开

 

       /* wait for sysfs to drop all references */

       //等待文件系统调用本适配器的release函数

       wait_for_completion(&adap->dev_released);

 

       /* free bus id */

       //释放本适配器占用的IDR结构内存(i2c_add_adapter添加adapter时申请,在注册失败的时候也会idr_remove)

       idr_remove(&i2c_adapter_idr,     adap->nr);

 

       dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);

 

//没有找到适配器,直接到这里

 out_unlock:

       mutex_unlock(&core_lists);

       return     res;

}

EXPORT_SYMBOL(i2c_del_adapter);

 

程序分析到这里,我们基本已经可以理清楚I2C驱动的结构了。

S3C2440的I2C总线上有一个adapters链表,而目标板上的I2C设备,则是按功能分为一个个adapter,每增加一个adapter,则执行以下操作:

1、  申请一个IDR用于快速的找到本adapter。

2、  将adapter作为一个设备注册到内核中

3、  将adapter挂载到adapters链表上

 

然后新增一个I2C设备时,相当于新加了一个client,这时候要执行以下操作:

1、  为client关联上这个设备的驱动操作函数,以及适配器

2、  将client关联到父adapter的clients链表中。

 

所以卸载适配器的时候,就执行了以下操作:

1、  在adapters中遍历,找到要删除的adapter。

2、  从驱动链表drivers中遍历所有的驱动,执行驱动关联的detach_adapter函数。一般这个函数没有关联,而参数adapter则是传递父adapter给detach_adapter,这个应该是作为本函数是否执行的一个依据。

3、  遍历adapter的clients链表,找出挂在本adapter下的所有设备,然后卸载他们。

4、  等待文件系统调用release函数来最后释放适配器(也就是说,在文件系统调用release之前,本adapter只是处于僵死状态)

5、  卸载adapter设备并释放IDR内存

 

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

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

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

I2C驱动注册,在I2C设备注册时调用

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

{

       int res;

 

       //驱动检查,因为新旧两种风格只能指定一种

       //新风格只需要指定probe和remove

       if (is_newstyle_driver(driver)) {

              if (driver->attach_adapter || driver->detach_adapter

                            || driver->detach_client) {

                     printk(KERN_WARNING

                                   "i2c-core: driver [%s] is confused\n",

                                   driver->driver.name);

                     return -EINVAL;

              }

       }

 

       /* add the driver to the list of i2c drivers in the driver core */

       //这部分代码是给新风格的驱动注册用的

       driver->driver.owner = owner;

       driver->driver.bus = &i2c_bus_type;    //i2c_bus_type关联了I2C总线支持的操作函数

 

       //注册驱动

       res = driver_register(&driver->driver);

       if (res)

              return res;

 

       mutex_lock(&core_lists);

 

       list_add_tail(&driver->list,    &drivers);      //将驱动挂载到drivers驱动链表上

       pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

 

       //这个操作是旧风格的驱动注册

       if (driver->attach_adapter) {                       //如果适配器的探测函数存在

              struct i2c_adapter *adapter;

 

              //从适配器链表adapters中取出每一个适配器

              list_for_each_entry(adapter, &adapters,     list) {

                     //调用探测函数

                     //从这里可以看出,这个函数的作用是探测驱动属于哪一个适配器的

                     driver->attach_adapter(adapter);

              }

       }

 

       mutex_unlock(&core_lists);

       return 0;

}

EXPORT_SYMBOL(i2c_register_driver);

 

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

删除驱动,卸载时调用

void       i2c_del_driver(struct i2c_driver   *driver)

{

       struct list_head   *item1, *item2, *_n;

       struct i2c_client  *client;

       struct i2c_adapter *adap;

 

       mutex_lock(&core_lists);

 

       //新风格驱动的处理

       if (is_newstyle_driver(driver))

              goto       unregister;

 

       //旧风格驱动的处理

       list_for_each(item1,       &adapters) {

              //从适配器链表中获取每一个适配器

              adap = list_entry(item1,       struct i2c_adapter, list);

 

              //调用卸载适配器探测(至少DS1337驱动上没有挂载这个函数的实现)

              if (driver->detach_adapter) {       

                     if (driver->detach_adapter(adap)) {

                            dev_err(&adap->dev, "detach_adapter failed "

                                   "for driver [%s]\n",

                                   driver->driver.name);

                     }

              } else {

                     //如果没有detach_adapter函数,则从适配器的clients链表中找驱动

                     list_for_each_safe(item2, _n, &adap->clients) {

                            client = list_entry(item2, struct i2c_client, list);

                            if (client->driver != driver)

                                   continue;        //不是指定的驱动,下一个

 

                            //找到指定的驱动(其实主要是找这个驱动的宿主client)

                            dev_dbg(&adap->dev, "detaching client [%s] "

                                   "at 0x%02x\n", client->name,

                                   client->addr);

 

                            //执行detach_client函数

                            if (driver->detach_client(client)) {

                                   dev_err(&adap->dev, "detach_client "

                                          "failed for client [%s] at "

                                          "0x%02x\n", client->name,

                                          client->addr);

                            }

                     }

              }

       }

 

//新风格时直接到这里

 unregister:

       driver_unregister(&driver->driver);            //卸载驱动

       list_del(&driver->list);                               //将驱动从drivers链表中删除

       pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name);

 

       mutex_unlock(&core_lists);

}

EXPORT_SYMBOL(i2c_del_driver);

 

看完这个函数,有点小疯,难怪要更改新的风格呢,按旧风格的做法,卸载的效率太低了。

 

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

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

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

这个函数是检查适配器adapter中是否已经存在地址为addr的设备。不存在则返回0

static int               __i2c_check_addr(

       struct i2c_adapter *adapter,

       unsigned int          addr)

{

       struct list_head   *item;

       struct i2c_client  *client;

 

       //遍历适配器的clients链表,取出他的每一个设备

       list_for_each(item, &adapter->clients) {

              client = list_entry(item,        struct i2c_client, list);    //获取设备的宿主client

              if (client->addr == addr)

                     return     -EBUSY;        //找到设备地址相同的设备,错误返回

       }

       return 0;

}

 

这个是上一个函数的对外接口

static int        i2c_check_addr(

       struct i2c_adapter *adapter,

       int                        addr)

{

       int   rval;

 

       mutex_lock(&adapter->clist_lock);

       rval = __i2c_check_addr(adapter, addr);

       mutex_unlock(&adapter->clist_lock);

 

       return rval;

}

 

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

检测设备的client,其核心操作就是将设备的client挂载到宿主adapter的clients链表上

int   i2c_attach_client(struct i2c_client        *client)

{

       struct i2c_adapter   *adapter = client->adapter;           //获取设备的宿主适配器

       int res = 0;

 

       mutex_lock(&adapter->clist_lock);

 

       //检查设备地址是否已经存在于适配器中

       //如果已经存在则直接返回错误

       //如果地址不冲突,则将新的设备client加入到适配器的clients链表中

       if (__i2c_check_addr(client->adapter, client->addr)) {

              res = -EBUSY;

              goto       out_unlock;

       }

       list_add_tail(&client->list,&adapter->clients);

 

       client->usage_count = 0;                     //本设备的使用者计数(好象没用到)

 

       client->dev.parent = &client->adapter->dev;       //本设备的宿主设备=适配器设备

       client->dev.bus = &i2c_bus_type;               //复制设备的I2C总线支持(主要是I2C支持的公用操作)

 

       if (client->driver)

              client->dev.driver = &client->driver->driver;     

       //这个地方要返回到ds1337的文件里看驱动结构体的定义才行,不然会晕

       //实际上,driver->driver里面只有一个.name成员用来标记驱动的名字

 

       //关联上release操作函数

       if (client->driver && !is_newstyle_driver(client->driver)) {

              client->dev.release = i2c_client_release;       //关联上release函数

              client->dev.uevent_suppress = 1;

       } else

              client->dev.release = i2c_client_dev_release;

 

       snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),

              "%d-%04x", i2c_adapter_id(adapter), client->addr);

       dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",

              client->name, client->dev.bus_id);

 

       //将client设备注册

       res = device_register(&client->dev);

       if (res)

              goto       out_list;          //注册失败

       mutex_unlock(&adapter->clist_lock);

 

       //如果adapter中有指定client_register函数,则执行

       //实际上,到目前为止,暂时没有看到这个函数有被指定

       if (adapter->client_register)  {

              if (adapter->client_register(client)) {

                     dev_dbg(&adapter->dev, "client_register "

                            "failed for client [%s] at 0x%02x\n",

                            client->name, client->addr);

              }

       }

 

       return 0;

 

//client设备注册失败,直接到这里

out_list:

       list_del(&client->list);          //将自己从adapter->clients链表中断开

       dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "

              "(%d)\n", client->name, client->addr, res);

 

//适配器中已经有相同地址的设备时候直接进入这里

out_unlock:

       mutex_unlock(&adapter->clist_lock);

       return     res;

}

EXPORT_SYMBOL(i2c_attach_client);

 

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

//卸载驱动时会调用这个函数

int   i2c_detach_client(struct i2c_client       *client)

{

       struct i2c_adapter *adapter = client->adapter;

       int res = 0;

 

       //如果使用者不为0,警告设备仍然被使用

       if (client->usage_count > 0) {

              dev_warn(&client->dev, "Client [%s] still busy, "

                      "can't detach\n", client->name);

              return -EBUSY;

       }

 

       //如果适配器上有这个函数指定,则执行他

       //实际到目前为止,还未看到这个函数的挂载

       if (adapter->client_unregister)  {

              res = adapter->client_unregister(client);

              if (res) {

                     dev_err(&client->dev,

                            "client_unregister [%s] failed, "

                            "client not detached\n", client->name);

                     goto out;

              }

       }

 

       //断开client在适配器上的链接

       //设备卸载

       //等待文件系统调用released函数

       mutex_lock(&adapter->clist_lock);

       list_del(&client->list);

       init_completion(&client->released);

       device_unregister(&client->dev);

       mutex_unlock(&adapter->clist_lock);

       wait_for_completion(&client->released);

 

 out:

       return res;

}

EXPORT_SYMBOL(i2c_detach_client);

 

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

//增加驱动的引用计数

int   i2c_use_client(struct i2c_client *client)

{

       int ret;

 

       //这里调用try_module_get来检测模块是否被插入内核

       ret = i2c_inc_use_client(client);

       if (ret)

              return ret;

 

       client->usage_count++;

 

       return 0;

}

EXPORT_SYMBOL(i2c_use_client);

 

//减少驱动的引用计数

int   i2c_release_client(struct i2c_client *client)

{

       if (!client->usage_count) {

              pr_debug("i2c-core: %s used one too many times\n",

                      __FUNCTION__);

              return -EPERM;

       }

 

       client->usage_count--;

       i2c_dec_use_client(client);

 

       return 0;

}

EXPORT_SYMBOL(i2c_release_client);

 

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

执行适配器中,设备驱动所支持的”.command”函数

void       i2c_clients_command(

       struct i2c_adapter *adap,

       unsigned int          cmd,

       void                     *arg)

{

       struct list_head  *item;

       struct i2c_client *client;

 

       mutex_lock(&adap->clist_lock);

       list_for_each(item, &adap->clients) {

              client = list_entry(item, struct i2c_client, list);     //获取设备的client结构体

              //如果模块没有被插入内核,则这里返回0

              if (!try_module_get(client->driver->driver.owner))

                     continue;

 

              //驱动的”.command”成员不为空

              if (NULL != client->driver->command) {

                     mutex_unlock(&adap->clist_lock);

                     client->driver->command(client,cmd,arg);   //执行命令

                     mutex_lock(&adap->clist_lock);

              }

              //这里发现一个问题,command是对该适配器的每一个client发出的,如果两个client的cmd参数冲突,那么其中一个就会被误执行,所以写命令参数的时候,要注意避免冲突

              module_put(client->driver->driver.owner);

     }

       mutex_unlock(&adap->clist_lock);

}

EXPORT_SYMBOL(i2c_clients_command);

 

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

static int        __init     i2c_init(void)

{

       int   retval;

 

       retval = bus_register(&i2c_bus_type);         //注册I2C总线

       if (retval)

              return     retval;

       return     class_register(&i2c_adapter_class);       //类注册

}

 

static void __exit i2c_exit(void)

{

       class_unregister(&i2c_adapter_class);

       bus_unregister(&i2c_bus_type);                  //卸载总线和类

}

 

subsys_initcall(i2c_init);

module_exit(i2c_exit);

 

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

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

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

这里有个疑问,adapter->algo是什么时候给挂上去的?

 

//数据的通讯,主要是调用了adap->algo->master_xfer函数

int   i2c_transfer(  

       struct i2c_adapter *       adap,

       struct i2c_msg              *msgs,

       int                               num)

{

       int ret;

 

       if (adap->algo->master_xfer) {

              mutex_lock_nested(&adap->bus_lock, adap->level);

              ret = adap->algo->master_xfer(adap,msgs,num);

              mutex_unlock(&adap->bus_lock);

 

              return ret;

       } else {

              dev_dbg(&adap->dev, "I2C level transfers not supported\n");

              return      -ENOSYS;

       }

}

EXPORT_SYMBOL(i2c_transfer);

 

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

这里调用了i2c_transfer

int   i2c_master_send(

       struct i2c_client     *client,

       const char             *buf ,

       int                        count)

{

       int ret;

       struct i2c_adapter *adap=client->adapter;            //获取设备的宿主适配器

       struct i2c_msg       msg;

 

       msg.addr = client->addr;

       msg.flags = client->flags & I2C_M_TEN;

       msg.len = count;

       msg.buf = (char *)buf;

 

       ret = i2c_transfer(adap, &msg, 1);

       return (ret == 1) ? count : ret;

}

EXPORT_SYMBOL(i2c_master_send);

 

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

int   i2c_master_recv(

       struct i2c_client     *client,

       char                     *buf ,

       int                        count)

{

       struct i2c_adapter *adap=client->adapter;

       struct i2c_msg msg;

       int ret;

 

       msg.addr = client->addr;

       msg.flags = client->flags & I2C_M_TEN;

       msg.flags |= I2C_M_RD;

       msg.len = count;

       msg.buf = buf;

 

       ret = i2c_transfer(adap, &msg, 1);

 

       return (ret == 1) ? count : ret;

}

EXPORT_SYMBOL(i2c_master_recv);

 

小结:

i2c_master_send

i2c_master_recv

这两个函数都是调用了i2c_transfer来进行最终的数据操作,而send和recv函数几乎是一样的,唯一的区别在于对msg.flag的设置,所以我们现在虽然还没有具体分析i2c_transfer中核心函数master_xfer的实现方法,但是从这里基本可以猜测出,msg的作用——设置I2C SMBUS的读写方式。

 

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

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

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

//在适配器中检查地址为addr的设备是否存在。(主要是靠是否返回ACK判断)

//核心函数为i2c_smbus_xfer

static int               i2c_probe_address(

       struct i2c_adapter *adapter,

       int                        addr,

       int                        kind,

       int (*found_proc) (struct i2c_adapter *, int, int))

{

       int err;

 

       //检查I2C设备地址有效

       if (addr < 0x03 || addr > 0x77) {

              dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",

                      addr);

              return -EINVAL;

       }

 

       //检查设备地址addr是否已经在适配器中

       if (i2c_check_addr(adapter, addr))

              return 0;

 

       //类型(kind)未知

       //i2c_smbus_xfer应该是实际操作I2C时序的实现函数,这里用来探测I2C设备有没有返回ACK

       if (kind < 0) {

              if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,

                               I2C_SMBUS_QUICK, NULL) < 0)

                     return 0;

 

              //如果地址是0x5x,再检查一次

              if ((addr & ~0x0f) == 0x50)

                     i2c_smbus_xfer(adapter, addr, 0, 0, 0,

                                   I2C_SMBUS_QUICK, NULL);

       }

 

       //运行到这里,说明已经找到了设备(至少I2C有回ACK),调用设备传进来的回调函数found_proc

       err = found_proc(adapter, addr, kind);

 

       //出错的处理

       if (err == -ENODEV)

              err = 0;

 

       if (err)

              dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",

                      addr, err);

       return err;

}

 

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

//探测函数,IIC驱动(一)的时候已经分析过这个函数了,这里就不重新COPY了

int   i2c_probe(

       struct i2c_adapter                       *adapter,

       struct i2c_client_address_data      *address_data,

       int (*found_proc) (struct i2c_adapter *, int, int))

{

       return 0;

}

EXPORT_SYMBOL(i2c_probe);

 

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

//探测是否有新的I2C设备,探测目标设备的地址写在addr_list中

struct i2c_client     *i2c_new_probed_device(

       struct i2c_adapter         *adap,

       struct i2c_board_info    *info,

       unsigned short const      *addr_list)

{

       int i;

 

       //检测adapter是否支持这个函数

       if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) {

              dev_err(&adap->dev, "Probing not supported\n");

              return NULL;

       }

 

       mutex_lock(&adap->clist_lock);

       for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {

              //检测地址有效

              if (addr_list[i] < 0x03 || addr_list[i] > 0x77) {

                     dev_warn(&adap->dev, "Invalid 7-bit address "

                             "0x%02x\n", addr_list[i]);

                     continue;

              }

 

              //检测地址是否已存在

              if (__i2c_check_addr(adap, addr_list[i])) {

                     dev_dbg(&adap->dev, "Address 0x%02x already in "

                            "use, not probing\n", addr_list[i]);

                     continue;

              }

 

              //核心代码

              if ((addr_list[i] & ~0x07) == 0x30

               || (addr_list[i] & ~0x0f) == 0x50

               || !i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK)) {

                     if (i2c_smbus_xfer(adap, addr_list[i], 0,

                                      I2C_SMBUS_READ, 0,

                                      I2C_SMBUS_BYTE, NULL) >= 0)

                            break;

              } else {

                     if (i2c_smbus_xfer(adap, addr_list[i], 0,

                                      I2C_SMBUS_WRITE, 0,

                                      I2C_SMBUS_QUICK, NULL) >= 0)

                            break;

              }

       }

       mutex_unlock(&adap->clist_lock);

 

       if (addr_list[i] == I2C_CLIENT_END) {

              dev_dbg(&adap->dev, "Probing failed, no device found\n");

              return NULL;

       }

 

       info->addr = addr_list[i];

       return     i2c_new_device(adap, info);         //新建一个设备

}

EXPORT_SYMBOL_GPL(i2c_new_probed_device);

 

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

//根据ID号取出对应的adapter

struct i2c_adapter*        i2c_get_adapter(int id)

{

       struct i2c_adapter *adapter;

 

       mutex_lock(&core_lists);

       adapter = (struct i2c_adapter *)idr_find(&i2c_adapter_idr,        id);

       if (adapter && !try_module_get(adapter->owner))

              adapter = NULL;

 

       mutex_unlock(&core_lists);

       return adapter;

}

EXPORT_SYMBOL(i2c_get_adapter);

 

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

void       i2c_put_adapter(struct i2c_adapter *adap)

{

       module_put(adap->owner);

}

EXPORT_SYMBOL(i2c_put_adapter);

 

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

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

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

SMBUS操作部分

 

//计算data的crc8值

//关于CRC算法,可以BAIDU相关资料,或看我STM32笔记的第0章,里面有具体介绍

#define   POLY    (0x1070U << 3)

static u8  crc8(u16 data)

{

       int i;

 

       for(i = 0; i < 8; i++) {

              if (data & 0x8000)

                     data = data ^ POLY;

              data = data << 1;

       }

       return (u8)(data >> 8);

}

 

//连续的CRC计算

static u8 i2c_smbus_pec(u8 crc, u8 *p,    size_t count)

{

       int i;

 

       for(i = 0; i < count; i++)

              crc = crc8((crc ^ p[i]) << 8);

       return crc;

}

 

//计算msg中数据的CRC值(含设备地址)

static u8         i2c_smbus_msg_pec(u8 pec,        struct i2c_msg *msg)

{

       //这里是设置第一个字节: addr<<1 | 读写标记(1-读,0-写)

       u8 addr = (msg->addr << 1) |      !!(msg->flags & I2C_M_RD);

       pec = i2c_smbus_pec(pec, &addr, 1);          //计算一次crc

 

       return     i2c_smbus_pec(pec, msg->buf, msg->len);

}

 

//将上面函数计算好的CRC值添加进帧(pec)中

static inline void    i2c_smbus_add_pec(struct i2c_msg      *msg)

{

       msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg);

       msg->len++;

}

 

这一组函数看到这里,基本是明白了作者想干什么了——就是给I2C通讯的数据里面加一个字节的校验位,来保证传输数据的可靠。

 

//不出所料,这个函数就用来检查CRC是否一致了

static int               i2c_smbus_check_pec(u8 cpec,    struct i2c_msg *msg)

{

       u8   rpec = msg->buf[--msg->len];             //取最后一个字节(CRC字节)

       cpec = i2c_smbus_msg_pec(cpec, msg);       //计算msg中的crc

 

       //比较crc是否一致

       if (rpec != cpec) {

              pr_debug("i2c-core: Bad PEC 0x%02x vs. 0x%02x\n",

                     rpec, cpec);

              return -1;

       }

 

       return 0;

}

 

 

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

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

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

以下这组函数,实际都是调用i2c_smbus_xfer来实现I2C的实际物理操作,只是不同的函数,传递进i2c_smbus_xfer的参数不一样而已。

s32         i2c_smbus_write_quick(struct i2c_client *client, u8 value)

s32         i2c_smbus_read_byte(struct i2c_client *client)

s32         i2c_smbus_write_byte(struct i2c_client *client, u8 value)

s32         i2c_smbus_read_byte_data(struct i2c_client *client, u8 command)

s32         i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value)

s32         i2c_smbus_read_word_data(struct i2c_client *client, u8 command)

s32         i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value)

s32         i2c_smbus_read_block_data(struct i2c_client *client, u8 command, u8 *values)

s32         i2c_smbus_write_block_data(struct i2c_client *client, u8 command,

                     u8 length, const u8 *values)

s32         i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command,

                     u8 length, u8 *values)

s32         i2c_smbus_write_i2c_block_data(struct i2c_client *client, u8 command,

                     u8 length, const u8 *values)

 

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

分析这个函数之前,我们先来看一个结构体:

#define   I2C_SMBUS_BLOCK_MAX       32    /* As specified in SMBus standard */

union i2c_smbus_data {

       __u8      byte;

       __u16     word;

       __u8      block[I2C_SMBUS_BLOCK_MAX + 2];

              /* block[0] is used for length */

           /* and one more for user-space compatibility */

};

 

s32         i2c_smbus_xfer(

       struct i2c_adapter *       adapter,                //适配器

       u16                             addr,                    //I2C设备地址

       unsigned short              flags,                    //操作标记

      char                           read_write,           //读/写

       u8                               command,             //

       int                               size,                      //操作字节大小

    union i2c_smbus_data * data)                            //数据指针

{

       s32 res;

 

       //只保留这两个标记位

       flags &= I2C_M_TEN | I2C_CLIENT_PEC;

 

       if (adapter->algo->smbus_xfer) {               

              //适配器smbus_xfer函数存在,执行适配器的smbus_xfer函数

              mutex_lock(&adapter->bus_lock);

              res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,

                                              command,size,data);

              mutex_unlock(&adapter->bus_lock);

       } else                                                       //函数不存在,直接透传下去

              res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,

                                             command,size,data);

 

       return res;

}

EXPORT_SYMBOL(i2c_smbus_xfer);

 

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

static s32       i2c_smbus_xfer_emulated(

       struct i2c_adapter * adapter,                     //适配器

       u16                      addr,                                   //I2C设备地址

     unsigned short       flags,                           //操作标记

      char                     read_write,                  //读写

       u8                        command,                    //命令(寄存器地址)

       int                        size,                             //操作几个字节

      union i2c_smbus_data   * data)                  //操作数据

{

       unsigned char        msgbuf0[I2C_SMBUS_BLOCK_MAX+3];

       unsigned char        msgbuf1[I2C_SMBUS_BLOCK_MAX+2];

       int   num = read_write == I2C_SMBUS_READ?2:1;         //读为2,写为1

       struct i2c_msg       msg[2] = {

              //地址,标记,长度,缓存

              { addr,    flags,                           1,    msgbuf0 },     //写操作字节

             { addr,    flags | I2C_M_RD,       0,    msgbuf1 }      //读操作字节

       };

       int i;

       u8   partial_pec = 0;

 

       msgbuf0[0] = command;                     //写操作的第一个字节为命令(寄存器地址)

       switch(size) {

       case I2C_SMBUS_QUICK:          //0

              msg[0].len = 0;                    //写长度为0

              //是否加读属性

              msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;

              num = 1;                             //操作为写

              break;

 

       case I2C_SMBUS_BYTE:            //1

              if (read_write == I2C_SMBUS_READ) {

                     msg[0].flags = I2C_M_RD | flags;              //加读属性

                     num = 1;                      //操作为写

              }

              break;

       case I2C_SMBUS_BYTE_DATA: //2

              if (read_write == I2C_SMBUS_READ)

                     //读操作,长度为1(第一个字节为command),不理解的话参考一下I2C通讯协议的读操作时序

                     msg[1].len = 1;            

              else {

                     msg[0].len = 2;             //写操作,长度为2

                     msgbuf0[1] = data->byte;      //要写的数据

              }

              break;

       case I2C_SMBUS_WORD_DATA:       //3

              if (read_write == I2C_SMBUS_READ)

                     msg[1].len = 2;             //读操作,长度为2

              else {

                     msg[0].len=3;               //写操作,长度为3,设置要写的数据

                     msgbuf0[1] = data->word & 0xff;

                     msgbuf0[2] = data->word >> 8;

              }

              break;

       case I2C_SMBUS_PROC_CALL:        //4

              num = 2;                                   //特殊操作,固定为读

              read_write = I2C_SMBUS_READ;      //读写属性改为读

              msg[0].len = 3;                           //写操作长度为3

              msg[1].len = 2;                           //读操作长度为2

              msgbuf0[1] = data->word & 0xff;

              msgbuf0[2] = data->word >> 8;    //要写的数据

              break;

 

       case I2C_SMBUS_BLOCK_DATA:

              if (read_write == I2C_SMBUS_READ) {

                     msg[1].flags |= I2C_M_RECV_LEN;

                     msg[1].len = 1;                   //读长度

              } else {

                     msg[0].len = data->block[0] + 2;  //写长度(多加上目标地址)

                     if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {

                            dev_err(&adapter->dev, "smbus_access called with "

                                   "invalid block write size (%d)\n",

                                   data->block[0]);

                            return -1;

                     }

                     for (i = 1; i < msg[0].len; i++)

                            msgbuf0[i] = data->block[i-1];     //设置要写的数据

              }

              break;

       case I2C_SMBUS_BLOCK_PROC_CALL:

              num = 2;                                           //固定为读

              read_write = I2C_SMBUS_READ;      //读操作

              if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {

                     dev_err(&adapter->dev, "%s called with invalid "

                            "block proc call size (%d)\n", __FUNCTION__,

                            data->block[0]);

                     return -1;

              }

              msg[0].len = data->block[0] + 2;

              for (i = 1; i < msg[0].len; i++)

                     msgbuf0[i] = data->block[i-1];

              msg[1].flags |= I2C_M_RECV_LEN;

              msg[1].len = 1;

              break;

       case I2C_SMBUS_I2C_BLOCK_DATA:      //读一块数据

              if (read_write == I2C_SMBUS_READ) {

                     msg[1].len = data->block[0];

              } else {

                     msg[0].len = data->block[0] + 1;

                     if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {

                            dev_err(&adapter->dev, "i2c_smbus_xfer_emulated called with "

                                   "invalid block write size (%d)\n",

                                   data->block[0]);

                            return -1;

                     }

                     for (i = 1; i <= data->block[0]; i++)

                            msgbuf0[i] = data->block[i];

              }

              break;

       default:

              dev_err(&adapter->dev, "smbus_access called with invalid size (%d)\n",

                     size);

              return -1;

       }

 

       //带CRC校验标记

       //大小不为0(I2C_SMBUS_QUICK)

       //大小不为8(I2C_SMBUS_I2C_BLOCK_DATA)

       i = ((flags & I2C_CLIENT_PEC)

              && (size       !=   I2C_SMBUS_QUICK)

              && (size       !=   I2C_SMBUS_I2C_BLOCK_DATA));

 

       //以上条件成立(带CRC校验属性)

       if (i) {

              if (!(msg[0].flags & I2C_M_RD)) {     //不带读属性

                     if (num == 1)               /* Write only */

                            i2c_smbus_add_pec(&msg[0]);     //计算CRC校验字节进最后一个字节

                     else                      /* Write followed by read */

                            partial_pec = i2c_smbus_msg_pec(0, &msg[0]);

                                                 //返回msg中数据的CRC字节

              }

             

              if (msg[num-1].flags & I2C_M_RD)    //带读属性,多读一个字节的校验

                     msg[num-1].len++;

       }

 

       //通讯主体

       if (i2c_transfer(adapter, msg, num) < 0)

              return -1;

 

       /* Check PEC if last message is a read */

       if (i &&  (msg[num-1].flags & I2C_M_RD)) {        //带读属性,CRC检查

              if (i2c_smbus_check_pec(partial_pec, &msg[num-1]) < 0)

                     return -1;

       }

 

       //读操作,将读到的数据复制到data中

       if (read_write == I2C_SMBUS_READ)

              switch(size) {

                     case I2C_SMBUS_BYTE:

                            data->byte = msgbuf0[0];

                            break;

                     case I2C_SMBUS_BYTE_DATA:

                            data->byte = msgbuf1[0];

                            break;

                     case I2C_SMBUS_WORD_DATA:

                     case I2C_SMBUS_PROC_CALL:

                            data->word = msgbuf1[0] | (msgbuf1[1] << 8);

                            break;

                     case I2C_SMBUS_I2C_BLOCK_DATA:

                            for (i = 0; i < data->block[0]; i++)

                                   data->block[i+1] = msgbuf1[i];

                            break;

                     case I2C_SMBUS_BLOCK_DATA:

                     case I2C_SMBUS_BLOCK_PROC_CALL:

                            for (i = 0; i < msgbuf1[0] + 1; i++)

                                   data->block[i] = msgbuf1[i];

                            break;

              }

       return 0;

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值