1 /*
2 * Copyright (C) 2019 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 #define ATRACE_TAG (ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)
18 #define LOG_TAG "android.hardware.vibrator@1.3-service.coral"
19
20 #include "Hardware.h"
21
22 #include <log/log.h>
23 #include <utils/Trace.h>
24
25 #include <iostream>
26
27 namespace android {
28 namespace hardware {
29 namespace vibrator {
30 namespace V1_3 {
31 namespace implementation {
32
33 template <typename T>
fileFromEnv(const char * env,T * outStream,std::string * outName=nullptr)34 static void fileFromEnv(const char *env, T *outStream, std::string *outName = nullptr) {
35 auto file = std::getenv(env);
36 auto mode = std::is_base_of_v<std::ostream, T> ? std::ios_base::out : std::ios_base::in;
37
38 if (file == nullptr) {
39 ALOGE("Failed get env %s", env);
40 return;
41 }
42
43 if (outName != nullptr) {
44 *outName = std::string(file);
45 }
46
47 // Force 'in' mode to prevent file creation
48 outStream->open(file, mode | std::ios_base::in);
49 if (!*outStream) {
50 ALOGE("Failed to open %s:%s (%d): %s", env, file, errno, strerror(errno));
51 }
52 }
53
pathsFromEnv(const char * env)54 static auto pathsFromEnv(const char *env) {
55 std::map<std::string, std::ifstream> ret;
56 auto value = std::getenv(env);
57
58 if (value == nullptr) {
59 return ret;
60 }
61
62 std::istringstream paths{value};
63 std::string path;
64
65 while (paths >> path) {
66 ret[path].open(path);
67 }
68
69 return ret;
70 }
71
trim(const std::string & str,const std::string & whitespace=" \\t")72 static std::string trim(const std::string &str, const std::string &whitespace = " \t") {
73 const auto str_begin = str.find_first_not_of(whitespace);
74 if (str_begin == std::string::npos) {
75 return "";
76 }
77
78 const auto str_end = str.find_last_not_of(whitespace);
79 const auto str_range = str_end - str_begin + 1;
80
81 return str.substr(str_begin, str_range);
82 }
83
84 template <typename T>
unpack(std::istream & stream,T * value)85 static Enable_If_Iterable<T, true> unpack(std::istream &stream, T *value) {
86 for (auto &entry : *value) {
87 stream >> entry;
88 }
89 }
90
91 template <typename T>
unpack(std::istream & stream,T * value)92 static Enable_If_Iterable<T, false> unpack(std::istream &stream, T *value) {
93 stream >> *value;
94 }
95
96 #define RECORD(args...) record(__FUNCTION__, ##args)
97
HwApi()98 HwApi::HwApi() {
99 // ostreams below are required
100 fileFromEnv("F0_FILEPATH", &mF0, &mNames[&mF0]);
101 fileFromEnv("REDC_FILEPATH", &mRedc, &mNames[&mRedc]);
102 fileFromEnv("Q_FILEPATH", &mQ, &mNames[&mQ]);
103 fileFromEnv("ACTIVATE_PATH", &mActivate, &mNames[&mActivate]);
104 fileFromEnv("DURATION_PATH", &mDuration, &mNames[&mDuration]);
105 fileFromEnv("STATE_PATH", &mState, &mNames[&mState]);
106 fileFromEnv("EFFECT_DURATION_PATH", &mEffectDuration, &mNames[&mEffectDuration]);
107 fileFromEnv("EFFECT_INDEX_PATH", &mEffectIndex, &mNames[&mEffectIndex]);
108 fileFromEnv("EFFECT_QUEUE_PATH", &mEffectQueue, &mNames[&mEffectQueue]);
109 fileFromEnv("EFFECT_SCALE_PATH", &mEffectScale, &mNames[&mEffectScale]);
110 fileFromEnv("GLOBAL_SCALE_PATH", &mGlobalScale, &mNames[&mGlobalScale]);
111 fileFromEnv("ASP_ENABLE_PATH", &mAspEnable, &mNames[&mAspEnable]);
112 fileFromEnv("GPIO_FALL_INDEX", &mGpioFallIndex, &mNames[&mGpioFallIndex]);
113 fileFromEnv("GPIO_FALL_SCALE", &mGpioFallScale, &mNames[&mGpioFallScale]);
114 fileFromEnv("GPIO_RISE_INDEX", &mGpioRiseIndex, &mNames[&mGpioRiseIndex]);
115 fileFromEnv("GPIO_RISE_SCALE", &mGpioRiseScale, &mNames[&mGpioRiseScale]);
116 }
117
118 template <typename T>
has(T & stream)119 bool HwApi::has(T &stream) {
120 return !!stream;
121 }
122
123 template <typename T, typename U>
get(T * value,U & stream)124 bool HwApi::get(T *value, U &stream) {
125 ATRACE_NAME("HwApi::get");
126 bool ret;
127 stream.seekg(0);
128 stream >> *value;
129 if (!(ret = !!stream)) {
130 ALOGE("Failed to read %s (%d): %s", mNames[&stream].c_str(), errno, strerror(errno));
131 }
132 stream.clear();
133 RECORD(*value, &stream);
134 return ret;
135 }
136
137 template <typename T, typename U>
set(const T & value,U & stream)138 bool HwApi::set(const T &value, U &stream) {
139 ATRACE_NAME("HwApi::set");
140 bool ret;
141 stream << value << std::endl;
142 if (!(ret = !!stream)) {
143 ALOGE("Failed to write %s (%d): %s", mNames[&stream].c_str(), errno, strerror(errno));
144 stream.clear();
145 }
146 RECORD(value, &stream);
147 return ret;
148 }
149
150 template <typename T>
record(const char * func,const T & value,void * stream)151 void HwApi::record(const char *func, const T &value, void *stream) {
152 mRecords.emplace_back(std::make_unique<Record<T>>(func, value, stream));
153 mRecords.erase(mRecords.begin());
154 }
155
debug(int fd)156 void HwApi::debug(int fd) {
157 dprintf(fd, "Kernel:\n");
158
159 for (auto &entry : pathsFromEnv("HWAPI_DEBUG_PATHS")) {
160 auto &path = entry.first;
161 auto &stream = entry.second;
162 std::string line;
163
164 dprintf(fd, " %s:\n", path.c_str());
165 while (std::getline(stream, line)) {
166 dprintf(fd, " %s\n", line.c_str());
167 }
168 }
169
170 dprintf(fd, " Records:\n");
171 for (auto &r : mRecords) {
172 if (r == nullptr) {
173 continue;
174 }
175 dprintf(fd, " %s\n", r->toString(mNames).c_str());
176 }
177 }
178
179 template <typename T>
toString(const NamesMap & names)180 std::string HwApi::Record<T>::toString(const NamesMap &names) {
181 std::stringstream ret;
182
183 ret << mFunc << " '" << names.at(mStream) << "' = '" << mValue << "'";
184
185 return ret.str();
186 }
187
HwCal()188 HwCal::HwCal() {
189 std::ifstream calfile;
190
191 fileFromEnv("CALIBRATION_FILEPATH", &calfile);
192
193 for (std::string line; std::getline(calfile, line);) {
194 if (line.empty() || line[0] == '#') {
195 continue;
196 }
197 std::istringstream is_line(line);
198 std::string key, value;
199 if (std::getline(is_line, key, ':') && std::getline(is_line, value)) {
200 mCalData[trim(key)] = trim(value);
201 }
202 }
203 }
204
205 template <typename T>
get(const char * key,T * value)206 bool HwCal::get(const char *key, T *value) {
207 ATRACE_NAME("HwCal::get");
208 auto it = mCalData.find(key);
209 if (it == mCalData.end()) {
210 ALOGE("Missing %s config!", key);
211 return false;
212 }
213 std::stringstream stream{it->second};
214 unpack(stream, value);
215 if (!stream || !stream.eof()) {
216 ALOGE("Invalid %s config!", key);
217 return false;
218 }
219 return true;
220 }
221
debug(int fd)222 void HwCal::debug(int fd) {
223 std::ifstream stream;
224 std::string path;
225 std::string line;
226
227 dprintf(fd, "Persist:\n");
228
229 fileFromEnv("CALIBRATION_FILEPATH", &stream, &path);
230
231 dprintf(fd, " %s:\n", path.c_str());
232 while (std::getline(stream, line)) {
233 dprintf(fd, " %s\n", line.c_str());
234 }
235 }
236
237 } // namespace implementation
238 } // namespace V1_3
239 } // namespace vibrator
240 } // namespace hardware
241 } // namespace android
242