Home | History | Annotate | Download | only in nanotool
      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 #include "androidcontexthub.h"
     18 
     19 #include <errno.h>
     20 #include <fcntl.h>
     21 #include <poll.h>
     22 #include <time.h>
     23 #include <unistd.h>
     24 #include <sys/stat.h>
     25 
     26 #include <chrono>
     27 #include <cstdint>
     28 #include <cstdio>
     29 #include <cstring>
     30 #include <thread>
     31 #include <vector>
     32 
     33 #include "calibrationfile.h"
     34 #include "log.h"
     35 
     36 namespace android {
     37 
     38 constexpr char kSensorDeviceFile[] = "/dev/nanohub";
     39 constexpr char kCommsDeviceFile[] = "/dev/nanohub_comms";
     40 constexpr char kLockDirectory[] = "/data/system/nanohub_lock";
     41 constexpr char kLockFile[] = "/data/system/nanohub_lock/lock";
     42 
     43 constexpr mode_t kLockDirPermissions = (S_IRUSR | S_IWUSR | S_IXUSR);
     44 
     45 constexpr auto kLockDelay = std::chrono::milliseconds(100);
     46 
     47 constexpr int kDeviceFileCount = 2;
     48 constexpr int kPollNoTimeout = -1;
     49 
     50 static const std::vector<std::tuple<const char *, SensorType>> kCalibrationKeys = {
     51     std::make_tuple("accel",     SensorType::Accel),
     52     std::make_tuple("gyro",      SensorType::Gyro),
     53     std::make_tuple("proximity", SensorType::Proximity),
     54     std::make_tuple("barometer", SensorType::Barometer),
     55     std::make_tuple("light",     SensorType::AmbientLightSensor),
     56 };
     57 
     58 static void AppendBytes(const void *data, size_t length, std::vector<uint8_t>& buffer) {
     59     const uint8_t *bytes = (const uint8_t *) data;
     60     for (size_t i = 0; i < length; i++) {
     61         buffer.push_back(bytes[i]);
     62     }
     63 }
     64 
     65 static bool CopyInt32Array(const char *key,
     66         sp<JSONObject> json, std::vector<uint8_t>& bytes) {
     67     sp<JSONArray> array;
     68     if (json->getArray(key, &array)) {
     69         for (size_t i = 0; i < array->size(); i++) {
     70             int32_t val = 0;
     71             array->getInt32(i, &val);
     72             AppendBytes(&val, sizeof(uint32_t), bytes);
     73         }
     74 
     75         return true;
     76     }
     77     return false;
     78 }
     79 
     80 static bool GetCalibrationBytes(const char *key, SensorType sensor_type,
     81         std::vector<uint8_t>& bytes) {
     82     bool success = true;
     83     std::shared_ptr<CalibrationFile> cal_file = CalibrationFile::Instance();
     84     if (!cal_file) {
     85         return false;
     86     }
     87     auto json = cal_file->GetJSONObject();
     88 
     89     switch (sensor_type) {
     90       case SensorType::Accel:
     91       case SensorType::Gyro:
     92         success = CopyInt32Array(key, json, bytes);
     93         break;
     94 
     95       case SensorType::AmbientLightSensor:
     96       case SensorType::Barometer: {
     97         float value = 0;
     98         success = json->getFloat(key, &value);
     99         if (success) {
    100             AppendBytes(&value, sizeof(float), bytes);
    101         }
    102         break;
    103       }
    104 
    105       case SensorType::Proximity: {
    106         // Proximity might be an int32 array with 4 values (CRGB) or a single
    107         // int32 value - try both
    108         success = CopyInt32Array(key, json, bytes);
    109         if (!success) {
    110             int32_t value = 0;
    111             success = json->getInt32(key, &value);
    112             if (success) {
    113                 AppendBytes(&value, sizeof(int32_t), bytes);
    114             }
    115         }
    116         break;
    117       }
    118 
    119       default:
    120         // If this log message gets printed, code needs to be added in this
    121         // switch statement
    122         LOGE("Missing sensor type to calibration data mapping sensor %d",
    123              static_cast<int>(sensor_type));
    124         success = false;
    125     }
    126 
    127     return success;
    128 }
    129 
    130 AndroidContextHub::~AndroidContextHub() {
    131     if (unlink(kLockFile) < 0) {
    132         LOGE("Couldn't remove lock file: %s", strerror(errno));
    133     }
    134     if (sensor_fd_ >= 0) {
    135         DisableActiveSensors();
    136         (void) close(sensor_fd_);
    137     }
    138     if (comms_fd_ >= 0) {
    139         (void) close(comms_fd_);
    140     }
    141 }
    142 
    143 void AndroidContextHub::TerminateHandler() {
    144     (void) unlink(kLockFile);
    145 }
    146 
    147 bool AndroidContextHub::Initialize() {
    148     // Acquire a lock on nanohub, so the HAL read threads won't take our events.
    149     // We need to delay after creating the file to have good confidence that
    150     // the HALs noticed the lock file creation.
    151     if (access(kLockDirectory, F_OK) < 0) {
    152         if (mkdir(kLockDirectory, kLockDirPermissions) < 0 && errno != EEXIST) {
    153             LOGE("Couldn't create lock directory: %s", strerror(errno));
    154         }
    155     }
    156     int lock_fd = open(kLockFile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
    157     if (lock_fd < 0) {
    158         LOGE("Couldn't create lock file: %s", strerror(errno));
    159         if (errno != EEXIST) {
    160             return false;
    161         }
    162     } else {
    163         close(lock_fd);
    164         std::this_thread::sleep_for(kLockDelay);
    165         LOGD("Lock sleep complete");
    166     }
    167 
    168     // Sensor device file is used for sensor requests, e.g. configure, etc., and
    169     // returns sensor events
    170     sensor_fd_ = open(kSensorDeviceFile, O_RDWR);
    171     if (sensor_fd_ < 0) {
    172         LOGE("Couldn't open device file: %s", strerror(errno));
    173         return false;
    174     }
    175 
    176     // The comms device file is used for more generic communication with
    177     // nanoapps. Calibration results are returned through this channel.
    178     comms_fd_ = open(kCommsDeviceFile, O_RDONLY);
    179     if (comms_fd_ < 0) {
    180         // TODO(bduddie): Currently informational only, as the kernel change
    181         // that adds this device file is not available/propagated yet.
    182         // Eventually this should be an error.
    183         LOGI("Couldn't open comms device file: %s", strerror(errno));
    184     }
    185 
    186     return true;
    187 }
    188 
    189 void AndroidContextHub::SetLoggingEnabled(bool logging_enabled) {
    190     if (logging_enabled) {
    191         LOGE("Logging is not supported on this platform");
    192     }
    193 }
    194 
    195 ContextHub::TransportResult AndroidContextHub::WriteEvent(
    196         const std::vector<uint8_t>& message) {
    197     ContextHub::TransportResult result;
    198 
    199     LOGD("Writing %zu bytes", message.size());
    200     LOGD_BUF(message.data(), message.size());
    201     int ret = write(sensor_fd_, message.data(), message.size());
    202     if (ret == -1) {
    203         LOGE("Couldn't write %zu bytes to device file: %s", message.size(),
    204              strerror(errno));
    205         result = TransportResult::GeneralFailure;
    206     } else if (ret != (int) message.size()) {
    207         LOGW("Write returned %d, expected %zu", ret, message.size());
    208         result = TransportResult::GeneralFailure;
    209     } else {
    210         LOGD("Successfully sent event");
    211         result = TransportResult::Success;
    212     }
    213 
    214     return result;
    215 }
    216 
    217 ContextHub::TransportResult AndroidContextHub::ReadEvent(
    218         std::vector<uint8_t>& message, int timeout_ms) {
    219     ContextHub::TransportResult result = TransportResult::GeneralFailure;
    220 
    221     struct pollfd pollfds[kDeviceFileCount];
    222     int fd_count = ResetPollFds(pollfds, kDeviceFileCount);
    223 
    224     int timeout = timeout_ms > 0 ? timeout_ms : kPollNoTimeout;
    225     int ret = poll(pollfds, fd_count, timeout);
    226     if (ret < 0) {
    227         LOGE("Polling failed: %s", strerror(errno));
    228         if (errno == EINTR) {
    229             result = TransportResult::Canceled;
    230         }
    231     } else if (ret == 0) {
    232         LOGD("Poll timed out");
    233         result = TransportResult::Timeout;
    234     } else {
    235         int read_fd = -1;
    236         for (int i = 0; i < kDeviceFileCount; i++) {
    237             if (pollfds[i].revents & POLLIN) {
    238                 read_fd = pollfds[i].fd;
    239                 break;
    240             }
    241         }
    242 
    243         if (read_fd == sensor_fd_) {
    244             LOGD("Data ready on sensors device file");
    245         } else if (read_fd == comms_fd_) {
    246             LOGD("Data ready on comms device file");
    247         }
    248 
    249         if (read_fd >= 0) {
    250             result = ReadEventFromFd(read_fd, message);
    251         } else {
    252             LOGE("Poll returned but none of expected files are ready");
    253         }
    254     }
    255 
    256     return result;
    257 }
    258 
    259 bool AndroidContextHub::FlashSensorHub(const std::vector<uint8_t>& bytes) {
    260     (void)bytes;
    261     LOGE("Flashing is not supported on this platform");
    262     return false;
    263 }
    264 
    265 bool AndroidContextHub::LoadCalibration() {
    266     std::vector<uint8_t> cal_data;
    267     bool success = true;
    268 
    269     for (size_t i = 0; success && i < kCalibrationKeys.size(); i++) {
    270         std::string key;
    271         SensorType sensor_type;
    272 
    273         std::tie(key, sensor_type) = kCalibrationKeys[i];
    274         if (GetCalibrationBytes(key.c_str(), sensor_type, cal_data)) {
    275             success = SendCalibrationData(sensor_type, cal_data);
    276         }
    277 
    278         cal_data.clear();
    279     }
    280 
    281     return success;
    282 }
    283 
    284 bool AndroidContextHub::SetCalibration(SensorType sensor_type, int32_t data) {
    285     LOGI("Setting calibration for sensor %d (%s) to %d",
    286          static_cast<int>(sensor_type),
    287          ContextHub::SensorTypeToAbbrevName(sensor_type).c_str(), data);
    288     auto cal_file = CalibrationFile::Instance();
    289     const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
    290     if (cal_file && key) {
    291         return cal_file->SetSingleAxis(key, data);
    292     }
    293     return false;
    294 }
    295 
    296 bool AndroidContextHub::SetCalibration(SensorType sensor_type, float data) {
    297     LOGI("Setting calibration for sensor %d (%s) to %f",
    298          static_cast<int>(sensor_type),
    299          ContextHub::SensorTypeToAbbrevName(sensor_type).c_str(), data);
    300     auto cal_file = CalibrationFile::Instance();
    301     const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
    302     if (cal_file && key) {
    303         return cal_file->SetSingleAxis(key, data);
    304     }
    305     return false;
    306 }
    307 
    308 bool AndroidContextHub::SetCalibration(SensorType sensor_type, int32_t x,
    309         int32_t y, int32_t z) {
    310     LOGI("Setting calibration for %d to %d %d %d", static_cast<int>(sensor_type),
    311          x, y, z);
    312     auto cal_file = CalibrationFile::Instance();
    313     const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
    314     if (cal_file && key) {
    315         return cal_file->SetTripleAxis(key, x, y, z);
    316     }
    317     return false;
    318 }
    319 
    320 bool AndroidContextHub::SetCalibration(SensorType sensor_type, int32_t x,
    321         int32_t y, int32_t z, int32_t w) {
    322     LOGI("Setting calibration for %d to %d %d %d %d", static_cast<int>(sensor_type),
    323          x, y, z, w);
    324     auto cal_file = CalibrationFile::Instance();
    325     const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
    326     if (cal_file && key) {
    327         return cal_file->SetFourAxis(key, x, y, z, w);
    328     }
    329     return false;
    330 }
    331 
    332 bool AndroidContextHub::SaveCalibration() {
    333     LOGI("Saving calibration data");
    334     auto cal_file = CalibrationFile::Instance();
    335     if (cal_file) {
    336         return cal_file->Save();
    337     }
    338     return false;
    339 }
    340 
    341 ContextHub::TransportResult AndroidContextHub::ReadEventFromFd(
    342         int fd, std::vector<uint8_t>& message) {
    343     ContextHub::TransportResult result = TransportResult::GeneralFailure;
    344 
    345     // Set the size to the maximum, so when we resize later, it's always a
    346     // shrink (otherwise it will end up clearing the bytes)
    347     message.resize(message.capacity());
    348 
    349     LOGD("Calling into read()");
    350     int ret = read(fd, message.data(), message.capacity());
    351     if (ret < 0) {
    352         LOGE("Couldn't read from device file: %s", strerror(errno));
    353         if (errno == EINTR) {
    354             result = TransportResult::Canceled;
    355         }
    356     } else if (ret == 0) {
    357         // We might need to handle this specially, if the driver implements this
    358         // to mean something specific
    359         LOGE("Read unexpectedly returned 0 bytes");
    360     } else {
    361         message.resize(ret);
    362         LOGD_VEC(message);
    363         result = TransportResult::Success;
    364     }
    365 
    366     return result;
    367 }
    368 
    369 int AndroidContextHub::ResetPollFds(struct pollfd *pfds, size_t count) {
    370     memset(pfds, 0, sizeof(struct pollfd) * count);
    371     pfds[0].fd = sensor_fd_;
    372     pfds[0].events = POLLIN;
    373 
    374     int nfds = 1;
    375     if (count > 1 && comms_fd_ >= 0) {
    376         pfds[1].fd = comms_fd_;
    377         pfds[1].events = POLLIN;
    378         nfds++;
    379     }
    380     return nfds;
    381 }
    382 
    383 const char *AndroidContextHub::SensorTypeToCalibrationKey(SensorType sensor_type) {
    384     for (size_t i = 0; i < kCalibrationKeys.size(); i++) {
    385         const char *key;
    386         SensorType sensor_type_for_key;
    387 
    388         std::tie(key, sensor_type_for_key) = kCalibrationKeys[i];
    389         if (sensor_type == sensor_type_for_key) {
    390             return key;
    391         }
    392     }
    393 
    394     LOGE("No calibration key mapping for sensor type %d",
    395          static_cast<int>(sensor_type));
    396     return nullptr;
    397 }
    398 
    399 }  // namespace android
    400