Android键盘系统相关代码分析(1)

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

你可能感兴趣的:(android)