Home | History | Annotate | Download | only in contexthubhal
      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 
     46 namespace android {
     47 
     48 namespace nanohub {
     49 
     50 inline 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 
     68 void 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 
     85 static 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 
    100 static 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 
    111 static 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 
    129 static 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 
    137 static 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 
    149 NanoHub::NanoHub() {
    150     reset();
    151 }
    152 
    153 NanoHub::~NanoHub() {
    154     if (mMsgCbkFunc) {
    155         ALOGD("Shutting down");
    156         closeHub();
    157     }
    158 }
    159 
    160 int 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 
    181 void 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 
    189 void* 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 
    206 void* 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 
    288 int 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 
    309 fail_pipe:
    310     close(mFd);
    311 
    312 fail_open:
    313     return ret;
    314 }
    315 
    316 int 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 
    352 int 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 
    384 int 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 
    420 static 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 
    431 context_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