1、問題
通過一個死循環將讀取鍵盤對應的設備文件將觸發鍵盤事件在屏幕上打印出來,按esc退出程序
代碼是在unbuntu10.04編譯執行通過的
2、input_event描述
在Linux內核中,input設備用input_dev結構體描述,使用input子系統實現輸入設備驅動的時候,驅動的核心工作就是向系統報告按鍵、觸摸屏、鍵盤、鼠標等輸入事件(event,通過input_event結構體描述),不再需要關心文件操作接口,因為Input子系統已經完成了文件操作接口
linux/input.h 這個文件定義了event事件的結構體,API和標准按鍵的編碼等;
struct input_event {
struct timeval time; //按鍵時間
__u16 type; //事件類型
__u16 code; //要模擬成什么按鍵
__s32 value;//是按下還是釋放
};
type,指事件類型,常見的事件類型有:
EV_KEY, 按鍵事件,如鍵盤的按鍵(按下哪個鍵),鼠標的左鍵右鍵(是非擊下)等;
EV_REL, 相對坐標,主要是指鼠標的移動事件(相對位移);
EV_ABS, 絕對坐標,主要指觸摸屏的移動事件
code:事件的代碼.
如果事件的類型代碼是EV_KEY,該代碼code為設備鍵盤代碼.0~127為鍵盤上的按鍵代碼
關於按鍵代碼的定義,部分如下所示:
#define KEY_RESERVED 0
#define KEY_ESC 1
#define KEY_1 2
#define KEY_2 3
#define KEY_3 4
#define KEY_4 5
#define KEY_5 6
#define KEY_6 7
#define KEY_7 8
#define KEY_8 9
#define KEY_9 10
#define KEY_0 11
#define KEY_MINUS 12
#define KEY_EQUAL 13
#define KEY_BACKSPACE 14
#define KEY_TAB 15
#define KEY_Q 16
#define KEY_W 17
#define KEY_E 18
#define KEY_R 19
#define KEY_T 20
value:
事件的值.如果事件的類型代碼是EV_KEY,當按鍵按下時值為1,松開時值為0;如果事件的類型代碼是EV_REL,value的正數值和負數值分別代表兩個不同方向的值.
3、相關代碼
key_simulator.c
- #include
- #include
- #include
- #include
- #include
- int main ()
- {
- int keys_fd;
- char ret[2];
- struct input_event t;
- keys_fd = open ("/dev/input/event2", O_RDONLY);
- if (keys_fd <= 0)
- {
- printf ("open /dev/input/event2 device error!\n");
- return 0;
- }
- while (1)
- {
- if (read (keys_fd, &t, sizeof (t)) == sizeof (t))
- {
- if (t.type == EV_KEY)
- if (t.value == 0 || t.value == 1)
- {
- printf ("key %d %s\n", t.code,
- (t.value) ? "Pressed" : "Released");
- if(t.code==KEY_ESC)
- break;
- }
- }
- }
- close (keys_fd);
- return 0;
- }
4、注意問題
1)不同的類型計算機,設備對應event信息會有所不同
可以用cat獲得的設備對應event信息:
# cat /proc/bus/input/devices
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=LNXPWRBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
U: Uniq=
H: Handlers=kbd event0
B: EV=3
B: KEY=100000 0 0 0
I: Bus=0017 Vendor=0001 Product=0001 Version=0100
N: Name="Macintosh mouse button emulation"
P: Phys=
S: Sysfs=/devices/virtual/input/input1
U: Uniq=
H: Handlers=mouse0 event1
B: EV=7
B: KEY=70000 0 0 0 0 0 0 0 0
B: REL=3
I: Bus=0011 Vendor=0001 Product=0001 Version=ab41
N: Name="AT Translated Set 2 keyboard"
P: Phys=isa0060/serio0/input0
S: Sysfs=/devices/platform/i8042/serio0/input/input2
U: Uniq=
H: Handlers=kbd event2
B: EV=120013
B: KEY=4 2000000 3803078 f800d001 feffffdf ffefffff ffffffff fffffffe
B: MSC=10
B: LED=7
I: Bus=0011 Vendor=0002 Product=0005 Version=0000
N: Name="ImPS/2 Generic Wheel Mouse"
P: Phys=isa0060/serio1/input0
S: Sysfs=/devices/platform/i8042/serio1/input/input3
U: Uniq=
H: Handlers=mouse1 event3
B: EV=7
B: KEY=70000 0 0 0 0 0 0 0 0
B: REL=103
I line:這行包含身份信息,顯示了 bus type是 3 (usb), vendor, product, version等信息。
N line:這行包含了名字信息。
P line:這行包含了物理設備信息。
H line: 這行包含了與設備關聯的 handler drivers。
B line: 這些行包含了顯示設備能力的一些位域 (bitfield)。
我的鍵盤對應的事件類型是event2
2)open /dev/input/event2 device error
需要使用sudo或者改變設備的屬性
如下:
sudo ./key_simulator
或者
chmod 777 /dev/input/event2
本文歡迎轉載,轉載請注明作者與出處
作者:流星
出處:http://blog.sina.com.cn/staratsky
認識linux input子系統 (一)
-0-.序
本來只是想寫個內核態的鍵盤記錄的,但是發現現在的linux驅動模型已經和以前版本不同,增加了input層,幾乎所有的底層驅動都把數據封裝在event里上報給input子系統,這樣一來,kernel看起來更加模塊化,但是沒有原來鍵盤驅動那種一站通的感覺了。
於是研究起input層比起鍵盤記錄更有意思了:)這里只是記錄下自己學習后理清的思路,其實自己學習過程挺亂的,最近才有所感悟input層,畢竟硬件的底子我是沒有的。
-1-.從用戶層看input(event事件)
經常搗鼓linux一定會對/dev,/sys,/proc這幾個目錄有所印象,這是從內核導出到用戶層的接口(從這里幾乎可以觀覽內核)。這下就方便了,kernel為我們導出了input在用戶態的接口,就是/dev/input/下的接口,這里我們只關注此目錄下的eventX字符設備。
那么這些eventX是干什么用的?簡單來說就是我們對計算機的輸入(包括敲擊鍵盤,移動鼠標等等操作)經過內核(底層驅動,input)處理最后就上報到這些eventX里面了。
而這里event0,event1,..就是用來區分各個外設的,可以通過命令來查看外設具體和哪個event相關聯:
cat /proc/bus/input/devices 這里結果比較多,應為現在PC外設也蠻多的,我們可以看下鍵盤對應的條目,這里我截取2段:
I: Bus=0011 Vendor=0001 Product=0001 Version=ab54
N: Name="AT Translated Set 2 keyboard"
P: Phys=isa0060/serio0/input0
S: Sysfs=/devices/platform/i8042/serio0/input/input4
U: Uniq=
H: Handlers=kbd event4
B: EV=120013
B: KEY=4 2000000 3803078 f800d001 feffffdf ffefffff ffffffff fffffffe
B: MSC=10
B: LED=7
I: Bus=0003 Vendor=046d Product=c52f Version=0111
N: Name="Logitech USB Receiver"
P: Phys=usb-0000:00:1d.0-1/input1
S: Sysfs=/devices/pci0000:00/0000:00:1d.0/usb6/6-1/6-1:1.1/input/input8
U: Uniq=
H: Handlers=kbd event8
B: EV=1f
B: KEY=837fff 2c3027 bf004444 0 0 1 f84 8a27c000 667bfa d9415fed 8e0000 0 0 0
B: REL=40
B: ABS=1 0
B: MSC=10
從上面我們可以看到H:一行是 Handlers=kbd event4(或Handlers=kbd event8)
kbd(KEYBOARD)代表鍵盤,而后面eventX就是此鍵盤在/dev/input/下就對應的eventX字符設備,
我們可以看到,linux為我准備了2個驅動,分別是AT鍵盤和USB鍵盤,這里我的筆記本使用的是AT鍵盤(又叫PS/2鍵盤,一般筆記本自帶的鍵盤都是AT兼容的)對應於/dev/input/event4,
bash> hexdump /dev/input/event4 [現在我們隨意敲擊鍵盤,會發現大量數據涌現]
其實這些數據都是組織好的,在linux/input.h中有這些數據的結構:
struct input_event {
struct timeval time;//事件發生的時間
__u16 type;//事件類類型:按鍵和移動鼠標就是不同類型
__u16 code;
__s32 value;//事件值:按鍵a和按鍵b就對應不同值
};
這里事件指的是我們對外設的操作,比如按鍵一次a可能就產生數個input_event數據
有了這個結構就好辦了,我們可以自己寫個測試程序讀取/dev/input/event4中的數據:
- #include <stdio.h>
- #include <sys/time.h>
- #include <linux/input.h>
- #include <stdlib.h>
- void usage(char *str)
- {
- fprintf(stderr, "<usage> %s /dev/input/eventX/n", str);
- exit(1);
- }
- int main(int argc,char **argv)
- {
- FILE *fp;
- struct input_event ie;
- if (argc != 2)
- usage(argv[0]);
- fp = fopen(argv[1], "r");
- if (fp == NULL) {
- perror(argv[1]);
- exit(1);
- }
- while (1) {
- fread((void *)&ie, sizeof(ie), 1, fp);
- if (ferror(fp)) {
- perror("fread");
- exit(1);
- }
- printf("[timeval:sec:%d,usec:%d,type:%d,code:%d,value:%d]/n",
- ie.time.tv_sec, ie.time.tv_usec,
- ie.type, ie.code, ie.value);
- }
- return 0;
- }
編譯成測試test1
bash> test1 /dev/input/event4 [按回車運行程序]
[timeval:sec:1285305058,usec:857706,type:4,code:4,value:28]
[timeval:sec:1285305058,usec:857718,type:1,code:28,value:0]
[timeval:sec:1285305058,usec:857721,type:0,code:0,value:0] [之后按鍵a]
[timeval:sec:1285305059,usec:978376,type:4,code:4,value:30]
[timeval:sec:1285305059,usec:978401,type:1,code:30,value:1]
[timeval:sec:1285305059,usec:978406,type:0,code:0,value:0]
a[timeval:sec:1285305060,usec:34315,type:4,code:4,value:30] [行首顯示我在此終端按下的a]
[timeval:sec:1285305060,usec:34327,type:1,code:30,value:0]
[timeval:sec:1285305060,usec:34329,type:0,code:0,value:0]
[timeval:sec:1285305061,usec:406962,type:4,code:4,value:29]
運行程序后,我們首先看到3組input_event顯示出來了,這三組數據其實是我們剛剛運行程序時按的回車鍵的UP碼
之后我們每按一次鍵都會有6組input_event顯示出來,前3組是DOWN碼,后3組是UP碼(DOWN是按鍵被按下,UP是按鍵彈起,鍵盤有彈性:P)
其實這個程序寫完善,配合分析input_event輸出便可以做個簡單的用戶態按鍵記錄了。
可以對照linux/input.h中分析input_event的結果,比如input_event.value值為30 對應於#define KEY_A 30 顯然這是按鍵a被按下的值
既然eventX可以讀,那么eventX當然可以寫,寫對應的結果就是 模擬人敲擊鍵盤,蠻有趣的,這有個完整的寫eventX測試代碼,
由於我們可能對input_event不是很了解,所以先從eventX讀input_event,稍加修改再回寫進eventX.
- #include <stdio.h>
- #include <sys/time.h>
- #include <linux/input.h>
- #include <stdlib.h>
- void usage(char *str)
- {
- fprintf(stderr, "<usage> %s /dev/input/eventX/n", str);
- exit(1);
- }
- int main(int argc,char **argv)
- {
- FILE *fp;
- struct input_event ie[6];
- if (argc != 2)
- usage(argv[0]);
- /* 先讀取回車UP碼對應的3個input_event 並將其丟棄 */
- fp = fopen(argv[1], "r");
- if (fp == NULL) {
- perror(argv[1]);
- exit(1);
- }
- fread((void *)ie, sizeof(struct input_event), 3, fp);
- if (ferror(fp))
- perror("fread");
- fclose(fp);
- /* 循環讀寫eventX 每次讀取完整按鍵碼:6組input_event
- * 稍加修改並寫回eventX
- */
- do {
- fp = fopen(argv[1], "r");
- if (fp == NULL) {
- perror(argv[1]);
- exit(1);
- }
- fread((void *)ie, sizeof(struct input_event), 6, fp);
- if (ferror(fp))
- perror("fread");
- fclose(fp);
- fp = fopen(argv[1], "w");
- if (fp == NULL) {
- perror(argv[1]);
- exit(1);
- }
- ie[1].code += 1;
- ie[4].code += 1;
- fwrite((void *)ie, sizeof(struct input_event), 6, fp);
- if (ferror(fp)) {
- perror("fwrite");
- exit(1);
- }
- fclose(fp);
- // printf("[timeval:sec:%d,usec:%d,type:%d,code:%d,value:%d]/n",
- // ie.time.tv_sec, ie.time.tv_usec,
- // ie.type, ie.code, ie.value);
- } while (1);
- return 0;
- }
編譯成test2
bash> test2 /dev/input/event4 [按回車]
asbncv [連續敲擊abc]
這里KEY_A == 30 KEY_S == 31(即ie[1].code += 1;ie[4].code += 1;)
以上測試代碼有幾個值得注意的地方:
1.首先要清除我們運行測試程序時 回車的UP碼
2.eventX不能同時讀寫,先讀出1次敲擊鍵盤的完整6個input_event,再回寫入eventX(若不這樣做,測試不會成功)
3.input_event.time不用修改,因為此項被內核忽視([?]從evdev_write()中可以看出內核並沒有檢測時間,而是直接響應事件)
經過測試,更改第0,3項input_event.value並沒有反映,更改第1,4項的code才有反映([?]這里更改此項只是欺騙Xwindows,Xwindows解析/dev/input/eventX向用戶回顯外設操作,至於詳細input_event解析就要到Xwindows代碼中去找了,我沒看過不敢多說)
這個測試程序只是簡單模擬了按鍵事件
-----------------------------------------------------------------------------
發現語言不簡潔,之后是從內核,從驅動看input:)