1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "EvdevModule"
18//#define LOG_NDEBUG 0
19
20#include <memory>
21#include <string>
22#include <thread>
23
24#include <assert.h>
25#include <hardware/hardware.h>
26#include <hardware/input.h>
27
28#include <utils/Log.h>
29
30#include "InputHub.h"
31#include "InputDeviceManager.h"
32#include "InputHost.h"
33
34namespace android {
35
36static const char kDevInput[] = "/dev/input";
37
38class EvdevModule {
39public:
40    // Takes ownership of the InputHostInterface
41    explicit EvdevModule(InputHostInterface* inputHost);
42
43    void init();
44    void notifyReport(input_report_t* r);
45
46private:
47    void loop();
48
49    std::unique_ptr<InputHostInterface> mInputHost;
50    std::shared_ptr<InputDeviceManager> mDeviceManager;
51    std::unique_ptr<InputHub> mInputHub;
52    std::thread mPollThread;
53};
54
55static std::unique_ptr<EvdevModule> gEvdevModule;
56
57EvdevModule::EvdevModule(InputHostInterface* inputHost) :
58    mInputHost(inputHost),
59    mDeviceManager(std::make_shared<InputDeviceManager>(mInputHost.get())),
60    mInputHub(std::make_unique<InputHub>(mDeviceManager)) {}
61
62void EvdevModule::init() {
63    ALOGV("%s", __func__);
64
65    mInputHub->registerDevicePath(kDevInput);
66    mPollThread = std::thread(&EvdevModule::loop, this);
67}
68
69void EvdevModule::notifyReport(input_report_t* r) {
70    ALOGV("%s", __func__);
71
72    // notifyReport() will be called from an arbitrary thread within the input
73    // host. Since InputHub is not threadsafe, this is how I expect this to
74    // work:
75    //   * notifyReport() will queue up the output report in the EvdevModule and
76    //     call wake() on the InputHub.
77    //   * In the main loop thread, after returning from poll(), the queue will
78    //     be processed with any pending work.
79}
80
81void EvdevModule::loop() {
82    ALOGV("%s", __func__);
83    for (;;) {
84        mInputHub->poll();
85
86        // TODO: process any pending work, like notify reports
87    }
88}
89
90extern "C" {
91
92static int dummy_open(const hw_module_t __unused *module, const char __unused *id,
93        hw_device_t __unused **device) {
94    ALOGW("open not implemented in the input HAL!");
95    return 0;
96}
97
98static void input_init(const input_module_t* module,
99        input_host_t* host, input_host_callbacks_t cb) {
100    LOG_ALWAYS_FATAL_IF(strcmp(module->common.id, INPUT_HARDWARE_MODULE_ID) != 0);
101    auto inputHost = new InputHost(host, cb);
102    gEvdevModule = std::make_unique<EvdevModule>(inputHost);
103    gEvdevModule->init();
104}
105
106static void input_notify_report(const input_module_t* module, input_report_t* r) {
107    LOG_ALWAYS_FATAL_IF(strcmp(module->common.id, INPUT_HARDWARE_MODULE_ID) != 0);
108    LOG_ALWAYS_FATAL_IF(gEvdevModule == nullptr);
109    gEvdevModule->notifyReport(r);
110}
111
112static struct hw_module_methods_t input_module_methods = {
113    .open = dummy_open,
114};
115
116input_module_t HAL_MODULE_INFO_SYM = {
117    .common = {
118        .tag                = HARDWARE_MODULE_TAG,
119        .module_api_version = INPUT_MODULE_API_VERSION_1_0,
120        .hal_api_version    = HARDWARE_HAL_API_VERSION,
121        .id                 = INPUT_HARDWARE_MODULE_ID,
122        .name               = "Input evdev HAL",
123        .author             = "The Android Open Source Project",
124        .methods            = &input_module_methods,
125        .dso                = NULL,
126        .reserved           = {0},
127    },
128
129    .init = input_init,
130    .notify_report = input_notify_report,
131};
132
133}  // extern "C"
134
135}  // namespace input
136