当前位置: 首页 > news >正文

input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序

往期内容

本专栏往期内容:

  1. input子系统的框架和重要数据结构详解-CSDN博客
  2. input device和input handler的注册以及匹配过程解析-CSDN博客
  3. input device和input handler的注册以及匹配过程解析-CSDN博客
  4. 编写一个简单的Iinput_dev框架-CSDN博客
  5. GPIO按键驱动分析与使用:input_dev层-CSDN博客

I2C子系统专栏:

  1. 专栏地址:IIC子系统_憧憬一下的博客-CSDN博客
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有往期内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树_憧憬一下的博客-CSDN博客
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有往期内容观看顺序


目录

  • 往期内容
  • 前言
  • 1. 驱动程序框架
  • 2. 设备树示例
  • 3. 驱动程序分析
    • 3.1 分配/设置/注册input_dev
    • 3.2 注册中断处理函数
    • 3.3 中断处理函数分析
  • 4.编写

前言

  • 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

本节主要讲解内核中提供的gt9xx.c,来看看是如何通过input子系统来实现一个IIC协议的传输,之后在此基础上编写一个简单的input子系统的IIC驱动

1. 驱动程序框架

img

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>;/* ...... */};
};	

这段设备树(Device Tree)定义配置了一个基于 I2C 总线的 Goodix GT9xx 系列触摸屏设备,设备地址为 0x5d

1. 节点和基础属性

&i2c2 {gt9xx@5d {compatible = "goodix,gt9xx";reg = <0x5d>;
  • &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>;
  • 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_resetpinctrl_touchscreen_int),用于触摸屏重置和中断引脚初始化。

3. GPIO 和中断标志

        reset-gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;irq-gpios = <&gpio1 5 IRQ_TYPE_EDGE_FALLING>;irq-flags = <2>; 
  • 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>;
  • 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>;
  • Goodix 特定属性goodix,xxx 属性定义了一些特性和行为,例如:

    • goodix,type-a-reportgoodix,driver-send-cfg:控制是否启用 A 类型报告和驱动是否发送配置信息。
    • goodix,create-wr-node:创建 WR(写)节点的标志。
    • goodix,wakeup-with-resetgoodix,power-off-sleep:控制唤醒和电源管理的行为。
    • 其他 Goodix 触摸屏特性配置,按需要启用或禁用。

这些属性通过设备树配置,使系统能够正确初始化并管理触摸屏设备。

3. 驱动程序分析

gt9xx.c

3.1 分配/设置/注册input_dev

img

gtp_proberet = gtp_request_input_dev(ts);ts->input_dev = input_allocate_device();......ret = input_register_device(ts->input_dev);ret = gtp_request_irq(ts);	

3.2 注册中断处理函数

img

ret = request_threaded_irq(ts->client->irq, NULL,gtp_irq_handler,ts->pdata->irq_flags | IRQF_ONESHOT,ts->client->name,ts);

3.3 中断处理函数分析

通过I2C函数( 2c_tansfer)读取数据、上报数据。

img

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_handlerret = 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)elsegtp_type_a_report(ts, point_state & 0x0f, points);   // Type A方式
}

(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];         // 触摸点 IDpoints[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 固定为 10if (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;  // 返回触摸屏状态
}

(2)gtp_irq_handler中你的gtp_mt_slot_report(ts, point_state & 0x0f, points); // Slot方式

  • 函数主要负责报告多点触控的触摸点状态,包括其位置、工具类型和压力等。
  • 使用位图(cur_touchpre_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)) {// 设置当前槽为 iinput_mt_slot(ts->input_dev, i);// 检查上次是否是笔 IDif (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_handlergtp_work_func(ts);point_state = gtp_get_points(ts, points, &key_value);gtp_i2c_readi2c_transfergtp_mt_slot_report(ts, point_state & 0x0f, points);input_mt_slotinput_mt_report_slot_stateinput_report_abs

有所疑问可以先看:

I2C相关结构体讲解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客

编写一个简单的Iinput_dev框架-CSDN博客

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_deverror = 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");

相关文章:

input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序

往期内容 本专栏往期内容&#xff1a; input子系统的框架和重要数据结构详解-CSDN博客input device和input handler的注册以及匹配过程解析-CSDN博客input device和input handler的注册以及匹配过程解析-CSDN博客编写一个简单的Iinput_dev框架-CSDN博客GPIO按键驱动分析与使用&…...

SQL-lab靶场less1-4

说明&#xff1a;部分内容来源于网络&#xff0c;如有侵权联系删除 前情提要&#xff1a;搭建sql-lab本地靶场的时候发现一些致命的报错&#xff1a; 这个程序只能在php 5.x上运行&#xff0c;在php 7及更高版本上&#xff0c;函数“mysql_query”和一些相关函数被删除&#xf…...

【生成模型之二】diffusion model模型

【算法简历修改、职业规划、校招实习咨询请私信联系】 【Latent-Diffusion 代码】 生成模型分类概述 Diffusion Model&#xff0c;这一深度生成模型&#xff0c;源自物理学中的扩散现象&#xff0c;呈现出令人瞩目的创新性。与传统的生成模型&#xff0c;如VAE、GAN相比&…...

记录 Maven 版本覆盖 Bug 的解决过程

背景 在使用 Maven 进行项目管理时&#xff0c;依赖版本的管理是一个常见且重要的环节。最近&#xff0c;在我的项目中遇到了一个关于依赖版本覆盖的 Bug&#xff0c;这个问题导致了 Apollo 框架的版本不一致&#xff0c;影响了项目的正常运行。以下是我解决这个问题的过程记录…...

【K8S系列】Kubernetes Service 基础知识 详细介绍

在 Kubernetes 中&#xff0c;Service 是一种抽象的资源&#xff0c;用于定义一组 Pod 的访问策略。它为这些 Pod 提供了一个稳定的访问入口&#xff0c;解决了 Pod 可能频繁变化的问题。本文将详细介绍 Kubernetes Service 的类型、功能、使用场景、DNS 和负载均衡等方面。 1.…...

python在物联网领域的数据应用分析与实战!

引言 物联网(IoT)是一个快速发展的领域,涉及到各种设备和传感器的连接与数据交换。随着设备数量的激增,数据的产生速度也在不断加快。 如何有效地分析和利用这些数据,成为了物联网应用成功的关键。Python作为一种强大的编程语言,因其简洁易用的特性和丰富的库支持,成为…...

目标跟踪算法-卡尔曼滤波详解

卡尔曼滤波是一种递归的优化算法&#xff0c;用于估计一个系统的动态状态&#xff0c;常用于跟踪、导航、时间序列分析等领域。它的关键在于使用一系列测量数据&#xff08;通常含噪声&#xff09;来估计系统的真实状态&#xff0c;使得估计值更接近实际情况。卡尔曼滤波器适合…...

SpringBoot后端开发常用工具详细介绍——application多环境配置与切换

文章目录 引言介绍application.yml&#xff08;主配置文件&#xff09;application-dev.yml&#xff08;开发环境配置&#xff09;application-test.yml&#xff08;测试环境配置&#xff09;application-prod.yml&#xff08;生产环境配置&#xff09;激活配置文件参考内容 引…...

php反序列化漏洞典型例题

1.靶场环境 ctfhub-技能树-pklovecloud 引用题目&#xff1a; 2021-第五空间智能安全大赛-Web-pklovecloud 2.过程 2.1源代码 启动靶场环境&#xff0c;访问靶场环境&#xff0c;显示源码&#xff1a;直接贴在下面&#xff1a; <?php include flag.php; class pks…...

浅析Android View绘制过程中的Surface

前言 在《浅析Android中View的测量布局流程》中我们对VSYNC信号到达App进程之后开启的View布局过程进行了分析&#xff0c;经过对整个App界面的View树进行遍历完成了测量和布局&#xff0c;确定了View的大小以及在屏幕中所处的位置。但是&#xff0c;如果想让用户在屏幕上看到…...

基于卷积神经网络的大豆种子缺陷识别系统,resnet50,mobilenet模型【pytorch框架+python源码】

更多目标检测和图像分类识别项目可看我主页其他文章 功能演示&#xff1a; 大豆种子缺陷识别系统&#xff0c;卷积神经网络&#xff0c;resnet50&#xff0c;mobilenet【pytorch框架&#xff0c;python源码】_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于卷积神…...

HarmonyOS项目开发一多简介

目录 一、布局能力概述 二、自适应布局 三、响应式布局 四、典型布局场景 一、布局能力概述 布局决定页面元素排布及显示&#xff1a;在页面设计及开发中&#xff0c;布局能力至关重要&#xff0c;主要通过组件结构来确定使用何种布局。 自适应布局与响应式布局&#xff1…...

C++基础三

构造函数 构造函数(初始化类成员变量)&#xff1a; 1、属于类的成员函数之一 2、构造函数没有返回类型 3、构造函数的函数名必须与类名相同 4、构造函数不允许手动调用(不能通过类对象调用) 5、构造函数在类对象创建时会被自动调用 6、如果没有显示声…...

利用ChatGPT完成2024年MathorCup大数据挑战赛-赛道A初赛:台风预测与分析

利用ChatGPT完成2024年MathorCup大数据挑战赛-赛道A初赛&#xff1a;台风预测与分析 引言 在2024年MathorCup大数据挑战赛中&#xff0c;赛道A聚焦于气象数据分析&#xff0c;特别是台风的生成、路径预测、和降水风速特性等内容。本次比赛的任务主要是建立一个分类评价模型&…...

Linux系统操作篇 one -文件指令及文件知识铺垫

Linux操作系统入门-系统篇 前言 Linux操作系统与Windows和MacOS这些系统不同&#xff0c;Linux是黑屏的操作系统&#xff0c;操作方式使用的是指令和代码行来进行&#xff0c;因此相对于Windows和MacOS这些带有图形化界面的系统&#xff0c;Linux的入门门槛和上手程度要更高&…...

隨筆20241028 ISR 的收缩与扩展及其机制解析

在 Kafka 中&#xff0c;ISR&#xff08;In-Sync Replicas&#xff09; 是一组副本&#xff0c;它们与 Leader 保持同步&#xff0c;确保数据一致性。然而&#xff0c;ISR 的大小会因多种因素而变化&#xff0c;包括收缩和扩展。以下是 ISR 收缩与扩展的详细解释及其背后的机制…...

linux-字符串相关命令

1、cut 提取文件每一行中的内容 下面是一些常用的 cut 命令选项的说明&#xff1a; -c, --characters列表&#xff1a;提取指定字符位置的数据。-d, --delimiter分界符&#xff1a;指定字段的分隔符&#xff0c;默认为制表符。-f, --fieldsLIST&#xff1a;提取指定字段的数据…...

ES6 函数的扩展

ES6 之前&#xff0c;不能直接为函数的参数指定默认值&#xff0c;只能采用变通的方法 ES6 允许为函数的参数设置默认值&#xff0c;即直接写在参数定义的后面 参数变量是默认声明的&#xff0c;所以不能用 let 或 const 再次声明 使用参数默认值时&#xff0c;函数不能有同名参…...

Mac 查看占用特定端口、终止占用端口的进程

在 macOS 上&#xff0c;可以使用以下命令来查看占用特定端口&#xff08;例如 8080&#xff09;的进程&#xff1a; lsof -i :8080命令说明 lsof&#xff1a;列出打开的文件和网络连接信息。-i :8080&#xff1a;筛选出正在监听 8080 端口的进程。 输出结果结构 执行上述命…...

C#入坑JAVA MyBatis入门 CURD 批量 联表分页查询

本文&#xff0c;分享 MyBatis 各种常用操作&#xff0c;不限于链表查询、分页查询等等。 1. 分页查询 在 下文的 的「3.4 selectPage」小节&#xff0c;我们使用 MyBatis Plus 实现了分页查询。除了这种方式&#xff0c;我们也可以使用 XML 实现分页查询。 这里&#xff0c…...

RabbitMQ 安装(Windows版本)和使用

安装 安装包获取 可以自己找资源&#xff0c;我这里也有百度云的资源&#xff0c;如果没失效的话可以直接用。 通过百度网盘分享的文件&#xff1a;RabbitMQ 链接&#xff1a;https://pan.baidu.com/s/1rzcdeTIYQ4BqzHLDSwCgyw?pwdfj79 提取码&#xff1a;fj79 安装教程…...

Apache paimon表管理

表管理 2.9.4.1 管理快照 1)快照过期 Paimon Writer每次提交都会生成一个或两个快照。每个快照可能会添加一些新的数据文件或将一些旧的数据文件标记为已删除。然而,标记的数据文件并没有真正被删除,因为Paimon还支持时间旅行到更早的快照。它们仅在快照过期时被删除。 …...

java 第19天

一.Lambda表达式 前提是&#xff1a;参数是函数式接口才可以书写Lambda表达式 函数式接口条件&#xff1a; 1.接口 2.只有一个抽象方法 lambda表达式又称为匿名函数&#xff0c;允许匿名函数以参数的形式传入方法&#xff0c;简化代码 lambda表达式分为两部分()->{} …...

什么是服务器?服务器与客户端的关系?本地方访问不了网址与服务器访问不了是什么意思?有何区别

服务器是一种高性能的计算机&#xff0c;它通过网络为其他计算机&#xff08;称为客户端&#xff09;提供服务。这些服务可以包括文件存储、打印服务、数据库服务或运行应用程序等。服务器通常具有强大的处理器、大量的内存和大容量的存储空间&#xff0c;以便能够处理多个客户…...

Spring(1)—Spring 框架:Java 开发者的春天

一、关于Spring 1.1 简介 Spring 框架是一个功能强大的开源框架&#xff0c;主要用于简化 Java 企业级应用的开发&#xff0c;由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立&#xff0c;并由Pivotal团队维护。它提供了全面的基础设施支持&#xff0c;使开发者…...

MT1401-MT1410 码题集 (c 语言详解)

目录 MT1401归并排序 MT1402堆排序 MT1403后3位排序 MT1404小大大小排序 MT1405小大大小排序II MT1406数字重排 MT1407插入 MT1408插入 MT1409旋转数组 MT1410逆时针旋转数组 MT1401归并排序 c 语言实现代码 #include <stdio.h>// merge two subarrays void merge(int a…...

React基础语法

1.React介绍 React由Meta公司开发&#xff0c;是一个用于构建Web和原生交互界面的库 1.1 React优势 相较于传统基于DOM开发的优势 1.组件化的开发方式 2.不错的性能 相较于其他前端框架的优势 1.丰富的生态 2.跨平台支持 1.2React的时长情况 全球最流行&#xff0c;大厂…...

《Kadane‘s Algorithm专题:最大和连续子数组》

&#x1f680; 博主介绍&#xff1a;大家好&#xff0c;我是无休居士&#xff01;一枚任职于一线Top3互联网大厂的Java开发工程师&#xff01; &#x1f680; &#x1f31f; 在这里&#xff0c;你将找到通往Java技术大门的钥匙。作为一个爱敲代码技术人&#xff0c;我不仅热衷…...

Vue基础(5)

ref属性 在 Vue2 中&#xff0c;ref是一个特殊的属性&#xff0c;用于在模板中获取对某个 DOM 元素或子组件的引用。通过 ref&#xff0c;我们可以在 JavaScript 代码中直接访问该 DOM 元素或组件实例。 示例: <template><div><input ref"inputField&quo…...

面对复杂的软件需求:5大关键策略!

面对软件需求来源和场景的复杂性&#xff0c;有效地管理和处理需求资料是确保项目成功的关键&#xff0c;能够提高需求理解的准确性&#xff0c;增强团队协作和沟通&#xff0c;降低项目风险&#xff0c;提高开发效率。反之&#xff0c;项目可能面临需求理解不准确、团队沟通不…...

微信商城网站怎么做/厦门最好的seo公司

pushState和replaceState是H5的API中新添加的两个方法。通过window.history方法来对浏览器历史记录的读写。 pushState和replaceState 在 HTML 文件中, history.pushState() 方法向浏览器历史添加了一个状态。 pushState() 带有三个参数&#xff1a;一个状态对象&#xff0c…...

施工企业施工生产计划/网页关键词排名优化

数码管 多位数码管&#xff0c;即是两个或两个以上单个数码管并列集中在一起形成一体的数码管。当多位一体时&#xff0c;它们内部的公共端是独立的&#xff0c;而负责显示什么数字的段线全部是连接在一起的&#xff0c;独立的公共端可以控制多位一体中的哪一位数码管点亮&…...

公司建网站费用怎么做分录/重庆百度搜索优化

题图 by Ovlivion 今天&#xff0c;有同事说我「变化还是挺大的」。 然后我认真思考了下&#xff0c;总结了以下几点变化&#xff0c;心里挺乐呵&#xff1a; 1.开始大量阅读 这里的阅读当然不是说看网易新闻、今日头条&#xff0c;或者看看抖音、快手。这些 App 现在有个说法叫…...

做户外运动的网站/最新的网络营销方式

有价值的git命令的博客: http://blog.csdn.net/ithomer/article/details/7529022 gitlab的使用方法&#xff1a; git命令&#xff1a; 1. 如果你是第一次使用git&#xff0c;那么先安装git吧&#xff1a; sudo apt-get install git2. 当安装完了 git 后&#xff0c;初始化…...

个人可以做导购网站吗/百度浏览器官网

为什么mysql要使用主从模型发布时间&#xff1a;2020-05-09 10:38:05来源&#xff1a;亿速云阅读&#xff1a;172作者&#xff1a;三月本文主要给大家介绍为什么mysql要使用主从模型&#xff0c;文章内容都是笔者用心摘选和编辑的&#xff0c;具有一定的针对性&#xff0c;对大…...

官方网站内容可做证据吗/快排seo软件

嗨&#xff0c;你好&#xff0c;我是阿锴。今天分享给大家一个高大上的设计技巧&#xff0c;能让你「傻瓜一键式」把低质量图片变高大上。这个技巧&#xff0c;就是「双色调」。可以把图片处理成下面这个样子&#xff1a;还挺帅对吧&#xff1f;这个技巧的实现原理是什么呢&…...