1/*
2 * Copyright (C) 2016 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 "NanohubHAL"
18
19#include <fcntl.h>
20#include <poll.h>
21#include <unistd.h>
22#include <sys/inotify.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25
26#include <hardware/context_hub.h>
27#include <hardware/hardware.h>
28
29#include <utils/Log.h>
30#include <cutils/properties.h>
31
32#include <nanohub/nanohub.h>
33
34#include <cinttypes>
35#include <iomanip>
36#include <sstream>
37
38#include "nanohub_perdevice.h"
39#include "system_comms.h"
40#include "nanohubhal.h"
41
42#define NANOHUB_LOCK_DIR        "/data/vendor/sensor/nanohub_lock"
43#define NANOHUB_LOCK_FILE       NANOHUB_LOCK_DIR "/lock"
44#define NANOHUB_LOCK_DIR_PERMS  (S_IRUSR | S_IWUSR | S_IXUSR)
45
46namespace android {
47
48namespace nanohub {
49
50inline std::ostream &operator << (std::ostream &os, const hub_app_name_t &appId)
51{
52    char vendor[6];
53    __be64 beAppId = htobe64(appId.id);
54    uint32_t seqId = appId.id & NANOAPP_VENDOR_ALL_APPS;
55
56    std::ios::fmtflags f(os.flags());
57    memcpy(vendor, (void*)&beAppId, sizeof(vendor) - 1);
58    vendor[sizeof(vendor) - 1] = 0;
59    if (strlen(vendor) == 5)
60        os << vendor << ", " << std::hex << std::setw(6)  << seqId;
61    else
62        os << "#" << std::hex << appId.id;
63    os.flags(f);
64
65    return os;
66}
67
68void dumpBuffer(const char *pfx, const hub_app_name_t &appId, uint32_t evtId, const void *data, size_t len, int status)
69{
70    std::ostringstream os;
71    const uint8_t *p = static_cast<const uint8_t *>(data);
72    os << pfx << ": [ID=" << appId << "; SZ=" << std::dec << len;
73    if (evtId)
74        os << "; EVT=" << std::hex << evtId;
75    os << "]:" << std::hex;
76    for (size_t i = 0; i < len; ++i) {
77        os << " "  << std::setfill('0') << std::setw(2) << (unsigned int)p[i];
78    }
79    if (status) {
80        os << "; status=" << status << " [" << std::setfill('0') << std::setw(8) << status << "]";
81    }
82    ALOGI("%s", os.str().c_str());
83}
84
85static int rwrite(int fd, const void *buf, int len)
86{
87    int ret;
88
89    do {
90        ret = write(fd, buf, len);
91    } while (ret < 0 && errno == EINTR);
92
93    if (ret != len) {
94        return errno ? -errno : -EIO;
95    }
96
97    return 0;
98}
99
100static int rread(int fd, void *buf, int len)
101{
102    int ret;
103
104    do {
105        ret = read(fd, buf, len);
106    } while (ret < 0 && errno == EINTR);
107
108    return ret;
109}
110
111static bool init_inotify(pollfd *pfd) {
112    bool success = false;
113
114    mkdir(NANOHUB_LOCK_DIR, NANOHUB_LOCK_DIR_PERMS);
115    pfd->fd = inotify_init1(IN_NONBLOCK);
116    if (pfd->fd < 0) {
117        ALOGE("Couldn't initialize inotify: %s", strerror(errno));
118    } else if (inotify_add_watch(pfd->fd, NANOHUB_LOCK_DIR, IN_CREATE | IN_DELETE) < 0) {
119        ALOGE("Couldn't add inotify watch: %s", strerror(errno));
120        close(pfd->fd);
121    } else {
122        pfd->events = POLLIN;
123        success = true;
124    }
125
126    return success;
127}
128
129static void discard_inotify_evt(pollfd &pfd) {
130    if ((pfd.revents & POLLIN)) {
131        char buf[sizeof(inotify_event) + NAME_MAX + 1];
132        int ret = read(pfd.fd, buf, sizeof(buf));
133        ALOGD("Discarded %d bytes of inotify data", ret);
134    }
135}
136
137static void wait_on_dev_lock(pollfd &pfd) {
138    // While the lock file exists, poll on the inotify fd (with timeout)
139    discard_inotify_evt(pfd);
140    while (access(NANOHUB_LOCK_FILE, F_OK) == 0) {
141        ALOGW("Nanohub is locked; blocking read thread");
142        int ret = poll(&pfd, 1, 5000);
143        if (ret > 0) {
144            discard_inotify_evt(pfd);
145        }
146    }
147}
148
149NanoHub::NanoHub() {
150    reset();
151}
152
153NanoHub::~NanoHub() {
154    if (mMsgCbkFunc) {
155        ALOGD("Shutting down");
156        closeHub();
157    }
158}
159
160int NanoHub::doSendToDevice(const hub_app_name_t name, const void *data, uint32_t len, uint32_t messageType)
161{
162    if (len > MAX_RX_PACKET) {
163        return -EINVAL;
164    }
165
166    // transmit message to FW in CHRE format
167    nano_message_chre msg = {
168        .hdr = {
169            .eventId = APP_FROM_HOST_CHRE_EVENT_ID,
170            .appId = name.id,
171            .len = static_cast<uint8_t>(len),
172            .appEventId = messageType,
173        },
174    };
175
176    memcpy(&msg.data[0], data, len);
177
178    return rwrite(mFd, &msg, len + sizeof(msg.hdr));
179}
180
181void NanoHub::doSendToApp(HubMessage &&msg)
182{
183    std::unique_lock<std::mutex> lk(mAppTxLock);
184    mAppTxQueue.push_back((HubMessage &&)msg);
185    lk.unlock();
186    mAppTxCond.notify_all();
187}
188
189void* NanoHub::runAppTx()
190{
191    std::unique_lock<std::mutex> lk(mAppTxLock);
192    while(true) {
193        mAppTxCond.wait(lk, [this] { return !mAppTxQueue.empty() || mAppQuit; });
194        if (mAppQuit) {
195            break;
196        }
197        HubMessage &m = mAppTxQueue.front();
198        lk.unlock();
199        mMsgCbkFunc(0, &m, mMsgCbkData);
200        lk.lock();
201        mAppTxQueue.pop_front();
202    };
203    return NULL;
204}
205
206void* NanoHub::runDeviceRx()
207{
208    enum {
209        IDX_NANOHUB,
210        IDX_CLOSE_PIPE,
211        IDX_INOTIFY
212    };
213    pollfd myFds[3] = {
214        [IDX_NANOHUB] = { .fd = mFd, .events = POLLIN, },
215        [IDX_CLOSE_PIPE] = { .fd = mThreadClosingPipe[0], .events = POLLIN, },
216    };
217    pollfd &inotifyFd = myFds[IDX_INOTIFY];
218    bool hasInotify = false;
219    int numPollFds = 2;
220
221    if (init_inotify(&inotifyFd)) {
222        numPollFds++;
223        hasInotify = true;
224    }
225
226    setDebugFlags(property_get_int32("persist.nanohub.debug", 0));
227
228    while (1) {
229        int ret = poll(myFds, numPollFds, -1);
230        if (ret <= 0) {
231            ALOGD("poll returned with an error: %s", strerror(errno));
232            continue;
233        }
234
235        if (hasInotify) {
236            wait_on_dev_lock(inotifyFd);
237        }
238
239        if (myFds[IDX_NANOHUB].revents & POLLIN) { // we have data
240
241            nano_message msg;
242
243            ret = rread(mFd, &msg, sizeof(msg));
244            if (ret <= 0) {
245                ALOGE("read failed with %d", ret);
246                break;
247            }
248            if (ret < (int)sizeof(msg.hdr)) {
249                ALOGE("Only read %d bytes", ret);
250                break;
251            }
252
253            uint32_t len = msg.hdr.len;
254
255            if (len > sizeof(msg.data)) {
256                ALOGE("malformed packet with len %" PRIu32, len);
257                break;
258            }
259
260            // receive message from FW in legacy format
261            if (ret != (int)(sizeof(msg.hdr) + len)) {
262                ALOGE("Expected %zu bytes, read %d bytes", sizeof(msg.hdr) + len, ret);
263                break;
264            }
265
266            ret = SystemComm::handleRx(&msg);
267            if (ret < 0) {
268                ALOGE("SystemComm::handleRx() returned %d", ret);
269            } else if (ret) {
270                hub_app_name_t app_name = { .id = msg.hdr.appId };
271                if (messageTracingEnabled()) {
272                    dumpBuffer("DEV -> APP", app_name, msg.hdr.eventId, &msg.data[0], msg.hdr.len);
273                }
274                doSendToApp(HubMessage(&app_name, msg.hdr.eventId, &msg.data[0], msg.hdr.len));
275            }
276        }
277
278        if (myFds[IDX_CLOSE_PIPE].revents & POLLIN) { // we have been asked to die
279            ALOGD("thread exiting");
280            break;
281        }
282    }
283
284    close(mFd);
285    return NULL;
286}
287
288int NanoHub::openHub()
289{
290    int ret = 0;
291
292    mFd = open(get_devnode_path(), O_RDWR);
293    if (mFd < 0) {
294        ALOGE("cannot find hub devnode '%s'", get_devnode_path());
295        ret = -errno;
296        goto fail_open;
297    }
298
299    if (pipe(mThreadClosingPipe)) {
300        ALOGE("failed to create signal pipe");
301        ret = -errno;
302        goto fail_pipe;
303    }
304
305    mPollThread = std::thread([this] { runDeviceRx(); });
306    mAppThread = std::thread([this] { runAppTx(); });
307    return 0;
308
309fail_pipe:
310    close(mFd);
311
312fail_open:
313    return ret;
314}
315
316int NanoHub::closeHub(void)
317{
318    char zero = 0;
319
320    // stop mPollThread
321    while(write(mThreadClosingPipe[1], &zero, 1) != 1) {
322        continue;
323    }
324
325    // stop mAppThread
326    {
327        std::unique_lock<std::mutex> lk(mAppTxLock);
328        mAppQuit = true;
329        lk.unlock();
330        mAppTxCond.notify_all();
331    }
332
333    //wait
334    if (mPollThread.joinable()) {
335        mPollThread.join();
336    }
337
338    //wait
339    if (mAppThread.joinable()) {
340        mAppThread.join();
341    }
342    //cleanup
343    ::close(mThreadClosingPipe[0]);
344    ::close(mThreadClosingPipe[1]);
345    ::close(mFd);
346
347    reset();
348
349    return 0;
350}
351
352int NanoHub::doSubscribeMessages(uint32_t hub_id, context_hub_callback *cbk, void *cookie)
353{
354    if (hub_id) {
355        return -ENODEV;
356    }
357
358    std::lock_guard<std::mutex> _l(mLock);
359    int ret = 0;
360
361    if (!mMsgCbkFunc && !cbk) { //we're off and staying off - do nothing
362
363        ALOGD("staying off");
364    } else if (cbk && mMsgCbkFunc) { //new callback but staying on
365
366        ALOGD("staying on");
367    } else if (mMsgCbkFunc) {     //we were on but turning off
368
369        ALOGD("turning off");
370
371        ret = closeHub();
372    } else if (cbk) {             //we're turning on
373
374        ALOGD("turning on");
375        ret = openHub();
376    }
377
378    mMsgCbkFunc = cbk;
379    mMsgCbkData = cookie;
380
381    return ret;
382}
383
384int NanoHub::doSendToNanohub(uint32_t hub_id, const hub_message_t *msg)
385{
386    if (hub_id) {
387        return -ENODEV;
388    }
389
390    int ret = 0;
391    std::lock_guard<std::mutex> _l(mLock);
392
393    if (!mMsgCbkFunc) {
394        ALOGW("refusing to send a message when nobody around to get a reply!");
395        ret = -EIO;
396    } else {
397        if (!msg || !msg->message) {
398            ALOGW("not sending invalid message 1");
399            ret = -EINVAL;
400        } else if (get_hub_info()->os_app_name == msg->app_name) {
401            //messages to the "system" app are special - hal handles them
402            if (messageTracingEnabled()) {
403                dumpBuffer("APP -> HAL", msg->app_name, msg->message_type, msg->message, msg->message_len);
404            }
405            ret = SystemComm::handleTx(msg);
406        } else if (msg->message_len > MAX_RX_PACKET) {
407            ALOGW("not sending invalid message 2");
408            ret = -EINVAL;
409        } else {
410            if (messageTracingEnabled()) {
411                dumpBuffer("APP -> DEV", msg->app_name, msg->message_type, msg->message, msg->message_len);
412            }
413            ret = doSendToDevice(msg->app_name, msg->message, msg->message_len, msg->message_type);
414        }
415    }
416
417    return ret;
418}
419
420static int hal_get_hubs(context_hub_module_t*, const context_hub_t ** list)
421{
422    *list = get_hub_info();
423
424    return 1; /* we have one hub */
425}
426
427}; // namespace nanohub
428
429}; // namespace android
430
431context_hub_module_t HAL_MODULE_INFO_SYM = {
432    .common = {
433        .tag = HARDWARE_MODULE_TAG,
434        .module_api_version = CONTEXT_HUB_DEVICE_API_VERSION_1_0,
435        .hal_api_version = HARDWARE_HAL_API_VERSION,
436        .id = CONTEXT_HUB_MODULE_ID,
437        .name = "Nanohub HAL",
438        .author = "Google",
439    },
440
441    .get_hubs = android::nanohub::hal_get_hubs,
442    .subscribe_messages = android::nanohub::NanoHub::subscribeMessages,
443    .send_message = android::nanohub::NanoHub::sendToNanohub,
444};
445