Merge "[DO NOT MERGE] cs40l26: single HAL for dual haptics" into tm-qpr-dev
This commit is contained in:
commit
62fde00943
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