1 /* 2 * Copyright (C) 2021 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 #pragma once 17 18 #include <glob.h> 19 20 #include <algorithm> 21 22 #include "HardwareBase.h" 23 #include "Vibrator.h" 24 25 #define PROC_SND_PCM "/proc/asound/pcm" 26 #define HAPTIC_PCM_DEVICE_SYMBOL "haptic nohost playback" 27 28 static struct pcm_config haptic_nohost_config = { 29 .channels = 1, 30 .rate = 48000, 31 .period_size = 80, 32 .period_count = 2, 33 .format = PCM_FORMAT_S16_LE, 34 }; 35 36 enum WaveformIndex : uint16_t { 37 /* Physical waveform */ 38 WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0, 39 WAVEFORM_RESERVED_INDEX_1 = 1, 40 WAVEFORM_CLICK_INDEX = 2, 41 WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3, 42 WAVEFORM_THUD_INDEX = 4, 43 WAVEFORM_SPIN_INDEX = 5, 44 WAVEFORM_QUICK_RISE_INDEX = 6, 45 WAVEFORM_SLOW_RISE_INDEX = 7, 46 WAVEFORM_QUICK_FALL_INDEX = 8, 47 WAVEFORM_LIGHT_TICK_INDEX = 9, 48 WAVEFORM_LOW_TICK_INDEX = 10, 49 WAVEFORM_RESERVED_MFG_1, 50 WAVEFORM_RESERVED_MFG_2, 51 WAVEFORM_RESERVED_MFG_3, 52 WAVEFORM_MAX_PHYSICAL_INDEX, 53 /* OWT waveform */ 54 WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX, 55 WAVEFORM_PWLE, 56 /* 57 * Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96. 58 * #define FF_GAIN 0x60 // 96 in decimal 59 * #define FF_MAX_EFFECTS FF_GAIN 60 */ 61 WAVEFORM_MAX_INDEX, 62 }; 63 64 namespace aidl { 65 namespace android { 66 namespace hardware { 67 namespace vibrator { 68 69 class HwApi : public Vibrator::HwApi, private HwApiBase { 70 public: HwApi()71 HwApi() { 72 HwApi::initFF(); 73 open("calibration/f0_stored", &mF0); 74 open("default/f0_offset", &mF0Offset); 75 open("calibration/redc_stored", &mRedc); 76 open("calibration/q_stored", &mQ); 77 open("default/vibe_state", &mVibeState); 78 open("default/num_waves", &mEffectCount); 79 open("default/owt_free_space", &mOwtFreeSpace); 80 open("default/f0_comp_enable", &mF0CompEnable); 81 open("default/redc_comp_enable", &mRedcCompEnable); 82 open("default/delay_before_stop_playback_us", &mMinOnOffInterval); 83 } 84 setF0(std::string value)85 bool setF0(std::string value) override { return set(value, &mF0); } setF0Offset(uint32_t value)86 bool setF0Offset(uint32_t value) override { return set(value, &mF0Offset); } setRedc(std::string value)87 bool setRedc(std::string value) override { return set(value, &mRedc); } setQ(std::string value)88 bool setQ(std::string value) override { return set(value, &mQ); } getEffectCount(uint32_t * value)89 bool getEffectCount(uint32_t *value) override { return get(value, &mEffectCount); } pollVibeState(uint32_t value,int32_t timeoutMs)90 bool pollVibeState(uint32_t value, int32_t timeoutMs) override { 91 return poll(value, &mVibeState, timeoutMs); 92 } hasOwtFreeSpace()93 bool hasOwtFreeSpace() override { return has(mOwtFreeSpace); } getOwtFreeSpace(uint32_t * value)94 bool getOwtFreeSpace(uint32_t *value) override { return get(value, &mOwtFreeSpace); } setF0CompEnable(bool value)95 bool setF0CompEnable(bool value) override { return set(value, &mF0CompEnable); } setRedcCompEnable(bool value)96 bool setRedcCompEnable(bool value) override { return set(value, &mRedcCompEnable); } setMinOnOffInterval(uint32_t value)97 bool setMinOnOffInterval(uint32_t value) override { return set(value, &mMinOnOffInterval); } getContextScale()98 uint32_t getContextScale() override { 99 return utils::getProperty("persist.vendor.vibrator.hal.context.scale", 100); 100 } getContextEnable()101 bool getContextEnable() override { 102 return utils::getProperty("persist.vendor.vibrator.hal.context.enable", false); 103 } getContextSettlingTime()104 uint32_t getContextSettlingTime() override { 105 return utils::getProperty("persist.vendor.vibrator.hal.context.settlingtime", 3000); 106 } getContextCooldownTime()107 uint32_t getContextCooldownTime() override { 108 return utils::getProperty("persist.vendor.vibrator.hal.context.cooldowntime", 1000); 109 } getContextFadeEnable()110 bool getContextFadeEnable() override { 111 return utils::getProperty("persist.vendor.vibrator.hal.context.fade", false); 112 } 113 114 // TODO(b/234338136): Need to add the force feedback HW API test cases initFF()115 bool initFF() override { 116 ATRACE_NAME(__func__); 117 const std::string INPUT_EVENT_NAME = std::getenv("INPUT_EVENT_NAME") ?: ""; 118 if (INPUT_EVENT_NAME.find("cs40l26") == std::string::npos) { 119 ALOGE("Invalid input name: %s", INPUT_EVENT_NAME.c_str()); 120 return false; 121 } 122 123 glob_t g = {}; 124 const std::string INPUT_EVENT_PATH = "/dev/input/event*"; 125 int fd = -1, ret; 126 uint32_t val = 0; 127 char str[256] = {0x00}; 128 // Scan /dev/input/event* to get the correct input device path for FF effects manipulation. 129 // Then constructs the /sys/class/input/event*/../../../ for driver attributes accessing 130 // across different platforms and different kernels. 131 for (uint8_t retry = 1; retry < 11 && !mInputFd.ok(); retry++) { 132 ret = glob(INPUT_EVENT_PATH.c_str(), 0, nullptr, &g); 133 if (ret) { 134 ALOGE("Failed to get input event paths (%d): %s", errno, strerror(errno)); 135 } else { 136 for (size_t i = 0; i < g.gl_pathc; i++) { 137 fd = TEMP_FAILURE_RETRY(::open(g.gl_pathv[i], O_RDWR)); 138 if (fd < 0) { 139 continue; 140 } 141 // Determine the input device path: 142 // 1. Check if EV_FF is flagged in event bits. 143 // 2. Match device name(s) with this CS40L26 HAL instance. 144 if (ioctl(fd, EVIOCGBIT(0, sizeof(val)), &val) > 0 && (val & (1 << EV_FF)) && 145 ioctl(fd, EVIOCGNAME(sizeof(str)), &str) > 0 && 146 strcmp(str, INPUT_EVENT_NAME.c_str()) == 0) { 147 // Get fd ready for input event ioctl(). 148 mInputFd.reset(fd); // mInputFd.ok() becomes true. 149 ALOGI("Control %s through %s", INPUT_EVENT_NAME.c_str(), g.gl_pathv[i]); 150 151 std::string path = g.gl_pathv[i]; 152 // Get fstream ready for input event write(). 153 saveName(path, &mInputIoStream); 154 mInputIoStream.open( 155 path, std::fstream::out | std::fstream::in | std::fstream::binary); 156 if (!mInputIoStream) { 157 ALOGE("Failed to open %s (%d): %s", path.c_str(), errno, 158 strerror(errno)); 159 } 160 161 // Construct the sysfs device path. 162 path = "/sys/class/input/" + 163 path.substr(path.find("event"), std::string::npos) + "/../../../"; 164 updatePathPrefix(path); 165 break; 166 } 167 close(fd); 168 memset(str, 0x00, sizeof(str)); 169 val = 0; 170 } 171 } 172 173 if (!mInputFd.ok()) { 174 sleep(1); 175 ALOGW("Retry #%d to search in %zu input devices...", retry, g.gl_pathc); 176 } 177 } 178 globfree(&g); 179 180 if (!mInputFd.ok()) { 181 ALOGE("Failed to get an input event with name %s", INPUT_EVENT_NAME.c_str()); 182 return false; 183 } 184 185 return true; 186 } setFFGain(uint16_t value)187 bool setFFGain(uint16_t value) override { 188 ATRACE_NAME(StringPrintf("%s %d%%", __func__, value).c_str()); 189 struct input_event gain = { 190 .type = EV_FF, 191 .code = FF_GAIN, 192 .value = value, 193 }; 194 if (value > 100) { 195 ALOGE("Invalid gain"); 196 return false; 197 } 198 mInputIoStream.write((const char *)&gain, sizeof(gain)); 199 mInputIoStream.flush(); 200 if (mInputIoStream.fail()) { 201 ALOGE("setFFGain fail"); 202 return false; 203 } 204 HWAPI_RECORD(StringPrintf("%d%%", value), &mInputIoStream); 205 return true; 206 } setFFEffect(struct ff_effect * effect,uint16_t timeoutMs)207 bool setFFEffect(struct ff_effect *effect, uint16_t timeoutMs) override { 208 ATRACE_NAME(StringPrintf("%s %dms", __func__, timeoutMs).c_str()); 209 if (effect == nullptr) { 210 ALOGE("Invalid ff_effect"); 211 return false; 212 } 213 if (((*effect).replay.length != timeoutMs) || (ioctl(mInputFd, EVIOCSFF, effect) < 0)) { 214 ALOGE("setFFEffect fail"); 215 return false; 216 } 217 HWAPI_RECORD(StringPrintf("#%d: %dms", (*effect).id, (*effect).replay.length), 218 &mInputIoStream); 219 return true; 220 } setFFPlay(int8_t index,bool value)221 bool setFFPlay(int8_t index, bool value) override { 222 ATRACE_NAME(StringPrintf("%s index:%d %s", __func__, index, value ? "on" : "off").c_str()); 223 struct input_event play = { 224 .type = EV_FF, 225 .code = static_cast<uint16_t>(index), 226 .value = value, 227 }; 228 mInputIoStream.write((const char *)&play, sizeof(play)); 229 mInputIoStream.flush(); 230 if (mInputIoStream.fail()) { 231 ALOGE("setFFPlay fail"); 232 return false; 233 } 234 HWAPI_RECORD(StringPrintf("#%d: %b", index, value), &mInputIoStream); 235 return true; 236 } getHapticAlsaDevice(int * card,int * device)237 bool getHapticAlsaDevice(int *card, int *device) override { 238 ATRACE_NAME(__func__); 239 std::string line; 240 std::ifstream myfile(PROC_SND_PCM); 241 if (myfile.is_open()) { 242 while (getline(myfile, line)) { 243 if (line.find(HAPTIC_PCM_DEVICE_SYMBOL) != std::string::npos) { 244 std::stringstream ss(line); 245 std::string currentToken; 246 std::getline(ss, currentToken, ':'); 247 sscanf(currentToken.c_str(), "%d-%d", card, device); 248 saveName(StringPrintf("/dev/snd/pcmC%uD%up", *card, *device), &mPcmStream); 249 return true; 250 } 251 } 252 myfile.close(); 253 } else { 254 ALOGE("Failed to read file: %s", PROC_SND_PCM); 255 } 256 return false; 257 } setHapticPcmAmp(struct pcm ** haptic_pcm,bool enable,int card,int device)258 bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device) override { 259 ATRACE_NAME(StringPrintf("%s %s", __func__, enable ? "enable" : "disable").c_str()); 260 int ret = 0; 261 262 if (enable) { 263 *haptic_pcm = pcm_open(card, device, PCM_OUT, &haptic_nohost_config); 264 if (!pcm_is_ready(*haptic_pcm)) { 265 ALOGE("cannot open pcm_out driver: %s", pcm_get_error(*haptic_pcm)); 266 goto fail; 267 } 268 HWAPI_RECORD(std::string("pcm_open"), &mPcmStream); 269 270 ret = pcm_prepare(*haptic_pcm); 271 if (ret < 0) { 272 ALOGE("cannot prepare haptic_pcm: %s", pcm_get_error(*haptic_pcm)); 273 goto fail; 274 } 275 HWAPI_RECORD(std::string("pcm_prepare"), &mPcmStream); 276 277 ret = pcm_start(*haptic_pcm); 278 if (ret < 0) { 279 ALOGE("cannot start haptic_pcm: %s", pcm_get_error(*haptic_pcm)); 280 goto fail; 281 } 282 HWAPI_RECORD(std::string("pcm_start"), &mPcmStream); 283 284 return true; 285 } else { 286 if (*haptic_pcm) { 287 pcm_close(*haptic_pcm); 288 HWAPI_RECORD(std::string("pcm_close"), &mPcmStream); 289 *haptic_pcm = NULL; 290 } 291 return true; 292 } 293 294 fail: 295 pcm_close(*haptic_pcm); 296 HWAPI_RECORD(std::string("pcm_close"), &mPcmStream); 297 *haptic_pcm = NULL; 298 return false; 299 } uploadOwtEffect(const uint8_t * owtData,const uint32_t numBytes,struct ff_effect * effect,uint32_t * outEffectIndex,int * status)300 bool uploadOwtEffect(const uint8_t *owtData, const uint32_t numBytes, struct ff_effect *effect, 301 uint32_t *outEffectIndex, int *status) override { 302 ATRACE_NAME(__func__); 303 if (owtData == nullptr || effect == nullptr || outEffectIndex == nullptr) { 304 ALOGE("Invalid argument owtData, ff_effect or outEffectIndex"); 305 *status = EX_NULL_POINTER; 306 return false; 307 } 308 if (status == nullptr) { 309 ALOGE("Invalid argument status"); 310 return false; 311 } 312 313 (*effect).u.periodic.custom_len = numBytes / sizeof(uint16_t); 314 memcpy((*effect).u.periodic.custom_data, owtData, numBytes); 315 316 if ((*effect).id != -1) { 317 ALOGE("(*effect).id != -1"); 318 } 319 320 /* Create a new OWT waveform to update the PWLE or composite effect. */ 321 (*effect).id = -1; 322 if (ioctl(mInputFd, EVIOCSFF, effect) < 0) { 323 ALOGE("Failed to upload effect %d (%d): %s", *outEffectIndex, errno, strerror(errno)); 324 *status = EX_ILLEGAL_STATE; 325 return false; 326 } 327 328 if ((*effect).id >= FF_MAX_EFFECTS || (*effect).id < 0) { 329 ALOGE("Invalid waveform index after upload OWT effect: %d", (*effect).id); 330 *status = EX_ILLEGAL_ARGUMENT; 331 return false; 332 } 333 *outEffectIndex = (*effect).id; 334 *status = 0; 335 HWAPI_RECORD(StringPrintf("#%d: %dB", *outEffectIndex, numBytes), &mInputIoStream); 336 return true; 337 } eraseOwtEffect(int8_t effectIndex,std::vector<ff_effect> * effect)338 bool eraseOwtEffect(int8_t effectIndex, std::vector<ff_effect> *effect) override { 339 ATRACE_NAME(__func__); 340 uint32_t effectCountBefore, effectCountAfter, i, successFlush = 0; 341 342 if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) { 343 ALOGE("Invalid waveform index for OWT erase: %d", effectIndex); 344 return false; 345 } 346 if (effect == nullptr || (*effect).empty()) { 347 ALOGE("Invalid argument effect"); 348 return false; 349 } 350 351 if (effectIndex < WAVEFORM_MAX_INDEX) { 352 /* Normal situation. Only erase the effect which we just played. */ 353 if (ioctl(mInputFd, EVIOCRMFF, effectIndex) < 0) { 354 ALOGE("Failed to erase effect %d (%d): %s", effectIndex, errno, strerror(errno)); 355 } 356 for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < WAVEFORM_MAX_INDEX; i++) { 357 if ((*effect)[i].id == effectIndex) { 358 (*effect)[i].id = -1; 359 break; 360 } 361 } 362 HWAPI_RECORD(StringPrintf("#%d", effectIndex), &mInputIoStream); 363 } else { 364 /* Flush all non-prestored effects of ff-core and driver. */ 365 getEffectCount(&effectCountBefore); 366 for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < FF_MAX_EFFECTS; i++) { 367 if (ioctl(mInputFd, EVIOCRMFF, i) >= 0) { 368 successFlush++; 369 HWAPI_RECORD(StringPrintf("#%d", i), &mInputIoStream); 370 } 371 } 372 getEffectCount(&effectCountAfter); 373 ALOGW("Flushed effects: ff: %d; driver: %d -> %d; success: %d", effectIndex, 374 effectCountBefore, effectCountAfter, successFlush); 375 /* Reset all OWT effect index of HAL. */ 376 for (i = WAVEFORM_MAX_PHYSICAL_INDEX; i < WAVEFORM_MAX_INDEX; i++) { 377 (*effect)[i].id = -1; 378 } 379 } 380 return true; 381 } isDbcSupported()382 bool isDbcSupported() override { 383 ATRACE_NAME(__func__); 384 return utils::getProperty("ro.vendor.vibrator.hal.dbc.enable", false); 385 } 386 enableDbc()387 bool enableDbc() override { 388 ATRACE_NAME(__func__); 389 if (isDbcSupported()) { 390 open("dbc/dbc_env_rel_coef", &mDbcEnvRelCoef); 391 open("dbc/dbc_rise_headroom", &mDbcRiseHeadroom); 392 open("dbc/dbc_fall_headroom", &mDbcFallHeadroom); 393 open("dbc/dbc_tx_lvl_thresh_fs", &mDbcTxLvlThreshFs); 394 open("dbc/dbc_tx_lvl_hold_off_ms", &mDbcTxLvlHoldOffMs); 395 open("default/pm_active_timeout_ms", &mPmActiveTimeoutMs); 396 open("dbc/dbc_enable", &mDbcEnable); 397 398 // Set values from config. Default if not found. 399 set(utils::getProperty("ro.vendor.vibrator.hal.dbc.envrelcoef", kDbcDefaultEnvRelCoef), 400 &mDbcEnvRelCoef); 401 set(utils::getProperty("ro.vendor.vibrator.hal.dbc.riseheadroom", 402 kDbcDefaultRiseHeadroom), 403 &mDbcRiseHeadroom); 404 set(utils::getProperty("ro.vendor.vibrator.hal.dbc.fallheadroom", 405 kDbcDefaultFallHeadroom), 406 &mDbcFallHeadroom); 407 set(utils::getProperty("ro.vendor.vibrator.hal.dbc.txlvlthreshfs", 408 kDbcDefaultTxLvlThreshFs), 409 &mDbcTxLvlThreshFs); 410 set(utils::getProperty("ro.vendor.vibrator.hal.dbc.txlvlholdoffms", 411 kDbcDefaultTxLvlHoldOffMs), 412 &mDbcTxLvlHoldOffMs); 413 set(utils::getProperty("ro.vendor.vibrator.hal.pm.activetimeout", 414 kDefaultPmActiveTimeoutMs), 415 &mPmActiveTimeoutMs); 416 set(kDbcEnable, &mDbcEnable); 417 return true; 418 } 419 return false; 420 } 421 debug(int fd)422 void debug(int fd) override { HwApiBase::debug(fd); } 423 424 private: 425 static constexpr uint32_t kDbcDefaultEnvRelCoef = 8353728; 426 static constexpr uint32_t kDbcDefaultRiseHeadroom = 1909602; 427 static constexpr uint32_t kDbcDefaultFallHeadroom = 1909602; 428 static constexpr uint32_t kDbcDefaultTxLvlThreshFs = 2516583; 429 static constexpr uint32_t kDbcDefaultTxLvlHoldOffMs = 0; 430 static constexpr uint32_t kDefaultPmActiveTimeoutMs = 5; 431 static constexpr uint32_t kDbcEnable = 1; 432 433 std::ofstream mF0; 434 std::ofstream mF0Offset; 435 std::ofstream mRedc; 436 std::ofstream mQ; 437 std::ifstream mEffectCount; 438 std::ifstream mVibeState; 439 std::ifstream mOwtFreeSpace; 440 std::ofstream mF0CompEnable; 441 std::ofstream mRedcCompEnable; 442 std::ofstream mMinOnOffInterval; 443 std::ofstream mInputIoStream; 444 std::ofstream mPcmStream; 445 ::android::base::unique_fd mInputFd; 446 447 // DBC Parameters 448 std::ofstream mDbcEnvRelCoef; 449 std::ofstream mDbcRiseHeadroom; 450 std::ofstream mDbcFallHeadroom; 451 std::ofstream mDbcTxLvlThreshFs; 452 std::ofstream mDbcTxLvlHoldOffMs; 453 std::ofstream mDbcEnable; 454 std::ofstream mPmActiveTimeoutMs; 455 }; 456 457 class HwCal : public Vibrator::HwCal, private HwCalBase { 458 private: 459 static constexpr char VERSION[] = "version"; 460 static constexpr char F0_CONFIG[] = "f0_measured"; 461 static constexpr char REDC_CONFIG[] = "redc_measured"; 462 static constexpr char Q_CONFIG[] = "q_measured"; 463 static constexpr char TICK_VOLTAGES_CONFIG[] = "v_tick"; 464 static constexpr char CLICK_VOLTAGES_CONFIG[] = "v_click"; 465 static constexpr char LONG_VOLTAGES_CONFIG[] = "v_long"; 466 467 static constexpr uint32_t VERSION_DEFAULT = 2; 468 static constexpr int32_t DEFAULT_FREQUENCY_SHIFT = 0; 469 static constexpr float DEFAULT_DEVICE_MASS = 0.21; 470 static constexpr float DEFAULT_LOC_COEFF = 2.5; 471 static constexpr std::array<uint32_t, 2> V_TICK_DEFAULT = {1, 100}; 472 static constexpr std::array<uint32_t, 2> V_CLICK_DEFAULT = {1, 100}; 473 static constexpr std::array<uint32_t, 2> V_LONG_DEFAULT = {1, 100}; 474 475 public: HwCal()476 HwCal() {} 477 getVersion(uint32_t * value)478 bool getVersion(uint32_t *value) override { 479 if (getPersist(VERSION, value)) { 480 return true; 481 } 482 *value = VERSION_DEFAULT; 483 return true; 484 } getLongFrequencyShift(int32_t * value)485 bool getLongFrequencyShift(int32_t *value) override { 486 return getProperty("long.frequency.shift", value, DEFAULT_FREQUENCY_SHIFT); 487 } getDeviceMass(float * value)488 bool getDeviceMass(float *value) override { 489 return getProperty("device.mass", value, DEFAULT_DEVICE_MASS); 490 } getLocCoeff(float * value)491 bool getLocCoeff(float *value) override { 492 return getProperty("loc.coeff", value, DEFAULT_LOC_COEFF); 493 } getF0(std::string * value)494 bool getF0(std::string *value) override { return getPersist(F0_CONFIG, value); } getRedc(std::string * value)495 bool getRedc(std::string *value) override { return getPersist(REDC_CONFIG, value); } getQ(std::string * value)496 bool getQ(std::string *value) override { return getPersist(Q_CONFIG, value); } getTickVolLevels(std::array<uint32_t,2> * value)497 bool getTickVolLevels(std::array<uint32_t, 2> *value) override { 498 if (getPersist(TICK_VOLTAGES_CONFIG, value)) { 499 return true; 500 } 501 *value = V_TICK_DEFAULT; 502 return true; 503 } getClickVolLevels(std::array<uint32_t,2> * value)504 bool getClickVolLevels(std::array<uint32_t, 2> *value) override { 505 if (getPersist(CLICK_VOLTAGES_CONFIG, value)) { 506 return true; 507 } 508 *value = V_CLICK_DEFAULT; 509 return true; 510 } getLongVolLevels(std::array<uint32_t,2> * value)511 bool getLongVolLevels(std::array<uint32_t, 2> *value) override { 512 if (getPersist(LONG_VOLTAGES_CONFIG, value)) { 513 return true; 514 } 515 *value = V_LONG_DEFAULT; 516 return true; 517 } isChirpEnabled()518 bool isChirpEnabled() override { 519 bool value; 520 getProperty("chirp.enabled", &value, false); 521 return value; 522 } getSupportedPrimitives(uint32_t * value)523 bool getSupportedPrimitives(uint32_t *value) override { 524 return getProperty("supported_primitives", value, (uint32_t)0); 525 } isF0CompEnabled()526 bool isF0CompEnabled() override { 527 bool value; 528 getProperty("f0.comp.enabled", &value, true); 529 return value; 530 } isRedcCompEnabled()531 bool isRedcCompEnabled() override { 532 bool value; 533 getProperty("redc.comp.enabled", &value, false); 534 return value; 535 } debug(int fd)536 void debug(int fd) override { HwCalBase::debug(fd); } 537 }; 538 539 } // namespace vibrator 540 } // namespace hardware 541 } // namespace android 542 } // namespace aidl 543