2259|0

310

帖子

1

TA的资源

一粒金砂(中级)

楼主
 

I2C驱动程序框架源码分析(2) [复制链接]

c. I2C设备驱动
前面已经说过I2C设备层驱动有两种实现方式,我们选择用户模式设备驱动方式,这种驱动依赖I2C子系统中的i2c-dev这个驱动。I2C设备驱动主要填充i2c_driveri2c_client结构体,同时提供readwriteioctlAPI供应用层使用。在分析设备驱动层的时候,我们要留意设备驱动层怎么找到总线驱动层相应的适配器的。
让我们先一起来填充一下i2c_driver
static struct i2c_driveri2cdev_driver = {

.driver= {

.name= "dev_driver",

},

.attach_adapter=i2cdev_attach_adapter, //探测适配器函数

.detach_adapter=i2cdev_detach_adapter,

};
那下面就需要看看这个结构体怎么被注册到内核中的了。
static int __initi2c_dev_init(void)
{

intres;

printk(KERN_INFO"i2c /dev entries driver\n");

res= register_chrdev(I2C_MAJOR, "i2c",&i2cdev_fops); //注册字符设备提供API

if(res)

gotoout;

i2c_dev_class= class_create(THIS_MODULE,"i2c-dev"); //注册类

if(IS_ERR(i2c_dev_class)) {

res= PTR_ERR(i2c_dev_class);

gotoout_unreg_chrdev;

}

res= i2c_add_driver(&i2cdev_driver); //调用核心层函数,注册i2c_driver结构体

if(res)

gotoout_unreg_class;

return0;

out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:

unregister_chrdev(I2C_MAJOR,"i2c");

out:

printk(KERN_ERR"%s: Driver Initialisation failed\n",__FILE__);

returnres;

}
这样,我们的这个I2C设备层驱动就被作为模块加载到内核中了。好了。我们继续看i2c_driver结构体中的i2cdev_attach_adapter怎么来探测适配器的吧。
static inti2cdev_attach_adapter(structi2c_adapter *adap)
{

structi2c_dev *i2c_dev;

intres;

//检查内核中是否注册过了适配器,如果没注册直接返回,注册了适配器那么就返
//回一个指向该适配器的i2c_dev结构体

i2c_dev= get_free_i2c_dev(adap);

if(IS_ERR(i2c_dev))

returnPTR_ERR(i2c_dev);

//注册这个I2C设备到核心层

i2c_dev->dev= device_create(i2c_dev_class, &adap->dev,

MKDEV(I2C_MAJOR,adap->nr), NULL,

"i2c-%d",adap->nr);

if(IS_ERR(i2c_dev->dev)) {

res= PTR_ERR(i2c_dev->dev);

gotoerror;

}

res= device_create_file(i2c_dev->dev, &dev_attr_name); //添加设备属性

if(res)

gotoerror_destroy;

pr_debug("i2c-dev:adapter [%s] registered as minor %d\n",

adap->name,adap->nr);

return0;

error_destroy:

device_destroy(i2c_dev_class,MKDEV(I2C_MAJOR, adap->nr));

error:
return_i2c_dev(i2c_dev);

returnres;

}
这样就完成了适配器的探测,至此,我们填充i2c_driver就分析至此了。接下来我们该去填充i2c_client了。此时,我们发现上面提到在i2c_dev_init里注册了一个字符设备,为我们提供了API,那我们来看看这些API里是否能找到我们填充了的i2c_client结构体呢?
static const structfile_operationsi2cdev_fops = {

.owner=THIS_MODULE,

.llseek= no_llseek,

.read= i2cdev_read,

.write= i2cdev_write,

.unlocked_ioctl= i2cdev_ioctl,

.open= i2cdev_open,

.release= i2cdev_release,

};
有字符设备常识的朋友,对上面代码应该很熟悉了。那我们一个个分析这些API吧。
static inti2cdev_open(struct inode *inode,struct file *file)
{

unsignedint minor = iminor(inode); //由struct inode节点获取次设备号

structi2c_client *client;

structi2c_adapter *adap;

structi2c_dev *i2c_dev;

intret = 0;

lock_kernel();//此处代码其实什么也没做

//将这个次设备号当做IDR机制中的ID查看是否有满足ID=minor的适配器

i2c_dev= i2c_dev_get_by_minor(minor);

if(!i2c_dev) {

ret= -ENODEV;

gotoout;

}

adap= i2c_get_adapter(i2c_dev->adap->nr); //获取匹配的适配器

if(!adap) {

ret= -ENODEV;

gotoout;

}

client= kzalloc(sizeof(*client), GFP_KERNEL); //为i2c_client分配空间

if(!client) {

i2c_put_adapter(adap);

ret= -ENOMEM;

gotoout;

}

snprintf(client->name,I2C_NAME_SIZE, "i2c-dev %d",adap->nr);

client->driver= &i2cdev_driver; //设置i2c_client的驱动

client->adapter=adap; //设置i2c_client的适配器

file->private_data=client; //将i2c_client作为文件的私有数据

out:
unlock_kernel();

returnret;

}
通过上面分析,在i2cdev_open中,我们发现i2c_client的分配并且找到了i2c_client与适配器和设备层驱动的联系。为了给用户提供API,字符设备还有readwriteioctl。我们主要分析一下i2cdev_ioctl这个函数。
static longi2cdev_ioctl(struct file *file,unsigned int cmd, unsigned long arg)
{

structi2c_client *client = (struct i2c_client*)file->private_data;

unsignedlong funcs;

dev_dbg(&client->adapter->dev,"ioctl, cmd=0x%02x,arg=0x%02lx\n",cmd, arg);

switch( cmd ) {

caseI2C_SLAVE:

caseI2C_SLAVE_FORCE:

if((arg > 0x3ff)||

(((client->flags& I2C_M_TEN) == 0) && arg >0x7f))

return-EINVAL;

if(cmd == I2C_SLAVE&& i2cdev_check_addr(client->adapter, arg))

return-EBUSY;

client->addr=arg; //设置从机地址

return0;

caseI2C_TENBIT: //是否有十位地址芯片的设置

if(arg)

client->flags|= I2C_M_TEN;

else

client->flags&= ~I2C_M_TEN;

return0;

caseI2C_PEC:

if(arg)

client->flags|= I2C_CLIENT_PEC;

else

client->flags&= ~I2C_CLIENT_PEC;

return0;

caseI2C_FUNCS: //适配器支持项设置

funcs=i2c_get_functionality(client->adapter);

returnput_user(funcs,(unsigned long __user *)arg);

caseI2C_RDWR: //读写控制

returni2cdev_ioctl_rdrw(client,arg);

caseI2C_SMBUS: //SMBUS总线通信协议设置

returni2cdev_ioctl_smbus(client,arg);

caseI2C_RETRIES: //重试次数设置

client->adapter->retries= arg;

break;

caseI2C_TIMEOUT: //超时时间设置

client->adapter->timeout= msecs_to_jiffies(arg * 10);

break;
default:

return-ENOTTY;

}

return0;

}
i2cdev_ioctl函数中,我们常用I2C_RETRIESI2C_TIMEOUTI2C_RDWR标签,对于I2C_RDWR标签,我们可以发现在此调用了i2cdev_ioctl_rdrw函数,为了清楚怎么进行读写控制,我们继续看看i2cdev_ioctl_rdrw函数吧。
static noinline inti2cdev_ioctl_rdrw(structi2c_client *client, unsigned long arg)
{

structi2c_rdwr_ioctl_data rdwr_arg;

structi2c_msg *rdwr_pa;

u8__user **data_ptrs;

inti, res;

if(copy_from_user(&rdwr_arg, (struct i2c_rdwr_ioctl_data __user*)arg,sizeof(rdwr_arg))) //复制用户空间的i2c_rdwr_ioctl_data到内核空间

return-EFAULT;

if(rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) //限制消息个数

return-EINVAL;

rdwr_pa= (struct i2c_msg*)

kmalloc(rdwr_arg.nmsgs*sizeof(struct i2c_msg),

GFP_KERNEL);

if(!rdwr_pa)

return-ENOMEM;

if(copy_from_user(rdwr_pa, rdwr_arg.msgs,

rdwr_arg.nmsgs* sizeof(struct i2c_msg))) { //拷贝获取 i2c_msg内容

kfree(rdwr_pa);

return-EFAULT;

}

data_ptrs= kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *),GFP_KERNEL);

if(data_ptrs == NULL) { //分配消息缓存区失败

kfree(rdwr_pa);

return-ENOMEM;

}

res= 0;

for(i = 0; i < rdwr_arg.nmsgs; i++) {

if((rdwr_pa.len > 8192) ||

(rdwr_pa.flags& I2C_M_RECV_LEN)) { //限制消息长度

res= -EINVAL;

break;
}

data_ptrs= (u8 __user*)rdwr_pa.buf; //将消息字符串给中间变量

rdwr_pa.buf=kmalloc(rdwr_pa.len, GFP_KERNEL);

if(rdwr_pa.buf == NULL) {

res= -ENOMEM;

break;
}

if(copy_from_user(rdwr_pa.buf,data_ptrs,rdwr_pa.len)) { //将中间变量传给内核中的i2c_msg结构体

++i;/* Needs to be kfreed too */

res=-EFAULT;

break;
}
}

if(res < 0) { //分配空间失败时需呀释放空间资源

intj;

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

kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa);

returnres;

}
//调用核心层i2c_transfer函数完成数据的传输

res= i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);

while(i-- > 0) {

//如果传输的数据后期需要读取,则将传输的数据放在中间变量的保存

if(res >= 0&& (rdwr_pa.flags & I2C_M_RD)) {

if (copy_to_user(data_ptrs,rdwr_pa.buf,rdwr_pa.len))

res= -EFAULT;

}

kfree(rdwr_pa.buf);//释放资源

}
kfree(data_ptrs);
kfree(rdwr_pa);

returnres;

}
我们再来看看i2cdev_ioctl_rdrw这个函数,其实这个函数只是对用户空间传过来的数据进行截取分类存放,然后调用i2c_transfer进行数据传输,我们继续跟踪下去。
int i2c_transfer(structi2c_adapter *adap,struct i2c_msg *msgs, int num)
{

unsignedlong orig_jiffies;

intret, try;

if(adap->algo->master_xfer) {

#ifdefDEBUG

for(ret = 0; ret < num; ret++) {

dev_dbg(&adap->dev,"master_xfer[%d]%c, addr=0x%02x, "

"len=%d%s\n",ret,(msgs[ret].flags & I2C_M_RD)

?'R': 'W', msgs[ret].addr, msgs[ret].len,

(msgs[ret].flags&I2C_M_RECV_LEN) ? "+" : "");

}
#endif

if(in_atomic() || irqs_disabled()) {

ret=mutex_trylock(&adap->bus_lock);

if(!ret)

/*I2Cactivity is ongoing. */

return-EAGAIN;

}else {

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

orig_jiffies= jiffies;

for(ret = 0, try = 0; try <= adap->retries; try++) {

ret=adap->algo->master_xfer(adap, msgs, num); //真正的数据传输

if(ret != -EAGAIN)

break;

if(time_after(jiffies, orig_jiffies +adap->timeout))

break;
}
mutex_unlock(&adap->bus_lock);

returnret;

}else {

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

return-EOPNOTSUPP;

}
}
通过上面的跟踪,我们很快发现i2c_transfer其实是调用了master_xfer函数进行的数据传输。至于master_xfer干什么的,我们再I2C总线层驱动分析中已经讲的很清楚了,master_xfer就是我们适配器的I2C通信协议函数。
好了,I2C的设备驱动层我们也讲完了,回顾一下,I2C的设备驱动层主要是填充了i2c_driveri2c_client结构体,然后注册了i2c_driver,在i2c_driver中,我们探测了适配器。同时为了给用户提供API,我们还注册了一个字符设备,在字符设备中的open函数中,我们完成了i2c_client结构体的填充,并获取了匹配的适配器。最后我们讲了ioctl函数,重点分析了I2C_RDWR标签下最终调用适配器的I2C通信协议函数master_xfer完成数据传输。

点赞 关注

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表