• Linux驱动子系统之I2C(一)


    主要是参考http://blog.csdn.net/cjok376240497/article/details/6972305,对I2C子系统讲解的很透彻的一篇文章,值得一读。

    1 硬件特性
    1.1 概述
    I2C总线是由Philips公司开发的两线式串行总线,这两根线为时钟线(SCL)和双向数据线(SDA)。由于I2C总线仅需要两根线,因此在电路板上占用的空间更少,带来的问题是带宽较窄。I2C在标准模式下传输速率最高100Kb/s,在快速模式下最高可达400kb/s。属于半双工。在嵌入式系统中,I2C应用非常广泛,大多数微控制器中集成了I2C总线,一般用于和RTC,EEPROM,智能电池电路,传感器,LCD以及其他类似设备之间的通信。I2C总线时钟都是由I2C主控器提供
    1.2 I2C总线传输时序


    1.3 I2C总线的信号状态
    1、  空闲状态:SDA和SCL都是高电平;
    2、  开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传输数据;
    3、  结束条件(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传输数据;
    4、  数据有效:在SCL的高电平期间,SDA保持稳定,数据有效。SDA的改变只能发生在SCL的低电平期间;
    5、  ACK信号:数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。
    读寄存器的标准流程为:
    1.    Master发送I2C addr(7bit)和w操作1(1bit),等待ACK
    2.    Slave发送ACK
    3.    Master发送reg addr(8bit),等待ACK
    4.    Slave发送ACK
    5.    Master发起START
    6.    Master发送I2C addr(7bit)和r操作1(1bit),等待ACK
    7.    Slave发送ACK
    8.    Slave发送data(8bit),即寄存器里的值
    9.    Master发送ACK
    10.  第8步和第9步可以重复多次,即顺序读多个寄存器
    10bit地址
    10bit的寻址扩展可能寻址的数目.有7bit地址和10bit地址的设备可以连接到相同的I2C总线上,而且7bit寻址和10bit寻址都可以用在所有的总线速度模式下.不过,10bit寻址用的不多.
    10bit的从机地址由开始条件(S)或重复开始条件(Sr)后的两个字节组成.第一个字节的前7位是1111 0XX,XX是10bit地址的最高有效位的前两位.第一个字节的第8bit是读写位,决定传输方向.
    尽管1111 XXX有8种可能的组合,然后只有1111 0XX这四种可以用于10bit寻址.剩下的1111 1XX这四种是为将来I2C扩展用的.

    1.4 从设备地址


    从datasheet发现,有三个IO口确定I2C从设备地址后三位,I2C总线从设备使用7位地址,最后一个为读写控制位。下图是TVP5158的原理图,我们可以计算出它的地址,在读取SII9135A的时候,手册上写得是0X60、0X68,这是8位,前7位有效,所以真实的I2C地址为0x30、0x34,第八位代表读写。


    1.5 I2C读写方式

    下面I2C写操作的步骤:


    多字节写的时序

     下面是I2C读操作的步骤:


    多字节读的时序


    具体可参考datasheet

    2  I2C子系统
    2.1 LinuxI2C子系统架构
    在内核中已经提供I2C子系统,所以在做I2C驱动之前,就必须要熟悉该子系统。


    2.2 三大组成部分
    1、I2C核心(i2c-core)
    I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(algorithm)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
    2、I2C总线驱动(I2Cadapter/Algo driver)
    I2C总线驱动是I2C适配器的软件实现,提供I2C适配器与从设备间完成数据通信的能力。
    I2C总线驱动由i2c_adapter和i2c_algorithm来描述
    3、I2C客户驱动程序(I2Cclient driver)
    I2C客户驱动是对I2C从设备的软件实现,一个具体的I2C客户驱动包括两个部分:一部分是i2c_driver,用于将设备挂接于i2c总线;另一部分是设备本身的驱动。
    I2C客户驱动程序由i2c_driver和i2c_client来描述
    2.3 所有的I2C驱动代码位于drivers/i2c目录下
    I2c-core.c    实现I2C核心的功能
    I2c-dev.c     通用的从设备驱动
    Chips       特定的I2C设备驱动
    Busses      I2C适配器的驱动
    Algos       实现了一些I2C总线适配器的algorithm
    2.4 I2C驱动编写的两种方法
    从上面的图我们可以看到两种编写驱动方法,一种是利用系统提供的i2c-dev.c来实现一个i2c适配器的设备文件,然后通过在应用层操作I2C适配器来控制I2C设备;另一种是为I2C从设备独立编写一个设备驱动,不需要i2c-dev.c文件。
    2.5 重要的数据结构
    每次分析子系统免不了分析它的数据结构,OK我们先来分析一下。
    I2c_adapter结构体代表I2C总线控制器

    struct i2c_adapter {
       struct module *owner;
       unsigned int class;       /*classes to allow probing for */
        const struct i2c_algorithm*algo; /* 总线上数据传输的算法*/
       void *algo_data;              /* algorithm 数据 */
     
       int timeout;            /* injiffies */
       int retries;             /* 重试次数 */
        struct device dev;      /* the adapter device */
     
       int nr;
       char name[48];                 /* 适配器名字 */
       struct completion dev_released;   /* 用于同步 */
    };

    I2c_algorithm对应一套通信方法

    struct i2c_algorithm {
        int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, intnum);
    int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, charread_write, u8 command, int size, unioni2c_smbus_data *data); u32 (*functionality) (structi2c_adapter *); };

    Functionality 函数用于返回algorithm所支持的通信协议,比如I2C_FUNC_I2C,I2C_FUNC_10BIT_ADDR等。
    smbus_xfer   函数SMBus传输函数指针,SMBus大部分基于I2C总线规范,SMBus不需增加额外引脚。与I2C总线相比,SMBus增加了一些新的功能特性,在访问时序也有一定的差异。
    Master_xfer  函数实现总线上数据传输,与具体的适配器有关
    Master_xfer  函数实现模板:

    static int i2c_adapter_xxx_xfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num)
    {
       ......
       for (i = 0; i < num; i++) {
           i2c_adapter_xxx_start();         /*产生起始位*/
           if (msgs[i]->flags & I2C_M_RD) {    /*读取*/
               i2c_adapter_xxx_setaddr((msg->addr << 1) | 1);  /*发送从设备地址*/
               i2c_adapter_xxx_wait_ack();   /*获得从设备的ACK*/
    i2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len);  /*读取len长度的数据到buf中*/
           } else {
               i2c_adapter_xxx_setaddr(msg->addr << 1);
               i2c_adapter_xxx_wait_ack();
               i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len);
           }
        }
       i2c_adapter_xxx_stop(); /*产生停止位*/
    }

    上面调用的函数用于完成适配器的底层硬件操作,与I2C适配器和CPU的具体硬件直接相关,需要由工程师根据芯片的数据手册来实现。在内核源码中,针对不同的I2C适配器都有master_xfer的实现,风格与模板不尽相同,但是可以用该模板作为参考来看源代码,受益匪浅。
    I2c_driver代表I2C从设备驱动

    struct i2c_driver {
             unsignedint class;
     
             int(*attach_adapter)(struct i2c_adapter *) __deprecated; /*依附i2c适配器函数指针*/
             int(*detach_adapter)(struct i2c_adapter *) __deprecated;/*脱离i2c适配器函数指针*/
     
             int (*probe)(struct i2c_client*, const struct i2c_device_id *);
             int (*remove)(struct i2c_client*);
     
             int(*suspend)(struct i2c_client *, pm_message_t mesg);
             int(*resume)(struct i2c_client *);
             void(*alert)(struct i2c_client *, unsigned int data);
             int(*command)(struct i2c_client *client, unsigned int cmd, void *arg);
     
             struct device_driver driver;
             const struct i2c_device_id*id_table;  /* 该驱动所支持的设备ID表 */
     
             /*Device detection callback for automatic device creation */
             int(*detect)(struct i2c_client *, struct i2c_board_info *);
             constunsigned short *address_list;
             structlist_head clients;
    };

    在新内核中,attach_adapter和detach_adapter已经被probe和remove取代
    Id_table用于i2c_driver和i2c_client的匹配
    I2c_client代表I2C从设备

    struct i2c_client {
    unsigned short flags;                 /*I2C_CLIENT_TEN:使用10位从地址,I2C_CLIENT_PEC:使用SMBus包错误检测*/
             unsignedshort addr;                 /* chipaddress - NOTE: 7bit    */
             charname[I2C_NAME_SIZE];
             struct i2c_adapter *adapter; /* 依附的i2c_adapter   */
             struct i2c_driver *driver;         /* 依附的i2c_driver*/
             structdevice dev;             /* the devicestructure             */
             intirq;                         /* irq issuedby device               */
             structlist_head detected;
    };

    2.6 核心层提供的接口函数
    1、  增加/删除I2C适配器
    int i2c_add_adapter(struct i2c_adapter *adapter)->static int i2c_register_adapter(struct i2c_adapter *adap)
    int i2c_del_adapter(struct i2c_adapter *adap)

    static int i2c_register_adapter(struct i2c_adapter *adap)
    {
        int res = 0;
    
        /* Can't register until after driver model init */
        if (unlikely(WARN_ON(!i2c_bus_type.p))) {
            res = -EAGAIN;
            goto out_list;
        }
    
        /* Sanity checks */
        if (unlikely(adap->name[0] == '\0')) {
            pr_err("i2c-core: Attempt to register an adapter with "
                   "no name!\n");
            return -EINVAL;
        }
        if (unlikely(!adap->algo)) {
            pr_err("i2c-core: Attempt to register adapter '%s' with "
                   "no algo!\n", adap->name);
            return -EINVAL;
        }
    
        rt_mutex_init(&adap->bus_lock);
        mutex_init(&adap->userspace_clients_lock);
        INIT_LIST_HEAD(&adap->userspace_clients);
    
        /* Set default timeout to 1 second if not already set */
        if (adap->timeout == 0)
            adap->timeout = HZ;
    
        dev_set_name(&adap->dev, "i2c-%d", adap->nr);
        adap->dev.bus = &i2c_bus_type;
        adap->dev.type = &i2c_adapter_type;
        res = device_register(&adap->dev);
        if (res)
            goto out_list;
    
        dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
    
    #ifdef CONFIG_I2C_COMPAT
        res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
                           adap->dev.parent);
        if (res)
            dev_warn(&adap->dev,
                 "Failed to create compatibility class link\n");
    #endif
    
        /* create pre-declared device nodes */
        if (adap->nr < __i2c_first_dynamic_bus_num)
            i2c_scan_static_board_info(adap);
    
        /* Notify drivers */
        mutex_lock(&core_lock);
        bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
        mutex_unlock(&core_lock);
    
        return 0;
    
    out_list:
        mutex_lock(&core_lock);
        idr_remove(&i2c_adapter_idr, adap->nr);
        mutex_unlock(&core_lock);
        return res;
    }

    Device_register(&adap->dev)  向I2C总线注册一个adapter设备
    i2c_scan_static_board_info(adap)   注册所有已知的i2c_client
    2、  增加/删除I2C从设备驱动 
    int i2c_add_driver(struct i2c_driver *driver)->int i2c_register_driver(struct module *owner, struct i2c_driver *driver) 
    void i2c_del_driver(struct i2c_driver *driver)  

    /*
     * An i2c_driver is used with one or more i2c_client (device) nodes to access
     * i2c slave chips, on a bus instance associated with some i2c_adapter.
     */
    int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
    {
        int res;
    
        /* Can't register until after driver model init */
        if (unlikely(WARN_ON(!i2c_bus_type.p)))
            return -EAGAIN;
    
        /* add the driver to the list of i2c drivers in the driver core */
        driver->driver.owner = owner;
        driver->driver.bus = &i2c_bus_type;
    
        /* When registration returns, the driver core
         * will have called probe() for all matching-but-unbound devices.
         */
        res = driver_register(&driver->driver);
        if (res)
            return res;
    
        pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
    
        INIT_LIST_HEAD(&driver->clients);
        /* Walk the adapters that are already present */
        mutex_lock(&core_lock);
        bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
        mutex_unlock(&core_lock);
    
        return 0;
    }
    EXPORT_SYMBOL(i2c_register_driver);

    向I2C总线注册一个i2c_driver
    3、  i2c传输,发送和接收
    int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg*msgs, int num) 
    int i2c_master_send(const struct i2c_client *client, constchar *buf, int count) 
    int i2c_master_recv(const struct i2c_client *client, char*buf, int count) 

    /**
     * i2c_transfer - execute a single or combined I2C message
     * @adap: Handle to I2C bus
     * @msgs: One or more messages to execute before STOP is issued to
     *    terminate the operation; each message begins with a START.
     * @num: Number of messages to be executed.
     *
     * Returns negative errno, else the number of messages executed.
     *
     * Note that there is no requirement that each message be sent to
     * the same slave address, although that is the most common model.
     */
    int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
    {
        unsigned long orig_jiffies;
        int ret, try;
    
        /* REVISIT the fault reporting model here is weak:
         *
         *  - When we get an error after receiving N bytes from a slave,
         *    there is no way to report "N".
         *
         *  - When we get a NAK after transmitting N bytes to a slave,
         *    there is no way to report "N" ... or to let the master
         *    continue executing the rest of this combined message, if
         *    that's the appropriate response.
         *
         *  - When for example "num" is two and we successfully complete
         *    the first message but get an error part way through the
         *    second, it's unclear whether that should be reported as
         *    one (discarding status on the second message) or errno
         *    (discarding status on the first one).
         */
    
        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
    
            if (in_atomic() || irqs_disabled()) {
                ret = i2c_trylock_adapter(adap);
                if (!ret)
                    /* I2C activity is ongoing. */
                    return -EAGAIN;
            } else {
                i2c_lock_adapter(adap);
            }
    
            /* Retry automatically on arbitration loss */
            orig_jiffies = jiffies;
            for (ret = 0, try = 0; try <= adap->retries; try++) {
                ret = adap->algo->master_xfer(adap, msgs, num);
                if (ret != -EAGAIN)
                    break;
                if (time_after(jiffies, orig_jiffies + adap->timeout))
                    break;
            }
            i2c_unlock_adapter(adap);
    
            return ret;
        } else {
            dev_dbg(&adap->dev, "I2C level transfers not supported\n");
            return -EOPNOTSUPP;
        }
    }
    EXPORT_SYMBOL(i2c_transfer);

    最终会调用到适配器实现的master_xfer函数来完成数据传输工作

    2.6 I2C子系统初始化

    struct bus_type i2c_bus_type = {
        .name        = "i2c",
        .match        = i2c_device_match,
        .probe        = i2c_device_probe,
        .remove        = i2c_device_remove,
        .shutdown    = i2c_device_shutdown,
        .pm        = &i2c_device_pm_ops,
    };
    EXPORT_SYMBOL_GPL(i2c_bus_type);
    //////////////////////////////
    static int __init i2c_init(void)
    {
        int retval;
    
        retval = bus_register(&i2c_bus_type);
        if (retval)
            return retval;
    #ifdef CONFIG_I2C_COMPAT
        i2c_adapter_compat_class = class_compat_register("i2c-adapter");
        if (!i2c_adapter_compat_class) {
            retval = -ENOMEM;
            goto bus_err;
        }
    #endif
        retval = i2c_add_driver(&dummy_driver);
        if (retval)
            goto class_err;
        return 0;
    
    class_err:
    #ifdef CONFIG_I2C_COMPAT
        class_compat_unregister(i2c_adapter_compat_class);
    bus_err:
    #endif
        bus_unregister(&i2c_bus_type);
        return retval;
    }
    
    static void __exit i2c_exit(void)
    {
        i2c_del_driver(&dummy_driver);
    #ifdef CONFIG_I2C_COMPAT
        class_compat_unregister(i2c_adapter_compat_class);
    #endif
        bus_unregister(&i2c_bus_type);
    }
    
    /* We must initialize early, because some subsystems register i2c drivers
     * in subsys_initcall() code, but are linked (and initialized) before i2c.
     */
    postcore_initcall(i2c_init);
    module_exit(i2c_exit);

    3  i2c-dev
    3.1 概述
    之前在介绍I2C子系统时,提到过使用i2c-dev.c文件在应用程序中实现我们的I2C从设备驱动。不过,它实现的是一个虚拟,临时的i2c_client,随着设备文件的打开而产生,并随着设备文件的关闭而撤销。I2c-dev.c针对每个I2C适配器生成一个主设备号为89的设备文件,实现了i2c_driver的成员函数以及文件操作接口,所以i2c-dev.c的主题是”i2c_driver成员函数+字符设备驱动”。
    3.2 i2c-dev.c源码分析
    初始化模块

    static int __init i2c_dev_init(void)
    {
             res= register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
     
             i2c_dev_class= class_create(THIS_MODULE, "i2c-dev");
     
             /*Keep track of adapters which will be added or removed later */
             res= bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
     
             /*绑定已经存在的适配器 */
             i2c_for_each_dev(NULL,i2cdev_attach_adapter);
    }

    I2c-dev初始化函数主要做了注册名为”i2c”的字符设备文件和”i2c-dev”的类
    i2cdev_read和i2cdev_write
    I2c-dev.c中实现的i2cdev_read和i2cdev_write函数不具有太强的通用性,只适合下面这种单开始信号情况:


    而不适合多开始信号的情况:


    所以我们经常会使用i2cdev_ioctl函数的I2C_RDWR,在分析i2cdev_ioctl函数之前,我们需要了解一个结构体:

    /* This is the structure as used in theI2C_RDWR ioctl call */
    struct i2c_rdwr_ioctl_data {
             structi2c_msg __user *msgs;         /* pointersto i2c_msgs */
             __u32nmsgs;                    /* number ofi2c_msgs */
    };

    Msgs     表示单个开始信号传递的数据;
    Nmsgs     表示有多少个msgs,比如上图,单开始信号时,nmsgs等于1;多开始信号时,nmsgs等于2

    struct i2c_msg {
             __u16addr;     /* slave address                         */
             __u16flags;  /* 默认为写入 */
    #define I2C_M_TEN                  0x0010     /*this is a ten bit chip address */
    #define I2C_M_RD           0x0001     /* read data,from slave to master */
    #define I2C_M_NOSTART                  0x4000     /* if I2C_FUNC_PROTOCOL_MANGLING */
    #define I2C_M_REV_DIR_ADDR     0x2000     /*if I2C_FUNC_PROTOCOL_MANGLING */
    #define I2C_M_IGNORE_NAK          0x1000     /*if I2C_FUNC_PROTOCOL_MANGLING */
    #define I2C_M_NO_RD_ACK           0x0800     /* if I2C_FUNC_PROTOCOL_MANGLING */
    #define I2C_M_RECV_LEN               0x0400     /* length will be first received byte */
             __u16len;                  /* msg length                              */
             __u8*buf;                 /* pointer to msgdata                       */
    };

    使用i2cdev_ioctl函数的I2C_RDWR指令会调用到i2cdev_ioctl_rdrw函数:

    static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,
                       unsignedlong arg)
    {
             structi2c_rdwr_ioctl_data rdwr_arg;
             structi2c_msg *rdwr_pa;
             u8__user **data_ptrs;
             inti, res;
     
             if(copy_from_user(&rdwr_arg,
                                   (struct i2c_rdwr_ioctl_data __user *)arg,
                                   sizeof(rdwr_arg)))
                       return-EFAULT;
     
             if(rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
                       return-EINVAL;
     
             rdwr_pa= kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);
     
             if(copy_from_user(rdwr_pa, rdwr_arg.msgs,
                                   rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
                       kfree(rdwr_pa);
                       return-EFAULT;
             }
     
             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);
             }
    }

    咋一看,还挺复杂,其实主要做了一件事情:把用户空间传递过来的i2c_rdwr_ioctl_data数据进行错误检查,然后调用i2c_transfer函数与适配器进行通信,如果是接收数据,代码会将访问到的数据传回i2c_rdwr_ioctl_data的buf中。I2c_transfer最终会调用到I2C适配器具体实现的master_xfer函数来与硬件进行通信。
    3.3 用户空间驱动模板

    #include <sys/ioctl.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdio.h>
    #include <linux/i2c.h>
    #include <linux/i2c-dev.h>
    
    #define OSA_SOK      0  ///< Status : OK
    #define OSA_EFAIL   -1  ///< Status : Generic error
    
    #define I2C_DEFAULT_INST_ID  (2)
    #define I2C_TRANSFER_SIZE_MAX   (254)
    
    #ifndef TRUE
    #define TRUE              1
    #endif
    
    #ifndef FALSE
    #define FALSE             0
    #endif
    
    typedef unsigned short    Bool;
    typedef unsigned long long Uint64;      ///< Unsigned 64-bit integer
    typedef unsigned int Uint32;            ///< Unsigned 32-bit integer
    typedef unsigned short Uint16;          ///< Unsigned 16-bit integer
    typedef unsigned char Uint8;            ///< Unsigned  8-bit integer
    
    #define OSA_ERROR(...) \
      do \
      { \
            fprintf(stderr, " ERROR  (%s|%s|%d): ", __FILE__, __func__, __LINE__); \
            fprintf(stderr, __VA_ARGS__); \
      } \
      while(0);
    
    typedef struct {
    
      int fd;
    
    } OSA_I2cHndl;
    
    #define OSA_I2C_DEBUG
    
    
    static char xtod(char c) {
      if (c>='0' && c<='9') return c-'0';
      if (c>='A' && c<='F') return c-'A'+10;
      if (c>='a' && c<='f') return c-'a'+10;
      return c=0;        // not Hex digit
    }
      
    
    static int HextoDec(char *hex, int l)
    {
      if (*hex==0) 
        return(l);
    
      return HextoDec(hex+1, l*16+xtod(*hex)); // hex+1?
    }
    
    
    int xstrtoi(char *hex)      // hex string to integer
    {
      return HextoDec(hex,0);
    }
    
    
    int OSA_i2cOpen(OSA_I2cHndl *hndl, Uint8 instId)
    {
            char deviceName[20];
            int status = 0;
    
            sprintf(deviceName, "/dev/i2c-%d", instId);
        printf("dev name = %s \n", deviceName);
    
            hndl->fd = open(deviceName, O_RDWR);
    
            if(hndl->fd<0)
                    return OSA_EFAIL;
    
            return status;
    }
    
    int OSA_i2cRead8(OSA_I2cHndl *hndl, Uint16 devAddr, Uint8 *reg, Uint8 *value, Uint32 count)
    {
            int i, j;
            int status = 0;
            struct i2c_msg * msgs = NULL;
            struct i2c_rdwr_ioctl_data data;
    
            msgs = (struct i2c_msg *) malloc(sizeof(struct i2c_msg) * count * 2);
    
            if(msgs==NULL)
            {
                    printf(" I2C (0x%02x): Malloc ERROR during Read !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
                    return OSA_EFAIL;
            }
    
            for (i = 0, j = 0; i < count * 2; i+=2, j++)
            {
                    msgs[i].addr  = devAddr;
                    msgs[i].flags = 0;
                    msgs[i].len   = 1;
                    msgs[i].buf   = &reg[j];
    
                    msgs[i+1].addr  = devAddr;
                    msgs[i+1].flags = I2C_M_RD /* | I2C_M_REV_DIR_ADDR */;
                    msgs[i+1].len   = 1;
                    msgs[i+1].buf   = &value[j];
            }
    
            data.msgs = msgs;
            data.nmsgs = count * 2;
    
            status = ioctl(hndl->fd, I2C_RDWR, &data);
            if(status < 0)
            {
                    status = OSA_EFAIL;
    #ifdef OSA_I2C_DEBUG
                    printf(" I2C (0x%02x): Read ERROR !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
    #endif
            }
            else
                    status = OSA_SOK;
    
            free(msgs);
    
            return status;
    }
    
    int OSA_i2cWrite8(OSA_I2cHndl *hndl, Uint16 devAddr,  Uint8 *reg, Uint8 *value, Uint32 count)
    {
            int i,j;
            unsigned char * bufAddr;
            int status = 0;
    
            struct i2c_msg * msgs = NULL;
            struct i2c_rdwr_ioctl_data data;
    
            msgs = (struct i2c_msg *) malloc(sizeof(struct i2c_msg) * count);
    
            if(msgs==NULL)
            {
                    printf(" I2C (0x%02x): Malloc ERROR during Write !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
                    return OSA_EFAIL;
            }
    
            bufAddr = (unsigned char *) malloc(sizeof(unsigned char) * count * 2);
    
            if(bufAddr == NULL)
            {
                    free(msgs);
    
                    printf(" I2C (0x%02x): Malloc ERROR during Write !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
                    return OSA_EFAIL;
            }
    
            for (i = 0, j = 0; i < count; i++, j+=2)
            {
                    bufAddr[j] = reg[i];
                    bufAddr[j + 1] = value[i];
    
                    msgs[i].addr  = devAddr;
                    msgs[i].flags = 0;
                    msgs[i].len   = 2;
                    msgs[i].buf   = &bufAddr[j];
            }
            data.msgs = msgs;
            data.nmsgs = count;
    
            status = ioctl(hndl->fd, I2C_RDWR, &data);
            if(status < 0)
            {
                    status = OSA_EFAIL;
    #ifdef OSA_I2C_DEBUG
                    printf(" I2C (0x%02x): Write ERROR !!! (reg[0x%02x], count = %d)\n", devAddr, reg[0], count);
    #endif
            }
            else
                    status = OSA_SOK;
    
            free(msgs);
            free(bufAddr);
    
            return status;
    }
    
    int OSA_i2cRawWrite8(OSA_I2cHndl *hndl, Uint16 devAddr, Uint8 *value, Uint32 count)
    {
            int status = 0;
    
            struct i2c_msg msgs[1];
            struct i2c_rdwr_ioctl_data data;
    
            msgs[0].addr  = devAddr;
            msgs[0].flags = 0;
            msgs[0].len   = count;
            msgs[0].buf   = value;
    
            data.msgs = msgs;
            data.nmsgs = 1;
    
            status = ioctl(hndl->fd, I2C_RDWR, &data);
            if(status < 0)
            {
                    status = OSA_EFAIL;
    #ifdef OSA_I2C_DEBUG
                    printf(" I2C (0x%02x): Raw Write ERROR !!! (count = %d)\n", devAddr, count);
    #endif
            }
            else
            status = OSA_SOK;
    
            return status;
    }
    
    
    int OSA_i2cRawRead8(OSA_I2cHndl *hndl, Uint16 devAddr, Uint8 *value, Uint32 count)
    {
            int status = 0;
    
            struct i2c_msg msgs[1];
            struct i2c_rdwr_ioctl_data data;
    
            msgs[0].addr  = devAddr;
            msgs[0].flags = I2C_M_RD;
            msgs[0].len   = count;
            msgs[0].buf   = value;
    
            data.msgs = msgs;
            data.nmsgs = 1;
    
            status = ioctl(hndl->fd, I2C_RDWR, &data);
            if(status < 0)
            {
                    status = OSA_EFAIL;
    #ifdef OSA_I2C_DEBUG
                    printf(" I2C (0x%02x): Raw Read ERROR !!! count = %d)\n", devAddr, count);
    #endif
            }
            else
                    status = OSA_SOK;
    
            return status;
    }
    
    int OSA_i2cClose(OSA_I2cHndl *hndl)
    {
            return close(hndl->fd);
    }
    
    
    int OSA_i2cTestShowUsage(char *str)
    {
            printf(" \n");
            printf(" I2C Test Utility, \r\n");
            printf(" Usage: %s -r|-w <devAddrInHex> <regAddrInHex> <regValueInHex or numRegsToReadInDec> \r\n", str);
            printf(" \n");
            return 0;
    }
    
    
    int main(int argc, char **argv)
    {
            OSA_I2cHndl i2cHndl;
            Uint8 devAddr, numRegs;
            Bool doRead;
            int status, i;
    
            static Uint8 regAddr[I2C_TRANSFER_SIZE_MAX], regValue8[I2C_TRANSFER_SIZE_MAX];
    
            if(argc<3) {
                    OSA_i2cTestShowUsage(argv[0]);
                    return -1;
            }
    
            if(strcmp(argv[1], "-r")==0)
                    doRead=TRUE;
            else
            if(strcmp(argv[1], "-w")==0)
                    doRead=FALSE;
            else {
                    OSA_i2cTestShowUsage(argv[0]);
                    return -1;
            }
    
            devAddr = 0;
            numRegs = 4;
            regValue8[0] = 0;
            regAddr[0] = 0;
    
            if(argc>2)
                    devAddr = xstrtoi(argv[2]);
    
            if(argc>3)
                    regAddr[0] = xstrtoi(argv[3]);
    
            if(argc>4) {
                    if(doRead)
                    {
                            numRegs = atoi(argv[4]);
                            if(numRegs>I2C_TRANSFER_SIZE_MAX)
                                    numRegs = I2C_TRANSFER_SIZE_MAX;
                    }
                    else {
                            regValue8[0] = xstrtoi(argv[4]);
                    }
            }
    
            if(devAddr==0) {
                    printf(" I2C: Invalid device address\n");
                    OSA_i2cTestShowUsage(argv[0]);
                    return -1;
            }
    
            status = OSA_i2cOpen(&i2cHndl, I2C_DEFAULT_INST_ID);
    
            if(status != OSA_SOK) {
                    OSA_ERROR("OSA_i2cOpen( instId = %d )\n", I2C_DEFAULT_INST_ID);
                    return status;
            }
    
            if(status==OSA_SOK)
            {
                    if(doRead) {
                    for(i=0; i<numRegs; i++)
                    regValue8[i] = 0;
    
                    for(i=1; i<numRegs; i++)
                    {
                            regAddr[i] = regAddr[0]+i;
                    }
    
                    status = OSA_i2cRead8(&i2cHndl, devAddr, regAddr, regValue8, numRegs);
    
                    if(status==OSA_SOK) {
                            for(i=0; i<numRegs; i++) {
                                    printf(" I2C (0x%02x): 0x%02x = 0x%02x \n", devAddr, regAddr[i], regValue8[i] );
                            }
                    } else {
                            printf(" I2C (0x%02x): Read ERROR !!! (reg[0x%02x], count = %d)\n", devAddr, regAddr[0], numRegs);
                    }
    
                    } else {
                            status = OSA_i2cWrite8(&i2cHndl, devAddr, regAddr, regValue8, 1);
                            if(status==OSA_SOK) {
                                    status = OSA_i2cRead8(&i2cHndl, devAddr, regAddr, regValue8, 1);
                            }
    
                            if(status==OSA_SOK) {
                                    printf(" I2C (0x%02x): 0x%02x = 0x%02x \n", devAddr, regAddr[0], regValue8[0] );
                            } else {
                                    printf(" I2C (0x%02x): Write ERROR !!! (reg[0x%02x], value = 0x%02x\n", devAddr, regAddr[0], regValue8[0]);
                            }
                    }
    
                    OSA_i2cClose(&i2cHndl);
            }
    
            return 0;
    }


     

    嵌入式QQ交流群:127085086
  • 相关阅读:
    centos7/RHEL7安装LibreOffice
    CentOS7开机启动管理systemd简介及使用
    Vim使用技巧
    16_用LVM扩展xfs文件系统(当分区空间不够时)
    15_RHEL7挂载NTFS分区
    14_RHEL7安装mplayer
    polyfill-eventsource added missing EventSource to window ie浏览器 解决方案
    关于vue,webpack 中 “exports is not defined”报错
    2018 vue前端面试题
    Error: No PostCSS Config found in... 报错 踩坑记
  • 原文地址:https://www.cnblogs.com/cslunatic/p/3134607.html
Copyright © 2020-2023  润新知