分析完i2c-core后,其实还遗留了不少问题,比如algo的挂载,I2C的ioctl命令的响应等,带着这些问题,我们继续分析i2c-dev.c的代码,看看能否找到一些答案
自旋锁和互斥锁:这两个锁很相似,只是自旋锁在等待的时候不会进入睡眠,而会一直占用CPU,这样会使得资料浪费严重,所以要慎用
小结:如果之前有接触过字符型驱动程序的朋友都知道,字符型驱动设备都会为设备关联上open,read等操作函数来给应用层使用,而我们在前两章内容分析的时候,却没有发现I2C设备有关联这些操作函数,原来这些操作函数的实现是在这里。
系统是将I2C总线看成了一个设备,而I2C总线上挂载的设备则需要分享这些命令。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
同样的,先是准备工作,看下变量的定义等:
struct i2c_dev {
struct list_head list;
struct i2c_adapter *adap; //适配器
struct device *dev; //设备
};
#define I2C_MINORS 256 //设备号
static LIST_HEAD(i2c_dev_list); //设备链表
static DEFINE_SPINLOCK(i2c_dev_list_lock); //自旋锁
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//这个函数名已经注释了这个函数的作用了:根据index获取设备信息
static struct i2c_dev *i2c_dev_get_by_minor(unsigned index)
{
struct i2c_dev *i2c_dev;
spin_lock(&i2c_dev_list_lock);
//遍历i2c_dev_list链表,取出结构体i2c_dev
list_for_each_entry(i2c_dev, &i2c_dev_list, list) {
if (i2c_dev->adap->nr == index) //适配器的nr号为指定的索引
goto found;
}
i2c_dev = NULL;
found:
spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//获取一个空的i2c_dev宿主结构体挂载adapter,并将该设备挂载到i2c_dev_list上
static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
//索引号过大,出错
if (adap->nr >= I2C_MINORS) {
printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n",
adap->nr);
return ERR_PTR(-ENODEV);
}
//申请一块内存,并关联上输入的adap信息
i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return ERR_PTR(-ENOMEM);
i2c_dev->adap = adap;
spin_lock(&i2c_dev_list_lock);
list_add_tail(&i2c_dev->list, &i2c_dev_list); //将设备链表挂载到i2c_dev_list上
spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//删除一个设备
static void return_i2c_dev(struct i2c_dev *i2c_dev)
{
spin_lock(&i2c_dev_list_lock);
list_del(&i2c_dev->list); //将设备从链表中断开
spin_unlock(&i2c_dev_list_lock);
kfree(i2c_dev); //释放设备所占用的内存
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//显示适配器的名字
static ssize_t show_adapter_name(
struct device *dev, //设备指针
struct device_attribute *attr, //设备属性
char *buf) //缓存
{
//通过device结构体的注解我们可以看到,devt成员是在建立文件系统的时候创建的
struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt));
if (!i2c_dev)
return -ENODEV;
return sprintf(buf, "%s\n", i2c_dev->adap->name);
}
static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL);
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
可见,这里是创建了一个dev_attr_name的设备属性结构体
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
这里开始是对I2C设备的操作代码,应该是应用层的操作了
//I2C设备读
static ssize_t i2cdev_read (
struct file *file, //文件句柄
char __user *buf, //操作缓存
size_t count, //操作字节数量
loff_t *offset) //偏移地址
{
char *tmp;
int ret;
//I2C设备的私有数据为client结构体
struct i2c_client *client = (struct i2c_client *)file->private_data;
//操作字节不能大于8K
if (count > 8192)
count = 8192;
//申请一块内存放操作数据
tmp = kmalloc(count, GFP_KERNEL);
if (tmp==NULL)
return -ENOMEM;
pr_debug("i2c-dev: i2c-%d reading %zd bytes.\n",
iminor(file->f_path.dentry->d_inode), count);
//调用i2c-core.c中的函数实现读操作
ret = i2c_master_recv(client,tmp,count);
if (ret >= 0) //读成功,将数据复制到用户buf中
ret = copy_to_user(buf, tmp, count)?-EFAULT:ret;
kfree(tmp); //释放内存
return ret;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//I2C设备的写操作
//代码基本和读操作类似,只是调用的实现函数是i2c_master_send
//因为代码类似,所以这里就删去了些不重要的代码
static ssize_t i2cdev_write (
struct file *file,
const char __user *buf,
size_t count,
loff_t *offset)
{
struct i2c_client *client = (struct i2c_client *)file->private_data;
tmp = kmalloc(count,GFP_KERNEL);
if (copy_from_user(tmp,buf,count)) {
kfree(tmp);
return -EFAULT;
}
ret = i2c_master_send(client,tmp,count);
kfree(tmp);
return ret;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//检查addr地址是否已经存在于adapter中,不存在则返回0
//在分析i2c-core.c时就已经有相同功能的函数了,所以这里就不再详细分析了
static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr)
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
static int i2cdev_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
struct i2c_client *client = (struct i2c_client *)file->private_data;
struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_smbus_ioctl_data data_arg;
union i2c_smbus_data temp;
struct i2c_msg *rdwr_pa; //SMBUS的数据结构体
u8 __user **data_ptrs;
int i,datasize,res;
unsigned long funcs;
//打印信息到设备的LOG中
dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg);
//根据不同的命令做响应
switch ( cmd ) {
//设置要操作的I2C设备地址
//由于在这个命令下,arg代表着I2C设备的地址,一般I2C设备地址为7位,所以arg值不应该大于0x7f。如果设备带有I2C_M_TEN参数,则说明I2C设备地址是10位,所以此时arg也不应该大于0x3ff
case I2C_SLAVE:
case I2C_SLAVE_FORCE:
if ((arg > 0x3ff) || //参数 >0x3ff
//设备属性不带I2C_M_TEN时,参数>0x7f
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL; //出错
if (cmd == I2C_SLAVE
&& i2cdev_check_addr(client->adapter, arg)) //设备地址(arg)不存在
return -EBUSY;
//将client的地址设置为传入的设备地址
//由于函数的一开始client是指向file->private_data的,所以这一步操作就相当于把file的地址设置为了arg地址了
client->addr = arg;
return 0;
//设置设备地址是7位还是10位(I2C_M_TEN)
case I2C_TENBIT:
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
//设置I2C传输的数据是否需要带CRC校验
//关于这里的CRC校验,实际就是在分析i2c-core.c文件中最后几个函数所用的crc算法,用来保护传输的数据是否正确
case I2C_PEC:
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return 0;
//获取适配器支持的操作
case I2C_FUNCS:
//获取adapter支持的操作
funcs = i2c_get_functionality(client->adapter);
return put_user(funcs, (unsigned long __user *)arg); //将数据传给arg
//设备的读写操作,注意传入参数的类型
case I2C_RDWR:
if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg)))
return -EFAULT;
//要操作的数据包(msg包)超过最大限制
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 == NULL) return -ENOMEM; //申请失败
//将要操作的数据用用户层复制到内核层
if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
kfree(rdwr_pa);
return -EFAULT;
}
//申请一组指针,每一个元素指向一个msg包
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[i].len > 8192) || //要操作的数据不能大于8K
(rdwr_pa[i].flags & I2C_M_RECV_LEN)) {
res = -EINVAL;
break;
}
data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf; //指针和用户层数据缓存关联
//根据长度申请内存,注意,此时buf地址关联上的是新申请的内存的地址
rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
if(rdwr_pa[i].buf == NULL) {
res = -ENOMEM;
break;
}
//将要操作的用户数据COPY到内核缓存中
//如果是读操作,那么这里复制的数据是没有意义的
//如果是写操作,那么这里就将要写入的数据给复制进了缓存中了
if(copy_from_user(rdwr_pa[i].buf,
data_ptrs[i],
rdwr_pa[i].len)) {
++i;
res = -EFAULT;
break;
}
} //for结束
//如果以上操作失败(一般是申请内存失败),则释放内存并返回
if (res < 0) {
int j;
for (j = 0; j < i; ++j)
kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}
//传输数据
res = i2c_transfer(client->adapter,
rdwr_pa,
rdwr_arg.nmsgs);
while(i-- > 0) {
//如果是读操作,则把数据复制出来
if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) {
if(copy_to_user(
data_ptrs[i],
rdwr_pa[i].buf,
rdwr_pa[i].len)) {
res = -EFAULT;
}
}
kfree(rdwr_pa[i].buf); //释放内存
}
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
//SMBUS格式的读写操作
//实际上就是直接调用i2c-core.c中的i2c_smbus_xfer函数来实现I2C通讯
case I2C_SMBUS:
if (copy_from_user(&data_arg,
(struct i2c_smbus_ioctl_data __user *) arg,
sizeof(struct i2c_smbus_ioctl_data)))
return -EFAULT;
//这里省略了很多参数合法性判断的代码
//当所有参数都判断合法之后,则调用这个核心函数来实现对应的功能
res = i2c_smbus_xfer(client->adapter,client->addr,client->flags,
data_arg.read_write,
data_arg.command,data_arg.size,&temp);
if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
(data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
(data_arg.read_write == I2C_SMBUS_READ))) {
if (copy_to_user(data_arg.data, &temp, datasize))
return -EFAULT;
}
return res;
//设置两个参数
case I2C_RETRIES:
client->adapter->retries = arg;
break;
case I2C_TIMEOUT:
client->adapter->timeout = arg;
break;
default:
return -ENOTTY;
}
return 0;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//打开设备
static int i2cdev_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode); //获取设备的次版本号
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
i2c_dev = i2c_dev_get_by_minor(minor); //根据次版本号在i2c_dev_list中获取设备
if (!i2c_dev)
return -ENODEV;
//根据关联的适配器号获取适配器,获取的同时,该适配器的引用计数加1
adap = i2c_get_adapter(i2c_dev->adap->nr);
if (!adap)
return -ENODEV;
//申请一块内存,准备挂载设备到文件的私有数据上
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap); //适配器的引用计数减1
return -ENOMEM;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
client->driver = &i2cdev_driver; //将i2cdev_driver挂载到设备驱动上
client->adapter = adap; //将适配器挂载上去
file->private_data = client; //文件的私有数据指向这个设备
return 0;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//释放设备
static int i2cdev_release(struct inode *inode, struct file *file)
{
struct i2c_client *client = file->private_data;
i2c_put_adapter(client->adapter); //适配器的引用计数减1
kfree(client); //释放设备client内存
file->private_data = NULL; //文件的私有数据指向空
return 0;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//这个结构就比较熟悉了,字符型驱动的操作函数
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE, //所有者
.llseek = no_llseek, //操作位置定位
.read = i2cdev_read, //读操作
.write = i2cdev_write, //写操作
.ioctl = i2cdev_ioctl, //IOCTL操作
.open = i2cdev_open, //打开文件
.release = i2cdev_release, //释放文件
};
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
static struct class *i2c_dev_class;
//适配器探测
static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
int res;
i2c_dev = get_free_i2c_dev(adap); //申请一块内存做i2c_dev并关联上adapter
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
//创建设备并挂载到结构体上
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr),
"i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
}
//根据设备的属性创建文件
//这一步应该就是将设备变成文件放到根目录中了
res = device_create_file(i2c_dev->dev, &dev_attr_name);
if (res)
goto error_destroy;
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
//创建文件出错
error_destroy:
//销毁设备
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
//创建设备出错
error:
return_i2c_dev(i2c_dev); //将函数一开始创建的i2c_dev结构体释放
return res;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//adapter断开时执行的函数
static int i2cdev_detach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
i2c_dev = i2c_dev_get_by_minor(adap->nr); //根据次版本号获取设备指针
if (!i2c_dev)
return 0;
device_remove_file(i2c_dev->dev, &dev_attr_name); //删除文件
return_i2c_dev(i2c_dev); //将i2c_dev占的内存释放
device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); //设备销毁
pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name);
return 0;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//设备client卸载时调用的函数(什么也没干)
static int i2cdev_detach_client(struct i2c_client *client)
{
return 0;
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//设备驱动
static struct i2c_driver i2cdev_driver = {
.driver = {
.name = "dev_driver",
},
.id = I2C_DRIVERID_I2CDEV,
.attach_adapter = i2cdev_attach_adapter,
.detach_adapter = i2cdev_detach_adapter,
.detach_client = i2cdev_detach_client,
};
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//模块的加载函数
module_init(i2c_dev_init);
module_exit(i2c_dev_exit);
//代码在初始化段中,将在开机时自动执行
static int __init i2c_dev_init(void)
{
int res;
//代码看到这里的时候,顺便看了一眼我的Linux启动时打印出来的LOG,果然找到了这句
printk(KERN_INFO "i2c /dev entries driver\n");
//用主设备号注册设备
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
if (res)
goto out;
//创建设备类
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class))
goto out_unreg_chrdev;
res = i2c_add_driver(&i2cdev_driver); //注册设备驱动
if (res)
goto out_unreg_class;
return 0;
//注册失败
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__);
return res;
}
static void __exit i2c_dev_exit(void)
{
i2c_del_driver(&i2cdev_driver); //删除驱动
class_destroy(i2c_dev_class); //销毁设备类
unregister_chrdev(I2C_MAJOR,"i2c"); //根据主设备号卸载设备
}