Linux输入子系统(Input Subsystem)

Linux输入子系统(Input Subsystem)


1.1.input子系统概述


输入设备(如按键,键盘,触摸屏,鼠标等)是典型的字符设备,其一般的工作机制是低层在按键,触摸等动作发生时产生一个中断(或驱动通过timer定时查询),然后cpu通过SPI,I2C或者外部存储器总线读取键值,坐标等数据,放一个缓冲区,字符设备驱动管理该缓冲区,而驱动的read()接口让用户可以读取键值,坐标等数据。


在Linux中,输入子系统是由输入子系统设备驱动层、输入子系统核心层(Input Core)和输入子系统事件处理层(Event Handler)组成。其中设备驱动层提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转换为标准的输入事件,再通过核心层提交给事件处理层;而核心层对下提供了设备驱动层的编程接口,对上又提供了事件处理层的编程接口;而事件处理层就为我们用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。所以这使得我们输入设备的驱动部分不在用关心对设备文件的操作,而是要关心对各硬件寄存器的操作和提交的输入事件。

1.2. input子系统结构图


input子系统结构图如下图1所示:

图1 输入子系统结构图


1.3.linux中输入设备驱动的分层


linux中输入设备驱动的分层如下图2所示:


图2 linux中输入设备的分层

1.4. 输入子系统设备驱动层实现原理


在Linux中,Input设备用input_dev结构体描述,定义在input.h中。设备的驱动只需按照如下步骤就可实现了。 
1).在驱动模块加载函数中设置Input设备支持input子系统的哪些事件; 
2).将Input设备注册到input子系统中; 
3).在Input设备发生输入操作时(如:键盘被按下/抬起、触摸屏被触摸/抬起/移动、鼠标被移动/单击/抬起时等),提交所发生的事件及对应的键值/坐标等状态。

1.5.软件设计流程


软件设计流程如下图3所示

图 3 input子系统软件设计流程

1.6.与软件设计有关的API函数


1.6.1.分配一个输入设备


Struct input_dev *input_allocate_device*(void);

1.6.2.注册一个输入设备


Int input_register_device(struct input_dev *dev);


1.6.3.驱动实现-事件支持


Set_bit告诉inout子系统它支持哪些事件 
Set_bit(EV_KEY,button_dev.evbit) 
Struct input_dev中有两个成员,一个是evbit;一个是keybit.分别用来表示设备所支持的事件类型和按键类型。

  input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);//支持的事件类型
  input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);// EV_LED事件支持的事件码
        for (i = 0; i < 255; i++)
                set_bit(usb_kbd_keycode[i], input_dev->keybit); //EV_KEY事件支持的事件码

include/linux/input.h中定义了支持的类型


定義按鍵
我們可以設定struct input_dev裡的evbit欄位,來定義所要接受的輸入類型,目前共有8種輸入類型如下:
* EV_KEY:Keys and buttons(按鍵與按鈕)。
* EV_REL:Relative axes(相對座標)。
* EV_ABS:Absolute axes(絕對座標)。
* EV_MSC:Misc events(其它事件)。
* EV_LED:LEDs。
* EV_SND:Sounds(聲音輸入)。
* EV_REP:Autorepeat values(自動重覆數值)。
* EV_FF:Force feedback事件。

以下是一個範例,我們指定dev可接受EV_KEY事件:
dev.evbit[0] = BIT(EV_KEY);
evbit是一個陣列,每個元素可以索引一種輸入類型。每種輸入類型均可指定特定的輸入資料,例如:TAB鍵。指定方式是使用set_bit()或BIT巨集來設定每種輸入類型的陣列。以下是各輸入類型的欄位名稱:
* keybit[NBITS(KEY_MAX)]:Keys and buttons(按鍵與按鈕)。
* relbit[NBITS(REL_MAX)]:Relative axes(相對座標)。
* absbit[NBITS(ABS_MAX)]:Absolute axes(絕對座標)。
* mscbit[NBITS(MSC_MAX)]:Misc events(其它事件)。
* ledbit[NBITS(LED_MAX)]:LEDs。
* sndbit[NBITS(SND_MAX)]:Sounds(聲音輸入)。
* ffbit[NBITS(FF_MAX)]:Force feedback事件。

以下是使用set_bit()的範例:
* set_bit(KEY_UP, dev.keybit);
* set_bit(KEY_LEFT, dev.keybit);
或是使用BIT巨集也可以:
* keybit[0] = BIT(KEYUP) | BIT(KEY_LEFT);
(KEYUP与KEY_LEFT都是抬起)
input.h裡做位元運算的3個巨集如下:
* NBITS(x):計算要幾個陣列元素,才夠紀錄第x個位。
* BIT(x):傳回單獨第x個位為1時所代表的數值,例如:x=0時為0x1,x=1時為0x2,x=2時為0x4。
*LONG(x):第x個位是屬於第幾個陣列元素(即索引值)
1.6.3.1事件类型


Linux中输入设备的事件类型有(这里只列出了常用的一些,更多请看linux/input.h中):EV_SYN 0x00 同步事件 
EV_KEY 0x01 按键事件 
EV_REL 0x02 相对坐标 
EV_ABS 0x03 绝对坐标 
EV_MSC 0x04 其它 
EV_LED 0x11 LED 
EV_SND 0x12 声音 
EV_REP 0x14 Repeat 
EV_FF 0x15 力反馈


1.6.4.驱动实现-报告事件


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_report_key與input_report_rel其實都是使用input_event()的巨集,input_event()的函數原型與參數說明如下:
* void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
* @dev:指向input device的指標。
* @type:輸入類型(EV_KEY、EV_ABS等)。
* @code:輸入按鍵(例如EV_KEY的KEY_1)。
*@value:按鍵值。(按下或抬起)
在触摸屏驱动设计中,一次坐标及按下状态的整个报告过程如下: 
Input_report_abs(input_dev,ABS_X,x);//X坐标 
Input_report_abs(input_dev,ABS_Y,y);//Y坐标 
Input_report_abs(input_dev,ABS_PRESSURE,pres);//压力 
input_sync(struct input_dev *dev);//同步 

 

Input Handler
任何的按鍵輸入,都應呼叫input_event()來將input event送到input.c,再由input.c分派事件(遶送)到每一個”input handler”。有註冊input handler的驅動程式,都能讀取輸入資料。

1.6.5释放与注销设备


Void input_free_device(struct input_dev *dev); 
Void input_unregister_device(struct input_dev *);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核提供了一个称为输入子系统(input subsystem)的框架来处理输入设备的数据。输入子系统输入设备的数据转换为标准的输入事件,并将其传递给应用程序或其他系统组件。 输入子系统包含多个组件,其中最重要的是输入驱动程序(input driver)和输入事件接口(input event interface)。输入驱动程序负责从硬件设备读取输入数据,并将其转换为标准输入事件。输入事件接口提供了一种向用户空间传递输入事件的机制。 要实现输入捕获,可以编写一个输入驱动程序来读取输入设备的数据,并将其转换为标准输入事件。驱动程序可以使用内核提供的输入子系统接口来注册自己,并将输入事件传递给输入事件接口。应用程序可以使用输入事件接口来读取输入事件,并根据需要对其进行处理。 以下是一个简单的示例,演示如何编写一个输入驱动程序来捕获鼠标输入: 1. 定义一个输入设备结构体,该结构体包含输入设备的名称、ID和类型等信息。 ```c struct input_dev *dev; dev = input_allocate_device(); dev->name = "My Mouse"; dev->phys = "my_mouse/input0"; dev->id.bustype = BUS_USB; dev->id.vendor = 0x1234; dev->id.product = 0x5678; dev->id.version = 0x0100; dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT); dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); ``` 2. 注册输入设备结构体,并指定输入事件处理函数。 ```c dev->open = my_mouse_open; dev->close = my_mouse_close; dev->event = my_mouse_event; input_register_device(dev); ``` 3. 在输入事件处理函数中读取鼠标输入数据,并将其转换为标准输入事件。 ```c void my_mouse_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { switch(type) { case EV_REL: switch(code) { case REL_X: input_event(dev, EV_REL, REL_X, value); break; case REL_Y: input_event(dev, EV_REL, REL_Y, value); break; } break; case EV_KEY: input_event(dev, EV_KEY, code, value); break; } } ``` 4. 在应用程序中打开输入设备,并读取输入事件。 ```c fd = open("/dev/input/event0", O_RDONLY); while(1) { struct input_event ev; read(fd, &ev, sizeof(ev)); printf("type=%d, code=%d, value=%d\n", ev.type, ev.code, ev.value); } ``` 以上是一个简单的示例,实际的输入驱动程序可能需要更复杂的逻辑来处理不同的输入设备和输入事件类型。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值