Chinaunix首页 | 论坛 | 博客
  • 博客访问: 955458
  • 博文数量: 376
  • 博客积分: 154
  • 博客等级: 入伍新兵
  • 技术积分: 1558
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-13 08:42
文章分类

全部博文(376)

文章存档

2014年(11)

2013年(88)

2012年(260)

2011年(17)

分类:

2012-08-31 14:15:41

原文地址:Linux I2C驱动(2) 作者:leon_yu

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. struct i2c_client_address_data {
  5.     unsigned short *normal_i2c;
  6.     unsigned short *probe;
  7.     unsigned short *ignore;
  8.     unsigned short **forces;
  9. };
我直接用全局参数&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.     if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA
  9.                      | I2C_FUNC_SMBUS_BYTE))
  10.         goto exit;

  11.     if (!(data = kzalloc(sizeof(struct msg213x_ts_data), GFP_KERNEL))) {
  12.         err = -ENOMEM;
  13.         goto exit;
  14.     }

  15.     new_client = &data->client;
  16.     //memset(data->data, 0xff, EEPROM_SIZE);
  17.     i2c_set_clientdata(new_client, data);
  18.     new_client->addr = address;
  19.     new_client->adapter = adapter;
  20.     new_client->driver = &msg213x_ts_driver;
  21.     new_client->flags = 0;

  22.     /* Fill in the remaining client fields */
  23.     strlcpy(new_client->name, "msg213x_ts", I2C_NAME_SIZE);

  24.     /* Tell the I2C layer a new client has arrived */
  25.     if ((err = i2c_attach_client(new_client)))
  26.         goto exit_kfree;
  27.     INIT_DELAYED_WORK(&data->work, msg213x_ts_poscheck);
  28.     data->irq = IRQ_EINT0;
  29.     s3c2410_gpio_cfgpin(S3C2410_GPF0,S3C2410_GPF0_EINT0);
  30.     set_irq_type(data->irq,IRQ_TYPE_EDGE_RISING);
  31.     err = request_irq(data->irq, msg213x_ts_isr,IRQF_SAMPLE_RANDOM ,"msg213x_ts", data);
  32.     if (err) {
  33.         printk("Unable to request uchscreen IRQ.\n");
  34.         goto exit_detach;
  35.     }
  36. #if 1
  37.     input = input_allocate_device();
  38.     if (!input) {
  39.         printk("Failed to allocate input device.\n");
  40.         err = -ENOMEM;
  41.         goto err1;
  42.     }

  43.     data->input = input;
  44.     input->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
  45.     input->keybit[BIT(BTN_TOUCH)] = BIT(BTN_TOUCH);

  46.     input_set_abs_params(input, ABS_X, 0, 0xfff, 0, 0);
  47.     input_set_abs_params(input, ABS_Y, 0, 0xfff, 0, 0);
  48.     input_set_abs_params(input, ABS_PRESSURE, 0, 1, 0, 0);

  49.     err = input_register_device(input);
  50.     if(err) {
  51.         err = -EIO;
  52.         goto fail;
  53.     }

  54.     return 0;

  55. fail:    
  56.     input_free_device(input);
  57.     kfree(input);
  58.     return err;
  59. #endif
  60.     return 0;
  61. err1:
  62.     input_free_device(input);
  63.     kfree(data);
  64. exit_detach:
  65.     i2c_detach_client(new_client);
  66. exit_kfree:
  67.     kfree(data);
  68. exit:
  69.     return err;
  70. }
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. int main(int argc, char **argv)
  9. {

  10.     int fd;
  11.     int addr = (0x26);    /* The I2C address */
  12.     char reg = 0x00;    /* Device register to access */
  13.     int tmp, val;

  14.     //char buf[10];

  15.     if (argc < 3) {
  16.         printf("too little args\n");
  17.         exit(1);
  18.     }

  19.     if ((fd = open("/dev/i2c-0", O_RDWR)) < 0) {
  20.         /* ERROR HANDLING; you can check errno to see what went wrong */
  21.         printf("can't open the i2c adapter dev\n");
  22.         exit(1);
  23.     }

  24.     if (ioctl(fd, I2C_SLAVE_FORCE, addr) < 0) {
  25.         printf("can't set i2c addr\n");
  26.         exit(1);
  27.     }

  28.     if (argc == 3 && !strcmp("r", argv[1])) {
  29.         reg = strtoul(argv[2], NULL, 16);
  30.         tmp = i2c_smbus_read_byte_data(fd, reg);
  31.         printf("read reg[0x%x]=%x\n", reg, tmp);
  32.     } else if (argc == 4 && !strcmp("w", argv[1])) {
  33.         reg = strtoul(argv[2], NULL, 16);
  34.         val = strtoul(argv[3], NULL, 16);
  35.         tmp = i2c_smbus_write_byte_data(fd, reg, val);
  36.         printf("i2c write....\n");
  37.         //tmp = i2c_smbus_read_byte_data(fd, reg);
  38.         //printf("read reg[0x%x]=%x\n", reg, tmp);

  39.     }

  40.     close(fd);
  41.     return 0;
  42. }


阅读(585) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~