diff --git a/vibrator/common/HardwareBase.cpp b/vibrator/common/HardwareBase.cpp index 7d61b57..329293a 100644 --- a/vibrator/common/HardwareBase.cpp +++ b/vibrator/common/HardwareBase.cpp @@ -40,10 +40,6 @@ void HwApiBase::saveName(const std::string &name, const std::ios *stream) { mNames[stream] = name; } -bool HwApiBase::has(const std::ios &stream) { - return !!stream; -} - void HwApiBase::debug(int fd) { dprintf(fd, "Kernel:\n"); diff --git a/vibrator/common/HardwareBase.h b/vibrator/common/HardwareBase.h index 448d29c..a957848 100644 --- a/vibrator/common/HardwareBase.h +++ b/vibrator/common/HardwareBase.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "utils.h" @@ -67,7 +68,8 @@ class HwApiBase { void saveName(const std::string &name, const std::ios *stream); template void open(const std::string &name, T *stream); - bool has(const std::ios &stream); + template + bool has(const T &stream); template bool get(T *value, std::istream *stream); template @@ -93,6 +95,16 @@ void HwApiBase::open(const std::string &name, T *stream) { utils::openNoCreate(mPathPrefix + name, stream); } +template +bool HwApiBase::has(const T &stream) { + if constexpr (std::is_same::value || std::is_same::value || + std::is_same::value) + return stream.is_open() && !stream.fail(); + + ALOGE("File stream is not of the correct type"); + return false; +} + template bool HwApiBase::get(T *value, std::istream *stream) { ATRACE_NAME("HwApi::get"); diff --git a/vibrator/common/utils.h b/vibrator/common/utils.h index 188554d..86dd37e 100644 --- a/vibrator/common/utils.h +++ b/vibrator/common/utils.h @@ -110,10 +110,12 @@ inline bool getProperty(const std::string &key, const bool def) { template static void openNoCreate(const std::string &file, T *outStream) { - auto mode = std::is_base_of_v ? std::ios_base::out : std::ios_base::in; + if (!std::filesystem::exists(file)) { + ALOGE("File does not exist: %s", file.c_str()); + return; + } - // Force 'in' mode to prevent file creation - outStream->open(file, mode | std::ios_base::in); + outStream->open(file); if (!*outStream) { ALOGE("Failed to open %s (%d): %s", file.c_str(), errno, strerror(errno)); } diff --git a/vibrator/cs40l26/Hardware.h b/vibrator/cs40l26/Hardware.h index 69cf9ae..e2c2d36 100644 --- a/vibrator/cs40l26/Hardware.h +++ b/vibrator/cs40l26/Hardware.h @@ -78,6 +78,9 @@ class HwApi : public Vibrator::HwApi, private HwApiBase { open("calibration/q_stored", &mQ); open("default/vibe_state", &mVibeState); open("default/num_waves", &mEffectCount); + open("default/braking_time_bank", &mEffectBrakingTimeBank); + open("default/braking_time_index", &mEffectBrakingTimeIndex); + open("default/braking_time_ms", &mEffectBrakingTimeMs); open("default/owt_free_space", &mOwtFreeSpace); open("default/f0_comp_enable", &mF0CompEnable); open("default/redc_comp_enable", &mRedcCompEnable); @@ -89,6 +92,16 @@ class HwApi : public Vibrator::HwApi, private HwApiBase { bool setRedc(std::string value) override { return set(value, &mRedc); } bool setQ(std::string value) override { return set(value, &mQ); } bool getEffectCount(uint32_t *value) override { return get(value, &mEffectCount); } + bool hasEffectBrakingTimeBank() override { return has(mEffectBrakingTimeBank); } + bool setEffectBrakingTimeBank(uint32_t value) override { + return set(value, &mEffectBrakingTimeBank); + } + bool setEffectBrakingTimeIndex(uint32_t value) override { + return set(value, &mEffectBrakingTimeIndex); + } + bool getEffectBrakingTimeMs(uint32_t *value) override { + return get(value, &mEffectBrakingTimeMs); + } bool pollVibeState(uint32_t value, int32_t timeoutMs) override { return poll(value, &mVibeState, timeoutMs); } @@ -282,6 +295,9 @@ class HwApi : public Vibrator::HwApi, private HwApiBase { std::ofstream mRedc; std::ofstream mQ; std::ifstream mEffectCount; + std::ofstream mEffectBrakingTimeBank; + std::ofstream mEffectBrakingTimeIndex; + std::ifstream mEffectBrakingTimeMs; std::ifstream mVibeState; std::ifstream mOwtFreeSpace; std::ofstream mF0CompEnable; diff --git a/vibrator/cs40l26/Vibrator.cpp b/vibrator/cs40l26/Vibrator.cpp index b3ce88e..bf3b535 100644 --- a/vibrator/cs40l26/Vibrator.cpp +++ b/vibrator/cs40l26/Vibrator.cpp @@ -485,8 +485,9 @@ Vibrator::Vibrator(std::unique_ptr hwApiDefault, std::unique_ptr h mFfEffects.resize(WAVEFORM_MAX_INDEX); mEffectDurations.resize(WAVEFORM_MAX_INDEX); mEffectDurations = { - 1000, 100, 12, 1000, 300, 130, 150, 500, 100, 5, 12, 1000, 1000, 1000, + 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; @@ -519,6 +520,11 @@ Vibrator::Vibrator(std::unique_ptr hwApiDefault, std::unique_ptr h 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 @@ -850,7 +856,7 @@ ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive, return status; } - *durationMs = mEffectDurations[effectIndex]; + *durationMs = mEffectDurations[effectIndex] + mEffectBrakingDurations[effectIndex]; } else { *durationMs = 0; } @@ -863,7 +869,6 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector &composi ALOGD("Vibrator::compose"); uint16_t size; uint16_t nextEffectDelay; - uint16_t totalDuration = 0; if (composite.size() > COMPOSE_SIZE_MAX || composite.empty()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); @@ -871,7 +876,6 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector &composi /* Check if there is a wait before the first effect. */ nextEffectDelay = composite.front().delayMs; - totalDuration += nextEffectDelay; if (nextEffectDelay > COMPOSE_DELAY_MAX_MS || nextEffectDelay < 0) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } else if (nextEffectDelay > 0) { @@ -913,7 +917,6 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector &composi effectScale = mPrimitiveMinScale[static_cast(e_curr.primitive)]; } effectVolLevel = intensityToVolLevel(effectScale, effectIndex); - totalDuration += mEffectDurations[effectIndex]; } /* Fetch the next composite effect delay and fill into the current section */ @@ -926,13 +929,14 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector &composi return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } nextEffectDelay = delay; - totalDuration += 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*/); } @@ -1395,19 +1399,20 @@ binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) { dprintf(fd, " FF effect:\n"); dprintf(fd, " Physical waveform:\n"); - dprintf(fd, "==== Base ====\n\tId\tIndex\tt ->\tt'\ttrigger button\n"); + dprintf(fd, "==== Base ====\n\tId\tIndex\tt ->\tt'\tBrake\ttrigger button\n"); uint8_t effectId; for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) { - dprintf(fd, "\t%d\t%d\t%d\t%d\t%X\n", mFfEffects[effectId].id, + 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, mFfEffects[effectId].trigger.button); + mFfEffects[effectId].replay.length, mEffectBrakingDurations[effectId], + mFfEffects[effectId].trigger.button); } if (mIsDual) { - dprintf(fd, "==== Flip ====\n\tId\tIndex\tt ->\tt'\ttrigger button\n"); + dprintf(fd, "==== Flip ====\n\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%X\n", mFfEffectsDual[effectId].id, + 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, + mFfEffectsDual[effectId].replay.length, mEffectBrakingDurations[effectId], mFfEffectsDual[effectId].trigger.button); } } diff --git a/vibrator/cs40l26/Vibrator.h b/vibrator/cs40l26/Vibrator.h index 8f89713..e21ef58 100644 --- a/vibrator/cs40l26/Vibrator.h +++ b/vibrator/cs40l26/Vibrator.h @@ -62,6 +62,15 @@ class Vibrator : public BnVibrator { virtual bool setQ(std::string value) = 0; // Reports the number of effect waveforms loaded in firmware. virtual bool getEffectCount(uint32_t *value) = 0; + // Checks whether braking time bank is supported. + virtual bool hasEffectBrakingTimeBank() = 0; + // Specifies the bank of the effect for querying braking time. + // 0: RAM bank, 2: OWT bank + virtual bool setEffectBrakingTimeBank(uint32_t value) = 0; + // Specifies the index of an effect whose braking time is to be read. + virtual bool setEffectBrakingTimeIndex(uint32_t value) = 0; + // Gets the braking time duration of SVC effects (returns 0 if not SVC). + virtual bool getEffectBrakingTimeMs(uint32_t *value) = 0; // Blocks until timeout or vibrator reaches desired state // (2 = ASP enabled, 1 = haptic enabled, 0 = disabled). virtual bool pollVibeState(uint32_t value, int32_t timeoutMs = -1) = 0; @@ -218,6 +227,7 @@ class Vibrator : public BnVibrator { std::vector mFfEffects; std::vector mFfEffectsDual; std::vector mEffectDurations; + std::vector mEffectBrakingDurations; std::vector> mEffectCustomData; std::vector> mEffectCustomDataDual; std::future mAsyncHandle; diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private.rc index e45356e..01b37bb 100644 --- a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private.rc +++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-private.rc @@ -14,6 +14,9 @@ on property:vendor.all.modules.ready=1 chown system system /sys/bus/i2c/devices/15-0043/calibration/redc_stored chown system system /sys/bus/i2c/devices/15-0043/default/vibe_state chown system system /sys/bus/i2c/devices/15-0043/default/num_waves + chown system system /sys/bus/i2c/devices/15-0043/default/braking_time_bank + chown system system /sys/bus/i2c/devices/15-0043/default/braking_time_index + chown system system /sys/bus/i2c/devices/15-0043/default/braking_time_ms chown system system /sys/bus/i2c/devices/15-0043/default/f0_offset chown system system /sys/bus/i2c/devices/15-0043/default/owt_free_space chown system system /sys/bus/i2c/devices/15-0043/default/f0_comp_enable @@ -25,6 +28,9 @@ on property:vendor.all.modules.ready=1 chown system system /sys/bus/i2c/devices/15-0042/calibration/redc_stored chown system system /sys/bus/i2c/devices/15-0042/default/vibe_state chown system system /sys/bus/i2c/devices/15-0042/default/num_waves + chown system system /sys/bus/i2c/devices/15-0042/default/braking_time_bank + chown system system /sys/bus/i2c/devices/15-0042/default/braking_time_index + chown system system /sys/bus/i2c/devices/15-0042/default/braking_time_ms chown system system /sys/bus/i2c/devices/15-0042/default/f0_offset chown system system /sys/bus/i2c/devices/15-0042/default/owt_free_space chown system system /sys/bus/i2c/devices/15-0042/default/f0_comp_enable @@ -56,6 +62,9 @@ service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service calibration/q_stored default/vibe_state default/num_waves + default/braking_time_bank + default/braking_time_index + default/braking_time_ms default/f0_offset default/owt_free_space default/f0_comp_enable diff --git a/vibrator/cs40l26/tests/mocks.h b/vibrator/cs40l26/tests/mocks.h index 641aba8..c521b02 100644 --- a/vibrator/cs40l26/tests/mocks.h +++ b/vibrator/cs40l26/tests/mocks.h @@ -39,6 +39,10 @@ class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi { MOCK_METHOD1(setRedc, bool(std::string value)); MOCK_METHOD1(setQ, bool(std::string value)); MOCK_METHOD1(getEffectCount, bool(uint32_t *value)); + MOCK_METHOD0(hasEffectBrakingTimeBank, bool()); + MOCK_METHOD1(setEffectBrakingTimeBank, bool(uint32_t value)); + MOCK_METHOD1(setEffectBrakingTimeIndex, bool(uint32_t value)); + MOCK_METHOD1(getEffectBrakingTimeMs, bool(uint32_t *value)); MOCK_METHOD2(pollVibeState, bool(uint32_t value, int32_t timeoutMs)); MOCK_METHOD0(hasOwtFreeSpace, bool()); MOCK_METHOD1(getOwtFreeSpace, bool(uint32_t *value)); diff --git a/vibrator/cs40l26/tests/test-vibrator.cpp b/vibrator/cs40l26/tests/test-vibrator.cpp index 3ea6bda..6c8cebb 100644 --- a/vibrator/cs40l26/tests/test-vibrator.cpp +++ b/vibrator/cs40l26/tests/test-vibrator.cpp @@ -74,7 +74,7 @@ static constexpr std::array V_TICK_DEFAULT = {1, 100}; static constexpr std::array V_CLICK_DEFAULT{1, 100}; static constexpr std::array V_LONG_DEFAULT{1, 100}; static constexpr std::array EFFECT_DURATIONS{ - 0, 100, 30, 1000, 300, 130, 150, 500, 100, 15, 20, 1000, 1000, 1000}; + 1000, 100, 9, 1000, 300, 130, 150, 500, 100, 5, 12, 1000, 1000, 1000}; // Constants With Prescribed Values @@ -375,6 +375,11 @@ TEST_F(VibratorTest, Constructor) { .WillOnce(DoAll(SetArgPointee<0>(supportedPrimitivesBits), Return(true))); EXPECT_CALL(*mMockApi, setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US)).WillOnce(Return(true)); + EXPECT_CALL(*mMockApi, setEffectBrakingTimeBank(0)).WillRepeatedly(Return(true)); + for (uint32_t i = 0; i < WAVEFORM_MAX_PHYSICAL_INDEX; i++) { + EXPECT_CALL(*mMockApi, setEffectBrakingTimeIndex(i)).WillRepeatedly(Return(true)); + EXPECT_CALL(*mMockApi, getEffectBrakingTimeMs(_)).WillRepeatedly(Return(true)); + } createVibrator(std::move(mockapi), std::move(mockcal), std::move(mockgpio), false); }