/* * Copyright (C) 2021 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 "Vibrator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0])) #endif #ifdef LOG_TAG #undef LOG_TAG #define LOG_TAG std::getenv("HAPTIC_NAME") #endif namespace aidl { namespace android { namespace hardware { namespace vibrator { static constexpr uint16_t FF_CUSTOM_DATA_LEN_MAX_COMP = 2044; // (COMPOSE_SIZE_MAX + 1) * 8 + 4 static constexpr uint16_t FF_CUSTOM_DATA_LEN_MAX_PWLE = 2302; static constexpr uint32_t WAVEFORM_DOUBLE_CLICK_SILENCE_MS = 100; static constexpr uint32_t WAVEFORM_LONG_VIBRATION_THRESHOLD_MS = 50; static constexpr uint8_t VOLTAGE_SCALE_MAX = 100; static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6; // I2C Transaction + DSP Return-From-Standby static constexpr int8_t MAX_PAUSE_TIMING_ERROR_MS = 1; // ALERT Irq Handling static constexpr uint32_t MAX_TIME_MS = UINT16_MAX; static constexpr auto ASYNC_COMPLETION_TIMEOUT = std::chrono::milliseconds(100); static constexpr auto POLLING_TIMEOUT = 50; static constexpr int32_t COMPOSE_DELAY_MAX_MS = 10000; /* nsections is 8 bits. Need to preserve 1 section for the first delay before the first effect. */ static constexpr int32_t COMPOSE_SIZE_MAX = 254; static constexpr int32_t COMPOSE_PWLE_SIZE_MAX_DEFAULT = 127; // Measured resonant frequency, f0_measured, is represented by Q10.14 fixed // point format on cs40l26 devices. The expression to calculate f0 is: // f0 = f0_measured / 2^Q14_BIT_SHIFT // See the LRA Calibration Support documentation for more details. static constexpr int32_t Q14_BIT_SHIFT = 14; // Measured ReDC. The LRA series resistance (ReDC), expressed as follows // redc(ohms) = redc_measured / 2^Q15_BIT_SHIFT. // This value represents the unit-specific ReDC input to the click compensation // algorithm. It can be overwritten at a later time by writing to the redc_stored // sysfs control. // See the LRA Calibration Support documentation for more details. static constexpr int32_t Q15_BIT_SHIFT = 15; // Measured Q factor, q_measured, is represented by Q8.16 fixed // point format on cs40l26 devices. The expression to calculate q is: // q = q_measured / 2^Q16_BIT_SHIFT // See the LRA Calibration Support documentation for more details. static constexpr int32_t Q16_BIT_SHIFT = 16; static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383; static constexpr uint32_t WT_LEN_CALCD = 0x00800000; static constexpr uint8_t PWLE_CHIRP_BIT = 0x8; // Dynamic/static frequency and voltage static constexpr uint8_t PWLE_BRAKE_BIT = 0x4; static constexpr uint8_t PWLE_AMP_REG_BIT = 0x2; static constexpr uint8_t PWLE_WT_TYPE = 12; static constexpr uint8_t PWLE_HEADER_WORD_COUNT = 3; static constexpr uint8_t PWLE_HEADER_FTR_SHIFT = 8; static constexpr uint8_t PWLE_SVC_METADATA_WORD_COUNT = 3; static constexpr uint32_t PWLE_SVC_METADATA_TERMINATOR = 0xFFFFFF; static constexpr uint8_t PWLE_SEGMENT_WORD_COUNT = 2; static constexpr uint8_t PWLE_HEADER_WCOUNT_WORD_OFFSET = 2; static constexpr uint8_t PWLE_WORD_SIZE = sizeof(uint32_t); static constexpr uint8_t PWLE_SVC_NO_BRAKING = -1; static constexpr uint8_t PWLE_SVC_CAT_BRAKING = 0; static constexpr uint8_t PWLE_SVC_OPEN_BRAKING = 1; static constexpr uint8_t PWLE_SVC_CLOSED_BRAKING = 2; static constexpr uint8_t PWLE_SVC_MIXED_BRAKING = 3; static constexpr uint32_t PWLE_SVC_MAX_BRAKING_TIME_MS = 1000; static constexpr uint8_t PWLE_FTR_BUZZ_BIT = 0x80; static constexpr uint8_t PWLE_FTR_CLICK_BIT = 0x00; static constexpr uint8_t PWLE_FTR_DYNAMIC_F0_BIT = 0x10; static constexpr uint8_t PWLE_FTR_SVC_METADATA_BIT = 0x04; static constexpr uint8_t PWLE_FTR_DVL_BIT = 0x02; static constexpr uint8_t PWLE_FTR_LF0T_BIT = 0x01; static constexpr float PWLE_LEVEL_MIN = 0.0; static constexpr float PWLE_LEVEL_MAX = 1.0; static constexpr float CS40L26_PWLE_LEVEL_MIN = -1.0; static constexpr float CS40L26_PWLE_LEVEL_MAX = 0.9995118; static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.00; static constexpr float PWLE_FREQUENCY_MIN_HZ = 1.00; static constexpr float PWLE_FREQUENCY_MAX_HZ = 1000.00; static constexpr float PWLE_BW_MAP_SIZE = 1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ); /* * [15] Edge, 0:Falling, 1:Rising * [14:12] GPI_NUM, 1:GPI1 (with CS40L26A, 1 is the only supported GPI) * [8] BANK, 0:RAM, 1:R0M * [7] USE_BUZZGEN, 0:Not buzzgen, 1:buzzgen * [6:0] WAVEFORM_INDEX * 0x9100 = 1001 0001 0000 0000: Rising + GPI1 + RAM + Not buzzgen */ static constexpr uint16_t GPIO_TRIGGER_CONFIG = 0x9100; static uint16_t amplitudeToScale(float amplitude, float maximum) { float ratio = 100; /* Unit: % */ if (maximum != 0) ratio = amplitude / maximum * 100; if (maximum == 0 || ratio > 100) ratio = 100; return std::round(ratio); } static float redcToFloat(std::string *caldata) { return static_cast(std::stoul(*caldata, nullptr, 16)) / (1 << Q15_BIT_SHIFT); } enum WaveformBankID : uint8_t { RAM_WVFRM_BANK, ROM_WVFRM_BANK, OWT_WVFRM_BANK, }; enum WaveformIndex : uint16_t { /* Physical waveform */ WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0, WAVEFORM_RESERVED_INDEX_1 = 1, WAVEFORM_CLICK_INDEX = 2, WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3, WAVEFORM_THUD_INDEX = 4, WAVEFORM_SPIN_INDEX = 5, WAVEFORM_QUICK_RISE_INDEX = 6, WAVEFORM_SLOW_RISE_INDEX = 7, WAVEFORM_QUICK_FALL_INDEX = 8, WAVEFORM_LIGHT_TICK_INDEX = 9, WAVEFORM_LOW_TICK_INDEX = 10, WAVEFORM_RESERVED_MFG_1, WAVEFORM_RESERVED_MFG_2, WAVEFORM_RESERVED_MFG_3, WAVEFORM_MAX_PHYSICAL_INDEX, /* OWT waveform */ WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX, WAVEFORM_PWLE, /* * Refer to , the WAVEFORM_MAX_INDEX must not exceed 96. * #define FF_GAIN 0x60 // 96 in decimal * #define FF_MAX_EFFECTS FF_GAIN */ WAVEFORM_MAX_INDEX, }; std::vector defaultSupportedPrimitives = { ndk::enum_range().begin(), ndk::enum_range().end()}; enum vibe_state { VIBE_STATE_STOPPED = 0, VIBE_STATE_HAPTIC, VIBE_STATE_ASP, }; std::mutex mActiveId_mutex; // protects mActiveId class DspMemChunk { private: std::unique_ptr head; size_t bytes = 0; uint8_t waveformType; uint8_t *_current; const uint8_t *_max; uint32_t _cache = 0; int _cachebits = 0; bool isEnd() const { return _current == _max; } int min(int x, int y) { return x < y ? x : y; } int write(int nbits, uint32_t val) { int nwrite, i; nwrite = min(24 - _cachebits, nbits); _cache <<= nwrite; _cache |= val >> (nbits - nwrite); _cachebits += nwrite; nbits -= nwrite; if (_cachebits == 24) { if (isEnd()) return -ENOSPC; _cache &= 0xFFFFFF; for (i = 0; i < sizeof(_cache); i++, _cache <<= 8) *_current++ = (_cache & 0xFF000000) >> 24; bytes += sizeof(_cache); _cachebits = 0; } if (nbits) return write(nbits, val); return 0; } int fToU16(float input, uint16_t *output, float scale, float min, float max) { if (input < min || input > max) return -ERANGE; *output = roundf(input * scale); return 0; } void constructPwleSegment(uint16_t delay, uint16_t amplitude, uint16_t frequency, uint8_t flags, uint32_t vbemfTarget = 0) { write(16, delay); write(12, amplitude); write(12, frequency); /* feature flags to control the chirp, CLAB braking, back EMF amplitude regulation */ write(8, (flags | 1) << 4); if (flags & PWLE_AMP_REG_BIT) { write(24, vbemfTarget); /* target back EMF voltage */ } } public: uint8_t *front() const { return head.get(); } uint8_t type() const { return waveformType; } size_t size() const { return bytes; } DspMemChunk(uint8_t type, size_t size) : head(new uint8_t[size]{0x00}) { waveformType = type; _current = head.get(); _max = _current + size; if (waveformType == WAVEFORM_COMPOSE) { write(8, 0); /* Padding */ write(8, 0); /* nsections placeholder */ write(8, 0); /* repeat */ } else if (waveformType == WAVEFORM_PWLE) { write(16, (PWLE_FTR_BUZZ_BIT | PWLE_FTR_DVL_BIT) << PWLE_HEADER_FTR_SHIFT); /* Feature flag */ write(8, PWLE_WT_TYPE); /* type12 */ write(24, PWLE_HEADER_WORD_COUNT); /* Header word count */ write(24, 0); /* Body word count placeholder */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) write(24, 0); /* Waveform length placeholder */ write(8, 0); /* Repeat */ write(12, 0); /* Wait time between repeats */ write(8, 0); /* nsections placeholder */ #endif } else { ALOGE("%s: Invalid type: %u", __func__, waveformType); } } int flush() { if (!_cachebits) return 0; return write(24 - _cachebits, 0); } int constructComposeSegment(uint32_t effectVolLevel, uint32_t effectIndex, uint8_t repeat, uint8_t flags, uint16_t nextEffectDelay) { if (waveformType != WAVEFORM_COMPOSE) { ALOGE("%s: Invalid type: %d", __func__, waveformType); return -EDOM; } if (effectVolLevel > 100 || effectIndex > WAVEFORM_MAX_PHYSICAL_INDEX) { ALOGE("%s: Invalid argument: %u, %u", __func__, effectVolLevel, effectIndex); return -EINVAL; } write(8, effectVolLevel); /* amplitude */ write(8, effectIndex); /* index */ write(8, repeat); /* repeat */ write(8, flags); /* flags */ write(16, nextEffectDelay); /* delay */ return 0; } int constructActiveSegment(int duration, float amplitude, float frequency, bool chirp) { uint16_t delay = 0; uint16_t amp = 0; uint16_t freq = 0; uint8_t flags = 0x0; if (waveformType != WAVEFORM_PWLE) { ALOGE("%s: Invalid type: %d", __func__, waveformType); return -EDOM; } if ((fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) || (fToU16(amplitude, &, 2048, CS40L26_PWLE_LEVEL_MIN, CS40L26_PWLE_LEVEL_MAX) < 0) || (fToU16(frequency, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ) < 0)) { ALOGE("%s: Invalid argument: %d, %f, %f", __func__, duration, amplitude, frequency); return -ERANGE; } if (chirp) { flags |= PWLE_CHIRP_BIT; } constructPwleSegment(delay, amp, freq, flags, 0 /*ignored*/); return 0; } int constructBrakingSegment(int duration, Braking brakingType) { uint16_t delay = 0; uint16_t freq = 0; uint8_t flags = 0x00; if (waveformType != WAVEFORM_PWLE) { ALOGE("%s: Invalid type: %d", __func__, waveformType); return -EDOM; } if (fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) { ALOGE("%s: Invalid argument: %d", __func__, duration); return -ERANGE; } fToU16(PWLE_FREQUENCY_MIN_HZ, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ); if (static_cast::type>(brakingType)) { flags |= PWLE_BRAKE_BIT; } constructPwleSegment(delay, 0 /*ignored*/, freq, flags, 0 /*ignored*/); return 0; } int updateWLength(uint32_t totalDuration) { uint8_t *f = front(); if (f == nullptr) { ALOGE("%s: head does not exist!", __func__); return -ENOMEM; } if (waveformType != WAVEFORM_PWLE) { ALOGE("%s: Invalid type: %d", __func__, waveformType); return -EDOM; } if (totalDuration > 0x7FFFF) { ALOGE("%s: Invalid argument: %u", __func__, totalDuration); return -EINVAL; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE; #endif totalDuration *= 8; /* Unit: 0.125 ms (since wlength played @ 8kHz). */ totalDuration |= WT_LEN_CALCD; /* Bit 23 is for WT_LEN_CALCD; Bit 22 is for WT_INDEFINITE. */ *(f + 0) = (totalDuration >> 24) & 0xFF; *(f + 1) = (totalDuration >> 16) & 0xFF; *(f + 2) = (totalDuration >> 8) & 0xFF; *(f + 3) = totalDuration & 0xFF; return 0; } int updateNSection(int segmentIdx) { uint8_t *f = front(); if (f == nullptr) { ALOGE("%s: head does not exist!", __func__); return -ENOMEM; } if (waveformType == WAVEFORM_COMPOSE) { if (segmentIdx > COMPOSE_SIZE_MAX + 1 /*1st effect may have a delay*/) { ALOGE("%s: Invalid argument: %d", __func__, segmentIdx); return -EINVAL; } *(f + 2) = (0xFF & segmentIdx); } else if (waveformType == WAVEFORM_PWLE) { if (segmentIdx > COMPOSE_PWLE_SIZE_MAX_DEFAULT) { ALOGE("%s: Invalid argument: %d", __func__, segmentIdx); return -EINVAL; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE; #endif *(f + 7) |= (0xF0 & segmentIdx) >> 4; /* Bit 4 to 7 */ *(f + 9) |= (0x0F & segmentIdx) << 4; /* Bit 3 to 0 */ } else { ALOGE("%s: Invalid type: %d", __func__, waveformType); return -EDOM; } return 0; } int updateWCount(int segmentCount) { uint8_t *f = front(); if (segmentCount > COMPOSE_SIZE_MAX + 1 /*1st effect may have a delay*/) { ALOGE("%s: Invalid argument: %d", __func__, segmentCount); return -EINVAL; } if (f == nullptr) { ALOGE("%s: head does not exist!", __func__); return -ENOMEM; } if (waveformType != WAVEFORM_PWLE) { ALOGE("%s: Invalid type: %d", __func__, waveformType); return -EDOM; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE; #endif uint32_t dataSize = segmentCount * PWLE_SEGMENT_WORD_COUNT + PWLE_HEADER_WORD_COUNT; *(f + 0) = (dataSize >> 24) & 0xFF; *(f + 1) = (dataSize >> 16) & 0xFF; *(f + 2) = (dataSize >> 8) & 0xFF; *(f + 3) = dataSize & 0xFF; return 0; } }; Vibrator::Vibrator(std::unique_ptr hwApiDefault, std::unique_ptr hwCalDefault, std::unique_ptr hwApiDual, std::unique_ptr hwCalDual, std::unique_ptr hwgpio) : mHwApiDef(std::move(hwApiDefault)), mHwCalDef(std::move(hwCalDefault)), mHwApiDual(std::move(hwApiDual)), mHwCalDual(std::move(hwCalDual)), mHwGPIO(std::move(hwgpio)), mAsyncHandle(std::async([] {})) { int32_t longFrequencyShift; std::string caldata{8, '0'}; uint32_t calVer; // ==================Single actuators and dual actuators checking ============================= if ((mHwApiDual != nullptr) && (mHwCalDual != nullptr)) mIsDual = true; // ==================INPUT Devices== Base ================= const char *inputEventName = std::getenv("INPUT_EVENT_NAME"); const char *inputEventPathName = std::getenv("INPUT_EVENT_PATH"); if ((strstr(inputEventName, "cs40l26") != nullptr) || (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) { glob_t inputEventPaths; int fd = -1; int ret; uint32_t val = 0; char str[20] = {0x00}; for (uint8_t retry = 0; retry < 10; retry++) { ret = glob(inputEventPathName, 0, nullptr, &inputEventPaths); if (ret) { ALOGE("Failed to get input event paths (%d): %s", errno, strerror(errno)); } else { for (int i = 0; i < inputEventPaths.gl_pathc; i++) { fd = TEMP_FAILURE_RETRY(open(inputEventPaths.gl_pathv[i], O_RDWR)); if (fd > 0) { if (ioctl(fd, EVIOCGBIT(0, sizeof(val)), &val) > 0 && (val & (1 << EV_FF)) && ioctl(fd, EVIOCGNAME(sizeof(str)), &str) > 0 && strstr(str, inputEventName) != nullptr) { mInputFd.reset(fd); ALOGI("Control %s through %s", inputEventName, inputEventPaths.gl_pathv[i]); break; } close(fd); } } } if (ret == 0) { globfree(&inputEventPaths); } if (mInputFd.ok()) { break; } sleep(1); ALOGW("Retry #%d to search in %zu input devices.", retry, inputEventPaths.gl_pathc); } if (!mInputFd.ok()) { ALOGE("Failed to get an input event with name %s", inputEventName); } } else { ALOGE("The input name %s is not cs40l26_input or cs40l26_dual_input", inputEventName); } // ==================INPUT Devices== Flip ================= if (mIsDual) { const char *inputEventNameDual = std::getenv("INPUT_EVENT_NAME_DUAL"); if ((strstr(inputEventNameDual, "cs40l26_dual_input") != nullptr)) { glob_t inputEventPaths; int fd = -1; int ret; uint32_t val = 0; char str[20] = {0x00}; for (uint8_t retry = 0; retry < 10; retry++) { ret = glob(inputEventPathName, 0, nullptr, &inputEventPaths); if (ret) { ALOGE("Failed to get flip's input event paths (%d): %s", errno, strerror(errno)); } else { for (int i = 0; i < inputEventPaths.gl_pathc; i++) { fd = TEMP_FAILURE_RETRY(open(inputEventPaths.gl_pathv[i], O_RDWR)); if (fd > 0) { if (ioctl(fd, EVIOCGBIT(0, sizeof(val)), &val) > 0 && (val & (1 << EV_FF)) && ioctl(fd, EVIOCGNAME(sizeof(str)), &str) > 0 && strstr(str, inputEventNameDual) != nullptr) { mInputFdDual.reset(fd); ALOGI("Control %s through %s", inputEventNameDual, inputEventPaths.gl_pathv[i]); break; } close(fd); } } } if (ret == 0) { globfree(&inputEventPaths); } if (mInputFdDual.ok()) { break; } sleep(1); ALOGW("Retry #%d to search in %zu input devices.", retry, inputEventPaths.gl_pathc); } if (!mInputFdDual.ok()) { ALOGE("Failed to get an input event with name %s", inputEventNameDual); } ALOGE("HWAPI: %s", std::getenv("HWAPI_PATH_PREFIX")); } else { ALOGE("The input name %s is not cs40l26_dual_input", inputEventNameDual); } } // ====================HAL internal effect table== Base ================================== mFfEffects.resize(WAVEFORM_MAX_INDEX); mEffectDurations.resize(WAVEFORM_MAX_INDEX); mEffectDurations = { 1000, 100, 9, 1000, 300, 130, 150, 500, 100, 5, 12, 1000, 1000, 1000, }; /* 11+3 waveforms. The duration must < UINT16_MAX */ mEffectBrakingDurations.resize(WAVEFORM_MAX_INDEX); mEffectCustomData.reserve(WAVEFORM_MAX_INDEX); uint8_t effectIndex; uint16_t numBytes = 0; for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) { if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) { /* Initialize physical waveforms. */ mEffectCustomData.push_back({RAM_WVFRM_BANK, effectIndex}); mFfEffects[effectIndex] = { .type = FF_PERIODIC, .id = -1, // Length == 0 to allow firmware control of the duration .replay.length = 0, .u.periodic.waveform = FF_CUSTOM, .u.periodic.custom_data = mEffectCustomData[effectIndex].data(), .u.periodic.custom_len = static_cast(mEffectCustomData[effectIndex].size()), }; // Bypass the waveform update due to different input name if ((strstr(inputEventName, "cs40l26") != nullptr) || (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) { // Let the firmware control the playback duration to avoid // cutting any effect that is played short if (!mHwApiDef->setFFEffect( mInputFd, &mFfEffects[effectIndex], mEffectDurations[effectIndex])) { ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno)); } } if (mFfEffects[effectIndex].id != effectIndex) { ALOGW("Unexpected effect index: %d -> %d", effectIndex, mFfEffects[effectIndex].id); } if (mHwApiDef->hasEffectBrakingTimeBank()) { mHwApiDef->setEffectBrakingTimeIndex(effectIndex); mHwApiDef->getEffectBrakingTimeMs(&mEffectBrakingDurations[effectIndex]); } } else { /* Initiate placeholders for OWT effects. */ numBytes = effectIndex == WAVEFORM_COMPOSE ? FF_CUSTOM_DATA_LEN_MAX_COMP : FF_CUSTOM_DATA_LEN_MAX_PWLE; std::vector tempVec(numBytes, 0); mEffectCustomData.push_back(std::move(tempVec)); mFfEffects[effectIndex] = { .type = FF_PERIODIC, .id = -1, .replay.length = 0, .u.periodic.waveform = FF_CUSTOM, .u.periodic.custom_data = mEffectCustomData[effectIndex].data(), .u.periodic.custom_len = 0, }; } } // ====================HAL internal effect table== Flip ================================== if (mIsDual) { mFfEffectsDual.resize(WAVEFORM_MAX_INDEX); mEffectCustomDataDual.reserve(WAVEFORM_MAX_INDEX); for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) { if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) { /* Initialize physical waveforms. */ mEffectCustomDataDual.push_back({RAM_WVFRM_BANK, effectIndex}); mFfEffectsDual[effectIndex] = { .type = FF_PERIODIC, .id = -1, // Length == 0 to allow firmware control of the duration .replay.length = 0, .u.periodic.waveform = FF_CUSTOM, .u.periodic.custom_data = mEffectCustomDataDual[effectIndex].data(), .u.periodic.custom_len = static_cast(mEffectCustomDataDual[effectIndex].size()), }; // Bypass the waveform update due to different input name if ((strstr(inputEventName, "cs40l26") != nullptr) || (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) { // Let the firmware control the playback duration to avoid // cutting any effect that is played short if (!mHwApiDual->setFFEffect( mInputFdDual, &mFfEffectsDual[effectIndex], mEffectDurations[effectIndex])) { ALOGE("Failed upload flip's effect %d (%d): %s", effectIndex, errno, strerror(errno)); } } if (mFfEffectsDual[effectIndex].id != effectIndex) { ALOGW("Unexpected effect index: %d -> %d", effectIndex, mFfEffectsDual[effectIndex].id); } } else { /* Initiate placeholders for OWT effects. */ numBytes = effectIndex == WAVEFORM_COMPOSE ? FF_CUSTOM_DATA_LEN_MAX_COMP : FF_CUSTOM_DATA_LEN_MAX_PWLE; std::vector tempVec(numBytes, 0); mEffectCustomDataDual.push_back(std::move(tempVec)); mFfEffectsDual[effectIndex] = { .type = FF_PERIODIC, .id = -1, .replay.length = 0, .u.periodic.waveform = FF_CUSTOM, .u.periodic.custom_data = mEffectCustomDataDual[effectIndex].data(), .u.periodic.custom_len = 0, }; } } } // ==============Calibration data checking====================================== if (mHwCalDef->getF0(&caldata)) { mHwApiDef->setF0(caldata); mResonantFrequency = static_cast(std::stoul(caldata, nullptr, 16)) / (1 << Q14_BIT_SHIFT); } if (mHwCalDef->getRedc(&caldata)) { mHwApiDef->setRedc(caldata); mRedc = redcToFloat(&caldata); } if (mHwCalDef->getQ(&caldata)) { mHwApiDef->setQ(caldata); } if (mHwCalDef->getF0SyncOffset(&mF0Offset)) { ALOGD("Vibrator::Vibrator: F0 offset calculated from both base and flip calibration data: " "%u", mF0Offset); } else { mHwCalDef->getLongFrequencyShift(&longFrequencyShift); if (longFrequencyShift > 0) { mF0Offset = longFrequencyShift * std::pow(2, 14); } else if (longFrequencyShift < 0) { mF0Offset = std::pow(2, 24) - std::abs(longFrequencyShift) * std::pow(2, 14); } else { mF0Offset = 0; } ALOGD("Vibrator::Vibrator: F0 offset calculated from long shift frequency: %u", mF0Offset); } if (mIsDual) { if (mHwCalDual->getF0(&caldata)) { mHwApiDual->setF0(caldata); } if (mHwCalDual->getRedc(&caldata)) { mHwApiDual->setRedc(caldata); } if (mHwCalDual->getQ(&caldata)) { mHwApiDual->setQ(caldata); } if (mHwCalDual->getF0SyncOffset(&mF0OffsetDual)) { ALOGD("Vibrator::Vibrator: Dual: F0 offset calculated from both base and flip " "calibration data: " "%u", mF0OffsetDual); } } mHwCalDef->getVersion(&calVer); if (calVer == 2) { mHwCalDef->getTickVolLevels(&(mTickEffectVol)); mHwCalDef->getClickVolLevels(&(mClickEffectVol)); mHwCalDef->getLongVolLevels(&(mLongEffectVol)); } else { ALOGW("Unsupported calibration version! Using the default calibration value"); mHwCalDef->getTickVolLevels(&(mTickEffectVol)); mHwCalDef->getClickVolLevels(&(mClickEffectVol)); mHwCalDef->getLongVolLevels(&(mLongEffectVol)); } // ================Project specific setting to driver=============================== mHwApiDef->setF0CompEnable(mHwCalDef->isF0CompEnabled()); mHwApiDef->setRedcCompEnable(mHwCalDef->isRedcCompEnabled()); mHwApiDef->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US); if (mIsDual) { mHwApiDual->setF0CompEnable(mHwCalDual->isF0CompEnabled()); mHwApiDual->setRedcCompEnable(mHwCalDual->isRedcCompEnabled()); mHwApiDual->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US); } // ===============Audio coupled haptics bool init ======== mIsUnderExternalControl = false; // =============== Compose PWLE check ===================================== mIsChirpEnabled = mHwCalDef->isChirpEnabled(); mHwCalDef->getSupportedPrimitives(&mSupportedPrimitivesBits); if (mSupportedPrimitivesBits > 0) { for (auto e : defaultSupportedPrimitives) { if (mSupportedPrimitivesBits & (1 << uint32_t(e))) { mSupportedPrimitives.emplace_back(e); } } } else { for (auto e : defaultSupportedPrimitives) { mSupportedPrimitivesBits |= (1 << uint32_t(e)); } mSupportedPrimitives = defaultSupportedPrimitives; } mPrimitiveMaxScale.resize(WAVEFORM_MAX_INDEX, 100); mPrimitiveMaxScale[WAVEFORM_CLICK_INDEX] = 95; mPrimitiveMaxScale[WAVEFORM_THUD_INDEX] = 75; mPrimitiveMaxScale[WAVEFORM_SPIN_INDEX] = 90; mPrimitiveMaxScale[WAVEFORM_LIGHT_TICK_INDEX] = 75; mPrimitiveMaxScale[WAVEFORM_LOW_TICK_INDEX] = 75; mPrimitiveMinScale.resize(WAVEFORM_MAX_INDEX, 0); mPrimitiveMinScale[WAVEFORM_CLICK_INDEX] = 1; mPrimitiveMinScale[WAVEFORM_THUD_INDEX] = 11; mPrimitiveMinScale[WAVEFORM_SPIN_INDEX] = 23; mPrimitiveMinScale[WAVEFORM_SLOW_RISE_INDEX] = 25; mPrimitiveMinScale[WAVEFORM_QUICK_FALL_INDEX] = 2; mPrimitiveMinScale[WAVEFORM_LIGHT_TICK_INDEX] = 3; mPrimitiveMinScale[WAVEFORM_LOW_TICK_INDEX] = 16; // ====== Get GPIO status and init it ================ mGPIOStatus = mHwGPIO->getGPIO(); if (!mGPIOStatus || !mHwGPIO->initGPIO()) { ALOGE("Vibrator: GPIO initialization process error"); } } ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) { ATRACE_NAME("Vibrator::getCapabilities"); int32_t ret = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK | IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY | IVibrator::CAP_GET_Q_FACTOR; if (hasHapticAlsaDevice()) { ret |= IVibrator::CAP_EXTERNAL_CONTROL; } else { ALOGE("No haptics ALSA device"); } if (mHwApiDef->hasOwtFreeSpace()) { ret |= IVibrator::CAP_COMPOSE_EFFECTS; if (mIsChirpEnabled) { ret |= IVibrator::CAP_FREQUENCY_CONTROL | IVibrator::CAP_COMPOSE_PWLE_EFFECTS; } } *_aidl_return = ret; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::off() { ATRACE_NAME("Vibrator::off"); bool ret{true}; const std::scoped_lock lock(mActiveId_mutex); if (mActiveId >= 0) { ALOGD("Off: Stop the active effect: %d", mActiveId); /* Stop the active effect. */ if (!mHwApiDef->setFFPlay(mInputFd, mActiveId, false)) { ALOGE("Off: Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno)); ret = false; } if (mIsDual && (!mHwApiDual->setFFPlay(mInputFdDual, mActiveId, false))) { ALOGE("Off: Failed to stop flip's effect %d (%d): %s", mActiveId, errno, strerror(errno)); ret = false; } if (!mHwGPIO->setGPIOOutput(false)) { ALOGE("Off: Failed to reset GPIO(%d): %s", errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } else { ALOGD("Off: Vibrator is already off"); } setGlobalAmplitude(false); if (mF0Offset) { mHwApiDef->setF0Offset(0); if (mIsDual && mF0OffsetDual) { mHwApiDual->setF0Offset(0); } } if (ret) { ALOGD("Off: Done."); mActiveId = -1; return ndk::ScopedAStatus::ok(); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs, const std::shared_ptr &callback) { ATRACE_NAME("Vibrator::on"); ALOGD("Vibrator::on"); if (timeoutMs > MAX_TIME_MS) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } const uint16_t index = (timeoutMs < WAVEFORM_LONG_VIBRATION_THRESHOLD_MS) ? WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX : WAVEFORM_LONG_VIBRATION_EFFECT_INDEX; if (MAX_COLD_START_LATENCY_MS <= MAX_TIME_MS - timeoutMs) { timeoutMs += MAX_COLD_START_LATENCY_MS; } setGlobalAmplitude(true); if (mF0Offset) { mHwApiDef->setF0Offset(mF0Offset); if (mIsDual && mF0OffsetDual) { mHwApiDual->setF0Offset(mF0OffsetDual); } } return on(timeoutMs, index, nullptr /*ignored*/, callback); } ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength, const std::shared_ptr &callback, int32_t *_aidl_return) { ATRACE_NAME("Vibrator::perform"); ALOGD("Vibrator::perform"); return performEffect(effect, strength, callback, _aidl_return); } ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector *_aidl_return) { *_aidl_return = {Effect::TEXTURE_TICK, Effect::TICK, Effect::CLICK, Effect::HEAVY_CLICK, Effect::DOUBLE_CLICK}; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) { ATRACE_NAME("Vibrator::setAmplitude"); if (amplitude <= 0.0f || amplitude > 1.0f) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } mLongEffectScale = amplitude; if (!isUnderExternalControl()) { return setGlobalAmplitude(true); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) { ATRACE_NAME("Vibrator::setExternalControl"); setGlobalAmplitude(enabled); if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) { if (!mHwApiDef->setHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) { ALOGE("Failed to %s haptic pcm device: %d", (enabled ? "enable" : "disable"), mDevice); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } else { ALOGE("No haptics ALSA device"); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } mIsUnderExternalControl = enabled; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t *maxDelayMs) { ATRACE_NAME("Vibrator::getCompositionDelayMax"); *maxDelayMs = COMPOSE_DELAY_MAX_MS; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t *maxSize) { ATRACE_NAME("Vibrator::getCompositionSizeMax"); *maxSize = COMPOSE_SIZE_MAX; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector *supported) { *supported = mSupportedPrimitives; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive, int32_t *durationMs) { ndk::ScopedAStatus status; uint32_t effectIndex; if (primitive != CompositePrimitive::NOOP) { status = getPrimitiveDetails(primitive, &effectIndex); if (!status.isOk()) { return status; } *durationMs = mEffectDurations[effectIndex] + mEffectBrakingDurations[effectIndex]; } else { *durationMs = 0; } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::compose(const std::vector &composite, const std::shared_ptr &callback) { ATRACE_NAME("Vibrator::compose"); ALOGD("Vibrator::compose"); uint16_t size; uint16_t nextEffectDelay; if (composite.size() > COMPOSE_SIZE_MAX || composite.empty()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } /* Check if there is a wait before the first effect. */ nextEffectDelay = composite.front().delayMs; if (nextEffectDelay > COMPOSE_DELAY_MAX_MS || nextEffectDelay < 0) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } else if (nextEffectDelay > 0) { size = composite.size() + 1; } else { size = composite.size(); } DspMemChunk ch(WAVEFORM_COMPOSE, FF_CUSTOM_DATA_LEN_MAX_COMP); const uint8_t header_count = ch.size(); /* Insert 1 section for a wait before the first effect. */ if (nextEffectDelay) { ch.constructComposeSegment(0 /*amplitude*/, 0 /*index*/, 0 /*repeat*/, 0 /*flags*/, nextEffectDelay /*delay*/); } for (uint32_t i_curr = 0, i_next = 1; i_curr < composite.size(); i_curr++, i_next++) { auto &e_curr = composite[i_curr]; uint32_t effectIndex = 0; uint32_t effectVolLevel = 0; float effectScale = e_curr.scale; if (effectScale < 0.0f || effectScale > 1.0f) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (e_curr.primitive != CompositePrimitive::NOOP) { ndk::ScopedAStatus status; status = getPrimitiveDetails(e_curr.primitive, &effectIndex); if (!status.isOk()) { return status; } effectVolLevel = intensityToVolLevel(effectScale, effectIndex); } /* Fetch the next composite effect delay and fill into the current section */ nextEffectDelay = 0; if (i_next < composite.size()) { auto &e_next = composite[i_next]; int32_t delay = e_next.delayMs; if (delay > COMPOSE_DELAY_MAX_MS || delay < 0) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } nextEffectDelay = delay; } if (effectIndex == 0 && nextEffectDelay == 0) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } nextEffectDelay += mEffectBrakingDurations[effectIndex]; ch.constructComposeSegment(effectVolLevel, effectIndex, 0 /*repeat*/, 0 /*flags*/, nextEffectDelay /*delay*/); } ch.flush(); if (ch.updateNSection(size) < 0) { ALOGE("%s: Failed to update the section count", __func__); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (header_count == ch.size()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } else { // Composition duration should be 0 to allow firmware to play the whole effect mFfEffects[WAVEFORM_COMPOSE].replay.length = 0; if (mIsDual) { mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = 0; } return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, &ch, callback); } } ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, const DspMemChunk *ch, const std::shared_ptr &callback) { ndk::ScopedAStatus status = ndk::ScopedAStatus::ok(); if (effectIndex >= FF_MAX_EFFECTS) { ALOGE("Invalid waveform index %d", effectIndex); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (mAsyncHandle.wait_for(ASYNC_COMPLETION_TIMEOUT) != std::future_status::ready) { ALOGE("Previous vibration pending: prev: %d, curr: %d", mActiveId, effectIndex); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } if (ch) { /* Upload OWT effect. */ if (ch->front() == nullptr) { ALOGE("Invalid OWT bank"); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (ch->type() != WAVEFORM_PWLE && ch->type() != WAVEFORM_COMPOSE) { ALOGE("Invalid OWT type"); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } effectIndex = ch->type(); uint32_t freeBytes; mHwApiDef->getOwtFreeSpace(&freeBytes); if (ch->size() > freeBytes) { ALOGE("Invalid OWT length: Effect %d: %zu > %d!", effectIndex, ch->size(), freeBytes); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (mIsDual) { mHwApiDual->getOwtFreeSpace(&freeBytes); if (ch-> size() > freeBytes) { ALOGE("Invalid OWT length in flip: Effect %d: %zu > %d!", effectIndex, ch-> size(), freeBytes); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } } int errorStatus; if (mGPIOStatus && mIsDual) { mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex; mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex; } else { ALOGD("Not dual haptics HAL and GPIO status fail"); } if (!mHwApiDef->uploadOwtEffect(mInputFd, ch->front(), ch->size(), &mFfEffects[effectIndex], &effectIndex, &errorStatus)) { ALOGE("Invalid uploadOwtEffect"); return ndk::ScopedAStatus::fromExceptionCode(errorStatus); } if (mIsDual && !mHwApiDual->uploadOwtEffect(mInputFdDual, ch->front(), ch->size(), &mFfEffectsDual[effectIndex], &effectIndex, &errorStatus)) { ALOGE("Invalid uploadOwtEffect in flip"); return ndk::ScopedAStatus::fromExceptionCode(errorStatus); } } else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX || effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) { /* Update duration for long/short vibration. */ mFfEffects[effectIndex].replay.length = static_cast(timeoutMs); if (mGPIOStatus && mIsDual) { mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex; mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex; } else { ALOGD("Not dual haptics HAL and GPIO status fail"); } if (!mHwApiDef->setFFEffect(mInputFd, &mFfEffects[effectIndex], static_cast(timeoutMs))) { ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } if (mIsDual) { mFfEffectsDual[effectIndex].replay.length = static_cast(timeoutMs); if (!mHwApiDual->setFFEffect(mInputFdDual, &mFfEffectsDual[effectIndex], static_cast(timeoutMs))) { ALOGE("Failed to edit flip's effect %d (%d): %s", effectIndex, errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } } { const std::scoped_lock lock(mActiveId_mutex); /* Play the event now. */ mActiveId = effectIndex; if (!mGPIOStatus) { ALOGE("GetVibrator: GPIO status error"); // Do playcode to play effect if (!mHwApiDef->setFFPlay(mInputFd, effectIndex, true)) { ALOGE("Failed to play effect %d (%d): %s", effectIndex, errno, strerror(errno)); mActiveId = -1; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } if (mIsDual && !mHwApiDual->setFFPlay(mInputFdDual, effectIndex, true)) { ALOGE("Failed to play flip's effect %d (%d): %s", effectIndex, errno, strerror(errno)); mActiveId = -1; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } else { // Using GPIO to play effect if ((effectIndex == WAVEFORM_CLICK_INDEX || effectIndex == WAVEFORM_LIGHT_TICK_INDEX)) { mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex; if (!mHwApiDef->setFFEffect(mInputFd, &mFfEffects[effectIndex], mFfEffects[effectIndex].replay.length)) { ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } if (mIsDual) { mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex; if (!mHwApiDual->setFFEffect(mInputFdDual, &mFfEffectsDual[effectIndex], mFfEffectsDual[effectIndex].replay.length)) { ALOGE("Failed to edit flip's effect %d (%d): %s", effectIndex, errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } } if (!mHwGPIO->setGPIOOutput(true)) { ALOGE("Failed to trigger effect %d (%d) by GPIO: %s", effectIndex, errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } } mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback); ALOGD("Vibrator::on, set done."); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum) { uint16_t scale = amplitudeToScale(amplitude, maximum); if (!mHwApiDef->setFFGain(mInputFd, scale)) { ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } if (mIsDual) { if (!mHwApiDual->setFFGain(mInputFdDual, scale)) { ALOGE("Failed to set flip's gain to %u (%d): %s", scale, errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::setGlobalAmplitude(bool set) { uint8_t amplitude = set ? roundf(mLongEffectScale * mLongEffectVol[1]) : VOLTAGE_SCALE_MAX; if (!set) { mLongEffectScale = 1.0; // Reset the scale for the later new effect. } return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX); } ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector * /*_aidl_return*/) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t /*id*/, Effect /*effect*/, EffectStrength /*strength*/) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t /*id*/) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) { *resonantFreqHz = mResonantFrequency; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) { std::string caldata{8, '0'}; if (!mHwCalDef->getQ(&caldata)) { ALOGE("Failed to get q factor (%d): %s", errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } *qFactor = static_cast(std::stoul(caldata, nullptr, 16)) / (1 << Q16_BIT_SHIFT); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) { int32_t capabilities; Vibrator::getCapabilities(&capabilities); if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) { *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ; return ndk::ScopedAStatus::ok(); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) { int32_t capabilities; Vibrator::getCapabilities(&capabilities); if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) { *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ; return ndk::ScopedAStatus::ok(); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector *_aidl_return) { // TODO(b/170919640): complete implementation int32_t capabilities; Vibrator::getCapabilities(&capabilities); if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) { std::vector bandwidthAmplitudeMap(PWLE_BW_MAP_SIZE, 1.0); *_aidl_return = bandwidthAmplitudeMap; return ndk::ScopedAStatus::ok(); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) { int32_t capabilities; Vibrator::getCapabilities(&capabilities); if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) { *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS; return ndk::ScopedAStatus::ok(); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) { int32_t capabilities; Vibrator::getCapabilities(&capabilities); if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) { *maxSize = COMPOSE_PWLE_SIZE_MAX_DEFAULT; return ndk::ScopedAStatus::ok(); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector *supported) { int32_t capabilities; Vibrator::getCapabilities(&capabilities); if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) { *supported = { Braking::NONE, }; return ndk::ScopedAStatus::ok(); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } static void resetPreviousEndAmplitudeEndFrequency(float *prevEndAmplitude, float *prevEndFrequency) { const float reset = -1.0; *prevEndAmplitude = reset; *prevEndFrequency = reset; } static void incrementIndex(int *index) { *index += 1; } ndk::ScopedAStatus Vibrator::composePwle(const std::vector &composite, const std::shared_ptr &callback) { ATRACE_NAME("Vibrator::composePwle"); int32_t capabilities; Vibrator::getCapabilities(&capabilities); if ((capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) == 0) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } if (composite.empty() || composite.size() > COMPOSE_PWLE_SIZE_MAX_DEFAULT) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } std::vector supported; Vibrator::getSupportedBraking(&supported); bool isClabSupported = std::find(supported.begin(), supported.end(), Braking::CLAB) != supported.end(); int segmentIdx = 0; uint32_t totalDuration = 0; float prevEndAmplitude; float prevEndFrequency; resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency); DspMemChunk ch(WAVEFORM_PWLE, FF_CUSTOM_DATA_LEN_MAX_PWLE); bool chirp = false; for (auto &e : composite) { switch (e.getTag()) { case PrimitivePwle::active: { auto active = e.get(); if (active.duration < 0 || active.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (active.startAmplitude < PWLE_LEVEL_MIN || active.startAmplitude > PWLE_LEVEL_MAX || active.endAmplitude < PWLE_LEVEL_MIN || active.endAmplitude > PWLE_LEVEL_MAX) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (active.startAmplitude > CS40L26_PWLE_LEVEL_MAX) { active.startAmplitude = CS40L26_PWLE_LEVEL_MAX; } if (active.endAmplitude > CS40L26_PWLE_LEVEL_MAX) { active.endAmplitude = CS40L26_PWLE_LEVEL_MAX; } if (active.startFrequency < PWLE_FREQUENCY_MIN_HZ || active.startFrequency > PWLE_FREQUENCY_MAX_HZ || active.endFrequency < PWLE_FREQUENCY_MIN_HZ || active.endFrequency > PWLE_FREQUENCY_MAX_HZ) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (!((active.startAmplitude == prevEndAmplitude) && (active.startFrequency == prevEndFrequency))) { if (ch.constructActiveSegment(0, active.startAmplitude, active.startFrequency, false) < 0) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } incrementIndex(&segmentIdx); } if (active.startFrequency != active.endFrequency) { chirp = true; } if (ch.constructActiveSegment(active.duration, active.endAmplitude, active.endFrequency, chirp) < 0) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } incrementIndex(&segmentIdx); prevEndAmplitude = active.endAmplitude; prevEndFrequency = active.endFrequency; totalDuration += active.duration; chirp = false; break; } case PrimitivePwle::braking: { auto braking = e.get(); if (braking.braking > Braking::CLAB) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } else if (!isClabSupported && (braking.braking == Braking::CLAB)) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (ch.constructBrakingSegment(0, braking.braking) < 0) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } incrementIndex(&segmentIdx); if (ch.constructBrakingSegment(braking.duration, braking.braking) < 0) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } incrementIndex(&segmentIdx); resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency); totalDuration += braking.duration; break; } } if (segmentIdx > COMPOSE_PWLE_SIZE_MAX_DEFAULT) { ALOGE("Too many PrimitivePwle section!"); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } } ch.flush(); /* Update wlength */ totalDuration += MAX_COLD_START_LATENCY_MS; if (totalDuration > 0x7FFFF) { ALOGE("Total duration is too long (%d)!", totalDuration); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } /* Update word count */ if (ch.updateWCount(segmentIdx) < 0) { ALOGE("%s: Failed to update the waveform word count", __func__); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } /* Update waveform length */ if (ch.updateWLength(totalDuration) < 0) { ALOGE("%s: Failed to update the waveform length length", __func__); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } /* Update nsections */ if (ch.updateNSection(segmentIdx) < 0) { ALOGE("%s: Failed to update the section count", __func__); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, &ch, callback); } bool Vibrator::isUnderExternalControl() { return mIsUnderExternalControl; } // BnCInterface APIs binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) { if (fd < 0) { ALOGE("Called debug() with invalid fd."); return STATUS_OK; } (void)args; (void)numArgs; dprintf(fd, "AIDL:\n"); dprintf(fd, " Active Effect ID: %" PRId32 "\n", mActiveId); dprintf(fd, " F0: %.02f\n", mResonantFrequency); dprintf(fd, " F0 Offset: base: %" PRIu32 " flip: %" PRIu32 "\n", mF0Offset, mF0OffsetDual); dprintf(fd, " Redc: %.02f\n", mRedc); dprintf(fd, " Voltage Levels:\n"); dprintf(fd, " Tick Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mTickEffectVol[0], mTickEffectVol[1]); dprintf(fd, " Click Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mClickEffectVol[0], mClickEffectVol[1]); dprintf(fd, " Long Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mLongEffectVol[0], mLongEffectVol[1]); uint8_t effectId; dprintf(fd, " Scales\n"); dprintf(fd, "\tId\tMinScale\tMaxScale\n"); for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) { dprintf(fd, "\t%d\t%d\t\t%d\n", effectId, mPrimitiveMinScale[effectId], mPrimitiveMaxScale[effectId]); } dprintf(fd, " Base FF effect:\n"); dprintf(fd, " Physical waveform:\n"); dprintf(fd, "\tId\tIndex\tt ->\tt'\tBrake\ttrigger button\n"); for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) { dprintf(fd, "\t%d\t%d\t%d\t%d\t%d\t%X\n", mFfEffects[effectId].id, mFfEffects[effectId].u.periodic.custom_data[1], mEffectDurations[effectId], mFfEffects[effectId].replay.length, mEffectBrakingDurations[effectId], mFfEffects[effectId].trigger.button); } dprintf(fd, " OWT waveform:\n"); dprintf(fd, "\tId\tBytes\tData\tt\ttrigger button\n"); for (effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX; effectId++) { uint32_t numBytes = mFfEffects[effectId].u.periodic.custom_len * 2; std::stringstream ss; ss << " "; for (int i = 0; i < numBytes; i++) { ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (uint16_t)(*( reinterpret_cast(mFfEffects[effectId].u.periodic.custom_data) + i)) << " "; } dprintf(fd, "\t%d\t%d\t{%s}\t%u\t%X\n", mFfEffects[effectId].id, numBytes, ss.str().c_str(), mFfEffectsDual[effectId].replay.length, mFfEffects[effectId].trigger.button); } if (mIsDual) { dprintf(fd, " Flip FF effect:\n"); dprintf(fd, " Physical waveform:\n"); dprintf(fd, "\tId\tIndex\tt ->\tt'\tBrake\ttrigger button\n"); for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) { dprintf(fd, "\t%d\t%d\t%d\t%d\t%d\t%X\n", mFfEffectsDual[effectId].id, mFfEffectsDual[effectId].u.periodic.custom_data[1], mEffectDurations[effectId], mFfEffectsDual[effectId].replay.length, mEffectBrakingDurations[effectId], mFfEffectsDual[effectId].trigger.button); } dprintf(fd, " OWT waveform:\n"); dprintf(fd, "\tId\tBytes\tData\tt\ttrigger button\n"); for (effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX; effectId++) { uint32_t numBytes = mFfEffectsDual[effectId].u.periodic.custom_len * 2; std::stringstream ss; ss << " "; for (int i = 0; i < numBytes; i++) { ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (uint16_t)(*(reinterpret_cast( mFfEffectsDual[effectId].u.periodic.custom_data) + i)) << " "; } dprintf(fd, "\t%d\t%d\t{%s}\t%u\t%X\n", mFfEffectsDual[effectId].id, numBytes, ss.str().c_str(), mFfEffectsDual[effectId].replay.length, mFfEffectsDual[effectId].trigger.button); } } dprintf(fd, "\n"); dprintf(fd, "Versions:\n"); const std::vector> moduleFolderNames = { {"cs40l26_core", "Haptics"}, {"cl_dsp_core", "DSP"}}; const std::string firmwareFolder = "/vendor/firmware/"; const std::string waveformName = "cs40l26.bin"; const std::array firmwareFileNames = {"cs40l26.wmfw", "cs40l26-calib.wmfw"}; const std::array tuningFileNames = {"cs40l26-svc.bin", "cs40l26-calib.bin", "cs40l26-dvl.bin", "cs40l26-dbc.bin"}; std::ifstream verFile; const auto verBinFileMode = std::ifstream::in | std::ifstream::binary; std::string ver; for (const auto &[folder, logTag] : moduleFolderNames) { verFile.open("/sys/module/" + folder + "/version"); if (verFile.is_open()) { getline(verFile, ver); dprintf(fd, " %s Driver: %s\n", logTag.c_str(), ver.c_str()); verFile.close(); } } for (auto &name : firmwareFileNames) { verFile.open(firmwareFolder + name, verBinFileMode); if (verFile.is_open()) { verFile.seekg(113); dprintf(fd, " %s: %d.%d.%d\n", name.c_str(), verFile.get(), verFile.get(), verFile.get()); verFile.close(); } } verFile.open(firmwareFolder + waveformName, verBinFileMode); if (verFile.is_open()) { while (getline(verFile, ver)) { auto pos = ver.find("Date: "); if (pos != std::string::npos) { ver = ver.substr(pos + 6, pos + 15); dprintf(fd, " %s: %s\n", waveformName.c_str(), ver.c_str()); break; } } verFile.close(); } for (auto &name : tuningFileNames) { verFile.open(firmwareFolder + name, verBinFileMode); if (verFile.is_open()) { verFile.seekg(36); getline(verFile, ver); ver = ver.substr(0, ver.find(".bin") + 4); ver = ver.substr(ver.rfind('\\') + 1); dprintf(fd, " %s: %s\n", name.c_str(), ver.c_str()); verFile.close(); } } dprintf(fd, "\n"); mHwApiDef->debug(fd); dprintf(fd, "\n"); mHwCalDef->debug(fd); dprintf(fd, "\n"); if (mIsDual) { mHwApiDual->debug(fd); dprintf(fd, "\n"); mHwCalDual->debug(fd); } fsync(fd); return STATUS_OK; } bool Vibrator::hasHapticAlsaDevice() { // We need to call findHapticAlsaDevice once only. Calling in the // constructor is too early in the boot process and the pcm file contents // are empty. Hence we make the call here once only right before we need to. if (!mConfigHapticAlsaDeviceDone) { if (mHwApiDef->getHapticAlsaDevice(&mCard, &mDevice)) { mHasHapticAlsaDevice = true; mConfigHapticAlsaDeviceDone = true; } else { ALOGE("Haptic ALSA device not supported"); } } else { ALOGD("Haptic ALSA device configuration done."); } return mHasHapticAlsaDevice; } ndk::ScopedAStatus Vibrator::getSimpleDetails(Effect effect, EffectStrength strength, uint32_t *outEffectIndex, uint32_t *outTimeMs, uint32_t *outVolLevel) { uint32_t effectIndex; uint32_t timeMs; float intensity; uint32_t volLevel; switch (strength) { case EffectStrength::LIGHT: intensity = 0.5f; break; case EffectStrength::MEDIUM: intensity = 0.7f; break; case EffectStrength::STRONG: intensity = 1.0f; break; default: return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } switch (effect) { case Effect::TEXTURE_TICK: effectIndex = WAVEFORM_LIGHT_TICK_INDEX; intensity *= 0.5f; break; case Effect::TICK: effectIndex = WAVEFORM_CLICK_INDEX; intensity *= 0.5f; break; case Effect::CLICK: effectIndex = WAVEFORM_CLICK_INDEX; intensity *= 0.7f; break; case Effect::HEAVY_CLICK: effectIndex = WAVEFORM_CLICK_INDEX; intensity *= 1.0f; break; default: return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } volLevel = intensityToVolLevel(intensity, effectIndex); timeMs = mEffectDurations[effectIndex] + MAX_COLD_START_LATENCY_MS; *outEffectIndex = effectIndex; *outTimeMs = timeMs; *outVolLevel = volLevel; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength strength, uint32_t *outTimeMs, DspMemChunk *outCh) { ndk::ScopedAStatus status; uint32_t timeMs = 0; uint32_t thisEffectIndex; uint32_t thisTimeMs; uint32_t thisVolLevel; switch (effect) { case Effect::DOUBLE_CLICK: status = getSimpleDetails(Effect::CLICK, strength, &thisEffectIndex, &thisTimeMs, &thisVolLevel); if (!status.isOk()) { return status; } timeMs += thisTimeMs; outCh->constructComposeSegment(thisVolLevel, thisEffectIndex, 0 /*repeat*/, 0 /*flags*/, WAVEFORM_DOUBLE_CLICK_SILENCE_MS); timeMs += WAVEFORM_DOUBLE_CLICK_SILENCE_MS + MAX_PAUSE_TIMING_ERROR_MS; status = getSimpleDetails(Effect::HEAVY_CLICK, strength, &thisEffectIndex, &thisTimeMs, &thisVolLevel); if (!status.isOk()) { return status; } timeMs += thisTimeMs; outCh->constructComposeSegment(thisVolLevel, thisEffectIndex, 0 /*repeat*/, 0 /*flags*/, 0 /*delay*/); outCh->flush(); if (outCh->updateNSection(2) < 0) { ALOGE("%s: Failed to update the section count", __func__); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } break; default: return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } *outTimeMs = timeMs; // Compositions should have 0 duration mFfEffects[WAVEFORM_COMPOSE].replay.length = 0; if (mIsDual) { mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = 0; } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getPrimitiveDetails(CompositePrimitive primitive, uint32_t *outEffectIndex) { uint32_t effectIndex; uint32_t primitiveBit = 1 << int32_t(primitive); if ((primitiveBit & mSupportedPrimitivesBits) == 0x0) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } switch (primitive) { case CompositePrimitive::NOOP: return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); case CompositePrimitive::CLICK: effectIndex = WAVEFORM_CLICK_INDEX; break; case CompositePrimitive::THUD: effectIndex = WAVEFORM_THUD_INDEX; break; case CompositePrimitive::SPIN: effectIndex = WAVEFORM_SPIN_INDEX; break; case CompositePrimitive::QUICK_RISE: effectIndex = WAVEFORM_QUICK_RISE_INDEX; break; case CompositePrimitive::SLOW_RISE: effectIndex = WAVEFORM_SLOW_RISE_INDEX; break; case CompositePrimitive::QUICK_FALL: effectIndex = WAVEFORM_QUICK_FALL_INDEX; break; case CompositePrimitive::LIGHT_TICK: effectIndex = WAVEFORM_LIGHT_TICK_INDEX; break; case CompositePrimitive::LOW_TICK: effectIndex = WAVEFORM_LOW_TICK_INDEX; break; default: return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } *outEffectIndex = effectIndex; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strength, const std::shared_ptr &callback, int32_t *outTimeMs) { ndk::ScopedAStatus status; uint32_t effectIndex; uint32_t timeMs = 0; uint32_t volLevel; std::optional maybeCh; switch (effect) { case Effect::TEXTURE_TICK: // fall-through case Effect::TICK: // fall-through case Effect::CLICK: // fall-through case Effect::HEAVY_CLICK: status = getSimpleDetails(effect, strength, &effectIndex, &timeMs, &volLevel); break; case Effect::DOUBLE_CLICK: maybeCh.emplace(WAVEFORM_COMPOSE, FF_CUSTOM_DATA_LEN_MAX_COMP); status = getCompoundDetails(effect, strength, &timeMs, &*maybeCh); volLevel = VOLTAGE_SCALE_MAX; break; default: status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); break; } if (status.isOk()) { DspMemChunk *ch = maybeCh ? &*maybeCh : nullptr; status = performEffect(effectIndex, volLevel, ch, callback); } *outTimeMs = timeMs; return status; } ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel, const DspMemChunk *ch, const std::shared_ptr &callback) { setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX); return on(MAX_TIME_MS, effectIndex, ch, callback); } void Vibrator::waitForComplete(std::shared_ptr &&callback) { ALOGD("waitForComplete: Callback status in waitForComplete(): callBack: %d", (callback != nullptr)); // Bypass checking flip part's haptic state if (!mHwApiDef->pollVibeState(VIBE_STATE_HAPTIC, POLLING_TIMEOUT)) { ALOGD("Failed to get state \"Haptic\""); } mHwApiDef->pollVibeState(VIBE_STATE_STOPPED); // Check flip's state after base was done if (mIsDual) { mHwApiDual->pollVibeState(VIBE_STATE_STOPPED); } ALOGD("waitForComplete: get STOP"); { const std::scoped_lock lock(mActiveId_mutex); if (mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) { if (!mHwApiDef->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects)) { ALOGE("Failed to clean up the composed effect %d", mActiveId); } if (mIsDual && (!mHwApiDual->eraseOwtEffect(mInputFdDual, mActiveId, &mFfEffectsDual))) { ALOGE("Failed to clean up flip's composed effect %d", mActiveId); } } else { ALOGD("waitForComplete: Vibrator is already off"); } mActiveId = -1; if (mGPIOStatus && !mHwGPIO->setGPIOOutput(false)) { ALOGE("waitForComplete: Failed to reset GPIO(%d): %s", errno, strerror(errno)); } // Do waveform number checking uint32_t effectCount = WAVEFORM_MAX_PHYSICAL_INDEX; mHwApiDef->getEffectCount(&effectCount); if (effectCount > WAVEFORM_MAX_PHYSICAL_INDEX) { // Forcibly clean all OWT waveforms if (!mHwApiDef->eraseOwtEffect(mInputFd, WAVEFORM_MAX_INDEX, &mFfEffects)) { ALOGE("Failed to clean up all base's composed effect"); } } if (mIsDual) { // Forcibly clean all OWT waveforms effectCount = WAVEFORM_MAX_PHYSICAL_INDEX; mHwApiDual->getEffectCount(&effectCount); if ((effectCount > WAVEFORM_MAX_PHYSICAL_INDEX) && (!mHwApiDual->eraseOwtEffect(mInputFdDual, WAVEFORM_MAX_INDEX, &mFfEffectsDual))) { ALOGE("Failed to clean up all flip's composed effect"); } } } if (callback) { auto ret = callback->onComplete(); if (!ret.isOk()) { ALOGE("Failed completion callback: %d", ret.getExceptionCode()); } } ALOGD("waitForComplete: Done."); } uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) { uint32_t volLevel; auto calc = [](float intst, std::array v) -> uint32_t { return std::lround(intst * (v[1] - v[0])) + v[0]; }; switch (effectIndex) { case WAVEFORM_LIGHT_TICK_INDEX: volLevel = calc(intensity, mTickEffectVol); break; case WAVEFORM_QUICK_RISE_INDEX: // fall-through case WAVEFORM_QUICK_FALL_INDEX: volLevel = calc(intensity, mLongEffectVol); break; case WAVEFORM_CLICK_INDEX: // fall-through case WAVEFORM_THUD_INDEX: // fall-through case WAVEFORM_SPIN_INDEX: // fall-through case WAVEFORM_SLOW_RISE_INDEX: // fall-through default: volLevel = calc(intensity, mClickEffectVol); break; } // The waveform being played must fall within the allowable scale range if (effectIndex < WAVEFORM_MAX_INDEX) { if (volLevel > mPrimitiveMaxScale[effectIndex]) { volLevel = mPrimitiveMaxScale[effectIndex]; } if (volLevel < mPrimitiveMinScale[effectIndex]) { volLevel = mPrimitiveMinScale[effectIndex]; } } return volLevel; } } // namespace vibrator } // namespace hardware } // namespace android } // namespace aidl