现在的位置: 首页 > 综合 > 正文

LINUX设备驱动i2c架构分析(四)

2013年09月18日 ⁄ 综合 ⁄ 共 7134字 ⁄ 字号 评论关闭
七:i2c dev节点操作
现在来分析上面架构图中的i2c-dev.c中的部份.这个部份为用户空间提供了操作adapter的接口.这部份代码其实对应就晃一个模块.它的初始化函数为:
module_init(i2c_dev_init);
i2c_dev_init()代码如下:
static int __init i2c_dev_init(void)
{
    int res;
 
    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;
}
首先为主册了一个主设备号为I2C_MAJOR(89),操作集为i2cdev_fops的字符设备.然后注册了一个名为”i2c-dev”的class.之后再注册了一个i2c的driver.如下所示:
res = i2c_add_driver(&i2cdev_driver);
    if (res)
        goto out_unreg_class;
i2cdev_driver定义如下:
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,
};
也就是说,当它注册或者有新的adapter注册后,就会它的attach_adapter()函数.该函数代码如下:
static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
    struct i2c_dev *i2c_dev;
    int res;
 
    i2c_dev = get_free_i2c_dev(adap);
    if (IS_ERR(i2c_dev))
        return PTR_ERR(i2c_dev);
 
    /* register this i2c device with the driver core */
    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);
    return res;
}
这个函数也很简单,首先调用get_free_i2c_dev()分配并初始化了一个struct i2c_dev结构,使i2c_dev->adap指向操作的adapter.之后,该i2c_dev会被链入链表i2c_dev_list中.再分别以I2C_MAJOR, adap->nr为主次设备号创建了一个device.如果此时系统配置了udev或者是hotplug,那么就么在/dev下自动创建相关的设备节点了.
刚才我们说过,所有主设备号为I2C_MAJOR的设备节点的操作函数是i2cdev_fops.它的定义如下所示:
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,
};
 
7.1:i2c dev的open操作
Open操作对应的函数为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_list链表中取得i2c_dev
    i2c_dev = i2c_dev_get_by_minor(minor);
    if (!i2c_dev)
        return -ENODEV;
 
    //以apapter的总线号从i2c_adapter_idr中找到adapter
    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.
     */
     //分配并初始化一个i2c_client结构
    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;
 
    //clinet->adapter指向操作的adapter
    client->adapter = adap;
    //关联到file
    file->private_data = client;
 
    return 0;
}
注意这里分配并初始化了一个struct i2c_client结构.但是没有注册这个clinet.此外,这个函数中还有一个比较奇怪的操作.不是在前面已经将i2c_dev->adap指向要操作的adapter么?为什么还要以adapter->nr为关键字从i2c_adapter_idr去找这个操作的adapter呢?注意了,调用i2c_get_adapter()从总线号nr找到操作的adapter的时候,还会增加module的引用计数.这样可以防止模块意外被释放掉.也许有人会有这样的疑问,那 i2c_dev->adap->nr操作,如果i2c_dev->adap被释放掉的话,不是一样会引起系统崩溃么?这里因为,在i2cdev_attach_adapter()间接的增加了一次adapter的一次引用计数.如下:
tatic int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
......
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
                     MKDEV(I2C_MAJOR, adap->nr),
                     "i2c-%d", adap->nr);
......
}
看到了么,i2c_dev内嵌的device是以adap->dev为父结点,在device_create()中会增次adap->dev的一次引用计数.
好了,open()操作到此就完成了.
 
7.2:read操作
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;
}
首先从file结构中取得struct i2c_clinet.然后在kernel同分配相同长度的缓存区,随之调用i2c_master_recv()从设备中读取数据.再将读取出来的数据copy到用户空间中.
I2c_master_recv()代码如下:
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;
}
看完前面的代码之后,这个函数应该很简单了,就是为读操作初始化了一个i2c_msg.然后调用i2c_tanster().代码中的client->flags & I2C_M_TEN表示adapter是否采用10位寻址的方式.在这里就不再详细分析了.
另外,有人可能看出了一个问题.这里clinet->addr是从哪来的呢?对,在read之前应该还要有一步操作来设置clinet->addr的值.这个过程是ioctl的操作.ioctl可以设置PEC标志,重试次数,超时时间,和发送接收数据等,我们在这里只看一下clinet->addr的设置.代码片段如下示:
static int i2cdev_ioctl(struct inode *inode, struct file *file,
        unsigned int cmd, unsigned long 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;
    ......
    ......
}
由此可见,调用I2C_SLAVE或者I2C_SLAVE_FORCE的Ioctl就会设置clinet->addr.另外,注释中也说得很清楚了.如果是I2C_SLAVE的话,还会调用其所长i2cdev_check_addr().进行地址检查,如果adapter已经关联到这个地址的设备,就会检查失败.
 
7.2:write操作
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 设备.

抱歉!评论已关闭.