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的数据机构
-
static int __init i2c_adapter_xxx_init(void)
-
{
-
Xxx_adapter_hw_init();
-
i2c_add_adapter(&xxx_adapter);
-
}
-
static void __exit i2c_adapter_xxx_exit(void)
-
{
-
xxx_adapter_hw_free();
-
i2c_del_adapter(&xxx_adapter);
-
}
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一般被定义为全局变量并初始化。
-
/* This is the driver that will be inserted */
-
static struct i2c_driver msg213x_ts_driver = {
-
.driver = {
-
.name = "msg213x_ts",
-
},
-
.id = I2C_DRIVERID_MSG213X,
-
.attach_adapter = msg213x_ts_attach_adapter,
-
.detach_client = msg213x_ts_detach_client,
-
.command = msg213x_ts_command,
-
};
(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()函数
-
static int msg213x_ts_attach_adapter(struct i2c_adapter *adapter)
-
{
-
return i2c_probe(adapter, &addr_data, msg213x_ts_detect);
-
}
第一个参数adapter是适配器指针,第二个参数是要探测的地址,第三个参数是探测函数。
关于探测地址,我手头2.6.21版内核数据结构定义如下
-
int i2c_probe(struct i2c_adapter *adapter,
-
struct i2c_client_address_data *address_data,
-
int (*found_proc) (struct i2c_adapter *, int, int))
-
-
struct i2c_client_address_data {
-
unsigned short *normal_i2c;
-
unsigned short *probe;
-
unsigned short *ignore;
-
unsigned short **forces;
-
};
我直接用全局参数&addr_data,并且在本文件有如下变量
static unsigned short normal_i2c[] = { 0x26,I2C_CLIENT_END };
适配器可以正确访问0x26设备,这个地址如何定义的,我没想明白。
i2c_probe()函数引发msg213x_ts_detect()的调用,可以在msg213x_ts_detect()中初始化i2c_client。
-
/* This function is called by i2c_probe */
-
static int msg213x_ts_detect(struct i2c_adapter *adapter, int address, int kind)
-
{
-
struct i2c_client *new_client;
-
struct msg213x_ts_data *data;
-
struct input_dev *input;
-
int err = 0;
-
-
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA
-
| I2C_FUNC_SMBUS_BYTE))
-
goto exit;
-
-
if (!(data = kzalloc(sizeof(struct msg213x_ts_data), GFP_KERNEL))) {
-
err = -ENOMEM;
-
goto exit;
-
}
-
-
new_client = &data->client;
-
//memset(data->data, 0xff, EEPROM_SIZE);
-
i2c_set_clientdata(new_client, data);
-
new_client->addr = address;
-
new_client->adapter = adapter;
-
new_client->driver = &msg213x_ts_driver;
-
new_client->flags = 0;
-
-
/* Fill in the remaining client fields */
-
strlcpy(new_client->name, "msg213x_ts", I2C_NAME_SIZE);
-
-
/* Tell the I2C layer a new client has arrived */
-
if ((err = i2c_attach_client(new_client)))
-
goto exit_kfree;
-
INIT_DELAYED_WORK(&data->work, msg213x_ts_poscheck);
-
data->irq = IRQ_EINT0;
-
s3c2410_gpio_cfgpin(S3C2410_GPF0,S3C2410_GPF0_EINT0);
-
set_irq_type(data->irq,IRQ_TYPE_EDGE_RISING);
-
err = request_irq(data->irq, msg213x_ts_isr,IRQF_SAMPLE_RANDOM ,"msg213x_ts", data);
-
if (err) {
-
printk("Unable to request uchscreen IRQ.\n");
-
goto exit_detach;
-
}
-
#if 1
-
input = input_allocate_device();
-
if (!input) {
-
printk("Failed to allocate input device.\n");
-
err = -ENOMEM;
-
goto err1;
-
}
-
-
data->input = input;
-
input->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
-
input->keybit[BIT(BTN_TOUCH)] = BIT(BTN_TOUCH);
-
-
input_set_abs_params(input, ABS_X, 0, 0xfff, 0, 0);
-
input_set_abs_params(input, ABS_Y, 0, 0xfff, 0, 0);
-
input_set_abs_params(input, ABS_PRESSURE, 0, 1, 0, 0);
-
-
err = input_register_device(input);
-
if(err) {
-
err = -EIO;
-
goto fail;
-
}
-
-
return 0;
-
-
fail:
-
input_free_device(input);
-
kfree(input);
-
return err;
-
#endif
-
return 0;
-
err1:
-
input_free_device(input);
-
kfree(data);
-
exit_detach:
-
i2c_detach_client(new_client);
-
exit_kfree:
-
kfree(data);
-
exit:
-
return err;
-
}
i2c_driver中重要函数yyy_command()实现了针对设备的控制命令,比如实时时钟,命令设置时间和获取时间,AD芯片,命令用来设置采集方式,选择通道等。
-
static int yyy_command(struct i2c_client *client,unsigned int cmd,void *arg)
-
{
-
switch (cmd)
-
{
-
case YYY_CMD0:
-
return yyy_cmd0(client,arg);
-
case YYY_CMD1:
-
return yyy_cmd1(client,arg);
-
default:
-
return – EINVAL;
-
}
-
}
具体命令实现通过i2c_msg消息数组,调用i2c核心传输发送函数完成。
4.Linux I2C设备驱动的文件接口操作
如果I2C设备不是I/O设备或储存设备,就并不需要给I2C设备提供读写函数,文件操作接口用的不多,一般用I2c-dev.c提供的i2c适配器设备文件接口就可以完成对i2c设备的读写。
一个实例。
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <unistd.h>
-
#include <sys/ioctl.h>
-
#include "i2c-dev.h"
-
//#include <linux/i2c.h>
-
#include <fcntl.h>
-
-
int main(int argc, char **argv)
-
{
-
-
int fd;
-
int addr = (0x26); /* The I2C address */
-
char reg = 0x00; /* Device register to access */
-
int tmp, val;
-
-
//char buf[10];
-
-
if (argc < 3) {
-
printf("too little args\n");
-
exit(1);
-
}
-
-
if ((fd = open("/dev/i2c-0", O_RDWR)) < 0) {
-
/* ERROR HANDLING; you can check errno to see what went wrong */
-
printf("can't open the i2c adapter dev\n");
-
exit(1);
-
}
-
-
if (ioctl(fd, I2C_SLAVE_FORCE, addr) < 0) {
-
printf("can't set i2c addr\n");
-
exit(1);
-
}
-
-
if (argc == 3 && !strcmp("r", argv[1])) {
-
reg = strtoul(argv[2], NULL, 16);
-
tmp = i2c_smbus_read_byte_data(fd, reg);
-
printf("read reg[0x%x]=%x\n", reg, tmp);
-
} else if (argc == 4 && !strcmp("w", argv[1])) {
-
reg = strtoul(argv[2], NULL, 16);
-
val = strtoul(argv[3], NULL, 16);
-
tmp = i2c_smbus_write_byte_data(fd, reg, val);
-
printf("i2c write....\n");
-
//tmp = i2c_smbus_read_byte_data(fd, reg);
-
//printf("read reg[0x%x]=%x\n", reg, tmp);
-
-
}
-
-
close(fd);
-
return 0;
-
}
阅读(1537) | 评论(0) | 转发(0) |