420 lines
13 KiB
C++
420 lines
13 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#include "androidcontexthub.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
#include "calibrationfile.h"
|
|
#include "log.h"
|
|
|
|
namespace android {
|
|
|
|
constexpr char kSensorDeviceFile[] = "/dev/nanohub";
|
|
constexpr char kCommsDeviceFile[] = "/dev/nanohub_comms";
|
|
constexpr char kLockDirectory[] = "/data/vendor/sensor/nanohub_lock";
|
|
constexpr char kLockFile[] = "/data/vendor/sensor/nanohub_lock/lock";
|
|
|
|
constexpr mode_t kLockDirPermissions = (S_IRUSR | S_IWUSR | S_IXUSR);
|
|
|
|
constexpr auto kLockDelay = std::chrono::milliseconds(100);
|
|
|
|
constexpr int kDeviceFileCount = 2;
|
|
constexpr int kPollNoTimeout = -1;
|
|
|
|
static const std::vector<std::tuple<const char *, SensorType>> kCalibrationKeys = {
|
|
std::make_tuple("accel", SensorType::Accel),
|
|
std::make_tuple("gyro", SensorType::Gyro),
|
|
std::make_tuple("mag", SensorType::Magnetometer),
|
|
std::make_tuple("proximity", SensorType::Proximity),
|
|
std::make_tuple("barometer", SensorType::Barometer),
|
|
std::make_tuple("light", SensorType::AmbientLightSensor),
|
|
};
|
|
|
|
static void AppendBytes(const void *data, size_t length, std::vector<uint8_t>& buffer) {
|
|
const uint8_t *bytes = (const uint8_t *) data;
|
|
for (size_t i = 0; i < length; i++) {
|
|
buffer.push_back(bytes[i]);
|
|
}
|
|
}
|
|
|
|
static bool CopyInt32Array(const char *key,
|
|
sp<JSONObject> json, std::vector<uint8_t>& bytes) {
|
|
sp<JSONArray> array;
|
|
if (json->getArray(key, &array)) {
|
|
for (size_t i = 0; i < array->size(); i++) {
|
|
int32_t val = 0;
|
|
array->getInt32(i, &val);
|
|
AppendBytes(&val, sizeof(uint32_t), bytes);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool CopyFloatArray(const char *key,
|
|
sp<JSONObject> json, std::vector<uint8_t>& bytes) {
|
|
sp<JSONArray> array;
|
|
if (json->getArray(key, &array)) {
|
|
for (size_t i = 0; i < array->size(); i++) {
|
|
float val = 0;
|
|
array->getFloat(i, &val);
|
|
AppendBytes(&val, sizeof(float), bytes);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool GetCalibrationBytes(const char *key, SensorType sensor_type,
|
|
std::vector<uint8_t>& bytes) {
|
|
bool success = true;
|
|
std::shared_ptr<CalibrationFile> cal_file = CalibrationFile::Instance();
|
|
if (!cal_file) {
|
|
return false;
|
|
}
|
|
auto json = cal_file->GetJSONObject();
|
|
|
|
switch (sensor_type) {
|
|
case SensorType::Accel:
|
|
case SensorType::Gyro:
|
|
success = CopyInt32Array(key, json, bytes);
|
|
break;
|
|
|
|
case SensorType::Magnetometer:
|
|
success = CopyFloatArray(key, json, bytes);
|
|
break;
|
|
|
|
case SensorType::AmbientLightSensor:
|
|
case SensorType::Barometer: {
|
|
float value = 0;
|
|
success = json->getFloat(key, &value);
|
|
if (success) {
|
|
AppendBytes(&value, sizeof(float), bytes);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SensorType::Proximity: {
|
|
// Proximity might be an int32 array with 4 values (CRGB) or a single
|
|
// int32 value - try both
|
|
success = CopyInt32Array(key, json, bytes);
|
|
if (!success) {
|
|
int32_t value = 0;
|
|
success = json->getInt32(key, &value);
|
|
if (success) {
|
|
AppendBytes(&value, sizeof(int32_t), bytes);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// If this log message gets printed, code needs to be added in this
|
|
// switch statement
|
|
LOGE("Missing sensor type to calibration data mapping sensor %d",
|
|
static_cast<int>(sensor_type));
|
|
success = false;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
AndroidContextHub::~AndroidContextHub() {
|
|
if (unlink(kLockFile) < 0) {
|
|
LOGE("Couldn't remove lock file: %s", strerror(errno));
|
|
}
|
|
if (sensor_fd_ >= 0) {
|
|
DisableActiveSensors();
|
|
(void) close(sensor_fd_);
|
|
}
|
|
if (comms_fd_ >= 0) {
|
|
(void) close(comms_fd_);
|
|
}
|
|
}
|
|
|
|
void AndroidContextHub::TerminateHandler() {
|
|
(void) unlink(kLockFile);
|
|
}
|
|
|
|
bool AndroidContextHub::Initialize() {
|
|
// Acquire a lock on nanohub, so the HAL read threads won't take our events.
|
|
// We need to delay after creating the file to have good confidence that
|
|
// the HALs noticed the lock file creation.
|
|
if (access(kLockDirectory, F_OK) < 0) {
|
|
if (mkdir(kLockDirectory, kLockDirPermissions) < 0 && errno != EEXIST) {
|
|
LOGE("Couldn't create lock directory: %s", strerror(errno));
|
|
}
|
|
}
|
|
int lock_fd = open(kLockFile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
|
if (lock_fd < 0) {
|
|
LOGE("Couldn't create lock file: %s", strerror(errno));
|
|
if (errno != EEXIST) {
|
|
return false;
|
|
}
|
|
} else {
|
|
close(lock_fd);
|
|
std::this_thread::sleep_for(kLockDelay);
|
|
LOGD("Lock sleep complete");
|
|
}
|
|
|
|
// Sensor device file is used for sensor requests, e.g. configure, etc., and
|
|
// returns sensor events
|
|
sensor_fd_ = open(kSensorDeviceFile, O_RDWR);
|
|
if (sensor_fd_ < 0) {
|
|
LOGE("Couldn't open device file: %s", strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
// The comms device file is used for more generic communication with
|
|
// nanoapps. Calibration results are returned through this channel.
|
|
comms_fd_ = open(kCommsDeviceFile, O_RDONLY);
|
|
if (comms_fd_ < 0) {
|
|
// TODO(bduddie): Currently informational only, as the kernel change
|
|
// that adds this device file is not available/propagated yet.
|
|
// Eventually this should be an error.
|
|
LOGI("Couldn't open comms device file: %s", strerror(errno));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void AndroidContextHub::SetLoggingEnabled(bool logging_enabled) {
|
|
if (logging_enabled) {
|
|
LOGE("Logging is not supported on this platform");
|
|
}
|
|
}
|
|
|
|
ContextHub::TransportResult AndroidContextHub::WriteEvent(
|
|
const std::vector<uint8_t>& message) {
|
|
ContextHub::TransportResult result;
|
|
|
|
LOGD("Writing %zu bytes", message.size());
|
|
LOGD_BUF(message.data(), message.size());
|
|
int ret = write(sensor_fd_, message.data(), message.size());
|
|
if (ret == -1) {
|
|
LOGE("Couldn't write %zu bytes to device file: %s", message.size(),
|
|
strerror(errno));
|
|
result = TransportResult::GeneralFailure;
|
|
} else if (ret != (int) message.size()) {
|
|
LOGW("Write returned %d, expected %zu", ret, message.size());
|
|
result = TransportResult::GeneralFailure;
|
|
} else {
|
|
LOGD("Successfully sent event");
|
|
result = TransportResult::Success;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
ContextHub::TransportResult AndroidContextHub::ReadEvent(
|
|
std::vector<uint8_t>& message, int timeout_ms) {
|
|
ContextHub::TransportResult result = TransportResult::GeneralFailure;
|
|
|
|
struct pollfd pollfds[kDeviceFileCount];
|
|
int fd_count = ResetPollFds(pollfds, kDeviceFileCount);
|
|
|
|
int timeout = timeout_ms > 0 ? timeout_ms : kPollNoTimeout;
|
|
int ret = poll(pollfds, fd_count, timeout);
|
|
if (ret < 0) {
|
|
LOGE("Polling failed: %s", strerror(errno));
|
|
if (errno == EINTR) {
|
|
result = TransportResult::Canceled;
|
|
}
|
|
} else if (ret == 0) {
|
|
LOGD("Poll timed out");
|
|
result = TransportResult::Timeout;
|
|
} else {
|
|
int read_fd = -1;
|
|
for (int i = 0; i < kDeviceFileCount; i++) {
|
|
if (pollfds[i].revents & POLLIN) {
|
|
read_fd = pollfds[i].fd;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (read_fd == sensor_fd_) {
|
|
LOGD("Data ready on sensors device file");
|
|
} else if (read_fd == comms_fd_) {
|
|
LOGD("Data ready on comms device file");
|
|
}
|
|
|
|
if (read_fd >= 0) {
|
|
result = ReadEventFromFd(read_fd, message);
|
|
} else {
|
|
LOGE("Poll returned but none of expected files are ready");
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool AndroidContextHub::FlashSensorHub(const std::vector<uint8_t>& bytes) {
|
|
(void)bytes;
|
|
LOGE("Flashing is not supported on this platform");
|
|
return false;
|
|
}
|
|
|
|
bool AndroidContextHub::LoadCalibration() {
|
|
std::vector<uint8_t> cal_data;
|
|
bool success = true;
|
|
|
|
for (size_t i = 0; success && i < kCalibrationKeys.size(); i++) {
|
|
std::string key;
|
|
SensorType sensor_type;
|
|
|
|
std::tie(key, sensor_type) = kCalibrationKeys[i];
|
|
if (GetCalibrationBytes(key.c_str(), sensor_type, cal_data)) {
|
|
success = SendCalibrationData(sensor_type, cal_data);
|
|
}
|
|
|
|
cal_data.clear();
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool AndroidContextHub::SetCalibration(SensorType sensor_type, int32_t data) {
|
|
LOGI("Setting calibration for sensor %d (%s) to %d",
|
|
static_cast<int>(sensor_type),
|
|
ContextHub::SensorTypeToAbbrevName(sensor_type).c_str(), data);
|
|
auto cal_file = CalibrationFile::Instance();
|
|
const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
|
|
if (cal_file && key) {
|
|
return cal_file->SetSingleAxis(key, data);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AndroidContextHub::SetCalibration(SensorType sensor_type, float data) {
|
|
LOGI("Setting calibration for sensor %d (%s) to %f",
|
|
static_cast<int>(sensor_type),
|
|
ContextHub::SensorTypeToAbbrevName(sensor_type).c_str(), data);
|
|
auto cal_file = CalibrationFile::Instance();
|
|
const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
|
|
if (cal_file && key) {
|
|
return cal_file->SetSingleAxis(key, data);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AndroidContextHub::SetCalibration(SensorType sensor_type, int32_t x,
|
|
int32_t y, int32_t z) {
|
|
LOGI("Setting calibration for %d to %d %d %d", static_cast<int>(sensor_type),
|
|
x, y, z);
|
|
auto cal_file = CalibrationFile::Instance();
|
|
const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
|
|
if (cal_file && key) {
|
|
return cal_file->SetTripleAxis(key, x, y, z);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AndroidContextHub::SetCalibration(SensorType sensor_type, int32_t x,
|
|
int32_t y, int32_t z, int32_t w) {
|
|
LOGI("Setting calibration for %d to %d %d %d %d", static_cast<int>(sensor_type),
|
|
x, y, z, w);
|
|
auto cal_file = CalibrationFile::Instance();
|
|
const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
|
|
if (cal_file && key) {
|
|
return cal_file->SetFourAxis(key, x, y, z, w);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AndroidContextHub::SaveCalibration() {
|
|
LOGI("Saving calibration data");
|
|
auto cal_file = CalibrationFile::Instance();
|
|
if (cal_file) {
|
|
return cal_file->Save();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ContextHub::TransportResult AndroidContextHub::ReadEventFromFd(
|
|
int fd, std::vector<uint8_t>& message) {
|
|
ContextHub::TransportResult result = TransportResult::GeneralFailure;
|
|
|
|
// Set the size to the maximum, so when we resize later, it's always a
|
|
// shrink (otherwise it will end up clearing the bytes)
|
|
message.resize(message.capacity());
|
|
|
|
LOGD("Calling into read()");
|
|
int ret = read(fd, message.data(), message.capacity());
|
|
if (ret < 0) {
|
|
LOGE("Couldn't read from device file: %s", strerror(errno));
|
|
if (errno == EINTR) {
|
|
result = TransportResult::Canceled;
|
|
}
|
|
} else if (ret == 0) {
|
|
// We might need to handle this specially, if the driver implements this
|
|
// to mean something specific
|
|
LOGE("Read unexpectedly returned 0 bytes");
|
|
} else {
|
|
message.resize(ret);
|
|
LOGD_VEC(message);
|
|
result = TransportResult::Success;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int AndroidContextHub::ResetPollFds(struct pollfd *pfds, size_t count) {
|
|
memset(pfds, 0, sizeof(struct pollfd) * count);
|
|
pfds[0].fd = sensor_fd_;
|
|
pfds[0].events = POLLIN;
|
|
|
|
int nfds = 1;
|
|
if (count > 1 && comms_fd_ >= 0) {
|
|
pfds[1].fd = comms_fd_;
|
|
pfds[1].events = POLLIN;
|
|
nfds++;
|
|
}
|
|
return nfds;
|
|
}
|
|
|
|
const char *AndroidContextHub::SensorTypeToCalibrationKey(SensorType sensor_type) {
|
|
for (size_t i = 0; i < kCalibrationKeys.size(); i++) {
|
|
const char *key;
|
|
SensorType sensor_type_for_key;
|
|
|
|
std::tie(key, sensor_type_for_key) = kCalibrationKeys[i];
|
|
if (sensor_type == sensor_type_for_key) {
|
|
return key;
|
|
}
|
|
}
|
|
|
|
LOGE("No calibration key mapping for sensor type %d",
|
|
static_cast<int>(sensor_type));
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace android
|