1.Linux I2C驱动流程

①提供I2C适配器的硬件驱动,探测、初始化I2C适配器(申请I2C的地址中断号等),驱动CPU控制的I2C适配器从硬件上产生各种信号以及处理I2C中断等



②提供I2C适配器的algorithm,用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋值给i2c_adapter的algo指针



③实现I2C设备驱动与i2c_driver接口,用具体设备yyy的yyy_attach_adapter()函数指针、yyy_detach_client()函数指针和yyy_command()函数指针的赋值给i2c_driver的attach_adapter、datach_adapter和datach_client指针。



④实现i2c设备驱动的文件操作接口,即实现具体设备yyy的yyy_read()、yyy_write()和yyy_ioctl()等。



2.I2C总线驱动


(1)I2C适配器驱动加载:


  ①初始化I2C适配器所使用的硬件资源,如申请I/O地址,中断号等。



  ②通过i2c_add_adapter()添加i2c_adapter的数据结构,当然这个i2c_adapter数据机构以及被xxx适配器的相应函数指针所初始化。


(2) I2C适配器驱动卸载:


  ①释放i2c适配器所使用的硬件资源,如释放I/O地址,中断号等。


  ②通过i2c_del_adapter()删除i2c_adapter的数据机构

1. static int __init i2c_adapter_xxx_init(void)
2. {
3. Xxx_adapter_hw_init();
4. i2c_add_adapter(&xxx_adapter);
5. }
6. static void __exit i2c_adapter_xxx_exit(void)
7. {
8. xxx_adapter_hw_free();
9. i2c_del_adapter(&xxx_adapter);
10. }

I2C总线通信方法,i2c_algorithm的master_xfer()和functionality()函数。



Functionality()函数非常简单,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C,I2C_FUNC_10BIT_ADDR,I2C_FUNC_SMBUS_READ_BYTE



master_xfer() 函数的I2C适配器上完成传递给它的i2c_msg数组中的每个i2c消息,是 真正操作硬件的函数 。


3.I2C设备驱动


主要使用两个文件结构i2c_driver和i2c_client,填充其中成员,并添加到总线上。I2c_client一般被包含在设备的私有信息结构体上,而i2c_driver一般被定义为全局变量并初始化。


    1. /* This is the driver that will be inserted */
    2. static struct i2c_driver msg213x_ts_driver = {
    3. .driver = {
    4. .name = "msg213x_ts",
    5. },
    6. .id = I2C_DRIVERID_MSG213X,
    7. .attach_adapter = msg213x_ts_attach_adapter,
    8. .detach_client = msg213x_ts_detach_client,
    9. .command = msg213x_ts_command,
    10. };


    (1)I2C设备驱动的模块加载


      ①通过register_chrdev()注册包含I2C的字符设备


      ②通过I2C核心的i2c_add_driver()函数添加i2c_driver


    (2)I2C设备驱动的模块卸载


      ①通过I2C核心的i2c_del_driver()函数添加i2c_driver



      ②通过unregister_chrdev()函数注销字符设备



    i2c_driver成员函数,当i2c_add_driver(&yyy_driver)执行的时候会引发i2c_driver结构体中的yyy_attach_adapter()函数的执行,可以在yyy_attach_adapter()函数里探测物理设备,为了实现探测,yyy_attach_adapter()也只需简单的调用i2c_probe()函数

    1. static int msg213x_ts_attach_adapter(struct i2c_adapter *adapter)
    2. {
    3. return i2c_probe(adapter, &addr_data, msg213x_ts_detect);
    4. }

    第一个参数adapter是适配器指针,第二个参数是要探测的地址,第三个参数是探测函数。



    关于探测地址,我手头2.6.21版内核数据结构定义如下

    1. int i2c_probe(struct i2c_adapter *adapter,
    2. struct i2c_client_address_data *address_data,
    3. int (*found_proc) (struct i2c_adapter *, int, int))
    4.
    5. struct i2c_client_address_data {
    6. unsigned short *normal_i2c;
    7. unsigned short *probe;
    8. unsigned short *ignore;
    9. unsigned short **forces;
    10. };

    我直接用全局参数&addr_data,并且在本文件有如下变量



    static unsigned short normal_i2c[] = { 0x26,I2C_CLIENT_END };



    适配器可以正确访问0x26设备,这个地址如何定义的,我没想明白。



    i2c_probe()函数引发msg213x_ts_detect()的调用,可以在msg213x_ts_detect()中初始化i2c_client。


      1. /* This function is called by i2c_probe */
      2. static int msg213x_ts_detect(struct i2c_adapter *adapter, int address, int kind)
      3. {
      4. struct i2c_client *new_client;
      5. struct msg213x_ts_data *data;
      6. struct input_dev *input;
      7. int err = 0;
      8.
      9. if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA
      10. | I2C_FUNC_SMBUS_BYTE))
      11. goto exit;
      12.
      13. if (!(data = kzalloc(sizeof(struct msg213x_ts_data), GFP_KERNEL))) {
      14. err = -ENOMEM;
      15. goto exit;
      16. }
      17.
      18. new_client = &data->client;
      19. //memset(data->data, 0xff, EEPROM_SIZE);
      20. i2c_set_clientdata(new_client, data);
      21. new_client->addr = address;
      22. new_client->adapter = adapter;
      23. new_client->driver = &msg213x_ts_driver;
      24. new_client->flags = 0;
      25.
      26. /* Fill in the remaining client fields */
      27. strlcpy(new_client->name, "msg213x_ts", I2C_NAME_SIZE);
      28.
      29. /* Tell the I2C layer a new client has arrived */
      30. if ((err = i2c_attach_client(new_client)))
      31. goto exit_kfree;
      32. INIT_DELAYED_WORK(&data->work, msg213x_ts_poscheck);
      33. data->irq = IRQ_EINT0;
      34. s3c2410_gpio_cfgpin(S3C2410_GPF0,S3C2410_GPF0_EINT0);
      35. set_irq_type(data->irq,IRQ_TYPE_EDGE_RISING);
      36. err = request_irq(data->irq, msg213x_ts_isr,IRQF_SAMPLE_RANDOM ,"msg213x_ts", data);
      37. if (err) {
      38. printk("Unable to request uchscreen IRQ.\n");
      39. goto exit_detach;
      40. }
      41. #if 1
      42. input = input_allocate_device();
      43. if (!input) {
      44. printk("Failed to allocate input device.\n");
      45. err = -ENOMEM;
      46. goto err1;
      47. }
      48.
      49. data->input = input;
      50. input->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
      51. input->keybit[BIT(BTN_TOUCH)] = BIT(BTN_TOUCH);
      52.
      53. input_set_abs_params(input, ABS_X, 0, 0xfff, 0, 0);
      54. input_set_abs_params(input, ABS_Y, 0, 0xfff, 0, 0);
      55. input_set_abs_params(input, ABS_PRESSURE, 0, 1, 0, 0);
      56.
      57. err = input_register_device(input);
      58. if(err) {
      59. err = -EIO;
      60. goto fail;
      61. }
      62.
      63. return 0;
      64.
      65. fail:
      66. input_free_device(input);
      67. kfree(input);
      68. return err;
      69. #endif
      70. return 0;
      71. err1:
      72. input_free_device(input);
      73. kfree(data);
      74. exit_detach:
      75. i2c_detach_client(new_client);
      76. exit_kfree:
      77. kfree(data);
      78. exit:
      79. return err;
      80. }


      i2c_driver中重要函数yyy_command()实现了针对设备的控制命令,比如实时时钟,命令设置时间和获取时间,AD芯片,命令用来设置采集方式,选择通道等。


        1. static int yyy_command(struct i2c_client *client,unsigned int cmd,void *arg)
        2. {
        3. switch (cmd)
        4. {
        5. case YYY_CMD0:
        6. return yyy_cmd0(client,arg);
        7. case YYY_CMD1:
        8. return yyy_cmd1(client,arg);
        9. default:
        10. return – EINVAL;
        11. }
        12. }


        具体命令实现通过i2c_msg消息数组,调用i2c核心传输发送函数完成。




        4.Linux I2C设备驱动的文件接口操作


        如果I2C设备不是I/O设备或储存设备,就并不需要给I2C设备提供读写函数,文件操作接口用的不多,一般用I2c-dev.c提供的i2c适配器设备文件接口就可以完成对i2c设备的读写。


        一个实例。


          1. #include <stdio.h>
          2. #include <stdlib.h>
          3. #include <unistd.h>
          4. #include <sys/ioctl.h>
          5. #include "i2c-dev.h"
          6. //#include <linux/i2c.h>
          7. #include <fcntl.h>
          8.
          9. int main(int argc, char **argv)
          10. {
          11.
          12. int fd;
          13. int addr = (0x26); /* The I2C address */
          14. char reg = 0x00; /* Device register to access */
          15. int tmp, val;
          16.
          17. //char buf[10];
          18.
          19. if (argc < 3) {
          20. printf("too little args\n");
          21. exit(1);
          22. }
          23.
          24. if ((fd = open("/dev/i2c-0", O_RDWR)) < 0) {
          25. /* ERROR HANDLING; you can check errno to see what went wrong */
          26. printf("can't open the i2c adapter dev\n");
          27. exit(1);
          28. }
          29.
          30. if (ioctl(fd, I2C_SLAVE_FORCE, addr) < 0) {
          31. printf("can't set i2c addr\n");
          32. exit(1);
          33. }
          34.
          35. if (argc == 3 && !strcmp("r", argv[1])) {
          36. reg = strtoul(argv[2], NULL, 16);
          37. tmp = i2c_smbus_read_byte_data(fd, reg);
          38. printf("read reg[0x%x]=%x\n", reg, tmp);
          39. } else if (argc == 4 && !strcmp("w", argv[1])) {
          40. reg = strtoul(argv[2], NULL, 16);
          41. val = strtoul(argv[3], NULL, 16);
          42. tmp = i2c_smbus_write_byte_data(fd, reg, val);
          43. printf("i2c write....\n");
          44. //tmp = i2c_smbus_read_byte_data(fd, reg);
          45. //printf("read reg[0x%x]=%x\n", reg, tmp);
          46.
          47. }
          48.
          49. close(fd);
          50. return 0;
          51. }