应用层, i2c-dev.c,和i2c-core.c的交互

本文介绍了I2C通信中应用层如何通过i2c-dev.c与驱动层进行交互,包括打开和关闭设备、ioctl命令、i2c_transfer()函数在i2c-core.c中的作用,以及i2c_read()和write()函数的系统调用过程,详细解析了一次完整的I2C通信协议流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

i2c 应用层与驱动层沟通的结构体就是下面这个, 定义在i2c-dev.c中:

static const struct file_operations i2cdev_fops = {
        .owner          = THIS_MODULE,
        .llseek         = no_llseek,
        .read           = i2cdev_read,
        .write          = i2cdev_write,
        .ioctl          = i2cdev_ioctl,
        .open           = i2cdev_open,
        .release        = i2cdev_release,
};
1. 应用层的open和close

应用层的打开和关闭设备函数:

fd = open(“/dev/i2c-0”, O_RDWR );
close(fd);
这两个系统调用分别会调用到i2c-dev.c 中的 .open, 和.release 函数

也就是:

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;

        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;

        /* registered with adapter, passed as client to user */
        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);
        kfree(client);
        file->private_data = NULL;

        return 0;
}
i2cdev_open() 中先通过次设备号,获得该i2c设备,也就是/dev/i2c-*

        i2c_dev = i2c_dev_get_by_minor(minor);
然后通过i2c_dev->adap->nr, 获得与该设备匹配的i2c_adapter。

       adap = i2c_get_adapter(i2c_dev->adap->nr);
然后申请一个i2c_client , 每次打开一个i2c设备,都会建立一个i2c_client结构体,用来表示被打开的一个i2c设备。

并给i2c_client赋值, 主要是复制client->driver, 和client->adapter.

        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;

        /* registered with adapter, passed as client to user */
        client->adapter = adap;
client->driver, 该i2c设备的驱动程序。

client->adapter, 该i2c设备依附的i2c_adapter.

2. 应用层的ioctl():

ioctl(fd, request, Addr);
fd 就是打开的i2c设备号。

request,是ioctl 传递给驱动层的commd, 有如下一些定义:

/* ----- commands for the ioctl like i2c_command call:
 * note that additional calls are defined in the algorithm and hw
 *      dependent layers - these can be listed here, or see the
 *      corresponding header files.
 */
                                /* -> bit-adapter specific ioctls       */
#define I2C_RETRIES     0x0701  /* number of times a device address      */
                                /* should be polled when not            */
                                /* acknowledging                        */
#define I2C_TIMEOUT     0x0702  /* set timeout - call with int          */


/* this is for i2c-dev.c        */
#define I2C_SLAVE       0x0703  /* Change slave address                 */
                                /* Attn.: Slave address is 7 or 10 bits */
#define I2C_SLAVE_FORCE 0x0706  /* Change slave address                 */
                                /* Attn.: Slave address is 7 or 10 bits */
                                /* This changes the address, even if it */
                                /* is already taken!                    */
#define I2C_TENBIT      0x0704  /* 0 for 7 bit addrs, != 0 for 10 bit   */

#define I2C_FUNCS       0x0705  /* Get the adapter functionality */
#define I2C_RDWR        0x0707  /* Combined R/W transfer (one stop only)*/
#define I2C_PEC         0x0708  /* != 0 for SMBus PEC                   */

#define I2C_SMBUS       0x0720  /* SMBus-level access */
Addr,就是传给驱动层的程序要访问I2C设备的地址,一般只有高7位,LSB用来设置读还是写。
应用层ioctl() 会调用到驱动层的ioctl().

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;
        u8 __user **data_ptrs;
        int i,datasize,res;
        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:
                if ((arg > 0x3ff) ||
                    (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
                        return -EINVAL;
                if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg))
                        return -EBUSY;
                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:
                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 == NULL) 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 */
                        if (rdwr_pa[i].len > 8192) {
                                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;

        case I2C_SMBUS:
                if (copy_from_user(&data_arg,
                                   (struct i2c_smbus_ioctl_data __user *) arg,
                                   sizeof(struct i2c_smbus_ioctl_data)))
                        return -EFAULT;
                if ((data_arg.size != I2C_SMBUS_BYTE) &&
                    (data_arg.size != I2C_SMBUS_QUICK) &&
                    (data_arg.size != I2C_SMBUS_BYTE_DATA) &&
                    (data_arg.size != I2C_SMBUS_WORD_DATA) &&
                    (data_arg.size != I2C_SMBUS_PROC_CALL) &&
                    (data_arg.size != I2C_SMBUS_BLOCK_DATA) &&
                    (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) &&
                    (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {
                        dev_dbg(&client->adapter->dev,
                                "size out of range (%x) in ioctl I2C_SMBUS.\n",
                                data_arg.size);
                        return -EINVAL;
                }
                /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1,
                   so the check is valid if size==I2C_SMBUS_QUICK too. */
                if ((data_arg.read_write != I2C_SMBUS_READ) &&
                    (data_arg.read_write != I2C_SMBUS_WRITE)) {
                        dev_dbg(&client->adapter->dev,
                                "read_write out of range (%x) in ioctl I2C_SMBUS.\n",
                                data_arg.read_write);
                        return -EINVAL;
                }

                /* Note that command values are always valid! */

                if ((data_arg.size == I2C_SMBUS_QUICK) ||
                    ((data_arg.size == I2C_SMBUS_BYTE) &&
                    (data_arg.read_write == I2C_SMBUS_WRITE)))
                        /* These are special: we do not use data */
                        return i2c_smbus_xfer(client->adapter, client->addr,
                                              client->flags,
                                              data_arg.read_write,
                                              data_arg.command,
                                              data_arg.size, NULL);

                if (data_arg.data == NULL) {
                        dev_dbg(&client->adapter->dev,
                                "data is NULL pointer in ioctl I2C_SMBUS.\n");
                        return -EINVAL;
                }

                if ((data_arg.size == I2C_SMBUS_BYTE_DATA) ||
                    (data_arg.size == I2C_SMBUS_BYTE))
                        datasize = sizeof(data_arg.data->byte);
                else if ((data_arg.size == I2C_SMBUS_WORD_DATA) ||
                         (data_arg.size == I2C_SMBUS_PROC_CALL))
                        datasize = sizeof(data_arg.data->word);
                else /* size == smbus block, i2c block, or block proc. call */
                        datasize = sizeof(data_arg.data->block);

                if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
                    (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||
                    (data_arg.read_write == I2C_SMBUS_WRITE)) {
                        if (copy_from_user(&temp, data_arg.data, datasize))
                                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;

        default:
                return i2c_control(client,cmd,arg);
        }
        return 0;
}
驱动层的ioctl(), 根据不同的command执行不同的操作。
应用层和驱动层之间数据传输用copy_from_user() 和copy_to_user(). 也可以put_user().

驱动层中核心的通信函数就是i2c_transfer().

这里如果i2c command是 I2C_RDWR, 则会用到i2c_transfer(), 这函数定义在 i2c-core.c 中:

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
        int ret;

        if (adap->algo->master_xfer) {
#ifdef DEBUG
                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

                mutex_lock_nested(&adap->bus_lock, adap->level);
                ret = adap->algo->master_xfer(adap,msgs,num);
                mutex_unlock(&adap->bus_lock);

                return ret;
        } else {
                dev_dbg(&adap->dev, "I2C level transfers not supported\n");
                return -ENOSYS;
        }
}
EXPORT_SYMBOL(i2c_transfer);
这函数的核心,其实也就是
               ret = adap->algo->master_xfer(adap,msgs,num);
这个函数指针其实就是在algo中赋值的,如这里用drivers/i2c/algos/i2c-algo-bit.c 的话,就是

static const struct i2c_algorithm i2c_bit_algo = {
        .master_xfer    = bit_xfer,
        .functionality  = bit_func,
};
也就是bit_xfer()函数:

static int bit_xfer(struct i2c_adapter *i2c_adap,
                    struct i2c_msg msgs[], int num)
{
        struct i2c_msg *pmsg;
        struct i2c_algo_bit_data *adap = i2c_adap->algo_data;

        int i,ret;
        unsigned short nak_ok;

#ifdef BUGGY_SLM
        unsigned long flags;
#endif

#ifdef BUGGY_SLM
                local_irq_save(flags);
                local_irq_disable();
#endif
        bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");
        i2c_start(adap);
        for (i=0;i<num;i++) {
                pmsg = &msgs[i];
                nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
                if (!(pmsg->flags & I2C_M_NOSTART)) {
                        if (i) {
                                bit_dbg(3, &i2c_adap->dev, "emitting "
                                        "repeated start condition\n");
                                i2c_repstart(adap);
                        }
                        ret = bit_doAddress(i2c_adap, pmsg);
                        if ((ret != 0) && !nak_ok) {
                                bit_dbg(1, &i2c_adap->dev, "NAK from "
                                        "device addr 0x%02x msg #%d\n",
                                        msgs[i].addr, i);
                                goto bailout;
                        }
                }
                if (pmsg->flags & I2C_M_RD ) {
                        /* read bytes into buffer*/
                        ret = readbytes(i2c_adap, pmsg);
                        if (ret >= 1)
                                bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n",
                                        ret, ret == 1 ? "" : "s");
                        if (ret < pmsg->len) {
                                if (ret >= 0)
                                        ret = -EREMOTEIO;
                                goto bailout;
                        }
                } else {
                        /* write bytes from buffer */
                        ret = sendbytes(i2c_adap, pmsg);
                        if (ret >= 1)
                                bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n",
                                        ret, ret == 1 ? "" : "s");
                        if (ret < pmsg->len) {
                                if (ret >= 0)
                                        ret = -EREMOTEIO;
                                goto bailout;
                        }
                }
        }
        ret = i;

bailout:
        bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
        i2c_stop(adap);
#ifdef BUGGY_SLM
                local_irq_restore(flags);
                local_irq_enable();
#endif
        return ret;
}
这个函数中,其实就包含了一次I2C通讯了:

从i2c_start(adap) --- 发送start信号

到bit_doAddress(i2c_adap, pmsg);  -- 发送地址+读写信号

再到  readbytes(i2c_adap, pmsg);   --- 读i2c设备数据

或者 sendbytes(i2c_adap, pmsg);  ---写i2c设备

最后 i2c_stop(adap);   --- 发送一个stop信号

这就是一次完整的i2c通信协议了。

3. 应用层的i2c_read()

read(fd, pBuf, nDataLen)
pBuf --- 读取数据的buffer

nDataLen --- 需要读取的数据字节数,

这个会系统调用到驱动层的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 = (struct i2c_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 %zd 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;
}
i2c_master_recv() 定义在 i2c-core.c 中:

int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
{
        struct i2c_adapter *adap=client->adapter;
        struct i2c_msg msg;
        int ret;

        msg.addr = client->addr;
        msg.flags = client->flags & I2C_M_TEN;
        msg.flags |= I2C_M_RD;
        msg.len = count;
        msg.buf = buf;

        ret = i2c_transfer(adap, &msg, 1);

        /* If everything went ok (i.e. 1 msg transmitted), return #bytes
           transmitted, else error code. */
        return (ret == 1) ? count : ret;
}
EXPORT_SYMBOL(i2c_master_recv);
其实还是用到了i2c_transfer().

4. 应用层的 write() 函数:

write(fd, pBuf, nDataLen)
pBuf --- 要写入数据的buffer

nDataLen --- 要写入i2c设备的数据的字节数

 这个会系统调用到i2c-dev.c 的 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 = (struct i2c_client *)file->private_data;

        if (count > 8192)
                count = 8192;

        tmp = kmalloc(count,GFP_KERNEL);
        if (tmp==NULL)
                return -ENOMEM;
        if (copy_from_user(tmp,buf,count)) {
                kfree(tmp);
                return -EFAULT;
        }

        pr_debug("i2c-dev: i2c-%d writing %zd bytes.\n",
                iminor(file->f_path.dentry->d_inode), count);

        ret = i2c_master_send(client,tmp,count);
        kfree(tmp);
        return ret;
}
其实就是 i2c_master_send() 函数,这个定义在i2c-core.c 中

int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
        int ret;
        struct i2c_adapter *adap=client->adapter;
        struct i2c_msg msg;

        msg.addr = client->addr;
        msg.flags = client->flags & I2C_M_TEN;
        msg.len = count;
        msg.buf = (char *)buf;

        ret = i2c_transfer(adap, &msg, 1);

        /* If everything went ok (i.e. 1 msg transmitted), return #bytes
           transmitted, else error code. */
        return (ret == 1) ? count : ret;
}
EXPORT_SYMBOL(i2c_master_send);
也还是i2c_transfer() 函数,所以这个函数绝对是I2C通信的核心了。







评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值