Input輸入
輸入設備(如按鍵,鍵盤,觸摸屏,鼠標等)是典型的字符設備,其一般的工作機制是低層在按鍵,觸摸等動作發生時產生一個中斷(或驅動通過timer定時查詢),然后cpu通過SPI,I2C或者外部存儲器總線讀取鍵值,坐標等數據,放一個緩沖區,字符設備驅動管理該緩沖區,而驅動的read()接口讓用戶可以讀取鍵值,坐標等數據。
Linux 輸入子系統是 Linux內核用於管理各種輸入設備(鍵盤,鼠標,遙控桿,書寫板等等)的。輸入子系統分為三塊: input core, drivers和 event handlers。正常的路徑是從底層硬件到驅動,從驅動到input core,從 input core到 event handler,從 event handler到user space。
在Linux中,輸入子系統是由輸入子系統設備驅動層、輸入子系統核心層(Input Core)和輸入子系統事件處理層(Event Handler)組成。其中設備驅動層提供對硬件各寄存器的讀寫訪問和將底層硬件對用戶輸入訪問的響應轉換為標准的輸入事件,再通過核心層提交給事件處理層;而核心層對下提供了設備驅動層的編程接口,對上又提供了事件處理層的編程接口;而事件處理層就為我們用戶空間的應用程序提供了統一訪問設備的接口和驅動層提交來的事件處理。所以這使得我們輸入設備的驅動部分不在用關心對設備文件的操作,而是要關心對各硬件寄存器的操作和提交的輸入事件。
定位輸入設備位置(以鍵盤舉例)
通過 #cat /proc/bus/input/devices 以查看到當前input子系統下面的所有event設備。
I: Bus=0011 Vendor=0001 Product=0001 Version=ab83 N: Name="AT Translated Set 2 keyboard" P: Phys=isa0060/serio0/input0 S: Sysfs=/devices/platform/i8042/serio0/input/input4 U: Uniq= H: Handlers=sysrq kbd event4 leds B: PROP=0 B: EV=120013 B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe B: MSC=10 B: LED=7
The B
in front stands for bitmap
, N
, P
, S
, U
, H
are simply first letter in corresponding name value and I
is for ID
. In ordered fashion:
I => @id: id of the device
(struct input_id)
Bus => id.bustype
Vendor => id.vendor
Product => id.product
Version => id.version
N => name of the device.
P => physical path to the device in the system hierarchy.
S => sysfs path.
U => unique identification code for the device (if device has it).
H => list of input handles associated with the device.
B => bitmaps
PROP => device properties and quirks.
EV => types of events supported by the device.
KEY => keys/buttons this device has.
MSC => miscellaneous events supported by the device.
LED => leds present on the device.
如圖是我的鍵盤設備,可以看到是event4
EVENT事件結構體
在/usr/include/linux/input.h中有定義,這個文件定義了event事件的結構體,API和標准按鍵的編碼等;我們需要將要寫入的按鍵編碼填充到結構體中,然后寫入鍵盤或者鼠標的事件設備驅動文件中。
輸入事件的結構體:
struct input_event {
struct timeval time; //按鍵時間
__u16 type; //事件的類型
__u16 code; //要模擬成什么按鍵
__s32 value; //是按下1還是釋放0
};
標准按鍵的編碼:(只列舉部分)
type:
事件的類型:
EV_KEY, 按鍵事件,如鍵盤的按鍵(按下哪個鍵),鼠標的左鍵右鍵(是非擊下)等;
EV_REL, 相對坐標,主要是指鼠標的移動事件(相對位移);
EV_ABS, 絕對坐標,主要指觸摸屏的移動事件,但好像這個不能用在鼠標上面,也就是說無法通過這個來獲取鼠標的絕對坐標(鼠標是一個相對位移的設備)。
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,指示鼠標中輪子方向REL_WHEEL。
value:
事件的值:如果事件的類型代碼是EV_KEY,當按鍵按下時值為1,松開時值為0;
如果事件的類型代碼是EV_ REL,value的正數值和負數值分別代表兩個不同方向的值。例如:如果code是REL_X,value是10的話,就表示鼠標相對於上一次的坐標,往x軸向右移動10個像素點。
代碼示例

#include <stdio.h> #include <linux/input.h> #include <fcntl.h> #include <sys/time.h> #include <unistd.h> void keyboard_test(){ int fd=open("/dev/input/event4",O_RDWR); if(fd <= 0){ printf("Can not open keyboard input file\n"); } struct input_event *event; char buf[128]={0}; fd_set rfds; FD_ZERO(&rfds); FD_SET(fd , &rfds); while (1) { int ret = select(fd +1, &rfds, NULL, NULL, NULL); if (ret < 0) continue; if (FD_ISSET(fd , &rfds)) { int readn = read(fd , buf, sizeof(struct input_event)); if (readn <= 0) { printf("uart read error %d\n", readn); continue; } struct input_event *my_event=(struct input_event*)buf; if(my_event->type==EV_KEY){ printf("zhe shi ge anjian:%d %d\n",my_event->code,my_event->value); } } } } int main(){ keyboard_test(); return 0; }
調試過程中我遇到的幾個錯誤:1、無法打開設備文件,后想到是權限問題,root權限后依然不行,發現少加了/