本文主要讲解[Android Input 子系统][6],我会从一下几个方面讲解:
以上三种节点在后面会讲解分别用作什么
static int __init input_init(void)
{
int err;
err = class_register(&input_class);
if (err) {
pr_err("unable to register input_dev class\n");
return err;
}
err = input_proc_init();
if (err)
goto fail1;
err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input");
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
static void __exit input_exit(void)
{
input_proc_exit();
unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES);
class_unregister(&input_class);
}
subsys_initcall(input_init);
注意上面这两个函数都提到了input_attach_handler()这意味着我们在注册我们的input device的时候会去匹配我们的input handler同时当我们去注册input handler的时候会去匹配input device
通常这两个函数是在我们的driver里面调用的
input_dev = input_allocate_device();
if (!input_dev) {
err = -ENOMEM;
dev_err(&client->dev, "[Focal][Touch] %s: failed to allocate input device\n", __func__);
goto exit_input_dev_alloc_failed;
}
ftxxxx_ts->input_dev = input_dev;
set_bit(KEY_BACK, input_dev->keybit);
set_bit(KEY_HOME, input_dev->keybit);
set_bit(KEY_APPSELECT, input_dev->keybit);
//set_bit(KEY_POWER, input_dev->keybit);
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
// printk("maxx=%d,maxy=%d\n",ftxxxx_ts->x_max,ftxxxx_ts->y_max);
input_mt_init_slots(input_dev, CFG_MAX_TOUCH_POINTS, 0);
// input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, CFG_MAX_TOUCH_POINTS, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 31, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ftxxxx_ts->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ftxxxx_ts->y_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, PRESS_MAX, 0, 0);
input_dev->name = Focal_input_dev_name;
err = input_register_device(input_dev);
上面这段代码便是我摘抄的触摸屏驱动的部分代码,主要是input device的注册过程,重点函数是input_register_device()正如我们上面所说的他会去input_handler_list里面去寻找匹配input handler,对于向触摸屏和鼠标等我们的内核已经为我们注册好了input handler代码位于:/kernel/driver/input/evdev.c:
这段代码主要注册了一个input handler,我们的触摸屏驱动匹配到的就是这个handler
/kernel/driver/input/evdev.c:
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
static void __exit evdev_exit(void)
{
input_unregister_handler(&evdev_handler);
}
module_init(evdev_init);
module_exit(evdev_exit);
在touch driver里面调用input_register_device()会有如下关键的一段代码:
将input device添加进input_dev_list,并且对于每一个注册好的input handler调用input_attach_handler()去匹配device(在这里使我们的触摸屏)
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
。。。
下面便是匹配的代码:对于我们的touch 这里匹配到的就是我们在evdev.c里面注册好的handler,紧接着调用handler->connect(handler, dev, id);
会到evdev.c里面会知道这里的connect是evdev_connect()
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
这个函数里面用我们的设备号作为标号作为设备名:
dev_set_name(&evdev->dev, “event%d”, dev_no);
紧接着得到我们的input device在这里就是我们的touch:
evdev->handle.dev = input_get_device(dev);
还有之前在input.c里面注册的input_class:
evdev->dev.class = &input_class;
还记得我们之前在input.c里面register_chrdev_region吗:
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);这里的INPUT_MAJOR就是我们在那里注册的主设备号
下面紧接着注册字符设备:此时注册的字符设备在/dev/input/event%d:
/dev/input:这个是我们在我们的input.c里面注册的,这里的event%d是以次设备号作为标号命名的,
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
。。。。。
。。。。。
dev_set_name(&evdev->dev, "event%d", dev_no);
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
cdev_init(&evdev->cdev, &evdev_fops);
evdev->cdev.kobj.parent = &evdev->dev.kobj;
error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
err_cleanup_evdev:
evdev_cleanup(evdev);
err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
err_free_minor:
input_free_minor(minor);
return error;
}
到此我们的touch有关的内核注册函数讲解完毕,下面讲解,当我们的手指触摸touch的时候内核里面的数据是如何上报的
当我们的手指触摸touch的时候会触发中断:下面是我贴出来的focal的touchdriver里面的中断函数:首先在中断里面会调用ftxxxx_read_Touchdata()读取手指数据(一般为i2c读取),这里不是我们关注的重点;紧接着调用ftxxxx_report_value():上报读到的数据
static irqreturn_t ftxxxx_ts_interrupt(int irq, void *dev_id)
{
ret = ftxxxx_read_Touchdata(ftxxxx_ts);
。。。。。
ftxxxx_report_value(ftxxxx_ts);
。。。。。
return IRQ_HANDLED;
}
这个函数里面会调用一系列的内核input子系统提供的API如:input_report_abs(),input_mt_slot(),input_report_key()等这些函数主要是按照一定的格式将我们读到的touch手指触摸信息写入我们在evdev.c注册好的/dev/input/event0:这里我们假设我们的touch对应的次设备号是0
然后我们的用户空间就可以读取这个节点的数据得到touch的触摸事件。这个在Android 的framwork层里面会讲解,其实不只是Android其他的只要是使用linux内核的只要使用input子系统都应该是类似的在用户空间读取这个节点
static void ftxxxx_report_value(struct ftxxxx_ts_data *data)
{
.....
/*protocol B*/
filter_touch_point = event->touch_point;
for (i = 0; i < event->touch_point; i++) {
report_point=true;
if(!report_point)
continue;
input_mt_slot(data->input_dev,event->au8_finger_id[i]);
if (event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2) {
input_mt_report_slot_state(data->input_dev,MT_TOOL_FINGER,true);
input_report_abs(data->input_dev, ABS_MT_PRESSURE, event->pressure[i]);
input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->area[i]);
input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->au16_x[i]);
input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->au16_y[i]);
} else {
uppoint++;
input_mt_report_slot_state(data->input_dev,MT_TOOL_FINGER,false);
}
}
if((last_touchpoint>0)&&(event->Cur_touchpoint==0))
{
for(i=0;i<CFG_MAX_TOUCH_POINTS;i++)
{
input_mt_slot(data->input_dev,i);
input_mt_report_slot_state(data->input_dev,MT_TOOL_FINGER,false);
}
last_touchpoint=0;
}
if(filter_touch_point == uppoint) {
input_report_key(data->input_dev, BTN_TOUCH, 0);
} else {
input_report_key(data->input_dev, BTN_TOUCH, event->touch_point > 0);
}
input_sync(data->input_dev);
last_touchpoint=event->Cur_touchpoint;
}
我们以input_report_key()为例看看我们的数据是如何写入event0的。
这里我省略了很多判断,只留下关键部分
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition;
。。。。。
if (disposition & INPUT_FLUSH) {
if (dev->num_vals >= 2)
input_pass_values(dev, dev->vals, dev->num_vals);
dev->num_vals = 0;
} else if (dev->num_vals >= dev->max_vals - 2) {
dev->vals[dev->num_vals++] = input_value_sync;
input_pass_values(dev, dev->vals, dev->num_vals);
dev->num_vals = 0;
}
}
static void input_pass_values(struct input_dev *dev,
struct input_value *vals, unsigned int count)
{
struct input_handle *handle;
。。。。。
handle = rcu_dereference(dev->grab);
if (handle) {
count = input_to_handler(handle, vals, count);
} else {
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
count = input_to_handler(handle, vals, count);
}
。。。。。
}
static unsigned int input_to_handler(struct input_handle *handle,
struct input_value *vals, unsigned int count)
{
struct input_handler *handler = handle->handler;
。。。。。
if (handler->events)
handler->events(handle, vals, count);
else if (handler->event)
for (v = vals; v != end; v++)
handler->event(handle, v->type, v->code, v->value);
return count;
}
到这里调用到了input_to_handler();这个函数里面调用handler->events;
对于touch这里的handler就是我们在evdev.c里面注册的因策这里的events函数就是:evdev.c里面的evdev_events():
evdev_events函数紧接着就会调用evdev_pass_values()
static void evdev_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
ktime_t time_mono, time_real;
time_mono = ktime_get();
time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_values(client, vals, count, time_mono, time_real);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_values(client, vals, count,
time_mono, time_real);
rcu_read_unlock();
}
static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count,
ktime_t mono, ktime_t real)
{
struct evdev *evdev = client->evdev;
const struct input_value *v;
struct input_event event;
bool wakeup = false;
event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
mono : real);
/* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
for (v = vals; v != vals + count; v++) {
event.type = v->type;
event.code = v->code;
event.value = v->value;
__pass_event(client, &event);
if (v->type == EV_SYN && v->code == SYN_REPORT)
wakeup = true;
}
spin_unlock(&client->buffer_lock);
if (wakeup)
wake_up_interruptible(&evdev->wait);
}
evdev_pass_value()会调用__pass_event()将我们的touch数据写入event0的buffer,这里我们就不继续往下看了,最后调用wake_up_interruptible()唤醒用户空间读取这里数据的进程对于Android就是我们framwork层的input子系统后面会讲
现在请大家跟着我的思绪,想象假设我们的kernel已经跑起来然后跑进了第一个init进程,init进程会启动zygote进程,而zygote进程会启动systemserver进程,这里的细节之后有时间我会在其他的博文里面讲解,在这里我们重点关注和input子系统相关的部分。现在我们假设系统跑到了zygote启动systemserver,之所以讲这里是由于我们的input子系统是systemserver里面的一个service:代码如下:
在systemserver进程启动的过程中会调用startOtherService(),这个函数里面会new InputManagerService(context);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
inputManager.start();这三个函数分别创建inputmanager,将inputmanager注册进binder机制(android 的进程间通信机制)
最后调用inputmanager.start();启动inputmanager;这里我们重点分析inputmanager的创建以及启动
位置:
/framwork/base/services/java/com/android/server/systemserver.java:
private void startOtherServices() {
。。。。。
inputManager = new InputManagerService(context);
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
mActivityManagerService.setWindowManager(wm); inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
。。。。。
}
public InputManagerService(Context context) {
this.mContext = context;
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
这里有一个jni调用nativeInit()会调用到/framwork/base/services/core/jni/com_android_server_input_InputManagerService.cpp里面的nativeInit()代码如下
static jlong nativeInit(JNIEnv* env, jclass clazz,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast(im);
}
调用到了
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp& looper) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv();
mContextObj = env->NewGlobalRef(contextObj);
mServiceObj = env->NewGlobalRef(serviceObj);
{
AutoMutex _l(mLock);
mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
}
sp eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
两个重点函数 EventHub()和InputManager()第一个参数是eventhub
主要创建InputReader和InputDispatcher以及在initialize里面创建InputReaderThread和InputDispatcherThread两个线程,不过此时线程并没有启动
代码位于:/framwork/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
const sp& eventHub,
const sp& readerPolicy,
const sp& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
下面我们返回到syetemserver.java里面inputManager.start();
这个函数里面又是一个jni调用这里调用到/framwork/base/services/core/jni/com_android_server_input_InputManagerService.cpp里面的nativeStart()
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
。。。。。
}
这里的im->getInputManager()->start()调用的是/framwork/native/services/inputflinger/InputManager.cpp的start
static void nativeStart(JNIEnv* env, jclass clazz, jlong ptr) {
NativeInputManager* im = reinterpret_cast(ptr);
status_t result = im->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
在这里会启动刚才常见的两个线程
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputDispatcher thread due to error %d.", result);
return result;
}
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputReader thread due to error %d.", result);
mDispatcherThread->requestExit();
return result;
}
return OK;
}
下面我们重点分析:
mReaderThread->run(“InputReader”, PRIORITY_URGENT_DISPLAY);
这里会调用到/framwork/native/services/inputflinger/InputReader.cpp里面的thread_loop();
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
下面函数中的mEventHub->getEvents会依次打开/dev/input/目录底下的节点阻塞等待,其中就包括我们的touch,加入这是我们的手指触摸touch则会触发中断上报数据,会唤醒eventhub读取数据
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
Vector<InputDeviceInfo> inputDevices;
{ // acquire lock
AutoMutex _l(mLock);
oldGeneration = mGeneration;
timeoutMillis = -1;
uint32_t changes = mConfigurationChangesToRefresh;
if (changes) {
mConfigurationChangesToRefresh = 0;
timeoutMillis = 0;
refreshConfigurationLocked(changes);
} else if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
} // release lock
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
processEventsLocked(mEventBuffer, count);
}
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
mNextTimeout = LLONG_MAX;
timeoutExpiredLocked(now);
}
}
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
getInputDevicesLocked(inputDevices);
}
} // release lock
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
mQueuedListener->flush();
}
这里我裁剪贴出了部分getevent代码
scanDevicesLocked()函数里面会依次打开我们的/dev/input底下的节点
并且将文件描述符fd添加进mEpollFd监控(这里类似于linux的select系统调用)
最后getevent会阻塞在epoll_wait()要么超时返回要么在/dev/input下的节点有事件要读取。关于linux的阻塞IO机制我觉得很重要后面有时间我会专门有一节讲解
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
。。。。。
for (;;) {
。。。。。
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
.....
......
// Grab the next input event.
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
if (eventItem.events & EPOLLIN) {
mPendingINotify = true;
} else {
ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
}
continue;
}
if (eventItem.data.u32 == EPOLL_ID_WAKE) {
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
awoken = true;
char buffer[16];
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
}
continue;
}
Device* device = mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN) {
int32_t readSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
.....
}
.....
}
.....
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
.....
return event - buffer;
}
写到这里我们已经进入framwork层的input子系统,不过我不打算向下写了,因为这里有一篇写的不错的文章,这里有别人的一篇写的很好我这里给出连接:
android5.0 Lollipop input子系统