/* * 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 <utils/Log.h> #include <cutils/properties.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, 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; 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 = poll(&pfd, 1, 5000); 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) { 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, }, }; 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::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 = poll(myFds, numPollFds, -1); if (ret <= 0) { ALOGD("poll returned with an error: %s", strerror(errno)); continue; } 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.hdr)) { ALOGE("Only read %d bytes", ret); break; } uint32_t len = msg.hdr.len; if (len > sizeof(msg.data)) { ALOGE("malformed packet with len %" PRIu32, len); break; } // receive message from FW in legacy format if (ret != (int)(sizeof(msg.hdr) + len)) { ALOGE("Expected %zu bytes, read %d bytes", sizeof(msg.hdr) + len, ret); break; } ret = SystemComm::handleRx(&msg); if (ret < 0) { ALOGE("SystemComm::handleRx() returned %d", ret); } else if (ret) { hub_app_name_t app_name = { .id = msg.hdr.appId }; if (messageTracingEnabled()) { dumpBuffer("DEV -> APP", app_name, msg.hdr.eventId, &msg.data[0], msg.hdr.len); } doSendToApp(HubMessage(&app_name, msg.hdr.eventId, &msg.data[0], msg.hdr.len)); } } 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, context_hub_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) { 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, msg->message, msg->message_len); } ret = SystemComm::handleTx(msg); } 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, msg->message, msg->message_len); } ret = doSendToDevice(msg->app_name, msg->message, msg->message_len, msg->message_type); } } return ret; } static int hal_get_hubs(context_hub_module_t*, const context_hub_t ** list) { *list = get_hub_info(); return 1; /* we have one hub */ } }; // namespace nanohub }; // namespace android context_hub_module_t HAL_MODULE_INFO_SYM = { .common = { .tag = HARDWARE_MODULE_TAG, .module_api_version = CONTEXT_HUB_DEVICE_API_VERSION_1_0, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = CONTEXT_HUB_MODULE_ID, .name = "Nanohub HAL", .author = "Google", }, .get_hubs = android::nanohub::hal_get_hubs, .subscribe_messages = android::nanohub::NanoHub::subscribeMessages, .send_message = android::nanohub::NanoHub::sendToNanohub, };