EventHub.cpp文件中open_device函数浅析
用户态中,与内核态联系紧密的一个函数是open_device,它位于EventHub.cpp文件中,该函数有大量的底层操作,以完成上层对硬件的操作。该函数的首部为:
int EventHub::open_device(const char *deviceName)
其中参数有scan_dir()函数获得。在这里,deviceName=”/dev/input/event0”
for (attempt = 0; attempt < 10; attempt++) {
fd = open(deviceName, O_RDWR);
if (fd >= 0) break;
usleep(100);
}
这个循环给了系统10次打开设备的机会,若失败,则等待100usec后继续尝试。
---
if(ioctl(fd, EVIOCGVERSION, &version))
if(ioctl(fd, EVIOCGID, &id))
if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1)
通过ioctl接口获取驱动版本号、id号及设备名。
List<String8>::iterator iter = mExcludedDevices.begin();
List<String8>::iterator end = mExcludedDevices.end();
for ( ; iter != end; iter++) {
const char* test = *iter;
if (strcmp(name, test) == 0) {
LOGI("ignoring event id %s driver %s/n", deviceName, test);
close(fd);
fd = -1;
return -1;
}
}
检查设备执行列变,若列表中有与上一步获的设备名相同的情况(设备已经打开),则关闭设备。
if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1)
if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1)
int devid = 0;
if (devid >= mNumDevicesById) { // mNumDevicesById在构造函数中被初始化为0;
//所以会执行下面的内容;
device_ent* new_devids = (device_ent*)realloc(mDevicesById,
sizeof(mDevicesById[0]) * (devid + 1));
if (new_devids == NULL) {
LOGE("out of memory");
return -1;
}
mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name);
当new一个device_t时,会把后面的参数传送到:
EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
: id(_id), path(_path), name(name), classes(0)
, keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) {
}
其中layoutMap初始化成了:new KeyLayoutMap(),因此,进入会KeyLayoutMap();
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0)
通过ioctl接口获取键盘位掩码。
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);
这段代码的作用是加载键盘布局文件。由
snprintf(keylayoutFilename, sizeof(keylayoutFilename)......
可知:keylayoutFilename与tmptn有关。由strcpy(tmpfn, name);可知tmptn=name。在前面的一个ioctl接口中,已经获取到了设备名称。由模拟机启动日志可以知道:name=qwerty2。若access(keylayoutFilename, R_OK)为假,则使用默认的布局文件qwerty.kl
device->layoutMap->load(keylayoutFilename);
接下来,加载这个键盘布局文件。
进入load函数,我们就可以能清楚地看到文件的加载过程。
int fd = open(filename, O_RDONLY); //打开文件
off_t len = lseek(fd, 0, SEEK_END); //获取文件长度
char* buf = (char*)malloc(len+1); //开辟读取文件的缓冲区
if (read(fd, buf, len) != len) //将文件内容读到缓冲区
buf[len] = '/0' //标志文件结束
int line = 1;
char const* p = buf;
enum { BEGIN, SCANCODE, KEYCODE, FLAG } state = BEGIN;
while (true) {
String8 token = next_token(&p, &line);
进入这个死循环之间的第一条语句是:String8 token = next_token(&p, &line);而该函数的返回为:return String8(begin, end-begin);从String8类的构造函数我们知道next_token到底返回了个什么东西:
String8::String8(const char16_t* o, size_t len)
: mString(allocFromUTF16(o, len)) { }
我们可以看到,String8的这个构造函数初始化了一个字符串常量(const char* mString;)
这个字符串常量在后面会用到:
scancode = strtol(token.string(), &end, 0);
因为token.string这个函数原型为;
inline const char* String8::string() const
{
return mString;
}
以上所有对底层地操作,如:
fd = open(deviceName, O_RDWR);
if(ioctl(fd, EVIOCGVERSION, &version))
等都不是直接对键盘驱动进行操作,而是对事件处理驱动进行的操作。也就是说,事件处理驱动对键盘驱动进行了封装。
接下来,是对底层驱动的分析:
键盘驱动函数Goldfish_events.c
输入核心驱动函数input.c
事件处理驱动函数evdev.c