[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:
parent
aa946e4dcb
commit
07468b074a
25 changed files with 542 additions and 884 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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(); };
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue