input输入子系统及调试实例简述

[Linux外设驱动详解]第10章 输入子系统(Input Subsystem) 输入子系统(Input Subsystem)摘要 Linux输入子系统是内核管理各类输入设备(键盘、鼠标、触摸屏等)的统一框架,采用分层架构: 架构层次: 用户空间:应用程序通过/dev/input/eventX接口访问 输入核心层:负责设备注册和事件分发 驱动层:硬件驱动实现具体设备操作 硬件层:物理设备连接 核心结构: input_dev结构体包含设备标识、事件位图、状态信息和回调函数 通过位图机制管理不同事件类型(按键、坐标、LED等) 关键功能: 提供标准API实现设备注册/注销 支持事件上报机制 阅读详情

一 引入输入子系统的目的

二 输入子系统框架

三 输入子系统之核心层简述

四 输入子系统之事件处理层简述

五 输入子系统之驱动层简述

六 TP调试记录


一 引入输入子系统的目的

个人理解是为了 将输入设备的功能直接提供给用户空间,如果按照普通字符设备的方式编写输入设备,那么我们自己的驱动会生成我们自己命名的设备节点,只有我们自己知道设备节点名称,也就是只有我们自己可以打开这个设备节点,此时这种驱动程序只能自己使用。如果想使自己的驱动程序生成的节点成为“公共的”,即生成的节点可以直接被打开使用,各种应用程序都可以调用该节点,那么就引入 “输入子系统”

二 输入子系统框架

输入子系统分为三部分:

1:设备层 如matrix_key.c / gpio_keys.c等等 :和硬件相关的底层驱动(我们需要实现的部分),主要实现对硬件设备的读写访问,中断设置,把底层硬件的输入事件 上报给核心层。

2:核心层 input.c :为 设备驱动层输入设备 以及 事件处理层 提供注册和操作的接口,并且传递设备层数据到时间处理层

3:事件处理层 Evdev.c :则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。

三 输入子系统之核心层简述

3.1 核心层功能
1创建注册字符设备
2为设备驱动层提供注册操作接口
3为事件处理层提供注册操作接口
4为 设备驱动层 与 事件处理层 提供匹配函数接口
3.2 关键函数举例
//创建字符设备结构体  : truct file_operations input_handlers_fileops

//注册字符设备  : __init input_init

//创建 proc 文件系统相关  : __init input_proc_init(void)

//用于匹配 底层驱动 与 上层handler (匹配成功则调用 handler的connect函数) :input_attach_handler

//进行 handler 与 device(输入设备) 的匹配  :struct input_device_id *input_match_device
3.3 关键函数说明
//注册字符设备
    static int __init input_init(void)
{
    int err;
//创建设备类
    err = class_register(&input_class); 
    if (err) {
        pr_err("unable to register input_dev class\n");
        return err;
    }
//proc文件系统相关
    err = input_proc_init();
    if (err)
        goto fail1;

//注册字符设备
    err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
                     INPUT_MAX_CHAR_DEVICES, "input");
    if (err) {
        pr_err("unable to register char major %d", INPUT_MAJOR);
        goto fail2;
    }

    return 0;

 fail2: input_proc_exit();
 fail1: class_unregister(&input_class);
    return err;
}


//创建 proc 文件系统相关
static int __init input_proc_init(void)
{
    struct proc_dir_entry *entry;

//在  /proc/bus 目录下创建 input目录
    proc_bus_input_dir = proc_mkdir("bus/input", NULL); 
    if (!proc_bus_input_dir)
        return -ENOMEM;

//在  /proc/bus/input 目录下创建 devices 文件
    entry = proc_create("devices", 0, proc_bus_input_dir,
                &input_devices_fileops);
    if (!entry)
        goto fail1;

//在  /proc/bus/input 目录下创建 handlers 文件
    entry = proc_create("handlers", 0, proc_bus_input_dir,
                &input_handlers_fileops);
    if (!entry)
        goto fail2;

    return 0;

 fail2: remove_proc_entry("devices", proc_bus_input_dir);
 fail1: remove_proc_entry("bus/input", NULL);
    return -ENOMEM;
}


//创建字符设备结构体
static const struct file_operations input_handlers_fileops = {
    .owner      = THIS_MODULE,
    .open       = input_proc_handlers_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = seq_release,
};

//用于匹配 底层驱动 与 上层handler (匹配成功则调用 handler的connect函数)
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;

//进行 handler 与 device(输入设备) 的匹配
id = input_match_device(handler, dev);
if (!id)
    return -ENODEV;

//如果匹配成功 则调用 handler中的connect函数进行连接
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
    pr_err("failed to attach handler %s to device %s, error: %d\n",
           handler->name, kobject_name(&dev->dev.kobj), error);

return error;
}


static const struct input_device_id *input_match_device(struct input_handler *handler,
                        struct input_dev *dev)
{
const struct input_device_id *id;

//遍历 handler->id_table 中的全部 input_device_id(输入设备ID)
for (id = handler->id_table; id->flags || id->driver_info; id++) {

    if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
        if (id->bustype != dev->id.bustype)
            continue;

    if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
        if (id->vendor != dev->id.vendor)
            continue;

    if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
        if (id->product != dev->id.product)
            continue;

    if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
        if (id->version != dev->id.version)
            continue;

    if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))
        continue;

    if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))
        continue;

    if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
        continue;

    if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
        continue;

    if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
        continue;

    if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))
        continue;

    if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))
        continue;

    if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))
        continue;

    if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))
        continue;

    if (!handler->match || handler->match(handler, dev))
        return id;
}

return NULL;
}
3.4 小结:

核心层为 设备驱动层 提供的接口有:

//分配 input_dev 结构体
input_allocate_device(void) :

//设置 输入设备所上报的事件
input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)

//向 核心层 注册设备
int input_register_device(struct input_dev *dev)

核心层为 事件层 提供的接口有:

input_register_handler :事件层向核心层注册 handler  (handler 与 input_dev成对应关系)

input_regiser_handle :事件层向核心层注册 handle (不同于 handler,handle是用于记录匹配成功的 handler与input_dev) handle是用于记录匹配成功的 handler与input_dev,可以通过 handle获得 input_dev 以及 handler的信息

核心层关键点1

输入系统注册设备时,设备注册 与 handler注册 后的匹配过过程 和 platform平台有些相似,设备与 handler 注册时都会各自把 dev 或 handler 挂在各自设备链表 或 事件链表上,然后去遍历对方的事件链表 或 设备链表,input_dev 与 handler是多对多的关系

核心层关键点2

input_dev_list 链表上的 input_dev设备节点
input_handler_list 链表上的 handler 事件节点
input_dev_list 链表 和 input_handler_list 链表 上对应的节点都会有一个 匹配记录结构体 handle,用于记录匹配信息,所以 两个链表上的节点 可以通过handle 互相访问;

核心层关键点3

核心层总结:
1创建注册字符设备
2为设备驱动层提供注册操作接口
3为事件处理层提供注册操作接口
4为 设备驱动层 与 事件处理层 提供匹配函数接口

四 输入子系统之事件处理层简述

注意 :此处 事件处理层 只做简单描述,主要是为了加强印象,日后有时间补充。

struct input_handler {  
  
    void *private;  
  
    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);  
    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;  
    const struct input_device_id *blacklist;  
  
    struct list_head    h_list;    //h_list是一个链表头,用来把handle挂载在这个上  
    struct list_head    node;      //这个node是用来连到input_handler_list上的  
};  
  
struct input_handle {  
  
    void *private;  
  
    int open;  
    const char *name;  
  
    struct input_dev *dev;              //指向input_dev  
    struct input_handler *handler;      //指向input_handler  
  
    struct list_head    d_node;     //连到input_dev的h_list上  
    struct list_head    h_node;     //连到input_handler的h_list上  
};  
1 事件处理层描述:

输入子系统上层其实是由多个事件(handler)组成的,各个事件之间的关系是平行关系,互不干扰,用的较多的是event,此处以event为例,事件处理层 属于 input输入子系统上层

2 事件处理层主要框架简述
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 },   /* Matches all devices */
{ },            /* Terminating zero entry */
};

MODULE_DEVICE_TABLE(input, evdev_ids);

static struct input_handler evdev_handler = {
.event      = evdev_event,
.events     = evdev_events,
.connect    = evdev_connect,//挂接函数
.disconnect = evdev_disconnect,
.legacy_minors  = true,
.minor      = EVDEV_MINOR_BASE,
.name       = "evdev",
.id_table   = evdev_ids,
};

static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}

static void __exit evdev_exit(void)
{
input_unregister_handler(&evdev_handler);
}

module_init(evdev_init);
module_exit(evdev_exit);

五 输入子系统之驱动层简述

struct input_dev {
	const char *name; //设备名
	const char *phys; //设备在系统中的物理路径
	const char *uniq; //设备唯一识别符
	struct input_id id;//设备ID,包含总线ID(PCI、USB)、厂商ID,与input_handler匹配的时会用到 

	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];

    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];     //支持的所有事件类型  
    unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];   //支持的键盘事件  
    unsigned long relbit[BITS_TO_LONGS(REL_CNT)];   //支持的鼠标相对值事件  
    unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];   //支持的鼠标绝对值事件  
    unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];   //支持的其它事件类型  
    unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];   //支持的LED灯事件  
    unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];   //支持的声效事件 
    unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];     //支持的力反馈事件  
    unsigned long swbit[BITS_TO_LONGS(SW_CNT)];     //支持的开关事件  

	unsigned int hint_events_per_packet;

	unsigned int keycodemax; //keycode表的大小
	unsigned int keycodesize; //keycode表中元素个数
	void *keycode; //设备的键盘表

	//配置keycode表  
	int (*setkeycode)(struct input_dev *dev,
			  const struct input_keymap_entry *ke,
			  unsigned int *old_keycode);
			  
	//获取keycode表  
	int (*getkeycode)(struct input_dev *dev,
			  struct input_keymap_entry *ke);

	struct ff_device *ff;

	unsigned int repeat_key;//保存上一个键值
	struct timer_list timer;

	int rep[REP_CNT];

	struct input_mt *mt;

	struct input_absinfo *absinfo;

	unsigned long key[BITS_TO_LONGS(KEY_CNT)];//按键有两种状态,按下和抬起,这个字段就是记录这两个状态。  
	unsigned long led[BITS_TO_LONGS(LED_CNT)];
	unsigned long snd[BITS_TO_LONGS(SND_CNT)];
	unsigned long sw[BITS_TO_LONGS(SW_CNT)];

	//操作接口
	int (*open)(struct input_dev *dev);
	void (*close)(struct input_dev *dev);
	int (*flush)(struct input_dev *dev, struct file *file);
	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

	struct input_handle __rcu *grab;

	spinlock_t event_lock;
	struct mutex mutex;

	unsigned int users;
	bool going_away;

	struct device dev;

    struct list_head    h_list;    //h_list是一个链表头,用来把handle挂载在这个上  
    struct list_head    node;      //这个node是用来连到input_dev_list上的  

	unsigned int num_vals;
	unsigned int max_vals;
	struct input_value *vals;

	bool devres_managed;
};


// input_dev->evbit 表示设备支持的事件类型,可以是下列值的组合
       #define EV_SYN           0x00  //同步事件
        #define EV_KEY           0x01 //绝对二进制值,如键盘或按钮
        #define EV_REL           0x02 //绝对结果,如鼠标设备
        #define EV_ABS           0x03 //绝对整数值,如操纵杆或书写板
        #define EV_MSC          0x04 //其它类
        #define EV_SW            0x05 //开关事件
        #define EV_LED          0x11 //LED或其它指示设备
        #define EV_SND         0x12 //声音输出,如蜂鸣器
        #define EV_REP         0x14 //允许按键自重复
        #define EV_FF             0x15 //力反馈
        #define EV_PWR        0x16 //电源管理事件

说明:

在输入子系统框架下,我们一般的编写驱动也就是对device部分j即驱动层进行编写(分配input_dev并配置,驱动入口,出口,中断时进行中断判断,然后上报事件等),然后对该device的input_dev进行注册。

问题:我们在编写 输入子系统驱动的时候,是怎么将驱动层与事务管理层联系起来的,驱动层千篇一律的一个步骤代码中道理做了什么?

5.1 驱动层关键代码说明
//步骤一:  
    1. 分配一个 input_dev 设备结构体
    2. 初始化  设置 input 输入设备结构体设备属性
    3. 设置你的input设备支持的事件类型以及所支持的事件
//步骤二:
	1. 注册中断处理函数,在中断处理程序中上报事件

//步骤三:
	1. 注册输入设备到输入字系统



注意:驱动层input输入子系统的工作很简单,主要在 prob函数 和 中断函数中,
static int matrix_keypad_probe(struct platform_device *pdev)
{
    //输入设备结构体
    struct input_dev *input_dev; 

    //将会分配一个 input_dev 设备结构体,并且在 /sys/class/input/input-n 下创建设备属性文件
    input_dev = input_allocate_device();

    //初始化  设置 input 输入设备结构体
    input_dev->name     = pdev->name;
    input_dev->id.bustype   = BUS_HOST;
    input_dev->dev.parent   = &pdev->dev;
    input_dev->open     = matrix_keypad_start;
    input_dev->close    = matrix_keypad_stop;

    //设置支持的按键事件类型,如设置支持按键类型
	input_dev->evbit[0] = BIT_MASK(EV_KEY); 
    
    //所支持的事件 如设置支持  KEY_F18/KEY_F19/KEY_F20/KEY_F21/KEY_F22按键事件
     set_bit(KEY_F18, input_dev->keybit);
     set_bit(KEY_F19, input_dev->keybit);
     set_bit(KEY_F20, input_dev->keybit);
     set_bit(KEY_F21, input_dev->keybit);
     set_bit(KEY_F22, input_dev->keybit);        


    //注册 input输入设备
    err = input_register_device(keypad->input_dev);
}

    //上报事件
    XXXX_interrupt
{
    //提交输入事件
    input_event(input_dev, EV_MSC, MSC_SCAN, code);

    //提交按键值
    input_report_key(input_dev,keycodes[code],new_state[col] & (1 << row));

    //同步
    input_sync(input_dev);
}
5.2 input_register_device关键信息分析
int input_register_device(struct input_dev *dev)
{
	struct input_devres *devres = NULL;
	struct input_handler *handler;
	unsigned int packet_size;
	const char *path;
	int error;

	if (dev->devres_managed) {
		devres = devres_alloc(devm_input_device_unregister,
				      sizeof(struct input_devres), GFP_KERNEL);
		if (!devres)
			return -ENOMEM;

		devres->input = dev;
	}

	/* 看注释 Every input device generates EV_SYN/SYN_REPORT events. */
	__set_bit(EV_SYN, dev->evbit);

	/* KEY_RESERVED is not supposed to be transmitted to userspace. */
	__clear_bit(KEY_RESERVED, dev->keybit);

	/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
	input_cleanse_bitmasks(dev);

	packet_size = input_estimate_events_per_packet(dev);
	if (dev->hint_events_per_packet < packet_size)
		dev->hint_events_per_packet = packet_size;

	dev->max_vals = dev->hint_events_per_packet + 2;
	dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
	if (!dev->vals) {
		error = -ENOMEM;
		goto err_devres_free;
	}

	/*
	 * If delay and period are pre-set by the driver, then autorepeating
	 * is handled by the driver itself and we don't do it in input.c.
	 */
	if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
		input_enable_softrepeat(dev, 250, 33);

	if (!dev->getkeycode)
		dev->getkeycode = input_default_getkeycode;

	if (!dev->setkeycode)
		dev->setkeycode = input_default_setkeycode;

	error = device_add(&dev->dev);
	if (error)
		goto err_free_vals;

	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
	pr_info("%s as %s\n",
		dev->name ? dev->name : "Unspecified device",
		path ? path : "N/A");
	kfree(path);

	error = mutex_lock_interruptible(&input_mutex);
	if (error)
		goto err_device_del;

	list_add_tail(&dev->node, &input_dev_list);
	
 / 将新分配的input设备连接到input_dev_list链表上  
	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler);///遍历input_handler_list链表,配对 input_dev 和 input_handler,

	input_wakeup_procfs_readers();

	mutex_unlock(&input_mutex);

	if (dev->devres_managed) {
		dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
			__func__, dev_name(&dev->dev));
		devres_add(dev->dev.parent, devres);
	}
	return 0;

err_device_del:
	device_del(&dev->dev);
err_free_vals:
	kfree(dev->vals);
	dev->vals = NULL;
err_devres_free:
	devres_free(devres);
	return error;
}

输入系统大致流程: 设备驱动 --> 核心层 --> 事件处理层 --> 用户空间

六 输入子系统实例1

场景说明:
3288主板I2C4 连接外部单片机小板,外部小板按键板按键 发生变化时通过中断提示3288主板,3288主板通过i2c获取外部小板某寄存器内数值。将这按键变化的数值 与内核输入子系统中的 KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22 一一对应。并将键值上报。

static int chensai_i2c_read( struct i2c_client* client,unsigned char reg,uint8_t *data, char device_addr)
{  
    int ret;
    struct i2c_msg msgs[] = {
            {
                 .addr = device_addr,
                 .flags = 0,
                // .len = 1,
                 .len = sizeof(reg),
                 .buf = &reg,// 寄存器地址
             },
            {
                 .addr = device_addr,
                // .flags = I2C_M_RD,0x01
                 .flags = I2C_M_RD,
                 .len = sizeof(data),
                 .buf = data,// 寄存器的值
             },
        };

        ret = i2c_transfer(client->adapter, msgs, 2);
            if (ret < 0)
            {
                printk("i2c read error\n");
            }
        
    return ret;
}


static irqreturn_t chensai_irq_handler(int irq, void *dev_id)
{
   
    int gpio_val;
    int keys_val[5];
    int i;//,j;

    disable_irq_nosync(chensai_irq_num);

    chensai_i2c_read(chensai_client, register_addr, key_status, chensai_iic_addr);
    key_status_val = key_status[0];

    DBG("%s  key_status_val=%d\n", __func__, key_status_val);


for( i=0; i < 5; i++)
            {
                if(key_status_val & (1 << i))
                {
                    keys_val[i] = 1;
                }else{
                    keys_val[i] = 0;
                }
            }    


   //上报
            input_report_key(input_dev, KEY_F18, keys_val[0]);
            input_report_key(input_dev, KEY_F19, keys_val[1]);
            input_report_key(input_dev, KEY_F20, keys_val[2]);
            input_report_key(input_dev, KEY_F21, keys_val[3]);
            input_report_key(input_dev, KEY_F22, keys_val[4]);
            
            input_sync(input_dev);

    
    enable_irq(chensai_irq_num);

    return IRQ_HANDLED;
}


static int chensai_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int error;
    int ret;
    unsigned int gpio_num;
    struct device_node *np = client->dev.of_node;
    enum of_gpio_flags flags;
    static struct task_struct *task;
    
    DBG("%s : chensai_probe\n", __func__);
    
    //将会分配一个 input_dev 设备结构体,并且在 /sys/class/input/input-n 下创建设备属性文件

    input_dev = input_allocate_device();
    

    // input输入子系统设备属性
    input_dev->name = "chensai_input_device";
    input_dev->phys = "chensai-input";

    input_dev->id.bustype = BUS_HOST;
    input_dev->id.vendor  = 1;
    input_dev->id.product = 1;
    input_dev->id.version = 1;


    input_dev->evbit[0] = BIT_MASK(EV_KEY);//设置支持的按键事件类型 
    

   //设置支持  KEY_F18/KEY_F19/KEY_F20/KEY_F21/KEY_F22按键事件
     set_bit(KEY_F18, input_dev->keybit);
     set_bit(KEY_F19, input_dev->keybit);
     set_bit(KEY_F20, input_dev->keybit);
     set_bit(KEY_F21, input_dev->keybit);
     set_bit(KEY_F22, input_dev->keybit);        


    error = input_register_device(input_dev);
    if(error){
        printk("Failed to register input_dev\n");
    }
    
    gpio_num =of_get_named_gpio_flags(np, "irq-gpio", 0, &flags);
    DBG("%s  gpio_num=%d\n", __func__, gpio_num);
    
    
    if (!gpio_is_valid(gpio_num)){
        DBG("%s  gpio_is_unvalid \n", __func__);
    }
    
    if (gpio_request(gpio_num, "irq-gpio")) {
        DBG("%s  failed to request  irq-gpio, gpio_num =%d\n", __func__, gpio_num);
    }
    
    gpio_direction_input(gpio_num);
    chensai_irq_num = gpio_to_irq(gpio_num);    //将gpio转换成对应的中断号
    DBG("%s  chensai_irq_num=%d\n", __func__, chensai_irq_num);
   
    ret = request_irq(chensai_irq_num, chensai_irq_handler, IRQ_TYPE_EDGE_FALLING, "chensai_irq", NULL);
    if (ret) {
            printk("request_irq error\n");
    }

    chensai_client = client;
    
    return 0;
}


static const struct i2c_device_id chensai_id[] = {
    {"chensai_keyboard", 0},
    {}
};
MODULE_DEVICE_TABLE(i2c, chensai_id);

static struct i2c_driver chensai_drv = {
    .driver     = {
        .name   = "chensai",
        .owner  = THIS_MODULE,
    },
    .probe = chensai_probe,
    .id_table = chensai_id,
};

static int chensai_init(void)
{
    i2c_add_driver(&chensai_drv);
    return 0;
}

static void chensai_exit(void)
{
    i2c_del_driver(&chensai_drv);

    input_unregister_device(input_dev);
    free_irq(chensai_irq_num, chensai_irq_handler);
}

module_init(chensai_init);
module_exit(chensai_exit);
MODULE_LICENSE("GPL");

六 TP调试记录

如果出现触摸屏出现触摸坐标偏移或者完全反向等现象,需要在驱动中调整

根据从设备树中 索引的 “tp-size”查找
mGtpChange_X2Y = FALSE;// X 轴 Y轴 坐标需要对调
mGtp_X_Reverse = FALSE; //X轴标是否需要对调
mGtp_Y_Reverse = FALSE; //Y轴是否需要对调

三个参数

深入Linux输入子系统:从`/dev/input/eventX`到`struct input_event`的完整数据流分析 本文深入解析Linux输入子系统从硬件中断到用户空间事件的完整数据流,涵盖`/dev/input/eventX`设备节点和`struct input_event`关键数据结构。通过分析键盘、鼠标和触屏等设备的处理流程,揭示Linux如何将物理输入转化为应用程序可处理的事件,并提供调试与性能优化实战建议。 阅读详情

相关推荐

数控弯管机xyz转换ybc的算法_数控弯管机技术的更新和应用.pdf

您所在位置:网站首页 > 海量文档&nbsp>&nbsp汽车/机械/制造&nbsp>&nbsp机械/模具设计数控弯管机技术的更新和应用.pdf3页本文档一共被下载:次,您可全文免费在线阅读后下载本文档。 下载提示1.本站不保证该用户上传的文档完整性,不预览、不比对内容而直接下载产生的反悔问题本站不予受理。2.该文档所...

weixin_31493101的博客 1520

【Linux】Linux input子系统之Input event codes

本文通过Linux input子系统中的函数引出Linux input子系统中的Input event codes部分,并给出了官方参考指南链接

永远年轻 永远热泪盈眶 5020

Webug4.0 打靶笔记

一、Webug 的安装 (1)基本信息 WeBug 名称定义为”我们的漏洞”靶场环境 ,基础环境是基于 PHP/mysql 制作搭建而成,中级环境与高级环境分别都是由互联网漏洞事件而收集的漏洞存在的操作环境。部分漏洞是基于Windows操作系统的漏洞所以将WeBug的web环境都装在了一个纯净版的Windows 2003的虚拟机中。在 webug3.0 发布后的四百多天 226安全团队终于在大年初二发布了 webug 的 4.0 版本。 (2)虚拟机的安装 最新超详细VMware虚拟机下载与安装_隐

m0_60716947的博客 6127

linux键盘按键驱动

1.先来介绍以下几个结构体使用和函数,下面代码中会用到 1)input_dev驱动设备结构体中常用成员如下: struct input_dev { void *private; const char *name; //设备名字 const char *phys; //文件路径,比如 input/buttons const char *uniq; struct input_id id; ...

hello15830的博客 1234

VCS HVP与验证计划planner:数字IC验证环境搭建与收敛实践

在数字IC验证中,VCS 是广泛使用的编译型仿真器,HVP 通常指面向硬件验证平台的高性能编译选项集,用于开启 SystemVerilog 类、随机约束和 NTB 等能力;planner 则指验证计划管理,核心是把功能点与测试用例、覆盖点建立映射。理解 VCS 先编译后仿真的原理,有助于优化增量编译、调试访问和覆盖率采集,提升回归收敛效率。规划驱动让激励不再散落,设计改版时能快速定位受影响用例,交接和覆盖率收敛更有依据。无论是 UVM 环境搭建、编译参数选择,还是验证计划表维护,都需要把工具选项与规划方法结

weixin_42543046的博客 336

【LTspice】005 伯德图绘制

目录 1 伯德图介绍 2 LTspice 截止频率验证 3 LTspice中如何添加光标 4 LTspice中如何将 幅频 和 相频曲线分开 1 伯德图介绍 Bode图由对数幅频特性和对数相频特性两张图组成。伯德图_百度百科 (1)对数幅频特性曲线 对数幅频特性是频率特性的对数值L(ω)=20lgA (ω)(dB)与频率ω的关系曲线;对数幅频特性的纵轴采用线性分度,为幅值的分贝数,即 L(ω)=20lgA(ω),A(ω)每增加10倍,L(ω)增加20dB;横坐标采用对数分度,即横轴上的ω取

m0_59642765的博客 1万+

Linux input子系统之核心层

上一节已经了解了input子系统的设备驱动层的驱动编写,本节就来讲解一下input子系统的核心层。input子系统的核心层有两个作用,对设备驱动层提供设备注册函数,对事件驱动层上报事件。 一、 input子系统初始化 static int __init input_init(void) { int err; err = class_register(&input_class); // 注册类,类名为input if (err) { pr_err("unable to registe

hwx1546的博客 969

线性代数知识点整理

线性代数基础知识点摘要整理

cuguanren的博客 3万+

Linux驱动input子系统基础之按键

linux中管理所有的输入类设备的体系。(例如鼠标、触摸屏、按键、键盘等等)linux中输入设备的编程模型//时间(时刻)__u16 type;//类型的编号(以键盘为例)__u16 code;//编码值(那个按键)//操作值(谈起还是按下)用这个结构体描述一个输入类事件,例如按一下按键或者动一下鼠标就是一个事件将各种不同类型的输入设备、不同的寄存器、不同操作方法囊括起来(2)input子系统分4个部分。

weixin_54683011的博客 1714

android studio 2022打开了v1 签名但是生成的apk没有v1签名问题

我使用了Android Studio Flamingo | 2022.2.1 Patch 2版本的IDE编译了一个apk,但是apksigner查看apk的签名信息时,发现只有v2签名,没有v1签名。试着改小一下minSdkVersion 为23重新编译apk,再检测一下签名版本v1居然打开了!参考网上搜索到的方法,手动在build.gradle里面添加。但是编译出来的apk还是只有v2的签名。里面提到了v1签名还和。

hedalei的博客 466

Thor芯片部署

英伟达Jetson Thor芯片性能分析与应用现状 摘要:英伟达最新发布的Jetson Thor芯片基于Blackwell架构,官方宣称AI算力达2070TFLOPS(FP4),较前代Orin提升7.5倍,配备14核Arm CPU和128GB内存。然而实际量产版算力缩水至700TOPS,且因4nm制程良率问题多次延期至2025-2026年交付。该芯片支持FP4/FP8量化、Transformer引擎等先进特性,适用于自动驾驶、机器人等边缘AI场景,但受供应链限制,国内厂商倾向自研方案。开发者可通过JetPa

xiaomu_347的博客 430

【Linux驱动开发】input子系统

input子系统

ty的博客 1878

红火蚁巢穴AI智能识别,提供方案与思路!

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值 。

**My Coding Family** 1041

Linux/Android多点触控

1、input事件   对于所有的input设备,报告input事件时候都分这么几部分,首先在probe函数中设置设备发送的事件类型、按键类型,设置设备一些属性信息。然后在发送事件时候要根据probe的设置来发送事件,否则就会被判为无效忽略掉。   input子系统事件分为type、code、value三部分。type表示事件的类型,按键、绝对坐标等。code表示键值、触摸坐标等。value表示数...

深海 1784

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Linux 输入设备调试详解(零基础开发)Rotary_Encoder旋转编码器驱动(EC11)通用GPIO为例 挂载input输入子系统

查看/proc/bus/input/devices文件 nano /proc/bus/input 现在可以看到两个设备 Name 设备名字 Handlers 设备节点event2 注册旋转编码器设备驱动 键盘

“逛丢一只鞋”的博客 6199

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

jz2440 输入子系统分析

目录步骤 本文是基于韦东山视频的学习笔记 汇总点这 步骤 分配一个input_dev结构体 设置(能产生哪类事件、能产生这类操作里的哪些事件) 注册 硬件相关的操作 static int buttons_init(void) { int i, ret; /* 1. 分配一个input_dev结构体 */ buttons_dev = input_allocate_device(); /* 2. 设置 */ /* 2.1 能产生哪类事件 */ set_bit(EV_KEY, button

weixin_36778746的博客 264

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Linux 输入子系统(Input Subsystem)一文吃透:结构、调用链、关键 API 与实战

本文深入解析 Linux 输入子系统(Input Subsystem)的核心机制与实现。文章从三层架构(设备驱动层、Input Core 层、事件处理层)展开,详细介绍了关键数据结构(input_dev、input_handler 等)、事件类型(EV_KEY/EV_ABS等)以及事件从硬件到用户空间的完整传递链路。通过一个最小按键驱动示例,演示了设备驱动开发的关键步骤,包括输入设备注册、事件上报等核心 API 的使用。最后还介绍了输入子系统的调试方法和实用工具,为开发者提供了一套完整的输入设备开发与调试指

Interview_TC的博客 1884

输入子系统

又叫做 input 子系统 一. 简介: 输入子系统主要涉及到比如键盘上的按键,鼠标的按键,滚轮等的事件,在linux内核中相应部分叫做输入子系统,按照子系统的规则,可以复用内核中代码,很简单的实现开发板上的按键实现鼠标的左键,键盘上的按键等的动作。 Linux之输入子系统分析(详解) 总结: 1. 注册输入子系统,进入input_init(): 创建主设备号为1...

老鹏 337

深入Linux输入子系统:从驱动开发到调试实战全解析

在Linux系统开发中,输入子系统是连接物理输入设备与用户应用的核心框架,它遵循机制与策略分离的设计哲学。其核心原理基于典型的三层架构:驱动层负责硬件交互与原始数据采集,核心层进行事件路由与抽象,事件层则通过标准接口(如evdev)向用户空间提供统一的事件流。这一设计的技术价值在于实现了硬件多样性的统一抽象,为上层应用提供了稳定、一致的输入API。在嵌入式设备、工控HMI、移动终端等应用场景中,深入理解输入子系统对于开发稳定的触摸屏、键盘、鼠标等设备驱动至关重要。本文聚焦于input_register_de

weixin_29174013的博客 262

RK3568之输入子系统

Linux 内核对其庞杂的功能体系进行了模块化的归类,主要涵盖核心子系统(Core subsystems)、输入之系统、GPIO子系统,pinctl子系统。本文将聚焦于“输入子系统”展开深入剖析。

weixin_51904054的博客 256

20251202 - Linux输入子系统

输入子系统是为了统一各类输入设备的接口。核心结构体是(时间、类型、代码、值)。核心流程:硬件 -> 驱动 -> Core -> Handler (evdev) -> APP。调试:先用找设备,再用evtest抓数据。

qq_50932511的博客 943

Linux输入子系统:从事件上报到用户空间交互

本文深入解析Linux输入子系统的工作原理,从事件上报机制到用户空间交互流程。详细介绍了输入设备的识别、事件解析方法,以及驱动开发实战,包括GPIO按键和触摸屏等多类型设备的支持。通过实际代码示例展示应用层事件读取和驱动开发技巧,帮助开发者掌握Linux输入子系统的核心概念与最佳实践。

z5a6b的博客 670

如何查看输入子系统Input上报信息

目录解决问题解决思路解决方法 解决问题 如何查看输入子系统Input上报信息 解决思路 百度input 参考input信息:https://blog.youkuaiyun.com/u014135607/article/details/79313085 解决方法 adb shell getevent -l /dev/input/event0 查看有哪些Input设备 adb shell getevent -i /dev/input/event0 查看input设备基本信息 ...

jiuguaqiao6494的博客 474

linux 输入子系统代码例子

linux 输入子系统代码例子

qq_32012843的博客 4374

输入子系统框架:从一次触摸屏失灵说起

《嵌入式开发中的输入子系统调试经验》摘要:文章分享了触摸屏驱动调试案例,重点剖析Linux输入子系统的三层架构(驱动层、核心层、事件处理层)。通过实际故障演示了数据上报必须包含input_sync()同步事件的关键细节,提供了/proc调试接口、中断检测等实用排查方法,并总结了坐标翻转、协议选择、电源管理等典型问题。作者建议开发时严格遵循事件上报规范,参考内核标准驱动实现,强调真实场景测试的重要性,指出输入设备开发需要兼顾技术实现和用户体验。

m0_73356859的博客 372
上一篇: RK3288 Android5.1 隐藏 蓝牙网络共享与移动网络设置项
下一篇: Binder进程间通信系统第二篇-----Binder驱动程序
Ma浩然
博客等级 码龄9年 1万+粉丝 462原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ma浩然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值