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. }