zoukankan      html  css  js  c++  java
  • Linux I2C设备驱动

    i2c设备:ts、camera、audio、gsensor、e2prom

    I2C基本协议:

    写:开始 -> 设备地址 -> 写标志 -> 应答 -> 内部地址 -> 应答 -> 数据 -> 应答 -> 停止

    读:开始 -> 设备地址 -> 写标志 -> 应答 -> 内部地址 -> 应答 -> 停止 -> 开始 -> 设备地址 -> 读标志 -> 应答 -> 数据 -> 应答 -> 停止

    开始信号:在SCL为高电平期间,SDA从高电平变为低电平

    结束信号:在SCL为高电平期间,SDA从低电平变为高电平

    应答信号:主设备发送完一个字节,释放SDA,SDA被从设备拉低为ACK,没有被拉低为NACK。

    I2C传输数据时,SCL为高电平时采集SDA信号(有效),SCL为低电平时SDA可变化(无效)。

    i2c驱动框架:

    应用层

    ----------------------------------------------------------------------------

    i2c driver 层:需要程序员实现

    1、与用户进行交互--->fops

    2、知道数据是什么,但是不知道如何传输个硬件

    ----------------------------------------------------------------------------

    i2c core 层:

    维护i2c总线,负责进行匹配

    代码位置:drivers/i2c/i2c-core.c

    ----------------------------------------------------------------------------

    i2c adapter 层:

    1、与硬件进行交互

    2、直到如何将数据传输给硬件,但不知道数据是什么

    代码位置:drivers/i2c/i2c-xxx.c

    ----------------------------------------------------------------------------

    i2c_adapter 结构体

    struct i2c_adapter {
      struct module *owner;  // 所属的模块

      const struct i2c_algorithm *algo;  // 总线通信方法结构体指针

      void *algo_data;  // algorithm数据

      int retries;  // 重试次数

      struct device dev;   // 适配器设备

      char name[48];  // 适配器名称

      ... ...
    };

    i2c_algorithm 结构体

    struct i2c_algorithm {
      int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);  // i2c传输函数指针
      int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write,  // smbus 传输函数指针
        u8 command, int size, union i2c_smbus_data *data);

      u32 (*functionality) (struct i2c_adapter *);  // 返回适配器支持的功能
    };

    i2c_driver 结构体

    struct i2c_driver {
      unsigned int class;

      int (*probe)(struct i2c_client *, const struct i2c_device_id *);
      int (*remove)(struct i2c_client *);

      struct device_driver driver;  // 设备驱动结构体
      const struct i2c_device_id *id_table;

      ... ...
    };

    i2c_client 结构体

    struct i2c_client {
      unsigned short flags;  // 标志
      unsigned short addr;   // 低7位芯片地址
      char name[I2C_NAME_SIZE];  // 设备名称
      struct i2c_adapter *adapter;   // 依附的 i2c_adapter 
      struct i2c_driver *driver;   // 依附的 i2c_driver 
      struct device dev;   // 设备结构体
      int irq;   // 设备中断
      struct list_head detected;  // 链表头
    };

    i2c_msg 结构体

    struct i2c_msg {
      __u16 addr;   // 从设备地址
      __u16 flags;  // 读写标志
      __u16 len;   // 消息包长度
      __u8 *buf;   // 消息包数据
    };

    i2c adapter 层中使用的是平台设备总线,每个i2c控制器有一个 platform_device,但只有一个 platform_driver,匹配成功后会执行 xxx_probe,每个platform_device 都会生成一个i2c适配器(struct i2c_adapter),i2c adapter 层会生成 i2c_client,并将 i2c_adapter 包含在 i2c_client;

    在 i2c driver 层,i2c_client 和 i2c_driver 在 i2c 总线上匹配成功后,会执行 i2c driver 层的 probe 函数,此时 i2c driver 层会获取到 i2c_client 信息,读写通过 i2c_client ---> adapter ---> algo ---> master_xfer 就可以完成与从设备通信。

    以下代码为S5PV210读写AT24C02:

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/miscdevice.h>
    #include <linux/i2c.h>
    #include <linux/slab.h>
    
    #include <asm/ioctl.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
    
    struct e2prom_private {
        int size;
        int version;
    };
    
    struct global_e2prom {
        struct i2c_client *client;
        struct e2prom_private *private;
    };
    
    struct global_e2prom *e2prom_dev;
    
    //i2c_master_send(const struct i2c_client * client,const char * buf,int count)
    //i2c_master_recv(const struct i2c_client * client,char * buf,int count)
    int at24_i2c_send(struct i2c_client *client, char *buf, int count)
    {
        int ret;
    
        struct i2c_adapter *adapter = client->adapter;
        struct i2c_msg msg;
    
        msg.addr = client->addr;
        msg.flags = 0;
        msg.len = count;
        msg.buf = buf;
    
        ret = i2c_transfer(adapter, &msg, 1);
    
        return (ret == 1) ? count : ret;
    }
    
    int at24_i2c_recv(struct i2c_client *client, char *buf, int count)
    {
        int ret;
    
        struct i2c_adapter *adapter = client->adapter;
        struct i2c_msg msg;
    
        msg.addr = client->addr;
        msg.flags = I2C_M_RD;
        msg.len = count;
        msg.buf = buf;
    
        ret = i2c_transfer(adapter, &msg, 1);
    
        return (ret == 1) ? count : ret;
    }
    
    int at24_drv_open(struct inode *inode, struct file *filp)
    {
        printk("-----%s-----
    ", __func__);
    
        return 0;
    }
    
    int at24_drv_close(struct inode *inode, struct file *filp)
    {
        printk("-----%s-----
    ", __func__);
    
        return 0;
    }
    
    ssize_t at24_drv_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
    {
        int ret;
        
        printk("-----%s-----
    ", __func__);
    
        if ((size < 0) || (size > e2prom_dev->private->size))
        {
            printk("read size error
    ");
            return -EINVAL;
        }
    
        char *tmp = kzalloc(size, GFP_KERNEL);
        if (tmp == NULL)
        {
            printk("kzalloc fail
    ");
            return -ENOMEM;
        }
    
        ret = at24_i2c_recv(e2prom_dev->client, tmp, size);
        if (ret < 0)
        {
            printk("at24_i2c_recv error
    ");
            ret = -EIO;
            goto err_free;
        }
    
        ret = copy_to_user(buf, tmp, size);
        if (ret > 0)
        {
            printk("copy_to_user error
    ");
            ret = -EFAULT;
            goto err_free;
        }
    
        kfree(tmp);
    
        return size;
    
    err_free:
        kfree(tmp);
    
        return ret;
    }
    
    ssize_t at24_drv_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
    {
        int ret;
    
        printk("-----%s-----
    ", __func__);
    
        if ((size < 0) || (size > e2prom_dev->private->size))
        {
            printk("write size error
    ");
            return -EINVAL;
        }
    
        char *tmp = kzalloc(size, GFP_KERNEL);
        if (tmp == NULL)
        {
            printk("kzalloc fail
    ");
            return -ENOMEM;
        }
    
        ret = copy_from_user(tmp, buf, size);
        if (ret > 0)
        {
            printk("copy_from_user error
    ");
            ret = -EINVAL;
            goto err_free;
        }
    
        ret = at24_i2c_send(e2prom_dev->client, tmp, size);
        if (ret < 0)
        {
            printk("at24_i2c_send error
    ");
            ret = -EIO;
            goto err_free;
        }
    
        kfree(tmp);
    
        return size;
    
    err_free:
        kfree(tmp);
    
        return ret;
    }
    
    const struct file_operations at24_fops = {
        .open = at24_drv_open,
        .release = at24_drv_close,
        .read = at24_drv_read,
        .write = at24_drv_write,
    };
    
    struct miscdevice at24_miscdev = {
        .minor = MISC_DYNAMIC_MINOR,  //自动分配次设备号
        .name = "e2prom_dev",  //用于生成设备节点
        .fops = &at24_fops,
    };
    
    int i2c_at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
    {
        int ret;
    
        e2prom_dev = kzalloc(sizeof(struct global_e2prom), GFP_KERNEL);
        if (e2prom_dev == NULL)
        {
            printk("kzalloc fail
    ");
            return -ENOMEM;
        }
    
        e2prom_dev->client = client;
        e2prom_dev->private = (struct e2prom_private *)id->driver_data;
        misc_register(&at24_miscdev);
        
        return 0;
    }
    
    int i2c_at24_remove(struct i2c_client *client)
    {
        misc_deregister(&at24_miscdev);
        kfree(e2prom_dev);
        
        return 0;
    }
    
    struct e2prom_private at24c02_private = {
        .size = 256,
        .version = 0x01,
    };
    
    const struct i2c_device_id at24_id_table[] = {
        {"at24c02", (kernel_ulong_t)&at24c02_private},
    };
    
    struct i2c_driver at24_drv = {
        .probe = i2c_at24_probe,
        .remove = i2c_at24_remove,
        .driver = {
            .name = "at24_drv",
        },
        .id_table = &at24_id_table,  //name用于匹配
    };
    
    static int __init i2c_at24_drv_init(void)
    {
        return i2c_add_driver(&at24_drv);
    }
    
    static void __exit i2c_at24_drv_exit(void)
    {
        i2c_del_driver(&at24_drv);
    }
    
    module_init(i2c_at24_drv_init);
    module_exit(i2c_at24_drv_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Aaron Lee");
  • 相关阅读:
    计算机安装Fedora操作系统——Win10+Linux双系统
    性能测试——压力测试指标
    系统吞吐量(TPS)、用户并发量、性能测试概念和公式
    在 数学吧 看到一个 极限题
    东方学帝 和 K歌之王 的 科学观 和 科学方法 的 对比
    走一走 欧拉先生 走过 的 路
    推导一个 经典物理 里 的 黑洞 的 坍缩半径
    四色定理 太简单了 , 来 玩 n 维空间 里 的 x 色定理
    今天看到了一个 求 平面图形 Centroid 的 办法
    记录一下这几天的一些讨论
  • 原文地址:https://www.cnblogs.com/lialong1st/p/7799423.html
Copyright © 2011-2022 走看看