/* * Copyright (C) 2019 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 "android.hardware.vibrator@1.3-service.redfin" #include "Hardware.h" #include #include namespace android { namespace hardware { namespace vibrator { namespace V1_3 { namespace implementation { template static void fileFromEnv(const char *env, T *outStream, std::string *outName = nullptr) { auto file = std::getenv(env); auto mode = std::is_base_of_v ? std::ios_base::out : std::ios_base::in; if (file == nullptr) { ALOGE("Failed get env %s", env); return; } if (outName != nullptr) { *outName = std::string(file); } // Force 'in' mode to prevent file creation outStream->open(file, mode | std::ios_base::in); if (!*outStream) { ALOGE("Failed to open %s:%s (%d): %s", env, file, errno, strerror(errno)); } } static auto pathsFromEnv(const char *env) { std::map ret; auto value = std::getenv(env); if (value == nullptr) { return ret; } std::istringstream paths{value}; std::string path; while (paths >> path) { ret[path].open(path); } return ret; } static std::string trim(const std::string &str, const std::string &whitespace = " \t") { const auto str_begin = str.find_first_not_of(whitespace); if (str_begin == std::string::npos) { return ""; } const auto str_end = str.find_last_not_of(whitespace); const auto str_range = str_end - str_begin + 1; return str.substr(str_begin, str_range); } template static Enable_If_Iterable unpack(std::istream &stream, T *value) { for (auto &entry : *value) { stream >> entry; } } template static Enable_If_Iterable unpack(std::istream &stream, T *value) { stream >> *value; } HwApi::HwApi() { // ostreams below are required fileFromEnv("F0_FILEPATH", &mF0, &mNames[&mF0]); fileFromEnv("REDC_FILEPATH", &mRedc, &mNames[&mRedc]); fileFromEnv("Q_FILEPATH", &mQ, &mNames[&mQ]); fileFromEnv("ACTIVATE_PATH", &mActivate, &mNames[&mActivate]); fileFromEnv("DURATION_PATH", &mDuration, &mNames[&mDuration]); fileFromEnv("STATE_PATH", &mState, &mNames[&mState]); fileFromEnv("EFFECT_DURATION_PATH", &mEffectDuration, &mNames[&mEffectDuration]); fileFromEnv("EFFECT_INDEX_PATH", &mEffectIndex, &mNames[&mEffectIndex]); fileFromEnv("EFFECT_QUEUE_PATH", &mEffectQueue, &mNames[&mEffectQueue]); fileFromEnv("EFFECT_SCALE_PATH", &mEffectScale, &mNames[&mEffectScale]); fileFromEnv("GLOBAL_SCALE_PATH", &mGlobalScale, &mNames[&mGlobalScale]); fileFromEnv("ASP_ENABLE_PATH", &mAspEnable, &mNames[&mAspEnable]); fileFromEnv("GPIO_FALL_INDEX", &mGpioFallIndex, &mNames[&mGpioFallIndex]); fileFromEnv("GPIO_FALL_SCALE", &mGpioFallScale, &mNames[&mGpioFallScale]); fileFromEnv("GPIO_RISE_INDEX", &mGpioRiseIndex, &mNames[&mGpioRiseIndex]); fileFromEnv("GPIO_RISE_SCALE", &mGpioRiseScale, &mNames[&mGpioRiseScale]); } template bool HwApi::has(T &stream) { return !!stream; } template bool HwApi::get(T *value, U &stream) { bool ret; stream.seekg(0); stream >> *value; if (!(ret = !!stream)) { ALOGE("Failed to read %s (%d): %s", mNames[&stream].c_str(), errno, strerror(errno)); } stream.clear(); return ret; } template bool HwApi::set(const T &value, U &stream) { bool ret; stream << value << std::endl; if (!(ret = !!stream)) { ALOGE("Failed to write %s (%d): %s", mNames[&stream].c_str(), errno, strerror(errno)); stream.clear(); } return ret; } void HwApi::debug(int fd) { dprintf(fd, "Kernel:\n"); for (auto &entry : pathsFromEnv("HWAPI_DEBUG_PATHS")) { auto &path = entry.first; auto &stream = entry.second; std::string line; dprintf(fd, " %s:\n", path.c_str()); while (std::getline(stream, line)) { dprintf(fd, " %s\n", line.c_str()); } } } HwCal::HwCal() { std::ifstream calfile; fileFromEnv("CALIBRATION_FILEPATH", &calfile); for (std::string line; std::getline(calfile, line);) { if (line.empty() || line[0] == '#') { continue; } std::istringstream is_line(line); std::string key, value; if (std::getline(is_line, key, ':') && std::getline(is_line, value)) { mCalData[trim(key)] = trim(value); } } } template bool HwCal::get(const char *key, T *value) { auto it = mCalData.find(key); if (it == mCalData.end()) { ALOGE("Missing %s config!", key); return false; } std::stringstream stream{it->second}; unpack(stream, value); if (!stream || !stream.eof()) { ALOGE("Invalid %s config!", key); return false; } return true; } void HwCal::debug(int fd) { std::ifstream stream; std::string path; std::string line; dprintf(fd, "Persist:\n"); fileFromEnv("CALIBRATION_FILEPATH", &stream, &path); dprintf(fd, " %s:\n", path.c_str()); while (std::getline(stream, line)) { dprintf(fd, " %s\n", line.c_str()); } } } // namespace implementation } // namespace V1_3 } // namespace vibrator } // namespace hardware } // namespace android