replace common qcom sources with samsung ones

This commit is contained in:
SaschaNes
2025-08-12 22:13:00 +02:00
parent ba24dcded9
commit 6f7753de11
5682 changed files with 2450203 additions and 103634 deletions

View 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",
],
}

View 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>

View 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

View 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",
],
}

View 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

View 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

View 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",
],
}

View 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

View 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

View 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

View 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",
],
}

View File

@@ -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;
}

View File

@@ -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

View 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;
}

View 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

View 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
}

View File

@@ -0,0 +1,4 @@
service vendor.qti.vibrator /vendor/bin/hw/vendor.qti.hardware.vibrator.service
class hal
user system
group system input

View File

@@ -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>

View 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: ["."]
}

View 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;
}

View 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

View 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;
}

View 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

View 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>

View 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