[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/felix/audio/felix/audio-tables.mk
include device/google/gs201/device-shipping-common.mk include device/google/gs201/device-shipping-common.mk
$(call soong_config_set,fp_hal_feature,pixel_product, product_a) $(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 include device/google/gs101/bluetooth/bluetooth.mk
ifeq ($(filter factory_felix, $(TARGET_PRODUCT)),) ifeq ($(filter factory_felix, $(TARGET_PRODUCT)),)
include device/google/felix/uwb/uwb_calibration.mk include device/google/felix/uwb/uwb_calibration.mk

View file

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

View file

@ -34,7 +34,6 @@ cc_defaults {
"android.hardware.vibrator-defaults.cs40l26-private", "android.hardware.vibrator-defaults.cs40l26-private",
], ],
shared_libs: [ shared_libs: [
"android.hardware.vibrator.cs40l26-private-cpp",
"libcutils", "libcutils",
"libtinyalsa", "libtinyalsa",
], ],
@ -53,9 +52,6 @@ cc_defaults {
"android.hardware.vibrator-impl.cs40l26-private", "android.hardware.vibrator-impl.cs40l26-private",
"libtinyalsa", "libtinyalsa",
], ],
shared_libs: [
"android.hardware.vibrator.cs40l26-private-cpp",
],
} }
cc_library { cc_library {
@ -63,8 +59,6 @@ cc_library {
defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"], defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"],
srcs: [ srcs: [
"Vibrator.cpp", "Vibrator.cpp",
"VibratorSync.cpp",
"VibratorManager.cpp",
], ],
export_include_dirs: ["."], export_include_dirs: ["."],
vendor_available: true, vendor_available: true,
@ -82,15 +76,3 @@ cc_binary {
], ],
proprietary: true, 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 { class HwApi : public Vibrator::HwApi, private HwApiBase {
public: public:
static std::unique_ptr<HwApi> Create() {
auto hwapi = std::unique_ptr<HwApi>(new HwApi());
return hwapi;
}
HwApi() { HwApi() {
open("calibration/f0_stored", &mF0); open("calibration/f0_stored", &mF0);
open("default/f0_offset", &mF0Offset); 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 { bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) override {
uint32_t effectCountBefore, effectCountAfter, i, successFlush = 0; 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) { if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
ALOGE("Invalid waveform index for OWT erase: %d", effectIndex); ALOGE("Invalid waveform index for OWT erase: %d", effectIndex);
return false; 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) { if (effectIndex < WAVEFORM_MAX_INDEX) {
/* Normal situation. Only erase the effect which we just played. */ /* Normal situation. Only erase the effect which we just played. */
if (ioctl(fd, EVIOCRMFF, effectIndex) < 0) { if (ioctl(fd, EVIOCRMFF, effectIndex) < 0) {
@ -249,19 +257,10 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
(*effect)[i].id = -1; (*effect)[i].id = -1;
} }
} }
// Turn on the waiting time for SVC init phase to complete
setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
return true; 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); } void debug(int fd) override { HwApiBase::debug(fd); }
@ -282,7 +281,7 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
private: private:
static constexpr char VERSION[] = "version"; static constexpr char VERSION[] = "version";
static constexpr char F0_CONFIG[] = "f0_measured"; 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 REDC_CONFIG[] = "redc_measured";
static constexpr char Q_CONFIG[] = "q_measured"; static constexpr char Q_CONFIG[] = "q_measured";
static constexpr char TICK_VOLTAGES_CONFIG[] = "v_tick"; static constexpr char TICK_VOLTAGES_CONFIG[] = "v_tick";
@ -297,6 +296,10 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
public: public:
HwCal() {} HwCal() {}
static std::unique_ptr<HwCal> Create() {
auto hwcal = std::unique_ptr<HwCal>(new HwCal());
return hwcal;
}
bool getVersion(uint32_t *value) override { bool getVersion(uint32_t *value) override {
if (getPersist(VERSION, value)) { if (getPersist(VERSION, value)) {
@ -313,7 +316,7 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
std::string cal_0{8, '0'}; std::string cal_0{8, '0'};
std::string cal_1{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_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_1 = static_cast<float>(std::stoul(cal_1, nullptr, 16)) / (1 << 14);
float f0_offset = std::abs(f0_0 - f0_1)/2; float f0_offset = std::abs(f0_0 - f0_1)/2;
@ -328,7 +331,7 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
return true; return true;
} else { } 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; *value = 0;
return false; return false;
} }

View file

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

View file

@ -33,6 +33,11 @@
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0])) #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
#endif #endif
#ifdef LOG_TAG
#undef LOG_TAG
#define LOG_TAG std::getenv("HAPTIC_NAME")
#endif
namespace aidl { namespace aidl {
namespace android { namespace android {
namespace hardware { 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 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 int8_t MAX_PAUSE_TIMING_ERROR_MS = 1; // ALERT Irq Handling
static constexpr uint32_t MAX_TIME_MS = UINT16_MAX; 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 ASYNC_COMPLETION_TIMEOUT = std::chrono::milliseconds(100);
static constexpr auto POLLING_TIMEOUT = 20; static constexpr auto POLLING_TIMEOUT = 20;
static constexpr auto POLLING_TIMEOUT_IN_SYNC = 100;
static constexpr int32_t COMPOSE_DELAY_MAX_MS = 10000; 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. */ /* 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 * [15] Edge, 0:Falling, 1:Rising
* [14:12] GPI_NUM, 1:GPI1 (with CS40L26A, 1 is the only supported GPI) * [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 * [7] USE_BUZZGEN, 0:Not buzzgen, 1:buzzgen
* [6:0] WAVEFORM_INDEX * [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; static constexpr uint16_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 uint16_t amplitudeToScale(float amplitude, float maximum) { static uint16_t amplitudeToScale(float amplitude, float maximum) {
float ratio = 100; /* Unit: % */ float ratio = 100; /* Unit: % */
@ -166,8 +163,6 @@ enum vibe_state {
VIBE_STATE_ASP, VIBE_STATE_ASP,
}; };
std::mutex mActiveId_mutex; // protects mActiveId
static int min(int x, int y) { static int min(int x, int y) {
return x < y ? x : 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); return dspmem_chunk_write(ch, 24 - ch->cachebits, 0);
} }
Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal) Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> hwCalDefault,
: mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)), mAsyncHandle(std::async([] {})) { 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; int32_t longFrequencyShift;
std::string caldata{8, '0'}; std::string caldata{8, '0'};
uint32_t calVer; 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 *inputEventName = std::getenv("INPUT_EVENT_NAME");
const char *inputEventPathName = std::getenv("INPUT_EVENT_PATH"); const char *inputEventPathName = std::getenv("INPUT_EVENT_PATH");
if ((strstr(inputEventName, "cs40l26") != nullptr) || 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); 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); mFfEffects.resize(WAVEFORM_MAX_INDEX);
mEffectDurations.resize(WAVEFORM_MAX_INDEX); mEffectDurations.resize(WAVEFORM_MAX_INDEX);
mEffectDurations = { 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 */ }; /* 11+3 waveforms. The duration must < UINT16_MAX */
uint8_t effectIndex; 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 // Bypass the waveform update due to different input name
if ((strstr(inputEventName, "cs40l26") != nullptr) || if ((strstr(inputEventName, "cs40l26") != nullptr) ||
(strstr(inputEventName, "cs40l26_dual_input") != nullptr)) { (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
if (!mHwApi->setFFEffect( if (!mHwApiDef->setFFEffect(
mInputFd, &mFfEffects[effectIndex], mInputFd, &mFfEffects[effectIndex],
static_cast<uint16_t>(mFfEffects[effectIndex].replay.length))) { static_cast<uint16_t>(mFfEffects[effectIndex].replay.length))) {
ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno)); 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)) { // ====================HAL internal effect table== Flip ==================================
mHwApi->setF0(caldata); 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)) { // ==============Calibration data checking======================================
mHwApi->setRedc(caldata);
if (mHwCalDef->getF0(&caldata)) {
mHwApiDef->setF0(caldata);
} }
if (mHwCal->getQ(&caldata)) { if (mHwCalDef->getRedc(&caldata)) {
mHwApi->setQ(caldata); mHwApiDef->setRedc(caldata);
}
if (mHwCalDef->getQ(&caldata)) {
mHwApiDef->setQ(caldata);
} }
if (mHwCal->getF0SyncOffset(&mF0Offset)) { if (mHwCalDef->getF0SyncOffset(&mF0Offset)) {
ALOGI("Vibrator::Vibrator: F0 offset calculated from both base and flip calibration data: %u", mF0Offset); ALOGD("Vibrator::Vibrator: F0 offset calculated from both base and flip calibration data: "
"%u",
mF0Offset);
} else { } else {
mHwCal->getLongFrequencyShift(&longFrequencyShift); mHwCalDef->getLongFrequencyShift(&longFrequencyShift);
if (longFrequencyShift > 0) { if (longFrequencyShift > 0) {
mF0Offset = longFrequencyShift * std::pow(2, 14); mF0Offset = longFrequencyShift * std::pow(2, 14);
} else if (longFrequencyShift < 0) { } else if (longFrequencyShift < 0) {
mF0Offset = std::pow(2, 24) - std::abs(longFrequencyShift) * std::pow(2, 14); mF0Offset = std::pow(2, 24) - std::abs(longFrequencyShift) * std::pow(2, 14);
} else { } else {
mF0Offset = 0; mF0Offset = 0;
} }
ALOGI("Vibrator::Vibrator: F0 offset calculated from long shift frequency: %u", mF0Offset); 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) { if (calVer == 2) {
mHwCal->getTickVolLevels(&mTickEffectVol); mHwCalDef->getTickVolLevels(&(mTickEffectVol));
mHwCal->getClickVolLevels(&mClickEffectVol); mHwCalDef->getClickVolLevels(&(mClickEffectVol));
mHwCal->getLongVolLevels(&mLongEffectVol); mHwCalDef->getLongVolLevels(&(mLongEffectVol));
} else { } else {
ALOGW("Unsupported calibration version! Using the default calibration value"); ALOGW("Unsupported calibration version! Using the default calibration value");
mHwCal->getTickVolLevels(&mTickEffectVol); mHwCalDef->getTickVolLevels(&(mTickEffectVol));
mHwCal->getClickVolLevels(&mClickEffectVol); mHwCalDef->getClickVolLevels(&(mClickEffectVol));
mHwCal->getLongVolLevels(&mLongEffectVol); mHwCalDef->getLongVolLevels(&(mLongEffectVol));
} }
mHwApi->setF0CompEnable(mHwCal->isF0CompEnabled()); // ================Project specific setting to driver===============================
mHwApi->setRedcCompEnable(mHwCal->isRedcCompEnabled());
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; mIsUnderExternalControl = false;
mIsChirpEnabled = mHwCal->isChirpEnabled(); // =============== Compose PWLE check =====================================
mIsChirpEnabled = mHwCalDef->isChirpEnabled();
mHwCal->getSupportedPrimitives(&mSupportedPrimitivesBits); mHwCalDef->getSupportedPrimitives(&mSupportedPrimitivesBits);
if (mSupportedPrimitivesBits > 0) { if (mSupportedPrimitivesBits > 0) {
for (auto e : defaultSupportedPrimitives) { for (auto e : defaultSupportedPrimitives) {
if (mSupportedPrimitivesBits & (1 << uint32_t(e))) { if (mSupportedPrimitivesBits & (1 << uint32_t(e))) {
@ -395,7 +529,12 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
} }
mSupportedPrimitives = defaultSupportedPrimitives; 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) { ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
@ -409,7 +548,7 @@ ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
} else { } else {
ALOGE("No haptics ALSA device"); ALOGE("No haptics ALSA device");
} }
if (mHwApi->hasOwtFreeSpace()) { if (mHwApiDef->hasOwtFreeSpace()) {
ret |= IVibrator::CAP_COMPOSE_EFFECTS; ret |= IVibrator::CAP_COMPOSE_EFFECTS;
if (mIsChirpEnabled) { if (mIsChirpEnabled) {
ret |= IVibrator::CAP_FREQUENCY_CONTROL | IVibrator::CAP_COMPOSE_PWLE_EFFECTS; 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); const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
if (mActiveId >= 0) { if (mActiveId >= 0) {
ALOGV("Off: Stop the active effect: %d", mActiveId); ALOGD("Off: Stop the active effect: %d", mActiveId);
/* Stop the active effect. */ /* Stop the active effect. */
if (!mHwApi->setFFPlay(mInputFd, mActiveId, false)) { if (!mHwApiDef->setFFPlay(mInputFd, mActiveId, false)) {
ALOGE("Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno)); ALOGE("Off: Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno));
ret = false; ret = false;
} }
if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) && if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) { (!mHwApiDef->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
ALOGE("Failed to clean up the composed effect %d", mActiveId); ALOGE("Off: Failed to clean up the composed effect %d", mActiveId);
ret = false; 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 { } else {
ALOGV("Off: Vibrator is already off"); ALOGD("Off: Vibrator is already off");
} }
mActiveId = -1; mActiveId = -1;
setGlobalAmplitude(false); setGlobalAmplitude(false);
if (mF0Offset) { if (mF0Offset) {
mHwApi->setF0Offset(0); mHwApiDef->setF0Offset(0);
if (mIsDual && mF0OffsetDual) {
mHwApiDual->setF0Offset(0);
}
} }
if (ret) { if (ret) {
ALOGD("Off: Done.");
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
} else { } else {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
@ -458,13 +615,8 @@ ndk::ScopedAStatus Vibrator::off() {
ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs, ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
const std::shared_ptr<IVibratorCallback> &callback) { const std::shared_ptr<IVibratorCallback> &callback) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::on"); ATRACE_NAME("Vibrator::on");
ALOGD("Vibrator::on");
if (isBusy()) {
ALOGD("Vibrator::on, isBusy");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (timeoutMs > MAX_TIME_MS) { if (timeoutMs > MAX_TIME_MS) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@ -477,7 +629,10 @@ ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
} }
setGlobalAmplitude(true); setGlobalAmplitude(true);
if (mF0Offset) { if (mF0Offset) {
mHwApi->setF0Offset(mF0Offset); mHwApiDef->setF0Offset(mF0Offset);
if (mIsDual && mF0OffsetDual) {
mHwApiDual->setF0Offset(mF0OffsetDual);
}
} }
return on(timeoutMs, index, nullptr /*ignored*/, callback); 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, ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
const std::shared_ptr<IVibratorCallback> &callback, const std::shared_ptr<IVibratorCallback> &callback,
int32_t *_aidl_return) { int32_t *_aidl_return) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::perform"); ATRACE_NAME("Vibrator::perform");
ALOGD("Vibrator::perform");
return performEffect(effect, strength, callback, _aidl_return); 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) { ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::setAmplitude"); ATRACE_NAME("Vibrator::setAmplitude");
if (amplitude <= 0.0f || amplitude > 1.0f) { if (amplitude <= 0.0f || amplitude > 1.0f) {
@ -513,17 +667,12 @@ ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
} }
ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) { ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::setExternalControl"); ATRACE_NAME("Vibrator::setExternalControl");
if (isSynced()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
setGlobalAmplitude(enabled); setGlobalAmplitude(enabled);
if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) { 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); ALOGE("Failed to %s haptic pcm device: %d", (enabled ? "enable" : "disable"), mDevice);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
@ -562,8 +711,8 @@ ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
if (!status.isOk()) { if (!status.isOk()) {
return status; return status;
} }
// Please check the overhead time detail in b/261841035
*durationMs = mEffectDurations[effectIndex]; *durationMs = mEffectDurations[effectIndex] + SETTING_TIME_OVERHEAD;
} else { } else {
*durationMs = 0; *durationMs = 0;
} }
@ -572,8 +721,8 @@ ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composite, ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composite,
const std::shared_ptr<IVibratorCallback> &callback) { const std::shared_ptr<IVibratorCallback> &callback) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::compose"); ATRACE_NAME("Vibrator::compose");
ALOGD("Vibrator::compose");
uint16_t size; uint16_t size;
uint16_t nextEffectDelay; uint16_t nextEffectDelay;
@ -677,68 +826,134 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
effectIndex = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE; effectIndex = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE;
uint32_t freeBytes; uint32_t freeBytes;
mHwApi->getOwtFreeSpace(&freeBytes); mHwApiDef->getOwtFreeSpace(&freeBytes);
if (dspmem_chunk_bytes(ch) > freeBytes) { if (dspmem_chunk_bytes(ch) > freeBytes) {
ALOGE("Invalid OWT length: Effect %d: %d > %d!", effectIndex, dspmem_chunk_bytes(ch), ALOGE("Invalid OWT length: Effect %d: %d > %d!", effectIndex, dspmem_chunk_bytes(ch),
freeBytes); freeBytes);
delete ch; delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
int errorStatus; if (mIsDual) {
if (isSynced()) { mHwApiDual->getOwtFreeSpace(&freeBytes);
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex; 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; delete ch;
ALOGE("Invalid uploadOwtEffect"); ALOGE("Invalid uploadOwtEffect");
return ndk::ScopedAStatus::fromExceptionCode(errorStatus); 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; delete ch;
} else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX || } else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX ||
effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) { effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) {
/* Update duration for long/short vibration. */ /* Update duration for long/short vibration. */
mFfEffects[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs); mFfEffects[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
if (isSynced()) { if (mGPIOStatus && mIsDual) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex; 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], if (!mHwApiDef->setFFEffect(mInputFd, &mFfEffects[effectIndex],
static_cast<uint16_t>(timeoutMs))) { static_cast<uint16_t>(timeoutMs))) {
ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno)); ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
} if (mIsDual) {
{ mFfEffectsDual[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
const std::scoped_lock<std::mutex> lock(mActiveId_mutex); if (!mHwApiDual->setFFEffect(mInputFdDual, &mFfEffectsDual[effectIndex],
mActiveId = effectIndex; static_cast<uint16_t>(timeoutMs))) {
if (isSynced() && ALOGE("Failed to edit flip's effect %d (%d): %s", effectIndex, errno,
(effectIndex == WAVEFORM_CLICK_INDEX || effectIndex == WAVEFORM_LIGHT_TICK_INDEX)) { strerror(errno));
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));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); 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)); 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); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
} }
} }
mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback); mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback);
ALOGD("Vibrator::on, set done.");
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
} }
ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum) { ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum) {
uint16_t scale = amplitudeToScale(amplitude, 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)); ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); 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(); return ndk::ScopedAStatus::ok();
} }
@ -747,6 +962,7 @@ ndk::ScopedAStatus Vibrator::setGlobalAmplitude(bool set) {
if (!set) { if (!set) {
mLongEffectScale = 1.0; // Reset the scale for the later new effect. mLongEffectScale = 1.0; // Reset the scale for the later new effect.
} }
return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX); return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX);
} }
@ -764,7 +980,7 @@ ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t /*id*/) {
ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) { ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
std::string caldata{8, '0'}; std::string caldata{8, '0'};
if (!mHwCal->getF0(&caldata)) { if (!mHwCalDef->getF0(&caldata)) {
ALOGE("Failed to get resonant frequency (%d): %s", errno, strerror(errno)); ALOGE("Failed to get resonant frequency (%d): %s", errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
@ -775,7 +991,7 @@ ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) { ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
std::string caldata{8, '0'}; std::string caldata{8, '0'};
if (!mHwCal->getQ(&caldata)) { if (!mHwCalDef->getQ(&caldata)) {
ALOGE("Failed to get q factor (%d): %s", errno, strerror(errno)); ALOGE("Failed to get q factor (%d): %s", errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
@ -1066,37 +1282,6 @@ bool Vibrator::isUnderExternalControl() {
return mIsUnderExternalControl; 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 // BnCInterface APIs
binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) { 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, "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, " 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]); 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]); 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]); mLongEffectVol[1]);
dprintf(fd, " FF effect:\n"); dprintf(fd, " FF effect:\n");
dprintf(fd, " Physical waveform:\n"); dprintf(fd, " Physical waveform:\n");
dprintf(fd, "\tId\tIndex\tt ->\tt'\ttrigger button\n"); dprintf(fd, "==== Base ====\n\tId\tIndex\tt ->\tt'\ttrigger button\n");
for (uint8_t effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) { 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, 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].u.periodic.custom_data[1], mEffectDurations[effectId],
mFfEffects[effectId].replay.length, mFfEffects[effectId].trigger.button); 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"); dprintf(fd, "\tId\tBytes\tData\ttrigger button\n");
for (uint8_t effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX; for (effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX; effectId++) {
effectId++) {
uint32_t numBytes = mFfEffects[effectId].u.periodic.custom_len * 2; uint32_t numBytes = mFfEffects[effectId].u.periodic.custom_len * 2;
std::stringstream ss; std::stringstream ss;
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(), dprintf(fd, "\t%d\t%d\t{%s}\t%X\n", mFfEffects[effectId].id, numBytes, ss.str().c_str(),
mFfEffects[effectId].trigger.button); 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");
dprintf(fd, "\n"); dprintf(fd, "\n");
mHwApi->debug(fd); mHwApiDef->debug(fd);
dprintf(fd, "\n"); dprintf(fd, "\n");
mHwCal->debug(fd); mHwCalDef->debug(fd);
if (mIsDual) {
mHwApiDual->debug(fd);
dprintf(fd, "\n");
mHwCalDual->debug(fd);
}
fsync(fd); fsync(fd);
return STATUS_OK; return STATUS_OK;
@ -1165,7 +1382,7 @@ bool Vibrator::hasHapticAlsaDevice() {
// constructor is too early in the boot process and the pcm file contents // 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. // are empty. Hence we make the call here once only right before we need to.
if (!mConfigHapticAlsaDeviceDone) { if (!mConfigHapticAlsaDeviceDone) {
if (mHwApi->getHapticAlsaDevice(&mCard, &mDevice)) { if (mHwApiDef->getHapticAlsaDevice(&mCard, &mDevice)) {
mHasHapticAlsaDevice = true; mHasHapticAlsaDevice = true;
mConfigHapticAlsaDeviceDone = true; mConfigHapticAlsaDeviceDone = true;
} else { } else {
@ -1373,26 +1590,52 @@ ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLev
} }
void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) { 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)); (callback != nullptr));
if (!mHwApi->pollVibeState(VIBE_STATE_HAPTIC, // Bypass checking flip part's haptic state
(mSyncedCallback) ? POLLING_TIMEOUT_IN_SYNC : POLLING_TIMEOUT)) { if (!mHwApiDef->pollVibeState(VIBE_STATE_HAPTIC, POLLING_TIMEOUT)) {
ALOGV("Failed to get state \"Haptic\""); 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); const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) && uint32_t effectCount;
(!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) { mHwApiDef->getEffectCount(&effectCount);
ALOGE("Failed to clean up the composed effect %d", mActiveId);
}
if (mActiveId >= 0) { 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; 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 { } 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()); ALOGE("Failed completion callback: %d", ret.getExceptionCode());
} }
} }
if (mSyncedCallback) { ALOGD("waitForComplete: Done.");
auto ret = mSyncedCallback->onComplete();
if (!ret.isOk()) {
ALOGE("Failed completion mSyncedCallback: %d", ret.exceptionCode());
}
mSyncedCallback = nullptr;
}
ALOGV("waitForComplete: Done");
} }
uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) { uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
@ -1442,16 +1678,6 @@ uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
return volLevel; 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 vibrator
} // namespace hardware } // namespace hardware
} // namespace android } // namespace android

View file

@ -17,7 +17,6 @@
#include <aidl/android/hardware/vibrator/BnVibrator.h> #include <aidl/android/hardware/vibrator/BnVibrator.h>
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
#include <android/hardware/vibrator/BnVibratorSyncCallback.h>
#include <linux/input.h> #include <linux/input.h>
#include <tinyalsa/asoundlib.h> #include <tinyalsa/asoundlib.h>
@ -30,12 +29,22 @@ namespace android {
namespace hardware { namespace hardware {
namespace vibrator { namespace vibrator {
using ::android::sp;
using ::android::binder::Status;
using ::android::hardware::vibrator::IVibratorSyncCallback;
class Vibrator : public BnVibrator { class Vibrator : public BnVibrator {
public: 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. // APIs for interfacing with the kernel driver.
class HwApi { class HwApi {
public: public:
@ -85,8 +94,6 @@ class Vibrator : public BnVibrator {
int *status) = 0; int *status) = 0;
// Erase OWT waveform // Erase OWT waveform
virtual bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) = 0; 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. // Emit diagnostic information to the given file.
virtual void debug(int fd) = 0; virtual void debug(int fd) = 0;
}; };
@ -129,7 +136,9 @@ class Vibrator : public BnVibrator {
}; };
public: 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 // BnVibrator APIs
ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return) override; ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return) override;
@ -163,10 +172,6 @@ class Vibrator : public BnVibrator {
ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite, ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
const std::shared_ptr<IVibratorCallback> &callback) override; const std::shared_ptr<IVibratorCallback> &callback) override;
// BnVibratorSync APIs
Status prepareSynced(const sp<IVibratorSyncCallback> &callback);
Status cancelSynced();
// BnCInterface APIs // BnCInterface APIs
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override; binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
@ -198,33 +203,36 @@ class Vibrator : public BnVibrator {
bool hasHapticAlsaDevice(); bool hasHapticAlsaDevice();
bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device); bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device);
bool isBusy(); std::unique_ptr<HwApi> mHwApiDef;
bool isSynced(); std::unique_ptr<HwCal> mHwCalDef;
std::unique_ptr<HwApi> mHwApiDual;
std::unique_ptr<HwApi> mHwApi; std::unique_ptr<HwCal> mHwCalDual;
std::unique_ptr<HwCal> mHwCal; std::unique_ptr<HwGPIO> mHwGPIO;
uint32_t mF0Offset; uint32_t mF0Offset;
uint32_t mF0OffsetDual;
std::array<uint32_t, 2> mTickEffectVol; std::array<uint32_t, 2> mTickEffectVol;
std::array<uint32_t, 2> mClickEffectVol; std::array<uint32_t, 2> mClickEffectVol;
std::array<uint32_t, 2> mLongEffectVol; std::array<uint32_t, 2> mLongEffectVol;
std::vector<ff_effect> mFfEffects; std::vector<ff_effect> mFfEffects;
std::vector<ff_effect> mFfEffectsDual;
std::vector<uint32_t> mEffectDurations; std::vector<uint32_t> mEffectDurations;
std::future<void> mAsyncHandle; std::future<void> mAsyncHandle;
::android::base::unique_fd mInputFd; ::android::base::unique_fd mInputFd;
::android::base::unique_fd mInputFdDual;
int8_t mActiveId{-1}; int8_t mActiveId{-1};
struct pcm *mHapticPcm; struct pcm *mHapticPcm;
int mCard; int mCard;
int mDevice; int mDevice;
bool mHasHapticAlsaDevice{false}; bool mHasHapticAlsaDevice{false};
bool mIsUnderExternalControl; bool mIsUnderExternalControl;
float mLongEffectScale = 1.0; float mLongEffectScale{1.0};
bool mIsChirpEnabled; bool mIsChirpEnabled;
uint32_t mSupportedPrimitivesBits = 0x0; uint32_t mSupportedPrimitivesBits = 0x0;
std::vector<CompositePrimitive> mSupportedPrimitives; std::vector<CompositePrimitive> mSupportedPrimitives;
bool mConfigHapticAlsaDeviceDone{false}; bool mConfigHapticAlsaDeviceDone{false};
// prevent concurrent execution of IVibrator and IVibratorSync APIs bool mGPIOStatus;
sp<IVibratorSyncCallback> mSyncedCallback; bool mIsDual{false};
std::recursive_mutex mApiMutex; std::mutex mActiveId_mutex; // protects mActiveId
}; };
} // namespace vibrator } // 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-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/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 /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
enable vendor.vibrator.cs40l26-dual
service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-private service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-private
class hal class hal
user system user system
group system input group system input
setenv HAPTIC_NAME HapticsBase setenv HAPTIC_NAME Haptics
setenv INPUT_EVENT_NAME cs40l26_input setenv INPUT_EVENT_NAME cs40l26_input
setenv INPUT_EVENT_NAME_DUAL cs40l26_dual_input
setenv INPUT_EVENT_PATH /dev/input/event* setenv INPUT_EVENT_PATH /dev/input/event*
setenv PROPERTY_PREFIX ro.vendor.vibrator.hal. setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal 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 /sys/bus/i2c/devices/i2c-cs40l26a/
setenv HWAPI_PATH_PREFIX_DUAL /sys/bus/i2c/devices/i2c-cs40l26a-dual/
setenv HWAPI_DEBUG_PATHS " setenv HWAPI_DEBUG_PATHS "
calibration/f0_stored calibration/f0_stored
calibration/redc_stored calibration/redc_stored
@ -61,30 +63,3 @@ service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service
disabled 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> <version>2</version>
<fqname>IVibrator/default</fqname> <fqname>IVibrator/default</fqname>
</hal> </hal>
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>2</version>
<fqname>IVibrator/dual</fqname>
</hal>
</manifest> </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 android.hardware.vibrator-service.cs40l26-private
BOARD_SEPOLICY_DIRS += \ BOARD_SEPOLICY_DIRS += \
device/google/felix-sepolicy/vibrator/common \ hardware/google/pixel-sepolicy/vibrator/common \
device/google/felix-sepolicy/vibrator/cs40l26 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 <log/log.h>
#include "Hardware.h" #include "Hardware.h"
#include "VibMgrHwApi.h"
#include "Vibrator.h" #include "Vibrator.h"
#include "VibratorSync.h"
using ::aidl::android::hardware::vibrator::HwApi; using ::aidl::android::hardware::vibrator::HwApi;
using ::aidl::android::hardware::vibrator::HwCal; using ::aidl::android::hardware::vibrator::HwCal;
using ::aidl::android::hardware::vibrator::VibMgrHwApi;
using ::aidl::android::hardware::vibrator::Vibrator; using ::aidl::android::hardware::vibrator::Vibrator;
using ::android::defaultServiceManager; using ::android::defaultServiceManager;
using ::android::ProcessState; using ::android::ProcessState;
using ::android::sp; using ::android::sp;
using ::android::String16; using ::android::String16;
using ::android::hardware::vibrator::VibratorSync;
#if !defined(VIBRATOR_NAME)
#define VIBRATOR_NAME "default"
#endif
int main() { int main() {
const char *inputEventName = std::getenv("INPUT_EVENT_NAME"); const char *hwApiPathPrefixDual = std::getenv("HWAPI_PATH_PREFIX_DUAL");
std::string vibName = ""; const char *calFilePath = std::getenv("CALIBRATION_FILEPATH");
if (strstr(inputEventName, "cs40l26_input") != nullptr) { const char *calFilePathDual = std::getenv("CALIBRATION_FILEPATH_DUAL");
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;
auto ext = sp<VibratorSync>::make(svc); auto hwgpio = VibMgrHwApi::Create();
const auto extName = std::stringstream() << ext->descriptor << "/" << vibName; 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"); ProcessState::initWithDriver("/dev/vndbinder");
defaultServiceManager()->addService(String16(extName.str().c_str()), ext);
auto svcBinder = svc->asBinder(); auto svcBinder = svc->asBinder();
binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str()); binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str());
LOG_ALWAYS_FATAL_IF(status != STATUS_OK); LOG_ALWAYS_FATAL_IF(status != STATUS_OK);

View file

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

View file

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

View file

@ -203,20 +203,24 @@ class VibratorTest : public Test {
setenv("INPUT_EVENT_NAME", "CS40L26TestSuite", true); setenv("INPUT_EVENT_NAME", "CS40L26TestSuite", true);
std::unique_ptr<MockApi> mockapi; std::unique_ptr<MockApi> mockapi;
std::unique_ptr<MockCal> mockcal; std::unique_ptr<MockCal> mockcal;
std::unique_ptr<MockGPIO> mockgpio;
createMock(&mockapi, &mockcal); createMock(&mockapi, &mockcal, &mockgpio);
createVibrator(std::move(mockapi), std::move(mockcal)); createVibrator(std::move(mockapi), std::move(mockcal), std::move(mockgpio));
} }
void TearDown() override { deleteVibrator(); } void TearDown() override { deleteVibrator(); }
protected: 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>(); *mockapi = std::make_unique<MockApi>();
*mockcal = std::make_unique<MockCal>(); *mockcal = std::make_unique<MockCal>();
*mockgpio = std::make_unique<MockGPIO>();
mMockApi = mockapi->get(); mMockApi = mockapi->get();
mMockCal = mockcal->get(); mMockCal = mockcal->get();
mMockGpio = mockgpio->get();
ON_CALL(*mMockApi, destructor()).WillByDefault(Assign(&mMockApi, nullptr)); ON_CALL(*mMockApi, destructor()).WillByDefault(Assign(&mMockApi, nullptr));
@ -242,15 +246,20 @@ class VibratorTest : public Test {
ON_CALL(*mMockCal, getLongVolLevels(_)) ON_CALL(*mMockCal, getLongVolLevels(_))
.WillByDefault(DoAll(SetArgPointee<0>(V_LONG_DEFAULT), Return(true))); .WillByDefault(DoAll(SetArgPointee<0>(V_LONG_DEFAULT), Return(true)));
ON_CALL(*mMockGpio, destructor()).WillByDefault(Assign(&mMockGpio, nullptr));
relaxMock(false); relaxMock(false);
} }
void createVibrator(std::unique_ptr<MockApi> mockapi, std::unique_ptr<MockCal> mockcal, 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) { if (relaxed) {
relaxMock(true); 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) { if (relaxed) {
relaxMock(false); relaxMock(false);
} }
@ -306,6 +315,7 @@ class VibratorTest : public Test {
protected: protected:
MockApi *mMockApi; MockApi *mMockApi;
MockCal *mMockCal; MockCal *mMockCal;
MockGPIO *mMockGpio;
std::shared_ptr<IVibrator> mVibrator; std::shared_ptr<IVibrator> mVibrator;
uint32_t mEffectIndex; uint32_t mEffectIndex;
}; };
@ -313,6 +323,7 @@ class VibratorTest : public Test {
TEST_F(VibratorTest, Constructor) { TEST_F(VibratorTest, Constructor) {
std::unique_ptr<MockApi> mockapi; std::unique_ptr<MockApi> mockapi;
std::unique_ptr<MockCal> mockcal; std::unique_ptr<MockCal> mockcal;
std::unique_ptr<MockGPIO> mockgpio;
std::string f0Val = std::to_string(std::rand()); std::string f0Val = std::to_string(std::rand());
std::string redcVal = std::to_string(std::rand()); std::string redcVal = std::to_string(std::rand());
std::string qVal = 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(*mMockApi, destructor()).WillOnce(DoDefault());
EXPECT_CALL(*mMockCal, destructor()).WillOnce(DoDefault()); EXPECT_CALL(*mMockCal, destructor()).WillOnce(DoDefault());
EXPECT_CALL(*mMockGpio, destructor()).WillOnce(DoDefault());
deleteVibrator(false); deleteVibrator(false);
createMock(&mockapi, &mockcal); createMock(&mockapi, &mockcal, &mockgpio);
EXPECT_CALL(*mMockCal, getF0(_)) EXPECT_CALL(*mMockCal, getF0(_))
.InSequence(f0Seq) .InSequence(f0Seq)
@ -363,7 +375,7 @@ TEST_F(VibratorTest, Constructor) {
.WillOnce(DoAll(SetArgPointee<0>(supportedPrimitivesBits), Return(true))); .WillOnce(DoAll(SetArgPointee<0>(supportedPrimitivesBits), Return(true)));
EXPECT_CALL(*mMockApi, setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US)).WillOnce(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) { TEST_F(VibratorTest, on) {