[DO NOT MERGE] cs40l26: single HAL for dual haptics

We need to sync both haptics on the single HAL
architecture to reduce the time overhead and prevent the
mismatch behavior from vibrator manager HAL implementation.

Bug: 260090235
Bug: 261687849
Bug: 261841035
Bug: 261832151
Test: atest PtsVibratorHalTestSuite \
PtsHapticsTestCases \
PtsHapticsFeatureTestCases \
VibratorHalCs40l26TestSuite \
VtsHalVibratorManagerTargetTest \
VtsHalVibratorTargetTest \
android.os.cts.VibratorTest \
android.os.cts.VibratorManagerTest \
android.os.cts.VibrationEffectTest \
android.os.cts.VibrationAttributesTest \
android.os.cts.CombinedVibrationTest \
Change-Id: I084e1da952ab2f63e5c217a7f708ac3d34635336
Signed-off-by: Chase Wu <chasewu@google.com>
This commit is contained in:
Chase Wu 2022-11-29 16:39:31 +08:00
parent aa946e4dcb
commit 07468b074a
25 changed files with 542 additions and 884 deletions

View file

@ -30,7 +30,7 @@ DEVICE_PACKAGE_OVERLAYS += device/google/felix/felix/overlay
include device/google/felix/audio/felix/audio-tables.mk
include device/google/gs201/device-shipping-common.mk
$(call soong_config_set,fp_hal_feature,pixel_product, product_a)
include device/google/felix/vibrator/cs40l26/device-stereo.mk
include device/google/felix/vibrator/cs40l26/device.mk
include device/google/gs101/bluetooth/bluetooth.mk
ifeq ($(filter factory_felix, $(TARGET_PRODUCT)),)
include device/google/felix/uwb/uwb_calibration.mk

View file

@ -71,7 +71,7 @@ void HwApiBase::debug(int fd) {
HwCalBase::HwCalBase() {
std::ifstream calfile;
std::ifstream calfile_other;
std::ifstream calfile_dual;
auto propertyPrefix = std::getenv("PROPERTY_PREFIX");
if (propertyPrefix != NULL) {
@ -93,16 +93,16 @@ HwCalBase::HwCalBase() {
}
}
utils::fileFromEnv("CALIBRATION_FILEPATH_OTHER", &calfile_other);
utils::fileFromEnv("CALIBRATION_FILEPATH_DUAL", &calfile_dual);
for (std::string line; std::getline(calfile_other, line);) {
for (std::string line; std::getline(calfile_dual, line);) {
if (line.empty() || line[0] == '#') {
continue;
}
std::istringstream is_line(line);
std::string key, value;
if (std::getline(is_line, key, ':') && std::getline(is_line, value)) {
key = utils::trim(key) + "_other";
key = utils::trim(key) + "_dual";
mCalData[key] = utils::trim(value);
}
}

View file

@ -34,7 +34,6 @@ cc_defaults {
"android.hardware.vibrator-defaults.cs40l26-private",
],
shared_libs: [
"android.hardware.vibrator.cs40l26-private-cpp",
"libcutils",
"libtinyalsa",
],
@ -53,9 +52,6 @@ cc_defaults {
"android.hardware.vibrator-impl.cs40l26-private",
"libtinyalsa",
],
shared_libs: [
"android.hardware.vibrator.cs40l26-private-cpp",
],
}
cc_library {
@ -63,8 +59,6 @@ cc_library {
defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"],
srcs: [
"Vibrator.cpp",
"VibratorSync.cpp",
"VibratorManager.cpp",
],
export_include_dirs: ["."],
vendor_available: true,
@ -82,15 +76,3 @@ cc_binary {
],
proprietary: true,
}
cc_binary {
name: "android.hardware.vibrator-service.cs40l26-stereo-private",
defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"],
init_rc: ["android.hardware.vibrator-service.cs40l26-stereo-private.rc"],
vintf_fragments: ["android.hardware.vibrator-service.cs40l26-stereo-private.xml"],
srcs: ["service-stereo.cpp"],
shared_libs: [
"android.hardware.vibrator-impl.cs40l26-private",
],
proprietary: true,
}

View file

@ -67,6 +67,10 @@ namespace vibrator {
class HwApi : public Vibrator::HwApi, private HwApiBase {
public:
static std::unique_ptr<HwApi> Create() {
auto hwapi = std::unique_ptr<HwApi>(new HwApi());
return hwapi;
}
HwApi() {
open("calibration/f0_stored", &mF0);
open("default/f0_offset", &mF0Offset);
@ -216,12 +220,16 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
}
bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) override {
uint32_t effectCountBefore, effectCountAfter, i, successFlush = 0;
static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time
if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
ALOGE("Invalid waveform index for OWT erase: %d", effectIndex);
return false;
}
// Turn off the waiting time for SVC init phase to complete since chip
// should already under STOP state
setMinOnOffInterval(0);
// Do erase flow
if (effectIndex < WAVEFORM_MAX_INDEX) {
/* Normal situation. Only erase the effect which we just played. */
if (ioctl(fd, EVIOCRMFF, effectIndex) < 0) {
@ -249,19 +257,10 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
(*effect)[i].id = -1;
}
}
// Turn on the waiting time for SVC init phase to complete
setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
return true;
}
void clearTrigBtn(int fd, struct ff_effect *effect, int8_t id) override {
if ((*effect).trigger.button != 0x00) {
(*effect).trigger.button = 0x00;
if (id < WAVEFORM_MAX_PHYSICAL_INDEX) {
/* Clear the trigger pin setting */
if ((ioctl(fd, EVIOCSFF, effect) < 0)) {
ALOGE("OFF: Failed to edit effect %d (%d): %s", id, errno, strerror(errno));
}
}
}
}
void debug(int fd) override { HwApiBase::debug(fd); }
@ -282,7 +281,7 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
private:
static constexpr char VERSION[] = "version";
static constexpr char F0_CONFIG[] = "f0_measured";
static constexpr char F0_CONFIG_OTHER[] = "f0_measured_other";
static constexpr char F0_CONFIG_DUAL[] = "f0_measured_dual";
static constexpr char REDC_CONFIG[] = "redc_measured";
static constexpr char Q_CONFIG[] = "q_measured";
static constexpr char TICK_VOLTAGES_CONFIG[] = "v_tick";
@ -297,6 +296,10 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
public:
HwCal() {}
static std::unique_ptr<HwCal> Create() {
auto hwcal = std::unique_ptr<HwCal>(new HwCal());
return hwcal;
}
bool getVersion(uint32_t *value) override {
if (getPersist(VERSION, value)) {
@ -313,7 +316,7 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
std::string cal_0{8, '0'};
std::string cal_1{8, '0'};
if (getPersist(F0_CONFIG, &cal_0) && getPersist(F0_CONFIG_OTHER, &cal_1)) {
if (getPersist(F0_CONFIG, &cal_0) && getPersist(F0_CONFIG_DUAL, &cal_1)) {
float f0_0 = static_cast<float>(std::stoul(cal_0, nullptr, 16)) / (1 << 14);
float f0_1 = static_cast<float>(std::stoul(cal_1, nullptr, 16)) / (1 << 14);
float f0_offset = std::abs(f0_0 - f0_1)/2;
@ -328,7 +331,7 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
return true;
} else {
ALOGI("Vibrator: Unable to load F0_CONFIG or F0_CONFIG_OTHER config");
ALOGE("Vibrator: Unable to load F0_CONFIG or F0_CONFIG_DUAL config");
*value = 0;
return false;
}

View file

@ -21,7 +21,7 @@
#include <map>
#include "VibratorManager.h"
#include "Vibrator.h"
#include "utils.h"
namespace aidl {
@ -29,7 +29,7 @@ namespace android {
namespace hardware {
namespace vibrator {
class VibMgrHwApi : public VibratorManager::HwApi {
class VibMgrHwApi : public Vibrator::HwGPIO {
private:
const uint32_t DEBUG_GPI_PIN = UINT16_MAX;
const uint32_t DEBUG_GPI_PIN_SHIFT = UINT16_MAX;
@ -68,9 +68,9 @@ class VibMgrHwApi : public VibratorManager::HwApi {
}
bool initGPIO() override {
const auto gpio_dev = std::string() + "/dev/gpiochip" + std::to_string(mGPIOPin);
int fd = open(gpio_dev.c_str(), O_RDONLY);
int fd = open(gpio_dev.c_str(), O_CREAT | O_WRONLY, 0777);
if (fd < 0) {
ALOGE("InitGPIO: Unabled to open gpio dev: %s", strerror(errno));
ALOGE("InitGPIO: Unable to open gpio dev: %s", strerror(errno));
return false;
}
@ -79,12 +79,15 @@ class VibMgrHwApi : public VibratorManager::HwApi {
mRq.flags = GPIOHANDLE_REQUEST_OUTPUT;
int ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &mRq);
close(fd);
if (ret == -1) {
ALOGE("InitGPIO: Unable to line handle from ioctl : %s", strerror(errno));
close(fd);
return false;
}
if (close(fd) == -1) {
ALOGE("Failed to close GPIO char dev");
return false;
}
// Reset gpio status to LOW
struct gpiohandle_data data;
data.values[0] = 0;
@ -97,7 +100,7 @@ class VibMgrHwApi : public VibratorManager::HwApi {
}
return true;
}
bool setTrigger(bool value) override {
bool setGPIOOutput(bool value) override {
struct gpiohandle_data data;
data.values[0] = value;
@ -107,7 +110,6 @@ class VibMgrHwApi : public VibratorManager::HwApi {
close(mRq.fd);
return false;
}
close(mRq.fd);
return true;
}

View file

@ -33,6 +33,11 @@
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
#endif
#ifdef LOG_TAG
#undef LOG_TAG
#define LOG_TAG std::getenv("HAPTIC_NAME")
#endif
namespace aidl {
namespace android {
namespace hardware {
@ -51,10 +56,14 @@ static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6; // I2C Transaction + DSP
static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time
static constexpr int8_t MAX_PAUSE_TIMING_ERROR_MS = 1; // ALERT Irq Handling
static constexpr uint32_t MAX_TIME_MS = UINT16_MAX;
static constexpr float SETTING_TIME_OVERHEAD = 26; // This time was combined by
// HAL set the effect to
// driver and the kernel
// executes the effect before
// chip play the effect
static constexpr auto ASYNC_COMPLETION_TIMEOUT = std::chrono::milliseconds(100);
static constexpr auto POLLING_TIMEOUT = 20;
static constexpr auto POLLING_TIMEOUT_IN_SYNC = 100;
static constexpr int32_t COMPOSE_DELAY_MAX_MS = 10000;
/* nsections is 8 bits. Need to preserve 1 section for the first delay before the first effect. */
@ -93,24 +102,12 @@ static constexpr float PWLE_BW_MAP_SIZE =
/*
* [15] Edge, 0:Falling, 1:Rising
* [14:12] GPI_NUM, 1:GPI1 (with CS40L26A, 1 is the only supported GPI)
* [8] BANK, 0:ROM, 1:RAM
* [8] BANK, 0:RAM, 1:R0M
* [7] USE_BUZZGEN, 0:Not buzzgen, 1:buzzgen
* [6:0] WAVEFORM_INDEX
* 0x9100 = 1001 0001 0000 0000: Rising + GPI1 + ROM + Not buzzgen
* 0x9100 = 1001 0001 0000 0000: Rising + GPI1 + RAM + Not buzzgen
*/
static constexpr uint32_t GPIO_TRIGGER_CONFIG = 0x9100;
const char *kHAPNAME = std::getenv("HAPTIC_NAME");
#undef ALOGV
#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, kHAPNAME, __VA_ARGS__))
#undef ALOGD
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, kHAPNAME, __VA_ARGS__))
#undef ALOGI
#define ALOGI(...) ((void)ALOG(LOG_INFO, kHAPNAME, __VA_ARGS__))
#undef ALOGW
#define ALOGW(...) ((void)ALOG(LOG_WARN, kHAPNAME, __VA_ARGS__))
#undef ALOGE
#define ALOGE(...) ((void)ALOG(LOG_ERROR, kHAPNAME, __VA_ARGS__))
static constexpr uint16_t GPIO_TRIGGER_CONFIG = 0x9100;
static uint16_t amplitudeToScale(float amplitude, float maximum) {
float ratio = 100; /* Unit: % */
@ -166,8 +163,6 @@ enum vibe_state {
VIBE_STATE_ASP,
};
std::mutex mActiveId_mutex; // protects mActiveId
static int min(int x, int y) {
return x < y ? x : y;
}
@ -242,12 +237,24 @@ static int dspmem_chunk_flush(struct dspmem_chunk *ch) {
return dspmem_chunk_write(ch, 24 - ch->cachebits, 0);
}
Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
: mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)), mAsyncHandle(std::async([] {})) {
Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> hwCalDefault,
std::unique_ptr<HwApi> hwApiDual, std::unique_ptr<HwCal> hwCalDual,
std::unique_ptr<HwGPIO> hwgpio)
: mHwApiDef(std::move(hwApiDefault)),
mHwCalDef(std::move(hwCalDefault)),
mHwApiDual(std::move(hwApiDual)),
mHwCalDual(std::move(hwCalDual)),
mHwGPIO(std::move(hwgpio)),
mAsyncHandle(std::async([] {})) {
int32_t longFrequencyShift;
std::string caldata{8, '0'};
uint32_t calVer;
// ==================Single actuators and dual actuators checking =============================
if ((mHwApiDual != nullptr) && (mHwCalDual != nullptr))
mIsDual = true;
// ==================INPUT Devices== Base =================
const char *inputEventName = std::getenv("INPUT_EVENT_NAME");
const char *inputEventPathName = std::getenv("INPUT_EVENT_PATH");
if ((strstr(inputEventName, "cs40l26") != nullptr) ||
@ -296,10 +303,63 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
ALOGE("The input name %s is not cs40l26_input or cs40l26_dual_input", inputEventName);
}
// ==================INPUT Devices== Flip =================
if (mIsDual) {
const char *inputEventNameDual = std::getenv("INPUT_EVENT_NAME_DUAL");
if ((strstr(inputEventNameDual, "cs40l26_dual_input") != nullptr)) {
glob_t inputEventPaths;
int fd = -1;
int ret;
uint32_t val = 0;
char str[20] = {0x00};
for (uint8_t retry = 0; retry < 10; retry++) {
ret = glob(inputEventPathName, 0, nullptr, &inputEventPaths);
if (ret) {
ALOGE("Failed to get flip's input event paths (%d): %s", errno,
strerror(errno));
} else {
for (int i = 0; i < inputEventPaths.gl_pathc; i++) {
fd = TEMP_FAILURE_RETRY(open(inputEventPaths.gl_pathv[i], O_RDWR));
if (fd > 0) {
if (ioctl(fd, EVIOCGBIT(0, sizeof(val)), &val) > 0 &&
(val & (1 << EV_FF)) &&
ioctl(fd, EVIOCGNAME(sizeof(str)), &str) > 0 &&
strstr(str, inputEventNameDual) != nullptr) {
mInputFdDual.reset(fd);
ALOGI("Control %s through %s", inputEventNameDual,
inputEventPaths.gl_pathv[i]);
break;
}
close(fd);
}
}
}
if (ret == 0) {
globfree(&inputEventPaths);
}
if (mInputFdDual.ok()) {
break;
}
sleep(1);
ALOGW("Retry #%d to search in %zu input devices.", retry, inputEventPaths.gl_pathc);
}
if (!mInputFdDual.ok()) {
ALOGE("Failed to get an input event with name %s", inputEventNameDual);
}
ALOGE("HWAPI: %s", std::getenv("HWAPI_PATH_PREFIX"));
} else {
ALOGE("The input name %s is not cs40l26_dual_input", inputEventNameDual);
}
}
// ====================HAL internal effect table== Base ==================================
mFfEffects.resize(WAVEFORM_MAX_INDEX);
mEffectDurations.resize(WAVEFORM_MAX_INDEX);
mEffectDurations = {
1000, 100, 30, 1000, 300, 130, 150, 500, 100, 15, 20, 1000, 1000, 1000,
1000, 100, 32, 1000, 300, 130, 150, 500, 100, 10, 12, 1000, 1000, 1000,
}; /* 11+3 waveforms. The duration must < UINT16_MAX */
uint8_t effectIndex;
@ -317,7 +377,7 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
// Bypass the waveform update due to different input name
if ((strstr(inputEventName, "cs40l26") != nullptr) ||
(strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
if (!mHwApi->setFFEffect(
if (!mHwApiDef->setFFEffect(
mInputFd, &mFfEffects[effectIndex],
static_cast<uint16_t>(mFfEffects[effectIndex].replay.length))) {
ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno));
@ -339,50 +399,124 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
}
}
if (mHwCal->getF0(&caldata)) {
mHwApi->setF0(caldata);
// ====================HAL internal effect table== Flip ==================================
if (mIsDual) {
mFfEffectsDual.resize(WAVEFORM_MAX_INDEX);
for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) {
if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
/* Initialize physical waveforms. */
mFfEffectsDual[effectIndex] = {
.type = FF_PERIODIC,
.id = -1,
.replay.length = static_cast<uint16_t>(mEffectDurations[effectIndex]),
.u.periodic.waveform = FF_CUSTOM,
.u.periodic.custom_data = new int16_t[2]{RAM_WVFRM_BANK, effectIndex},
.u.periodic.custom_len = FF_CUSTOM_DATA_LEN,
};
// Bypass the waveform update due to different input name
if ((strstr(inputEventName, "cs40l26") != nullptr) ||
(strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
if (!mHwApiDual->setFFEffect(
mInputFdDual, &mFfEffectsDual[effectIndex],
static_cast<uint16_t>(mFfEffectsDual[effectIndex].replay.length))) {
ALOGE("Failed upload flip's effect %d (%d): %s", effectIndex, errno,
strerror(errno));
}
}
if (mFfEffectsDual[effectIndex].id != effectIndex) {
ALOGW("Unexpected effect index: %d -> %d", effectIndex,
mFfEffectsDual[effectIndex].id);
}
} else {
/* Initiate placeholders for OWT effects. */
mFfEffectsDual[effectIndex] = {
.type = FF_PERIODIC,
.id = -1,
.replay.length = 0,
.u.periodic.waveform = FF_CUSTOM,
.u.periodic.custom_data = nullptr,
.u.periodic.custom_len = 0,
};
}
}
}
if (mHwCal->getRedc(&caldata)) {
mHwApi->setRedc(caldata);
// ==============Calibration data checking======================================
if (mHwCalDef->getF0(&caldata)) {
mHwApiDef->setF0(caldata);
}
if (mHwCal->getQ(&caldata)) {
mHwApi->setQ(caldata);
if (mHwCalDef->getRedc(&caldata)) {
mHwApiDef->setRedc(caldata);
}
if (mHwCalDef->getQ(&caldata)) {
mHwApiDef->setQ(caldata);
}
if (mHwCal->getF0SyncOffset(&mF0Offset)) {
ALOGI("Vibrator::Vibrator: F0 offset calculated from both base and flip calibration data: %u", mF0Offset);
if (mHwCalDef->getF0SyncOffset(&mF0Offset)) {
ALOGD("Vibrator::Vibrator: F0 offset calculated from both base and flip calibration data: "
"%u",
mF0Offset);
} else {
mHwCal->getLongFrequencyShift(&longFrequencyShift);
if (longFrequencyShift > 0) {
mF0Offset = longFrequencyShift * std::pow(2, 14);
} else if (longFrequencyShift < 0) {
mF0Offset = std::pow(2, 24) - std::abs(longFrequencyShift) * std::pow(2, 14);
} else {
mF0Offset = 0;
}
ALOGI("Vibrator::Vibrator: F0 offset calculated from long shift frequency: %u", mF0Offset);
mHwCalDef->getLongFrequencyShift(&longFrequencyShift);
if (longFrequencyShift > 0) {
mF0Offset = longFrequencyShift * std::pow(2, 14);
} else if (longFrequencyShift < 0) {
mF0Offset = std::pow(2, 24) - std::abs(longFrequencyShift) * std::pow(2, 14);
} else {
mF0Offset = 0;
}
ALOGD("Vibrator::Vibrator: F0 offset calculated from long shift frequency: %u", mF0Offset);
}
mHwCal->getVersion(&calVer);
if (mIsDual) {
if (mHwCalDual->getF0(&caldata)) {
mHwApiDual->setF0(caldata);
}
if (mHwCalDual->getRedc(&caldata)) {
mHwApiDual->setRedc(caldata);
}
if (mHwCalDual->getQ(&caldata)) {
mHwApiDual->setQ(caldata);
}
if (mHwCalDual->getF0SyncOffset(&mF0OffsetDual)) {
ALOGD("Vibrator::Vibrator: Dual: F0 offset calculated from both base and flip "
"calibration data: "
"%u",
mF0OffsetDual);
}
}
mHwCalDef->getVersion(&calVer);
if (calVer == 2) {
mHwCal->getTickVolLevels(&mTickEffectVol);
mHwCal->getClickVolLevels(&mClickEffectVol);
mHwCal->getLongVolLevels(&mLongEffectVol);
mHwCalDef->getTickVolLevels(&(mTickEffectVol));
mHwCalDef->getClickVolLevels(&(mClickEffectVol));
mHwCalDef->getLongVolLevels(&(mLongEffectVol));
} else {
ALOGW("Unsupported calibration version! Using the default calibration value");
mHwCal->getTickVolLevels(&mTickEffectVol);
mHwCal->getClickVolLevels(&mClickEffectVol);
mHwCal->getLongVolLevels(&mLongEffectVol);
mHwCalDef->getTickVolLevels(&(mTickEffectVol));
mHwCalDef->getClickVolLevels(&(mClickEffectVol));
mHwCalDef->getLongVolLevels(&(mLongEffectVol));
}
mHwApi->setF0CompEnable(mHwCal->isF0CompEnabled());
mHwApi->setRedcCompEnable(mHwCal->isRedcCompEnabled());
// ================Project specific setting to driver===============================
mHwApiDef->setF0CompEnable(mHwCalDef->isF0CompEnabled());
mHwApiDef->setRedcCompEnable(mHwCalDef->isRedcCompEnabled());
mHwApiDef->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
if (mIsDual) {
mHwApiDual->setF0CompEnable(mHwCalDual->isF0CompEnabled());
mHwApiDual->setRedcCompEnable(mHwCalDual->isRedcCompEnabled());
mHwApiDual->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
}
// ===============Audio coupled haptics bool init ========
mIsUnderExternalControl = false;
mIsChirpEnabled = mHwCal->isChirpEnabled();
// =============== Compose PWLE check =====================================
mIsChirpEnabled = mHwCalDef->isChirpEnabled();
mHwCal->getSupportedPrimitives(&mSupportedPrimitivesBits);
mHwCalDef->getSupportedPrimitives(&mSupportedPrimitivesBits);
if (mSupportedPrimitivesBits > 0) {
for (auto e : defaultSupportedPrimitives) {
if (mSupportedPrimitivesBits & (1 << uint32_t(e))) {
@ -395,7 +529,12 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
}
mSupportedPrimitives = defaultSupportedPrimitives;
}
mHwApi->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
// ====== Get GPIO status and init it ================
mGPIOStatus = mHwGPIO->getGPIO();
if (!mGPIOStatus || !mHwGPIO->initGPIO()) {
ALOGE("Vibrator: GPIO initialization process error");
}
}
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
@ -409,7 +548,7 @@ ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
} else {
ALOGE("No haptics ALSA device");
}
if (mHwApi->hasOwtFreeSpace()) {
if (mHwApiDef->hasOwtFreeSpace()) {
ret |= IVibrator::CAP_COMPOSE_EFFECTS;
if (mIsChirpEnabled) {
ret |= IVibrator::CAP_FREQUENCY_CONTROL | IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
@ -425,31 +564,49 @@ ndk::ScopedAStatus Vibrator::off() {
const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
if (mActiveId >= 0) {
ALOGV("Off: Stop the active effect: %d", mActiveId);
ALOGD("Off: Stop the active effect: %d", mActiveId);
/* Stop the active effect. */
if (!mHwApi->setFFPlay(mInputFd, mActiveId, false)) {
ALOGE("Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno));
if (!mHwApiDef->setFFPlay(mInputFd, mActiveId, false)) {
ALOGE("Off: Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno));
ret = false;
}
if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
ALOGE("Failed to clean up the composed effect %d", mActiveId);
(!mHwApiDef->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
ALOGE("Off: Failed to clean up the composed effect %d", mActiveId);
ret = false;
}
mHwApi->clearTrigBtn(mInputFd, &mFfEffects[mActiveId], mActiveId);
if (mIsDual) {
if (!mHwApiDual->setFFPlay(mInputFdDual, mActiveId, false)) {
ALOGE("Off: Failed to stop flip's effect %d (%d): %s", mActiveId, errno,
strerror(errno));
ret = false;
}
if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApiDual->eraseOwtEffect(mInputFdDual, mActiveId, &mFfEffectsDual))) {
ALOGE("Off: Failed to clean up flip's the composed effect %d", mActiveId);
ret = false;
}
}
if (!mHwGPIO->setGPIOOutput(false)) {
ALOGE("Off: Failed to reset GPIO(%d): %s", errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
} else {
ALOGV("Off: Vibrator is already off");
ALOGD("Off: Vibrator is already off");
}
mActiveId = -1;
setGlobalAmplitude(false);
if (mF0Offset) {
mHwApi->setF0Offset(0);
mHwApiDef->setF0Offset(0);
if (mIsDual && mF0OffsetDual) {
mHwApiDual->setF0Offset(0);
}
}
if (ret) {
ALOGD("Off: Done.");
return ndk::ScopedAStatus::ok();
} else {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
@ -458,13 +615,8 @@ ndk::ScopedAStatus Vibrator::off() {
ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
const std::shared_ptr<IVibratorCallback> &callback) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::on");
if (isBusy()) {
ALOGD("Vibrator::on, isBusy");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ALOGD("Vibrator::on");
if (timeoutMs > MAX_TIME_MS) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@ -477,7 +629,10 @@ ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
}
setGlobalAmplitude(true);
if (mF0Offset) {
mHwApi->setF0Offset(mF0Offset);
mHwApiDef->setF0Offset(mF0Offset);
if (mIsDual && mF0OffsetDual) {
mHwApiDual->setF0Offset(mF0OffsetDual);
}
}
return on(timeoutMs, index, nullptr /*ignored*/, callback);
}
@ -485,8 +640,8 @@ ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
const std::shared_ptr<IVibratorCallback> &callback,
int32_t *_aidl_return) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::perform");
ALOGD("Vibrator::perform");
return performEffect(effect, strength, callback, _aidl_return);
}
@ -497,7 +652,6 @@ ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect> *_aidl_retu
}
ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::setAmplitude");
if (amplitude <= 0.0f || amplitude > 1.0f) {
@ -513,17 +667,12 @@ ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
}
ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::setExternalControl");
if (isSynced()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
setGlobalAmplitude(enabled);
if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) {
if (!mHwApi->setHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) {
if (!mHwApiDef->setHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) {
ALOGE("Failed to %s haptic pcm device: %d", (enabled ? "enable" : "disable"), mDevice);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
@ -562,8 +711,8 @@ ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
if (!status.isOk()) {
return status;
}
*durationMs = mEffectDurations[effectIndex];
// Please check the overhead time detail in b/261841035
*durationMs = mEffectDurations[effectIndex] + SETTING_TIME_OVERHEAD;
} else {
*durationMs = 0;
}
@ -572,8 +721,8 @@ ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composite,
const std::shared_ptr<IVibratorCallback> &callback) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::compose");
ALOGD("Vibrator::compose");
uint16_t size;
uint16_t nextEffectDelay;
@ -677,68 +826,134 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
effectIndex = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE;
uint32_t freeBytes;
mHwApi->getOwtFreeSpace(&freeBytes);
mHwApiDef->getOwtFreeSpace(&freeBytes);
if (dspmem_chunk_bytes(ch) > freeBytes) {
ALOGE("Invalid OWT length: Effect %d: %d > %d!", effectIndex, dspmem_chunk_bytes(ch),
freeBytes);
delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
int errorStatus;
if (isSynced()) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
if (mIsDual) {
mHwApiDual->getOwtFreeSpace(&freeBytes);
if (dspmem_chunk_bytes(ch) > freeBytes) {
ALOGE("Invalid OWT length in flip: Effect %d: %d > %d!", effectIndex,
dspmem_chunk_bytes(ch), freeBytes);
delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
if (!mHwApi->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch),
&mFfEffects[effectIndex], &effectIndex, &errorStatus)) {
int errorStatus;
if (mGPIOStatus && mIsDual) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
} else {
ALOGD("Not dual haptics HAL and GPIO status fail");
}
if (!mHwApiDef->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch),
&mFfEffects[effectIndex], &effectIndex, &errorStatus)) {
delete ch;
ALOGE("Invalid uploadOwtEffect");
return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
}
if (mIsDual && !mHwApiDual->uploadOwtEffect(mInputFdDual, ch->head, dspmem_chunk_bytes(ch),
&mFfEffectsDual[effectIndex], &effectIndex,
&errorStatus)) {
delete ch;
ALOGE("Invalid uploadOwtEffect in flip");
return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
}
delete ch;
} else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX ||
effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) {
/* Update duration for long/short vibration. */
mFfEffects[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
if (isSynced()) {
if (mGPIOStatus && mIsDual) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
} else {
ALOGD("Not dual haptics HAL and GPIO status fail");
}
if (!mHwApi->setFFEffect(mInputFd, &mFfEffects[effectIndex],
static_cast<uint16_t>(timeoutMs))) {
if (!mHwApiDef->setFFEffect(mInputFd, &mFfEffects[effectIndex],
static_cast<uint16_t>(timeoutMs))) {
ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
{
const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
mActiveId = effectIndex;
if (isSynced() &&
(effectIndex == WAVEFORM_CLICK_INDEX || effectIndex == WAVEFORM_LIGHT_TICK_INDEX)) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
if (!mHwApi->setFFEffect(mInputFd, &mFfEffects[effectIndex], mFfEffects[effectIndex].replay.length)) {
ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
if (mIsDual) {
mFfEffectsDual[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
if (!mHwApiDual->setFFEffect(mInputFdDual, &mFfEffectsDual[effectIndex],
static_cast<uint16_t>(timeoutMs))) {
ALOGE("Failed to edit flip's effect %d (%d): %s", effectIndex, errno,
strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
} else if (!isSynced()) {
// /* Play the event now. */
if (!mHwApi->setFFPlay(mInputFd, effectIndex, true)) {
}
}
{
const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
/* Play the event now. */
mActiveId = effectIndex;
if (!mGPIOStatus) {
ALOGE("GetVibrator: GPIO status error");
// Do playcode to play effect
if (!mHwApiDef->setFFPlay(mInputFd, effectIndex, true)) {
ALOGE("Failed to play effect %d (%d): %s", effectIndex, errno, strerror(errno));
mActiveId = -1;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mIsDual && !mHwApiDual->setFFPlay(mInputFdDual, effectIndex, true)) {
ALOGE("Failed to play flip's effect %d (%d): %s", effectIndex, errno,
strerror(errno));
mActiveId = -1;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
} else {
// Using GPIO to play effect
if ((effectIndex == WAVEFORM_CLICK_INDEX || effectIndex == WAVEFORM_LIGHT_TICK_INDEX)) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
if (!mHwApiDef->setFFEffect(mInputFd, &mFfEffects[effectIndex],
mFfEffects[effectIndex].replay.length)) {
ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mIsDual) {
mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
if (!mHwApiDual->setFFEffect(mInputFdDual, &mFfEffectsDual[effectIndex],
mFfEffectsDual[effectIndex].replay.length)) {
ALOGE("Failed to edit flip's effect %d (%d): %s", effectIndex, errno,
strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
}
if (!mHwGPIO->setGPIOOutput(true)) {
ALOGE("Failed to trigger effect %d (%d) by GPIO: %s", effectIndex, errno,
strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
}
mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback);
ALOGD("Vibrator::on, set done.");
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum) {
uint16_t scale = amplitudeToScale(amplitude, maximum);
if (!mHwApi->setFFGain(mInputFd, scale)) {
if (!mHwApiDef->setFFGain(mInputFd, scale)) {
ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mIsDual) {
if (!mHwApiDual->setFFGain(mInputFdDual, scale)) {
ALOGE("Failed to set flip's gain to %u (%d): %s", scale, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
return ndk::ScopedAStatus::ok();
}
@ -747,6 +962,7 @@ ndk::ScopedAStatus Vibrator::setGlobalAmplitude(bool set) {
if (!set) {
mLongEffectScale = 1.0; // Reset the scale for the later new effect.
}
return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX);
}
@ -764,7 +980,7 @@ ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t /*id*/) {
ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
std::string caldata{8, '0'};
if (!mHwCal->getF0(&caldata)) {
if (!mHwCalDef->getF0(&caldata)) {
ALOGE("Failed to get resonant frequency (%d): %s", errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
@ -775,7 +991,7 @@ ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
std::string caldata{8, '0'};
if (!mHwCal->getQ(&caldata)) {
if (!mHwCalDef->getQ(&caldata)) {
ALOGE("Failed to get q factor (%d): %s", errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
@ -1066,37 +1282,6 @@ bool Vibrator::isUnderExternalControl() {
return mIsUnderExternalControl;
}
// BnVibratorSync APIs
Status Vibrator::prepareSynced(const sp<IVibratorSyncCallback> &callback) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::prepareSynced");
if (isBusy() || isSynced() || isUnderExternalControl()) {
ALOGE("Vibrator::prepareSynced, isBusy or isSynced");
return Status::fromExceptionCode(EX_ILLEGAL_STATE);
}
mSyncedCallback = callback;
return Status::ok();
}
Status Vibrator::cancelSynced() {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::cancelSynced");
if (!isSynced()) {
ALOGE("Vibrator::cancelSynced, isSynced");
return Status::fromExceptionCode(EX_ILLEGAL_STATE);
}
off();
mSyncedCallback = nullptr;
return Status::ok();
}
// BnCInterface APIs
binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
@ -1110,29 +1295,38 @@ binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
dprintf(fd, "AIDL:\n");
dprintf(fd, " F0 Offset: %" PRIu32 "\n", mF0Offset);
dprintf(fd, " F0 Offset: base: %" PRIu32 " flip: %" PRIu32 "\n", mF0Offset, mF0OffsetDual);
dprintf(fd, " Voltage Levels:\n");
dprintf(fd, " Tick Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mTickEffectVol[0],
dprintf(fd, " Tick Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mTickEffectVol[0],
mTickEffectVol[1]);
dprintf(fd, " Click Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mClickEffectVol[0],
dprintf(fd, " Click Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mClickEffectVol[0],
mClickEffectVol[1]);
dprintf(fd, " Long Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mLongEffectVol[0],
dprintf(fd, " Long Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mLongEffectVol[0],
mLongEffectVol[1]);
dprintf(fd, " FF effect:\n");
dprintf(fd, " Physical waveform:\n");
dprintf(fd, "\tId\tIndex\tt ->\tt'\ttrigger button\n");
for (uint8_t effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
dprintf(fd, "==== Base ====\n\tId\tIndex\tt ->\tt'\ttrigger button\n");
uint8_t effectId;
for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
dprintf(fd, "\t%d\t%d\t%d\t%d\t%X\n", mFfEffects[effectId].id,
mFfEffects[effectId].u.periodic.custom_data[1], mEffectDurations[effectId],
mFfEffects[effectId].replay.length, mFfEffects[effectId].trigger.button);
}
if (mIsDual) {
dprintf(fd, "==== Flip ====\n\tId\tIndex\tt ->\tt'\ttrigger button\n");
for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
dprintf(fd, "\t%d\t%d\t%d\t%d\t%X\n", mFfEffectsDual[effectId].id,
mFfEffectsDual[effectId].u.periodic.custom_data[1], mEffectDurations[effectId],
mFfEffectsDual[effectId].replay.length,
mFfEffectsDual[effectId].trigger.button);
}
}
dprintf(fd, " OWT waveform:\n");
dprintf(fd, "Base: OWT waveform:\n");
dprintf(fd, "\tId\tBytes\tData\ttrigger button\n");
for (uint8_t effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX;
effectId++) {
for (effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX; effectId++) {
uint32_t numBytes = mFfEffects[effectId].u.periodic.custom_len * 2;
std::stringstream ss;
ss << " ";
@ -1146,15 +1340,38 @@ binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
dprintf(fd, "\t%d\t%d\t{%s}\t%X\n", mFfEffects[effectId].id, numBytes, ss.str().c_str(),
mFfEffects[effectId].trigger.button);
}
if (mIsDual) {
dprintf(fd, "Flip: OWT waveform:\n");
dprintf(fd, "\tId\tBytes\tData\ttrigger button\n");
for (effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX; effectId++) {
uint32_t numBytes = mFfEffectsDual[effectId].u.periodic.custom_len * 2;
std::stringstream ss;
ss << " ";
for (int i = 0; i < numBytes; i++) {
ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex
<< (uint16_t)(*(reinterpret_cast<uint8_t *>(
mFfEffectsDual[effectId].u.periodic.custom_data) +
i))
<< " ";
}
dprintf(fd, "\t%d\t%d\t{%s}\t%X\n", mFfEffectsDual[effectId].id, numBytes,
ss.str().c_str(), mFfEffectsDual[effectId].trigger.button);
}
}
dprintf(fd, "\n");
dprintf(fd, "\n");
mHwApi->debug(fd);
mHwApiDef->debug(fd);
dprintf(fd, "\n");
mHwCal->debug(fd);
mHwCalDef->debug(fd);
if (mIsDual) {
mHwApiDual->debug(fd);
dprintf(fd, "\n");
mHwCalDual->debug(fd);
}
fsync(fd);
return STATUS_OK;
@ -1165,7 +1382,7 @@ bool Vibrator::hasHapticAlsaDevice() {
// constructor is too early in the boot process and the pcm file contents
// are empty. Hence we make the call here once only right before we need to.
if (!mConfigHapticAlsaDeviceDone) {
if (mHwApi->getHapticAlsaDevice(&mCard, &mDevice)) {
if (mHwApiDef->getHapticAlsaDevice(&mCard, &mDevice)) {
mHasHapticAlsaDevice = true;
mConfigHapticAlsaDeviceDone = true;
} else {
@ -1373,26 +1590,52 @@ ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLev
}
void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
ALOGD("Callback status in waitForComplete(): mSync: %d, callBack: %d", isSynced(),
ALOGD("waitForComplete: Callback status in waitForComplete(): callBack: %d",
(callback != nullptr));
if (!mHwApi->pollVibeState(VIBE_STATE_HAPTIC,
(mSyncedCallback) ? POLLING_TIMEOUT_IN_SYNC : POLLING_TIMEOUT)) {
ALOGV("Failed to get state \"Haptic\"");
// Bypass checking flip part's haptic state
if (!mHwApiDef->pollVibeState(VIBE_STATE_HAPTIC, POLLING_TIMEOUT)) {
ALOGD("Failed to get state \"Haptic\"");
}
mHwApi->pollVibeState(VIBE_STATE_STOPPED);
mHwApiDef->pollVibeState(VIBE_STATE_STOPPED);
// Check flip's state after base was done
if (mIsDual) {
mHwApiDual->pollVibeState(VIBE_STATE_STOPPED);
}
ALOGD("waitForComplete: get STOP");
{
const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
ALOGE("Failed to clean up the composed effect %d", mActiveId);
}
uint32_t effectCount;
mHwApiDef->getEffectCount(&effectCount);
if (mActiveId >= 0) {
mHwApi->clearTrigBtn(mInputFd, &mFfEffects[mActiveId], mActiveId);
if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApiDef->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
ALOGE("Failed to clean up the composed effect %d", mActiveId);
}
if (mIsDual && (mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApiDual->eraseOwtEffect(mInputFdDual, mActiveId, &mFfEffectsDual))) {
ALOGE("Failed to clean up flip's composed effect %d", mActiveId);
}
if (mGPIOStatus && !mHwGPIO->setGPIOOutput(false)) {
ALOGE("waitForComplete: Failed to reset GPIO(%d): %s", errno, strerror(errno));
}
mActiveId = -1;
} else if (effectCount > WAVEFORM_MAX_PHYSICAL_INDEX) {
if (!mHwApiDef->eraseOwtEffect(mInputFd, effectCount - 1, &mFfEffects)) {
ALOGE("Failed to clean up the composed effect %d", effectCount - 1);
}
if (mIsDual) {
mHwApiDual->getEffectCount(&effectCount);
if ((effectCount > WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApiDual->eraseOwtEffect(mInputFdDual, effectCount - 1, &mFfEffectsDual))) {
ALOGE("Failed to clean up flip's composed effect %d", effectCount - 1);
}
}
} else {
ALOGV("waitForComplete: Vibrator is already off");
ALOGD("waitForComplete: Vibrator is already off");
}
}
@ -1402,14 +1645,7 @@ void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
ALOGE("Failed completion callback: %d", ret.getExceptionCode());
}
}
if (mSyncedCallback) {
auto ret = mSyncedCallback->onComplete();
if (!ret.isOk()) {
ALOGE("Failed completion mSyncedCallback: %d", ret.exceptionCode());
}
mSyncedCallback = nullptr;
}
ALOGV("waitForComplete: Done");
ALOGD("waitForComplete: Done.");
}
uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
@ -1442,16 +1678,6 @@ uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
return volLevel;
}
bool Vibrator::isBusy() {
auto timeout = std::chrono::seconds::zero();
auto status = mAsyncHandle.wait_for(timeout);
return status != std::future_status::ready;
}
bool Vibrator::isSynced() {
return (mSyncedCallback != nullptr);
}
} // namespace vibrator
} // namespace hardware
} // namespace android

View file

@ -17,7 +17,6 @@
#include <aidl/android/hardware/vibrator/BnVibrator.h>
#include <android-base/unique_fd.h>
#include <android/hardware/vibrator/BnVibratorSyncCallback.h>
#include <linux/input.h>
#include <tinyalsa/asoundlib.h>
@ -30,12 +29,22 @@ namespace android {
namespace hardware {
namespace vibrator {
using ::android::sp;
using ::android::binder::Status;
using ::android::hardware::vibrator::IVibratorSyncCallback;
class Vibrator : public BnVibrator {
public:
// APIs for interfacing with the GPIO pin.
class HwGPIO {
public:
virtual ~HwGPIO() = default;
// Get the GPIO pin num and address shift information
virtual bool getGPIO() = 0;
// Init the GPIO function
virtual bool initGPIO() = 0;
// Trigger the GPIO pin to synchronize both vibrators's play
virtual bool setGPIOOutput(bool value) = 0;
// Emit diagnostic information to the given file.
virtual void debug(int fd) = 0;
};
// APIs for interfacing with the kernel driver.
class HwApi {
public:
@ -85,8 +94,6 @@ class Vibrator : public BnVibrator {
int *status) = 0;
// Erase OWT waveform
virtual bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) = 0;
// Erase trigger button information
virtual void clearTrigBtn(int fd, struct ff_effect *effect, int8_t id) = 0;
// Emit diagnostic information to the given file.
virtual void debug(int fd) = 0;
};
@ -129,7 +136,9 @@ class Vibrator : public BnVibrator {
};
public:
Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal);
Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> hwCalDefault,
std::unique_ptr<HwApi> hwApiDual, std::unique_ptr<HwCal> hwCalDual,
std::unique_ptr<HwGPIO> hwgpio);
// BnVibrator APIs
ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return) override;
@ -163,10 +172,6 @@ class Vibrator : public BnVibrator {
ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
const std::shared_ptr<IVibratorCallback> &callback) override;
// BnVibratorSync APIs
Status prepareSynced(const sp<IVibratorSyncCallback> &callback);
Status cancelSynced();
// BnCInterface APIs
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
@ -198,33 +203,36 @@ class Vibrator : public BnVibrator {
bool hasHapticAlsaDevice();
bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device);
bool isBusy();
bool isSynced();
std::unique_ptr<HwApi> mHwApi;
std::unique_ptr<HwCal> mHwCal;
std::unique_ptr<HwApi> mHwApiDef;
std::unique_ptr<HwCal> mHwCalDef;
std::unique_ptr<HwApi> mHwApiDual;
std::unique_ptr<HwCal> mHwCalDual;
std::unique_ptr<HwGPIO> mHwGPIO;
uint32_t mF0Offset;
uint32_t mF0OffsetDual;
std::array<uint32_t, 2> mTickEffectVol;
std::array<uint32_t, 2> mClickEffectVol;
std::array<uint32_t, 2> mLongEffectVol;
std::vector<ff_effect> mFfEffects;
std::vector<ff_effect> mFfEffectsDual;
std::vector<uint32_t> mEffectDurations;
std::future<void> mAsyncHandle;
::android::base::unique_fd mInputFd;
::android::base::unique_fd mInputFdDual;
int8_t mActiveId{-1};
struct pcm *mHapticPcm;
int mCard;
int mDevice;
bool mHasHapticAlsaDevice{false};
bool mIsUnderExternalControl;
float mLongEffectScale = 1.0;
float mLongEffectScale{1.0};
bool mIsChirpEnabled;
uint32_t mSupportedPrimitivesBits = 0x0;
std::vector<CompositePrimitive> mSupportedPrimitives;
bool mConfigHapticAlsaDeviceDone{false};
// prevent concurrent execution of IVibrator and IVibratorSync APIs
sp<IVibratorSyncCallback> mSyncedCallback;
std::recursive_mutex mApiMutex;
bool mGPIOStatus;
bool mIsDual{false};
std::mutex mActiveId_mutex; // protects mActiveId
};
} // namespace vibrator

View file

@ -1,229 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VibratorManager.h"
#include <android/hardware/vibrator/BnVibratorSyncCallback.h>
#include <log/log.h>
#include <utils/Trace.h>
#include <numeric>
namespace aidl {
namespace android {
namespace hardware {
namespace vibrator {
const char *kHAPMGRNAME = std::getenv("HAPTIC_MGR_NAME");
#undef ALOGV
#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, kHAPMGRNAME, __VA_ARGS__))
#undef ALOGD
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, kHAPMGRNAME, __VA_ARGS__))
#undef ALOGI
#define ALOGI(...) ((void)ALOG(LOG_INFO, kHAPMGRNAME, __VA_ARGS__))
#undef ALOGW
#define ALOGW(...) ((void)ALOG(LOG_WARN, kHAPMGRNAME, __VA_ARGS__))
#undef ALOGE
#define ALOGE(...) ((void)ALOG(LOG_ERROR, kHAPMGRNAME, __VA_ARGS__))
using ::android::sp;
using ::android::binder::Status;
using ::android::hardware::vibrator::BnVibratorSyncCallback;
class VibratorSyncCallback : public BnVibratorSyncCallback {
public:
Status onComplete() override {
mPromise.set_value();
return Status::ok();
}
auto getFuture() { return mPromise.get_future(); }
private:
std::promise<void> mPromise;
};
VibratorManager::VibratorManager(std::unique_ptr<HwApi> hwapi,
const std::vector<VibratorTuple> &&vibrators)
: mHwApi(std::move(hwapi)), mVibrators(std::move(vibrators)), mAsyncHandle(std::async([] {})) {
mGPIOStatus = mHwApi->getGPIO();
}
// BnVibratorManager APIs
ndk::ScopedAStatus VibratorManager::getCapabilities(int32_t *_aidl_return) {
ATRACE_NAME("VibratorManager::getCapabilities");
int32_t ret =
IVibratorManager::CAP_SYNC | IVibratorManager::CAP_PREPARE_ON |
IVibratorManager::CAP_PREPARE_PERFORM | IVibratorManager::CAP_PREPARE_COMPOSE |
IVibratorManager::CAP_MIXED_TRIGGER_ON | IVibratorManager::CAP_MIXED_TRIGGER_PERFORM |
IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE | IVibratorManager::CAP_TRIGGER_CALLBACK;
*_aidl_return = ret;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::getVibratorIds(std::vector<int> *_aidl_return) {
ATRACE_NAME("VibratorManager::getVibratorIds");
_aidl_return->resize(mVibrators.size());
std::iota(_aidl_return->begin(), _aidl_return->end(), 0);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::getVibrator(int vibratorId,
std::shared_ptr<IVibrator> *_aidl_return) {
ATRACE_NAME("VibratorManager::getVibrator");
if (!mGPIOStatus) {
ALOGE("GetVibrator: GPIO status error");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (vibratorId >= mVibrators.size()) {
ALOGE("GetVibrator: wrong requested vibrator ID");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
std::tie(*_aidl_return, std::ignore) = mVibrators.at(vibratorId);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::prepareSynced(const std::vector<int32_t> &ids) {
ATRACE_NAME("VibratorManager::prepareSynced");
if (!mGPIOStatus) {
ALOGE("prepareSynced: GPIO status error");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (ids.empty()) {
ALOGE("PrepareSynced: No vibrator could be synced");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (!mSyncContext.empty()) {
ALOGE("PrepareSynced: mSyncContext is not EMPTY!!!");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (isBusy()) {
ALOGE("PrepareSynced: IS BUSY!!!");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
for (auto &id : ids) {
auto &[vib, ext] = mVibrators.at(id);
auto callback = sp<VibratorSyncCallback>::make();
if (ext->prepareSynced(callback).isOk()) {
mSyncContext.emplace_back(id, callback->getFuture());
} else {
cancelSynced();
ALOGV("prepareSynced: Fail: %d", id);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
ALOGV("prepareSynced: Done");
if (mHwApi->initGPIO()) {
return ndk::ScopedAStatus::ok();
} else {
ALOGE("PrepareSynced: GPIO status init fail");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
ndk::ScopedAStatus VibratorManager::triggerSynced(
const std::shared_ptr<IVibratorCallback> &callback) {
ATRACE_NAME("VibratorManager::triggerSynced");
ALOGV("TriggerSynced");
if (isBusy()) {
ALOGE("TriggerSynced isBusy");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
mHwApi->setTrigger(true);
doAsync([=]() {
{
std::shared_lock lock(mContextMutex);
for (auto &[id, future] : mSyncContext) {
future.wait();
}
}
{
std::unique_lock lock(mContextMutex);
mSyncContext.clear();
}
if (callback) {
auto ret = callback->onComplete();
if (!ret.isOk()) {
ALOGE("Failed completion callback: %d", ret.getExceptionCode());
}
ALOGD("Callback in MANAGER onComplete()");
}
});
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::cancelSynced() {
ATRACE_NAME("VibratorManager::cancelSynced");
ALOGV("Do cancelSynced");
mHwApi->setTrigger(false);
{
std::shared_lock lock(mContextMutex);
for (auto &[id, future] : mSyncContext) {
auto &[vib, ext] = mVibrators.at(id);
ext->cancelSynced();
}
}
{
std::unique_lock lock(mContextMutex);
mSyncContext.clear();
}
mAsyncHandle.wait();
return ndk::ScopedAStatus::ok();
}
// BnCInterface APIs
binder_status_t VibratorManager::dump(int fd, const char ** /*args*/, uint32_t /*numArgs*/) {
if (fd < 0) {
ALOGE("Called debug() with invalid fd.");
return STATUS_OK;
}
mHwApi->debug(fd);
fsync(fd);
return STATUS_OK;
}
// Private Methods
template <typename Func, typename... Args>
void VibratorManager::doAsync(Func &&func, Args &&...args) {
mAsyncHandle = std::async(func, std::forward<Args>(args)...);
}
bool VibratorManager::isBusy() {
auto timeout = std::chrono::seconds::zero();
auto status = mAsyncHandle.wait_for(timeout);
return status != std::future_status::ready;
}
} // namespace vibrator
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -1,83 +0,0 @@
/*
* Copyright (C) 2021 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.
*/
#pragma once
#include <aidl/android/hardware/vibrator/BnVibrator.h>
#include <aidl/android/hardware/vibrator/BnVibratorManager.h>
#include <android/hardware/vibrator/IVibratorSync.h>
#include <future>
#include <shared_mutex>
namespace aidl {
namespace android {
namespace hardware {
namespace vibrator {
using ::android::hardware::vibrator::IVibratorSync;
class VibratorManager : public BnVibratorManager {
public:
// APIs for interfacing with the kernel driver.
class HwApi {
public:
virtual ~HwApi() = default;
// Get the GPIO pin num and address shift information
virtual bool getGPIO() = 0;
// Init the GPIO function
virtual bool initGPIO() = 0;
// Trigger activation of the synchronized vibrators.
virtual bool setTrigger(bool value) = 0;
// Emit diagnostic information to the given file.
virtual void debug(int fd) = 0;
};
using VibratorTuple = std::tuple<std::shared_ptr<IVibrator>, ::android::sp<IVibratorSync>>;
public:
VibratorManager(std::unique_ptr<HwApi> hwapi, const std::vector<VibratorTuple> &&vibrators);
// BnVibratorManager APIs
ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return);
ndk::ScopedAStatus getVibratorIds(std::vector<int> *_aidl_return);
ndk::ScopedAStatus getVibrator(int vibratorId, std::shared_ptr<IVibrator> *_aidl_return);
ndk::ScopedAStatus prepareSynced(const std::vector<int32_t> &ids) override;
ndk::ScopedAStatus triggerSynced(const std::shared_ptr<IVibratorCallback> &callback) override;
ndk::ScopedAStatus cancelSynced() override;
// BnCInterface APIs
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
private:
template <typename Func, typename... Args>
void doAsync(Func &&func, Args &&...args);
bool isBusy();
private:
using SyncContext = std::tuple<int32_t, std::future<void>>;
const std::unique_ptr<HwApi> mHwApi;
const std::vector<VibratorTuple> mVibrators;
std::vector<SyncContext> mSyncContext;
std::shared_mutex mContextMutex;
std::future<void> mAsyncHandle;
bool mGPIOStatus;
};
} // namespace vibrator
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -1,39 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VibratorSync.h"
namespace android {
namespace hardware {
namespace vibrator {
VibratorSync::VibratorSync(std::shared_ptr<Vibrator> vibrator) : mVibrator(vibrator) {
ALOGE("VibratorSync constructor");
}
// BnVibratorSync APIs
binder::Status VibratorSync::prepareSynced(const sp<IVibratorSyncCallback> &callback) {
return mVibrator->prepareSynced(callback);
}
binder::Status VibratorSync::cancelSynced() {
return mVibrator->cancelSynced();
}
} // namespace vibrator
} // namespace hardware
} // namespace android

View file

@ -1,46 +0,0 @@
/*
* Copyright (C) 2021 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.
*/
#pragma once
#include <android/hardware/vibrator/BnVibratorSync.h>
#include <array>
#include <fstream>
#include <future>
#include "Vibrator.h"
namespace android {
namespace hardware {
namespace vibrator {
using ::aidl::android::hardware::vibrator::Vibrator;
class VibratorSync : public BnVibratorSync {
public:
VibratorSync(std::shared_ptr<Vibrator> vibrator);
// BnVibratorSync APIs
binder::Status prepareSynced(const android::sp<IVibratorSyncCallback> &callback) override;
binder::Status cancelSynced() override;
private:
std::shared_ptr<Vibrator> mVibrator;
};
} // namespace vibrator
} // namespace hardware
} // namespace android

View file

@ -1,22 +0,0 @@
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "device_google_felix_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["device_google_felix_license"],
}
aidl_interface {
name: "android.hardware.vibrator.cs40l26-private",
srcs: [
"android/hardware/vibrator/*.aidl"
],
backend: {
java: {
enabled: false,
},
},
unstable: true,
vendor_available: true,
}

View file

@ -1,24 +0,0 @@
/*
* Copyright (C) 2020 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.
*/
package android.hardware.vibrator;
import android.hardware.vibrator.IVibratorSyncCallback;
interface IVibratorSync {
void prepareSynced(in IVibratorSyncCallback callback);
void cancelSynced();
}

View file

@ -1,21 +0,0 @@
/*
* Copyright (C) 2021 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.
*/
package android.hardware.vibrator;
interface IVibratorSyncCallback {
oneway void onComplete();
}

View file

@ -29,23 +29,25 @@ on property:vendor.all.modules.ready=1
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/redc_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/delay_before_stop_playback_us
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/delay_before_stop_playback_us
chown system system /dev/gpiochip44
enable vendor.vibrator.cs40l26
enable vendor.vibrator.cs40l26-dual
service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-private
class hal
user system
group system input
setenv HAPTIC_NAME HapticsBase
setenv HAPTIC_NAME Haptics
setenv INPUT_EVENT_NAME cs40l26_input
setenv INPUT_EVENT_NAME_DUAL cs40l26_dual_input
setenv INPUT_EVENT_PATH /dev/input/event*
setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal
setenv CALIBRATION_FILEPATH_OTHER /mnt/vendor/persist/haptics/cs40l26_dual.cal
setenv CALIBRATION_FILEPATH_DUAL /mnt/vendor/persist/haptics/cs40l26_dual.cal
setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a/
setenv HWAPI_PATH_PREFIX_DUAL /sys/bus/i2c/devices/i2c-cs40l26a-dual/
setenv HWAPI_DEBUG_PATHS "
calibration/f0_stored
calibration/redc_stored
@ -61,30 +63,3 @@ service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service
disabled
service vendor.vibrator.cs40l26-dual /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-private
class hal
user system
group system input
setenv HAPTIC_NAME HapticsFlip
setenv INPUT_EVENT_NAME cs40l26_dual_input
setenv INPUT_EVENT_PATH /dev/input/event*
setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26_dual.cal
setenv CALIBRATION_FILEPATH_OTHER /mnt/vendor/persist/haptics/cs40l26.cal
setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a-dual/
setenv HWAPI_DEBUG_PATHS "
calibration/f0_stored
calibration/redc_stored
calibration/q_stored
default/vibe_state
default/num_waves
default/f0_offset
default/owt_free_space
default/f0_comp_enable
default/redc_comp_enable
default/delay_before_stop_playback_us
"
disabled

View file

@ -4,9 +4,4 @@
<version>2</version>
<fqname>IVibrator/default</fqname>
</hal>
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>2</version>
<fqname>IVibrator/dual</fqname>
</hal>
</manifest>

View file

@ -1,15 +0,0 @@
on property:vendor.all.modules.ready=1
wait_for_prop init.svc.vendor.vibrator.cs40l26 running
wait_for_prop init.svc.vendor.vibrator.cs40l26-dual running
enable vendor.vibrator.cs40l26-stereo
service vendor.vibrator.cs40l26-stereo /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-stereo-private
class hal
user root
group root
setenv HAPTIC_MGR_NAME HapticsMgr
setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
disabled

View file

@ -1,7 +0,0 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>2</version>
<fqname>IVibratorManager/default</fqname>
</hal>
</manifest>

View file

@ -1,7 +0,0 @@
PRODUCT_PACKAGES += \
android.hardware.vibrator-service.cs40l26-private \
android.hardware.vibrator-service.cs40l26-stereo-private
BOARD_SEPOLICY_DIRS += \
hardware/google/pixel-sepolicy/vibrator/common \
hardware/google/pixel-sepolicy/vibrator/cs40l26

View file

@ -2,5 +2,5 @@ PRODUCT_PACKAGES += \
android.hardware.vibrator-service.cs40l26-private
BOARD_SEPOLICY_DIRS += \
device/google/felix-sepolicy/vibrator/common \
device/google/felix-sepolicy/vibrator/cs40l26
hardware/google/pixel-sepolicy/vibrator/common \
hardware/google/pixel-sepolicy/vibrator/cs40l26

View file

@ -1,82 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <log/log.h>
#include "VibMgrHwApi.h"
#include "VibratorManager.h"
using ::aidl::android::hardware::vibrator::IVibrator;
using ::aidl::android::hardware::vibrator::VibMgrHwApi;
using ::aidl::android::hardware::vibrator::VibratorManager;
using ::android::ProcessState;
using ::android::String16;
using ::android::waitForService;
using ::android::hardware::vibrator::IVibratorSync;
#if !defined(VIBRATOR_NAME)
#define VIBRATOR_NAME "default"
#endif
using ndk::SharedRefBase;
using ndk::SpAIBinder;
int main() {
auto hwapi = VibMgrHwApi::Create();
if (!hwapi) {
return EXIT_FAILURE;
}
const std::string vibratorInstances[] = {
"default",
"dual",
};
std::vector<VibratorManager::VibratorTuple> vibrators;
ProcessState::initWithDriver("/dev/vndbinder");
for (auto &instance : vibratorInstances) {
const auto svcName = std::string() + IVibrator::descriptor + "/" + instance;
const auto extName = std::stringstream() << IVibratorSync::descriptor << "/" << instance;
SpAIBinder svcBinder;
svcBinder = SpAIBinder(AServiceManager_getService(svcName.c_str()));
auto svc = IVibrator::fromBinder(svcBinder);
auto ext = waitForService<IVibratorSync>(String16(extName.str().c_str()));
vibrators.emplace_back(svc, ext);
}
auto mgr = ndk::SharedRefBase::make<VibratorManager>(std::move(hwapi), std::move(vibrators));
binder_status_t status;
const std::string mgrInst = std::string() + VibratorManager::descriptor + "/" VIBRATOR_NAME;
status = AServiceManager_addService(mgr->asBinder().get(), mgrInst.c_str());
LOG_ALWAYS_FATAL_IF(status != STATUS_OK);
// Only used for callbacks
ProcessState::self()->setThreadPoolMaxThreadCount(1);
ProcessState::self()->startThreadPool();
ABinderProcess_setThreadPoolMaxThreadCount(0);
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reach
}

View file

@ -20,40 +20,60 @@
#include <log/log.h>
#include "Hardware.h"
#include "VibMgrHwApi.h"
#include "Vibrator.h"
#include "VibratorSync.h"
using ::aidl::android::hardware::vibrator::HwApi;
using ::aidl::android::hardware::vibrator::HwCal;
using ::aidl::android::hardware::vibrator::VibMgrHwApi;
using ::aidl::android::hardware::vibrator::Vibrator;
using ::android::defaultServiceManager;
using ::android::ProcessState;
using ::android::sp;
using ::android::String16;
using ::android::hardware::vibrator::VibratorSync;
#if !defined(VIBRATOR_NAME)
#define VIBRATOR_NAME "default"
#endif
int main() {
const char *inputEventName = std::getenv("INPUT_EVENT_NAME");
std::string vibName = "";
if (strstr(inputEventName, "cs40l26_input") != nullptr) {
vibName.assign("default");
} else if (strstr(inputEventName, "cs40l26_dual_input") != nullptr) {
vibName.assign("dual");
} else {
ALOGE("Failed to init vibrator HAL");
return EXIT_FAILURE; // should not reach
}
auto svc = ndk::SharedRefBase::make<Vibrator>(std::make_unique<HwApi>(),
std::make_unique<HwCal>());
const auto svcName = std::string() + svc->descriptor + "/" + vibName;
const char *hwApiPathPrefixDual = std::getenv("HWAPI_PATH_PREFIX_DUAL");
const char *calFilePath = std::getenv("CALIBRATION_FILEPATH");
const char *calFilePathDual = std::getenv("CALIBRATION_FILEPATH_DUAL");
auto ext = sp<VibratorSync>::make(svc);
const auto extName = std::stringstream() << ext->descriptor << "/" << vibName;
auto hwgpio = VibMgrHwApi::Create();
if (!hwgpio) {
return EXIT_FAILURE;
}
auto hwApiDef = HwApi::Create();
if (!hwApiDef) {
return EXIT_FAILURE;
}
auto hwCalDef = HwCal::Create();
if (!hwCalDef) {
return EXIT_FAILURE;
}
std::shared_ptr<Vibrator> svc;
// Synchronize base and flip actuator F0.
// Replace dual cal file path to base and copy the base to dual's path.
if ((hwApiPathPrefixDual != nullptr) && !setenv("HWAPI_PATH_PREFIX", hwApiPathPrefixDual, 1) &&
(calFilePathDual != nullptr) && !setenv("CALIBRATION_FILEPATH", calFilePathDual, 1) &&
!setenv("CALIBRATION_FILEPATH_DUAL", calFilePath, 1)) {
ALOGD("Init dual HAL: %s", std::getenv("HWAPI_PATH_PREFIX"));
svc = ndk::SharedRefBase::make<Vibrator>(std::move(hwApiDef), std::move(hwCalDef),
std::make_unique<HwApi>(),
std::make_unique<HwCal>(), std::move(hwgpio));
} else {
ALOGD("Failed to init dual HAL");
svc = ndk::SharedRefBase::make<Vibrator>(std::move(hwApiDef), std::move(hwCalDef), nullptr,
nullptr, std::move(hwgpio));
}
const auto svcName = std::string() + svc->descriptor + "/" + VIBRATOR_NAME;
ProcessState::initWithDriver("/dev/vndbinder");
defaultServiceManager()->addService(String16(extName.str().c_str()), ext);
auto svcBinder = svc->asBinder();
binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str());
LOG_ALWAYS_FATAL_IF(status != STATUS_OK);

View file

@ -22,8 +22,8 @@ cc_test {
defaults: ["VibratorHalCs40l26TestDefaultsPrivate"],
srcs: [
"test-hwcal.cpp",
"test-hwapi.cpp",
"test-vibrator.cpp",
"test-hwapi.cpp",
"test-vibrator.cpp",
],
static_libs: [
"libc++fs",

View file

@ -20,6 +20,17 @@
#include "Vibrator.h"
class MockGPIO : public ::aidl::android::hardware::vibrator::Vibrator::HwGPIO {
public:
MOCK_METHOD0(destructor, void());
MOCK_METHOD0(getGPIO, bool());
MOCK_METHOD0(initGPIO, bool());
MOCK_METHOD1(setGPIOOutput, bool(bool value));
MOCK_METHOD1(debug, void(int fd));
~MockGPIO() override { destructor(); };
};
class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
public:
MOCK_METHOD0(destructor, void());
@ -43,7 +54,6 @@ class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
bool(int fd, uint8_t *owtData, uint32_t numBytes, struct ff_effect *effect,
uint32_t *outEffectIndex, int *status));
MOCK_METHOD3(eraseOwtEffect, bool(int fd, int8_t effectIndex, std::vector<ff_effect> *effect));
MOCK_METHOD3(clearTrigBtn, void(int fd, struct ff_effect *effect, int8_t index));
MOCK_METHOD1(debug, void(int fd));
~MockApi() override { destructor(); };

View file

@ -203,20 +203,24 @@ class VibratorTest : public Test {
setenv("INPUT_EVENT_NAME", "CS40L26TestSuite", true);
std::unique_ptr<MockApi> mockapi;
std::unique_ptr<MockCal> mockcal;
std::unique_ptr<MockGPIO> mockgpio;
createMock(&mockapi, &mockcal);
createVibrator(std::move(mockapi), std::move(mockcal));
createMock(&mockapi, &mockcal, &mockgpio);
createVibrator(std::move(mockapi), std::move(mockcal), std::move(mockgpio));
}
void TearDown() override { deleteVibrator(); }
protected:
void createMock(std::unique_ptr<MockApi> *mockapi, std::unique_ptr<MockCal> *mockcal) {
void createMock(std::unique_ptr<MockApi> *mockapi, std::unique_ptr<MockCal> *mockcal,
std::unique_ptr<MockGPIO> *mockgpio) {
*mockapi = std::make_unique<MockApi>();
*mockcal = std::make_unique<MockCal>();
*mockgpio = std::make_unique<MockGPIO>();
mMockApi = mockapi->get();
mMockCal = mockcal->get();
mMockGpio = mockgpio->get();
ON_CALL(*mMockApi, destructor()).WillByDefault(Assign(&mMockApi, nullptr));
@ -242,15 +246,20 @@ class VibratorTest : public Test {
ON_CALL(*mMockCal, getLongVolLevels(_))
.WillByDefault(DoAll(SetArgPointee<0>(V_LONG_DEFAULT), Return(true)));
ON_CALL(*mMockGpio, destructor()).WillByDefault(Assign(&mMockGpio, nullptr));
relaxMock(false);
}
void createVibrator(std::unique_ptr<MockApi> mockapi, std::unique_ptr<MockCal> mockcal,
bool relaxed = true) {
std::unique_ptr<MockGPIO> mockgpio, bool relaxed = true) {
if (relaxed) {
relaxMock(true);
}
mVibrator = ndk::SharedRefBase::make<Vibrator>(std::move(mockapi), std::move(mockcal));
// TODO(b/261415845): Need to add dual parameters to test the vibrator HAL's code in haptics
// mock test
mVibrator = ndk::SharedRefBase::make<Vibrator>(std::move(mockapi), std::move(mockcal),
nullptr, nullptr, std::move(mockgpio));
if (relaxed) {
relaxMock(false);
}
@ -306,6 +315,7 @@ class VibratorTest : public Test {
protected:
MockApi *mMockApi;
MockCal *mMockCal;
MockGPIO *mMockGpio;
std::shared_ptr<IVibrator> mVibrator;
uint32_t mEffectIndex;
};
@ -313,6 +323,7 @@ class VibratorTest : public Test {
TEST_F(VibratorTest, Constructor) {
std::unique_ptr<MockApi> mockapi;
std::unique_ptr<MockCal> mockcal;
std::unique_ptr<MockGPIO> mockgpio;
std::string f0Val = std::to_string(std::rand());
std::string redcVal = std::to_string(std::rand());
std::string qVal = std::to_string(std::rand());
@ -323,10 +334,11 @@ TEST_F(VibratorTest, Constructor) {
EXPECT_CALL(*mMockApi, destructor()).WillOnce(DoDefault());
EXPECT_CALL(*mMockCal, destructor()).WillOnce(DoDefault());
EXPECT_CALL(*mMockGpio, destructor()).WillOnce(DoDefault());
deleteVibrator(false);
createMock(&mockapi, &mockcal);
createMock(&mockapi, &mockcal, &mockgpio);
EXPECT_CALL(*mMockCal, getF0(_))
.InSequence(f0Seq)
@ -363,7 +375,7 @@ TEST_F(VibratorTest, Constructor) {
.WillOnce(DoAll(SetArgPointee<0>(supportedPrimitivesBits), Return(true)));
EXPECT_CALL(*mMockApi, setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US)).WillOnce(Return(true));
createVibrator(std::move(mockapi), std::move(mockcal), false);
createVibrator(std::move(mockapi), std::move(mockcal), std::move(mockgpio), false);
}
TEST_F(VibratorTest, on) {