You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
447 lines
12 KiB
447 lines
12 KiB
/*
|
|
* 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
|