书接上回,在讨论完i2c设备、i2c适配器等初始化和删除相应驱动的程序后,我们在这个小节把注意力放在file_operations里面的几个函数操作上,先贴上file_operations结构体代码,让我们先看看其包含了哪几个函数。
static const struct file_operations i2cdev_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = i2cdev_read, .write = i2cdev_write, .unlocked_ioctl = i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release, };
这个结构体想必大家对其结构都相当清晰啦,由此可见,i2c驱动为用户空间提供的操作函数包括:
1.no_llseek
2.i2cdev_read
3.i2cdev_write
4.i2cdev_ioctl
5.i2cdev_open
6.i2cdev_release
下面就对它们逐一进行分析:
1.no_llseek
loff_t no_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; }
从结构体i2cdev_fops 的成员名字llseek推测,其作用应该是改变当前I2C器件读写的位置,而现在正如大家所见,驱动程序并未实现这功能。
2.i2cdev_read
static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { char *tmp; int ret; struct i2c_client *client = file->private_data; if (count > 8192) count = 8192; tmp = kmalloc(count, GFP_KERNEL); if (tmp == NULL) return -ENOMEM; pr_debug("i2c-dev: i2c-%d reading %zu bytes./n", iminor(file->f_path.dentry->d_inode), count); ret = i2c_master_recv(client, tmp, count); if (ret >= 0) ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret; kfree(tmp); return ret; }
read函数主要做的事情有:
(1)struct i2c_client *client = file->private_data;
通过参数传入的file(在上一篇博文有阐述)而找到像对应的一个i2c设备,接下来函数就是对该设备进行操作。
(2)ret = i2c_master_recv(client, tmp, count);
其中i2c_master_recv函数在i2c-core.c 中实现,为了使本文结构更加清晰,故把跟硬件打交道的代码文件i2c-core.c 放在后面的博文分析。在这里,我们只需简单地理解为i2c_master_recv函数的作用是通过调用上文提及的i2c设备,并从其中提取count 字节的内容存到tmp 缓冲区里。
(3)ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
若无异常情况发生,就会调用咱们非常熟悉的copy_to_user 函数,这个函数在这里就不赘述了,建议还没掌握的朋友查找相关资料,或者我前面的博文都有提及过,这个是基础吖!
3.i2cdev_write
static ssize_t i2cdev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) { int ret; char *tmp; struct i2c_client *client = file->private_data; if (count > 8192) count = 8192; tmp = memdup_user(buf, count); if (IS_ERR(tmp)) return PTR_ERR(tmp); pr_debug("i2c-dev: i2c-%d writing %zu bytes./n", iminor(file->f_path.dentry->d_inode), count); ret = i2c_master_send(client, tmp, count); kfree(tmp); return ret; }
write函数又做了什么事情呢?
(1)struct i2c_client *client = file->private_data;
见上文read函数中的解释。
(2)tmp = memdup_user(buf, count);
memdup_user 函数在Util.c 中实现,源代码如下
void *memdup_user(const void __user *src, size_t len) { void *p; /* * Always use GFP_KERNEL, since copy_from_user() can sleep and * cause pagefault, which makes it pointless to use GFP_NOFS * or GFP_ATOMIC. */ p = kmalloc_track_caller(len, GFP_KERNEL); if (!p) return ERR_PTR(-ENOMEM); if (copy_from_user(p, src, len)) { kfree(p); return ERR_PTR(-EFAULT); } return p; }
函数的作用一目了然,就是把指针src 所指向长度为len 的内容中copy_from_user 到函数返回的指针。
(3)ret = i2c_master_send(client, tmp, count);
把缓存器tmp 里的内容发送到设备client 。
4.i2cdev_ioctl
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct i2c_client *client = file->private_data; unsigned long funcs; dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx/n", cmd, arg); switch (cmd) { case I2C_SLAVE: case I2C_SLAVE_FORCE: /* NOTE: devices set up to work with "new style" drivers * can't use I2C_SLAVE, even when the device node is not * bound to a driver. Only I2C_SLAVE_FORCE will work. * * Setting the PEC flag here won't affect kernel drivers, * which will be using the i2c_client node registered with * the driver model core. Likewise, when that client has * the PEC flag already set, the i2c-dev driver won't see * (or use) this setting. */ 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; /* REVISIT: address could become busy later */ client->addr = arg; return 0; case I2C_TENBIT: if (arg) client->flags |= I2C_M_TEN; else client->flags &= ~I2C_M_TEN; return 0; case I2C_PEC: if (arg) client->flags |= I2C_CLIENT_PEC; else client->flags &= ~I2C_CLIENT_PEC; return 0; case I2C_FUNCS: funcs = i2c_get_functionality(client->adapter); return put_user(funcs, (unsigned long __user *)arg); case I2C_RDWR: return i2cdev_ioctl_rdrw(client, arg); case I2C_SMBUS: return i2cdev_ioctl_smbus(client, arg); case I2C_RETRIES: client->adapter->retries = arg; break; case I2C_TIMEOUT: /* For historical reasons, user-space sets the timeout * value in units of 10 ms. */ client->adapter->timeout = msecs_to_jiffies(arg * 10); break; default: /* NOTE: returning a fault code here could cause trouble * in buggy userspace code. Some old kernel bugs returned * zero in this case, and userspace code might accidentally * have depended on that bug. */ return -ENOTTY; } return 0; }
由于i2c适配器的设备节点代表的是整条i2c总线,所以在对其进行具体的文件系统操作之前还必须指明
待访问设备的总线地址。指明地址的操作通过ioctl 系统调用完成的,它最终调用设备方法i2cdev_ioctl。ioctl函数还是采用我们非常熟悉的switch-case逻辑结构,其各种命令对应的操作分析如下
case I2C_SLAVE:
case I2C_SLAVE_FORCE: 设置从设备地址
case I2C_TENBIT: 设置7bit 地址 or 10bit 地址
case I2C_PEC: 如果I2C_PEC != 0 , to use PEC with SMBus
case I2C_FUNCS: 返回控制器算法所支持的传输种类
case I2C_RDWR: 执行i2cdev_ioctl_rdrw函数,代码如下
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client, unsigned long arg) { struct i2c_rdwr_ioctl_data rdwr_arg; struct i2c_msg *rdwr_pa; u8 __user **data_ptrs; int i, res; if (copy_from_user(&rdwr_arg, (struct i2c_rdwr_ioctl_data __user *)arg, sizeof(rdwr_arg))) return -EFAULT; /* Put an arbitrary limit on the number of messages that can * be sent at once */ 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))) { 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++) { /* Limit the size of the message to a sane amount; * and don't let length change either. */ if ((rdwr_pa[i].len > 8192) || (rdwr_pa[i].flags & I2C_M_RECV_LEN)) { res = -EINVAL; break; } data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf; rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL); if (rdwr_pa[i].buf == NULL) { res = -ENOMEM; break; } if (copy_from_user(rdwr_pa[i].buf, data_ptrs[i], rdwr_pa[i].len)) { ++i; /* Needs to be kfreed too */ res = -EFAULT; break; } } 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; }
整个函数主要做了以下几件事
(1)copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg))
获得用户传进来的命令,并存放在rdwr_arg。
(2)copy_from_user(rdwr_pa, rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg))
把指针rdwr_pa指向结构体rdwr_arg的成员msgs。
(3)整个for循环实质就是把要进行传输的每个msg的信息copy到字符串指针data_ptrs当中。
(4)res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
i2c_transfer ()函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程。至于i2c_algorithm和master_xfer等在接下来的博文介绍。
(5)while循环里把data_ptrs所存的信息统统copy_to_user。
case I2C_SMBUS: 执行i2cdev_ioctl_smbus 函数,主要调用i2c_smbus_xfer,函数在i2c-core.c 里实现。在这里我们先简单理解此函数作用为选择smbus 通信协议。
case I2C_RETRIES: 设置重发次数
case I2C_TIMEOUT: 设置超时时间,设定为 arg * 10 (ms)
呼呼~ioctl函数总算带过了,具体没有深入的地方,均是涉及到i2c-core.c 和数据结构i2c_algorithm的地方,这两个东东留待接下来的博文再来收拾。
5.i2cdev_open
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); if (!i2c_dev) return -ENODEV; adap = i2c_get_adapter(i2c_dev->adap->nr); if (!adap) return -ENODEV; /* This creates an anonymous i2c_client, which may later be * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. * * This client is ** NEVER REGISTERED ** with the driver model * or I2C core code!! It just holds private copies of addressing * information and maybe a PEC flag. */ client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) { i2c_put_adapter(adap); return -ENOMEM; } snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); client->driver = &i2cdev_driver; client->adapter = adap; file->private_data = client; return 0; }
最后两个函数来点轻松的,一眼扫下来,就是一些注册、初始化等工作,唯一要注意地方是这个open函数比之前所介绍的其他驱动多了一点绑定的工作,如:
client->driver = &i2cdev_driver;
client->adapter = adap;
file->private_data = client;
至于其用意,不记得的朋友建议翻看本i2c驱动系列的第一篇博文。
6.i2cdev_release
static int i2cdev_release(struct inode *inode, struct file *file) { struct i2c_client *client = file->private_data; i2c_put_adapter(client->adapter); kfree(client); file->private_data = NULL; return 0; }
对照着open函数来看,这个release岂不是小菜一碟?
到此为止,i2c-dev.c 的驱动代码分析暂告一段落,由于本人水平所限,不能很深刻、到位的写出这份驱动代码的分析,还请各位看客多多提点。内容错漏,请严厉指出!接下来本系列的第4篇博文将讲述i2c-core.c 和数据结构i2c_algorithm,敬请关注!
本系列博文链接:
I2C驱动(1)http://blog.csdn.net/jarvis_xian/archive/2011/05/27/6449939.aspx
I2C驱动(2)http://blog.csdn.net/jarvis_xian/archive/2011/05/27/6451168.aspx
I2C驱动(3)http://blog.csdn.net/jarvis_xian/archive/2011/05/28/6452431.aspx
I2C驱动(4)http://blog.csdn.net/jarvis_xian/archive/2011/05/30/6455697.aspx