往期内容
本专栏往期内容:
- input子系统的框架和重要数据结构详解-CSDN博客
- input device和input handler的注册以及匹配过程解析-CSDN博客
- input device和input handler的注册以及匹配过程解析-CSDN博客
- 编写一个简单的Iinput_dev框架-CSDN博客
- GPIO按键驱动分析与使用:input_dev层-CSDN博客
I2C子系统专栏:
- 专栏地址:IIC子系统_憧憬一下的博客-CSDN博客
- 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
– 末篇,有往期内容观看顺序总线和设备树专栏:
- 专栏地址:总线和设备树_憧憬一下的博客-CSDN博客
- 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
– 末篇,有往期内容观看顺序
前言
-
Linux 4.x内核
- Documentation\devicetree\bindings\input\touchscreen\goodix.txt
- drivers/input/touchscreen/gt9xx/gt9xx.c ?gt9xx.c
-
设备树
- IMX6ULL:
Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dts
- IMX6ULL:
本节主要讲解内核中提供的gt9xx.c,来看看是如何通过input子系统来实现一个IIC协议的传输,之后在此基础上编写一个简单的input子系统的IIC驱动
1. 驱动程序框架
2. 设备树示例
&i2c2 {
gt9xx@5d {
compatible = "goodix,gt9xx";
reg = <0x5d>;
status = "okay";
interrupt-parent = <&gpio1>;
interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc_reset &pinctrl_touchscreen_int>;
/*pinctrl-1 = <&pinctrl_tsc_irq>;*/
/*
pinctrl-names = "default", "int-output-low", "int-output-high", "int-input";
pinctrl-0 = <&ts_int_default>;
pinctrl-1 = <&ts_int_output_low>;
pinctrl-2 = <&ts_int_output_high>;
pinctrl-3 = <&ts_int_input>;
*/
reset-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;
irq-gpios = <&gpio1 5 IRQ_TYPE_EDGE_FALLING>;
irq-flags = <2>; /*1:rising 2: falling*/
touchscreen-max-id = <5>;
touchscreen-size-x = <800>;
touchscreen-size-y = <480>;
touchscreen-max-w = <1024>;
touchscreen-max-p = <1024>;
/*touchscreen-key-map = <172>, <158>;*/ /*KEY_HOMEPAGE, KEY_BACK*/
goodix,type-a-report = <0>;
goodix,driver-send-cfg = <0>;
goodix,create-wr-node = <1>;
goodix,wakeup-with-reset = <0>;
goodix,resume-in-workqueue = <0>;
goodix,int-sync = <0>;
goodix,swap-x2y = <0>;
goodix,esd-protect = <0>;
goodix,pen-suppress-finger = <0>;
goodix,auto-update = <0>;
goodix,auto-update-cfg = <0>;
goodix,power-off-sleep = <0>;
/* ...... */
};
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
这段设备树(Device Tree)定义配置了一个基于 I2C 总线的 Goodix GT9xx 系列触摸屏设备,设备地址为 0x5d
。
1. 节点和基础属性
&i2c2 {
gt9xx@5d {
compatible = "goodix,gt9xx";
reg = <0x5d>;
- 1
- 2
- 3
- 4
&i2c2
:指向 I2C 总线控制器的节点,这意味着该设备挂载在 I2C 总线 2 上。gt9xx@5d
:表示该触摸屏设备的 I2C 地址为0x5d
。compatible = "goodix,gt9xx";
:指定设备兼容字符串,匹配 Goodix 的 GT9xx 系列触摸屏设备驱动程序。reg = <0x5d>;
:设备的 I2C 地址,定义为0x5d
。
2. 中断与引脚控制配置
status = "okay";
interrupt-parent = <&gpio1>;
interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc_reset &pinctrl_touchscreen_int>;
- 1
- 2
- 3
- 4
- 5
status = "okay";
:设备状态,标记为启用。interrupt-parent = <&gpio1>;
:指定中断的父节点为gpio1
,即 GPIO 控制器 1。interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
:中断配置,使用 GPIO1 控制器的第 5 个引脚,触发模式为下降沿触发(IRQ_TYPE_EDGE_FALLING
)。pinctrl-names
和pinctrl-0
:配置引脚控制器,pinctrl-names
指定了配置的名字,pinctrl-0
则定义了相关引脚配置(pinctrl_tsc_reset
和pinctrl_touchscreen_int
),用于触摸屏重置和中断引脚初始化。
3. GPIO 和中断标志
reset-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;
irq-gpios = <&gpio1 5 IRQ_TYPE_EDGE_FALLING>;
irq-flags = <2>;
- 1
- 2
- 3
reset-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;
:触摸屏的复位引脚定义在gpio5
的第 2 引脚,信号低电平有效。irq-gpios = <&gpio1 5 IRQ_TYPE_EDGE_FALLING>;
:触摸屏的中断引脚配置为gpio1
控制器的第 5 引脚,设置为下降沿触发。irq-flags = <2>;
:中断触发标志,2
表示下降沿触发。
4. 触摸屏参数
touchscreen-max-id = <5>;
touchscreen-size-x = <800>;
touchscreen-size-y = <480>;
touchscreen-max-w = <1024>;
touchscreen-max-p = <1024>;
- 1
- 2
- 3
- 4
- 5
touchscreen-max-id = <5>;
:最大触摸点数为 5,表示最多支持 5 个手指的多点触控。touchscreen-size-x
和touchscreen-size-y
:触摸屏 X、Y 轴的分辨率,分别为 800 和 480。touchscreen-max-w
和touchscreen-max-p
:触控宽度和压力的最大值,均为 1024。
5. Goodix 驱动特性
goodix,type-a-report = <0>;
goodix,driver-send-cfg = <0>;
goodix,create-wr-node = <1>;
goodix,wakeup-with-reset = <0>;
goodix,resume-in-workqueue = <0>;
goodix,int-sync = <0>;
goodix,swap-x2y = <0>;
goodix,esd-protect = <0>;
goodix,pen-suppress-finger = <0>;
goodix,auto-update = <0>;
goodix,auto-update-cfg = <0>;
goodix,power-off-sleep = <0>;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
-
Goodix 特定属性:
goodix,xxx
属性定义了一些特性和行为,例如:goodix,type-a-report
和goodix,driver-send-cfg
:控制是否启用 A 类型报告和驱动是否发送配置信息。goodix,create-wr-node
:创建 WR(写)节点的标志。goodix,wakeup-with-reset
和goodix,power-off-sleep
:控制唤醒和电源管理的行为。- 其他 Goodix 触摸屏特性配置,按需要启用或禁用。
这些属性通过设备树配置,使系统能够正确初始化并管理触摸屏设备。
3. 驱动程序分析
gt9xx.c
3.1 分配/设置/注册input_dev
gtp_probe
ret = gtp_request_input_dev(ts);
ts->input_dev = input_allocate_device();
......
ret = input_register_device(ts->input_dev);
ret = gtp_request_irq(ts);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
3.2 注册中断处理函数
ret = request_threaded_irq(ts->client->irq, NULL,
gtp_irq_handler,
ts->pdata->irq_flags | IRQF_ONESHOT,
ts->client->name,
ts);
- 1
- 2
- 3
- 4
- 5
3.3 中断处理函数分析
通过I2C函数( 2c_tansfer)读取数据、上报数据。
static irqreturn_t gtp_irq_handler(int irq, void *dev_id)
{
struct goodix_ts_data *ts = dev_id;
gtp_work_func(ts);
return IRQ_HANDLED;
}
static void gtp_work_func(struct goodix_ts_data *ts)
{
u8 point_state = 0; // 保存触摸点的状态
u8 key_value = 0; // 保存按键的状态
s32 i = 0; // 循环变量,用于迭代按键事件
s32 ret = -1; // 函数调用返回值
static u8 pre_key; // 保存上一次的按键值,用于识别按键变化
struct goodix_point_t points[GTP_MAX_TOUCH_ID]; // 存储触摸点信息
// 检查是否正在执行复位操作,若是,则退出函数
if (test_bit(PANEL_RESETTING, &ts->flags))
return;
// 检查工作线程是否启用,若未启用,则退出函数
if (!test_bit(WORK_THREAD_ENABLED, &ts->flags))
return;
// 手势事件处理,检测滑动唤醒功能是否启用,且设备是否处于休眠模式
if (ts->pdata->slide_wakeup && test_bit(DOZE_MODE, &ts->flags)) {
// 调用手势处理函数 gtp_gesture_handler
ret = gtp_gesture_handler(ts);
if (ret) {
// 若处理失败,记录错误日志
dev_err(&ts->client->dev,
"Failed handler gesture event %d\n", ret);
}
return; // 处理完成后退出函数
}
// 获取触摸点的状态及按键状态
-----------(1)
point_state = gtp_get_points(ts, points, &key_value);
if (!point_state) {
// 若未检测到有效的触摸点,记录调试日志
dev_dbg(&ts->client->dev, "Invalid finger points\n");
return; // 没有有效触摸点,直接返回
}
// 处理触摸按键事件
if (key_value & 0xf0 || pre_key & 0xf0) { // 判断是否有按键事件
// 处理手写笔的按键
switch (key_value) {
case 0x40: // 两个按键都按下
input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 1);
input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 1);
break;
case 0x10: // 按下第一个按键
input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 1);
input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 0);
dev_dbg(&ts->client->dev, "pen button1 down\n");
break;
case 0x20: // 按下第二个按键
input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 0);
input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 1);
break;
default: // 没有按键按下,恢复初始状态
input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 0);
input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 0);
dev_dbg(&ts->client->dev, "button1 up\n");
break;
}
input_sync(ts->input_dev); // 同步输入事件
pre_key = key_value; // 更新上次按键状态
}
else if (key_value & 0x0f || pre_key & 0x0f) { // 判断是否有面板按键事件
// 遍历所有按键,检测是否有按键状态发生变化
for (i = 0; i < ts->pdata->key_nums; i++) {
if ((pre_key | key_value) & (0x01 << i))
input_report_key(ts->input_dev,
ts->pdata->key_map[i], // 具体按键映射
key_value & (0x01 << i)); // 按键状态
}
input_sync(ts->input_dev); // 同步输入事件
pre_key = key_value; // 更新上次按键状态
}
// 选择不同的报告格式进行触摸点的处理
if (!ts->pdata->type_a_report)
gtp_mt_slot_report(ts, point_state & 0x0f, points); // Slot方式
----------------------(2)
else
gtp_type_a_report(ts, point_state & 0x0f, points); // Type A方式
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
(1)point_state = gtp_get_points(ts, points, &key_value);
: 从 I2C 总线上读取触摸点的数据和触摸状态。提取坐标、压力值、工具类型等信息,若需要则交换 X/Y 坐标。 将手写笔和普通触摸点区分,按需要调整触摸点数据。完成数据读取后,通过发送结束命令结束当前触摸事件。 ret = gtp_i2c_read(ts->client, point_data, 12); //这里内部就调用到了i2c_transfer发起传输
static u8 gtp_get_points(struct goodix_ts_data *ts,
struct goodix_point_t *points,
u8 *key_value)
{
int ret; // 保存 I2C 传输返回状态
int i; // 循环变量
u8 *coor_data = NULL; // 指向触摸坐标数据的指针
u8 finger_state = 0; // 保存手指状态信息
u8 touch_num = 0; // 保存当前触摸点的数量
u8 end_cmd[3] = { // 用于向触摸屏发送 "结束" 命令
GTP_READ_COOR_ADDR >> 8,
GTP_READ_COOR_ADDR & 0xFF,
0
};
u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH_ID + 1] = { // 存储触摸点的数据
GTP_READ_COOR_ADDR >> 8,
GTP_READ_COOR_ADDR & 0xFF
};
// 读取触摸点的坐标数据
ret = gtp_i2c_read(ts->client, point_data, 12); //这里内部就调用到了i2c_transfer发起传输
if (ret < 0) {
dev_err(&ts->client->dev, "I2C transfer error. errno:%d\n ", ret);
return 0; // 读取失败返回0
}
finger_state = point_data[GTP_ADDR_LENGTH]; // 获取触摸屏状态
if (finger_state == 0x00) // 若无触摸点
return 0;
// 获取触摸点数量(低 4 位表示触摸点数量)
touch_num = finger_state & 0x0f;
// 检查触摸状态是否合法,如超出最大触摸数或没有触摸
if ((finger_state & MASK_BIT_8) == 0 || touch_num > ts->pdata->max_touch_id) {
dev_err(&ts->client->dev, "Invalid touch state: 0x%x", finger_state);
finger_state = 0; // 若非法则重置触摸状态
goto exit_get_point; // 跳转到退出点
}
// 若有多个触摸点,则读取后续触摸点数据
if (touch_num > 1) {
u8 buf[8 * GTP_MAX_TOUCH_ID] = {
(GTP_READ_COOR_ADDR + 10) >> 8,
(GTP_READ_COOR_ADDR + 10) & 0xff
};
ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1));
if (ret < 0) {
dev_err(&ts->client->dev, "I2C error. %d\n", ret);
finger_state = 0;
goto exit_get_point;
}
memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
}
// 触摸按键事件,低 4 位标识按键事件
*key_value = point_data[3 + 8 * touch_num];
// 初始化触摸点信息数组
memset(points, 0, sizeof(*points) * GTP_MAX_TOUCH_ID);
for (i = 0; i < touch_num; i++) {
coor_data = &point_data[i * 8 + 3]; // 获取触摸点的坐标数据
points[i].id = coor_data[0]; // 触摸点 ID
points[i].x = coor_data[1] | (coor_data[2] << 8); // 触摸点的 X 坐标
points[i].y = coor_data[3] | (coor_data[4] << 8); // 触摸点的 Y 坐标
points[i].w = coor_data[5] | (coor_data[6] << 8); // 触摸点的宽度
points[i].p = coor_data[5] | (coor_data[6] << 8); // 压力值
// 判断是否需要交换 X/Y 坐标
if (ts->pdata->swap_x2y)
GTP_SWAP(points[i].x, points[i].y);
dev_dbg(&ts->client->dev, "[%d][%d %d %d]\n", points[i].id, points[i].x, points[i].y, points[i].p);
// 判断是否为手写笔触摸点
if (points[i].id & 0x80) {
points[i].tool_type = GTP_TOOL_PEN;
points[i].id = 10; // 手写笔 ID 固定为 10
if (ts->pdata->pen_suppress_finger) {
points[0] = points[i];
memset(++points, 0, sizeof(*points) * (GTP_MAX_TOUCH_ID - 1));
finger_state &= 0xf0;
finger_state |= 0x01; // 设置第一个触摸点为手写笔
break;
}
} else {
points[i].tool_type = GTP_TOOL_FINGER;
}
}
exit_get_point:
// 若未处于 RAW 数据模式,写入结束命令以结束当前触摸事件
if (!test_bit(RAW_DATA_MODE, &ts->flags)) {
ret = gtp_i2c_write(ts->client, end_cmd, 3);
if (ret < 0)
dev_info(&ts->client->dev, "I2C write end_cmd error!");
}
return finger_state; // 返回触摸屏状态
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
(2)gtp_irq_handler中你的gtp_mt_slot_report(ts, point_state & 0x0f, points); // Slot方式
。
- 函数主要负责报告多点触控的触摸点状态,包括其位置、工具类型和压力等。
- 使用位图(
cur_touch
和pre_touch
)跟踪当前和先前的触摸 ID,从而确定哪些触摸点是新触摸、哪些是结束触摸。
static void gtp_mt_slot_report(struct goodix_ts_data *ts, u8 touch_num,
struct goodix_point_t *points)
{
int i; // 循环变量
u16 cur_touch = 0; // 当前触摸 ID 位图
static u16 pre_touch; // 上一帧的触摸 ID 位图
static u8 pre_pen_id; // 上一次触摸的笔 ID
// 遍历每个可能的触摸点,最大触摸 ID 由 ts->pdata->max_touch_id 决定
for (i = 0; i < ts->pdata->max_touch_id; i++) {
// 检查当前触摸点是否有效
if (touch_num && i == points->id) {
// 设置当前触摸点的槽(slot)
input_mt_slot(ts->input_dev, points->id);
// 判断工具类型
if (points->tool_type == GTP_TOOL_PEN) {
// 如果是笔工具,报告笔的状态为真
input_mt_report_slot_state(ts->input_dev, MT_TOOL_PEN, true);
pre_pen_id = points->id; // 更新上一次的笔 ID
} else {
// 否则为手指工具,报告状态为真
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
}
// 报告触摸点的 X 坐标
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, points->x);
// 报告触摸点的 Y 坐标
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, points->y);
// 报告触摸点的宽度
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, points->w);
// 报告触摸点的压力
input_report_abs(ts->input_dev, ABS_MT_PRESSURE, points->p);
// 更新当前触摸 ID 位图
cur_touch |= 0x01 << points->id;
points++; // 移动到下一个触摸点
}
// 如果在上一帧中这个槽是活跃的
else if (pre_touch & (0x01 << i)) {
// 设置当前槽为 i
input_mt_slot(ts->input_dev, i);
// 检查上次是否是笔 ID
if (pre_pen_id == i) {
// 如果是笔,报告笔的状态为假
input_mt_report_slot_state(ts->input_dev, MT_TOOL_PEN, false);
/* 有效的 ID 应小于 10,所以将 ID 设置为 0xff
* 来表示无效状态
*/
pre_pen_id = 0xff; // 重置笔 ID
} else {
// 否则报告手指状态为假
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
}
}
}
// 更新上一帧的触摸 ID 位图为当前帧
pre_touch = cur_touch;
// 同步当前的触摸帧
input_mt_sync_frame(ts->input_dev);
// 同步输入设备状态
input_sync(ts->input_dev);
}
gtp_irq_handler
gtp_work_func(ts);
point_state = gtp_get_points(ts, points, &key_value);
gtp_i2c_read
i2c_transfer
gtp_mt_slot_report(ts, point_state & 0x0f, points);
input_mt_slot
input_mt_report_slot_state
input_report_abs
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
有所疑问可以先看:
4.编写
这里主要是参考?gpio_keys.c,但思路其实和上面介绍的差不多,设置Input_dev并注册、配置事件类型和支持的事件、请求中断注册中断等。
#define TOUCHSCREEN_POLL_TIME_MS 10 // 定义轮询时间,单位为毫秒
// 定义模拟触摸屏硬件数据结构
struct qemu_ts_con {
volatile unsigned int pressure; // 触摸压力,0表示未触摸,非0表示触摸
volatile unsigned int x; // 触摸X坐标
volatile unsigned int y; // 触摸Y坐标
volatile unsigned int clean; // 清除标志
};
// 全局变量定义
static struct input_dev *g_input_dev; // 输入设备
static int g_irq; // 中断号
static struct qemu_ts_con *ts_con; // 指向模拟触摸屏的结构体
struct timer_list ts_timer; // 定时器,用于轮询触摸状态
// 定时器回调函数,用于定期轮询触摸状态
static void ts_irq_timer(unsigned long _data)
{
// 如果检测到触摸事件
if (ts_con->pressure) {
// 报告触摸位置
input_event(g_input_dev, EV_ABS, ABS_X, ts_con->x);
input_event(g_input_dev, EV_ABS, ABS_Y, ts_con->y);
input_sync(g_input_dev);
// 重新启动定时器
mod_timer(&ts_timer, jiffies + msecs_to_jiffies(TOUCHSCREEN_POLL_TIME_MS));
}
}
// 中断服务函数,触发触摸事件处理
static irqreturn_t input_dev_demo_isr(int irq, void *dev_id)
{
// 如果有触摸事件发生
if (ts_con->pressure) {
// 报告触摸位置和触摸按键状态
input_event(g_input_dev, EV_ABS, ABS_X, ts_con->x);
input_event(g_input_dev, EV_ABS, ABS_Y, ts_con->y);
input_event(g_input_dev, EV_KEY, BTN_TOUCH, 1);
input_sync(g_input_dev);
// 启动定时器继续监控触摸状态
mod_timer(&ts_timer, jiffies + msecs_to_jiffies(TOUCHSCREEN_POLL_TIME_MS));
} else {
// 如果没有触摸,报告触摸键松开
input_event(g_input_dev, EV_KEY, BTN_TOUCH, 0);
input_sync(g_input_dev);
}
return IRQ_HANDLED;
}
// 设备初始化函数,负责资源分配和设备注册
static int input_dev_demo_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int error;
struct resource *io;
int gpio;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
// 获取GPIO编号
gpio = of_get_gpio(pdev->dev.of_node, 0);
// 分配并初始化input_dev结构体
g_input_dev = devm_input_allocate_device(dev);
if (!g_input_dev)
return -ENOMEM;
// 配置input_dev的基础信息
g_input_dev->name = "input_dev_demo";
g_input_dev->phys = "input_dev_demo";
g_input_dev->dev.parent = dev;
g_input_dev->id.bustype = BUS_HOST;
g_input_dev->id.vendor = 0x0001;
g_input_dev->id.product = 0x0001;
g_input_dev->id.version = 0x0100;
// 配置事件类型和支持的事件
__set_bit(EV_KEY, g_input_dev->evbit);
__set_bit(EV_ABS, g_input_dev->evbit);
__set_bit(INPUT_PROP_DIRECT, g_input_dev->propbit);
__set_bit(BTN_TOUCH, g_input_dev->keybit);
__set_bit(ABS_X, g_input_dev->absbit);
__set_bit(ABS_Y, g_input_dev->absbit);
// 配置触摸屏的绝对坐标范围
input_set_abs_params(g_input_dev, ABS_X, 0, 0xffff, 0, 0);
input_set_abs_params(g_input_dev, ABS_Y, 0, 0xffff, 0, 0);
// 注册input_dev
error = input_register_device(g_input_dev);
if (error)
return error;
// 获取硬件寄存器的I/O资源并映射
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ts_con = ioremap(io->start, resource_size(io));
// 获取并请求GPIO的中断号
g_irq = gpio_to_irq(gpio);
error = request_irq(g_irq, input_dev_demo_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "input_dev_demo_irq", NULL);
if (error)
return error;
// 初始化定时器
setup_timer(&ts_timer, ts_irq_timer, (unsigned long)NULL);
return 0;
}
// 设备卸载函数,释放资源
static int input_dev_demo_remove(struct platform_device *pdev)
{
del_timer_sync(&ts_timer); // 停止定时器
iounmap(ts_con); // 取消内存映射
free_irq(g_irq, NULL); // 释放中断
input_unregister_device(g_input_dev); // 注销设备
return 0;
}
// 设备树匹配表
static const struct of_device_id input_dev_demo_of_match[] = {
{ .compatible = "100ask,input_dev_demo", },
{ },
};
// 平台驱动结构体定义
static struct platform_driver input_dev_demo_driver = {
.probe = input_dev_demo_probe,
.remove = input_dev_demo_remove,
.driver = {
.name = "input_dev_demo",
.of_match_table = input_dev_demo_of_match,
}
};
// 模块初始化函数
static int __init input_dev_demo_init(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return platform_driver_register(&input_dev_demo_driver);
}
// 模块退出函数
static void __exit input_dev_demo_exit(void)
{
platform_driver_unregister(&input_dev_demo_driver);
}
module_init(input_dev_demo_init);
module_exit(input_dev_demo_exit);
MODULE_LICENSE("GPL");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
评论记录:
回复评论: