452 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			452 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2017 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 "utils.h"
 | |
| 
 | |
| #include <cutils/properties.h>
 | |
| #include <hardware/hardware.h>
 | |
| #include <hardware/vibrator.h>
 | |
| #include <log/log.h>
 | |
| #include <utils/Trace.h>
 | |
| 
 | |
| #include <cinttypes>
 | |
| #include <cmath>
 | |
| #include <fstream>
 | |
| #include <iostream>
 | |
| 
 | |
| namespace aidl {
 | |
| namespace android {
 | |
| namespace hardware {
 | |
| namespace vibrator {
 | |
| 
 | |
| static constexpr int8_t MAX_RTP_INPUT = 127;
 | |
| static constexpr int8_t MIN_RTP_INPUT = 0;
 | |
| 
 | |
| static constexpr char RTP_MODE[] = "rtp";
 | |
| static constexpr char WAVEFORM_MODE[] = "waveform";
 | |
| 
 | |
| // Use effect #1 in the waveform library for CLICK effect
 | |
| static constexpr uint8_t WAVEFORM_CLICK_EFFECT_INDEX = 1;
 | |
| 
 | |
| // Use effect #2 in the waveform library for TICK effect
 | |
| static constexpr char WAVEFORM_TICK_EFFECT_INDEX = 2;
 | |
| 
 | |
| // Use effect #3 in the waveform library for DOUBLE_CLICK effect
 | |
| static constexpr char WAVEFORM_DOUBLE_CLICK_EFFECT_INDEX = 3;
 | |
| 
 | |
| // Use effect #4 in the waveform library for HEAVY_CLICK effect
 | |
| static constexpr char WAVEFORM_HEAVY_CLICK_EFFECT_INDEX = 4;
 | |
| 
 | |
| static std::uint32_t freqPeriodFormula(std::uint32_t in) {
 | |
|     return 1000000000 / (24615 * in);
 | |
| }
 | |
| 
 | |
| static float freqPeriodFormulaFloat(std::uint32_t in) {
 | |
|     return static_cast<float>(1000000000) / static_cast<float>(24615 * in);
 | |
| }
 | |
| 
 | |
| using utils::toUnderlying;
 | |
| 
 | |
| Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
 | |
|     : mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)) {
 | |
|     std::string autocal;
 | |
|     uint32_t lraPeriod;
 | |
|     bool dynamicConfig;
 | |
| 
 | |
|     if (!mHwApi->setState(true)) {
 | |
|         ALOGE("Failed to set state (%d): %s", errno, strerror(errno));
 | |
|     }
 | |
| 
 | |
|     if (mHwCal->getAutocal(&autocal)) {
 | |
|         mHwApi->setAutocal(autocal);
 | |
|     }
 | |
|     mHwCal->getLraPeriod(&lraPeriod);
 | |
| 
 | |
|     mHwCal->getCloseLoopThreshold(&mCloseLoopThreshold);
 | |
|     mHwCal->getDynamicConfig(&dynamicConfig);
 | |
| 
 | |
|     if (dynamicConfig) {
 | |
|         uint32_t longFreqencyShift;
 | |
|         uint32_t shortVoltageMax, longVoltageMax;
 | |
| 
 | |
|         mHwCal->getLongFrequencyShift(&longFreqencyShift);
 | |
|         mHwCal->getShortVoltageMax(&shortVoltageMax);
 | |
|         mHwCal->getLongVoltageMax(&longVoltageMax);
 | |
| 
 | |
|         mEffectConfig.reset(new VibrationConfig({
 | |
|                 .shape = WaveShape::SINE,
 | |
|                 .odClamp = shortVoltageMax,
 | |
|                 .olLraPeriod = lraPeriod,
 | |
|         }));
 | |
|         mSteadyConfig.reset(new VibrationConfig({
 | |
|                 .shape = WaveShape::SQUARE,
 | |
|                 .odClamp = longVoltageMax,
 | |
|                 // 1. Change long lra period to frequency
 | |
|                 // 2. Get frequency': subtract the frequency shift from the frequency
 | |
|                 // 3. Get final long lra period after put the frequency' to formula
 | |
|                 .olLraPeriod = freqPeriodFormula(freqPeriodFormula(lraPeriod) - longFreqencyShift),
 | |
|         }));
 | |
|     } else {
 | |
|         mHwApi->setOlLraPeriod(lraPeriod);
 | |
|     }
 | |
| 
 | |
|     mHwCal->getClickDuration(&mClickDuration);
 | |
|     mHwCal->getTickDuration(&mTickDuration);
 | |
|     mHwCal->getDoubleClickDuration(&mDoubleClickDuration);
 | |
|     mHwCal->getHeavyClickDuration(&mHeavyClickDuration);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
 | |
|     ATRACE_NAME("Vibrator::getCapabilities");
 | |
|     int32_t ret = IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY;
 | |
|     if (mHwApi->hasRtpInput()) {
 | |
|         ret |= IVibrator::CAP_AMPLITUDE_CONTROL;
 | |
|     }
 | |
|     *_aidl_return = ret;
 | |
|     return ndk::ScopedAStatus::ok();
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, const char mode[],
 | |
|                                 const std::unique_ptr<VibrationConfig> &config) {
 | |
|     LoopControl loopMode = LoopControl::OPEN;
 | |
| 
 | |
|     // Open-loop mode is used for short click for over-drive
 | |
|     // Close-loop mode is used for long notification for stability
 | |
|     if (mode == RTP_MODE && timeoutMs > mCloseLoopThreshold) {
 | |
|         loopMode = LoopControl::CLOSE;
 | |
|     }
 | |
| 
 | |
|     mHwApi->setCtrlLoop(toUnderlying(loopMode));
 | |
|     if (!mHwApi->setDuration(timeoutMs)) {
 | |
|         ALOGE("Failed to set duration (%d): %s", errno, strerror(errno));
 | |
|         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 | |
|     }
 | |
| 
 | |
|     mHwApi->setMode(mode);
 | |
|     if (config != nullptr) {
 | |
|         mHwApi->setLraWaveShape(toUnderlying(config->shape));
 | |
|         mHwApi->setOdClamp(config->odClamp);
 | |
|         mHwApi->setOlLraPeriod(config->olLraPeriod);
 | |
|     }
 | |
| 
 | |
|     if (!mHwApi->setActivate(1)) {
 | |
|         ALOGE("Failed to activate (%d): %s", errno, strerror(errno));
 | |
|         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 | |
|     }
 | |
| 
 | |
|     return ndk::ScopedAStatus::ok();
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
 | |
|                                 const std::shared_ptr<IVibratorCallback> &callback) {
 | |
|     ATRACE_NAME("Vibrator::on");
 | |
|     if (callback) {
 | |
|         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
|     }
 | |
|     return on(timeoutMs, RTP_MODE, mSteadyConfig);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::off() {
 | |
|     ATRACE_NAME("Vibrator::off");
 | |
|     if (!mHwApi->setActivate(0)) {
 | |
|         ALOGE("Failed to turn vibrator off (%d): %s", errno, strerror(errno));
 | |
|         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 | |
|     }
 | |
|     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);
 | |
|     }
 | |
| 
 | |
|     int32_t rtp_input = std::round(amplitude * (MAX_RTP_INPUT - MIN_RTP_INPUT) + MIN_RTP_INPUT);
 | |
| 
 | |
|     if (!mHwApi->setRtpInput(rtp_input)) {
 | |
|         ALOGE("Failed to set amplitude (%d): %s", errno, strerror(errno));
 | |
|         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 | |
|     }
 | |
| 
 | |
|     return ndk::ScopedAStatus::ok();
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
 | |
|     ATRACE_NAME("Vibrator::setExternalControl");
 | |
|     ALOGE("Not support in DRV2624 solution, %d", enabled);
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| 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, "  Close Loop Thresh: %" PRIu32 "\n", mCloseLoopThreshold);
 | |
|     if (mSteadyConfig) {
 | |
|         dprintf(fd, "  Steady Shape: %" PRIu32 "\n", mSteadyConfig->shape);
 | |
|         dprintf(fd, "  Steady OD Clamp: %" PRIu32 "\n", mSteadyConfig->odClamp);
 | |
|         dprintf(fd, "  Steady OL LRA Period: %" PRIu32 "\n", mSteadyConfig->olLraPeriod);
 | |
|     }
 | |
|     if (mEffectConfig) {
 | |
|         dprintf(fd, "  Effect Shape: %" PRIu32 "\n", mEffectConfig->shape);
 | |
|         dprintf(fd, "  Effect OD Clamp: %" PRIu32 "\n", mEffectConfig->odClamp);
 | |
|         dprintf(fd, "  Effect OL LRA Period: %" PRIu32 "\n", mEffectConfig->olLraPeriod);
 | |
|     }
 | |
|     dprintf(fd, "  Click Duration: %" PRIu32 "\n", mClickDuration);
 | |
|     dprintf(fd, "  Tick Duration: %" PRIu32 "\n", mTickDuration);
 | |
|     dprintf(fd, "  Double Click Duration: %" PRIu32 "\n", mDoubleClickDuration);
 | |
|     dprintf(fd, "  Heavy Click Duration: %" PRIu32 "\n", mHeavyClickDuration);
 | |
| 
 | |
|     dprintf(fd, "\n");
 | |
| 
 | |
|     mHwApi->debug(fd);
 | |
| 
 | |
|     dprintf(fd, "\n");
 | |
| 
 | |
|     mHwCal->debug(fd);
 | |
| 
 | |
|     fsync(fd);
 | |
|     return STATUS_OK;
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect> *_aidl_return) {
 | |
|     *_aidl_return = {Effect::TEXTURE_TICK, Effect::TICK, Effect::CLICK, Effect::HEAVY_CLICK,
 | |
|                      Effect::DOUBLE_CLICK};
 | |
|     return ndk::ScopedAStatus::ok();
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
 | |
|                                      const std::shared_ptr<IVibratorCallback> &callback,
 | |
|                                      int32_t *_aidl_return) {
 | |
|     ATRACE_NAME("Vibrator::perform");
 | |
|     ndk::ScopedAStatus status;
 | |
| 
 | |
|     if (callback) {
 | |
|         status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
|     } else {
 | |
|         status = performEffect(effect, strength, _aidl_return);
 | |
|     }
 | |
| 
 | |
|     return status;
 | |
| }
 | |
| 
 | |
| static ndk::ScopedAStatus convertEffectStrength(EffectStrength strength, uint8_t *outScale) {
 | |
|     uint8_t scale;
 | |
| 
 | |
|     switch (strength) {
 | |
|         case EffectStrength::LIGHT:
 | |
|             scale = 2;  // 50%
 | |
|             break;
 | |
|         case EffectStrength::MEDIUM:
 | |
|         case EffectStrength::STRONG:
 | |
|             scale = 0;  // 100%
 | |
|             break;
 | |
|         default:
 | |
|             return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
|     }
 | |
| 
 | |
|     *outScale = scale;
 | |
| 
 | |
|     return ndk::ScopedAStatus::ok();
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getEffectDetails(Effect effect, uint8_t *outIndex,
 | |
|                                               uint32_t *outTimeMs) {
 | |
|     switch (effect) {
 | |
|         case Effect::TEXTURE_TICK:
 | |
|             *outIndex = WAVEFORM_TICK_EFFECT_INDEX;
 | |
|             *outTimeMs = mTickDuration;
 | |
|             break;
 | |
|         case Effect::CLICK:
 | |
|             *outIndex = WAVEFORM_CLICK_EFFECT_INDEX;
 | |
|             *outTimeMs = mClickDuration;
 | |
|             break;
 | |
|         case Effect::DOUBLE_CLICK:
 | |
|             *outIndex = WAVEFORM_DOUBLE_CLICK_EFFECT_INDEX;
 | |
|             *outTimeMs = mDoubleClickDuration;
 | |
|             break;
 | |
|         case Effect::TICK:
 | |
|             *outIndex = WAVEFORM_TICK_EFFECT_INDEX;
 | |
|             *outTimeMs = mTickDuration;
 | |
|             break;
 | |
|         case Effect::HEAVY_CLICK:
 | |
|             *outIndex = WAVEFORM_HEAVY_CLICK_EFFECT_INDEX;
 | |
|             *outTimeMs = mHeavyClickDuration;
 | |
|             break;
 | |
|         default:
 | |
|             return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
|     }
 | |
| 
 | |
|     return ndk::ScopedAStatus::ok();
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strength,
 | |
|                                            int32_t *outTimeMs) {
 | |
|     ndk::ScopedAStatus status;
 | |
|     uint8_t index;
 | |
|     uint32_t timeMS;
 | |
|     uint8_t scale;
 | |
| 
 | |
|     status = getEffectDetails(effect, &index, &timeMS);
 | |
|     if (!status.isOk()) {
 | |
|         return status;
 | |
|     }
 | |
| 
 | |
|     status = convertEffectStrength(strength, &scale);
 | |
|     if (!status.isOk()) {
 | |
|         return status;
 | |
|     }
 | |
| 
 | |
|     mHwApi->setSequencer(std::to_string(index) + " 0");
 | |
|     mHwApi->setScale(scale);
 | |
|     status = on(timeMS, WAVEFORM_MODE, mEffectConfig);
 | |
|     if (!status.isOk()) {
 | |
|         return status;
 | |
|     }
 | |
| 
 | |
|     *outTimeMs = timeMS;
 | |
| 
 | |
|     return ndk::ScopedAStatus::ok();
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect> *_aidl_return) {
 | |
|     *_aidl_return = {
 | |
|             Effect::CLICK,       Effect::DOUBLE_CLICK, Effect::TICK,
 | |
|             Effect::HEAVY_CLICK, Effect::TEXTURE_TICK,
 | |
|     };
 | |
|     return ndk::ScopedAStatus::ok();
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
 | |
|     if (id != 0) {
 | |
|         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
|     }
 | |
| 
 | |
|     ndk::ScopedAStatus status;
 | |
|     uint8_t index;
 | |
|     uint32_t timeMs;
 | |
|     uint8_t scale;
 | |
| 
 | |
|     status = getEffectDetails(effect, &index, &timeMs);
 | |
|     if (!status.isOk()) {
 | |
|         return status;
 | |
|     }
 | |
| 
 | |
|     status = convertEffectStrength(strength, &scale);
 | |
|     if (!status.isOk()) {
 | |
|         return status;
 | |
|     }
 | |
| 
 | |
|     if (!mHwApi->setLpTriggerEffect(index)) {
 | |
|         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 | |
|     }
 | |
| 
 | |
|     if (!mHwApi->setLpTriggerScale(scale)) {
 | |
|         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 | |
|     }
 | |
| 
 | |
|     return ndk::ScopedAStatus::ok();
 | |
| }
 | |
| ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id) {
 | |
|     if (id != 0) {
 | |
|         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
|     }
 | |
| 
 | |
|     mHwApi->setLpTriggerEffect(0);
 | |
| 
 | |
|     return ndk::ScopedAStatus::ok();
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t * /*maxDelayMs*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t * /*maxSize*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getSupportedPrimitives(
 | |
|         std::vector<CompositePrimitive> * /*supported*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive /*primitive*/,
 | |
|                                                   int32_t * /*durationMs*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> & /*composite*/,
 | |
|                                      const std::shared_ptr<IVibratorCallback> & /*callback*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
 | |
|     uint32_t lraPeriod;
 | |
|     if(!mHwCal->getLraPeriod(&lraPeriod)) {
 | |
|         ALOGE("Failed to get resonant frequency (%d): %s", errno, strerror(errno));
 | |
|         return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
 | |
|     }
 | |
|     *resonantFreqHz = freqPeriodFormulaFloat(lraPeriod);
 | |
|     return ndk::ScopedAStatus::ok();
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getQFactor(float * /*qFactor*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getFrequencyResolution(float * /*freqResolutionHz*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float * /*freqMinimumHz*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> * /*_aidl_return*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t * /*durationMs*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t * /*maxSize*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> * /*supported*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> & /*composite*/,
 | |
|                                          const std::shared_ptr<IVibratorCallback> & /*callback*/) {
 | |
|     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
 | |
| }
 | |
| 
 | |
| }  // namespace vibrator
 | |
| }  // namespace hardware
 | |
| }  // namespace android
 | |
| }  // namespace aidl
 |