按键驱动调试总结
今天把6516的按键调试结束总结一下;
本身mtk6516的工程已经整合的很好,客户在修改上很方便,有个专门的工具来修改按键,通过对pcb上的按键进行row colum的定位就知道你需要
修改的按键在什么地方了, 当然这个要根据情况来修改, 说实话,真的太简单了。。
调试后分析了一下,按键的整个调试和驱动的流程。
\alps\frameworks\base\libs\ui\EventHub.cpp 这个文件就用户输入系统的中枢。 我分析了一下这个文件。
bool EventHub::penPlatformInput(void)
res = scan_dir(device_path);
//从目录下查找设备
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; }找到后就调用 int EventHub::open_device(const char *deviceName) 打开设备。 static const char *device_path = "/dev/input"; 这个目录下的都是被搜索到。
int EventHub::open_device(const char *deviceName) { int version; int fd; struct pollfd *new_mFDs; device_t **new_devices; char **new_device_names; char name[80]; char location[80]; char idstr[80]; struct input_id id; LOGV("Opening device: %s", deviceName); AutoMutex _l(mLock); fd = open(deviceName, O_RDWR); if(fd < 0) { LOGE("could not open %s, %s\n", deviceName, strerror(errno)); return -1; } if(ioctl(fd, EVIOCGVERSION, &version)) { LOGE("could not get driver version for %s, %s\n", deviceName, strerror(errno)); return -1; } if(ioctl(fd, EVIOCGID, &id)) { LOGE("could not get driver id for %s, %s\n", deviceName, strerror(errno)); return -1; } name[sizeof(name) - 1] = '\0'; location[sizeof(location) - 1] = '\0'; idstr[sizeof(idstr) - 1] = '\0'; if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno)); name[0] = '\0'; } // check to see if the device is on our excluded list 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) { //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno)); location[0] = '\0'; } if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { //fprintf(stderr, "could not get idstring for %s, %s\n", deviceName, strerror(errno)); idstr[0] = '\0'; } int devid = 0; while (devid < mNumDevicesById) { if (mDevicesById[devid].device == NULL) { break; } devid++; } if (devid >= mNumDevicesById) { device_ent* new_devids = (device_ent*)realloc(mDevicesById, sizeof(mDevicesById[0]) * (devid + 1)); if (new_devids == NULL) { LOGE("out of memory"); return -1; } mDevicesById = new_devids; mNumDevicesById = devid+1; mDevicesById[devid].device = NULL; mDevicesById[devid].seq = 0; } mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK; if (mDevicesById[devid].seq == 0) { mDevicesById[devid].seq = 1<<SEQ_SHIFT; } new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1)); new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1)); if (new_mFDs == NULL || new_devices == NULL) { LOGE("out of memory"); return -1; } mFDs = new_mFDs; mDevices = new_devices; #if 0 LOGI("add device %d: %s\n", mFDCount, deviceName); LOGI(" bus: %04x\n" " vendor %04x\n" " product %04x\n" " version %04x\n", id.bustype, id.vendor, id.product, id.version); LOGI(" name: \"%s\"\n", name); LOGI(" location: \"%s\"\n" " id: \"%s\"\n", location, idstr); LOGI(" version: %d.%d.%d\n", version >> 16, (version >> 8) & 0xff, version & 0xff); #endif device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name); if (device == NULL) { LOGE("out of memory"); return -1; } mFDs[mFDCount].fd = fd; mFDs[mFDCount].events = POLLIN; // figure out the kinds of events the device reports // See if this is a keyboard, and classify it. Note that we only // consider up through the function keys; we don't want to include // ones after that (play cd etc) so we don't mistakenly consider a // controller to be a keyboard. uint8_t key_bitmask[(KEY_MAX+7)/8]; memset(key_bitmask, 0, sizeof(key_bitmask)); LOGV("Getting keys..."); 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; } } } // See if this is a trackball. if (test_bit(BTN_MOUSE, key_bitmask)) { uint8_t rel_bitmask[(REL_MAX+7)/8]; memset(rel_bitmask, 0, sizeof(rel_bitmask)); LOGV("Getting relative controllers..."); if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) { if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) { device->classes |= CLASS_TRACKBALL; } } } uint8_t abs_bitmask[(ABS_MAX+7)/8]; memset(abs_bitmask, 0, sizeof(abs_bitmask)); LOGV("Getting absolute controllers..."); ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask); // Is this a new modern multi-touch driver? if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask) && test_bit(ABS_MT_POSITION_X, abs_bitmask) && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) { device->classes |= CLASS_TOUCHSCREEN | CLASS_TOUCHSCREEN_MT; // Is this an old style single-touch driver? } else if (test_bit(BTN_TOUCH, key_bitmask) && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) { device->classes |= CLASS_TOUCHSCREEN; } #ifdef EV_SW // figure out the switches this device reports uint8_t sw_bitmask[(SW_MAX+7)/8]; memset(sw_bitmask, 0, sizeof(sw_bitmask)); if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) { for (int i=0; i<EV_SW; i++) { //LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask)); if (test_bit(i, sw_bitmask)) { if (mSwitches[i] == 0) { mSwitches[i] = device->id; } } } } #endif if ((device->classes&CLASS_KEYBOARD) != 0) { char tmpfn[sizeof(name)]; char keylayoutFilename[300]; // a more descriptive name device->name = name; // replace all the spaces with underscores strcpy(tmpfn, name); for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) *p = '_'; // find the .kl file we need for this device const char* root = getenv("ANDROID_ROOT");//取得root权限 snprintf(keylayoutFilename, sizeof(keylayoutFilename), "%s/usr/keylayout/%s.kl", root, tmpfn); bool defaultKeymap = false; if (access(keylayoutFilename, R_OK)) { snprintf(keylayoutFilename, sizeof(keylayoutFilename),//不能调用特定的kl文件就要用默认的。 "%s/usr/keylayout/%s", root, "qwerty.kl"); defaultKeymap = true; } device->layoutMap->load(keylayoutFilename);//交给keylayoutmap 解析,这个是一个内部类 // tell the world about the devname (the descriptive name) if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) { // the built-in keyboard has a well-known device ID of 0, // this device better not go away. mHaveFirstKeyboard = true; mFirstKeyboardId = device->id; property_set("hw.keyboards.0.devname", name); } else { // ensure mFirstKeyboardId is set to -something-. if (mFirstKeyboardId == 0) { mFirstKeyboardId = device->id; } } char propName[100]; sprintf(propName, "hw.keyboards.%u.devname", device->id); property_set(propName, name); // 'Q' key support = cheap test of whether this is an alpha-capable kbd if (hasKeycode(device, kKeyCodeQ)) { device->classes |= CLASS_ALPHAKEY; } // See if this has a DPAD. if (hasKeycode(device, kKeyCodeDpadUp) && hasKeycode(device, kKeyCodeDpadDown) && hasKeycode(device, kKeyCodeDpadLeft) && hasKeycode(device, kKeyCodeDpadRight) && hasKeycode(device, kKeyCodeDpadCenter)) { device->classes |= CLASS_DPAD; } LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n", device->id, name, propName, keylayoutFilename); } LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes); LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n", deviceName, device, mFDCount, devid, device->classes); mDevicesById[devid].device = device; device->next = mOpeningDevices; mOpeningDevices = device; mDevices[mFDCount] = device; mFDCount++; return 0; }
看到这个一段程序
// find the .kl file we need for this device const char* root = getenv("ANDROID_ROOT");//取得root权限 snprintf(keylayoutFilename, sizeof(keylayoutFilename), "%s/usr/keylayout/%s.kl", root, tmpfn); bool defaultKeymap = false; if (access(keylayoutFilename, R_OK)) { snprintf(keylayoutFilename, sizeof(keylayoutFilename),//不能调用特定的kl文件就要用默认的。 "%s/usr/keylayout/%s", root, "qwerty.kl"); defaultKeymap = true; } device->layoutMap->load(keylayoutFilename);//交给keylayoutmap 解析,这个是一个内部类还有一个消息获取函数
//获取事件,无限循环得到事件,调用阻塞函数等待 bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, int32_t* outValue, nsecs_t* outWhen) { *outDeviceId = 0; *outType = 0; *outScancode = 0; *outKeycode = 0; *outFlags = 0; *outValue = 0; *outWhen = 0; status_t err; fd_set readfds; int maxFd = -1; int cc; int i; int res; int pollres; struct input_event iev; // Note that we only allow one caller to getEvent(), so don't need // to do locking here... only when adding/removing devices. if (!mOpened) { mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; mOpened = true; } 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; } release_wake_lock(WAKE_LOCK_ID); // 阻塞等待!!!!! pollres = poll(mFDs, mFDCount, -1); acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); if (pollres <= 0) { if (errno != EINTR) { LOGW("select failed (errno=%d)\n", errno); usleep(100000); } continue; } //printf("poll %d, returned %d\n", mFDCount, pollres); // 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; } #ifdef HAVE_AEE_FEATURE #ifdef AEE_MANUAL_DB_DUMP aee_aed_dump_db((unsigned int)iev.code, (signed int)iev.value, 0); #endif #endif } 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); } continue; } } } } // 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); } } }
常用输入设备的定义有:
enum {
CLASS_KEYBOARD = 0x00000001, //键盘
CLASS_ALPHAKEY = 0x00000002, //
CLASS_TOUCHSCREEN = 0x00000004, //触摸屏
CLASS_TRACKBALL = 0x00000008 //轨迹球
};
对于按键事件,调用mDevices->layoutMap->map进行映射,调用的是文件 KeyLayoutMap.cpp
status_t KeyLayoutMap::load(const char* filename)通过解析 <Driver name>.kl 把按键的
由映射关系 KeyedVector<int32_t,Key> m_keys 把扫描码转换成andorid上层可以识别的按键。
二、按键映射
Key character maps的路径是 /system/usr/keychars,第一个查找的名字是按键驱动的名字,例如
qwerty.kl是 UTF-8类型的,格式为:key SCANCODE KEYCODE [FLAGS...]。
SCANCODE表示按键扫描码;
KEYCODE表示键值,例如HOME,BACK,1,2,3...
FLAGS有如下定义:
SHIFT: While pressed, the shift key modifier is set
ALT: While pressed, the alt key modifier is set
CAPS: While pressed, the caps lock key modifier is set
WAKE: When this key is pressed while the device is asleep, the device will
WAKE_DROPPED: When this key is pressed while the device is asleep, the device
qwerty.kcm文件为了节省空间,在编译过程中会用工具makekcharmap转化为二进制文件qwerty.bin。
在frameworks/base/services/java/com/android/server/WindowManagerService.java里创
在WindowManagerService类的构造函数WindowManagerService()中:
mQueue = new KeyQ(); //读取按键
mInputThread = new InputDispatcherThread(); //创建分发线程
...
mInputThread.start();
在启动的线程InputDispatcherThread中:
run()
process();
QueuedEvent ev = mQueue.getEvent(...)
在process() 方法中进行处理事件:
switch (ev.classType)
case RawInputEvent.CLASS_KEYBOARD:
...
dispatchKey((KeyEvent)ev.event, 0, 0);
mQueue.recycleEvent(ev);
break;
case RawInputEvent.CLASS_TOUCHSCREEN:
//Log.i(TAG, "Read next event " + ev);
dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
break;
case RawInputEvent.CLASS_TRACKBALL:
dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
break;
2、上层读取按键的流程
|
KeyQ() //KeyQ 是抽象类 KeyInputQueue 的实现
|
InputDeviceReader //在 KeyInputQueue 类中创建的线程
|
readEvent() //
|
android_server_KeyInputQueue_readEvent() //frameworks\base\services\jni\
|
hub->getEvent()
|
EventHub::getEvent() //frameworks\base\libs\ui\EventHub.cpp
|
res = read(mFDs.fd, &iev, sizeof(iev)); //
摘取网上的一段资料
键盘消息是window manager从驱动里面获取的,然后分发给应用程序框架的
具体参考如下:
2.1 第一步:用户数据收集及其初步判定
KeyInputQ在WindowMangerService中建立一个独立的线程InputDeviceReader,使用Native函数readEvent来读取Linux Driver的数据构建RawEvent,放入到KeyQ消息队列中。
preProcessEvent()@KeyInptQ@KeyInputQueue.java这个是在输入系统中的第一个拦截函数原型。KeyQ重载了preProcessEvent()@WindowManagerService.java。在该成员函数中进行如下动作:
(1) 根据PowerManager获取的Screen on,Screen off状态来判定用户输入的是否WakeUPScreen。
(2) 如果按键式应用程序切换按键,则切换应用程序。
(3) 根据WindowManagerPolicy觉得该用户输入是否投递。
2.2 第二步 消息分发第一层面
InputDispatcherThread从KeyQ中读取Events,找到Window Manager中的Focus Window,通过Focus Window记录的mClient接口,将Events专递到Client端。
如何将KeyEvent对象传到Client端:
在前面的章节(窗口管理ViewRoot,Window Manager Proxy)我们已经知道:在客户端建立Window Manager Proxy后,添加窗口到Window Manager service时,带了一个跟客户ViewRoot相关的IWindow接口实例过去,记录在WindowState中的mClient成员变量中。通过IWindow这个AIDL接口实例,Service可以访问客户端的信息,IWindow是Service连接View桥梁。
看看在Client ViewRootKeyEvent的分发过程
IWindow:dispatchKey(event)
dispatchKey(event)@W@ViewRoot@ViewRoot.java
ViewRoot.dispatchKey(event)@ViewRoot.java
message>
sendMessageAtTime(msg)@Handler@Handler.java
至此我们通过前面的Looper,Handler详解章节的分析结论,我们可以知道Key Message已经放入到应用程序的消息队列。
2.3第三步:应用消息队列分发
消息的分发,在Looper,Handler详解章节我们分析了Looper.loop()在最后后面调用了handleMesage.
…
ActivityThread.main()
Looper.loop()
ViewRoot$RootHandler().dispatch()
handleMessage
....
注意到在分发的调用msg.target.dispatch(),而这个target在第二层将消息sendMessageAtTime到消息队列时填入了mag.target=this即为msg.target=ViewRoot实例。所有此时handleMessage就是ViewRoot重载的handleMessage函数。
handlerMessage@ViewRoot@ViewRoot.java
deliverkeyEvent
如果输入法存在,dispatchKey到输入法服务。
否则deliverKeyEventToViewHierarchy@ViewRoot.java
在这里需要强调的是,输入法的KeyEvent的拦截并没有放入到Window Manager Service中,而是放入到了客户端的RootView中来处理。
2.4第四步:向焦点进发,完成焦点路径的遍历。
分发函数调用栈
deliverKeyEventToViewHierarchy@ViewRoot.java
mView.dispatchKeyEvent:mView是与ViewRoot相对应的Top-Level View.如果mView是一个ViewGroup则分发消息到他的mFocus。
mView.dispatchKeyEvent @ViewGroup (ViewRoot@root)
Event.dispatch
mFocus.dispatchKeyEevnet
如果此时的mFocu还是一个ViewGroup,这回将事件专递到下一层的焦点,直到mFocus为一个View。通过这轮调用,就遍历了焦点Path,至此,用户事件传递完成一个段落。
2.5第五步 缺省处理
如果事件在上述Focus View没有处理掉,并且为方向键之类的焦点转换相关按键,则转移焦点到下一个View。