Chinaunix首页 | 论坛 | 博客
  • 博客访问: 201437
  • 博文数量: 37
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 375
  • 用 户 组: 普通用户
  • 注册时间: 2015-04-23 21:00
个人简介

潜心静气。。慢慢出成果

文章分类

全部博文(37)

文章存档

2018年(5)

2017年(6)

2016年(23)

2015年(3)

我的朋友

分类: Android平台

2018-06-01 15:03:04

Android的触摸机制

kernel部分:

 这个文章研究的蛮详细,可供参考。

首先保证I2C移植成功并正常工作,如下:

系统启动后通过串口或adb shell进入系统命令行窗口,查询/sys/bus/i2c/devices目录下是否有0-0038信息,查询/sys/bus/i2c/drivers目录下是否存在‘ft5x16’设备名;先保证i2c能够正常通信;

setup_arch->setup_machine_fdt+unflatten_device_tree->arch_initcall_sync(arm64_device_init)

以上是设备驱动的执行,开始执行设备驱动。

触摸驱动部分的加入:以ft5X0X系列的驱动为例分析。

module_init(tpd_driver_init);->

tpd_driver_init->tpd_driver_add

这个地方是MTK的适配机制吧,不同的驱动挂载进去。通过硬件适配,比如I2C设备总线与从机地址通信,没有的话就会报错返回。

late_initcall(tpd_device_init);

直接执行到probe的探测函数。

g_tpd_drv->tpd_local_init();通过名字查找,直接执行tpd_local_init,以此执行驱动的probe函数。

这里初始化两个部分,tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);

这里的触摸分两个区域,一个是虚拟按键,就是常用手机的home,返回,和查看活动列表的。

#define key_1 250,1950

#define key_2 900,1950

#define TPD_KEY_COUNT           3

#define TPD_KEYS {KEY_MENU,KEY_HOMEPAGE, KEY_BACK}

 

#ifdef TPD_HAVE_BUTTON

tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);// initialize tpd button data

#endif

tpd_probe:也是基本常用的,一是配置中断口,复位按键和设备节点。中断函数。

mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);

mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);

mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);

 

mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, EINTF_TRIGGER_FALLING, tpd_eint_interrupt_handler, 1);

中断触发函数,中断来临时,触发

有点搞不明白,硬中断触发后,进入到touch_event_handler线程中去。

thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);

然后在touch_event_handler中数据上报。

 

系统挂载和使用:

systemserver.java中调用:

inputManager = new InputManagerService(context);

进入到InputManagerService.java的构造函数。

->nativeInit

nativeInit的定义在com_android_server_input_InputManagerService.cpp中,这里就是AndroidJNI的调用了。

NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,

            messageQueue->getLooper());

执行到NativeInputManager的构造函数。

sp eventHub = new EventHub();

mInputManager = new InputManager(eventHub, this, this);

然后执行到InputManager的构造函数。

mDispatcher = new InputDispatcher(dispatcherPolicy);

mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

initialize();

这里终于见到了是哪个主角,eventhub的主要作用是从/dev/input/eventX中读取插入,拔出,中断产生的数据。这里主要是开机初始化干的事情,如果是event则是执行add,如果是键盘,产生FINISHED_DEVICE_SCAN加载键盘配置文件。

inputreader则是调用getevent函数获得raw_events构造数据,通过socket管道通知inputdipatcher分发出去。

initialize();则是分别创造除非死机,否则不会撤销的线程。

windowmanagerserviceinputmanagerservice建立连接。具体以后分析。

那么线程的执行是如何开启的呢?

如下:继续回到systemserver.java中去。

inputManager.start();

执行到inputmanagerservice.javastart

nativeStart();进入到com_android_server_input_InputManagerService.cpp中执行。

进入到inputmanager.cpp中去执行start

mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);

mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);

开始各自的线程执行。

bool InputDispatcherThread::threadLoop() {

    mDispatcher->dispatchOnce();

    return true;

}

->

dispatchOnce

->

至于怎么执行到这里,需要研究inputreader的函数,一会详细交代。

mLooper->pollOnce(timeoutMillis);

pollOnce();

->

result = pollInner(timeoutMillis);

->

struct epoll_event eventItems[EPOLL_MAX_EVENTS];

int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

这里标志着阻塞在这里,等着inputreader队列里消息。

最终唤起这个进程的是inputdispatcher

 if (needWake) {

        mLooper->wake();

    }

wake();->

nWrite = write(mWakeWritePipeFd, "W", 1);

这里还要插入一知识点,为什么在EventHub的构造函数里初始化的是mWakeReadPipeFd,但这里写入的是mWakeWritePipeFd,这里就是Linuxpipe的知识了,

int wakeFds[2];

result = pipe(wakeFds);

mWakeReadPipeFd = wakeFds[0];

mWakeWritePipeFd = wakeFds[1];

result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);

result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);

epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);

写入到mWakeWritePipeFd,然后mWakeReadPipeFd读取到,唤醒这里的阻塞,真的很棒。

终于这里调用过程接上道了。

进入到inputreader的分析。

 

bool InputReaderThread::threadLoop() {

    mReader->loopOnce();

    return true;

}

先执行的是inputreader的线程,然后合成构造数据后,通过管道通知inputdispatcher执行。

 

进入到inputreader的分析中。

mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

进入到eventhub中执行。

getEvents中主要分析了三种情况,一种是设备的插入记载,remove记载,和中断数据产生记载。

for (;;) 也是进入到for循环中。

不过这里分析下epollinotify的机制,细节不叙述。

inotifyLinux的文件系统变化监听机制,创建,删除,读写。

mINotifyFd = inotify_init();

int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);

监听dev/input/下的所有文件变化。

但监听时却没有主动的上报,只是记载,因为用epoll用来监听上报。

int epfd = epoll_create(intsize);  创造一个句柄,最大监听大小是size

删除和添加监听的fb

result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);

这个是阻塞,触发就把mPendingEventItems加入队列,然后就清空,否则一直等待这里。

int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

分别监听event的数据和管道,用来通知readerdispatcher

然后再次进入到getevent中进行分析。

scanDevicesLocked();这是进入到打开设备中,open event

int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

唤醒epoll的金监控,继续进入到mReader->loopOnce进行分析。

inputreader线程在getevents数据后的处理过程:

这个调用过程中,会分为add device remove device,和中断数据处理。

其中addremove其实就是device->addMapper(new SwitchInputMapper(device))的过程。

processEventsLocked->processEventsForDeviceLocked

device->process(rawEvents, count);

这里是个virtual基类,调用的是派生类的函数,因为会进入到类似void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent)

这里插句话:raw_event中的数据就是code代表的坐标。

类似如下处理,说明一个raw_event里只是一个坐标。这个设计会不会太操蛋。也可能是我没理解到位。

一个最初级的触摸需要四个raw_event(x1,y1),(x2,y2).

触摸事件一共三种:down up  move .

down类型的touch事件需要四个RawEvent来完成,第一个是X坐标(ABS_X),第二个是Y坐标(ABS_Y),第三个代表方向(ABS_PRESSURE)(0的时候是up1的时候是down,所以这里应该是1),第四个是结束标志(SYN_REPORT)。

move类型的touch事件需要三个RawEvent来完成,第一个是X坐标,第二个是Y坐标,第三个是结束标志。up类型的touch事件需要两个RawEvent来完成,第一个代表方向(0的时候是up1的时候是down,所以这里应该是0),第四个是结束标志。可能你已经注意到了up事件是没有坐标信息的,它的坐标信息与down(没有move)或最后一个movedownup之间有move事件产生)事件的坐标相同

switch (rawEvent->code) {

            case ABS_MT_POSITION_X:

                slot->mInUse = true;

                slot->mAbsMTPositionX = rawEvent->value;

                break;

            case ABS_MT_POSITION_Y:

                slot->mInUse = true;

                slot->mAbsMTPositionY = rawEvent->value;

                break;

mQueuedListener->flush();创建NotifyArgs类型的元素,将数据压入队列中。

flush();->

args->notify(mInnerListener);

这里执行以motion为例。

void NotifyMotionArgs::notify(const sp& listener) const {

    listener->notifyMotion(this);

}

大家关注下mInnerListener是如何实例化的。

QueuedInputListener::QueuedInputListener(const sp& innerListener) :

        mInnerListener(innerListener) {

}

InputReader.cpp中构造函数中mQueuedListener = new QueuedInputListener(listener);

inputmanager.cpp中构造函数。

InputManager::InputManager(

        const sp& eventHub,

        const sp& readerPolicy,

        const sp& dispatcherPolicy) {

    mDispatcher = new InputDispatcher(dispatcherPolicy);

    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

    initialize();

}

这里可以看到mInnerListener就是InputDispatcher的实例,其实就是对象拷贝。good!!!

进入到InputDispatcher::notifyMotion

mLooper->pollOnce(timeoutMillis);

pollOnce();

->

result = pollInner(timeoutMillis)

if (needWake) {

        mLooper->wake();接上开始分析了。

}唤醒dispatchOnce

继续下一轮的执行mDispatcher->dispatchOnce();

接下来分析inputdispacther如何把事件上报给APP的。

->dispatchOnceInnerLocked

这里会根据type进入到不通的switch 下执行。

case EventEntry::TYPE_KEY

case EventEntry::TYPE_MOTION

->dispatchMotionLocked

最后所有的不同事件,都会执行到

dispatchEventLocked(currentTime, entry, inputTargets);

->prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);

->enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);

->startDispatchCycleLocked(currentTime, connection);

这里的主要作用则是加入到共享内存中去。

case EventEntry::TYPE_MOTION:

{

status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,

                    motionEntry->deviceId, motionEntry->source,

                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,

                    motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,

                    xOffset, yOffset,

                    motionEntry->xPrecision, motionEntry->yPrecision,

                    motionEntry->downTime, motionEntry->eventTime,

                    motionEntry->pointerCount, motionEntry->pointerProperties,

                    usingCoords);

}

publishMotionEvent:

执行写入到共享内存中,并发消息通知

for (uint32_t i = 0; i < pointerCount; i++) {

        msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);

        msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);

        

        /// M: input systrace @{

        if (ATRACE_ENABLED()) {

            char buffer[50];

            sprintf(buffer, "pub_x : %d, pub_y : %d, seq=%u",

                (int)pointerCoords[i].getX(), (int)pointerCoords[i].getY(), seq);

            ATRACE_BEGIN(buffer);

            ATRACE_END();

        }

        /// @}

    }

return mChannel->sendMessage(&msg);

->nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);

这里的符号蛮奇葩的哈,表示send函数的作用域无限大。这里表示调用的是全局函数。

这里的send难道是接着

pollInner();

->

int callbackResult = response.request.callback->handleEvent(fd, events, data);

这里又是socket通信,Android果然脱离不开Linux的本质呀。

looper中会监控socket fd的变化。

回调当时注册的函数NativeInputEventRecieverhandleEvent的。

->

status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);

->

status_t status = mInputConsumer.consume(&mInputEventFactory,

                consumeBatches, frameTime, &seq, &inputEvent);

这里返回consumeEventsfor循环中,执行JNI的调用JAVA -dispatchInputevent

env->CallVoidMethod(receiverObj.get(),

gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);

这里是调用到了JAVA世界的dispatchInputEvent

 

 

 

 

阅读(1976) | 评论(0) | 转发(0) |
0

上一篇:自己编写从应用到DTS与驱动

下一篇:没有了

给主人留下些什么吧!~~