epoll和input子系統實現input事件分發
前言
在嵌入式開發中,按鍵和觸控板等輸入系統很常見,其驅動檔案位於/dev/input
下,以event開頭,後接數字。epoll是2.6核心引入的I/O多路複用的新的高效的介面,我在以前的部落格中,使用其實現了一個聊天室伺服器程式。這裡我們使用其來監控input子系統事件。
在嵌入式軟體開發中,小的程式,我們可以直接使用I/O介面來獲得外部的時間輸入,而如果對於一個大型的嵌入式軟體系統,其會被很多地方用到,那麼就需要進行相應的封裝處理,對外提供統一的介面。
Dispatcher
本篇我們就來實現我們自己的Dispatcher,開始的時候我只監控KeyBoard的事件,所以叫KeyDispatcher
檢視本機的Input裝置
使用cat /pro/bus/input/devices
命令,我得到了我的鍵盤和滑鼠的驅動檔名。
I: Bus=0003 Vendor=24ae Product=2000 Version=0101
N: Name="RAPOO RAPOO 2.4G Wireless Device"
P: Phys=usb-0000:00:1d.0-1.3/input1
S: Sysfs=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/input/input15
U: Uniq=
H: Handlers=kbd mouse0 event7
B: PROP=0
B: EV=1f
B: KEY=4837fff072ff32d bf54444600000000 1f0001 20f908b17c000 677bfad941dfed 9ed68000004400 10000002
B: REL=1c3
我使用的是RAPOO的無線滑鼠,H: Handlers=kbd mouse0 event7
mouse0,為/dev/input
目錄下的event7檔案。
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/input4
U: Uniq=
H: Handlers=sysrq kbd event4
B: PROP=0
B: EV=120013
B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=7
keyboard為/dev/input
下的event4。
在這個工程中,我使用了我前面寫的signal機制。我們使用的事件觸發方式時ET,邊沿觸發,其在狀態轉變時只觸發一次訊號,所以對於讀寫,一定徹底。
keydispatcher.h
#ifndef KEYDISPATCHER_H
#define KEYDISPATCHER_H
#include "signal.h"
struct epoll_event;
class KeyDispatcher
{
public:
KeyDispatcher();
~KeyDispatcher();
bool init();
void run();
Signal<void (*)(int, int, int)> m_key;
private:
int m_epfd;
int m_kdfd;
int m_msfd;
void doInput(const epoll_event* ev);
};
#endif // KEYDISPATCHER_H
keydispatcher.cpp
#include "keydispatcher.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <sys/epoll.h>
#include <string.h>
#define MAX_EVENTS 10
struct epoll_event ev;
struct epoll_event events[MAX_EVENTS];
char buffer[sizeof(input_event)];
KeyDispatcher::KeyDispatcher()
{
}
KeyDispatcher::~KeyDispatcher()
{
}
bool KeyDispatcher::init()
{
bool re = false;
m_epfd = epoll_create(1);
if (m_epfd == -1) {
printf("epoll create fail!\n");
}
else {
m_kdfd = open("/dev/input/event4", O_RDONLY | O_NONBLOCK);
m_msfd = open("/dev/input/event7", O_RDONLY | O_NONBLOCK);
if (m_kdfd == -1 || m_msfd == -1) {
printf("open kd device or mouse fail!\n");
}
else {
ev.data.fd = m_kdfd;
ev.events = EPOLLIN | EPOLLET;
int i = epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_kdfd, &ev);
ev.data.fd = m_msfd;
ev.events = EPOLLIN | EPOLLET;
int j = epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_msfd, &ev);
if (i == -1 || j == -1) {
printf("add kd device in epoll fail!\n");
}
else {
re = true;
}
}
}
return re;
}
void KeyDispatcher::doInput(const epoll_event* ev)
{
ssize_t resize = 0;
ssize_t n = 0;
struct input_event input_ev;
while ((resize = read(ev->data.fd, buffer + n, sizeof(struct input_event) - n)) > 0) {
n += resize;
if (n == sizeof(input_event)) {
memcpy((void*)(&input_ev), buffer, sizeof(input_event));
m_key.eemit((int)input_ev.type, (int)input_ev.code, (int)input_ev.value);
n = 0;
}
}
}
void KeyDispatcher::run()
{
//struct input_event input_ev;
int re = 0;
while (true) {
re = epoll_wait(m_epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < re; ++i) {
if (events[i].events & EPOLLIN) {
doInput(events + i);
// while (read(events[i].data.fd, &input_ev, sizeof(struct input_event)) == sizeof(struct input_event)) {
// printf("type:%d, code:%d, value:%d \n", input_ev.type, input_ev.code, input_ev.value);
// m_key.eemit((int)input_ev.type, (int)input_ev.code, (int)input_ev.value);
// }
}
}
re = 0;
}
}
關於epoll和input子系統,請各位找些資料看下。
main.cpp
#include "keydispatcher.h"
#include <stdio.h>
#include <pthread.h>
#define KEY 0x01
#define REL 0x02
#define ABS 0x03
class Log
{
public:
void log(int type, int code, int value) {
do {
if (type == KEY) {
printf("KEY\n");
printf("---------------------------------------------------\n");
printf("type:%d, code:%d, value:%d \n", type, code, value);
printf("+++++++++++++++++++++++++++++++++++++++\n");
break;
}
if (type == REL) {
printf("REL\n");
printf("---------------------------------------------------\n");
printf("type:%d, code:%d, value:%d \n", type, code, value);
printf("+++++++++++++++++++++++++++++++++++++++\n");
break;
}
if (type == ABS) {
printf("ABS\n");
printf("---------------------------------------------------\n");
printf("type:%d, code:%d, value:%d \n", type, code, value);
printf("+++++++++++++++++++++++++++++++++++++++\n");
break;
}
} while(0);
fflush(stdout);
}
};
void* pt_callback(void * arg)
{
KeyDispatcher *keydispatcher = (KeyDispatcher*)(arg);
keydispatcher->run();
return NULL;
}
int main()
{
pthread_t ptid;
KeyDispatcher dispatcher;
Log logger;
dispatcher.m_key.connect(&logger, &Log::log);
if (dispatcher.init()) {
int ptre = pthread_create(&ptid, NULL, pt_callback, (void*)(&dispatcher));
if (ptre != 0) {
printf("dispatcher thread create fail!\n");
}
else {
pthread_join(ptid, NULL);
}
//dispatcher.run();
}
else {
printf("init fail!\n");
}
return 0;
}
mouse和keyboard都是輸入裝置,所以我們只監聽了可讀訊號,不寫,所以簡單很多。
基於這個,通過為上層應用提供統一介面新增一個編碼解析層,上層來實現處理介面,通過呼叫這些介面我們就可以實現輸入裝置的事件分發到各上層應用中。
專案使用qmake管理,在當前的目錄下,執行qmake,生成Makefile檔案,專案中的signal使用了c++0x的特性,所以請在Makefile的CXXFLAGS一項中,追加-std=c++0x
。