/* * 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 #include #include #include "DspMemChunk.h" #include "Stats.h" #include "Trace.h" #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0])) #endif namespace vibrator_aconfig_flags = vendor::vibrator::hal::flags; namespace aidl { namespace android { namespace hardware { namespace vibrator { #ifdef VIBRATOR_TRACE /* Function Trace */ #define VFTRACE(...) \ ATRACE_NAME(StringPrintf("Vibrator::%s", __func__).c_str()); \ auto f_trace_ = std::make_unique("Vibrator", __func__); \ __VA_OPT__(f_trace_->addParameter(PREPEND_EACH_ARG_WITH_NAME(__VA_ARGS__))); \ f_trace_->save() /* Effect Trace */ #define VETRACE(i, s, d, ch) \ auto e_trace_ = std::make_unique(i, s, d, ch); \ e_trace_->save() #else #define VFTRACE(...) ATRACE_NAME(StringPrintf("Vibrator::%s", __func__).c_str()) #define VETRACE(...) #endif 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 uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time 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; // POLLING_TIMEOUT < ASYNC_COMPLETION_TIMEOUT static constexpr int32_t COMPOSE_DELAY_MAX_MS = 10000; // 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 float PWLE_LEVEL_MIN = 0.0; static constexpr float PWLE_LEVEL_MAX = 1.0; static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.00; static constexpr float RESONANT_FREQUENCY_DEFAULT = 145.0f; static constexpr float PWLE_BW_MAP_SIZE = 1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ); 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 // Discrete points of frequency:max_level pairs around resonant(145Hz default) frequency // Initialize the actuator LUXSHARE_ICT_081545 limits to 0.447 and others 1.0 #if defined(LUXSHARE_ICT_081545) static std::map discretePwleMaxLevels = { {120.0, 0.447}, {130.0, 0.346}, {140.0, 0.156}, {145.0, 0.1}, {150.0, 0.167}, {160.0, 0.391}, {170.0, 0.447}}; std::vector pwleMaxLevelLimitMap(PWLE_BW_MAP_SIZE, 0.447); #else static std::map discretePwleMaxLevels = {}; std::vector pwleMaxLevelLimitMap(PWLE_BW_MAP_SIZE, 1.0); #endif static float redcToFloat(std::string *caldata) { return static_cast(std::stoul(*caldata, nullptr, 16)) / (1 << Q15_BIT_SHIFT); } Vibrator::Vibrator(std::unique_ptr hwapi, std::unique_ptr hwcal, std::unique_ptr statsapi) : mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)), mStatsApi(std::move(statsapi)), mAsyncHandle(std::async([] {})) { int32_t longFrequencyShift; std::string caldata{8, '0'}; uint32_t calVer; const std::string INPUT_EVENT_NAME = std::getenv("INPUT_EVENT_NAME") ?: ""; mFfEffects.resize(WAVEFORM_MAX_INDEX); mEffectDurations.resize(WAVEFORM_MAX_INDEX); mEffectDurations = { #if defined(UNSPECIFIED_ACTUATOR) /* For Z-LRA actuators */ 1000, 100, 25, 1000, 300, 133, 150, 500, 100, 6, 12, 1000, 13, 5, #else 1000, 100, 12, 1000, 300, 133, 150, 500, 100, 5, 12, 1000, 13, 5, #endif }; /* 11+3 waveforms. The duration must < UINT16_MAX */ 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 (INPUT_EVENT_NAME.find("cs40l26") != std::string::npos) { // Let the firmware control the playback duration to avoid // cutting any effect that is played short if (!mHwApi->setFFEffect(&mFfEffects[effectIndex], mEffectDurations[effectIndex])) { mStatsApi->logError(kHwApiError); 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); } } 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, }; } } if (mHwCal->getF0(&caldata)) { mHwApi->setF0(caldata); mResonantFrequency = static_cast(std::stoul(caldata, nullptr, 16)) / (1 << Q14_BIT_SHIFT); } else { mStatsApi->logError(kHwCalError); ALOGE("Failed to get resonant frequency (%d): %s, using default resonant HZ: %f", errno, strerror(errno), RESONANT_FREQUENCY_DEFAULT); mResonantFrequency = RESONANT_FREQUENCY_DEFAULT; } if (mHwCal->getRedc(&caldata)) { mHwApi->setRedc(caldata); mRedc = redcToFloat(&caldata); } if (mHwCal->getQ(&caldata)) { mHwApi->setQ(caldata); } mHwCal->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; } mHwCal->getVersion(&calVer); if (calVer == 2) { mHwCal->getTickVolLevels(&mTickEffectVol); mHwCal->getClickVolLevels(&mClickEffectVol); mHwCal->getLongVolLevels(&mLongEffectVol); } else { ALOGD("Unsupported calibration version: %u!", calVer); } mHwApi->setF0CompEnable(mHwCal->isF0CompEnabled()); mHwApi->setRedcCompEnable(mHwCal->isRedcCompEnabled()); mHasPassthroughHapticDevice = mHwApi->isPassthroughI2sHapticSupported(); mIsUnderExternalControl = false; mIsChirpEnabled = mHwCal->isChirpEnabled(); mHwCal->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; } mHwApi->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US); createPwleMaxLevelLimitMap(); createBandwidthAmplitudeMap(); // We need to do this until it's supported through WISCE mHwApi->enableDbc(); #ifdef ADAPTIVE_HAPTICS_V1 updateContext(); #endif /*ADAPTIVE_HAPTICS_V1*/ } ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) { VFTRACE(_aidl_return); 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 (mHasPassthroughHapticDevice || hasHapticAlsaDevice()) { ret |= IVibrator::CAP_EXTERNAL_CONTROL; } else { mStatsApi->logError(kAlsaFailError); ALOGE("No haptics ALSA device"); } if (mHwApi->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() { VFTRACE(); bool ret{true}; const std::scoped_lock lock(mActiveId_mutex); const auto startTime = std::chrono::system_clock::now(); const auto endTime = startTime + std::chrono::milliseconds(POLLING_TIMEOUT); auto now = startTime; while (halState == ISSUED && now <= endTime) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); now = std::chrono::system_clock::now(); } if (halState == ISSUED && now > endTime) { ALOGE("Timeout waiting for the actuator activation! (%d ms)", POLLING_TIMEOUT); } else if (halState == PLAYING) { ALOGD("Took %lld ms to wait for the actuator activation.", std::chrono::duration_cast(now - startTime).count()); } if (mActiveId >= 0) { /* Stop the active effect. */ if (!mHwApi->setFFPlay(mActiveId, false)) { mStatsApi->logError(kHwApiError); ALOGE("Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno)); ret = false; } halState = STOPPED; if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) && (!mHwApi->eraseOwtEffect(mActiveId, &mFfEffects))) { mStatsApi->logError(kHwApiError); ALOGE("Failed to clean up the composed effect %d", mActiveId); ret = false; } } else { ALOGV("Vibrator is already off"); } mActiveId = -1; if (mF0Offset) { mHwApi->setF0Offset(0); } halState = RESTORED; if (ret) { return ndk::ScopedAStatus::ok(); } else { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs, const std::shared_ptr &callback) { VFTRACE(timeoutMs, callback); mStatsApi->logLatencyStart(kWaveformEffectLatency); if (timeoutMs > MAX_TIME_MS) { mStatsApi->logError(kBadTimeoutError); 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; } if (mF0Offset) { mHwApi->setF0Offset(mF0Offset); } mStatsApi->logWaveform(index, timeoutMs); return on(timeoutMs, index, nullptr /*ignored*/, callback); } ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength, const std::shared_ptr &callback, int32_t *_aidl_return) { VFTRACE(effect, strength, callback, _aidl_return); mStatsApi->logLatencyStart(kPrebakedEffectLatency); return performEffect(effect, strength, callback, _aidl_return); } ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector *_aidl_return) { VFTRACE(_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) { VFTRACE(amplitude); if (amplitude <= 0.0f || amplitude > 1.0f) { mStatsApi->logError(kBadAmplitudeError); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (!isUnderExternalControl()) { mGlobalAmplitude = amplitude; auto volLevel = intensityToVolLevel(mGlobalAmplitude, WAVEFORM_LONG_VIBRATION_EFFECT_INDEX); return setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX, true); } else { mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) { VFTRACE(enabled); if (enabled) { setEffectAmplitude(VOLTAGE_SCALE_MAX, VOLTAGE_SCALE_MAX, enabled); } if (!mHasPassthroughHapticDevice) { if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) { if (!mHwApi->setHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) { mStatsApi->logError(kHwApiError); ALOGE("Failed to %s haptic pcm device: %d", (enabled ? "enable" : "disable"), mDevice); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } else { mStatsApi->logError(kAlsaFailError); 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) { VFTRACE(maxDelayMs); *maxDelayMs = COMPOSE_DELAY_MAX_MS; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t *maxSize) { VFTRACE(maxSize); *maxSize = COMPOSE_SIZE_MAX; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector *supported) { VFTRACE(supported); *supported = mSupportedPrimitives; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive, int32_t *durationMs) { VFTRACE(primitive, durationMs); ndk::ScopedAStatus status; uint32_t effectIndex; if (primitive != CompositePrimitive::NOOP) { status = getPrimitiveDetails(primitive, &effectIndex); if (!status.isOk()) { return status; } *durationMs = mEffectDurations[effectIndex]; } else { *durationMs = 0; } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::compose(const std::vector &composite, const std::shared_ptr &callback) { VFTRACE(composite, callback); uint16_t size; uint16_t nextEffectDelay; uint16_t totalDuration = 0; mStatsApi->logLatencyStart(kCompositionEffectLatency); if (composite.size() > COMPOSE_SIZE_MAX || composite.empty()) { ALOGE("%s: Invalid size", __func__); mStatsApi->logError(kBadCompositeError); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } /* Check if there is a wait before the first effect. */ nextEffectDelay = composite.front().delayMs; totalDuration += nextEffectDelay; if (nextEffectDelay > COMPOSE_DELAY_MAX_MS || nextEffectDelay < 0) { ALOGE("%s: Invalid delay %u", __func__, nextEffectDelay); mStatsApi->logError(kBadCompositeError); 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; if (e_curr.scale < 0.0f || e_curr.scale > 1.0f) { ALOGE("%s: #%u: Invalid scale %f", __func__, i_curr, e_curr.scale); mStatsApi->logError(kBadCompositeError); 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(e_curr.scale, effectIndex); totalDuration += mEffectDurations[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) { ALOGE("%s: #%u: Invalid delay %d", __func__, i_next, delay); mStatsApi->logError(kBadCompositeError); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } nextEffectDelay = delay; totalDuration += delay; } if (effectIndex == 0 && nextEffectDelay == 0) { ALOGE("%s: #%u: Invalid results", __func__, i_curr); mStatsApi->logError(kBadCompositeError); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } mStatsApi->logPrimitive(effectIndex); ch.constructComposeSegment(effectVolLevel, effectIndex, 0 /*repeat*/, 0 /*flags*/, nextEffectDelay /*delay*/); } ch.flush(); if (ch.updateNSection(size) < 0) { mStatsApi->logError(kComposeFailError); ALOGE("%s: Failed to update the section count", __func__); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (header_count == ch.size()) { ALOGE("%s: Failed to append effects", __func__); mStatsApi->logError(kComposeFailError); 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; 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) { VFTRACE(timeoutMs, effectIndex, ch, callback); ndk::ScopedAStatus status = ndk::ScopedAStatus::ok(); if (effectIndex >= FF_MAX_EFFECTS) { mStatsApi->logError(kBadEffectError); ALOGE("Invalid waveform index %d", effectIndex); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (mAsyncHandle.wait_for(ASYNC_COMPLETION_TIMEOUT) != std::future_status::ready) { mStatsApi->logError(kAsyncFailError); 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) { mStatsApi->logError(kBadCompositeError); ALOGE("Invalid OWT bank"); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (ch->type() != WAVEFORM_PWLE && ch->type() != WAVEFORM_COMPOSE) { mStatsApi->logError(kBadCompositeError); ALOGE("Invalid OWT type"); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } effectIndex = ch->type(); uint32_t freeBytes; mHwApi->getOwtFreeSpace(&freeBytes); if (ch->size() > freeBytes) { mStatsApi->logError(kBadCompositeError); ALOGE("Invalid OWT length: Effect %d: %zu > %d!", effectIndex, ch->size(), freeBytes); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } int errorStatus; if (!mHwApi->uploadOwtEffect(ch->front(), ch->size(), &mFfEffects[effectIndex], &effectIndex, &errorStatus)) { mStatsApi->logError(kHwApiError); ALOGE("Invalid uploadOwtEffect"); 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. */ // We can pass in the timeout for long/short vibration effects mFfEffects[effectIndex].replay.length = static_cast(timeoutMs); if (!mHwApi->setFFEffect(&mFfEffects[effectIndex], static_cast(timeoutMs))) { mStatsApi->logError(kHwApiError); ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } } const std::scoped_lock lock(mActiveId_mutex); mActiveId = effectIndex; /* Play the event now. */ VETRACE(effectIndex, mGlobalAmplitude, timeoutMs, ch); mStatsApi->logLatencyEnd(); if (!mHwApi->setFFPlay(effectIndex, true)) { mStatsApi->logError(kHwApiError); ALOGE("Failed to play effect %d (%d): %s", effectIndex, errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } halState = ISSUED; mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback); return ndk::ScopedAStatus::ok(); } uint16_t Vibrator::amplitudeToScale(float amplitude, float maximum, bool scalable) { VFTRACE(amplitude, maximum, scalable); float ratio = 100; /* Unit: % */ if (maximum != 0) ratio = amplitude / maximum * 100; if (maximum == 0 || ratio > 100) ratio = 100; #ifdef ADAPTIVE_HAPTICS_V1 if (scalable && mContextEnable && mContextListener) { uint32_t now = CapoDetector::getCurrentTimeInMs(); uint32_t last_played = mLastEffectPlayedTime; uint32_t lastFaceUpTime = 0; uint8_t carriedPosition = 0; float context_scale = 1.0; bool device_face_up = false; float pre_scaled_ratio = ratio; mLastEffectPlayedTime = now; mContextListener->getCarriedPositionInfo(&carriedPosition, &lastFaceUpTime); device_face_up = carriedPosition == capo::PositionType::ON_TABLE_FACE_UP; ALOGD("Vibrator Now: %u, Last: %u, ScaleTime: %u, Since? %d", now, lastFaceUpTime, mScaleTime, (now < lastFaceUpTime + mScaleTime)); /* If the device is face-up or within the fade scaling range, find new scaling factor */ if (device_face_up || now < lastFaceUpTime + mScaleTime) { /* Device is face-up, so we will scale it down. Start with highest scaling factor */ context_scale = mScalingFactor <= 100 ? static_cast(mScalingFactor) / 100 : 1.0; if (mFadeEnable && mScaleTime > 0 && (context_scale < 1.0) && (now < lastFaceUpTime + mScaleTime) && !device_face_up) { float fade_scale = static_cast(now - lastFaceUpTime) / static_cast(mScaleTime); context_scale += ((1.0 - context_scale) * fade_scale); ALOGD("Vibrator fade scale applied: %f", fade_scale); } ratio *= context_scale; ALOGD("Vibrator adjusting for face-up: pre: %f, post: %f", std::round(pre_scaled_ratio), std::round(ratio)); } /* If we haven't played an effect within the cooldown time, save the scaling factor */ if ((now - last_played) > mScaleCooldown) { ALOGD("Vibrator updating lastplayed scale, old: %f, new: %f", mLastPlayedScale, context_scale); mLastPlayedScale = context_scale; } else { /* Override the scale to match previously played scale */ ratio = mLastPlayedScale * pre_scaled_ratio; ALOGD("Vibrator repeating last scale: %f, new ratio: %f, duration since last: %u", mLastPlayedScale, ratio, (now - last_played)); } } #else // Suppress compiler warning (void)scalable; #endif /*ADAPTIVE_HAPTICS_V1*/ return std::round(ratio); } void Vibrator::updateContext() { /* Don't enable capo from HAL if flag is set to remove it */ if (vibrator_aconfig_flags::remove_capo()) { mContextEnable = false; return; } VFTRACE(); mContextEnable = mHwApi->getContextEnable(); if (mContextEnable && !mContextEnabledPreviously) { mContextListener = CapoDetector::start(); if (mContextListener == nullptr) { ALOGE("%s, CapoDetector failed to start", __func__); } else { mFadeEnable = mHwApi->getContextFadeEnable(); mScalingFactor = mHwApi->getContextScale(); mScaleTime = mHwApi->getContextSettlingTime(); mScaleCooldown = mHwApi->getContextCooldownTime(); ALOGD("%s, CapoDetector started successfully! NanoAppID: 0x%x, Scaling Factor: %d, " "Scaling Time: %d, Cooldown Time: %d", __func__, (uint32_t)mContextListener->getNanoppAppId(), mScalingFactor, mScaleTime, mScaleCooldown); /* We no longer need to use this path */ mContextEnabledPreviously = true; } } } ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum, bool scalable) { VFTRACE(amplitude, maximum, scalable); uint16_t scale; #ifdef ADAPTIVE_HAPTICS_V1 updateContext(); #endif /*ADAPTIVE_HAPTICS_V1*/ scale = amplitudeToScale(amplitude, maximum, scalable); if (!mHwApi->setFFGain(scale)) { mStatsApi->logError(kHwApiError); ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno)); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector * /*_aidl_return*/) { VFTRACE(); mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t /*id*/, Effect /*effect*/, EffectStrength /*strength*/) { VFTRACE(); mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t /*id*/) { mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) { VFTRACE(resonantFreqHz); *resonantFreqHz = mResonantFrequency; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) { VFTRACE(qFactor); std::string caldata{8, '0'}; if (!mHwCal->getQ(&caldata)) { mStatsApi->logError(kHwCalError); 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) { VFTRACE(freqResolutionHz); int32_t capabilities; Vibrator::getCapabilities(&capabilities); if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) { *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ; return ndk::ScopedAStatus::ok(); } else { mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) { VFTRACE(freqMinimumHz); int32_t capabilities; Vibrator::getCapabilities(&capabilities); if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) { *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ; return ndk::ScopedAStatus::ok(); } else { mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } void Vibrator::createPwleMaxLevelLimitMap() { VFTRACE(); int32_t capabilities; Vibrator::getCapabilities(&capabilities); if (!(capabilities & IVibrator::CAP_FREQUENCY_CONTROL)) { mStatsApi->logError(kUnsupportedOpError); ALOGE("Frequency control not support."); return; } if (discretePwleMaxLevels.empty()) { mStatsApi->logError(kInitError); ALOGE("Discrete PWLE max level maps are empty."); return; } int32_t pwleMaxLevelLimitMapIdx = 0; std::map::iterator itr0 = discretePwleMaxLevels.begin(); if (discretePwleMaxLevels.size() == 1) { ALOGD("Discrete PWLE max level map size is 1"); pwleMaxLevelLimitMapIdx = (itr0->first - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ; pwleMaxLevelLimitMap[pwleMaxLevelLimitMapIdx] = itr0->second; return; } auto itr1 = std::next(itr0, 1); while (itr1 != discretePwleMaxLevels.end()) { float x0 = itr0->first; float y0 = itr0->second; float x1 = itr1->first; float y1 = itr1->second; const float ratioOfXY = ((y1 - y0) / (x1 - x0)); pwleMaxLevelLimitMapIdx = (itr0->first - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ; // FixLater: avoid floating point loop counters // NOLINTBEGIN(clang-analyzer-security.FloatLoopCounter,cert-flp30-c) for (float xp = x0; xp < (x1 + PWLE_FREQUENCY_RESOLUTION_HZ); xp += PWLE_FREQUENCY_RESOLUTION_HZ) { // NOLINTEND(clang-analyzer-security.FloatLoopCounter,cert-flp30-c) float yp = y0 + ratioOfXY * (xp - x0); pwleMaxLevelLimitMap[pwleMaxLevelLimitMapIdx++] = yp; } itr0++; itr1++; } } void Vibrator::createBandwidthAmplitudeMap() { VFTRACE(); // Use constant Q Factor of 10 from HW's suggestion const float qFactor = 10.0f; const float blSys = 1.1f; const float gravity = 9.81f; const float maxVoltage = 11.0f; float deviceMass = 0, locCoeff = 0; mHwCal->getDeviceMass(&deviceMass); mHwCal->getLocCoeff(&locCoeff); if (!deviceMass || !locCoeff) { mStatsApi->logError(kInitError); ALOGE("Failed to get Device Mass: %f and Loc Coeff: %f", deviceMass, locCoeff); return; } // Resistance value need to be retrieved from calibration file if (mRedc == 0.0) { std::string caldata{8, '0'}; if (mHwCal->getRedc(&caldata)) { mHwApi->setRedc(caldata); mRedc = redcToFloat(&caldata); } else { mStatsApi->logError(kHwCalError); ALOGE("Failed to get resistance value from calibration file"); return; } } std::vector bandwidthAmplitudeMap(PWLE_BW_MAP_SIZE, 1.0); const float wnSys = mResonantFrequency * 2 * M_PI; const float powWnSys = pow(wnSys, 2); const float var2Para = wnSys / qFactor; float frequencyHz = PWLE_FREQUENCY_MIN_HZ; float frequencyRadians = 0.0f; float vLevel = 0.4473f; float vSys = (mLongEffectVol[1] / 100.0) * maxVoltage * vLevel; float maxAsys = 0; const float amplitudeSysPara = blSys * locCoeff / mRedc / deviceMass; for (int i = 0; i < PWLE_BW_MAP_SIZE; i++) { frequencyRadians = frequencyHz * 2 * M_PI; vLevel = pwleMaxLevelLimitMap[i]; vSys = (mLongEffectVol[1] / 100.0) * maxVoltage * vLevel; float var1 = pow((powWnSys - pow(frequencyRadians, 2)), 2); float var2 = pow((var2Para * frequencyRadians), 2); float psysAbs = sqrt(var1 + var2); // The equation and all related details can be found in the bug float amplitudeSys = (vSys * amplitudeSysPara) * pow(frequencyRadians, 2) / psysAbs / gravity; // Record the maximum acceleration for the next for loop if (amplitudeSys > maxAsys) maxAsys = amplitudeSys; bandwidthAmplitudeMap[i] = amplitudeSys; frequencyHz += PWLE_FREQUENCY_RESOLUTION_HZ; } // Scaled the map between 0 and 1.0 if (maxAsys > 0) { for (int j = 0; j < PWLE_BW_MAP_SIZE; j++) { bandwidthAmplitudeMap[j] = std::floor((bandwidthAmplitudeMap[j] / maxAsys) * 1000) / 1000; } mBandwidthAmplitudeMap = bandwidthAmplitudeMap; mCreateBandwidthAmplitudeMapDone = true; } else { mCreateBandwidthAmplitudeMapDone = false; } } ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector *_aidl_return) { VFTRACE(_aidl_return); int32_t capabilities; Vibrator::getCapabilities(&capabilities); if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) { if (!mCreateBandwidthAmplitudeMapDone) { createPwleMaxLevelLimitMap(); createBandwidthAmplitudeMap(); } *_aidl_return = mBandwidthAmplitudeMap; return (!mBandwidthAmplitudeMap.empty()) ? ndk::ScopedAStatus::ok() : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } else { mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) { VFTRACE(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 { mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) { VFTRACE(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 { mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector *supported) { VFTRACE(supported); int32_t capabilities; Vibrator::getCapabilities(&capabilities); if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) { *supported = { Braking::NONE, }; return ndk::ScopedAStatus::ok(); } else { mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } } static void resetPreviousEndAmplitudeEndFrequency(float *prevEndAmplitude, float *prevEndFrequency) { VFTRACE(prevEndAmplitude, prevEndFrequency); const float reset = -1.0; *prevEndAmplitude = reset; *prevEndFrequency = reset; } static void incrementIndex(int *index) { VFTRACE(index); *index += 1; } ndk::ScopedAStatus Vibrator::composePwle(const std::vector &composite, const std::shared_ptr &callback) { VFTRACE(composite, callback); int32_t capabilities; mStatsApi->logLatencyStart(kPwleEffectLatency); Vibrator::getCapabilities(&capabilities); if ((capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) == 0) { ALOGE("%s: Not supported", __func__); mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } if (composite.empty() || composite.size() > COMPOSE_PWLE_SIZE_MAX_DEFAULT) { ALOGE("%s: Invalid size", __func__); mStatsApi->logError(kBadCompositeError); 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; uint16_t c = 0; 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) { mStatsApi->logError(kBadPrimitiveError); ALOGE("%s: #%u: active: Invalid duration %d", __func__, c, active.duration); 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) { mStatsApi->logError(kBadPrimitiveError); ALOGE("%s: #%u: active: Invalid scale %f, %f", __func__, c, active.startAmplitude, active.endAmplitude); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (active.startAmplitude > CS40L26_PWLE_LEVEL_MAX) { active.startAmplitude = CS40L26_PWLE_LEVEL_MAX; ALOGD("%s: #%u: active: trim the start scale", __func__, c); } if (active.endAmplitude > CS40L26_PWLE_LEVEL_MAX) { active.endAmplitude = CS40L26_PWLE_LEVEL_MAX; ALOGD("%s: #%u: active: trim the end scale", __func__, c); } 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) { mStatsApi->logError(kBadPrimitiveError); ALOGE("%s: #%u: active: Invalid frequency %f, %f", __func__, c, active.startFrequency, active.endFrequency); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } /* Append a new segment if current and previous amplitude and * frequency are not all the same. */ if (!((active.startAmplitude == prevEndAmplitude) && (active.startFrequency == prevEndFrequency))) { if (ch.constructActiveSegment(0, active.startAmplitude, active.startFrequency, false) < 0) { mStatsApi->logError(kPwleConstructionFailError); ALOGE("%s: #%u: active: Failed to construct for the start scale and " "frequency %f, %f", __func__, c, active.startAmplitude, active.startFrequency); 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) { mStatsApi->logError(kPwleConstructionFailError); ALOGE("%s: #%u: active: Failed to construct for the end scale and frequency " "%f, %f", __func__, c, active.startAmplitude, active.startFrequency); 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) { mStatsApi->logError(kBadPrimitiveError); ALOGE("%s: #%u: braking: Invalid braking type %s", __func__, c, toString(braking.braking).c_str()); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } else if (!isClabSupported && (braking.braking == Braking::CLAB)) { mStatsApi->logError(kBadPrimitiveError); ALOGE("%s: #%u: braking: Unsupported CLAB braking", __func__, c); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) { mStatsApi->logError(kBadPrimitiveError); ALOGE("%s: #%u: braking: Invalid duration %d", __func__, c, braking.duration); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } if (ch.constructBrakingSegment(0, braking.braking) < 0) { mStatsApi->logError(kPwleConstructionFailError); ALOGE("%s: #%u: braking: Failed to construct for type %s", __func__, c, toString(braking.braking).c_str()); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } incrementIndex(&segmentIdx); if (ch.constructBrakingSegment(braking.duration, braking.braking) < 0) { mStatsApi->logError(kPwleConstructionFailError); ALOGE("%s: #%u: braking: Failed to construct for type %s with duration %d", __func__, c, toString(braking.braking).c_str(), braking.duration); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } incrementIndex(&segmentIdx); resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency); totalDuration += braking.duration; break; } } if (segmentIdx > COMPOSE_PWLE_SIZE_MAX_DEFAULT) { mStatsApi->logError(kPwleConstructionFailError); ALOGE("Too many PrimitivePwle section!"); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } c++; } ch.flush(); /* Update wlength */ totalDuration += MAX_COLD_START_LATENCY_MS; if (totalDuration > 0x7FFFF) { mStatsApi->logError(kPwleConstructionFailError); ALOGE("Total duration is too long (%d)!", totalDuration); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } else { // For now, let's pass the duration for PWLEs mFfEffects[WAVEFORM_PWLE].replay.length = totalDuration; } /* Update word count */ if (ch.updateWCount(segmentIdx) < 0) { mStatsApi->logError(kPwleConstructionFailError); 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) { mStatsApi->logError(kPwleConstructionFailError); ALOGE("%s: Failed to update the waveform length length", __func__); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } /* Update nsections */ if (ch.updateNSection(segmentIdx) < 0) { mStatsApi->logError(kPwleConstructionFailError); 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() { VFTRACE(); return mIsUnderExternalControl; } 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, " Global Amplitude: %0.2f\n", mGlobalAmplitude); dprintf(fd, " Active Effect ID: %" PRId32 "\n", mActiveId); dprintf(fd, " F0: %.02f\n", mResonantFrequency); dprintf(fd, " F0 Offset: %" PRIu32 "\n", mF0Offset); dprintf(fd, " Redc: %.02f\n", mRedc); dprintf(fd, " HAL State: %" PRIu32 "\n", halState); 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]); dprintf(fd, " FF Effect:\n"); dprintf(fd, " Physical Waveform:\n"); dprintf(fd, "\tId\tIndex\tt ->\tt'\n"); for (uint8_t effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) { dprintf(fd, "\t%d\t%d\t%d\t%d\n", mFfEffects[effectId].id, mFfEffects[effectId].u.periodic.custom_data[1], mEffectDurations[effectId], mFfEffects[effectId].replay.length); } dprintf(fd, " OWT Waveform:\n"); dprintf(fd, "\tId\tBytes\tData\n"); for (uint8_t 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}\n", mFfEffects[effectId].id, numBytes, ss.str().c_str()); } dprintf(fd, "\n"); dprintf(fd, "Versions:\n"); std::ifstream verFile; const auto verBinFileMode = std::ifstream::in | std::ifstream::binary; std::string ver; verFile.open("/sys/module/cs40l26_core/version"); if (verFile.is_open()) { getline(verFile, ver); dprintf(fd, " Haptics Driver: %s\n", ver.c_str()); verFile.close(); } verFile.open("/sys/module/cl_dsp_core/version"); if (verFile.is_open()) { getline(verFile, ver); dprintf(fd, " DSP Driver: %s\n", ver.c_str()); verFile.close(); } verFile.open("/vendor/firmware/cs40l26.wmfw", verBinFileMode); if (verFile.is_open()) { verFile.seekg(113); dprintf(fd, " cs40l26.wmfw: %d.%d.%d\n", verFile.get(), verFile.get(), verFile.get()); verFile.close(); } verFile.open("/vendor/firmware/cs40l26-calib.wmfw", verBinFileMode); if (verFile.is_open()) { verFile.seekg(113); dprintf(fd, " cs40l26-calib.wmfw: %d.%d.%d\n", verFile.get(), verFile.get(), verFile.get()); verFile.close(); } verFile.open("/vendor/firmware/cs40l26.bin", 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, " cs40l26.bin: %s\n", ver.c_str()); break; } } verFile.close(); } verFile.open("/vendor/firmware/cs40l26-svc.bin", verBinFileMode); if (verFile.is_open()) { verFile.seekg(36); getline(verFile, ver); ver = ver.substr(ver.rfind('\\') + 1); dprintf(fd, " cs40l26-svc.bin: %s\n", ver.c_str()); verFile.close(); } verFile.open("/vendor/firmware/cs40l26-calib.bin", verBinFileMode); if (verFile.is_open()) { verFile.seekg(36); getline(verFile, ver); ver = ver.substr(ver.rfind('\\') + 1); dprintf(fd, " cs40l26-calib.bin: %s\n", ver.c_str()); verFile.close(); } verFile.open("/vendor/firmware/cs40l26-dvl.bin", verBinFileMode); if (verFile.is_open()) { verFile.seekg(36); getline(verFile, ver); ver = ver.substr(0, ver.find('\0') + 1); ver = ver.substr(ver.rfind('\\') + 1); dprintf(fd, " cs40l26-dvl.bin: %s\n", ver.c_str()); verFile.close(); } dprintf(fd, "\n"); mHwApi->debug(fd); dprintf(fd, "\n"); mHwCal->debug(fd); dprintf(fd, "\n"); dprintf(fd, "Capo Info:\n"); dprintf(fd, "Capo Enabled: %d\n", mContextEnable); if (mContextListener) { dprintf(fd, "Capo ID: 0x%x\n", (uint32_t)(mContextListener->getNanoppAppId())); dprintf(fd, "Capo State: %d\n", mContextListener->getCarriedPosition()); } dprintf(fd, "\n"); mStatsApi->debug(fd); if (mHwApi->isDbcSupported()) { dprintf(fd, "\nDBC Enabled\n"); } #ifdef VIBRATOR_TRACE Trace::debug(fd); #endif fsync(fd); return STATUS_OK; } bool Vibrator::hasHapticAlsaDevice() { VFTRACE(); // 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 (mHwApi->getHapticAlsaDevice(&mCard, &mDevice)) { mHasHapticAlsaDevice = true; mConfigHapticAlsaDeviceDone = true; } else { mStatsApi->logError(kAlsaFailError); 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) { VFTRACE(effect, strength, outEffectIndex, outTimeMs, 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: mStatsApi->logError(kUnsupportedOpError); 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: mStatsApi->logError(kUnsupportedOpError); 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) { VFTRACE(effect, strength, outTimeMs, 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()) { mStatsApi->logError(kBadEffectError); 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()) { mStatsApi->logError(kBadEffectError); return status; } timeMs += thisTimeMs; outCh->constructComposeSegment(thisVolLevel, thisEffectIndex, 0 /*repeat*/, 0 /*flags*/, 0 /*delay*/); outCh->flush(); if (outCh->updateNSection(2) < 0) { mStatsApi->logError(kComposeFailError); ALOGE("%s: Failed to update the section count", __func__); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } break; default: mStatsApi->logError(kUnsupportedOpError); return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } *outTimeMs = timeMs; // Compositions should have 0 duration mFfEffects[WAVEFORM_COMPOSE].replay.length = 0; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Vibrator::getPrimitiveDetails(CompositePrimitive primitive, uint32_t *outEffectIndex) { VFTRACE(primitive, outEffectIndex); uint32_t effectIndex; uint32_t primitiveBit = 1 << int32_t(primitive); if ((primitiveBit & mSupportedPrimitivesBits) == 0x0) { mStatsApi->logError(kUnsupportedOpError); 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: mStatsApi->logError(kUnsupportedOpError); 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) { VFTRACE(effect, strength, callback, 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: mStatsApi->logError(kUnsupportedOpError); 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) { VFTRACE(effectIndex, volLevel, ch, callback); setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX, false); return on(MAX_TIME_MS, effectIndex, ch, callback); } void Vibrator::waitForComplete(std::shared_ptr &&callback) { VFTRACE(callback); if (!mHwApi->pollVibeState(VIBE_STATE_HAPTIC, POLLING_TIMEOUT)) { ALOGW("Failed to get state \"Haptic\""); } halState = PLAYING; ATRACE_BEGIN("Vibrating"); mHwApi->pollVibeState(VIBE_STATE_STOPPED); ATRACE_END(); halState = STOPPED; const std::scoped_lock lock(mActiveId_mutex); uint32_t effectCount = WAVEFORM_MAX_PHYSICAL_INDEX; if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) && (!mHwApi->eraseOwtEffect(mActiveId, &mFfEffects))) { mStatsApi->logError(kHwApiError); ALOGE("Failed to clean up the composed effect %d", mActiveId); } else { ALOGD("waitForComplete: Vibrator is already off"); } mHwApi->getEffectCount(&effectCount); // Do waveform number checking if ((effectCount > WAVEFORM_MAX_PHYSICAL_INDEX) && (!mHwApi->eraseOwtEffect(WAVEFORM_MAX_INDEX, &mFfEffects))) { mStatsApi->logError(kHwApiError); ALOGE("Failed to forcibly clean up all composed effect"); } mActiveId = -1; halState = RESTORED; if (callback) { auto ret = callback->onComplete(); if (!ret.isOk()) { ALOGE("Failed completion callback: %d", ret.getExceptionCode()); } } } uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) { VFTRACE(intensity, 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_LONG_VIBRATION_EFFECT_INDEX: // fall-through case WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX: // fall-through 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 case WAVEFORM_LOW_TICK_INDEX: // fall-through default: volLevel = calc(intensity, mClickEffectVol); break; } return volLevel; } } // namespace vibrator } // namespace hardware } // namespace android } // namespace aidl