对于驱动开发者来说,对按键 触摸屏 鼠标等设备分别进行文件操作显得很繁琐,他们具有一些相同的规律,即内核负责记录数据,应用负责读取数据,因此,内核开发者为了简化驱动开发者的工作,特地创造了输入子系统。
输入子系统分为两层,一个是驱动子系统,一个是文件操作子系统。驱动子系统依旧由驱动开发者完成,当发生一个事件时,驱动向子系统核心发送一个事件报告,子系统核心将这个报告交给文件操作子系统,由后者将具体的事件封装成一个input_event结构体,并传递给应用程序,这样应用程序就知道硬件发生了什么。
以触摸屏驱动程序为例,大致介绍输入子系统的使用。
触摸屏驱动程序

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/delay.h>struct input_dev *ts_dev;
struct timer_list ts_timer;struct adc_regs {unsigned long adccon;unsigned long adctsc;unsigned long adcdly;unsigned long adcdat0;unsigned long adcdat1;unsigned long adcupdn;unsigned long adcclrint;unsigned long reserved;unsigned long adcclrintpndnup;
};static struct adc_regs *adc_regs;//等待按下
static void wait_for_pen_down(void)
{adc_regs->adctsc = 0xd3;
}//等待抬起
static void wait_for_pen_up(void)
{adc_regs->adctsc = 0x1d3;
}//xy坐标自动转换
static void measure_xy_mode(void)
{adc_regs->adctsc = ((1<<7) | (1<<6) | (1<<4) | (1<<3) | (1<<2));
}//启动adc转换
static void start_adc(void)
{adc_regs->adccon |= (1<<0);
}//触摸中断
irqreturn_t ts_irq(int irq, void *dev_id)
{unsigned long data0, data1;int down;data0 = adc_regs->adcdat0;data1 = adc_regs->adcdat1;down = (!(data0 & (1 << 15))) && (!(data1 & (1 << 15)));//只有xy都没有值时down才会为0,表示松开if(!down){wait_for_pen_down();//等待按下input_event(ts_dev, EV_ABS, ABS_PRESSURE, 0);input_event(ts_dev, EV_KEY, BTN_TOUCH, 0);input_sync(ts_dev);//报告失败事件}else{measure_xy_mode();//xy自动转换start_adc();//开启adc转换}
//  adc_regs->adcclrint = 0;//清除adc中断标志adc_regs->adcclrintpndnup = 0;//清除触摸中断标志return IRQ_HANDLED;
}
//adc中断
irqreturn_t adc_irq(int irq, void *dev_id)
{mod_timer(&ts_timer, jiffies + HZ / 100);//10mswait_for_pen_up();//等待抬起adc_regs->adcclrint = 0;//清除adc中断标志
//  adc_regs->adcclrintpndnup = 0;//清除触摸中断标志return IRQ_HANDLED;
}
//定时器中断
void ts_timer_function(unsigned long data)
{int x, y;unsigned long data0, data1;int down;data0 = adc_regs->adcdat0;data1 = adc_regs->adcdat1;down = (!(data0 & (1 << 15))) && (!(data1 & (1 << 15)));//只有xy都没有值时down才会为0,表示松开if(!down){wait_for_pen_down();//等待按下input_event(ts_dev, EV_ABS, ABS_PRESSURE, 0);input_event(ts_dev, EV_KEY, BTN_TOUCH, 0);input_sync(ts_dev);//报告失败事件}else{x = data0 & 0xfff;y = data1 & 0xfff;input_event(ts_dev, EV_ABS, ABS_X, x);input_event(ts_dev, EV_ABS, ABS_Y, y);input_event(ts_dev, EV_ABS, ABS_PRESSURE, 1);input_event(ts_dev, EV_KEY, BTN_TOUCH, 1);input_sync(ts_dev);measure_xy_mode();//xy自动转换start_adc();//开启adc转换}
}int ts_init(void)
{struct clk *clk;ts_dev = input_allocate_device();//分配输入结构体set_bit(EV_KEY, ts_dev->evbit);//key事件set_bit(EV_ABS, ts_dev->evbit);//绝对地址set_bit(BTN_TOUCH, ts_dev->keybit);//触摸input_set_abs_params(ts_dev, ABS_X, 0, 0xfff, 0, 0);//设置x坐标的范围input_set_abs_params(ts_dev, ABS_Y, 0, 0xfff, 0, 0);input_set_abs_params(ts_dev, ABS_PRESSURE, 0, 1, 0, 0);input_register_device(ts_dev);//注册设备adc_regs = ioremap(0x7e00b000, sizeof(struct adc_regs));//虚拟地址映射clk = clk_get(NULL, "adc");clk_enable(clk);adc_regs->adccon = (1 << 16) | (1 << 14) | (65 << 6);adc_regs->adcdly = 0xffff;adc_regs->adcclrintpndnup = 0;//清除触摸中断标志,该标志为1时进入中断,必须软件清零request_irq(IRQ_TC, ts_irq, IRQF_SHARED, "ts", NULL);//申请触摸中断request_irq(IRQ_ADC, adc_irq, IRQF_SHARED, "adc", NULL);//adc转换完成中断init_timer(&ts_timer);ts_timer.function = ts_timer_function;add_timer(&ts_timer);wait_for_pen_down();//等待按下return 0;
}void ts_exit(void)
{del_timer(&ts_timer);free_irq(IRQ_TC, NULL);free_irq(IRQ_ADC, NULL);iounmap(adc_regs);input_unregister_device(ts_dev);input_free_device(ts_dev);
}module_init(ts_init);
module_exit(ts_exit);
MODULE_LICENSE("GPL");

输入子系统分析
大致看一下一个输入设备结构体包含哪些成员,其余成员暂且不介绍

struct input_dev
{unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//所支持的事件类型,包括EV_KEY按键 EV_ABS绝对地址,触摸屏 EV_REL相对地址,鼠标unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//对于按键事件,有哪些选项struct device dev;//设备结构体struct list_head  node;
};

首先,看一下设备分配函数

struct input_dev *input_allocate_device(void)
{struct input_dev *dev;dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);if (dev) {dev->dev.type = &input_dev_type;dev->dev.class = &input_class;device_initialize(&dev->dev);mutex_init(&dev->mutex);spin_lock_init(&dev->event_lock);INIT_LIST_HEAD(&dev->h_list);INIT_LIST_HEAD(&dev->node);__module_get(THIS_MODULE);}return dev;
}

一般情况下,都会采用设备嵌套的结构,而分配函数也就是对input_dev->dev的初始化以及自身相关的初始化
接下来,看看注册函数

int input_register_device(struct input_dev *dev)
{list_add_tail(&dev->node, &input_dev_list);//将结点添加到input_dev链表list_for_each_entry(handler, &input_handler_list, node)//遍历链表input_attach_handler(dev, handler);
}
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{const struct input_device_id *id;id = input_match_device(handler, dev);error = handler->connect(handler, dev, id);
}

在进行一番比较后,如果设备和处理函数匹配,则连接。input_handler结构体如下,该结构体负责处理对应input_dev的输入事件。

struct input_handler {void *private;void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);bool (*match)(struct input_handler *handler, struct input_dev *dev);int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);void (*disconnect)(struct input_handle *handle);void (*start)(struct input_handle *handle);const struct file_operations *fops;int minor;const char *name;const struct input_device_id *id_table;struct list_head h_list;struct list_head node;
}

接下来,介绍一下输入事件
事件结构体如下

struct input_event {struct timeval time;__u16 type;//事件类型__u16 code;//事件的具体参数类型__s32 value;//参数值
};

发送事件函数如下

void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{if (is_event_supported(type, dev->evbit, EV_MAX)) {//测试是否设置了该事件,一般如下设置set_bit(EV_KEY, ts_dev->evbit);input_handle_event(dev, type, code, value);}
}

下面以input_event(ts_dev, EV_ABS, ABS_X, x)具体分析input_handle_event函数

static void input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{switch (type) //首先判断类型,为EV_ABS{case EV_ABS:if (is_event_supported(code, dev->absbit, ABS_MAX))//测试EV_ABS事件是否设置了该参数disposition = input_handle_abs_event(dev, code, &value);break;}
}

继续调用input_handle_abs_event函数

static int input_handle_abs_event(struct input_dev *dev,unsigned int code, int *pval)
{if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {input_abs_set_val(dev, ABS_MT_SLOT, dev->slot);input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);}
}

如下是关于ABS_X的定义以及结构体input_absinfo

#define ABS_MT_SLOT      0x2f    /* MT slot being modified */
#define ABS_X           0x00
struct input_absinfo {__s32 value;__s32 minimum;__s32 maximum;__s32 fuzz;__s32 flat;__s32 resolution;
};

最后一步,在input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);中调用input_handler的event函数,将消息整合成input_event结构体的形式发送给用户,由用户读取信息。具体代码没有深入研究,猜测类似于read、write之类的函数。
触摸屏相关分析

如图所示,是电阻型触摸屏获取坐标值的原理。 
对于触摸屏驱动来说,有两个中断,分别是IRQ_TC和IRQ_ADC,前者是触摸中断,后者是ADC转换完成中断。
当进入IRQ_TC中断时,启动adc转换,adc转换的结果x坐标、y坐标分别存放在ADCDAT0和ADCDAT1寄存器中,进入ADC中断后将事件上报给输入子系统。

linux驱动之输入子系统相关推荐

  1. 《Linux 驱动:输入子系统》

    前言 输入子系统作为一个模块存在,向上,为用户层提供调用接口:向下,为驱动层程序提供统一的注册接口.这样,就能够使输入设备的事件通过输入子系统发送给用户层应用程序,用户层应用程序也可以通过输入子系统通 ...

  2. Linux的input输入子系统:设备驱动之按键驱动

    环境:kernel-2.6.30.4,arm-linux-gcc-4.3.3,目标板TQ2440 一.设备层驱动程序: #include <linux/module.h> #include ...

  3. ARM Linux内核Input输入子系统浅解

    --以触摸屏驱动为例 第一章.了解linux input子系统       Linux输入设备总类繁杂,常见的包括有按键.键盘.触摸屏.鼠标.摇杆等等,他们本身就是字符设备,而linux内核将这些设备 ...

  4. 【linux驱动】USB子系统分析

    本文针对Linux内核下USB子系统进行分析,主要会涉及一下几个方面: USB基础知识:介绍USB设备相关的基础知识 Linux USB子系统分析:分析USB系统框架,USB HCD/ROOT HUB ...

  5. Linux输入事件类型EV_SW,Linux的input输入子系统:总体框架

    一.input输入子系统总体框架 Linux输入子系统将输入驱动抽象为三层:设备驱动层.核心层.事件处理层. 设备驱动层:将底层的硬件输入事件转化为统一事件形式,向输入核心(Input Core)汇报 ...

  6. 【Linux驱动】input子系统与按键驱动

    input子系统架构总览 在网上能找到一些关于input子系统架构相关的示意图,大体表达的意思都差不多. linux输入子系统(linux input subsystem)从上到下由三层实现,分别为: ...

  7. linux按键驱动中的结构体,linux 驱动之input子系统(gpio-keys)实现

    1.概述 Gpio-keys 是基于input子系统实现的一个通用按键驱动,该驱动也符合linux驱动实现模型,即driver和device分离模型.一般按键驱动,都是基于gpio-keys进行开发的 ...

  8. linux驱动之I2C子系统

    目录 一.I2C基本原理 二.linux内核的I2C子系统详解 1.linux内核的I2C驱动框架总览 2.I2C子系统的4个关键结构体(kernel/include/linux/i2c.h) 3.关 ...

  9. Linux驱动分析——I2C子系统

    stm32mp157  盘古开发板  Linux内核版本4.19 目录 1.朱有鹏老师视频笔记 2.I2C子系统的4个关键结构体 3.关键文件 4.i2c-core.c初步分析 ​4.1.smbus代 ...

  10. linux驱动之i2c子系统mpu6050设备驱动

    以下是mpu6050简单的驱动实现,mpu6050是I2C接口的6轴传感器,可以作为字符设备注册到内核,本代码运行环境是3.4.2内核,4.3.2版本的编译链,12.04版本的Ubuntu,硬件环境是 ...

最新文章

  1. C++读取文件名,过滤文件路径及后缀
  2. 机器学习的优化目标、期望最大化(Expectation-Maximum, EM)算法、期望最大化(EM)和梯度下降对比
  3. 原创经验:微信小程序开发总结
  4. linux的开机网络设置
  5. 视频需求超平常数 10 倍,却节省了 60% 的 IT 成本投入是一种什么样的体验?
  6. 上交大计算机导师俞凯,WLA青科聊高考①|偶像剧“男主”、上海交大教授俞凯的学霸人生...
  7. 徐波 博士 计算机,徐波教授:医工联合促进智能肿瘤学发展——探秘肿瘤精准治疗中的AI技术...
  8. MSSQL 2008 企业管理器打开命令
  9. 【OS学习笔记】二十 保护模式六:保户模式下操作系统内核如何加载用户程序并运行 对应的汇编代码之主引导扇区程序
  10. JAVA能做flash游戏吗_FLASH+JAVA开发实时网络游戏 (转:闪客帝国)
  11. Web App 前端构建(纯净版)
  12. 数据库-如何快速创建连接字符串
  13. android apk下载 安装程序,Android之APK的下载安装
  14. 软件测试的重要性与必要性,软件测试的目的和意义
  15. python生成exe文件与exe文件的反编译
  16. python安装出错0x80072ee7_python3.6.4安装错误0x80072efd
  17. 翁恺慕课C语言作业 第三章时区换算
  18. qsnctf 哥哥打篮球 wp
  19. 华硕ASUS 笔记本 改WIN7 BIOS 设置详解
  20. 粒子物理中的粒子鉴别

热门文章

  1. Ubuntu18.04添加vim配置
  2. VSFTPD 上传文件 200 227 553错误
  3. dw怎么做html鼠标变化,dw鼠标经过单元格变色 DW 鼠标经过表格 背景变色
  4. C语言 枚举——熄灯问题
  5. 4.4 Recovery和Removal分析
  6. 解决esp8266无法连接手机和电脑热点的问题
  7. 论文阅读: (ECCV 2022) Content-Oriented Learned Image Compression
  8. 积分商城系统积分兑换运营开源架构
  9. 计算机用户中的配置文件,我的电脑的用户配置文件中多了一个未知的帐户 – 手机爱问...
  10. 三年前端路:分享我的工作经验与学习经历