From 8e38bbd395f5d1c0d24cc21d058db9ec1942e8c3 Mon Sep 17 00:00:00 2001 From: Daniel Jacob Chittoor Date: Thu, 16 Sep 2021 14:02:00 +0200 Subject: [PATCH] aidl: Import QTI Vibrator AIDL * CAF tag LA.UM.9.1.r1-11600.03-SMxxx0.QSSI12.0 Change-Id: I48b8326aab719b236ff6b39c05dc4df1eeb6f688 --- aidl/vibrator/Android.bp | 87 +++ aidl/vibrator/Vibrator.cpp | 586 ++++++++++++++++++ aidl/vibrator/VibratorOffload.cpp | 305 +++++++++ aidl/vibrator/include/Vibrator.h | 124 ++++ aidl/vibrator/service.cpp | 50 ++ ...r.qti.hardware.vibrator.offload.service.rc | 4 + ....qti.hardware.vibrator.offload.service.xml | 33 + .../vendor.qti.hardware.vibrator.service.rc | 4 + .../vendor.qti.hardware.vibrator.service.xml | 33 + 9 files changed, 1226 insertions(+) create mode 100644 aidl/vibrator/Android.bp create mode 100644 aidl/vibrator/Vibrator.cpp create mode 100644 aidl/vibrator/VibratorOffload.cpp create mode 100644 aidl/vibrator/include/Vibrator.h create mode 100644 aidl/vibrator/service.cpp create mode 100644 aidl/vibrator/vendor.qti.hardware.vibrator.offload.service.rc create mode 100644 aidl/vibrator/vendor.qti.hardware.vibrator.offload.service.xml create mode 100644 aidl/vibrator/vendor.qti.hardware.vibrator.service.rc create mode 100644 aidl/vibrator/vendor.qti.hardware.vibrator.service.xml diff --git a/aidl/vibrator/Android.bp b/aidl/vibrator/Android.bp new file mode 100644 index 0000000..42b271f --- /dev/null +++ b/aidl/vibrator/Android.bp @@ -0,0 +1,87 @@ +Common_CFlags = ["-Wall"] +Common_CFlags += ["-Werror"] + +cc_library_shared { + name: "vendor.qti.hardware.vibrator.impl", + vendor: true, + cflags: Common_CFlags, + srcs: [ + "Vibrator.cpp", + ], + shared_libs: [ + "libcutils", + "libutils", + "liblog", + "libqtivibratoreffect", + "libsoc_helper", + "libbinder_ndk", + "android.hardware.vibrator-ndk_platform", + ], + export_include_dirs: ["include"] +} + +cc_binary { + name: "vendor.qti.hardware.vibrator.service", + vendor: true, + relative_install_path: "hw", + init_rc: ["vendor.qti.hardware.vibrator.service.rc"], + vintf_fragments: [ + "vendor.qti.hardware.vibrator.service.xml", + ], + cflags: Common_CFlags, + srcs: [ + "service.cpp", + ], + shared_libs: [ + "libcutils", + "libutils", + "libbase", + "libbinder_ndk", + "android.hardware.vibrator-ndk_platform", + "vendor.qti.hardware.vibrator.impl", + ], +} + +cc_library_shared { + name: "vendor.qti.hardware.vibrator.offload.impl", + vendor: true, + cflags: Common_CFlags + ["-DTARGET_SUPPORTS_OFFLOAD"], + srcs: [ + "Vibrator.cpp", + "VibratorOffload.cpp", + ], + shared_libs: [ + "libcutils", + "libutils", + "liblog", + "libqtivibratoreffect", + "libqtivibratoreffectoffload", + "libsoc_helper", + "libbinder_ndk", + "android.hardware.vibrator-ndk_platform", + ], + export_include_dirs: ["include"], + header_libs: ["qti_kernel_headers"], +} + +cc_binary { + name: "vendor.qti.hardware.vibrator.offload.service", + vendor: true, + relative_install_path: "hw", + init_rc: ["vendor.qti.hardware.vibrator.offload.service.rc"], + vintf_fragments: [ + "vendor.qti.hardware.vibrator.offload.service.xml", + ], + cflags: Common_CFlags + ["-DTARGET_SUPPORTS_OFFLOAD"], + srcs: [ + "service.cpp", + ], + shared_libs: [ + "libcutils", + "libutils", + "libbase", + "libbinder_ndk", + "android.hardware.vibrator-ndk_platform", + "vendor.qti.hardware.vibrator.offload.impl", + ], +} diff --git a/aidl/vibrator/Vibrator.cpp b/aidl/vibrator/Vibrator.cpp new file mode 100644 index 0000000..fab0415 --- /dev/null +++ b/aidl/vibrator/Vibrator.cpp @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "vendor.qti.vibrator" + +#include +#include +#include +#include +#include +#include +#include + +#include "include/Vibrator.h" +#ifdef USE_EFFECT_STREAM +#include "effect.h" +#endif + +extern "C" { +#include "libsoc_helper.h" +} +namespace aidl { +namespace android { +namespace hardware { +namespace vibrator { + +#define STRONG_MAGNITUDE 0x7fff +#define MEDIUM_MAGNITUDE 0x5fff +#define LIGHT_MAGNITUDE 0x3fff +#define INVALID_VALUE -1 +#define CUSTOM_DATA_LEN 3 +#define NAME_BUF_SIZE 32 + +#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8))) + +static const char LED_DEVICE[] = "/sys/class/leds/vibrator"; + +InputFFDevice::InputFFDevice() +{ + DIR *dp; + struct dirent *dir; + uint8_t ffBitmask[FF_CNT / 8]; + char devicename[PATH_MAX]; + const char *INPUT_DIR = "/dev/input/"; + char name[NAME_BUF_SIZE]; + int fd, ret; + soc_info_v0_1_t soc = {MSM_CPU_UNKNOWN}; + + mVibraFd = INVALID_VALUE; + mSupportGain = false; + mSupportEffects = false; + mSupportExternalControl = false; + mCurrAppId = INVALID_VALUE; + mCurrMagnitude = 0x7fff; + mInExternalControl = false; + + dp = opendir(INPUT_DIR); + if (!dp) { + ALOGE("open %s failed, errno = %d", INPUT_DIR, errno); + return; + } + + memset(ffBitmask, 0, sizeof(ffBitmask)); + while ((dir = readdir(dp)) != NULL){ + if (dir->d_name[0] == '.' && + (dir->d_name[1] == '\0' || + (dir->d_name[1] == '.' && dir->d_name[2] == '\0'))) + continue; + + snprintf(devicename, PATH_MAX, "%s%s", INPUT_DIR, dir->d_name); + fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR)); + if (fd < 0) { + ALOGE("open %s failed, errno = %d", devicename, errno); + continue; + } + + ret = TEMP_FAILURE_RETRY(ioctl(fd, EVIOCGNAME(sizeof(name)), name)); + if (ret == -1) { + ALOGE("get input device name %s failed, errno = %d\n", devicename, errno); + close(fd); + continue; + } + + if (strcmp(name, "qcom-hv-haptics") && strcmp(name, "qti-haptics")) { + ALOGD("not a qcom/qti haptics device\n"); + close(fd); + continue; + } + + ALOGI("%s is detected at %s\n", name, devicename); + ret = TEMP_FAILURE_RETRY(ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffBitmask)), ffBitmask)); + if (ret == -1) { + ALOGE("ioctl failed, errno = %d", errno); + close(fd); + continue; + } + + if (test_bit(FF_CONSTANT, ffBitmask) || + test_bit(FF_PERIODIC, ffBitmask)) { + mVibraFd = fd; + if (test_bit(FF_CUSTOM, ffBitmask)) + mSupportEffects = true; + if (test_bit(FF_GAIN, ffBitmask)) + mSupportGain = true; + + get_soc_info(&soc); + ALOGD("msm CPU SoC ID: %d\n", soc.msm_cpu); + switch (soc.msm_cpu) { + case MSM_CPU_LAHAINA: + case APQ_CPU_LAHAINA: + case MSM_CPU_SHIMA: + case MSM_CPU_SM8325: + case APQ_CPU_SM8325P: + case MSM_CPU_YUPIK: + mSupportExternalControl = true; + break; + default: + mSupportExternalControl = false; + break; + } + break; + } + + close(fd); + } + + closedir(dp); +} + +/** Play vibration + * + * @param effectId: ID of the predefined effect will be played. If effectId is valid + * (non-negative value), the timeoutMs value will be ignored, and the + * real playing length will be set in param@playLengtMs and returned + * to VibratorService. If effectId is invalid, value in param@timeoutMs + * will be used as the play length for playing a constant effect. + * @param timeoutMs: playing length, non-zero means playing, zero means stop playing. + * @param playLengthMs: the playing length in ms unit which will be returned to + * VibratorService if the request is playing a predefined effect. + * The custom_data in periodic is reused for returning the playLengthMs + * from kernel space to userspace if the pattern is defined in kernel + * driver. It's been defined with following format: + * . + * The effect-ID is used for passing down the predefined effect to + * kernel driver, and the rest two parameters are used for returning + * back the real playing length from kernel driver. + */ +int InputFFDevice::play(int effectId, uint32_t timeoutMs, long *playLengthMs) { + struct ff_effect effect; + struct input_event play; + int16_t data[CUSTOM_DATA_LEN] = {0, 0, 0}; + int ret; +#ifdef USE_EFFECT_STREAM + const struct effect_stream *stream; +#endif + + /* For QMAA compliance, return OK even if vibrator device doesn't exist */ + if (mVibraFd == INVALID_VALUE) { + if (playLengthMs != NULL) + *playLengthMs = 0; + return 0; + } + + if (timeoutMs != 0) { + if (mCurrAppId != INVALID_VALUE) { + ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId)); + if (ret == -1) { + ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno); + goto errout; + } + mCurrAppId = INVALID_VALUE; + } + + memset(&effect, 0, sizeof(effect)); + if (effectId != INVALID_VALUE) { + data[0] = effectId; + effect.type = FF_PERIODIC; + effect.u.periodic.waveform = FF_CUSTOM; + effect.u.periodic.magnitude = mCurrMagnitude; + effect.u.periodic.custom_data = data; + effect.u.periodic.custom_len = sizeof(int16_t) * CUSTOM_DATA_LEN; +#ifdef USE_EFFECT_STREAM + stream = get_effect_stream(effectId); + if (stream != NULL) { + effect.u.periodic.custom_data = (int16_t *)stream; + effect.u.periodic.custom_len = sizeof(*stream); + } +#endif + } else { + effect.type = FF_CONSTANT; + effect.u.constant.level = mCurrMagnitude; + effect.replay.length = timeoutMs; + } + + effect.id = mCurrAppId; + effect.replay.delay = 0; + + ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCSFF, &effect)); + if (ret == -1) { + ALOGE("ioctl EVIOCSFF failed, errno = %d", -errno); + goto errout; + } + + mCurrAppId = effect.id; + if (effectId != INVALID_VALUE && playLengthMs != NULL) { + *playLengthMs = data[1] * 1000 + data[2]; +#ifdef USE_EFFECT_STREAM + if (stream != NULL && stream->play_rate_hz != 0) + *playLengthMs = ((stream->length * 1000) / stream->play_rate_hz) + 1; +#endif + } + + play.value = 1; + play.type = EV_FF; + play.code = mCurrAppId; + play.time.tv_sec = 0; + play.time.tv_usec = 0; + ret = TEMP_FAILURE_RETRY(write(mVibraFd, (const void*)&play, sizeof(play))); + if (ret == -1) { + ALOGE("write failed, errno = %d\n", -errno); + ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId)); + if (ret == -1) + ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno); + goto errout; + } + } else if (mCurrAppId != INVALID_VALUE) { + ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId)); + if (ret == -1) { + ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno); + goto errout; + } + mCurrAppId = INVALID_VALUE; + } + return 0; + +errout: + mCurrAppId = INVALID_VALUE; + return ret; +} + +int InputFFDevice::on(int32_t timeoutMs) { + return play(INVALID_VALUE, timeoutMs, NULL); +} + +int InputFFDevice::off() { + return play(INVALID_VALUE, 0, NULL); +} + +int InputFFDevice::setAmplitude(uint8_t amplitude) { + int tmp, ret; + struct input_event ie; + + /* For QMAA compliance, return OK even if vibrator device doesn't exist */ + if (mVibraFd == INVALID_VALUE) + return 0; + + tmp = amplitude * (STRONG_MAGNITUDE - LIGHT_MAGNITUDE) / 255; + tmp += LIGHT_MAGNITUDE; + ie.type = EV_FF; + ie.code = FF_GAIN; + ie.value = tmp; + + ret = TEMP_FAILURE_RETRY(write(mVibraFd, &ie, sizeof(ie))); + if (ret == -1) { + ALOGE("write FF_GAIN failed, errno = %d", -errno); + return ret; + } + + mCurrMagnitude = tmp; + return 0; +} + +int InputFFDevice::playEffect(int effectId, EffectStrength es, long *playLengthMs) { + switch (es) { + case EffectStrength::LIGHT: + mCurrMagnitude = LIGHT_MAGNITUDE; + break; + case EffectStrength::MEDIUM: + mCurrMagnitude = MEDIUM_MAGNITUDE; + break; + case EffectStrength::STRONG: + mCurrMagnitude = STRONG_MAGNITUDE; + break; + default: + return -1; + } + + return play(effectId, INVALID_VALUE, playLengthMs); +} + +LedVibratorDevice::LedVibratorDevice() { + char devicename[PATH_MAX]; + int fd; + + mDetected = false; + + snprintf(devicename, sizeof(devicename), "%s/%s", LED_DEVICE, "activate"); + fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR)); + if (fd < 0) { + ALOGE("open %s failed, errno = %d", devicename, errno); + return; + } + + mDetected = true; +} + +int LedVibratorDevice::write_value(const char *file, const char *value) { + int fd; + int ret; + + fd = TEMP_FAILURE_RETRY(open(file, O_WRONLY)); + if (fd < 0) { + ALOGE("open %s failed, errno = %d", file, errno); + return -errno; + } + + ret = TEMP_FAILURE_RETRY(write(fd, value, strlen(value) + 1)); + if (ret == -1) { + ret = -errno; + } else if (ret != strlen(value) + 1) { + /* even though EAGAIN is an errno value that could be set + by write() in some cases, none of them apply here. So, this return + value can be clearly identified when debugging and suggests the + caller that it may try to call vibrator_on() again */ + ret = -EAGAIN; + } else { + ret = 0; + } + + errno = 0; + close(fd); + + return ret; +} + +int LedVibratorDevice::on(int32_t timeoutMs) { + char file[PATH_MAX]; + char value[32]; + int ret; + + snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "state"); + ret = write_value(file, "1"); + if (ret < 0) + goto error; + + snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "duration"); + snprintf(value, sizeof(value), "%u\n", timeoutMs); + ret = write_value(file, value); + if (ret < 0) + goto error; + + snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate"); + ret = write_value(file, "1"); + if (ret < 0) + goto error; + + return 0; + +error: + ALOGE("Failed to turn on vibrator ret: %d\n", ret); + return ret; +} + +int LedVibratorDevice::off() +{ + char file[PATH_MAX]; + int ret; + + snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate"); + ret = write_value(file, "0"); + return ret; +} + +ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) { + *_aidl_return = IVibrator::CAP_ON_CALLBACK; + + if (ledVib.mDetected) { + *_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK; + ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return); + return ndk::ScopedAStatus::ok(); + } + + if (ff.mSupportGain) + *_aidl_return |= IVibrator::CAP_AMPLITUDE_CONTROL; + if (ff.mSupportEffects) + *_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK; + if (ff.mSupportExternalControl) + *_aidl_return |= IVibrator::CAP_EXTERNAL_CONTROL; + + ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::off() { + int ret; + + ALOGD("QTI Vibrator off"); + if (ledVib.mDetected) + ret = ledVib.off(); + else + ret = ff.off(); + if (ret != 0) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC)); + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs, + const std::shared_ptr& callback) { + int ret; + + ALOGD("Vibrator on for timeoutMs: %d", timeoutMs); + if (ledVib.mDetected) + ret = ledVib.on(timeoutMs); + else + ret = ff.on(timeoutMs); + + if (ret != 0) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC)); + + if (callback != nullptr) { + std::thread([=] { + ALOGD("Starting on on another thread"); + usleep(timeoutMs * 1000); + ALOGD("Notifying on complete"); + if (!callback->onComplete().isOk()) { + ALOGE("Failed to call onComplete"); + } + }).detach(); + } + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength es, const std::shared_ptr& callback, int32_t* _aidl_return) { + long playLengthMs; + int ret; + + if (ledVib.mDetected) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); + + ALOGD("Vibrator perform effect %d", effect); + +#ifdef TARGET_SUPPORTS_OFFLOAD + if (effect < Effect::CLICK || effect > Effect::RINGTONE_15) +#else + if (effect < Effect::CLICK || effect > Effect::HEAVY_CLICK) +#endif + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); + + if (es != EffectStrength::LIGHT && es != EffectStrength::MEDIUM && es != EffectStrength::STRONG) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); + + ret = ff.playEffect((static_cast(effect)), es, &playLengthMs); + if (ret != 0) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC)); + + if (callback != nullptr) { + std::thread([=] { + ALOGD("Starting perform on another thread"); + usleep(playLengthMs * 1000); + ALOGD("Notifying perform complete"); + callback->onComplete(); + }).detach(); + } + + *_aidl_return = playLengthMs; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector* _aidl_return) { + if (ledVib.mDetected) + return ndk::ScopedAStatus::ok(); + +#ifdef TARGET_SUPPORTS_OFFLOAD + *_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD, + Effect::POP, Effect::HEAVY_CLICK, Effect::RINGTONE_12, + Effect::RINGTONE_13, Effect::RINGTONE_14, Effect::RINGTONE_15}; +#else + *_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD, + Effect::POP, Effect::HEAVY_CLICK}; +#endif + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) { + uint8_t tmp; + int ret; + + if (ledVib.mDetected) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); + + ALOGD("Vibrator set amplitude: %f", amplitude); + + if (amplitude <= 0.0f || amplitude > 1.0f) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT)); + + if (ff.mInExternalControl) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); + + tmp = (uint8_t)(amplitude * 0xff); + ret = ff.setAmplitude(tmp); + if (ret != 0) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC)); + + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) { + if (ledVib.mDetected) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); + + ALOGD("Vibrator set external control: %d", enabled); + if (!ff.mSupportExternalControl) + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); + + ff.mInExternalControl = enabled; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs __unused) { + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); +} + +ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize __unused) { + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); +} + +ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector* supported __unused) { + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); +} + +ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive __unused, + int32_t* durationMs __unused) { + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); +} + +ndk::ScopedAStatus Vibrator::compose(const std::vector& composite __unused, + const std::shared_ptr& callback __unused) { + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); +} + +ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector* _aidl_return __unused) { + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); +} + +ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id __unused, Effect effect __unused, + EffectStrength strength __unused) { + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); +} + +ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id __unused) { + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION)); +} + +} // namespace vibrator +} // namespace hardware +} // namespace android +} // namespace aidl + diff --git a/aidl/vibrator/VibratorOffload.cpp b/aidl/vibrator/VibratorOffload.cpp new file mode 100644 index 0000000..8c4e877 --- /dev/null +++ b/aidl/vibrator/VibratorOffload.cpp @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "vendor.qti.vibrator.offload" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/Vibrator.h" +#include "VibratorPatterns.h" + +extern "C" { +#include "libsoc_helper.h" +} + +namespace aidl { +namespace android { +namespace hardware { +namespace vibrator { + +#define UEVENT_MSG_LEN 1024 +#define SLATE_EVENT "SLATE_EVENT=" +#define SLATE_EVENT_STRING_LEN 12 //length of SLATE_EVENT + +PatternOffload::PatternOffload() +{ + std::thread t(&PatternOffload::SSREventListener, this); + t.detach(); +} + +void PatternOffload::SSREventListener(void) +{ + int device_fd, n, ssr_event = 0; + char msg[UEVENT_MSG_LEN + 2]; + char *msg_ptr = msg; + + /* Offload during the bootup */ + SendPatterns(); + + device_fd = uevent_open_socket(64*1024, true); + if(device_fd < 0) + { + ALOGE("open socket failed: %d", device_fd); + return; + } + + while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) { + if (n <= 0 || n >= UEVENT_MSG_LEN) { + ALOGE("Message length %d is not correct\n", n); + continue; + } + msg[n] = '\0'; + msg[n+1] = '\0'; + if (strstr(msg, "slate_com_dev")) { + while(*msg_ptr) { + if(!strncmp(msg_ptr, SLATE_EVENT, SLATE_EVENT_STRING_LEN)) { + msg_ptr += SLATE_EVENT_STRING_LEN; + ssr_event = (atoi(msg_ptr)); + switch(ssr_event) { + case SLATE_AFTER_POWER_UP: + ALOGD("SLATE is powered up"); + SendPatterns(); + break; + } + } + while(*msg_ptr++); + } + } + } +} + +/** Offload patterns + * The sequence of steps in offloading patterns. + * 1. Open the Glink channel to offload the patterns + * 2. Send the configuration/meta data to co-proc + * 3. Wait for the response from the co-proc + * 4. Send the pattern data to co-proc + * 5. Wait for the response + * 6. Exit + */ +void PatternOffload::SendPatterns() +{ + uint8_t *data; + uint32_t len; + int32_t rc; + + rc = initChannel(); + if (rc < 0) + return; + + rc = get_pattern_config(&data, &len); + if (rc < 0 || !data) + return; + + /* Send config data */ + rc = sendData(data, len); + if (rc < 0) + return; + + rc = get_pattern_data(&data, &len); + if (rc < 0 || !data) + return; + + /* Send pattern data */ + rc = sendData(data, len); + if (rc < 0) + return; + + free_pattern_mem(data); + + ALOGI("Patterns offloaded successfully\n"); +} + +int PatternOffload::sendData(uint8_t *data, int len) +{ + int rc, status = 0; + + if (!data || !len) + return -EINVAL; + + rc = GlinkCh.GlinkWrite(data, len); + if (rc < 0) + return rc; + + rc = GlinkCh.GlinkPoll(); + if (rc < 0) + return rc; + + rc = GlinkCh.GlinkRead((uint8_t *)&status, 4); + if (rc < 0) + return rc; + + if (status != OFFLOAD_SUCCESS) + return -EIO; + + return 0; +} + + +int PatternOffload::initChannel() +{ + std::string chname = "/dev/glinkpkt_slate_haptics_offload"; + int rc; + + rc = GlinkCh.GlinkOpen(chname); + if (rc < 0) + { + ALOGE("Failed to open Glink channel name %s\n", chname.c_str()); + return rc; + } + + return 0; +} + +#define GLINK_MAX_CONN_RETRIES 60 +int OffloadGlinkConnection::GlinkOpen(std::string& dev) +{ + int tries = GLINK_MAX_CONN_RETRIES; + dev_name = dev; + + do { + fd = ::open(dev_name.c_str(), O_RDWR); + tries--; + if (fd < 0) + { + ALOGE("%s: %s: open error(%s)", __func__, dev.c_str(), strerror(errno)); + sleep(1); + } + } while(-ETIMEDOUT == errno && tries > 0 ); + + return fd; +} + +int OffloadGlinkConnection::GlinkClose() +{ + if (fd >= 0) + { + ::close(fd); + fd = -1; + } + return 0; +} + +int OffloadGlinkConnection::GlinkPoll() +{ + ssize_t rc = 0; + struct pollfd poll_fd; + + // wait for Rx data available in fd, for 2 seconds timeout + poll_fd.fd = fd; + poll_fd.events = POLLIN; + + rc = ::poll(&poll_fd, 1, 2000); + + if(rc > 0) + { + if (poll_fd.revents & POLLIN) + return 0; + } else if (rc == 0) { + ALOGE("Glink poll timeout"); + } else { + ALOGE("Glink poll error: %s\n", strerror(errno)); + } + + return -1; +} + +int OffloadGlinkConnection::GlinkRead(uint8_t *data, size_t size) +{ + int rc = 0; + size_t bytes_read = 0; + + if (fd < 0) + return -1; + + if (0 != GlinkPoll()) + return -1; + + while (bytes_read < size) + { + rc = ::read(fd, data+bytes_read, size-bytes_read); + if (rc < 0) { + if (errno != EAGAIN) { + ALOGE("%s: Read error: %s, rc %d", __func__, + strerror(errno), rc); + goto read_error; + } + } else if (rc == 0) { + ALOGE("%s: Zero length packet received or hardware connection went off", + __func__); + } + bytes_read += rc; + } + return 0; + +read_error: + return -1; +} + +int OffloadGlinkConnection::GlinkWrite(uint8_t *buf, size_t buflen) +{ + size_t bytes_written_out = 0; + int rc = 0; + + if (fd < 0) + return -1; + + if (!buflen) { + ALOGE("%s: Invalid buffer len", __func__); + return 0; + } + + while (bytes_written_out < buflen) { + rc = ::write (fd, buf+bytes_written_out, buflen-bytes_written_out); + if (rc < 0) { + ALOGE("%s: Write returned failure %d", __func__, rc); + return errno; + } + bytes_written_out += rc; + } + rc = 0; + + return rc; +} + +} // namespace vibrator +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/vibrator/include/Vibrator.h b/aidl/vibrator/include/Vibrator.h new file mode 100644 index 0000000..8bfa9f5 --- /dev/null +++ b/aidl/vibrator/include/Vibrator.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2018,2020-2021, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace vibrator { + +class InputFFDevice { +public: + InputFFDevice(); + int playEffect(int effectId, EffectStrength es, long *playLengthMs); + int on(int32_t timeoutMs); + int off(); + int setAmplitude(uint8_t amplitude); + bool mSupportGain; + bool mSupportEffects; + bool mSupportExternalControl; + bool mInExternalControl; +private: + int play(int effectId, uint32_t timeoutMs, long *playLengthMs); + int mVibraFd; + int16_t mCurrAppId; + int16_t mCurrMagnitude; +}; + +class LedVibratorDevice { +public: + LedVibratorDevice(); + int on(int32_t timeoutMs); + int off(); + bool mDetected; +private: + int write_value(const char *file, const char *value); +}; + +#ifdef TARGET_SUPPORTS_OFFLOAD +class OffloadGlinkConnection { +public: + int GlinkOpen(std::string& dev); + int GlinkClose(); + int GlinkPoll(); + int GlinkRead(uint8_t *data, size_t size); + int GlinkWrite(uint8_t *buf, size_t buflen); +private: + std::string dev_name; + int fd; +}; + +class PatternOffload { +public: + PatternOffload(); + void SSREventListener(void); + void SendPatterns(); +private: + OffloadGlinkConnection GlinkCh; + int initChannel(); + int sendData(uint8_t *data, int len); +}; +#endif + +class Vibrator : public BnVibrator { +public: + class InputFFDevice ff; + class LedVibratorDevice ledVib; +#ifdef TARGET_SUPPORTS_OFFLOAD + class PatternOffload Offload; +#endif + ndk::ScopedAStatus getCapabilities(int32_t* _aidl_return) override; + ndk::ScopedAStatus off() override; + ndk::ScopedAStatus on(int32_t timeoutMs, + const std::shared_ptr& callback) override; + ndk::ScopedAStatus perform(Effect effect, EffectStrength strength, + const std::shared_ptr& callback, + int32_t* _aidl_return) override; + ndk::ScopedAStatus getSupportedEffects(std::vector* _aidl_return) override; + ndk::ScopedAStatus setAmplitude(float amplitude) override; + ndk::ScopedAStatus setExternalControl(bool enabled) override; + ndk::ScopedAStatus getCompositionDelayMax(int32_t* maxDelayMs); + ndk::ScopedAStatus getCompositionSizeMax(int32_t* maxSize); + ndk::ScopedAStatus getSupportedPrimitives(std::vector* supported) override; + ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive, + int32_t* durationMs) override; + ndk::ScopedAStatus compose(const std::vector& composite, + const std::shared_ptr& callback) override; + ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector* _aidl_return) override; + ndk::ScopedAStatus alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) override; + ndk::ScopedAStatus alwaysOnDisable(int32_t id) override; +}; + +} // namespace vibrator +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/aidl/vibrator/service.cpp b/aidl/vibrator/service.cpp new file mode 100644 index 0000000..4fe3118 --- /dev/null +++ b/aidl/vibrator/service.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "vendor.qti.hardware.vibrator.service" + +#include +#include +#include + +#include "Vibrator.h" + +using aidl::android::hardware::vibrator::Vibrator; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr vib = ndk::SharedRefBase::make(); + + const std::string instance = std::string() + Vibrator::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(vib->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/aidl/vibrator/vendor.qti.hardware.vibrator.offload.service.rc b/aidl/vibrator/vendor.qti.hardware.vibrator.offload.service.rc new file mode 100644 index 0000000..8675c62 --- /dev/null +++ b/aidl/vibrator/vendor.qti.hardware.vibrator.offload.service.rc @@ -0,0 +1,4 @@ +service vendor.qti.vibrator /vendor/bin/hw/vendor.qti.hardware.vibrator.offload.service + class hal + user system + group system input diff --git a/aidl/vibrator/vendor.qti.hardware.vibrator.offload.service.xml b/aidl/vibrator/vendor.qti.hardware.vibrator.offload.service.xml new file mode 100644 index 0000000..c8c5a51 --- /dev/null +++ b/aidl/vibrator/vendor.qti.hardware.vibrator.offload.service.xml @@ -0,0 +1,33 @@ + + + + android.hardware.vibrator + IVibrator/default + + diff --git a/aidl/vibrator/vendor.qti.hardware.vibrator.service.rc b/aidl/vibrator/vendor.qti.hardware.vibrator.service.rc new file mode 100644 index 0000000..ffa8ce9 --- /dev/null +++ b/aidl/vibrator/vendor.qti.hardware.vibrator.service.rc @@ -0,0 +1,4 @@ +service vendor.qti.vibrator /vendor/bin/hw/vendor.qti.hardware.vibrator.service + class hal + user system + group system input diff --git a/aidl/vibrator/vendor.qti.hardware.vibrator.service.xml b/aidl/vibrator/vendor.qti.hardware.vibrator.service.xml new file mode 100644 index 0000000..df29ada --- /dev/null +++ b/aidl/vibrator/vendor.qti.hardware.vibrator.service.xml @@ -0,0 +1,33 @@ + + + + android.hardware.vibrator + IVibrator/default + +