| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "NanohubHAL" |
| |
| #include <fcntl.h> |
| #include <poll.h> |
| #include <unistd.h> |
| #include <sys/inotify.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| #include <hardware/context_hub.h> |
| #include <hardware/hardware.h> |
| |
| #include <cutils/properties.h> |
| #include <log/log.h> |
| |
| #include <nanohub/nanohub.h> |
| |
| #include <cinttypes> |
| #include <iomanip> |
| #include <sstream> |
| |
| #include "nanohub_perdevice.h" |
| #include "system_comms.h" |
| #include "nanohubhal.h" |
| |
| #define NANOHUB_LOCK_DIR "/data/vendor/sensor/nanohub_lock" |
| #define NANOHUB_LOCK_FILE NANOHUB_LOCK_DIR "/lock" |
| #define NANOHUB_LOCK_DIR_PERMS (S_IRUSR | S_IWUSR | S_IXUSR) |
| |
| namespace android { |
| |
| namespace nanohub { |
| |
| inline std::ostream &operator << (std::ostream &os, const hub_app_name_t &appId) |
| { |
| char vendor[6]; |
| __be64 beAppId = htobe64(appId.id); |
| uint32_t seqId = appId.id & NANOAPP_VENDOR_ALL_APPS; |
| |
| std::ios::fmtflags f(os.flags()); |
| memcpy(vendor, (void*)&beAppId, sizeof(vendor) - 1); |
| vendor[sizeof(vendor) - 1] = 0; |
| if (strlen(vendor) == 5) |
| os << vendor << ", " << std::hex << std::setw(6) << seqId; |
| else |
| os << "#" << std::hex << appId.id; |
| os.flags(f); |
| |
| return os; |
| } |
| |
| void dumpBuffer(const char *pfx, const hub_app_name_t &appId, uint32_t evtId, uint16_t endpoint, const void *data, size_t len, int status) |
| { |
| std::ostringstream os; |
| const uint8_t *p = static_cast<const uint8_t *>(data); |
| os << pfx << ": [ID=" << appId << "; SZ=" << std::dec << len; |
| if (evtId) |
| os << "; EVT=" << std::hex << evtId; |
| if (endpoint) |
| os << "; EPT=" << std::hex << endpoint; |
| os << "]:" << std::hex; |
| for (size_t i = 0; i < len; ++i) { |
| os << " " << std::setfill('0') << std::setw(2) << (unsigned int)p[i]; |
| } |
| if (status) { |
| os << "; status=" << status << " [" << std::setfill('0') << std::setw(8) << status << "]"; |
| } |
| ALOGI("%s", os.str().c_str()); |
| } |
| |
| static int rwrite(int fd, const void *buf, int len) |
| { |
| int ret; |
| |
| do { |
| ret = write(fd, buf, len); |
| } while (ret < 0 && errno == EINTR); |
| |
| if (ret != len) { |
| return errno ? -errno : -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int rread(int fd, void *buf, int len) |
| { |
| int ret; |
| |
| do { |
| ret = read(fd, buf, len); |
| } while (ret < 0 && errno == EINTR); |
| |
| return ret; |
| } |
| |
| static bool init_inotify(pollfd *pfd) { |
| bool success = false; |
| |
| mkdir(NANOHUB_LOCK_DIR, NANOHUB_LOCK_DIR_PERMS); |
| pfd->fd = inotify_init1(IN_NONBLOCK); |
| if (pfd->fd < 0) { |
| ALOGE("Couldn't initialize inotify: %s", strerror(errno)); |
| } else if (inotify_add_watch(pfd->fd, NANOHUB_LOCK_DIR, IN_CREATE | IN_DELETE) < 0) { |
| ALOGE("Couldn't add inotify watch: %s", strerror(errno)); |
| close(pfd->fd); |
| } else { |
| pfd->events = POLLIN; |
| success = true; |
| } |
| |
| return success; |
| } |
| |
| static void discard_inotify_evt(pollfd &pfd) { |
| if ((pfd.revents & POLLIN)) { |
| char buf[sizeof(inotify_event) + NAME_MAX + 1]; |
| int ret = read(pfd.fd, buf, sizeof(buf)); |
| ALOGD("Discarded %d bytes of inotify data", ret); |
| } |
| } |
| |
| static void wait_on_dev_lock(pollfd &pfd) { |
| // While the lock file exists, poll on the inotify fd (with timeout) |
| discard_inotify_evt(pfd); |
| while (access(NANOHUB_LOCK_FILE, F_OK) == 0) { |
| ALOGW("Nanohub is locked; blocking read thread"); |
| int ret = TEMP_FAILURE_RETRY(poll(&pfd, 1, 5000)); |
| if (ret < 0) { |
| ALOGE("poll returned with an error: %s", strerror(errno)); |
| break; |
| } else if (ret > 0) { |
| discard_inotify_evt(pfd); |
| } |
| } |
| } |
| |
| NanoHub::NanoHub() { |
| reset(); |
| } |
| |
| NanoHub::~NanoHub() { |
| if (mMsgCbkFunc) { |
| ALOGD("Shutting down"); |
| closeHub(); |
| } |
| } |
| |
| int NanoHub::doSendToDevice(const hub_app_name_t name, const void *data, uint32_t len, uint32_t messageType, uint16_t endpoint) |
| { |
| if (len > MAX_RX_PACKET) { |
| return -EINVAL; |
| } |
| |
| // transmit message to FW in CHRE format |
| nano_message_chre msg = { |
| .hdr = { |
| .eventId = APP_FROM_HOST_CHRE_EVENT_ID, |
| .appId = name.id, |
| .len = static_cast<uint8_t>(len), |
| .appEventId = messageType, |
| .endpoint = endpoint, |
| }, |
| }; |
| |
| memcpy(&msg.data[0], data, len); |
| |
| return rwrite(mFd, &msg, len + sizeof(msg.hdr)); |
| } |
| |
| void NanoHub::doSendToApp(HubMessage &&msg) |
| { |
| std::unique_lock<std::mutex> lk(mAppTxLock); |
| mAppTxQueue.push_back((HubMessage &&)msg); |
| lk.unlock(); |
| mAppTxCond.notify_all(); |
| } |
| |
| void NanoHub::doDumpAppInfo(std::string &result) |
| { |
| SystemComm::dumpAppInfo(result); |
| } |
| |
| void* NanoHub::runAppTx() |
| { |
| std::unique_lock<std::mutex> lk(mAppTxLock); |
| while(true) { |
| mAppTxCond.wait(lk, [this] { return !mAppTxQueue.empty() || mAppQuit; }); |
| if (mAppQuit) { |
| break; |
| } |
| HubMessage &m = mAppTxQueue.front(); |
| lk.unlock(); |
| mMsgCbkFunc(0, m, mMsgCbkData); |
| lk.lock(); |
| mAppTxQueue.pop_front(); |
| }; |
| return NULL; |
| } |
| |
| void* NanoHub::runDeviceRx() |
| { |
| enum { |
| IDX_NANOHUB, |
| IDX_CLOSE_PIPE, |
| IDX_INOTIFY |
| }; |
| pollfd myFds[3] = { |
| [IDX_NANOHUB] = { .fd = mFd, .events = POLLIN, }, |
| [IDX_CLOSE_PIPE] = { .fd = mThreadClosingPipe[0], .events = POLLIN, }, |
| }; |
| pollfd &inotifyFd = myFds[IDX_INOTIFY]; |
| bool hasInotify = false; |
| int numPollFds = 2; |
| |
| if (init_inotify(&inotifyFd)) { |
| numPollFds++; |
| hasInotify = true; |
| } |
| |
| setDebugFlags(property_get_int32("persist.nanohub.debug", 0)); |
| |
| while (1) { |
| int ret = TEMP_FAILURE_RETRY(poll(myFds, numPollFds, -1)); |
| if (ret == 0) |
| continue; |
| else if (ret < 0) { |
| ALOGE("poll returned with an error: %s", strerror(errno)); |
| break; |
| } |
| |
| if (hasInotify) { |
| wait_on_dev_lock(inotifyFd); |
| } |
| |
| if (myFds[IDX_NANOHUB].revents & POLLIN) { // we have data |
| |
| nano_message msg; |
| |
| ret = rread(mFd, &msg, sizeof(msg)); |
| if (ret <= 0) { |
| ALOGE("read failed with %d", ret); |
| break; |
| } |
| if (ret < (int)sizeof(msg.raw.hdr)) { |
| ALOGE("Only read %d bytes", ret); |
| break; |
| } |
| |
| uint32_t len = msg.raw.hdr.len; |
| |
| if (len > MAX_RX_PACKET) { |
| ALOGE("malformed packet with len %" PRIu32, len); |
| break; |
| } |
| |
| // receive message from FW in legacy format |
| if (ret == (int)(sizeof(msg.raw.hdr) + len)) { |
| ret = SystemComm::handleRx(&msg.raw); |
| if (ret > 0) { |
| hub_app_name_t app_name = { .id = msg.raw.hdr.appId }; |
| if (messageTracingEnabled()) { |
| dumpBuffer("(RAW) DEV -> APP", app_name, msg.raw.hdr.eventId, 0, &msg.raw.data[0], len); |
| } |
| doSendToApp(HubMessage(&app_name, msg.raw.hdr.eventId, ENDPOINT_BROADCAST, &msg.raw.data[0], len)); |
| } |
| // receive message from FW in chre format |
| } else if (ret == (int)(sizeof(msg.chre.hdr) + len)) { |
| ret = SystemComm::handleRx(&msg.chre); |
| if (ret > 0) { |
| hub_app_name_t app_name = { .id = msg.chre.hdr.appId }; |
| if (messageTracingEnabled()) { |
| dumpBuffer("(CHRE) DEV -> APP", app_name, msg.chre.hdr.appEventId, msg.chre.hdr.endpoint, &msg.chre.data[0], len); |
| } |
| doSendToApp(HubMessage(&app_name, msg.chre.hdr.appEventId, msg.chre.hdr.endpoint, &msg.chre.data[0], len)); |
| } |
| } else { |
| ALOGE("Expected (%zu|%zu) bytes, read %d bytes", sizeof(msg.raw.hdr) + len, sizeof(msg.chre.hdr) + len, ret); |
| break; |
| } |
| |
| if (ret < 0) |
| ALOGE("SystemComm::handleRx() returned %d", ret); |
| } |
| |
| if (myFds[IDX_CLOSE_PIPE].revents & POLLIN) { // we have been asked to die |
| ALOGD("thread exiting"); |
| break; |
| } |
| } |
| |
| close(mFd); |
| return NULL; |
| } |
| |
| int NanoHub::openHub() |
| { |
| int ret = 0; |
| |
| mFd = open(get_devnode_path(), O_RDWR); |
| if (mFd < 0) { |
| ALOGE("cannot find hub devnode '%s'", get_devnode_path()); |
| ret = -errno; |
| goto fail_open; |
| } |
| |
| if (pipe(mThreadClosingPipe)) { |
| ALOGE("failed to create signal pipe"); |
| ret = -errno; |
| goto fail_pipe; |
| } |
| |
| mPollThread = std::thread([this] { runDeviceRx(); }); |
| mAppThread = std::thread([this] { runAppTx(); }); |
| return 0; |
| |
| fail_pipe: |
| close(mFd); |
| |
| fail_open: |
| return ret; |
| } |
| |
| int NanoHub::closeHub(void) |
| { |
| char zero = 0; |
| |
| // stop mPollThread |
| while(write(mThreadClosingPipe[1], &zero, 1) != 1) { |
| continue; |
| } |
| |
| // stop mAppThread |
| { |
| std::unique_lock<std::mutex> lk(mAppTxLock); |
| mAppQuit = true; |
| lk.unlock(); |
| mAppTxCond.notify_all(); |
| } |
| |
| //wait |
| if (mPollThread.joinable()) { |
| mPollThread.join(); |
| } |
| |
| //wait |
| if (mAppThread.joinable()) { |
| mAppThread.join(); |
| } |
| //cleanup |
| ::close(mThreadClosingPipe[0]); |
| ::close(mThreadClosingPipe[1]); |
| ::close(mFd); |
| |
| reset(); |
| |
| return 0; |
| } |
| |
| int NanoHub::doSubscribeMessages(uint32_t hub_id, Contexthub_callback *cbk, void *cookie) |
| { |
| if (hub_id) { |
| return -ENODEV; |
| } |
| |
| std::lock_guard<std::mutex> _l(mLock); |
| int ret = 0; |
| |
| if (!mMsgCbkFunc && !cbk) { //we're off and staying off - do nothing |
| |
| ALOGD("staying off"); |
| } else if (cbk && mMsgCbkFunc) { //new callback but staying on |
| |
| ALOGD("staying on"); |
| } else if (mMsgCbkFunc) { //we were on but turning off |
| |
| ALOGD("turning off"); |
| |
| ret = closeHub(); |
| } else if (cbk) { //we're turning on |
| |
| ALOGD("turning on"); |
| ret = openHub(); |
| } |
| |
| mMsgCbkFunc = cbk; |
| mMsgCbkData = cookie; |
| |
| return ret; |
| } |
| |
| int NanoHub::doSendToNanohub(uint32_t hub_id, const hub_message_t *msg, uint32_t transaction_id, uint16_t endpoint) |
| { |
| if (hub_id) { |
| return -ENODEV; |
| } |
| |
| int ret = 0; |
| std::lock_guard<std::mutex> _l(mLock); |
| |
| if (!mMsgCbkFunc) { |
| ALOGW("refusing to send a message when nobody around to get a reply!"); |
| ret = -EIO; |
| } else { |
| if (!msg || !msg->message) { |
| ALOGW("not sending invalid message 1"); |
| ret = -EINVAL; |
| } else if (get_hub_info()->os_app_name == msg->app_name) { |
| //messages to the "system" app are special - hal handles them |
| if (messageTracingEnabled()) { |
| dumpBuffer("APP -> HAL", msg->app_name, msg->message_type, 0, msg->message, msg->message_len); |
| } |
| ret = SystemComm::handleTx(msg, transaction_id); |
| } else if (msg->message_len > MAX_RX_PACKET) { |
| ALOGW("not sending invalid message 2"); |
| ret = -EINVAL; |
| } else { |
| if (messageTracingEnabled()) { |
| dumpBuffer("APP -> DEV", msg->app_name, msg->message_type, endpoint, msg->message, msg->message_len); |
| } |
| ret = doSendToDevice(msg->app_name, msg->message, msg->message_len, msg->message_type, endpoint); |
| } |
| } |
| |
| return ret; |
| } |
| |
| }; // namespace nanohub |
| |
| }; // namespace android |