EventHub中主要被调用的是getEvent:
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
{
....
if (!mOpened) {
mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
mOpened = true;
}
.....
}
在该函数中首先判断/dev/input下的文件是否扫描打开过,如果没有进入openPlatformInput():
/*
* Open the platform-specific input device.
*/
bool EventHub::openPlatformInput(void)
{
/*
* Open platform-specific input device(s).
*/
int res;
mFDCount = 1;
mFDs = (pollfd *)calloc(1, sizeof(mFDs[0]));
mDevices = (device_t **)calloc(1, sizeof(mDevices[0]));
mFDs[0].events = POLLIN;
mDevices[0] = NULL;
#ifdef HAVE_INOTIFY
mFDs[0].fd = inotify_init();
res = inotify_add_watch(mFDs[0].fd, device_path, IN_DELETE | IN_CREATE);
if(res < 0) {
LOGE("could not add watch for %s, %s/n", device_path, strerror(errno));
}
#else
/*
* The code in EventHub::getEvent assumes that mFDs[0] is an inotify fd.
* We allocate space for it and set it to something invalid.
*/
mFDs[0].fd = -1;
#endif
res = scan_dir(device_path);
if(res < 0) {
LOGE("scan dir failed for %s/n", device_path);
//open_device("/dev/input/event0");
}
return true;
}
mFDCount 用来存储要poll的文件个数,使用 inotify机制来检测该目录,这样在该目录临时加入的文件也能及时打开,特别实用于以模块方式加载的驱动。然后就会扫描整个目录并打开文件。
int EventHub::scan_dir(const char *dirname)
{
char devname[PATH_MAX];
char *filename;
DIR *dir;
struct dirent *de;
dir = opendir(dirname);
if(dir == NULL)
return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
while((de = readdir(dir))) {
if(de->d_name[0] == '.' &&
(de->d_name[1] == '/0' ||
(de->d_name[1] == '.' && de->d_name[2] == '/0')))
continue;
strcpy(filename, de->d_name);
open_device(devname);
}
closedir(dir);
return 0;
}
在扫描以后就调用 open_device打开文件:
int EventHub::open_device(const char *deviceName)
{
....
fd = open(deviceName, O_RDWR);//打开文件
...
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
//LOGI("MAP/n");
//for (int i=0; i<((KEY_MAX+7)/8); i++) {
// LOGI("%d: 0x%02x/n", i, key_bitmask[i]);
//}
for (int i=0; i<((BTN_MISC+7)/8); i++) {
if (key_bitmask[i] != 0) {
device->classes |= CLASS_KEYBOARD;
break;
}
}
if ((device->classes & CLASS_KEYBOARD) != 0) {
device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
if (device->keyBitmask != NULL) {
memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
} else {
delete device;
LOGE("out of memory allocating key bitmask");
return -1;
}
}
}
判断改设备是不是键盘,如果是设置device属性;
以同样的方式判断是否是轨迹球,多点触摸,单点触摸,并设置属性;
if ((device->classes&CLASS_KEYBOARD) != 0) {
如果是键盘要做如下处理:
/ find the .kl file we need for this device
const char* root = getenv("ANDROID_ROOT");
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s.kl", root, tmpfn);
bool defaultKeymap = false;
if (access(keylayoutFilename, R_OK)) {
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s", root, "qwerty.kl");
defaultKeymap = true;
}
device->layoutMap->load(keylayoutFilename);
加载要使用的映射文件,这里默认使用的是qwerty.kl,这里是可以更改的,很多公司都会更改以使用自家的映射。
这样所有的文件都打开了,也设置了属性,我们再回到getEvent接着向下分析:
while(1) {
// First, report any devices that had last been added/removed.
if (mClosingDevices != NULL) {
device_t* device = mClosingDevices;
LOGV("Reporting device closed: id=0x%x, name=%s/n",
device->id, device->path.string());
mClosingDevices = device->next;
*outDeviceId = device->id;
if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
*outType = DEVICE_REMOVED;
delete device;
return true;
}
if (mOpeningDevices != NULL) {
device_t* device = mOpeningDevices;
LOGV("Reporting device opened: id=0x%x, name=%s/n",
device->id, device->path.string());
mOpeningDevices = device->next;
*outDeviceId = device->id;
if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
*outType = DEVICE_ADDED;
return true;
}
查看是否完全打开,如果没有完全打开就返回;
pollres = poll(mFDs, mFDCount, -1);对打开的所有文件进行poll,
// mFDs[0] is used for inotify, so process regular events starting at mFDs[1]
for(i = 1; i < mFDCount; i++) {
if(mFDs[i].revents) {
LOGV("revents for %d = 0x%08x", i, mFDs[i].revents);
if(mFDs[i].revents & POLLIN) {
res = read(mFDs[i].fd, &iev, sizeof(iev));
if (res == sizeof(iev)) {
LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
mDevices[i]->path.string(),
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
iev.type, iev.code, iev.value);
*outDeviceId = mDevices[i]->id;
if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
*outType = iev.type;
*outScancode = iev.code;
if (iev.type == EV_KEY) {
err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d/n",
iev.code, *outKeycode, *outFlags, err);
if (err != 0) {
*outKeycode = 0;
*outFlags = 0;
}
} else {
*outKeycode = iev.code;
}
*outValue = iev.value;
*outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
return true;
} else {
if (res<0) {
LOGW("could not get event (errno=%d)", errno);
} else {
LOGE("could not get event (wrong size: %d)", res);
如果可读,就判断是否是按键类型,如果是就调用映射文件,改变code,如果不是就直接上报该值。
// read_notify() will modify mFDs and mFDCount, so this must be done after
// processing all other events.
if(mFDs[0].revents & POLLIN) {
read_notify(mFDs[0].fd);
}
}
}
这个用来时时监测notify机制。这样数据就完全读出来了。其他的方法等用到时再仔细看吧。