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