c. I2C设备驱动
前面已经说过I2C设备层驱动有两种实现方式,我们选择用户模式设备驱动方式,这种驱动依赖I2C子系统中的i2c-dev这个驱动。I2C设备驱动主要填充i2c_driver和i2c_client结构体,同时提供read,write,ioctl等API供应用层使用。在分析设备驱动层的时候,我们要留意设备驱动层怎么找到总线驱动层相应的适配器的。
让我们先一起来填充一下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,字符设备还有read,write,ioctl。我们主要分析一下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_RETRIES、I2C_TIMEOUT和I2C_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_driver和i2c_client结构体,然后注册了i2c_driver,在i2c_driver中,我们探测了适配器。同时为了给用户提供API,我们还注册了一个字符设备,在字符设备中的open函数中,我们完成了i2c_client结构体的填充,并获取了匹配的适配器。最后我们讲了ioctl函数,重点分析了I2C_RDWR标签下最终调用适配器的I2C通信协议函数master_xfer完成数据传输。
|