您的位置:首页 > 其它

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

2011-08-17 23:11 387 查看
在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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: