linux下s3c2440–i2c驱动学习之三"深入i2c,linux下S3C2440–I2C驱动学习之三“深入i2c...

本文详细解读了I2C驱动中file_operations结构体中的关键函数,包括no_llseek、i2cdev_read、i2cdev_write、i2cdev_ioctl、i2cdev_open和i2cdev_release,介绍了它们的功能、实现细节以及在设备通信中的作用。
摘要由CSDN通过智能技术生成

在讨论完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;

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就很简单了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值