input子系统一 i2c设备
2016-12-06 13:48
316 查看
一、I2C体系结构
Linux的I2C体系结构分为3个组成部分:I2C核心、I2C总线驱动、I2C设备驱动,如下图所示。I2C核心提供总线驱动和设备驱动的注册、注销方法、I2C通信方法(简称algorithm);I2C总线驱动对硬件体系结构中适配器的实现,主要包括适配器i2c_adapter、适配器通信算法i2c_algorithm,如果CPU集成了I2C控制器并且linux内核支持这个CPU,那么总线驱动就不用管,比如S3C2440就属于这类情况,在后文中我们将分析它的总线驱动;I2C设备驱动是具体的一个设备(如AT24C02),挂接在CPU控制的I2C适配器的设备驱动,有了这部分驱动才能使用控制器操作该设备,设备驱动主要包括i2c_driver
和i2c_client数据结构。
二、具体描叙
1、i2c核心
I2C核心是总线驱动和设备驱动的纽带,源码位于drivers/i2c/i2c-core.c,它并不依赖于硬件平台的接口函数,I2C核心中一些重要函数如下:
增加/删除i2c_adapter
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_del_adapter(struct i2c_adapter *adapter)
增加/删除i2c_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver(struct i2c_driver *driver) //调用i2c_register_driver
void i2c_del_driver(struct i2c_driver *driver)
增加/删除i2c_client
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
void i2c_unregister_device(struct i2c_client *client)
I2C传输、发送接收
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
i2c_transfer()函数用于进行I2C 适配器和I2C 设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息,i2c_transfer()本身不能和硬件完成消息交互,它寻找i2c_adapter对应的i2c_algorithm,要实现数据传送就要实现i2c_algorithm的master_xfer(),在总线驱动中就是重点。
2、i2c总线驱动
I2C总线驱动模块的加载函数要完成两个工作:
(1)初始化I2C适配器所使用的硬件资源,如申请I/O地址、终端号;
(2)通过I2C核心提供的i2c_add_adapter()添加i2c_adapter的数据结构,当然这个i2c_adaper数据结构的成员已经被适配器的相应函数指针所初始化;
I2C总线驱动模块的卸载函数要完n成的工作与加载函数刚好相反;
(1)释放i2c适配器所使用的硬件资源,如释放I/O地址、中断号等;
(2)通过i2c_del_adapter()删除i2c_adapter删除i2c_adapter的数据结构;
3、i2c总线通信方法
我们需要为特定的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消息。
单开始信号情况:
合多开始信号的情况:
三、四个结构体描叙i2c设备
i2c结构体系主要由以下四个结构体描叙:
1、i2c_adapter适配器通俗一点说就是一种起中间连接作用的配件:
2、 i2c_driver
3、struct i2c_client
4、struct i2c_algorithm
i2c体系主要由上面四个结构体描叙,另外还有一个描叙i2c设备的的结构体:
kernel/include/linux/device.h
5、四者之间的关系
(1)i2c_adapter与i2c_algotithm
i2c_adapter对应于物理上的一个适配器,而i2c_algotithm对应于一套通信方法,一个i2c适配器需要i2c_algotithm中提供的通信函数来控制适配器上产生特定的访问周期,缺少i2c_algotithm的i2c_adapter什么也做不了,因此i2c_adapter结构体中需要包含其使用的i2c_algotithm。
i2c_algotithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即i2c消息)为单位,i2c_msg结构体也非常重要,其内容为:
struct i2c_msg{
_ _u16 addr;/*设备地址*/
_ _u16 flags;/*标志 0->发送数据,I2C_M_RD->接收数据*/
_ _u16 len; /*消息长度*/
_ _u8 *buf;/*消息数据*/
}
(2)i2c_driver与i2c_client
i2c_driver对应一套驱动方法,其只要成员函数是probe()、remove()、suspend()、resume()等,i2c_client用于描叙真实的物理设备,每一个I2C都需要一个i2c_client来描叙,i2c_driver与i2c_client的关系是一对多,一个i2c_driver上可以支持多个同等类型的i2c_client 。
(3)i2c_adpater与i2c_client
i2c_adapter与i2c_client的关系与i2c硬件体系中适配器和设备的关系一致,即i2c_client依附于依附于i2c_adpater。由于一个适配器可以连接多个I2C设备,所以一个i2c_adapter也可以被多个i2c_client依附,i2c_adapter中包括依附于它的i2c_client链表。
四、驱动代码分析(tp)
输入设备(按键、键盘、触摸屏等)是典型的字符设备,其一般的工作原理都是:底层在按键、触摸等工作发送时产生一个终端(或驱动通过timer定时轮询),然后CPU通过spi、i2c或外部存储器总线读取键值、坐标等数据,放入一个缓冲区,字符设备驱动管理该缓冲区,而驱动的read()接口让用户可以读取键值或坐标等数据。
1、驱动程序初始化和销毁模块
module_init(ft5x0x_ts_init);
module_exit(ft5x0x_ts_exit);
2、注册设备和注销设备
I2C设备驱动的模块加载函数通用的方法是在I2C设备驱动的模块加载函数通过i2c核心提供的i2c_add_driver()函数添加i2c_driver的工作,而卸载函数做相反的工作,通过i2c核心提供的i2c_del_driver()函数删除i2c_driver,代码如下:
3、I2C设备驱动要使用i2c_driver数据结构填充i2c_driver中的成员函数
static struct i2c_driver ft5x0x_ts_driver = {
.probe = ft5x0x_ts_probe,
.remove = ft5x0x_ts_remove,
.id_table = ft5x0x_ts_id,
.driver = {
.name = FOCALTECH_TS_NAME,
.owner = THIS_MODULE,
.of_match_table = focaltech_of_match,
},
.suspend = ft5x0x_suspend,
.resume = ft5x0x_resume,
};
id_table 中设备的名字,i2c总线根据设备client名字和id_table中的名字进行匹配的。如果匹配了,则返回id值,在i2c_device_match中则返回真。也就是bus的match函数将会返回真。那将会进入driver_probe_device()。
4、实现 ft5x0x_ts_probe
5、TP键值读取到上报的过程
(1)触摸产生中断,通过I2C读取底层数据
在上面的驱动probe函数中通过 kthread_run(touch_event_handler, 0, "focal-wait-queue");创建了一个线程用来处理中断函数touch_event_handler,
static int touch_event_handler(void *unused)
{
do {
mutex_lock(&g_mutex);
......
ft5x0x_update_data(); //中断执行函数
mutex_unlock(&g_mutex);
} while (!kthread_should_stop());
}
static int ft5x0x_update_data(void)
{
.........
ret = ft5x0x_i2c_rxdata(buf, 31); //通过i2c读取底层数据
input_report_abs(ft5x0x_ts->input_dev, ABS_MT_POSITION_X, x);
input_report_abs(ft5x0x_ts->input_dev, ABS_MT_POSITION_Y, y);
input_report_key(ft5x0x_ts->input_dev, BTN_TOUCH, 1);
.........
}
static int ft5x0x_i2c_rxdata(char *rxdata, int length)//I2C数据传送
{
int ret = 0;
struct i2c_msg msgs[] = {
{
.addr = this_client->addr,
.flags = 0, //发送数据
.len = 1,
.buf = rxdata,
},
{
.addr = this_client->addr,
.flags = I2C_M_RD, //接收数据
.len = length, //接收数据长度
.buf = rxdata, //接收数据保存位置
},
};
if (i2c_transfer(this_client->adapter, msgs, 2) != 2) {
ret = -EIO;
pr_err("msg %s i2c read error: %d\n", __func__, ret);
}
return ret;
}
i nt i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
函数路径:kernel/driver/i2c/i2c-core.c
最终通过调用i2c_algotithm(通信方法)中的关键函数master_xfer(),这个函数用于产生I2C访问周期需要的信号,是以i2c_mag(即I2C消息)为单位。
其中 adap 为此次主机与从机通信的适配器;msgs 为通信的数据包,这里可以是单个或多个数据包;num 用于指定数据包的个数,如果大于1则表明将进行不止一次的通信。通信一次就需要寻址一次,如果需要多次通信就需要多次寻址,前面2个接口都是进行一次通信,所以 num 为1;有的情况下我们要读一个寄存器的值,就需要先向从机发送一个寄存器地址然后再接收数据,这样如果想自己封装一个接口就需要将 num 设置为2。接口的返回值如果失败则为负数,如果成功则返回传输的数据包个数。
下篇会分析具体数据上报过程。
到这里从i2c读取数据到中断的上报介绍过程就已经完毕了,下篇分析linux中的Input子系统架构。
作者:frank_zyp
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。
Linux的I2C体系结构分为3个组成部分:I2C核心、I2C总线驱动、I2C设备驱动,如下图所示。I2C核心提供总线驱动和设备驱动的注册、注销方法、I2C通信方法(简称algorithm);I2C总线驱动对硬件体系结构中适配器的实现,主要包括适配器i2c_adapter、适配器通信算法i2c_algorithm,如果CPU集成了I2C控制器并且linux内核支持这个CPU,那么总线驱动就不用管,比如S3C2440就属于这类情况,在后文中我们将分析它的总线驱动;I2C设备驱动是具体的一个设备(如AT24C02),挂接在CPU控制的I2C适配器的设备驱动,有了这部分驱动才能使用控制器操作该设备,设备驱动主要包括i2c_driver
和i2c_client数据结构。
二、具体描叙
1、i2c核心
I2C核心是总线驱动和设备驱动的纽带,源码位于drivers/i2c/i2c-core.c,它并不依赖于硬件平台的接口函数,I2C核心中一些重要函数如下:
增加/删除i2c_adapter
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_del_adapter(struct i2c_adapter *adapter)
增加/删除i2c_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver(struct i2c_driver *driver) //调用i2c_register_driver
void i2c_del_driver(struct i2c_driver *driver)
增加/删除i2c_client
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
void i2c_unregister_device(struct i2c_client *client)
I2C传输、发送接收
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
i2c_transfer()函数用于进行I2C 适配器和I2C 设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息,i2c_transfer()本身不能和硬件完成消息交互,它寻找i2c_adapter对应的i2c_algorithm,要实现数据传送就要实现i2c_algorithm的master_xfer(),在总线驱动中就是重点。
2、i2c总线驱动
I2C总线驱动模块的加载函数要完成两个工作:
(1)初始化I2C适配器所使用的硬件资源,如申请I/O地址、终端号;
(2)通过I2C核心提供的i2c_add_adapter()添加i2c_adapter的数据结构,当然这个i2c_adaper数据结构的成员已经被适配器的相应函数指针所初始化;
I2C总线驱动模块的卸载函数要完n成的工作与加载函数刚好相反;
(1)释放i2c适配器所使用的硬件资源,如释放I/O地址、中断号等;
(2)通过i2c_del_adapter()删除i2c_adapter删除i2c_adapter的数据结构;
3、i2c总线通信方法
我们需要为特定的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消息。
static int i2c_adapter_xxx_xfer(structi2c_adapter *adap, struct i2c_msg *msgs, int num) { ...... for (i = 0; i < num; i++) { i2c_adapter_xxx_start(); /*产生起始位*/ if (msgs[i]->flags & I2C_M_RD) { /*读取*/ i2c_adapter_xxx_setaddr((msg->addr << 1) | 1);/*发送从设备地址*/ i2c_adapter_xxx_wait_ack(); /*获得从设备的ACK*/ i2c_adapter_xxx_readbytes(msgs[i]->buf,msgs[i]->len); /*读取len长度的数据到buf中*/ } else { i2c_adapter_xxx_setaddr(msg->addr << 1); i2c_adapter_xxx_wait_ack(); i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len); } } i2c_adapter_xxx_stop(); /*产生停止位*/ }
单开始信号情况:
合多开始信号的情况:
三、四个结构体描叙i2c设备
i2c结构体系主要由以下四个结构体描叙:
1、i2c_adapter适配器通俗一点说就是一种起中间连接作用的配件:
struct i2c_adapter { struct module *owner; /*所属模块*/ unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* the algorithm to access the bus 总线通信方法结构体指针*/ void *algo_data; /*algorithm数据 */ /* data fields that are valid for all devices */ struct rt_mutex bus_lock; /*控制并发访问的自旋锁*/ int timeout; /* in jiffies */ int retries;/* 重试次数 */ struct device dev; /* the adapter device 适配器设备*/ int nr; char name[48]; /*适配器名称*/ struct completion dev_released; /*用于同步*/ struct mutex userspace_clients_lock; struct list_head userspace_clients; /*client链表头*/ struct i2c_bus_recovery_info *bus_recovery_info; };
2、 i2c_driver
struct i2c_driver { unsigned int class; /* Notifies the driver that a new bus has appeared. You should avoid * using this, it will be removed in a near future. */ int (*attach_adapter)(struct i2c_adapter *) __deprecated; /* Standard driver model interfaces */ int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *); int (*suspend)(struct i2c_client *, pm_message_t mesg); int (*resume)(struct i2c_client *); /* Alert callback, for example for the SMBus alert protocol. * The format and meaning of the data value depends on the protocol. * For the SMBus alert protocol, there is a single bit of data passed * as the alert response's low bit ("event flag"). */ void (*alert)(struct i2c_client *, unsigned int data); /* a ioctl like command that can be used to perform specific functions * with the device. */ int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; const struct i2c_device_id *id_table; //驱动所支持的i2c设备的ID表 /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients; };
3、struct i2c_client
struct i2c_client { unsigned short flags; /* div., see below */ unsigned short addr; /* chip address - NOTE: 7bit 芯片地址 */ /* addresses are stored in the */ /* _LOWER_ 7 bits */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* the adapter we sit on */ struct i2c_driver *driver; /* and our access routines */ struct device dev; /* the device structure */ int irq; /* irq issued by device */ struct list_head detected; };
4、struct i2c_algorithm
struct i2c_algorithm { /* If an adapter algorithm can't do I2C-level access, set master_xfer to NULL. If an adapter algorithm can do SMBus access, set smbus_xfer. If set to NULL, the SMBus protocol is simulated using common I2C messages */ /* master_xfer should return the number of messages successfully processed, or a negative value on error */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality) (struct i2c_adapter *); };
i2c体系主要由上面四个结构体描叙,另外还有一个描叙i2c设备的的结构体:
kernel/include/linux/device.h
struct device { struct device * parent; //父设备,一般一个bus也对应一个设备。 struct kobject kobj;//代表自身 char bus_id[BUS_ID_SIZE]; struct bus_type * bus; /* 所属的总线 */ struct device_driver *driver; /* 匹配的驱动*/ struct device_node *of_node; void *driver_data; /* data private to the driver 指向驱动 */ void *platform_data; /* Platform specific data,由驱动定义并使用*/ ///更多字段忽略了 };
5、四者之间的关系
(1)i2c_adapter与i2c_algotithm
i2c_adapter对应于物理上的一个适配器,而i2c_algotithm对应于一套通信方法,一个i2c适配器需要i2c_algotithm中提供的通信函数来控制适配器上产生特定的访问周期,缺少i2c_algotithm的i2c_adapter什么也做不了,因此i2c_adapter结构体中需要包含其使用的i2c_algotithm。
i2c_algotithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即i2c消息)为单位,i2c_msg结构体也非常重要,其内容为:
struct i2c_msg{
_ _u16 addr;/*设备地址*/
_ _u16 flags;/*标志 0->发送数据,I2C_M_RD->接收数据*/
_ _u16 len; /*消息长度*/
_ _u8 *buf;/*消息数据*/
}
(2)i2c_driver与i2c_client
i2c_driver对应一套驱动方法,其只要成员函数是probe()、remove()、suspend()、resume()等,i2c_client用于描叙真实的物理设备,每一个I2C都需要一个i2c_client来描叙,i2c_driver与i2c_client的关系是一对多,一个i2c_driver上可以支持多个同等类型的i2c_client 。
(3)i2c_adpater与i2c_client
i2c_adapter与i2c_client的关系与i2c硬件体系中适配器和设备的关系一致,即i2c_client依附于依附于i2c_adpater。由于一个适配器可以连接多个I2C设备,所以一个i2c_adapter也可以被多个i2c_client依附,i2c_adapter中包括依附于它的i2c_client链表。
四、驱动代码分析(tp)
输入设备(按键、键盘、触摸屏等)是典型的字符设备,其一般的工作原理都是:底层在按键、触摸等工作发送时产生一个终端(或驱动通过timer定时轮询),然后CPU通过spi、i2c或外部存储器总线读取键值、坐标等数据,放入一个缓冲区,字符设备驱动管理该缓冲区,而驱动的read()接口让用户可以读取键值或坐标等数据。
1、驱动程序初始化和销毁模块
module_init(ft5x0x_ts_init);
module_exit(ft5x0x_ts_exit);
2、注册设备和注销设备
I2C设备驱动的模块加载函数通用的方法是在I2C设备驱动的模块加载函数通过i2c核心提供的i2c_add_driver()函数添加i2c_driver的工作,而卸载函数做相反的工作,通过i2c核心提供的i2c_del_driver()函数删除i2c_driver,代码如下:
static int __init ft5x0x_ts_init(void) { return i2c_add_driver(&ft5x0x_ts_driver); } static void __exit ft5x0x_ts_exit(void) { i2c_del_driver(&ft5x0x_ts_driver); } i2c_add_driver 调用 i2c_register_driver函数: i2c_register_driver会调用driver_register() 来将设备驱动添加到总线的设备驱动链表中: int i2c_register_driver(struct module *owner, struct i2c_driver *driver) { ........ /* add the driver to the list of i2c drivers in the driver core */ driver->driver.owner = owner; driver->driver.bus = &i2c_bus_type; res = driver_register(&driver->driver); INIT_LIST_HEAD(&driver->clients); //初始化链表 /* Walk the adapters that are already present */ i2c_for_each_dev(driver, __process_new_driver); ........ } int driver_register(struct device_driver *drv) { ........... other = driver_find(drv->name, drv->bus); //判断驱动是否已经注册 ret = bus_add_driver(drv); //将设备驱动添加到bus(总线)上 ret = driver_add_groups(drv, drv->groups); //如果grop不为空的话,将在驱动文件夹下创建以group名字的子文件夹,然后在子文件夹下 添加group的属性文件,在sysfs下表现为同一个目录 ........... }i2c总线注册好后将会有如下文件夹结构/sys/bus/i2c/,在/sys/bus/i2c/文件夹下会有如下文件夹uevent、devices、drivers、drivers_probe、drivers_autoprobe,当你注册驱动的时候,将会在/sys/bus/i2c/drivers/下注册一个改驱动的文件夹,比如ov7675,那么它将会注册成/sys/bus/i2c/drivers/ov7675/,其实这些文件夹都对应一个kobject,通过kset容器组成一个很清晰的层次结构。当driver中的.compatible与dts中的匹配上就开始执行probe函数。
3、I2C设备驱动要使用i2c_driver数据结构填充i2c_driver中的成员函数
static struct i2c_driver ft5x0x_ts_driver = {
.probe = ft5x0x_ts_probe,
.remove = ft5x0x_ts_remove,
.id_table = ft5x0x_ts_id,
.driver = {
.name = FOCALTECH_TS_NAME,
.owner = THIS_MODULE,
.of_match_table = focaltech_of_match,
},
.suspend = ft5x0x_suspend,
.resume = ft5x0x_resume,
};
id_table 中设备的名字,i2c总线根据设备client名字和id_table中的名字进行匹配的。如果匹配了,则返回id值,在i2c_device_match中则返回真。也就是bus的match函数将会返回真。那将会进入driver_probe_device()。
4、实现 ft5x0x_ts_probe
ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ft5x0x_ts_platform_data *pdata = client->dev.platform_data;//保存数据 pdata = ft5x0x_ts_parse_dt(&client->dev);//读取设备树dts中的配置信息 if (np && !pdata){ pdata = ft5x0x_ts_parse_dt(&client->dev); //接收返回的dts配置信息 if(pdata){ //获取成功 client->dev.platform_data = pdata; //将获得的数据传输给client->device->dev->platform } else{ //获取失败 err = -ENOMEM; goto exit_alloc_platform_data_failed; } } //检查适配器通信协议是否匹配I2C_FUNC_I2C if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { err = -ENODEV; goto exit_check_functionality_failed; } ft5x0x_ts->platform_data = pdata;//将获取的dts资源赋给驱动程序 this_client = client;//将client结构体赋给全局变量 ft5x0x_ts_hw_init(ft5x0x_ts);//初始化引脚,开启电源等 i2c_set_clientdata(client, ft5x0x_ts);//设置设备的私有信息 //将上面ft5x0x_ts_parse_dt定义的中断引脚设置为中断模式 client->irq = gpio_to_irq(pdata->irq_gpio_number); err = ft5x0x_read_reg(FT5X0X_REG_CIPHER, &uc_reg_value);//检查tp型号 ft5x0x_write_reg(FT5X0X_REG_PERIODACTIVE, 7); //设置报点率 INIT_WORK(&ft5x0x_ts->resume_work, ft5x0x_ts_resume_work);//初始化工作队列:用于唤醒 tp,开启中断等工作; input_dev = input_allocate_device(); //申请一个Input_dev设备 __set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);//设置键值类型 __set_bit(ABS_MT_POSITION_X, input_dev->absbit); //给设备的input_dev结构体初始化 input_set_abs_params(input_dev,ABS_MT_POSITION_X, 0, pdata->TP_MAX_X, 0, 0); input_set_abs_params(input_dev,ABS_MT_POSITION_Y, 0, pdata->TP_MAX_Y, 0, 0); err = input_register_device(input_dev); //注册输入Input_dev设备 // request_irq() 函数来注册中断服务函数 err = request_irq(client->irq, ft5x0x_ts_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_NO_SUSPEND, client->name, ft5x0x_ts); thread = kthread_run(touch_event_handler, 0, "focal-wait-queue");//创建线程执行中断函数 ......... } static inline void i2c_set_clientdata(struct i2c_client *dev, void *data) { //driver_data是驱动特殊信息的私有指针,i2c_set_clientdata(client, dev)就是将自定义的设备结构dev赋给设备 //驱动client的私有指针,我猜测是用来区别其他驱动client, dev_set_drvdata(&dev->dev, data); } static void ft5x0x_ts_hw_init(struct ft5x0x_ts_data *ft5x0x_ts) { struct regulator *reg_vdd; struct i2c_client *client = ft5x0x_ts->client; struct ft5x0x_ts_platform_data *pdata = ft5x0x_ts->platform_data; pr_info("[FST] %s [irq=%d];[rst=%d]\n",__func__, pdata->irq_gpio_number,pdata->reset_gpio_number); //获取初始化引脚 gpio_request(pdata->irq_gpio_number, "ts_irq_pin"); //获取中断引脚 gpio_request(pdata->reset_gpio_number, "ts_rst_pin"); gpio_direction_output(pdata->reset_gpio_number, 1); //引脚输出1 gpio_direction_input(pdata->irq_gpio_number); //引脚输入 reg_vdd = regulator_get(&client->dev, pdata->vdd_name); if (!WARN(IS_ERR(reg_vdd), "[FST] ft5x0x_ts_hw_init regulator: failed to get %s.\n", pdata->vdd_name)) { regulator_set_voltage(reg_vdd, 2800000, 2800000); regulator_enable(reg_vdd); } msleep(100); ft5x0x_ts_reset();//复位操作 } static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func) { return (func & i2c_get_functionality(adap)) == func; } static struct ft5x0x_ts_platform_data *ft5x0x_ts_parse_dt(struct device *dev) { struct ft5x0x_ts_platform_data *pdata; struct device_node *np = dev->of_node; int ret; pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) { dev_err(dev, "Could not allocate struct ft5x0x_ts_platform_data"); return NULL; } pdata->reset_gpio_number = of_get_gpio(np, 0); //获得复位引脚 if(pdata->reset_gpio_number < 0){ dev_err(dev, "fail to get reset_gpio_number\n"); goto fail; } pdata->irq_gpio_number = of_get_gpio(np, 1); //获得中断引脚信息 if(pdata->reset_gpio_number < 0){ dev_err(dev, "fail to get reset_gpio_number\n"); goto fail; } ret = of_property_read_string(np, "vdd_name", &pdata->vdd_name); //读取供电电压 if(ret){ dev_err(dev, "fail to get vdd_name\n"); goto fail; } ret = of_property_read_u32_array(np, "virtualkeys", &pdata->virtualkeys,12); //读取虚拟按键信息 if(ret){ dev_err(dev, "fail to get virtualkeys\n"); goto fail; } ret = of_property_read_u32(np, "TP_MAX_X", &pdata->TP_MAX_X); //读取X的最大值 if(ret){ dev_err(dev, "fail to get TP_MAX_X\n"); goto fail; } return pdata; //返回获得的数据 fail: kfree(pdata); return NULL; } struct ft5x0x_ts_platform_data{//描述tp硬件信息的结构体 int irq_gpio_number; int reset_gpio_number; const char *vdd_name; int virtualkeys[12]; int TP_MAX_X; int TP_MAX_Y; };
5、TP键值读取到上报的过程
(1)触摸产生中断,通过I2C读取底层数据
在上面的驱动probe函数中通过 kthread_run(touch_event_handler, 0, "focal-wait-queue");创建了一个线程用来处理中断函数touch_event_handler,
static int touch_event_handler(void *unused)
{
do {
mutex_lock(&g_mutex);
......
ft5x0x_update_data(); //中断执行函数
mutex_unlock(&g_mutex);
} while (!kthread_should_stop());
}
static int ft5x0x_update_data(void)
{
.........
ret = ft5x0x_i2c_rxdata(buf, 31); //通过i2c读取底层数据
input_report_abs(ft5x0x_ts->input_dev, ABS_MT_POSITION_X, x);
input_report_abs(ft5x0x_ts->input_dev, ABS_MT_POSITION_Y, y);
input_report_key(ft5x0x_ts->input_dev, BTN_TOUCH, 1);
.........
}
static int ft5x0x_i2c_rxdata(char *rxdata, int length)//I2C数据传送
{
int ret = 0;
struct i2c_msg msgs[] = {
{
.addr = this_client->addr,
.flags = 0, //发送数据
.len = 1,
.buf = rxdata,
},
{
.addr = this_client->addr,
.flags = I2C_M_RD, //接收数据
.len = length, //接收数据长度
.buf = rxdata, //接收数据保存位置
},
};
if (i2c_transfer(this_client->adapter, msgs, 2) != 2) {
ret = -EIO;
pr_err("msg %s i2c read error: %d\n", __func__, ret);
}
return ret;
}
i nt i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
函数路径:kernel/driver/i2c/i2c-core.c
最终通过调用i2c_algotithm(通信方法)中的关键函数master_xfer(),这个函数用于产生I2C访问周期需要的信号,是以i2c_mag(即I2C消息)为单位。
其中 adap 为此次主机与从机通信的适配器;msgs 为通信的数据包,这里可以是单个或多个数据包;num 用于指定数据包的个数,如果大于1则表明将进行不止一次的通信。通信一次就需要寻址一次,如果需要多次通信就需要多次寻址,前面2个接口都是进行一次通信,所以 num 为1;有的情况下我们要读一个寄存器的值,就需要先向从机发送一个寄存器地址然后再接收数据,这样如果想自己封装一个接口就需要将 num 设置为2。接口的返回值如果失败则为负数,如果成功则返回传输的数据包个数。
下篇会分析具体数据上报过程。
到这里从i2c读取数据到中断的上报介绍过程就已经完毕了,下篇分析linux中的Input子系统架构。
作者:frank_zyp
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。
相关文章推荐
- input子系统——i2c设备
- Linux设备模型之input子系统详解
- linux ------ I2C 子系统及设备驱动
- Linux i2c子系统(二) _通过i2c-dev.c访问设备的方法
- Linux i2c子系统(一) _动手写一个i2c设备驱动
- input子系统学习笔记三 驱动的分层及设备驱动层实现原理
- 设备驱动工程师之路——input子系统
- Linux设备驱动之——input子系统
- linux设备模型之i2c子系统
- 设备驱动-----Linux设备模型之input子系统详解
- Linux输入子系统:输入设备编程指南 -- input-programming.txt
- input子系统二 驱动层input设备注册
- linux IIC子系统分析(七)——实例分析通过i2c-dev操作I2C设备
- Linux I2C子系统分析-I2C设备驱动
- Linux I2C Input设备驱动代码的几点理解
- Linux输入子系统:输入设备编程指南 -- input-programming.txt
- linux IIC子系统分析(八)——实例分析通过sysfs访问I2c设备
- Linux I2C子系统分析-I2C设备驱动
- Linux设备驱动之——input子系统
- Linux输入子系统:输入设备编程指南 -- input-programming.txt