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