一种是利用系统提供的i2c-dev.c来实现一个i2c适配器的设备文件,然后通过在应用层操作I2C适配器来控制I2C设备;另一种是为I2C从设备独立编写一个设备驱动,不需要i2c-dev.c文件。由于前者比较简单通用性强,我们采用前者来展开。

     根据android层次划分,我们照例对开发分为如下几步:

     1. 添加HAL层接口模块访问设备

     2. 使用JNI在应用程序框架层添加服务访问接口

     3. 使用服务接口api开发应用程序


一. 添加HAL层接口模块访问设备

      首先确认物理设备正常。根据开发板说明书获知设备挂载在/dev/i2c-1上,检测到该设备的存在,则通用设备驱动正常。

      eeprom设备为at24c**系列,根据说明书获知设备从地址为0x50,准备工作完毕。

     1. 编写hal层接口模块头文件iic.h 

     进入源码根目录下hardware/libhardware/include/hardware目录新建iic.h,代码如下:

#ifndef ANDROID_IIC_INTERFACE_H  
    #define ANDROID_IIC_INTERFACE_H  
    #include <hardware/hardware.h>  
      
    __BEGIN_DECLS  
      
    /*定义模块ID*/  
    #define IIC_HARDWARE_MODULE_ID "iic"  
      
    /*硬件模块结构体*/  
    struct iic_module_t {  
        struct hw_module_t common;  
    };  
      
    /*硬件接口结构体*/  
    struct iic_device_t {  
        struct hw_device_t common;  
        int fd;  
        int (*iic_write)(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, unsigned short subAddr, int len);  
        int (*iic_read)(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, int len);  
    };  
      
    __END_DECLS  
      
    #endif



 

这里定义了iic_write和iic_read两个接口,头文件按照hal规范编写。

2. 编写hal层接口模块文件

     进入源码根目录下hardware/libhardware/modules目录新建iic目录,并在iic目录中添加iic.c,代码如下:

#include <hardware/hardware.h>  
#include <hardware/iic.h>  
#include <fcntl.h>  
#include <errno.h>  
#include <cutils/log.h>  
#include <cutils/atomic.h> 
#include <stdio.h>
#include<linux/i2c.h>
#include<linux/i2c-dev.h>
#include <stdlib.h>
#include <linux/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <string.h>
#define DEVICE_NAME "/dev/i2c-1"  
#define MODULE_NAME "iic"  
#define MODULE_AUTHOR "mfayz@sohu.com"  


#define I2C_RETRIES 0x0701/* number of times a device address should be polled when not acknowledging */
#define I2C_TIMEOUT 0x0702/* set timeout in units of 10 ms */
#define I2C_RDWR         0x0707


/*********定义struct i2c_rdwr_ioctl_data和struct i2c_msg,要和内核一致*******/
struct i2c_msg
       {
         unsigned short addr;
         unsigned short flags;
         #define I2C_M_TEN 0x0010
         #define I2C_M_RD 0x0001
         unsigned short len;
         unsigned char *buf;
       };


struct i2c_rdwr_ioctl_data {
struct i2c_msg *msgs;/* pointers to i2c_msgs */
int nmsgs; /* number of i2c_msgs */
}; 


    /*设备打开和关闭接口*/  
static int iic_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);  
static int iic_device_close(struct hw_device_t* device);  
      
    /*设备访问接口*/  
static int iic_write(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, unsigned short subAddr, int len);  
static int iic_read(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, int len); 
    
    /*模块方法表*/  
static struct hw_module_methods_t iic_module_methods = {  
        open: iic_device_open  
};  


struct i2c_rdwr_ioctl_data iic_data;
int ret;


    /*模块实例变量*/  
struct iic_module_t HAL_MODULE_INFO_SYM = {  
        common: {  
            tag: HARDWARE_MODULE_TAG,  
            version_major: 1,  
            version_minor: 0,  
            id: IIC_HARDWARE_MODULE_ID,  
            name: MODULE_NAME,  
            author: MODULE_AUTHOR,  
            methods: &iic_module_methods, //实现了一个open的方法供jni层调用,从而实例化eeprom_device_t 
        }  
};  


static int iic_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device){  
    struct iic_device_t* dev;
    dev = (struct iic_device_t*)malloc(sizeof(struct iic_device_t));  
    if(!dev) {  
        LOGE("iic Stub: failed to alloc space");  
        return -EFAULT;  
    }else{
LOGE("hal: alloc space succ!");
    }  
  
    memset(dev, 0, sizeof(struct iic_device_t));  
    dev->common.tag = HARDWARE_DEVICE_TAG;  
    dev->common.version = 0;  
    dev->common.module = (hw_module_t*)module;  
    dev->common.close = iic_device_close;  
    dev->iic_write = iic_write;
    dev->iic_read = iic_read;
    *device = &dev->common;     //将实例化后的iic_device_t地址返回给jni层,这样jni层就可以直接调用方法了。


    if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {  
        LOGE("iic Stub hal: failed to open /dev/i2c-1 -- %s.", strerror(errno));
free(dev);  
        return -EFAULT;  
    }else{    
        LOGI("iic Stub hal: open /dev/i2c-1 successfully."); 
iic_data.nmsgs=2; 
iic_data.msgs=(struct i2c_msg*)malloc(iic_data.nmsgs*sizeof(struct i2c_msg)); 


if(!iic_data.msgs){
                LOGE("malloc error");
close(dev->fd);
                exit(1);
        }
ioctl(dev->fd, I2C_TIMEOUT, 2);//设置超时时间
        ioctl(dev->fd, I2C_RETRIES, 1);//设置重发次数
    }
    return 0;  
} 


static int iic_device_close(struct hw_device_t* device) {  
        struct iic_device_t* iic_device = (struct iic_device_t*)device;  
      
        if(iic_device) {  
            close(iic_device->fd);  
            free(iic_device);  
        }  
          
        return 0;  
}  
      
static int iic_write(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, unsigned short subAddr, int len) {
int count = 0;
unsigned char data[2];
unsigned char bytes;


        LOGI("iic Stub hal: set value %s to device.", dataBuf); 
iic_data.nmsgs=1; 
        (iic_data.msgs[0]).len=2; //写入地址位和数据长度
        (iic_data.msgs[0]).addr=slaveAddr;// 设备地址0x50
        (iic_data.msgs[0]).flags=0; //write
        (iic_data.msgs[0]).buf=(unsigned char*)malloc(2);
while(count<len){
 bytes = 0;
 data[bytes++] =  subAddr;//先写子地址 
 data[bytes]   = dataBuf[count];//再写value
          LOGI("IIC write HAL: %x,%x", data[0],data[1]);
          (iic_data.msgs[0]).buf=data;//the data to write 
          ret=ioctl(dev->fd,I2C_RDWR,(unsigned long)&iic_data);
          if(ret<0){
             LOGI("IIC HAL ioctl error");
          }
          count++;
 subAddr++;
 usleep(3000);//延迟3毫秒
        } 
      LOGI("you have write %s into iic at %x address len: %d",dataBuf, subAddr, len);

        return 0;  
}  
      
static int iic_read(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, int len){  
      int count = 0;
      
      iic_data.nmsgs=1;
      
      (iic_data.msgs[0]).len=1; 
      (iic_data.msgs[0]).addr=slaveAddr; //  设备地址
      (iic_data.msgs[0]).flags=I2C_M_RD;//read
      (iic_data.msgs[0]).buf=(unsigned char*)malloc(1);
      while(count<len){
         (iic_data.msgs[0]).buf= dataBuf++;
if(ioctl(dev->fd,I2C_RDWR,(unsigned long)&iic_data)<0){
                        LOGE("ioctl read error");
         }
LOGI("IIC read HAL: %x", dataBuf[count]);
count++;
      } 
      
        return 0; 
}





注意:需打开设备/dev/i2c-1权限,否则会碰到Pemission Denied错误。从源码根目录下进入system/core/rootdir目录,打开ueventd.rc 添加一行:/dev/i2c-1 0666 root root (这里设备各开发板可能不同)

3. 在iic目录下编写android.mk进行编译

LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      LOCAL_MODULE_TAGS := optional
      LOCAL_PRELINK_MODULE := false
      LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
      LOCAL_SRC_FILES := iic.c
      LOCAL_MODULE := iic.default
      include $(BUILD_SHARED_LIBRARY)





编译命令:mmm -B hardware/libhardware/module/iic 编译成功会得到iic.default.so,打包进img默认会被加载。


(待续)