replace common qcom sources with samsung ones
This commit is contained in:
85
qcom/opensource/vibrator/aidl/Android.bp
Normal file
85
qcom/opensource/vibrator/aidl/Android.bp
Normal file
@@ -0,0 +1,85 @@
|
||||
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-V2-ndk",
|
||||
"vendor.qti.hardware.vibratorOL.impl",
|
||||
"vendor.qti.hardware.vibratorCL.impl",
|
||||
"vendor.qti.hardware.vibratorSel.impl",
|
||||
],
|
||||
export_include_dirs: ["include"],
|
||||
include_dirs: [
|
||||
"external/expat/lib",
|
||||
]
|
||||
}
|
||||
|
||||
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-V2-ndk",
|
||||
"vendor.qti.hardware.vibrator.impl",
|
||||
],
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "HapticsPolicy.xml",
|
||||
vendor: true,
|
||||
src: "HapticsPolicy.xml",
|
||||
}
|
||||
|
||||
cc_fuzz {
|
||||
name: "vendor.qti.hardware.vibrator-service.aidl_fuzzer",
|
||||
vendor: true,
|
||||
|
||||
shared_libs: [
|
||||
"liblog",
|
||||
"libbase",
|
||||
"libutils",
|
||||
"libcutils",
|
||||
"libbinder",
|
||||
"libbinder_ndk",
|
||||
"android.hardware.vibrator-V2-ndk",
|
||||
"vendor.qti.hardware.vibrator.impl",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libbinder_random_parcel",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"fuzzer.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-Wno-unused-parameter",
|
||||
"-Wno-unused-variable",
|
||||
"-fexceptions",
|
||||
],
|
||||
}
|
||||
56
qcom/opensource/vibrator/aidl/HapticsPolicy.xml
Normal file
56
qcom/opensource/vibrator/aidl/HapticsPolicy.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. -->
|
||||
<!-- SPDX-License-Identifier: BSD-3-Clause-Clear -->
|
||||
<!-- -->
|
||||
<!-- Redistribution and use in source and binary forms, with or without -->
|
||||
<!-- modification, are permitted (subject to the limitations in the -->
|
||||
<!-- disclaimer below) 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 Qualcomm Innovation Center, Inc. nor the names of its -->
|
||||
<!-- contributors may be used to endorse or promote products derived -->
|
||||
<!-- from this software without specific prior written permission. -->
|
||||
<!-- -->
|
||||
<!-- NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE -->
|
||||
<!-- GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT -->
|
||||
<!-- HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED -->
|
||||
<!-- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -->
|
||||
<!-- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -->
|
||||
<!-- IN NO EVENT SHALL THE COPYRIGHT HOLDER 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. -->
|
||||
|
||||
<!--The following policies will be applicable for Openloop (OL) by default-->
|
||||
<!--And rest everything will be considered for Closedloop (CL) -->
|
||||
|
||||
<hapticsPolicyConfiguration>
|
||||
<!--If timeout <= maxMs, then OL else CL -->
|
||||
<hapticsONAPI>
|
||||
<maxMs>3000</maxMs>
|
||||
</hapticsONAPI>
|
||||
<!--Following effects are supported by OL, remaining by CL-->
|
||||
<hapticsPerformAPI>
|
||||
<!-- 0 - CLICK, 1 - DOUBLE_CLICK, 2 - TICK, 3 - THUD, 4 - POP, 5 - HEAVY_CLICK-->
|
||||
<effect_id>0,1,2,3,4,5</effect_id>
|
||||
</hapticsPerformAPI>
|
||||
<!--If support == True, then OL else CL-->
|
||||
<hapticsComposeAPI>
|
||||
<SupportCompose>True</SupportCompose>
|
||||
</hapticsComposeAPI>
|
||||
<!--If support == True, then OL else CL-->
|
||||
<hapticsComposePwleAPI>
|
||||
<SupportComposePWLE>False</SupportComposePWLE>
|
||||
</hapticsComposePwleAPI>
|
||||
</hapticsPolicyConfiguration>
|
||||
364
qcom/opensource/vibrator/aidl/Vibrator.cpp
Normal file
364
qcom/opensource/vibrator/aidl/Vibrator.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Changes from Qualcomm Innovation Center, Inc. are provided under the following license:
|
||||
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#define LOG_TAG "vendor.qti.vibrator"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <log/log.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Vibrator.h"
|
||||
#include "VibratorOL/Vibrator.h"
|
||||
#include "VibratorCL/Vibrator.h"
|
||||
#include "VibratorSelector/VibratorSelector.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace vibrator {
|
||||
|
||||
class Vibrator::VibratorPrivate {
|
||||
private:
|
||||
VibratorOL mVibratorOL;
|
||||
VibratorCL mVibratorCL;
|
||||
IVibrator* mSelectedVibrator;
|
||||
std::mutex VibratorSelectionLock;
|
||||
bool mSupportCL;
|
||||
std::shared_ptr<VibratorSelector> mVibSelector;
|
||||
public:
|
||||
VibratorPrivate() {
|
||||
mSupportCL = mVibratorOL.mSupportVISense;
|
||||
mVibSelector = nullptr;
|
||||
mSelectedVibrator = &mVibratorOL;
|
||||
int32_t ret;
|
||||
|
||||
if (mSupportCL) {
|
||||
ret = VibratorSelector::init();
|
||||
if (!ret) {
|
||||
mVibSelector = VibratorSelector::GetInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus getCapabilities(int32_t* _aidl_return) {
|
||||
int32_t capabilities;
|
||||
|
||||
/* Merge the capabilities of both mVibratorOL and mVibratorCL */
|
||||
mVibratorOL.getCapabilities(_aidl_return);
|
||||
if (mSupportCL) {
|
||||
mVibratorCL.getCapabilities(&capabilities);
|
||||
*_aidl_return |= capabilities;
|
||||
}
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus on(int32_t timeoutMs, const std::shared_ptr<IVibratorCallback>& callback) {
|
||||
ndk::ScopedAStatus status;
|
||||
|
||||
VibratorSelectionLock.lock();
|
||||
|
||||
mSelectedVibrator = &mVibratorOL;
|
||||
if (mVibSelector && mVibSelector->getVibForOnApi(timeoutMs) == VIB_TYPE_CL)
|
||||
mSelectedVibrator = &mVibratorCL;
|
||||
|
||||
status = mSelectedVibrator->on(timeoutMs, callback);
|
||||
VibratorSelectionLock.unlock();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus off() {
|
||||
/* The selected vibrator should be always used to turn off vibration */
|
||||
ndk::ScopedAStatus status;
|
||||
|
||||
VibratorSelectionLock.lock();
|
||||
status = mSelectedVibrator->off();
|
||||
VibratorSelectionLock.unlock();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus perform(Effect effect, EffectStrength es, const std::shared_ptr<IVibratorCallback>& callback, int32_t* _aidl_return) {
|
||||
|
||||
int effect_id = static_cast<int> (effect);
|
||||
ndk::ScopedAStatus status;
|
||||
|
||||
VibratorSelectionLock.lock();
|
||||
|
||||
mSelectedVibrator = &mVibratorOL;
|
||||
if (mVibSelector && mVibSelector->getVibForPerformApi(effect_id) == VIB_TYPE_CL)
|
||||
mSelectedVibrator = &mVibratorCL;
|
||||
|
||||
status = mSelectedVibrator->perform(effect, es, callback, _aidl_return);
|
||||
VibratorSelectionLock.unlock();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus getSupportedEffects(std::vector<Effect>* _aidl_return) {
|
||||
std::vector<Effect> effectsCL;
|
||||
|
||||
/* Merge the effects being supported by both mVibratorOL and mVibratorCL */
|
||||
mVibratorOL.getSupportedEffects(_aidl_return);
|
||||
if (mSupportCL) {
|
||||
mVibratorCL.getSupportedEffects(&effectsCL);
|
||||
for (uint32_t i = 0; i < effectsCL.size() ; i++) {
|
||||
if (std::find(_aidl_return->begin(), _aidl_return->end(), effectsCL[i]) == _aidl_return->end())
|
||||
_aidl_return->insert(_aidl_return->end(), effectsCL[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus setAmplitude(float amplitude) {
|
||||
/* Set amplitude should be only called after On() vibration is enabled so use existing mSelectedVibrator */
|
||||
ndk::ScopedAStatus status;
|
||||
|
||||
VibratorSelectionLock.lock();
|
||||
status = mSelectedVibrator->setAmplitude(amplitude);
|
||||
VibratorSelectionLock.unlock();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus setExternalControl(bool enabled) {
|
||||
ndk::ScopedAStatus status;
|
||||
|
||||
VibratorSelectionLock.lock();
|
||||
|
||||
if (mSupportCL)
|
||||
mVibratorCL.setExternalControl(enabled);
|
||||
|
||||
status = mVibratorOL.setExternalControl(enabled);
|
||||
|
||||
VibratorSelectionLock.unlock();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus getCompositionDelayMax(int32_t* maxDelayMs) {
|
||||
ndk::ScopedAStatus status;
|
||||
|
||||
VibratorSelectionLock.lock();
|
||||
|
||||
mSelectedVibrator = &mVibratorOL;
|
||||
if (mVibSelector && mVibSelector->getVibForComposeApi() == VIB_TYPE_CL)
|
||||
mSelectedVibrator = &mVibratorCL;
|
||||
|
||||
status = mSelectedVibrator->getCompositionDelayMax(maxDelayMs);
|
||||
VibratorSelectionLock.unlock();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus getCompositionSizeMax(int32_t* maxSize) {
|
||||
ndk::ScopedAStatus status;
|
||||
|
||||
VibratorSelectionLock.lock();
|
||||
|
||||
mSelectedVibrator = &mVibratorOL;
|
||||
if (mVibSelector && mVibSelector->getVibForComposeApi() == VIB_TYPE_CL)
|
||||
mSelectedVibrator = &mVibratorCL;
|
||||
|
||||
status = mSelectedVibrator->getCompositionSizeMax(maxSize);
|
||||
VibratorSelectionLock.unlock();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
|
||||
ndk::ScopedAStatus status;
|
||||
|
||||
VibratorSelectionLock.lock();
|
||||
|
||||
mSelectedVibrator = &mVibratorOL;
|
||||
if (mVibSelector && mVibSelector->getVibForComposeApi() == VIB_TYPE_CL)
|
||||
mSelectedVibrator = &mVibratorCL;
|
||||
|
||||
status = mSelectedVibrator->getSupportedPrimitives(supported);
|
||||
VibratorSelectionLock.unlock();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive, int32_t* durationMs) {
|
||||
ndk::ScopedAStatus status;
|
||||
|
||||
VibratorSelectionLock.lock();
|
||||
|
||||
mSelectedVibrator = &mVibratorOL;
|
||||
if (mVibSelector && mVibSelector->getVibForComposeApi() == VIB_TYPE_CL)
|
||||
mSelectedVibrator = &mVibratorCL;
|
||||
|
||||
status = mSelectedVibrator->getPrimitiveDuration(primitive, durationMs);
|
||||
VibratorSelectionLock.unlock();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus compose(const std::vector<CompositeEffect>& composite,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) {
|
||||
ndk::ScopedAStatus status;
|
||||
|
||||
VibratorSelectionLock.lock();
|
||||
|
||||
mSelectedVibrator = &mVibratorOL;
|
||||
if (mVibSelector && mVibSelector->getVibForComposeApi() == VIB_TYPE_CL)
|
||||
mSelectedVibrator = &mVibratorCL;
|
||||
|
||||
status = mSelectedVibrator->compose(composite, callback);
|
||||
VibratorSelectionLock.unlock();
|
||||
return status;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Vibrator::Vibrator() {
|
||||
pImpl = new Vibrator::VibratorPrivate;
|
||||
}
|
||||
|
||||
Vibrator::~Vibrator() {
|
||||
if (NULL != pImpl) {
|
||||
delete pImpl;
|
||||
pImpl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
|
||||
return pImpl->getCapabilities(_aidl_return);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::off() {
|
||||
return pImpl->off();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) {
|
||||
return pImpl->on(timeoutMs, callback);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength es, const std::shared_ptr<IVibratorCallback>& callback, int32_t* _aidl_return) {
|
||||
return pImpl->perform(effect, es, callback, _aidl_return);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
|
||||
return pImpl->getSupportedEffects(_aidl_return);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
|
||||
return pImpl->setAmplitude(amplitude);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
|
||||
return pImpl->setExternalControl(enabled);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) {
|
||||
return pImpl->getCompositionDelayMax(maxDelayMs);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) {
|
||||
return pImpl->getCompositionSizeMax(maxSize);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
|
||||
return pImpl->getSupportedPrimitives(supported);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
|
||||
int32_t* durationMs) {
|
||||
return pImpl->getPrimitiveDuration(primitive, durationMs);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) {
|
||||
return pImpl->compose(composite, callback);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _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));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getResonantFrequency(float* resonantFreqHz __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getQFactor(float* qFactor __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getFrequencyResolution(float* freqResolutionHz __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float* freqMinimumHz __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float>* _aidl_return __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t* durationMs __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t* maxSize __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking>* supported __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle>& composite __unused,
|
||||
const std::shared_ptr<IVibratorCallback>& callback __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
} // namespace vibrator
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
||||
22
qcom/opensource/vibrator/aidl/VibratorCL/Android.bp
Normal file
22
qcom/opensource/vibrator/aidl/VibratorCL/Android.bp
Normal file
@@ -0,0 +1,22 @@
|
||||
COMMON_CFLAGS = ["-Wall"]
|
||||
COMMON_CFLAGS += ["-Werror"]
|
||||
|
||||
cc_library_shared {
|
||||
name: "vendor.qti.hardware.vibratorCL.impl",
|
||||
cflags: COMMON_CFLAGS,
|
||||
vendor: true,
|
||||
srcs: ["Vibrator.cpp"],
|
||||
include_dirs: ["vendor/qcom/opensource/pal/inc",
|
||||
"vendor/qcom/proprietary/args/"],
|
||||
header_libs: [
|
||||
"libspf-headers",
|
||||
"libarosal_headers",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"android.hardware.vibrator-V2-ndk",
|
||||
"libbinder_ndk",
|
||||
"liblog",
|
||||
"libpalclient",
|
||||
],
|
||||
}
|
||||
749
qcom/opensource/vibrator/aidl/VibratorCL/Vibrator.cpp
Normal file
749
qcom/opensource/vibrator/aidl/VibratorCL/Vibrator.cpp
Normal file
@@ -0,0 +1,749 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted (subject to the limitations in the
|
||||
* disclaimer below) 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 Qualcomm Innovation Center, Inc. nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
|
||||
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
|
||||
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.vibratorCL"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/input.h>
|
||||
#include <log/log.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include "PalApi.h"
|
||||
#include "PalDefs.h"
|
||||
#include "rx_haptics_api.h"
|
||||
#include "wsa_haptics_vi_api.h"
|
||||
#include "Vibrator.h"
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace vibrator {
|
||||
|
||||
#define VIB_INVALID_VALUE -1
|
||||
#define WAKEUP_MIN_IDLE_CHECK (1000 * 10)
|
||||
#define MIN_EFFECT_TIME 50
|
||||
#define DYNAMIC_CALIB_TIMEOUT (30 * 60)
|
||||
#define OUT_BUFFER_SIZE 480
|
||||
#define OUT_BUFFER_COUNT 2
|
||||
#define MAX_EFFECTS_SUPPORTED 100
|
||||
#define COMPOSE_EFFECT_DURATION_INMS 10
|
||||
|
||||
static constexpr int32_t ComposeDelayMaxMs = 1000;
|
||||
static constexpr int32_t ComposeSizeMax = 256;
|
||||
|
||||
static struct pal_stream_attributes stream_attributes;
|
||||
static struct pal_device *pal_devices;
|
||||
static pal_stream_handle_t *pal_stream_handle_;
|
||||
struct pal_buffer_config out_buf_config;
|
||||
struct pal_buffer_config in_buf_config;
|
||||
struct pal_buffer out_buffer;
|
||||
uint8_t HapticsState = 2;
|
||||
int MaxSupportedPCMeffect = 0;
|
||||
uint8_t pcm_playback_supported;
|
||||
int GlobaleffectId = 0;
|
||||
|
||||
int32_t HapticsSetParameters(uint32_t param_mode, pal_param_haptics_cnfg_t payload);
|
||||
|
||||
std::vector<haptics_effect_config_t> VibratorCL::PcmEffectInfo;
|
||||
std::mutex VibratorCL::EventMutex;
|
||||
std::mutex VibratorCL::HapticsMutex;
|
||||
std::condition_variable VibratorCL::cv;
|
||||
std::condition_variable VibratorCL::Eventcv;
|
||||
std::thread VibratorCL::OffThread;
|
||||
std::atomic<bool> VibratorCL::CalThrdCreated;
|
||||
|
||||
bool VibratorCL::OffThrdCreated;
|
||||
bool VibratorCL::ActiveUsecase = false;
|
||||
bool VibratorCL::inComposition = false;
|
||||
|
||||
VibratorCL::VibratorCL()
|
||||
{
|
||||
int ret;
|
||||
mSupportGain = true;
|
||||
mSupportEffects = true;
|
||||
mSupportExternalControl = true;
|
||||
inComposition = false;
|
||||
|
||||
ret = pal_init();
|
||||
if (ret) {
|
||||
ALOGD("pal_init failed ret=(%d)", ret);
|
||||
}
|
||||
|
||||
std::thread dynamicCalThread(&VibratorCL::HapticsCalibThread, this);
|
||||
dynamicCalThread.detach();
|
||||
}
|
||||
|
||||
VibratorCL::~VibratorCL()
|
||||
{
|
||||
CalThrdCreated.store(false);
|
||||
}
|
||||
|
||||
void VibratorCL::HapticsCalibThread() {
|
||||
|
||||
int32_t status = 0;
|
||||
|
||||
HapticsPCMRead();
|
||||
|
||||
pal_haptics_payload hapModeVal;
|
||||
|
||||
CalThrdCreated.store(true);
|
||||
hapModeVal.operationMode = PAL_HAP_MODE_DYNAMIC_CAL;
|
||||
|
||||
while (CalThrdCreated.load()) {
|
||||
ALOGE("set dynamic calib param\n");
|
||||
status = pal_set_param(PAL_PARAM_ID_HAPTICS_MODE,
|
||||
(void*)&hapModeVal, sizeof(pal_haptics_payload));
|
||||
if(status != 0)
|
||||
ALOGE("Error:Dynamic cal set failed\n");
|
||||
|
||||
ALOGE("wait for %d seconds\n",DYNAMIC_CALIB_TIMEOUT);
|
||||
sleep(DYNAMIC_CALIB_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
void VibratorCL::HapticsPCMRead() {
|
||||
|
||||
std::ifstream inFile;
|
||||
std::string effect,FinalFilepath;
|
||||
std::string file_path = "/vendor/etc/effect";
|
||||
uint8_t effectCount = 0;
|
||||
float EffectDuration;
|
||||
struct haptics_effect_config_t HapticPcmCfg = {};
|
||||
|
||||
PcmEffectInfo.clear();
|
||||
for (effectCount = 0;effectCount < MAX_EFFECTS_SUPPORTED; effectCount++) {
|
||||
effect = std::to_string(effectCount);
|
||||
FinalFilepath = file_path + effect +".raw";
|
||||
inFile.open(FinalFilepath, std::ios::binary);
|
||||
if (!inFile.is_open()) {
|
||||
ALOGE("File open failure exiting the thread.\n");
|
||||
break;
|
||||
}
|
||||
inFile.seekg(0, std::ios::end);
|
||||
HapticPcmCfg.size = inFile.tellg();
|
||||
//Assuming the haptics pcm data config 48K SampleRate,16 bitWidth,1 ch.
|
||||
EffectDuration = HapticPcmCfg.size/96000.0f;
|
||||
HapticPcmCfg.duration = EffectDuration * 1000;
|
||||
PcmEffectInfo.push_back(HapticPcmCfg);
|
||||
inFile.seekg(0, std::ios::beg);
|
||||
PcmEffectInfo[effectCount].data = (uint8_t *) calloc(1, HapticPcmCfg.size);
|
||||
|
||||
inFile.read(reinterpret_cast<char*> (PcmEffectInfo[effectCount].data), HapticPcmCfg.size);
|
||||
MaxSupportedPCMeffect++;
|
||||
inFile.close();
|
||||
}
|
||||
exit:
|
||||
MaxSupportedPCMeffect = MaxSupportedPCMeffect - 1;
|
||||
ALOGE("HapticsPCMREAD maxSupportedPCMeffects %d", MaxSupportedPCMeffect);
|
||||
}
|
||||
|
||||
bool VibratorCL::IsPCMSupported(int effectID) {
|
||||
if (effectID >= 0 && effectID <= MaxSupportedPCMeffect)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 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 VibratorCLService. If effectId is invalid, value in param@timeoutMs
|
||||
* will be used as the play length for playing a constant effect.
|
||||
* @param strenght: Strength of the haptics predefined 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
|
||||
* VibratorCLService 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:
|
||||
* <effect-ID, play-time-in-seconds, play-time-in-milliseconds>.
|
||||
* 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 VibratorCL::play(int effectId, int strength, long *playLengthMs, uint32_t timeoutMs, bool isCompose, float amplitude) {
|
||||
|
||||
int status = 0;
|
||||
pal_param_haptics_cnfg_t payload;
|
||||
|
||||
int32_t no_of_devices = 1;
|
||||
stream_attributes.type = PAL_STREAM_HAPTICS;
|
||||
stream_attributes.direction = PAL_AUDIO_OUTPUT;
|
||||
stream_attributes.info.opt_stream_info.haptics_type = PAL_STREAM_HAPTICS_TOUCH;
|
||||
|
||||
GlobaleffectId = effectId;
|
||||
pcm_playback_supported = IsPCMSupported(GlobaleffectId);
|
||||
|
||||
pal_devices = (struct pal_device *) calloc(no_of_devices, sizeof(struct pal_device));
|
||||
if (pal_devices == NULL)
|
||||
return -1;
|
||||
|
||||
pal_devices[0].id = PAL_DEVICE_OUT_HAPTICS_DEVICE;
|
||||
pal_devices[0].config.bit_width = 16;
|
||||
pal_devices[0].config.sample_rate = 48000;
|
||||
pal_devices[0].config.ch_info.channels = 1;
|
||||
|
||||
HapticsMutex.lock();
|
||||
ActiveUsecase = true;
|
||||
HapticsState = 0;
|
||||
cv.notify_all();
|
||||
|
||||
if (pal_stream_handle_ == 0) {
|
||||
status = pal_stream_open(&stream_attributes, no_of_devices, pal_devices, 0, NULL,
|
||||
(pal_stream_callback) &VibratorCL::StreamHapticsCallback, 0, &pal_stream_handle_);
|
||||
if (status) {
|
||||
ALOGE("Error:Failed to open stream\n");
|
||||
goto exit;
|
||||
}
|
||||
ALOGD("Stream Opened successful\n");
|
||||
}
|
||||
|
||||
payload.mode = PAL_STREAM_HAPTICS_TOUCH;
|
||||
payload.effect_id = effectId;
|
||||
payload.strength = strength;
|
||||
payload.time = timeoutMs;
|
||||
payload.ch_mask = 1;
|
||||
payload.isCompose = isCompose;
|
||||
payload.amplitude = isCompose ? amplitude : 0.5;
|
||||
payload.buffer_size = 0;
|
||||
if (pcm_playback_supported) {
|
||||
payload.mode = PAL_STREAM_HAPTICS_PCM;
|
||||
payload.buffer_size = PcmEffectInfo[GlobaleffectId].size;
|
||||
ALOGD("pcm playback Effect ID %d", GlobaleffectId);
|
||||
}
|
||||
|
||||
status = HapticsSetParameters(PAL_PARAM_ID_HAPTICS_CNFG, payload);
|
||||
if (status) {
|
||||
ALOGD("Error:Failed to Set haptics wavegen param for haptics");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
status = pal_stream_start(pal_stream_handle_);
|
||||
if (status) {
|
||||
ALOGE("Error:Failed to Start haptics");
|
||||
goto close_stream;
|
||||
}
|
||||
|
||||
goto exit;
|
||||
|
||||
close_stream:
|
||||
pal_stream_close(pal_stream_handle_);
|
||||
pal_stream_handle_ = NULL;
|
||||
|
||||
exit:
|
||||
HapticsMutex.unlock();
|
||||
return status;
|
||||
}
|
||||
|
||||
void VibratorCL::offEffect() {
|
||||
int status = 0;
|
||||
|
||||
if (pal_stream_handle_) {
|
||||
HapticsWait();
|
||||
if (!ActiveUsecase && pal_stream_handle_) {
|
||||
status = StopHapticsStream();
|
||||
}
|
||||
}
|
||||
OffThrdCreated = false;
|
||||
ALOGD("Offeffect exit");
|
||||
}
|
||||
|
||||
int32_t VibratorCL::StopHapticsStream() {
|
||||
int status = 0;
|
||||
HapticsMutex.lock();
|
||||
status = pal_stream_stop(pal_stream_handle_);
|
||||
if (status) {
|
||||
ALOGE("Error:Failed to stop haptics stream");
|
||||
}
|
||||
status = pal_stream_close(pal_stream_handle_);
|
||||
if (status) {
|
||||
ALOGE("Error:Failed to close haptics stream");
|
||||
}
|
||||
pal_stream_handle_ = NULL;
|
||||
if (pal_devices)
|
||||
free(pal_devices);
|
||||
HapticsState = 2;
|
||||
HapticsMutex.unlock();
|
||||
return status;
|
||||
}
|
||||
|
||||
int32_t HapticsSetParameters(uint32_t param_mode, pal_param_haptics_cnfg_t payload)
|
||||
{
|
||||
int32_t status = -1;
|
||||
pal_param_payload *param_payload = NULL;
|
||||
|
||||
switch (param_mode) {
|
||||
case PAL_PARAM_ID_HAPTICS_CNFG:
|
||||
{
|
||||
pal_param_haptics_cnfg_t *hpconf;
|
||||
param_payload = (pal_param_payload *) calloc (1,
|
||||
sizeof(pal_param_payload) +
|
||||
sizeof(pal_param_haptics_cnfg_t) + payload.buffer_size);
|
||||
if (!param_payload)
|
||||
return status;
|
||||
|
||||
param_payload->payload_size = sizeof(pal_param_haptics_cnfg_t) + payload.buffer_size;
|
||||
hpconf = (struct pal_param_haptics_cnfg_t *)param_payload->payload;
|
||||
hpconf->mode = payload.mode;
|
||||
hpconf->effect_id = payload.effect_id;
|
||||
hpconf->strength = payload.strength;
|
||||
hpconf->time = payload.time;
|
||||
hpconf->amplitude = payload.amplitude;
|
||||
hpconf->ch_mask = payload.ch_mask;
|
||||
hpconf->isCompose = payload.isCompose;
|
||||
hpconf->buffer_size = payload.buffer_size;
|
||||
if (payload.buffer_size) {
|
||||
hpconf->buffer_ptr = (uint8_t *) param_payload->payload + sizeof(pal_param_haptics_cnfg_t);
|
||||
memcpy((uint8_t*) hpconf->buffer_ptr,
|
||||
(uint8_t *)&VibratorCL::PcmEffectInfo[GlobaleffectId].data[0], hpconf->buffer_size);
|
||||
}
|
||||
ALOGE("%s : size of buffer %d", __func__, sizeof(payload.buffer_ptr));
|
||||
status = pal_stream_set_param(pal_stream_handle_, param_mode, param_payload);
|
||||
|
||||
break;
|
||||
}
|
||||
case PARAM_ID_HAPTICS_WAVE_DESIGNER_STOP_PARAM:
|
||||
{
|
||||
param_id_haptics_wave_designer_wave_designer_stop_param_t HapticsStopParam;
|
||||
param_payload = (pal_param_payload *) calloc (1,
|
||||
sizeof(pal_param_payload) +
|
||||
sizeof(param_id_haptics_wave_designer_wave_designer_stop_param_t));
|
||||
if (!param_payload)
|
||||
return status;
|
||||
|
||||
HapticsStopParam.channel_mask = payload.ch_mask;
|
||||
param_payload->payload_size =
|
||||
sizeof(param_id_haptics_wave_designer_wave_designer_stop_param_t);
|
||||
memcpy(param_payload->payload, &HapticsStopParam, param_payload->payload_size);
|
||||
status = pal_stream_set_param(pal_stream_handle_, param_mode, param_payload);
|
||||
break;
|
||||
}
|
||||
case PARAM_ID_HAPTICS_WAVE_DESIGNER_UPDATE_PARAM:
|
||||
{
|
||||
param_payload = (pal_param_payload *) calloc (1,
|
||||
sizeof(pal_param_payload) +
|
||||
sizeof(pal_param_haptics_cnfg_t));
|
||||
if (!param_payload)
|
||||
return status;
|
||||
|
||||
param_payload->payload_size = sizeof(pal_param_haptics_cnfg_t);
|
||||
memcpy(param_payload->payload, &payload, param_payload->payload_size);
|
||||
status = pal_stream_set_param(pal_stream_handle_, param_mode, param_payload);
|
||||
break;
|
||||
}
|
||||
case PARAM_ID_HAPTICS_EX_VI_PERSISTENT:
|
||||
{
|
||||
param_payload = (pal_param_payload *) calloc (1,
|
||||
sizeof(pal_param_payload)+
|
||||
sizeof(pal_param_haptics_cnfg_t));
|
||||
if (!param_payload)
|
||||
return status;
|
||||
param_payload->payload_size = sizeof(pal_param_haptics_cnfg_t);
|
||||
memcpy(param_payload->payload, &payload, param_payload->payload_size);
|
||||
status = pal_stream_set_param(pal_stream_handle_, param_mode, param_payload);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ALOGE("%s : Param_mode is undefined %d", __func__, param_mode);
|
||||
break;
|
||||
}
|
||||
if(param_payload)
|
||||
free(param_payload);
|
||||
return status;
|
||||
}
|
||||
|
||||
int32_t VibratorCL::StreamHapticsCallback (uint64_t *stream_handle,
|
||||
uint32_t event_id, uint32_t *event_data, uint32_t event_size, uint64_t cookie)
|
||||
{
|
||||
int32_t status = 0;
|
||||
ALOGE("event received from DSP %d", *event_data);
|
||||
Eventcv.notify_all();
|
||||
HapticsState = *event_data;
|
||||
return status;
|
||||
}
|
||||
|
||||
int32_t VibratorCL::offCurrentEffect()
|
||||
{
|
||||
int status = 0;
|
||||
pal_param_haptics_cnfg_t payload;
|
||||
|
||||
if (pal_stream_handle_ && HapticsState == 0) {
|
||||
payload.ch_mask = 1;
|
||||
status = HapticsSetParameters(PARAM_ID_HAPTICS_WAVE_DESIGNER_STOP_PARAM,
|
||||
payload);
|
||||
if (status)
|
||||
ALOGD("Error:Failed to Set haptics stop param");
|
||||
else
|
||||
ALOGD("%s: stop effect successfull", __func__);
|
||||
HapticsState = 2;
|
||||
}
|
||||
else {
|
||||
ALOGD("%s: No current Effect is playing, skipping stop",__func__);
|
||||
}
|
||||
|
||||
if (pal_stream_handle_)
|
||||
status = HapticsSetParameters(PARAM_ID_HAPTICS_EX_VI_PERSISTENT,
|
||||
payload);
|
||||
pcm_playback_supported = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
void VibratorCL::HapticsWait()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(EventMutex);
|
||||
cv.wait_for(lock,
|
||||
std::chrono::milliseconds(WAKEUP_MIN_IDLE_CHECK));
|
||||
}
|
||||
|
||||
void VibratorCL::HapticsWaitTillWaveformComp()
|
||||
{
|
||||
std::unique_lock<std::mutex> eventlock(EventMutex);
|
||||
Eventcv.wait_for(eventlock,
|
||||
std::chrono::milliseconds(WAKEUP_MIN_IDLE_CHECK));
|
||||
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getCapabilities(int32_t* _aidl_return) {
|
||||
*_aidl_return = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
|
||||
IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL |
|
||||
IVibrator::CAP_COMPOSE_EFFECTS;
|
||||
ALOGD("VibratorCL reporting capabilities: %d", *_aidl_return);
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::off() {
|
||||
int ret = 0;
|
||||
|
||||
ALOGE("VibratorCL off ");
|
||||
ret = offCurrentEffect();
|
||||
if (ret)
|
||||
StopHapticsStream();
|
||||
|
||||
cv.notify_all();
|
||||
inComposition = false;
|
||||
ActiveUsecase = false;
|
||||
OffThread = std::thread (&VibratorCL::offEffect, this);
|
||||
OffThread.detach();
|
||||
OffThrdCreated = true;
|
||||
|
||||
exit:
|
||||
if (ret != 0)
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::on(int32_t timeoutMs,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) {
|
||||
int ret = 0;
|
||||
|
||||
if (ActiveUsecase) {
|
||||
ALOGE("VibratorCL ON: Haptics is already active skipping this instance");
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ALOGE("VibratorCL on for timeoutMs %d", timeoutMs);
|
||||
|
||||
ret = play(VIB_INVALID_VALUE, VIB_INVALID_VALUE, NULL, timeoutMs, false, VIB_INVALID_VALUE);
|
||||
|
||||
if (ret != 0)
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
|
||||
|
||||
if (callback != nullptr) {
|
||||
std::thread([=] {
|
||||
ALOGD("Starting ON on another thread");
|
||||
HapticsWaitTillWaveformComp();
|
||||
ALOGD("Notifying on complete");
|
||||
if (!callback->onComplete().isOk()) {
|
||||
ALOGE("Failed to call onComplete");
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::perform(Effect effect, EffectStrength es,
|
||||
const std::shared_ptr<IVibratorCallback>& callback, int32_t* _aidl_return) {
|
||||
int ret;
|
||||
long playLengthMs;
|
||||
|
||||
if (ActiveUsecase) {
|
||||
ALOGE("VibratorCL PERFORM: Haptics is already active skipping this instance");
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ALOGE("VibratorCL perform effect %d", effect);
|
||||
|
||||
if (effect < Effect::CLICK ||
|
||||
effect > Effect::HEAVY_CLICK)
|
||||
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 = play((static_cast<int>(effect)), (static_cast<int>(es)), &playLengthMs, VIB_INVALID_VALUE, false, VIB_INVALID_VALUE);
|
||||
|
||||
if (ret != 0)
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
|
||||
|
||||
if (callback != nullptr) {
|
||||
std::thread([=] {
|
||||
ALOGD("Starting perform on another thread");
|
||||
HapticsWaitTillWaveformComp();
|
||||
ALOGD("Notifying perform complete");
|
||||
callback->onComplete();
|
||||
}).detach();
|
||||
}
|
||||
|
||||
if(pcm_playback_supported) {
|
||||
ALOGD("effect Duration %d\n", PcmEffectInfo[GlobaleffectId].duration);
|
||||
*_aidl_return = PcmEffectInfo[GlobaleffectId].duration;
|
||||
} else
|
||||
*_aidl_return = MIN_EFFECT_TIME;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getSupportedEffects(std::vector<Effect>* _aidl_return) {
|
||||
*_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
|
||||
Effect::POP, Effect::HEAVY_CLICK};
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::setAmplitude(float amplitude) {
|
||||
|
||||
pal_param_haptics_cnfg_t payload;
|
||||
int status = -1;
|
||||
ALOGD("VibratorCL set amplitude: %f", amplitude);
|
||||
|
||||
if (amplitude <= 0.0f || amplitude > 1.0f)
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
|
||||
|
||||
payload.ch_mask = 1;
|
||||
payload.amplitude = amplitude;
|
||||
status = HapticsSetParameters(PARAM_ID_HAPTICS_WAVE_DESIGNER_UPDATE_PARAM, payload);
|
||||
if (status) {
|
||||
ALOGD("Error:Failed to Set update haptics param");
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
|
||||
}
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::setExternalControl(bool enabled) {
|
||||
|
||||
ALOGD("VibratorCL set external control: %d", enabled);
|
||||
if (!mSupportExternalControl)
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
|
||||
*supported = {
|
||||
CompositePrimitive::NOOP, CompositePrimitive::CLICK,
|
||||
CompositePrimitive::THUD, CompositePrimitive::SPIN,
|
||||
CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
|
||||
CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
|
||||
CompositePrimitive::LOW_TICK,
|
||||
};
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getPrimitiveDuration(CompositePrimitive primitive,
|
||||
int32_t* durationMs) {
|
||||
uint32_t primitive_id = static_cast<uint32_t>(primitive);
|
||||
*durationMs = MIN_EFFECT_TIME;
|
||||
|
||||
ALOGD("primitive ID %d duration is %dms", primitive, *durationMs);
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
void VibratorCL::composePlayThread(const std::vector<CompositeEffect>& composite,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) {
|
||||
long playLengthMs = 0;
|
||||
int ret = 0;
|
||||
|
||||
ALOGD("start a new thread for composeEffect");
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
auto duration = duration_cast<std::chrono::milliseconds>(stop - start);
|
||||
|
||||
for (auto& e : composite) {
|
||||
if (inComposition) {
|
||||
|
||||
ALOGD("Delay: %d, Scale: %f, primitive id: %d", e.delayMs, e.scale, static_cast<int>(e.primitive));
|
||||
if (e.delayMs) {
|
||||
if (duration < std::chrono::milliseconds(e.delayMs))
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(duration - std::chrono::milliseconds(e.delayMs)));
|
||||
}
|
||||
|
||||
ret = play((static_cast<int>(e.primitive)), VIB_INVALID_VALUE, &playLengthMs, VIB_INVALID_VALUE, true, e.scale);
|
||||
|
||||
if (ret != 0) {
|
||||
ALOGD("Play got failed");
|
||||
return;
|
||||
}
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
HapticsWaitTillWaveformComp();
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
|
||||
duration = duration_cast<std::chrono::milliseconds>(stop - start) - std::chrono::milliseconds(COMPOSE_EFFECT_DURATION_INMS);
|
||||
ALOGD("Delay in getting Waveform complete event: %d", duration);
|
||||
}
|
||||
}
|
||||
|
||||
ALOGD("Notifying composite complete");
|
||||
if (callback)
|
||||
callback->onComplete();
|
||||
|
||||
inComposition = false;
|
||||
}
|
||||
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::compose(const std::vector<CompositeEffect>& composite,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) {
|
||||
int status;
|
||||
|
||||
if (ActiveUsecase || inComposition) {
|
||||
ALOGE("VibratorCL Compose: Haptics is already active skipping this instance");
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
if (composite.size() > ComposeSizeMax) {
|
||||
ALOGE("Invalid Composite Size");
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
std::vector<CompositePrimitive> supported;
|
||||
getSupportedPrimitives(&supported);
|
||||
|
||||
for (auto& e : composite) {
|
||||
if (e.delayMs > ComposeDelayMaxMs) {
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
if (e.scale < 0.0f || e.scale > 1.0f) {
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
}
|
||||
|
||||
inComposition = true;
|
||||
|
||||
std::thread composeThread(&VibratorCL::composePlayThread, this, composite, callback);
|
||||
|
||||
composeThread.detach();
|
||||
ALOGD("trigger composition successfully");
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getCompositionDelayMax(int32_t* maxDelayMs) {
|
||||
*maxDelayMs = ComposeDelayMaxMs;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getCompositionSizeMax(int32_t* maxSize) {
|
||||
*maxSize = ComposeSizeMax;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::alwaysOnEnable(int32_t id __unused, Effect effect __unused,
|
||||
EffectStrength strength __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::alwaysOnDisable(int32_t id __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getResonantFrequency(float *resonantFreqHz __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getQFactor(float *qFactor __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getFrequencyResolution(float *freqResolutionHz __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getFrequencyMinimum(float *freqMinimumHz __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getPwlePrimitiveDurationMax(int32_t *durationMs __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getPwleCompositionSizeMax(int32_t *maxSize __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::getSupportedBraking(std::vector<Braking> *supported __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorCL::composePwle(const std::vector<PrimitivePwle> &composite __unused,
|
||||
const std::shared_ptr<IVibratorCallback> &callback __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
} // namespace vibrator
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
||||
124
qcom/opensource/vibrator/aidl/VibratorCL/Vibrator.h
Normal file
124
qcom/opensource/vibrator/aidl/VibratorCL/Vibrator.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted (subject to the limitations in the
|
||||
* disclaimer below) 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 Qualcomm Innovation Center, Inc. nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
|
||||
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
|
||||
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 <aidl/android/hardware/vibrator/BnVibrator.h>
|
||||
#include <thread>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace vibrator {
|
||||
|
||||
struct haptics_effect_config_t {
|
||||
size_t size;
|
||||
uint32_t duration;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
class VibratorCL : public BnVibrator {
|
||||
public:
|
||||
VibratorCL();
|
||||
~VibratorCL();
|
||||
bool mSupportGain;
|
||||
bool mSupportEffects;
|
||||
bool mSupportExternalControl;
|
||||
bool mInExternalControl;
|
||||
static std::mutex HapticsMutex;
|
||||
static std::mutex EventMutex;
|
||||
static std::condition_variable cv;
|
||||
static std::condition_variable Eventcv;
|
||||
static std::thread OffThread;
|
||||
static std::atomic<bool> CalThrdCreated;
|
||||
static std::vector<haptics_effect_config_t> PcmEffectInfo;
|
||||
static bool OffThrdCreated;
|
||||
static bool ActiveUsecase;
|
||||
enum haptics_mode_t {
|
||||
HAPTICS_HOSTLESS,
|
||||
HAPTICS_STREAMING,
|
||||
} haptics_mode_t;
|
||||
static bool inComposition;
|
||||
int on(int32_t timeoutMs);
|
||||
void offEffect();
|
||||
void HapticsWait();
|
||||
void HapticsCalibThread();
|
||||
void HapticsWaitTillWaveformComp();
|
||||
void HapticsPCMRead();
|
||||
bool IsPCMSupported(int effectID);
|
||||
int32_t StopHapticsStream();
|
||||
int32_t offCurrentEffect();
|
||||
static int32_t StreamHapticsCallback(uint64_t *stream_handle,
|
||||
uint32_t event_id, uint32_t *event_data,
|
||||
uint32_t event_size, uint64_t cookie);
|
||||
ndk::ScopedAStatus getCapabilities(int32_t* _aidl_return) override;
|
||||
ndk::ScopedAStatus off() override;
|
||||
ndk::ScopedAStatus on(int32_t timeoutMs,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) override;
|
||||
ndk::ScopedAStatus perform(Effect effect, EffectStrength strength,
|
||||
const std::shared_ptr<IVibratorCallback>& callback,
|
||||
int32_t* _aidl_return) override;
|
||||
ndk::ScopedAStatus getSupportedEffects(std::vector<Effect>* _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<CompositePrimitive>* supported) override;
|
||||
ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive,
|
||||
int32_t* durationMs) override;
|
||||
ndk::ScopedAStatus compose(const std::vector<CompositeEffect>& composite,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) override;
|
||||
ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) override;
|
||||
ndk::ScopedAStatus alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) override;
|
||||
ndk::ScopedAStatus alwaysOnDisable(int32_t id) override;
|
||||
ndk::ScopedAStatus getResonantFrequency(float *resonantFreqHz) override;
|
||||
ndk::ScopedAStatus getQFactor(float *qFactor) override;
|
||||
ndk::ScopedAStatus getFrequencyResolution(float *freqResolutionHz) override;
|
||||
ndk::ScopedAStatus getFrequencyMinimum(float *freqMinimumHz) override;
|
||||
ndk::ScopedAStatus getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) override;
|
||||
ndk::ScopedAStatus getPwlePrimitiveDurationMax(int32_t *durationMs) override;
|
||||
ndk::ScopedAStatus getPwleCompositionSizeMax(int32_t *maxSize) override;
|
||||
ndk::ScopedAStatus getSupportedBraking(std::vector<Braking>* supported) override;
|
||||
ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
|
||||
const std::shared_ptr<IVibratorCallback> &callback) override;
|
||||
private:
|
||||
int play(int effectId, int strength, long* playLengthMs, uint32_t timeoutMs, bool isCompose, float amplitude);
|
||||
void composePlayThread(const std::vector<CompositeEffect>& composite,
|
||||
const std::shared_ptr<IVibratorCallback>& callback);
|
||||
};
|
||||
|
||||
} // namespace vibrator
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
||||
19
qcom/opensource/vibrator/aidl/VibratorOL/Android.bp
Normal file
19
qcom/opensource/vibrator/aidl/VibratorOL/Android.bp
Normal file
@@ -0,0 +1,19 @@
|
||||
cc_library_shared {
|
||||
name: "vendor.qti.hardware.vibratorOL.impl",
|
||||
vendor: true,
|
||||
cflags: Common_CFlags,
|
||||
srcs: [
|
||||
"Vibrator.cpp",
|
||||
"VibratorOffload.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libcutils",
|
||||
"libutils",
|
||||
"liblog",
|
||||
"libqtivibratoreffect",
|
||||
"libqtivibratoreffectoffload",
|
||||
"libsoc_helper",
|
||||
"libbinder_ndk",
|
||||
"android.hardware.vibrator-V2-ndk",
|
||||
],
|
||||
}
|
||||
997
qcom/opensource/vibrator/aidl/VibratorOL/Vibrator.cpp
Normal file
997
qcom/opensource/vibrator/aidl/VibratorOL/Vibrator.cpp
Normal file
@@ -0,0 +1,997 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* Not a contribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Changes from Qualcomm Innovation Center, Inc. are provided under the following license:
|
||||
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#define LOG_TAG "vendor.qti.vibratorOL"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/input.h>
|
||||
#include <log/log.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <bits/epoll_event.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/poll.h>
|
||||
#include <thread>
|
||||
|
||||
#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 PRIMITIVE_ID_MASK 0x8000
|
||||
#define MAX_PATTERN_ID 32767
|
||||
|
||||
#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8)))
|
||||
|
||||
static const char LED_DEVICE[] = "/sys/class/leds/vibrator";
|
||||
static const char HAPTICS_SYSFS[] = "/sys/class/qcom-haptics";
|
||||
|
||||
static constexpr int32_t ComposeDelayMaxMs = 1000;
|
||||
static constexpr int32_t ComposeSizeMax = 256;
|
||||
|
||||
enum composeEvent {
|
||||
STOP_COMPOSE = 0,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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_KALAMA:
|
||||
case MSM_CPU_PINEAPPLE:
|
||||
case MSM_CPU_SUN:
|
||||
mSupportExternalControl = true;
|
||||
break;
|
||||
default:
|
||||
mSupportExternalControl = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
}
|
||||
|
||||
bool InputFFDevice::isPresent() {
|
||||
return (mVibraFd != INVALID_VALUE);
|
||||
}
|
||||
|
||||
/** 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:
|
||||
* <effect-ID, play-time-in-seconds, play-time-in-milliseconds>.
|
||||
* 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
|
||||
|
||||
mtx.lock();
|
||||
/* For QMAA compliance, return OK even if vibrator device doesn't exist */
|
||||
if (!isPresent()) {
|
||||
if (playLengthMs != NULL)
|
||||
*playLengthMs = 0;
|
||||
mtx.unlock();
|
||||
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;
|
||||
}
|
||||
mtx.unlock();
|
||||
return 0;
|
||||
|
||||
errout:
|
||||
mCurrAppId = INVALID_VALUE;
|
||||
mtx.unlock();
|
||||
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 (!isPresent())
|
||||
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) {
|
||||
if (effectId > MAX_PATTERN_ID) {
|
||||
ALOGE("effect id %d exceeds %d", effectId, MAX_PATTERN_ID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int InputFFDevice::playPrimitive(int primitiveId, float amplitude, long *playLengthMs) {
|
||||
int8_t tmp;
|
||||
int ret = 0;
|
||||
|
||||
if (primitiveId > MAX_PATTERN_ID) {
|
||||
ALOGE("primitive id %d exceeds %d", primitiveId, MAX_PATTERN_ID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
primitiveId |= PRIMITIVE_ID_MASK;
|
||||
tmp = (uint8_t)(amplitude * 0xff);
|
||||
mCurrMagnitude = tmp * (STRONG_MAGNITUDE - LIGHT_MAGNITUDE) / 255;
|
||||
mCurrMagnitude += LIGHT_MAGNITUDE;
|
||||
|
||||
ret = play(primitiveId, INVALID_VALUE, playLengthMs);
|
||||
if (ret != 0)
|
||||
ALOGE("Failed to play primitive %d", primitiveId);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static bool is_VI_sense_supported() {
|
||||
char visense_sysfs[50];
|
||||
char visense[3];
|
||||
int fd, ret;
|
||||
|
||||
ret = snprintf(visense_sysfs, sizeof(visense_sysfs), "%s%s", HAPTICS_SYSFS, "/visense_enabled");
|
||||
if (ret < 0) {
|
||||
ALOGE("Failed to generate visense_enabled path name, ret = %d\n", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
fd = TEMP_FAILURE_RETRY(open(visense_sysfs, O_RDONLY));
|
||||
if (fd < 0) {
|
||||
ALOGE("Open %s failed, fd = %d\n", visense_sysfs, fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = TEMP_FAILURE_RETRY(read(fd, visense, sizeof(visense)));
|
||||
close(fd);
|
||||
if (ret < 0) {
|
||||
ALOGE("Failed to read %s, errno = %d\n", visense_sysfs, errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
return atoi(visense);
|
||||
}
|
||||
|
||||
VibratorOL::VibratorOL() {
|
||||
struct epoll_event ev;
|
||||
|
||||
mSupportVISense = is_VI_sense_supported();
|
||||
|
||||
epollfd = INVALID_VALUE;
|
||||
pipefd[0] = INVALID_VALUE;
|
||||
pipefd[1] = INVALID_VALUE;
|
||||
inComposition = false;
|
||||
|
||||
if (!ff.mSupportEffects)
|
||||
return;
|
||||
|
||||
if (pipe(pipefd)) {
|
||||
ALOGE("Failed to get pipefd error=%d", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
epollfd = epoll_create1(0);
|
||||
if (epollfd < 0) {
|
||||
ALOGE("Failed to create epoll fd error=%d", errno);
|
||||
goto pipefd_close;
|
||||
}
|
||||
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = pipefd[0];
|
||||
|
||||
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefd[0], &ev) == -1) {
|
||||
ALOGE("Failed to add pipefd to epoll ctl error=%d", errno);
|
||||
goto epollfd_close;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
epollfd_close:
|
||||
close(epollfd);
|
||||
epollfd = INVALID_VALUE;
|
||||
pipefd_close:
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
pipefd[0] = INVALID_VALUE;
|
||||
pipefd[1] = INVALID_VALUE;
|
||||
}
|
||||
|
||||
VibratorOL::~VibratorOL() {
|
||||
if (epollfd != INVALID_VALUE)
|
||||
close(epollfd);
|
||||
if (pipefd[0] != INVALID_VALUE)
|
||||
close(pipefd[0]);
|
||||
if (pipefd[1] != INVALID_VALUE)
|
||||
close(pipefd[1]);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::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;
|
||||
*_aidl_return |= IVibrator::CAP_COMPOSE_EFFECTS;
|
||||
}
|
||||
if (ff.mSupportExternalControl)
|
||||
*_aidl_return |= IVibrator::CAP_EXTERNAL_CONTROL;
|
||||
|
||||
ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::off() {
|
||||
int ret;
|
||||
int composeEven = STOP_COMPOSE;
|
||||
|
||||
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));
|
||||
|
||||
if (inComposition) {
|
||||
ret = write(pipefd[1], &composeEven, sizeof(composeEven));
|
||||
if (ret < 0) {
|
||||
ALOGE("Failed to send STOP_COMPOSE event");
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
|
||||
}
|
||||
}
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::on(int32_t timeoutMs,
|
||||
const std::shared_ptr<IVibratorCallback>& 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 VibratorOL::perform(Effect effect, EffectStrength es, const std::shared_ptr<IVibratorCallback>& 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);
|
||||
|
||||
if (Offload.mEnabled == 1) {
|
||||
if ((effect < Effect::CLICK) ||
|
||||
((effect > Effect::HEAVY_CLICK) && (effect < Effect::RINGTONE_12)) ||
|
||||
(effect > Effect::RINGTONE_15))
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
} else {
|
||||
if (effect < Effect::CLICK || effect > Effect::HEAVY_CLICK)
|
||||
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<int>(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 VibratorOL::getSupportedEffects(std::vector<Effect>* _aidl_return) {
|
||||
if (ledVib.mDetected)
|
||||
return ndk::ScopedAStatus::ok();
|
||||
|
||||
if (Offload.mEnabled == 1)
|
||||
*_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};
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::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 VibratorOL::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 VibratorOL::getCompositionDelayMax(int32_t* maxDelayMs) {
|
||||
*maxDelayMs = ComposeDelayMaxMs;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getCompositionSizeMax(int32_t* maxSize) {
|
||||
*maxSize = ComposeSizeMax;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
|
||||
*supported = {
|
||||
CompositePrimitive::NOOP, CompositePrimitive::CLICK,
|
||||
CompositePrimitive::THUD, CompositePrimitive::SPIN,
|
||||
CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
|
||||
CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
|
||||
CompositePrimitive::LOW_TICK,
|
||||
};
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
static int getPrimitiveDurationFromSysfs(uint32_t primitive_id, int32_t* durationMs) {
|
||||
int count = 0;
|
||||
int fd = 0;
|
||||
int ret = 0;
|
||||
/* the Max primitive id is 32767, so define the size of primitive_buf to 6 */
|
||||
char primitive_buf[6];
|
||||
/* the max primitive_duration is the max value of int32, so define the size to 10 */
|
||||
char primitive_duration[10];
|
||||
char primitive_duration_sysfs[50];
|
||||
|
||||
ret = snprintf(primitive_duration_sysfs, sizeof(primitive_duration_sysfs), "%s%s", HAPTICS_SYSFS, "/primitive_duration");
|
||||
if (ret < 0) {
|
||||
ALOGE("Failed to get primitive duration node, ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
count = snprintf(primitive_buf, sizeof(primitive_buf), "%d%c", primitive_id, '\n');
|
||||
if (count < 0) {
|
||||
ALOGE("Failed to get primitive id, count = %d\n", count);
|
||||
ret = count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
fd = TEMP_FAILURE_RETRY(open(primitive_duration_sysfs, O_RDWR));
|
||||
if (fd < 0) {
|
||||
ALOGE("open %s failed, errno = %d", primitive_duration_sysfs, errno);
|
||||
ret = fd;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = TEMP_FAILURE_RETRY(write(fd, primitive_buf, count));
|
||||
if (ret < 0) {
|
||||
ALOGE("write primitive %d failed, errno = %d", primitive_id, errno);
|
||||
goto close_fd;
|
||||
}
|
||||
|
||||
ret = TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET));
|
||||
if (ret < 0) {
|
||||
ALOGE("lseek fd to file head failed, errno = %d", errno);
|
||||
goto close_fd;
|
||||
}
|
||||
|
||||
ret = TEMP_FAILURE_RETRY(read(fd, primitive_duration, sizeof(primitive_duration)));
|
||||
if (ret < 0) {
|
||||
ALOGE("read primitive %d failed, errno = %d", primitive_id, errno);
|
||||
goto close_fd;
|
||||
}
|
||||
|
||||
*durationMs = atoi(primitive_duration);
|
||||
*durationMs /= 1000;
|
||||
|
||||
close_fd:
|
||||
ret = TEMP_FAILURE_RETRY(close(fd));
|
||||
if (ret < 0) {
|
||||
ALOGE("close primitive duration device failed, errno = %d", errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getPrimitiveDuration(CompositePrimitive primitive,
|
||||
int32_t* durationMs) {
|
||||
uint32_t primitive_id = static_cast<uint32_t>(primitive);
|
||||
int ret = 0;
|
||||
|
||||
#ifdef USE_EFFECT_STREAM
|
||||
primitive_id |= PRIMITIVE_ID_MASK ;
|
||||
const struct effect_stream *stream;
|
||||
stream = get_effect_stream(primitive_id);
|
||||
if (stream != NULL && stream->play_rate_hz != 0)
|
||||
*durationMs = ((stream->length * 1000) / stream->play_rate_hz) + 1;
|
||||
|
||||
ALOGD("primitive-%d duration is %dms", primitive, *durationMs);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
#endif
|
||||
|
||||
/* For QMAA compliance */
|
||||
if (!ff.isPresent()) {
|
||||
*durationMs = 10; /* fake a constant duration for all primitives */
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ret = getPrimitiveDurationFromSysfs(primitive_id, durationMs);
|
||||
if (ret < 0)
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
|
||||
ALOGD("primitive-%d duration is %dms", primitive, *durationMs);
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
void VibratorOL::composePlayThread(VibratorOL *vibrator,
|
||||
const std::vector<CompositeEffect>& composite,
|
||||
const std::shared_ptr<IVibratorCallback>& callback){
|
||||
struct epoll_event events;
|
||||
long playLengthMs = 0;
|
||||
int nfd = 0;
|
||||
int status = 0;
|
||||
int ret = 0;
|
||||
|
||||
ALOGD("start a new thread for composeEffect");
|
||||
for (auto& e : composite) {
|
||||
if (e.delayMs) {
|
||||
nfd = epoll_wait(vibrator->epollfd, &events, 1, e.delayMs);
|
||||
if ((nfd == -1) && (errno != EINTR)) {
|
||||
ALOGE("Failed to wait delayMs, error=%d", errno);
|
||||
break;
|
||||
}
|
||||
|
||||
if (nfd > 0) {
|
||||
/* It's supposed that STOP_COMPOSE command is received so quit the composition */
|
||||
ret = read(vibrator->pipefd[0], &status, sizeof(int));
|
||||
if (ret < 0) {
|
||||
ALOGE("Failed to read stop status from pipe(delayMs), status = %d", status);
|
||||
break;
|
||||
}
|
||||
if (status == STOP_COMPOSE)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vibrator->ff.playPrimitive((static_cast<int>(e.primitive)), e.scale, &playLengthMs);
|
||||
nfd = epoll_wait(vibrator->epollfd, &events, 1, playLengthMs);
|
||||
if (nfd == -1 && (errno != EINTR)) {
|
||||
ALOGE("Failed to wait sleep playLengthMs, error=%d", errno);
|
||||
break;
|
||||
}
|
||||
|
||||
if (nfd > 0) {
|
||||
/* It's supposed that STOP_COMPOSE command is received so quit the composition */
|
||||
ret = read(vibrator->pipefd[0], &status, sizeof(int));
|
||||
if (ret < 0) {
|
||||
ALOGE("Failed to read stop status from pipe(playLengthMs), status = %d", status);
|
||||
break;
|
||||
}
|
||||
if (status == STOP_COMPOSE) {
|
||||
|
||||
/*
|
||||
* There is a corner case that the off() command could be executed in
|
||||
* main thread before the primitive play is triggered in the child thread,
|
||||
* such as, when playing a very short primitive effect while the system is
|
||||
* pretty busy (one example is enabling all kernel console log after executed
|
||||
* "echo Y > /sys/module/printk/parameters/ignore_loglevel"), the child thread
|
||||
* may not be able to schedule out for running before the main thread times out
|
||||
* on the primitive duration and sent the off() command, there won't be any
|
||||
* off() command coming again to stop the primitive effect after it's triggered.
|
||||
*
|
||||
* However, the primitive could be played out and stopped automatically but the
|
||||
* haptics driver does expect an explicit off() command to restore HW/SW logic
|
||||
* after that, so call it here. It would result a redundant off() command in
|
||||
* normal case but it won't do any harm because it would be ignored and not sent
|
||||
* to haptics driver because of an invalid mCurrAppId. It would also result in the
|
||||
* primitive effect to stop immediately right after it's triggered in such
|
||||
* corner case. But considering the main thread has stopped it before off() is
|
||||
* called here, take this as a limitation and it is expected not playing the
|
||||
* vibration out.
|
||||
*/
|
||||
|
||||
vibrator->ff.off();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ALOGD("Notifying composite complete, playlength= %ld", playLengthMs);
|
||||
if (callback)
|
||||
callback->onComplete();
|
||||
|
||||
vibrator->inComposition = false;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::compose(const std::vector<CompositeEffect>& composite,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) {
|
||||
int status, nfd = 0, durationMs = 0, timeoutMs = 0;
|
||||
struct epoll_event events;
|
||||
|
||||
if (composite.size() > ComposeSizeMax) {
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
std::vector<CompositePrimitive> supported;
|
||||
getSupportedPrimitives(&supported);
|
||||
|
||||
for (auto& e : composite) {
|
||||
if (e.delayMs > ComposeDelayMaxMs) {
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
if (e.scale < 0.0f || e.scale > 1.0f) {
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
getPrimitiveDuration(e.primitive, &durationMs);
|
||||
timeoutMs += durationMs + e.delayMs;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for 2 times of the play length timeout to make sure last play has been
|
||||
* terminated successfully.
|
||||
*/
|
||||
timeoutMs = (timeoutMs + 10) * 2;
|
||||
/* Stop previous composition if it has not yet been completed */
|
||||
if (inComposition) {
|
||||
ALOGD("Last composePlayThread has not done yet, stop it manually");
|
||||
off();
|
||||
|
||||
while (inComposition && timeoutMs--)
|
||||
usleep(1000);
|
||||
|
||||
if (timeoutMs == 0) {
|
||||
ALOGE("wait for last composePlayThread done timeout");
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the pipe again to remove any stale data before triggering a new play */
|
||||
nfd = epoll_wait(epollfd, &events, 1, 0);
|
||||
if (nfd == -1 && (errno != EINTR)) {
|
||||
ALOGE("Failed to wait sleep playLengthMs, error=%d", errno);
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
|
||||
}
|
||||
if (nfd > 0) {
|
||||
ALOGD("A stale event is cached in the pipe, remove it");
|
||||
read(pipefd[0], &status, sizeof(int));
|
||||
}
|
||||
|
||||
inComposition = true;
|
||||
composeThread = std::thread(composePlayThread, this, composite, callback);
|
||||
composeThread.detach();
|
||||
|
||||
ALOGD("trigger composition successfully");
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::alwaysOnEnable(int32_t id __unused, Effect effect __unused,
|
||||
EffectStrength strength __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::alwaysOnDisable(int32_t id __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getResonantFrequency(float *resonantFreqHz __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getQFactor(float *qFactor __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getFrequencyResolution(float *freqResolutionHz __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getFrequencyMinimum(float *freqMinimumHz __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getPwlePrimitiveDurationMax(int32_t *durationMs __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getPwleCompositionSizeMax(int32_t *maxSize __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::getSupportedBraking(std::vector<Braking> *supported __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus VibratorOL::composePwle(const std::vector<PrimitivePwle> &composite __unused,
|
||||
const std::shared_ptr<IVibratorCallback> &callback __unused) {
|
||||
return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
} // namespace vibrator
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
||||
154
qcom/opensource/vibrator/aidl/VibratorOL/Vibrator.h
Normal file
154
qcom/opensource/vibrator/aidl/VibratorOL/Vibrator.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Changes from Qualcomm Innovation Center, Inc. are provided under the following license:
|
||||
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/vibrator/BnVibrator.h>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace vibrator {
|
||||
|
||||
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();
|
||||
int mEnabled;
|
||||
private:
|
||||
OffloadGlinkConnection GlinkCh;
|
||||
int initChannel();
|
||||
int sendData(uint8_t *data, int len);
|
||||
};
|
||||
|
||||
class InputFFDevice {
|
||||
public:
|
||||
InputFFDevice();
|
||||
int playEffect(int effectId, EffectStrength es, long *playLengthMs);
|
||||
int playPrimitive(int primitiveId, float amplitude, long *playLengthMs);
|
||||
int on(int32_t timeoutMs);
|
||||
int off();
|
||||
int setAmplitude(uint8_t amplitude);
|
||||
bool isPresent();
|
||||
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;
|
||||
std::mutex mtx;
|
||||
};
|
||||
|
||||
class LedVibratorDevice {
|
||||
public:
|
||||
LedVibratorDevice();
|
||||
int on(int32_t timeoutMs);
|
||||
int off();
|
||||
bool mDetected;
|
||||
private:
|
||||
int write_value(const char *file, const char *value);
|
||||
};
|
||||
|
||||
class VibratorOL : public BnVibrator {
|
||||
public:
|
||||
bool mSupportVISense;
|
||||
class InputFFDevice ff;
|
||||
class LedVibratorDevice ledVib;
|
||||
VibratorOL();
|
||||
~VibratorOL();
|
||||
|
||||
class PatternOffload Offload;
|
||||
|
||||
ndk::ScopedAStatus getCapabilities(int32_t* _aidl_return) override;
|
||||
ndk::ScopedAStatus off() override;
|
||||
ndk::ScopedAStatus on(int32_t timeoutMs,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) override;
|
||||
ndk::ScopedAStatus perform(Effect effect, EffectStrength strength,
|
||||
const std::shared_ptr<IVibratorCallback>& callback,
|
||||
int32_t* _aidl_return) override;
|
||||
ndk::ScopedAStatus getSupportedEffects(std::vector<Effect>* _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<CompositePrimitive>* supported) override;
|
||||
ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive,
|
||||
int32_t* durationMs) override;
|
||||
ndk::ScopedAStatus compose(const std::vector<CompositeEffect>& composite,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) override;
|
||||
ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) override;
|
||||
ndk::ScopedAStatus alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) override;
|
||||
ndk::ScopedAStatus alwaysOnDisable(int32_t id) override;
|
||||
ndk::ScopedAStatus getResonantFrequency(float *resonantFreqHz) override;
|
||||
ndk::ScopedAStatus getQFactor(float *qFactor) override;
|
||||
ndk::ScopedAStatus getFrequencyResolution(float *freqResolutionHz) override;
|
||||
ndk::ScopedAStatus getFrequencyMinimum(float *freqMinimumHz) override;
|
||||
ndk::ScopedAStatus getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) override;
|
||||
ndk::ScopedAStatus getPwlePrimitiveDurationMax(int32_t *durationMs) override;
|
||||
ndk::ScopedAStatus getPwleCompositionSizeMax(int32_t *maxSize) override;
|
||||
ndk::ScopedAStatus getSupportedBraking(std::vector<Braking>* supported) override;
|
||||
ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
|
||||
const std::shared_ptr<IVibratorCallback> &callback) override;
|
||||
private:
|
||||
static void composePlayThread(VibratorOL *vibrator,
|
||||
const std::vector<CompositeEffect>& composite,
|
||||
const std::shared_ptr<IVibratorCallback>& callback);
|
||||
std::thread composeThread;
|
||||
int epollfd;
|
||||
int pipefd[2];
|
||||
std::atomic<bool> inComposition;
|
||||
};
|
||||
|
||||
} // namespace vibrator
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
||||
323
qcom/opensource/vibrator/aidl/VibratorOL/VibratorOffload.cpp
Normal file
323
qcom/opensource/vibrator/aidl/VibratorOL/VibratorOffload.cpp
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Changes from Qualcomm Innovation Center, Inc. are provided under the following license:
|
||||
* Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#define LOG_TAG "vendor.qti.vibrator.offload"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#include <linux/input.h>
|
||||
#include <log/log.h>
|
||||
#include <fcntl.h>
|
||||
#include <cutils/log.h>
|
||||
#include <cutils/uevent.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#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
|
||||
/*
|
||||
* TODO Need to work on solution to get this from kernel header
|
||||
* without effecting other kernel versions where this change
|
||||
* goes in.
|
||||
*/
|
||||
#define SLATE_AFTER_POWER_UP 4
|
||||
|
||||
PatternOffload::PatternOffload()
|
||||
{
|
||||
char prop_str[PROPERTY_VALUE_MAX];
|
||||
mEnabled = 0;
|
||||
|
||||
if (property_get("ro.vendor.qc_aon_presence", prop_str, NULL))
|
||||
mEnabled = atoi(prop_str);
|
||||
|
||||
if (mEnabled != 1)
|
||||
return;
|
||||
|
||||
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
|
||||
18
qcom/opensource/vibrator/aidl/VibratorSelector/Android.bp
Normal file
18
qcom/opensource/vibrator/aidl/VibratorSelector/Android.bp
Normal file
@@ -0,0 +1,18 @@
|
||||
cc_library_shared {
|
||||
name: "vendor.qti.hardware.vibratorSel.impl",
|
||||
vendor: true,
|
||||
srcs: [
|
||||
"VibratorSelector.cpp",
|
||||
],
|
||||
include_dirs: [
|
||||
"external/expat/lib",
|
||||
],
|
||||
shared_libs: [
|
||||
"libcutils",
|
||||
"libutils",
|
||||
"liblog",
|
||||
"libbinder_ndk",
|
||||
"libexpat",
|
||||
"android.hardware.vibrator-V2-ndk",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted (subject to the limitations in the
|
||||
* disclaimer below) 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 Qualcomm Innovation Center, Inc. nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
|
||||
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
|
||||
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <log/log.h>
|
||||
#include <string>
|
||||
#include "VibratorSelector.h"
|
||||
|
||||
#define HAPTICS_XML_FILE "/vendor/etc/HapticsPolicy.xml"
|
||||
|
||||
std::shared_ptr<VibratorSelector> VibratorSelector::me_ = nullptr;
|
||||
uint32_t VibratorSelector::maxTimeoutOnConfig;
|
||||
std::vector <int> VibratorSelector::effectIDPerformConfig;
|
||||
vibrator_type VibratorSelector::vibForComposition;
|
||||
vibrator_type VibratorSelector::vibForPwle;
|
||||
|
||||
VibratorSelector::VibratorSelector()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
VibratorSelector::~VibratorSelector()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::shared_ptr<VibratorSelector> VibratorSelector::GetInstance()
|
||||
{
|
||||
if (!me_)
|
||||
me_ = std::shared_ptr<VibratorSelector>(new VibratorSelector);
|
||||
return me_;
|
||||
}
|
||||
|
||||
int VibratorSelector::init()
|
||||
{
|
||||
int ret;
|
||||
int bytes_read;
|
||||
|
||||
ret = VibratorSelector::XmlParser(HAPTICS_XML_FILE);
|
||||
if (ret) {
|
||||
ALOGE("Error in haptics xml parsing ret %d", ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VibratorSelector::resetDataBuf(haptics_policy_xml_data* data)
|
||||
{
|
||||
data->offs = 0;
|
||||
data->data_buf[data->offs] = '\0';
|
||||
}
|
||||
|
||||
void VibratorSelector::startTag(void *userdata, const XML_Char *tag_name,
|
||||
const XML_Char **attr)
|
||||
{
|
||||
haptics_policy_xml_data *data = (haptics_policy_xml_data *)userdata;
|
||||
resetDataBuf(data);
|
||||
if (!strcmp(tag_name, "hapticsPolicyConfiguration")) {
|
||||
data->hapticstag = TAG_HAPTICS_POLICY_XML_ROOT;
|
||||
} else if (!strcmp(tag_name, "hapticsONAPI")) {
|
||||
data->hapticstag = TAG_HAPTICS_ON_API;
|
||||
} else if (!strcmp(tag_name, "hapticsPerformAPI")) {
|
||||
data->hapticstag = TAG_HAPTICS_PERFORM_API;
|
||||
} else if (!strcmp(tag_name, "hapticsComposeAPI")) {
|
||||
data->hapticstag = TAG_HAPTICS_COMPOSE_API;
|
||||
} else if (!strcmp(tag_name, "hapticsComposePwleAPI")) {
|
||||
data->hapticstag = TAG_HAPTICS_COMPOSE_PWLE_API;
|
||||
} else {
|
||||
ALOGE("No matching Tag found");
|
||||
}
|
||||
}
|
||||
|
||||
void VibratorSelector::endTag(void *userdata, const XML_Char *tag_name)
|
||||
{
|
||||
haptics_policy_xml_data *data = (haptics_policy_xml_data *)userdata;
|
||||
int size = -1;
|
||||
|
||||
process_xml_info(data, tag_name);
|
||||
resetDataBuf(data);
|
||||
return;
|
||||
}
|
||||
|
||||
void VibratorSelector::handleData(void *userdata, const char *s, int len)
|
||||
{
|
||||
haptics_policy_xml_data *data = (haptics_policy_xml_data *)userdata;
|
||||
if (len + data->offs >= sizeof(data->data_buf) ) {
|
||||
data->offs += len;
|
||||
/* string length overflow, return */
|
||||
return;
|
||||
} else {
|
||||
memcpy(data->data_buf + data->offs, s, len);
|
||||
data->offs += len;
|
||||
}
|
||||
}
|
||||
|
||||
int VibratorSelector::XmlParser(std::string xmlFile) {
|
||||
XML_Parser parser;
|
||||
FILE *file = NULL;
|
||||
int ret = 0;
|
||||
int bytes_read;
|
||||
void *buf = NULL;
|
||||
haptics_policy_xml_data data;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
ALOGI("XML parsing started %s", xmlFile.c_str());
|
||||
file = fopen(xmlFile.c_str(), "r");
|
||||
if (!file) {
|
||||
ALOGE("Failed to open xml");
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
parser = XML_ParserCreate(NULL);
|
||||
if (!parser) {
|
||||
ALOGE("Failed to create XML");
|
||||
goto closeFile;
|
||||
}
|
||||
XML_SetUserData(parser,&data);
|
||||
XML_SetElementHandler(parser, startTag, endTag);
|
||||
XML_SetCharacterDataHandler(parser, handleData);
|
||||
|
||||
while (1) {
|
||||
buf = XML_GetBuffer(parser, 1024);
|
||||
if (buf == NULL) {
|
||||
ALOGE("XML_Getbuffer failed");
|
||||
ret = -EINVAL;
|
||||
goto freeParser;
|
||||
}
|
||||
|
||||
bytes_read = fread(buf, 1, 1024, file);
|
||||
if (bytes_read < 0) {
|
||||
ALOGE("fread failed");
|
||||
ret = -EINVAL;
|
||||
goto freeParser;
|
||||
}
|
||||
|
||||
if (XML_ParseBuffer(parser, bytes_read, bytes_read == 0) == XML_STATUS_ERROR) {
|
||||
ALOGE("XML ParseBuffer failed ");
|
||||
ret = -EINVAL;
|
||||
goto freeParser;
|
||||
}
|
||||
if (bytes_read == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
freeParser:
|
||||
XML_ParserFree(parser);
|
||||
closeFile:
|
||||
fclose(file);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VibratorSelector::process_xml_info(haptics_policy_xml_data *data,
|
||||
const XML_Char *tag_name)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
if (data->hapticstag == TAG_HAPTICS_ON_API) {
|
||||
if (!strcmp(tag_name, "maxMs")) {
|
||||
maxTimeoutOnConfig = atoi(data->data_buf);
|
||||
ALOGI("maxMs: %d", maxTimeoutOnConfig);
|
||||
}
|
||||
}
|
||||
|
||||
if (data->hapticstag == TAG_HAPTICS_PERFORM_API) {
|
||||
if (!strcmp(tag_name, "effect_id")) {
|
||||
int j = 0;
|
||||
int data_len = strlen(data->data_buf);
|
||||
while (j < data_len) {
|
||||
effectIDPerformConfig.push_back(atoi(&data->data_buf[j]));
|
||||
for (; j < data_len; j++) {
|
||||
if (data->data_buf[j] == ',') {
|
||||
j = j + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < effectIDPerformConfig.size(); i++) {
|
||||
ALOGI("Effect ID: %d", effectIDPerformConfig[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data->hapticstag == TAG_HAPTICS_COMPOSE_API) {
|
||||
if (!strcmp(tag_name, "SupportCompose")) {
|
||||
ALOGE("support value for Composition: %s", data->data_buf);
|
||||
if (strstr(data->data_buf, "False")) {
|
||||
vibForComposition = VIB_TYPE_CL;
|
||||
} else {
|
||||
vibForComposition = VIB_TYPE_OL;
|
||||
}
|
||||
ALOGI("support value for Composition: %d", vibForComposition);
|
||||
}
|
||||
}
|
||||
|
||||
if (data->hapticstag == TAG_HAPTICS_COMPOSE_PWLE_API) {
|
||||
if (!strcmp(tag_name, "SupportComposePWLE")) {
|
||||
if (strstr(data->data_buf, "False")) {
|
||||
vibForPwle = VIB_TYPE_CL;
|
||||
} else {
|
||||
vibForPwle = VIB_TYPE_OL;
|
||||
}
|
||||
ALOGI("support value for Compose PWL: %d", vibForPwle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vibrator_type VibratorSelector::getVibForOnApi(int32_t timeout)
|
||||
{
|
||||
vibrator_type vibType = VIB_TYPE_OL;
|
||||
|
||||
vibType = timeout <= maxTimeoutOnConfig ? VIB_TYPE_OL : VIB_TYPE_CL;
|
||||
|
||||
return vibType;
|
||||
}
|
||||
|
||||
vibrator_type VibratorSelector::getVibForPerformApi(int effect_id)
|
||||
{
|
||||
vibrator_type vibType = VIB_TYPE_OL;
|
||||
|
||||
if (std::find(effectIDPerformConfig.begin(), effectIDPerformConfig.end(),
|
||||
effect_id) == effectIDPerformConfig.end()) {
|
||||
vibType = VIB_TYPE_CL;
|
||||
}
|
||||
|
||||
return vibType;
|
||||
}
|
||||
|
||||
vibrator_type VibratorSelector::getVibForComposeApi()
|
||||
{
|
||||
return vibForComposition;
|
||||
}
|
||||
|
||||
vibrator_type VibratorSelector::getVibForPwleApi()
|
||||
{
|
||||
return vibForPwle;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted (subject to the limitations in the
|
||||
* disclaimer below) 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 Qualcomm Innovation Center, Inc. nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
|
||||
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
|
||||
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
|
||||
*/
|
||||
|
||||
#ifndef VIBRATOR_SELECTOR_H
|
||||
#define VIBRATOR_SELECTOR_H
|
||||
|
||||
#include <vector>
|
||||
#include <expat.h>
|
||||
|
||||
typedef enum {
|
||||
TAG_HAPTICS_POLICY_XML_ROOT,
|
||||
TAG_HAPTICS_ON_API,
|
||||
TAG_HAPTICS_PERFORM_API,
|
||||
TAG_HAPTICS_COMPOSE_API,
|
||||
TAG_HAPTICS_COMPOSE_PWLE_API,
|
||||
} haptics_policy_xml_tag;
|
||||
|
||||
typedef enum {
|
||||
VIB_TYPE_OL,
|
||||
VIB_TYPE_CL,
|
||||
} vibrator_type;
|
||||
|
||||
typedef struct haptics_policy_xml_data {
|
||||
char data_buf[1024];
|
||||
size_t offs;
|
||||
haptics_policy_xml_tag hapticstag;
|
||||
} haptics_policy_xml_data;
|
||||
|
||||
class VibratorSelector
|
||||
{
|
||||
public:
|
||||
VibratorSelector();
|
||||
~VibratorSelector();
|
||||
static int XmlParser(std::string xmlFile);
|
||||
static void endTag(void *userdata, const XML_Char *tag_name);
|
||||
static void startTag(void *userdata, const XML_Char *tag_name, const XML_Char **attr);
|
||||
static void handleData(void *userdata, const char *s, int len);
|
||||
static void resetDataBuf(haptics_policy_xml_data *data);
|
||||
static void process_xml_info(haptics_policy_xml_data *data, const XML_Char *tag_name);
|
||||
vibrator_type getVibForOnApi(int32_t timeoutMs);
|
||||
vibrator_type getVibForComposeApi();
|
||||
vibrator_type getVibForPerformApi(int effect_id);
|
||||
vibrator_type getVibForPwleApi();
|
||||
static int init();
|
||||
static std::shared_ptr<VibratorSelector> GetInstance();
|
||||
private:
|
||||
static uint32_t maxTimeoutOnConfig;
|
||||
static std::vector <int> effectIDPerformConfig;
|
||||
static vibrator_type vibForComposition;
|
||||
static vibrator_type vibForPwle;
|
||||
static std::shared_ptr <VibratorSelector> me_;
|
||||
};
|
||||
#endif
|
||||
26
qcom/opensource/vibrator/aidl/fuzzer.cpp
Normal file
26
qcom/opensource/vibrator/aidl/fuzzer.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include <fuzzbinder/libbinder_ndk_driver.h>
|
||||
#include <fuzzer/FuzzedDataProvider.h>
|
||||
|
||||
#include "Vibrator.h"
|
||||
|
||||
using aidl::android::hardware::vibrator::Vibrator;
|
||||
|
||||
std::shared_ptr<Vibrator> service;
|
||||
|
||||
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
|
||||
service = ndk::SharedRefBase::make<Vibrator>();
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
if (service == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
android::fuzzService(service->asBinder().get(), FuzzedDataProvider(data, size));
|
||||
return 0;
|
||||
}
|
||||
90
qcom/opensource/vibrator/aidl/include/Vibrator.h
Normal file
90
qcom/opensource/vibrator/aidl/include/Vibrator.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Changes from Qualcomm Innovation Center, Inc. are provided under the following license:
|
||||
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/vibrator/BnVibrator.h>
|
||||
#include <thread>
|
||||
|
||||
namespace aidl {
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace vibrator {
|
||||
|
||||
|
||||
class Vibrator : public BnVibrator {
|
||||
public:
|
||||
Vibrator();
|
||||
~Vibrator();
|
||||
|
||||
ndk::ScopedAStatus getCapabilities(int32_t* _aidl_return) override;
|
||||
ndk::ScopedAStatus off() override;
|
||||
ndk::ScopedAStatus on(int32_t timeoutMs,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) override;
|
||||
ndk::ScopedAStatus perform(Effect effect, EffectStrength strength,
|
||||
const std::shared_ptr<IVibratorCallback>& callback,
|
||||
int32_t* _aidl_return) override;
|
||||
ndk::ScopedAStatus getSupportedEffects(std::vector<Effect>* _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<CompositePrimitive>* supported) override;
|
||||
ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive,
|
||||
int32_t* durationMs) override;
|
||||
ndk::ScopedAStatus compose(const std::vector<CompositeEffect>& composite,
|
||||
const std::shared_ptr<IVibratorCallback>& callback) override;
|
||||
ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return) override;
|
||||
ndk::ScopedAStatus alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) override;
|
||||
ndk::ScopedAStatus alwaysOnDisable(int32_t id) override;
|
||||
ndk::ScopedAStatus getResonantFrequency(float *resonantFreqHz) override;
|
||||
ndk::ScopedAStatus getQFactor(float *qFactor) override;
|
||||
ndk::ScopedAStatus getFrequencyResolution(float *freqResolutionHz) override;
|
||||
ndk::ScopedAStatus getFrequencyMinimum(float *freqMinimumHz) override;
|
||||
ndk::ScopedAStatus getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) override;
|
||||
ndk::ScopedAStatus getPwlePrimitiveDurationMax(int32_t *durationMs) override;
|
||||
ndk::ScopedAStatus getPwleCompositionSizeMax(int32_t *maxSize) override;
|
||||
ndk::ScopedAStatus getSupportedBraking(std::vector<Braking>* supported) override;
|
||||
ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
|
||||
const std::shared_ptr<IVibratorCallback> &callback) override;
|
||||
private:
|
||||
// Forward declare opaque internal implementation class
|
||||
class VibratorPrivate;
|
||||
// Pointer to the internal implementation
|
||||
VibratorPrivate *pImpl;
|
||||
};
|
||||
|
||||
} // namespace vibrator
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
} // namespace aidl
|
||||
50
qcom/opensource/vibrator/aidl/service.cpp
Normal file
50
qcom/opensource/vibrator/aidl/service.cpp
Normal file
@@ -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 <android-base/logging.h>
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.h>
|
||||
|
||||
#include "Vibrator.h"
|
||||
|
||||
using aidl::android::hardware::vibrator::Vibrator;
|
||||
|
||||
int main() {
|
||||
ABinderProcess_setThreadPoolMaxThreadCount(0);
|
||||
std::shared_ptr<Vibrator> vib = ndk::SharedRefBase::make<Vibrator>();
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
service vendor.qti.vibrator /vendor/bin/hw/vendor.qti.hardware.vibrator.service
|
||||
class hal
|
||||
user system
|
||||
group system input
|
||||
@@ -0,0 +1,34 @@
|
||||
<!-- Copyright (c) 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.
|
||||
-->
|
||||
<manifest version="1.0" type="device">
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.vibrator</name>
|
||||
<version>2</version>
|
||||
<fqname>IVibrator/default</fqname>
|
||||
</hal>
|
||||
</manifest>
|
||||
30
qcom/opensource/vibrator/effect/Android.bp
Normal file
30
qcom/opensource/vibrator/effect/Android.bp
Normal file
@@ -0,0 +1,30 @@
|
||||
Common_CFlags = ["-Wall"]
|
||||
Common_CFlags += ["-Werror"]
|
||||
|
||||
cc_library_shared {
|
||||
name: "libqtivibratoreffect",
|
||||
vendor: true,
|
||||
cflags: Common_CFlags,
|
||||
srcs: [
|
||||
"effect.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libcutils",
|
||||
"libutils",
|
||||
],
|
||||
export_include_dirs: ["."]
|
||||
}
|
||||
|
||||
cc_library_shared {
|
||||
name: "libqtivibratoreffectoffload",
|
||||
vendor: true,
|
||||
cflags: Common_CFlags,
|
||||
srcs: [
|
||||
"VibratorPatterns.cpp"
|
||||
],
|
||||
shared_libs: [
|
||||
"libcutils",
|
||||
"libutils",
|
||||
],
|
||||
export_include_dirs: ["."]
|
||||
}
|
||||
524
qcom/opensource/vibrator/effect/VibratorPatterns.cpp
Normal file
524
qcom/opensource/vibrator/effect/VibratorPatterns.cpp
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Changes from Qualcomm Innovation Center, Inc. are provided under the following license:
|
||||
* Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "VibratorPatterns.h"
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
|
||||
|
||||
static const uint8_t effect_0[] = {
|
||||
/* (Amp MSB 1 bit) (Amp LSB 8bit) (Period) FLRA */
|
||||
0x00, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
};
|
||||
|
||||
static const uint8_t effect_1[] = {
|
||||
0x00, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
};
|
||||
|
||||
static const uint8_t effect_2[] = {
|
||||
0x00, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
};
|
||||
|
||||
static const uint8_t effect_3[] = {
|
||||
0x00, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
};
|
||||
|
||||
static const uint8_t effect_4[] = {
|
||||
0x00, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
};
|
||||
|
||||
static const uint8_t effect_5[] = {
|
||||
0x00, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x00, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x7F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x5F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x3F, S_PERIOD_T_LRA, 0,
|
||||
0x01, 0x1F, S_PERIOD_T_LRA, 0,
|
||||
};
|
||||
|
||||
static const uint8_t effect_6[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04,
|
||||
0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
|
||||
0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x06, 0x05, 0x03, 0x01, 0x00, 0xff, 0xfd, 0xfa, 0xf8, 0xf6,
|
||||
0xf3, 0xf1, 0xee, 0xec, 0xe9, 0xe6, 0xe3, 0xe0, 0xdc, 0xd9,
|
||||
0xd5, 0xd1, 0xcd, 0xc8, 0xc4, 0xbf, 0xbb, 0xb9, 0xb7, 0xb6,
|
||||
0xb7, 0xb8, 0xba, 0xbd, 0xc1, 0xc5, 0xcb, 0xd1, 0xd7, 0xde,
|
||||
0xe5, 0xed, 0xf5, 0xfd, 0x04, 0x0d, 0x15, 0x1e, 0x26, 0x2e,
|
||||
0x36, 0x3d, 0x44, 0x4b, 0x51, 0x56, 0x5b, 0x5f, 0x62, 0x64,
|
||||
0x65, 0x65, 0x64, 0x63, 0x5f, 0x5b, 0x56, 0x50, 0x4a, 0x45,
|
||||
0x3f, 0x3a, 0x34, 0x2f, 0x29, 0x24, 0x1f, 0x1a, 0x14, 0x0f,
|
||||
0x0a, 0x05, 0x00, 0xfc, 0xf8, 0xf3, 0xef, 0xea, 0xe6, 0xe2,
|
||||
0xdf, 0xdc, 0xd9, 0xd6, 0xd4, 0xd3, 0xd2, 0xd1, 0xd1, 0xd1,
|
||||
0xd2, 0xd4, 0xd7, 0xda, 0xde, 0xe2, 0xe8, 0xee, 0xf6, 0xfd,
|
||||
0x02, 0x08, 0x0d, 0x12, 0x16, 0x1a, 0x1d, 0x20, 0x23, 0x26,
|
||||
0x28, 0x2a, 0x2b, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x30, 0x31,
|
||||
0x31, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x26, 0x25, 0x24, 0x23, 0x22,
|
||||
0x21, 0x20, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x16,
|
||||
0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x06, 0x05, 0x04,
|
||||
0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc,
|
||||
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
|
||||
0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04,
|
||||
0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
|
||||
0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x06, 0x05, 0x03, 0x01, 0x00, 0xff, 0xfd, 0xfa, 0xf8, 0xf6,
|
||||
0xf3, 0xf1, 0xee, 0xec, 0xe9, 0xe6, 0xe3, 0xe0, 0xdc, 0xd9,
|
||||
0xd5, 0xd1, 0xcd, 0xc8, 0xc4, 0xbf, 0xbb, 0xb9, 0xb7, 0xb6,
|
||||
0xb7, 0xb8, 0xba, 0xbd, 0xc1, 0xc5, 0xcb, 0xd1, 0xd7, 0xde,
|
||||
0xe5, 0xed, 0xf5, 0xfd, 0x04, 0x0d, 0x15, 0x1e, 0x26, 0x2e,
|
||||
0x36, 0x3d, 0x44, 0x4b, 0x51, 0x56, 0x5b, 0x5f, 0x62, 0x64,
|
||||
0x65, 0x65, 0x64, 0x63, 0x5f, 0x5b, 0x56, 0x50, 0x4a, 0x45,
|
||||
0x3f, 0x3a, 0x34, 0x2f, 0x29, 0x24, 0x1f, 0x1a, 0x14, 0x0f,
|
||||
0x0a, 0x05, 0x00, 0xfc, 0xf8, 0xf3, 0xef, 0xea, 0xe6, 0xe2,
|
||||
0xdf, 0xdc, 0xd9, 0xd6, 0xd4, 0xd3, 0xd2, 0xd1, 0xd1, 0xd1,
|
||||
0xd2, 0xd4, 0xd7, 0xda, 0xde, 0xe2, 0xe8, 0xee, 0xf6, 0xfd,
|
||||
0x02, 0x08, 0x0d, 0x12, 0x16, 0x1a, 0x1d, 0x20, 0x23, 0x26,
|
||||
0x28, 0x2a, 0x2b, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x30, 0x31,
|
||||
0x31, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x26, 0x25, 0x24, 0x23, 0x22,
|
||||
0x21, 0x20, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x16,
|
||||
0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x06, 0x05, 0x04,
|
||||
0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc,
|
||||
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
|
||||
0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
|
||||
static const uint8_t effect_7[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04,
|
||||
0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
|
||||
0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x06, 0x05, 0x03, 0x01, 0x00, 0xff, 0xfd, 0xfa, 0xf8, 0xf6,
|
||||
0xf3, 0xf1, 0xee, 0xec, 0xe9, 0xe6, 0xe3, 0xe0, 0xdc, 0xd9,
|
||||
0xd5, 0xd1, 0xcd, 0xc8, 0xc4, 0xbf, 0xbb, 0xb9, 0xb7, 0xb6,
|
||||
0xb7, 0xb8, 0xba, 0xbd, 0xc1, 0xc5, 0xcb, 0xd1, 0xd7, 0xde,
|
||||
0xe5, 0xed, 0xf5, 0xfd, 0x04, 0x0d, 0x15, 0x1e, 0x26, 0x2e,
|
||||
0x36, 0x3d, 0x44, 0x4b, 0x51, 0x56, 0x5b, 0x5f, 0x62, 0x64,
|
||||
0x65, 0x65, 0x64, 0x63, 0x5f, 0x5b, 0x56, 0x50, 0x4a, 0x45,
|
||||
0x3f, 0x3a, 0x34, 0x2f, 0x29, 0x24, 0x1f, 0x1a, 0x14, 0x0f,
|
||||
0x0a, 0x05, 0x00, 0xfc, 0xf8, 0xf3, 0xef, 0xea, 0xe6, 0xe2,
|
||||
0xdf, 0xdc, 0xd9, 0xd6, 0xd4, 0xd3, 0xd2, 0xd1, 0xd1, 0xd1,
|
||||
0xd2, 0xd4, 0xd7, 0xda, 0xde, 0xe2, 0xe8, 0xee, 0xf6, 0xfd,
|
||||
0x02, 0x08, 0x0d, 0x12, 0x16, 0x1a, 0x1d, 0x20, 0x23, 0x26,
|
||||
0x28, 0x2a, 0x2b, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x30, 0x31,
|
||||
0x31, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x26, 0x25, 0x24, 0x23, 0x22,
|
||||
0x21, 0x20, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x16,
|
||||
0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x06, 0x05, 0x04,
|
||||
0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc,
|
||||
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
|
||||
0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04,
|
||||
0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
|
||||
0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x06, 0x05, 0x03, 0x01, 0x00, 0xff, 0xfd, 0xfa, 0xf8, 0xf6,
|
||||
0xf3, 0xf1, 0xee, 0xec, 0xe9, 0xe6, 0xe3, 0xe0, 0xdc, 0xd9,
|
||||
0xd5, 0xd1, 0xcd, 0xc8, 0xc4, 0xbf, 0xbb, 0xb9, 0xb7, 0xb6,
|
||||
0xb7, 0xb8, 0xba, 0xbd, 0xc1, 0xc5, 0xcb, 0xd1, 0xd7, 0xde,
|
||||
0xe5, 0xed, 0xf5, 0xfd, 0x04, 0x0d, 0x15, 0x1e, 0x26, 0x2e,
|
||||
0x36, 0x3d, 0x44, 0x4b, 0x51, 0x56, 0x5b, 0x5f, 0x62, 0x64,
|
||||
0x65, 0x65, 0x64, 0x63, 0x5f, 0x5b, 0x56, 0x50, 0x4a, 0x45,
|
||||
0x3f, 0x3a, 0x34, 0x2f, 0x29, 0x24, 0x1f, 0x1a, 0x14, 0x0f,
|
||||
0x0a, 0x05, 0x00, 0xfc, 0xf8, 0xf3, 0xef, 0xea, 0xe6, 0xe2,
|
||||
0xdf, 0xdc, 0xd9, 0xd6, 0xd4, 0xd3, 0xd2, 0xd1, 0xd1, 0xd1,
|
||||
0xd2, 0xd4, 0xd7, 0xda, 0xde, 0xe2, 0xe8, 0xee, 0xf6, 0xfd,
|
||||
0x02, 0x08, 0x0d, 0x12, 0x16, 0x1a, 0x1d, 0x20, 0x23, 0x26,
|
||||
0x28, 0x2a, 0x2b, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x30, 0x31,
|
||||
0x31, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x26, 0x25, 0x24, 0x23, 0x22,
|
||||
0x21, 0x20, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x16,
|
||||
0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x06, 0x05, 0x04,
|
||||
0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc,
|
||||
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
|
||||
0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
|
||||
static const uint8_t effect_8[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04,
|
||||
0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
|
||||
0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x06, 0x05, 0x03, 0x01, 0x00, 0xff, 0xfd, 0xfa, 0xf8, 0xf6,
|
||||
0xf3, 0xf1, 0xee, 0xec, 0xe9, 0xe6, 0xe3, 0xe0, 0xdc, 0xd9,
|
||||
0xd5, 0xd1, 0xcd, 0xc8, 0xc4, 0xbf, 0xbb, 0xb9, 0xb7, 0xb6,
|
||||
0xb7, 0xb8, 0xba, 0xbd, 0xc1, 0xc5, 0xcb, 0xd1, 0xd7, 0xde,
|
||||
0xe5, 0xed, 0xf5, 0xfd, 0x04, 0x0d, 0x15, 0x1e, 0x26, 0x2e,
|
||||
0x36, 0x3d, 0x44, 0x4b, 0x51, 0x56, 0x5b, 0x5f, 0x62, 0x64,
|
||||
0x65, 0x65, 0x64, 0x63, 0x5f, 0x5b, 0x56, 0x50, 0x4a, 0x45,
|
||||
0x3f, 0x3a, 0x34, 0x2f, 0x29, 0x24, 0x1f, 0x1a, 0x14, 0x0f,
|
||||
0x0a, 0x05, 0x00, 0xfc, 0xf8, 0xf3, 0xef, 0xea, 0xe6, 0xe2,
|
||||
0xdf, 0xdc, 0xd9, 0xd6, 0xd4, 0xd3, 0xd2, 0xd1, 0xd1, 0xd1,
|
||||
0xd2, 0xd4, 0xd7, 0xda, 0xde, 0xe2, 0xe8, 0xee, 0xf6, 0xfd,
|
||||
0x02, 0x08, 0x0d, 0x12, 0x16, 0x1a, 0x1d, 0x20, 0x23, 0x26,
|
||||
0x28, 0x2a, 0x2b, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x30, 0x31,
|
||||
0x31, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x26, 0x25, 0x24, 0x23, 0x22,
|
||||
0x21, 0x20, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x16,
|
||||
0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x06, 0x05, 0x04,
|
||||
0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc,
|
||||
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
|
||||
0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04,
|
||||
0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
|
||||
0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x06, 0x05, 0x03, 0x01, 0x00, 0xff, 0xfd, 0xfa, 0xf8, 0xf6,
|
||||
0xf3, 0xf1, 0xee, 0xec, 0xe9, 0xe6, 0xe3, 0xe0, 0xdc, 0xd9,
|
||||
0xd5, 0xd1, 0xcd, 0xc8, 0xc4, 0xbf, 0xbb, 0xb9, 0xb7, 0xb6,
|
||||
0xb7, 0xb8, 0xba, 0xbd, 0xc1, 0xc5, 0xcb, 0xd1, 0xd7, 0xde,
|
||||
0xe5, 0xed, 0xf5, 0xfd, 0x04, 0x0d, 0x15, 0x1e, 0x26, 0x2e,
|
||||
0x36, 0x3d, 0x44, 0x4b, 0x51, 0x56, 0x5b, 0x5f, 0x62, 0x64,
|
||||
0x65, 0x65, 0x64, 0x63, 0x5f, 0x5b, 0x56, 0x50, 0x4a, 0x45,
|
||||
0x3f, 0x3a, 0x34, 0x2f, 0x29, 0x24, 0x1f, 0x1a, 0x14, 0x0f,
|
||||
0x0a, 0x05, 0x00, 0xfc, 0xf8, 0xf3, 0xef, 0xea, 0xe6, 0xe2,
|
||||
0xdf, 0xdc, 0xd9, 0xd6, 0xd4, 0xd3, 0xd2, 0xd1, 0xd1, 0xd1,
|
||||
0xd2, 0xd4, 0xd7, 0xda, 0xde, 0xe2, 0xe8, 0xee, 0xf6, 0xfd,
|
||||
0x02, 0x08, 0x0d, 0x12, 0x16, 0x1a, 0x1d, 0x20, 0x23, 0x26,
|
||||
0x28, 0x2a, 0x2b, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x30, 0x31,
|
||||
0x31, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x26, 0x25, 0x24, 0x23, 0x22,
|
||||
0x21, 0x20, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x16,
|
||||
0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x06, 0x05, 0x04,
|
||||
0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc,
|
||||
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
|
||||
0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
|
||||
static const uint8_t effect_9[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04,
|
||||
0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
|
||||
0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x06, 0x05, 0x03, 0x01, 0x00, 0xff, 0xfd, 0xfa, 0xf8, 0xf6,
|
||||
0xf3, 0xf1, 0xee, 0xec, 0xe9, 0xe6, 0xe3, 0xe0, 0xdc, 0xd9,
|
||||
0xd5, 0xd1, 0xcd, 0xc8, 0xc4, 0xbf, 0xbb, 0xb9, 0xb7, 0xb6,
|
||||
0xb7, 0xb8, 0xba, 0xbd, 0xc1, 0xc5, 0xcb, 0xd1, 0xd7, 0xde,
|
||||
0xe5, 0xed, 0xf5, 0xfd, 0x04, 0x0d, 0x15, 0x1e, 0x26, 0x2e,
|
||||
0x36, 0x3d, 0x44, 0x4b, 0x51, 0x56, 0x5b, 0x5f, 0x62, 0x64,
|
||||
0x65, 0x65, 0x64, 0x63, 0x5f, 0x5b, 0x56, 0x50, 0x4a, 0x45,
|
||||
0x3f, 0x3a, 0x34, 0x2f, 0x29, 0x24, 0x1f, 0x1a, 0x14, 0x0f,
|
||||
0x0a, 0x05, 0x00, 0xfc, 0xf8, 0xf3, 0xef, 0xea, 0xe6, 0xe2,
|
||||
0xdf, 0xdc, 0xd9, 0xd6, 0xd4, 0xd3, 0xd2, 0xd1, 0xd1, 0xd1,
|
||||
0xd2, 0xd4, 0xd7, 0xda, 0xde, 0xe2, 0xe8, 0xee, 0xf6, 0xfd,
|
||||
0x02, 0x08, 0x0d, 0x12, 0x16, 0x1a, 0x1d, 0x20, 0x23, 0x26,
|
||||
0x28, 0x2a, 0x2b, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x30, 0x31,
|
||||
0x31, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x26, 0x25, 0x24, 0x23, 0x22,
|
||||
0x21, 0x20, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x16,
|
||||
0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x06, 0x05, 0x04,
|
||||
0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc,
|
||||
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
|
||||
0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04,
|
||||
0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
|
||||
0x0f, 0x0f, 0x0e, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x06, 0x05, 0x03, 0x01, 0x00, 0xff, 0xfd, 0xfa, 0xf8, 0xf6,
|
||||
0xf3, 0xf1, 0xee, 0xec, 0xe9, 0xe6, 0xe3, 0xe0, 0xdc, 0xd9,
|
||||
0xd5, 0xd1, 0xcd, 0xc8, 0xc4, 0xbf, 0xbb, 0xb9, 0xb7, 0xb6,
|
||||
0xb7, 0xb8, 0xba, 0xbd, 0xc1, 0xc5, 0xcb, 0xd1, 0xd7, 0xde,
|
||||
0xe5, 0xed, 0xf5, 0xfd, 0x04, 0x0d, 0x15, 0x1e, 0x26, 0x2e,
|
||||
0x36, 0x3d, 0x44, 0x4b, 0x51, 0x56, 0x5b, 0x5f, 0x62, 0x64,
|
||||
0x65, 0x65, 0x64, 0x63, 0x5f, 0x5b, 0x56, 0x50, 0x4a, 0x45,
|
||||
0x3f, 0x3a, 0x34, 0x2f, 0x29, 0x24, 0x1f, 0x1a, 0x14, 0x0f,
|
||||
0x0a, 0x05, 0x00, 0xfc, 0xf8, 0xf3, 0xef, 0xea, 0xe6, 0xe2,
|
||||
0xdf, 0xdc, 0xd9, 0xd6, 0xd4, 0xd3, 0xd2, 0xd1, 0xd1, 0xd1,
|
||||
0xd2, 0xd4, 0xd7, 0xda, 0xde, 0xe2, 0xe8, 0xee, 0xf6, 0xfd,
|
||||
0x02, 0x08, 0x0d, 0x12, 0x16, 0x1a, 0x1d, 0x20, 0x23, 0x26,
|
||||
0x28, 0x2a, 0x2b, 0x2d, 0x2e, 0x2f, 0x30, 0x30, 0x30, 0x31,
|
||||
0x31, 0x31, 0x31, 0x30, 0x30, 0x2f, 0x2f, 0x2e, 0x2d, 0x2c,
|
||||
0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x26, 0x25, 0x24, 0x23, 0x22,
|
||||
0x21, 0x20, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x16,
|
||||
0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c,
|
||||
0x0b, 0x0a, 0x0a, 0x09, 0x08, 0x07, 0x07, 0x06, 0x05, 0x04,
|
||||
0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc,
|
||||
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
|
||||
0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
|
||||
#define OFFSET_EFFECT_0 0
|
||||
|
||||
#define OFFSET_EFFECT_1 ARRAY_SIZE(effect_0)
|
||||
|
||||
#define OFFSET_EFFECT_2 OFFSET_EFFECT_1 + ARRAY_SIZE(effect_1)
|
||||
|
||||
#define OFFSET_EFFECT_3 OFFSET_EFFECT_2 + ARRAY_SIZE(effect_2)
|
||||
|
||||
#define OFFSET_EFFECT_4 OFFSET_EFFECT_3 + ARRAY_SIZE(effect_3)
|
||||
|
||||
#define OFFSET_EFFECT_5 OFFSET_EFFECT_4 + ARRAY_SIZE(effect_4)
|
||||
|
||||
#define OFFSET_EFFECT_6 OFFSET_EFFECT_5 + ARRAY_SIZE(effect_5)
|
||||
|
||||
#define OFFSET_EFFECT_7 OFFSET_EFFECT_6 + ARRAY_SIZE(effect_6)
|
||||
|
||||
#define OFFSET_EFFECT_8 OFFSET_EFFECT_7 + ARRAY_SIZE(effect_7)
|
||||
|
||||
#define OFFSET_EFFECT_9 OFFSET_EFFECT_8 + ARRAY_SIZE(effect_8)
|
||||
|
||||
#define LEN_TOTAL_PATTERNS OFFSET_EFFECT_9 + ARRAY_SIZE(effect_9)
|
||||
|
||||
#define NUM_TOTAL_PATTERNS 10
|
||||
|
||||
const static struct effect config_data[] = {
|
||||
{
|
||||
.effect_id = 0, /* CLICK */
|
||||
.effect_type = EFFECT_TYPE_PATTERN,
|
||||
.effect_len = ARRAY_SIZE(effect_0),
|
||||
.offset = OFFSET_EFFECT_0,
|
||||
.play_rate = S_PERIOD_T_LRA,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 1, /* DOUBLE_CLICK */
|
||||
.effect_type = EFFECT_TYPE_PATTERN,
|
||||
.effect_len = ARRAY_SIZE(effect_1),
|
||||
.offset = OFFSET_EFFECT_1,
|
||||
.play_rate = S_PERIOD_T_LRA,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 2, /* TICK */
|
||||
.effect_type = EFFECT_TYPE_PATTERN,
|
||||
.effect_len = ARRAY_SIZE(effect_2),
|
||||
.offset = OFFSET_EFFECT_2,
|
||||
.play_rate = S_PERIOD_T_LRA,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 3, /* THUD */
|
||||
.effect_type = EFFECT_TYPE_PATTERN,
|
||||
.effect_len = ARRAY_SIZE(effect_3),
|
||||
.offset = OFFSET_EFFECT_3,
|
||||
.play_rate = S_PERIOD_T_LRA,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 4, /* POP */
|
||||
.effect_type = EFFECT_TYPE_PATTERN,
|
||||
.effect_len = ARRAY_SIZE(effect_4),
|
||||
.offset = OFFSET_EFFECT_4,
|
||||
.play_rate = S_PERIOD_T_LRA,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 5, /* HEAVY_CLICK */
|
||||
.effect_type = EFFECT_TYPE_PATTERN,
|
||||
.effect_len = ARRAY_SIZE(effect_5),
|
||||
.offset = OFFSET_EFFECT_5,
|
||||
.play_rate = S_PERIOD_T_LRA,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 17, /* RINGTONE_12 */
|
||||
.effect_type = EFFECT_TYPE_FIFO_ENVELOPE,
|
||||
.effect_len = ARRAY_SIZE(effect_6),
|
||||
.offset = OFFSET_EFFECT_6,
|
||||
.play_rate = S_PERIOD_T_LRA,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 18, /* RINGTONE_13 */
|
||||
.effect_type = EFFECT_TYPE_FIFO_ENVELOPE,
|
||||
.effect_len = ARRAY_SIZE(effect_7),
|
||||
.offset = OFFSET_EFFECT_7,
|
||||
.play_rate = S_PERIOD_T_LRA_X_8,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 19, /* RINGTONE_14 */
|
||||
.effect_type = EFFECT_TYPE_FIFO_ENVELOPE,
|
||||
.effect_len = ARRAY_SIZE(effect_8),
|
||||
.offset = OFFSET_EFFECT_8,
|
||||
.play_rate = S_PERIOD_T_LRA_X_8,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 20, /* RINGTONE_15 */
|
||||
.effect_type = EFFECT_TYPE_FIFO_ENVELOPE,
|
||||
.effect_len = ARRAY_SIZE(effect_9),
|
||||
.offset = OFFSET_EFFECT_9,
|
||||
.play_rate = S_PERIOD_T_LRA_X_8,
|
||||
},
|
||||
};
|
||||
|
||||
int get_pattern_config(uint8_t **ptr, uint32_t *size)
|
||||
{
|
||||
*ptr = (uint8_t *)config_data;
|
||||
*size = sizeof(config_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_pattern_data(uint8_t **ptr, uint32_t *size)
|
||||
{
|
||||
uint8_t *pattern_data;
|
||||
uint8_t *pbuf;
|
||||
int i;
|
||||
const uint8_t *pattern[] = {effect_0, effect_1, effect_2, effect_3, effect_4,
|
||||
effect_5, effect_6, effect_7, effect_8, effect_9};
|
||||
|
||||
uint32_t pattern_size[] = {sizeof(effect_0), sizeof(effect_1),
|
||||
sizeof(effect_2), sizeof(effect_3),
|
||||
sizeof(effect_4), sizeof(effect_5),
|
||||
sizeof(effect_6), sizeof(effect_7),
|
||||
sizeof(effect_8), sizeof(effect_9)};
|
||||
|
||||
pattern_data = new uint8_t[LEN_TOTAL_PATTERNS];
|
||||
if (!pattern_data) {
|
||||
*ptr = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pbuf = pattern_data;
|
||||
for (i = 0; i < NUM_TOTAL_PATTERNS; i++) {
|
||||
memcpy(pbuf, pattern[i], pattern_size[i]);
|
||||
pbuf = pbuf + pattern_size[i]/sizeof(char);
|
||||
}
|
||||
|
||||
*ptr = pattern_data;
|
||||
*size = LEN_TOTAL_PATTERNS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_pattern_mem(uint8_t *ptr)
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
78
qcom/opensource/vibrator/effect/VibratorPatterns.h
Normal file
78
qcom/opensource/vibrator/effect/VibratorPatterns.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Changes from Qualcomm Innovation Center, Inc. are provided under the following license:
|
||||
* Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef VIBRATOR_PATTERNS_H
|
||||
#define VIBRATOR_PATTERNS_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
struct effect {
|
||||
uint16_t effect_id;
|
||||
uint16_t effect_type;
|
||||
uint16_t effect_len;
|
||||
uint16_t offset;
|
||||
uint32_t play_rate;
|
||||
};
|
||||
|
||||
enum period {
|
||||
S_PERIOD_T_LRA = 0,
|
||||
S_PERIOD_T_LRA_DIV_2,
|
||||
S_PERIOD_T_LRA_DIV_4,
|
||||
S_PERIOD_T_LRA_DIV_8,
|
||||
S_PERIOD_T_LRA_X_2,
|
||||
S_PERIOD_T_LRA_X_4,
|
||||
S_PERIOD_T_LRA_X_8,
|
||||
/* F_8KHZ to F_48KHZ can be specified only for FIFO patterns */
|
||||
S_PERIOD_F_8KHZ = 8,
|
||||
S_PERIOD_F_16KHZ,
|
||||
S_PERIOD_F_24KHZ,
|
||||
S_PERIOD_F_32KHZ,
|
||||
S_PERIOD_F_44P1KHZ,
|
||||
S_PERIOD_F_48KHZ,
|
||||
};
|
||||
|
||||
enum effect_type {
|
||||
EFFECT_TYPE_PATTERN = 1,
|
||||
EFFECT_TYPE_FIFO_ENVELOPE,
|
||||
EFFECT_TYPE_FIFO_STREAMING,
|
||||
};
|
||||
|
||||
enum offload_status {
|
||||
OFFLOAD_SUCCESS = 0,
|
||||
OFFLOAD_FAILURE = 1
|
||||
};
|
||||
|
||||
int get_pattern_config(uint8_t **ptr, uint32_t *size);
|
||||
int get_pattern_data(uint8_t **ptr, uint32_t *size);
|
||||
void free_pattern_mem(uint8_t *ptr);
|
||||
#endif
|
||||
133
qcom/opensource/vibrator/effect/effect.cpp
Normal file
133
qcom/opensource/vibrator/effect/effect.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Changes from Qualcomm Innovation Center are provided under the following license:
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "effect.h"
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
|
||||
|
||||
/* ~170 HZ sine waveform */
|
||||
static const int8_t effect_0[] = {
|
||||
17, 34, 50, 65, 79, 92, 103, 112, 119, 124,
|
||||
127, 127, 126, 122, 116, 108, 98, 86, 73, 58,
|
||||
42, 26, 9, -8, -25, -41, -57, -72, -85, -97,
|
||||
-108, -116, -122, -126, -127, -127, -125, -120,
|
||||
-113, -104, -93, -80, -66, -51, -35, -18, -1,
|
||||
};
|
||||
|
||||
static const int8_t effect_1[] = {
|
||||
-1, -18, -35, -51, -66, -80, -93, -104, -113,
|
||||
-120, -125, -127, -127, -126, -122, -116, -108,
|
||||
-97, -85, -72, -57, -41, -25, -8, 9, 26, 42,
|
||||
58, 73, 86, 98, 108, 116, 122, 126, 127, 127,
|
||||
124, 119, 112, 103, 92, 79, 65, 50, 34, 17,
|
||||
};
|
||||
|
||||
static const int8_t primitive_0[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static const int8_t primitive_1[] = {
|
||||
17, 34, 50, 65, 79, 92, 103, 112, 119, 124,
|
||||
127, 127, 126, 122, 116, 108, 98, 86, 73, 58,
|
||||
42, 26, 9, -8, -25, -41, -57, -72, -85, -97,
|
||||
-108, -116, -122, -126, -127, -127, -125, -120,
|
||||
-113, -104, -93, -80, -66, -51, -35, -18, -1,
|
||||
};
|
||||
|
||||
static const int8_t primitive_2[] = {
|
||||
17, 34, 50, 65, 79, 92, 103, 112, 119, 124,
|
||||
127, 127, 126, 122, 116, 108, 98, 86, 73, 58,
|
||||
42, 26, 9, -8, -25, -41, -57, -72, -85, -97,
|
||||
-108, -116, -122, -126, -127, -127, -125, -120,
|
||||
-113, -104, -93, -80, -66, -51, -35, -18, -1,
|
||||
};
|
||||
|
||||
static const struct effect_stream effects[] = {
|
||||
{
|
||||
.effect_id = 0,
|
||||
.data = effect_0,
|
||||
.length = ARRAY_SIZE(effect_0),
|
||||
.play_rate_hz = 8000,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 1,
|
||||
.data = effect_1,
|
||||
.length = ARRAY_SIZE(effect_1),
|
||||
.play_rate_hz = 8000,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct effect_stream primitives[] = {
|
||||
{
|
||||
.effect_id = 0,
|
||||
.data = primitive_0,
|
||||
.length = ARRAY_SIZE(primitive_0),
|
||||
.play_rate_hz = 8000,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 1,
|
||||
.data = primitive_1,
|
||||
.length = ARRAY_SIZE(primitive_1),
|
||||
.play_rate_hz = 8000,
|
||||
},
|
||||
|
||||
{
|
||||
.effect_id = 2,
|
||||
.data = primitive_2,
|
||||
.length = ARRAY_SIZE(primitive_2),
|
||||
.play_rate_hz = 8000,
|
||||
},
|
||||
};
|
||||
|
||||
const struct effect_stream *get_effect_stream(uint32_t effect_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ((effect_id & 0x8000) != 0) {
|
||||
effect_id = effect_id & 0x7fff;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(primitives); i++) {
|
||||
if (effect_id == primitives[i].effect_id)
|
||||
return &primitives[i];
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < ARRAY_SIZE(effects); i++) {
|
||||
if (effect_id == effects[i].effect_id)
|
||||
return &effects[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
43
qcom/opensource/vibrator/effect/effect.h
Normal file
43
qcom/opensource/vibrator/effect/effect.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef QTI_VIBRATOR_EFFECT_STREAM_H
|
||||
#define QTI_VIBRATOR_EFFECT_STREAM_H
|
||||
#include <sys/types.h>
|
||||
|
||||
struct effect_stream {
|
||||
uint32_t effect_id;
|
||||
uint32_t length;
|
||||
uint32_t play_rate_hz;
|
||||
const int8_t *data;
|
||||
};
|
||||
|
||||
const struct effect_stream *get_effect_stream(uint32_t effect_id);
|
||||
|
||||
#endif
|
||||
31
qcom/opensource/vibrator/excluded-input-devices.xml
Executable file
31
qcom/opensource/vibrator/excluded-input-devices.xml
Executable file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (c) 2018, 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. -->
|
||||
<devices>
|
||||
<device name="qti-haptics"/>
|
||||
<device name="qcom-hv-haptics"/>
|
||||
</devices>
|
||||
22
qcom/opensource/vibrator/vibrator-vendor-product.mk
Normal file
22
qcom/opensource/vibrator/vibrator-vendor-product.mk
Normal file
@@ -0,0 +1,22 @@
|
||||
TARGET_DISABLE_VIBRATOR := false
|
||||
ifeq ($(TARGET_USES_QMAA),true)
|
||||
ifneq ($(TARGET_USES_QMAA_OVERRIDE_VIBRATOR),true)
|
||||
|
||||
TARGET_DISABLE_VIBRATOR := true
|
||||
|
||||
endif #TARGET_USES_QMAA_OVERRIDE_VIBRATOR
|
||||
endif #TARGET_USES_QMAA
|
||||
|
||||
ifneq ($(TARGET_DISABLE_VIBRATOR),true)
|
||||
QTI_VIBRATOR_HAL_SERVICE := \
|
||||
vendor.qti.hardware.vibrator.service
|
||||
|
||||
PRODUCT_PACKAGES += $(QTI_VIBRATOR_HAL_SERVICE)
|
||||
|
||||
PRODUCT_COPY_FILES += \
|
||||
vendor/qcom/opensource/vibrator/excluded-input-devices.xml:vendor/etc/excluded-input-devices.xml
|
||||
|
||||
PRODUCT_COPY_FILES += \
|
||||
vendor/qcom/opensource/vibrator/aidl/HapticsPolicy.xml:vendor/etc/HapticsPolicy.xml
|
||||
|
||||
endif
|
||||
Reference in New Issue
Block a user