Linux设备驱动之——input子系统

什么是INPUT    

Input子系统处理输入事务,任何输入设备的驱动程序都可以通过Input输入子系统提供的接口注册到内核,利用子系统提供的功能来与用户空间交互。输入设备一般包括键盘,鼠标,触摸屏等,在内核中都是以输入设备出现的。下面分析input输入子系统的结构,以及功能实现。

linux中input系统主设备号是13

次设备号: 

0-31      joystick(游戏杆)

32-62  mouse(鼠标)

63   mice(鼠标)

64-95     事件(Event)设备

Input子系统的结构

  1. Input子系统是分层结构的,总共分为三层: 硬件驱动层,子系统核心层,事件处理层。

(1)其中硬件驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,需要驱动程序的作者来编写。
(2)子系统核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
(3)事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。

  2.Input子系统的三个重要结构体:

input_dev 是硬件驱动层,代表一个input设备

input_handler 是事件处理层,代表一个事件处理器

input_handle 个人认为属于核心层,代表一个配对的input设备与input事件处理器

struct input_handle {  
    void *private;   //每个配对的事件处理器都会分配一个对应的设备结构,如evdev事件处理器的evdev结构,注意这个结构与设备驱动层的input_dev不同,初始化handle时,保存到这里。   
    int open;        //打开标志,每个input_handle 打开后才能操作,这个一般通过事件处理器的open方法间接设置   
    const char *name;   
    struct input_dev *dev;  //关联的input_dev结构   
    struct input_handler *handler; //关联的input_handler结构   
    struct list_head    d_node;  //input_handle通过d_node连接到了input_dev上的h_list链表上   
    struct list_head    h_node;  //input_handle通过h_node连接到了input_handler的h_list链表上   
};  
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;
	struct list_head	node;
};
struct input_dev {

	void *private;

	const char *name;
	const char *phys;
	const char *uniq;
	struct input_id id;
	........
	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 class_device cdev;
	union {			/* temporarily so while we switching to struct device */
		struct device *parent;
	} dev;

	struct list_head	h_list;
	struct list_head	node;
}

  • 在内核中,input_dev 表示一个 input设备;input_handler 来表示input设备的 interface。 所有的input_dev 用双向链表 input_dev_list 连起来。
  • 在调用 int input_register_device(struct input_dev *dev) 的时候,会将新的 input_dev 加入到这个链表中。所有的input_handler     用双向链表 input_handler_list 连起来。
  • 在调用 int input_register_handler(struct input_handler *handler) 的时候,会将新的 input_handler 加入到这个链表中。每 个input_dev 和 input_handler 是要关联上才能工作的,在注册 input_dev 或者 input_handler的时候,就遍历上面的列表,找到相匹配的,然后调用 input_handler 的 connect函数来将它们联系到一起。
  • 通常在input_handler 的 connect函数中,就会创建 input_handle, input_handle就是负责将 input_dev 和input_handler 联系在一起的.



输入事件

     各层之间通信的基本单位就是事件,任何一个输入设备的动作都可以抽象成一种事件,如键盘的按下,触摸屏的按下,鼠标的移动等。事件有三种属性:类型(type),编码(code),值(value),Input子系统支持的所有事件都定义在input.h中,包括所有支持的类型,所属类型支持的编码等。事件传送的方向是 硬件驱动层-->子系统核心-->事件处理层-->用户空间

设备有着自己特殊的按键键码,我需要将一些标准的按键,比如0-9,X-Z等模拟成标准按键,比如KEY_0,KEY-Z等,所以需要用到按键模拟,具体 方法就是操作/dev/input/event1文件,向它写入个input_event结构体就可以模拟按键的输入了。


linux/input.h中有定义,这个文件还定义了标准按键的编码等

struct input_event {

struct timeval time; //按键时间

__u16 type; //类型,在下面有定义

__u16 code; //要模拟成什么按键

__s32 value;//是按下还是释放

};

code:

事件的代码.如果事件的类型代码是EV_KEY,该代码code为设备键盘代码.代码植0~127为键盘上的按键代码,0x110~0x116 为鼠标上按键代码,其中0x110(BTN_ LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标右键,0x112(BTN_ MIDDLE)为鼠标中键.其它代码含义请参看include/linux/input.h文件. 如果事件的类型代码是EV_REL,code值表示轨迹的类型.如指示鼠标的X轴方向REL_X(代码为0x00),指示鼠标的Y轴方向REL_Y(代码 为0x01),指示鼠标中轮子方向REL_WHEEL(代码为0x08).

value:

事件的值.如果事件的类型代码是EV_KEY,当按键按下时值为1,松开时值为0;如果事件的类型代码是EV_ REL,value的正数值和负数值分别代表两个不同方向的值.

type: 

/*

 * Event types功能实现
 */

#define EV_SYN0x00 // 
表示设备支持所有的事件
#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 // 电源管理事件
#define EV_FF_STATUS0x17
#define EV_MAX 0x1f


有哪些API

分配/释放一个输入设备:

struct input_dev *input_allocate_device(void);

void input_free_device(struct input_dev *dev);

注册/注销输入设备:

int __must_check input_register_device(struct input_dev *);

void input_unregister_device(struct input_dev *);

报告输入事件:    

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);/* 报告指定type、code的输入事件 */

void input_report_key(struct input_dev *dev, unsigned int code, int value);/* 报告键值 */

        void input_report_rel(struct input_dev *dev, unsigned int code, int value);/* 报告相对坐标 */

       void input_report_abs(struct input_dev *dev, unsigned int code, int value);/* 报告绝对坐标 */

                                    void input_sync(struct input_dev *dev);/* 报告同步事件 */


Input驱动编写步骤

1.分配一个输入设备;
2.注册一个输入设备;
3.驱动支持什么事件;
Set_bit告诉inout子系统它支持哪些事件
Set_bit(EV_KEY,button_dev.evbit)
Struct input_dev中有两个成员,一个是evbit;一个是keybit.分别用来表示设备所支持的事件类型和按键类型。
4.驱动事件报告;
5.释放和注销设备;


  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在开发输入子系统设备驱动时,通常遵循以下步骤: 1. 包含必要的头文件:在驱动程序的源文件中,你需要包含一些必要的头文件,例如`linux/input.h`、`linux/module.h`、`linux/init.h`等。 2. 定义驱动模块:使用`module_init`宏定义一个初始化函数,用来加载驱动程序。例如: ```c static int __init myinput_init(void) { // 驱动初始化逻辑 return 0; } module_init(myinput_init); ``` 3. 注册输入设备:在初始化函数中,你需要创建一个输入设备并进行注册。你可以使用`input_allocate_device`函数分配一个输入设备结构体,并设置一些属性,例如设备名称、支持的事件类型等。然后,使用`input_register_device`函数注册输入设备。例如: ```c static int __init myinput_init(void) { struct input_dev *myinput_dev; myinput_dev = input_allocate_device(); if (!myinput_dev) { pr_err("Failed to allocate input device\n"); return -ENOMEM; } // 设置设备名称、支持的事件类型等 input_register_device(myinput_dev); return 0; } ``` 4. 处理输入事件:注册完输入设备后,你需要实现一个中断处理函数或者定时器处理函数,用来处理输入事件。当触发输入事件时,驱动程序会调用该函数进行处理。你可以使用`input_report_*`系列函数上报输入事件,例如鼠标移动、按键按下等。例如: ```c static irqreturn_t myinput_interrupt(int irq, void *dev_id) { // 处理输入事件的逻辑 input_report_key(myinput_dev, KEY_A, 1); // 模拟按下 A 键 input_sync(myinput_dev); // 同步输入事件 return IRQ_HANDLED; } ``` 这只是一个简单的示例,实际的输入子系统设备驱动可能还需要处理更多的细节和特定的硬件接口。更详细的编写方法和实现细节可以参考Linux内核源码中的驱动示例和相关文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值