Replace vibrator/ from git_tm-qpr-dev

Lastest commit:
2724d61 vibrator/cs40l26: Fix unsupport primitive effects test

Bug: 267926374
Bug: 264625320
Test: HAL init properly.
Change-Id: I1c01ea8c6d584ae58d8d20f21a9ac43a8977f435
This commit is contained in:
Tai Kuo 2023-02-06 11:17:10 +08:00
parent 83184424e0
commit cfabdfaacc
26 changed files with 628 additions and 893 deletions

View file

@ -30,7 +30,7 @@ DEVICE_PACKAGE_OVERLAYS += device/google/felix/felix/overlay
include device/google/felix/audio/felix/audio-tables.mk include device/google/felix/audio/felix/audio-tables.mk
include device/google/gs201/device-shipping-common.mk include device/google/gs201/device-shipping-common.mk
$(call soong_config_set,fp_hal_feature,pixel_product, product_a) $(call soong_config_set,fp_hal_feature,pixel_product, product_a)
include hardware/google/pixel/vibrator/cs40l26/device-stereo.mk include device/google/felix/vibrator/cs40l26/device.mk
include device/google/gs-common/bcmbt/bluetooth.mk include device/google/gs-common/bcmbt/bluetooth.mk
include device/google/gs-common/touch/gti/gti.mk include device/google/gs-common/touch/gti/gti.mk
include device/google/gs-common/touch/stm/stm6.mk include device/google/gs-common/touch/stm/stm6.mk
@ -207,7 +207,9 @@ PRODUCT_VENDOR_PROPERTIES += \
# Vibrator HAL # Vibrator HAL
PRODUCT_PRODUCT_PROPERTIES +=\ PRODUCT_PRODUCT_PROPERTIES +=\
ro.vendor.vibrator.hal.long.frequency.shift=0 ro.vendor.vibrator.hal.long.frequency.shift=0 \
ro.vendor.vibrator.hal.gpio.num=44 \
ro.vendor.vibrator.hal.gpio.shift=2
ACTUATOR_MODEL := luxshare_ict_lt_xlra1906d ACTUATOR_MODEL := luxshare_ict_lt_xlra1906d
# Fingerprint # Fingerprint

View file

@ -71,6 +71,7 @@ void HwApiBase::debug(int fd) {
HwCalBase::HwCalBase() { HwCalBase::HwCalBase() {
std::ifstream calfile; std::ifstream calfile;
std::ifstream calfile_dual;
auto propertyPrefix = std::getenv("PROPERTY_PREFIX"); auto propertyPrefix = std::getenv("PROPERTY_PREFIX");
if (propertyPrefix != NULL) { if (propertyPrefix != NULL) {
@ -91,6 +92,20 @@ HwCalBase::HwCalBase() {
mCalData[utils::trim(key)] = utils::trim(value); mCalData[utils::trim(key)] = utils::trim(value);
} }
} }
utils::fileFromEnv("CALIBRATION_FILEPATH_DUAL", &calfile_dual);
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) + "_dual";
mCalData[key] = utils::trim(value);
}
}
} }
void HwCalBase::debug(int fd) { void HwCalBase::debug(int fd) {

View file

@ -34,7 +34,6 @@ cc_defaults {
"android.hardware.vibrator-defaults.cs40l26-private", "android.hardware.vibrator-defaults.cs40l26-private",
], ],
shared_libs: [ shared_libs: [
"android.hardware.vibrator.cs40l26-private-cpp",
"libcutils", "libcutils",
"libtinyalsa", "libtinyalsa",
], ],
@ -53,9 +52,6 @@ cc_defaults {
"android.hardware.vibrator-impl.cs40l26-private", "android.hardware.vibrator-impl.cs40l26-private",
"libtinyalsa", "libtinyalsa",
], ],
shared_libs: [
"android.hardware.vibrator.cs40l26-private-cpp",
],
} }
cc_library { cc_library {
@ -63,8 +59,6 @@ cc_library {
defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"], defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"],
srcs: [ srcs: [
"Vibrator.cpp", "Vibrator.cpp",
"VibratorSync.cpp",
"VibratorManager.cpp",
], ],
export_include_dirs: ["."], export_include_dirs: ["."],
vendor_available: true, vendor_available: true,
@ -80,39 +74,5 @@ cc_binary {
shared_libs: [ shared_libs: [
"android.hardware.vibrator-impl.cs40l26-private", "android.hardware.vibrator-impl.cs40l26-private",
], ],
cflags: [
"-DLOG_TAG=\"android.hardware.vibrator-cs40l26-private\"",
],
proprietary: true,
}
cc_binary {
name: "android.hardware.vibrator-service.cs40l26-dual-private",
defaults: ["VibratorHalCs40l26BinaryDefaultsPrivate"],
init_rc: ["android.hardware.vibrator-service.cs40l26-dual-private.rc"],
vintf_fragments: ["android.hardware.vibrator-service.cs40l26-dual-private.xml"],
srcs: ["service.cpp"],
shared_libs: [
"android.hardware.vibrator-impl.cs40l26-private",
],
cflags: [
"-DVIBRATOR_NAME=\"dual\"",
"-DLOG_TAG=\"android.hardware.vibrator-cs40l26-dual-private\"",
],
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",
],
cflags: [
"-DLOG_TAG=\"android.hardware.vibrator-cs40l26-stereo-private\"",
],
proprietary: true, proprietary: true,
} }

View file

@ -16,6 +16,7 @@
#pragma once #pragma once
#include <algorithm> #include <algorithm>
#include <cmath>
#include "HardwareBase.h" #include "HardwareBase.h"
#include "Vibrator.h" #include "Vibrator.h"
@ -66,6 +67,10 @@ namespace vibrator {
class HwApi : public Vibrator::HwApi, private HwApiBase { class HwApi : public Vibrator::HwApi, private HwApiBase {
public: public:
static std::unique_ptr<HwApi> Create() {
auto hwapi = std::unique_ptr<HwApi>(new HwApi());
return hwapi;
}
HwApi() { HwApi() {
open("calibration/f0_stored", &mF0); open("calibration/f0_stored", &mF0);
open("default/f0_offset", &mF0Offset); open("default/f0_offset", &mF0Offset);
@ -220,7 +225,10 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
ALOGE("Invalid waveform index for OWT erase: %d", effectIndex); ALOGE("Invalid waveform index for OWT erase: %d", effectIndex);
return false; return false;
} }
// Turn off the waiting time for SVC init phase to complete since chip
// should already under STOP state
setMinOnOffInterval(0);
// Do erase flow
if (effectIndex < WAVEFORM_MAX_INDEX) { if (effectIndex < WAVEFORM_MAX_INDEX) {
/* Normal situation. Only erase the effect which we just played. */ /* Normal situation. Only erase the effect which we just played. */
if (ioctl(fd, EVIOCRMFF, effectIndex) < 0) { if (ioctl(fd, EVIOCRMFF, effectIndex) < 0) {
@ -248,19 +256,10 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
(*effect)[i].id = -1; (*effect)[i].id = -1;
} }
} }
// Turn on the waiting time for SVC init phase to complete
setMinOnOffInterval(Vibrator::MIN_ON_OFF_INTERVAL_US);
return true; return true;
} }
void clearTrigBtn(int fd, struct ff_effect *effect, int8_t id) override {
if ((*effect).trigger.button != 0x00) {
(*effect).trigger.button = 0x00;
if (id < WAVEFORM_MAX_PHYSICAL_INDEX) {
/* Clear the trigger pin setting */
if ((ioctl(fd, EVIOCSFF, effect) < 0)) {
ALOGE("OFF: Failed to edit effect %d (%d): %s", id, errno, strerror(errno));
}
}
}
}
void debug(int fd) override { HwApiBase::debug(fd); } void debug(int fd) override { HwApiBase::debug(fd); }
@ -281,6 +280,7 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
private: private:
static constexpr char VERSION[] = "version"; static constexpr char VERSION[] = "version";
static constexpr char F0_CONFIG[] = "f0_measured"; static constexpr char F0_CONFIG[] = "f0_measured";
static constexpr char F0_CONFIG_DUAL[] = "f0_measured_dual";
static constexpr char REDC_CONFIG[] = "redc_measured"; static constexpr char REDC_CONFIG[] = "redc_measured";
static constexpr char Q_CONFIG[] = "q_measured"; static constexpr char Q_CONFIG[] = "q_measured";
static constexpr char TICK_VOLTAGES_CONFIG[] = "v_tick"; static constexpr char TICK_VOLTAGES_CONFIG[] = "v_tick";
@ -295,6 +295,10 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
public: public:
HwCal() {} HwCal() {}
static std::unique_ptr<HwCal> Create() {
auto hwcal = std::unique_ptr<HwCal>(new HwCal());
return hwcal;
}
bool getVersion(uint32_t *value) override { bool getVersion(uint32_t *value) override {
if (getPersist(VERSION, value)) { if (getPersist(VERSION, value)) {
@ -307,6 +311,30 @@ class HwCal : public Vibrator::HwCal, private HwCalBase {
return getProperty("long.frequency.shift", value, DEFAULT_FREQUENCY_SHIFT); return getProperty("long.frequency.shift", value, DEFAULT_FREQUENCY_SHIFT);
} }
bool getF0(std::string *value) override { return getPersist(F0_CONFIG, value); } bool getF0(std::string *value) override { return getPersist(F0_CONFIG, value); }
bool getF0SyncOffset(uint32_t *value) override {
std::string cal_0{8, '0'};
std::string cal_1{8, '0'};
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;
if (f0_0 < f0_1) {
*value = static_cast<uint32_t>(f0_offset * std::pow(2, 14));
} else if (f0_0 > f0_1) {
*value = static_cast<uint32_t>(std::pow(2, 24) - std::abs(f0_offset) * std::pow(2, 14));
} else {
*value = 0;
}
return true;
} else {
ALOGE("Vibrator: Unable to load F0_CONFIG or F0_CONFIG_DUAL config");
*value = 0;
return false;
}
}
bool getRedc(std::string *value) override { return getPersist(REDC_CONFIG, value); } bool getRedc(std::string *value) override { return getPersist(REDC_CONFIG, value); }
bool getQ(std::string *value) override { return getPersist(Q_CONFIG, value); } bool getQ(std::string *value) override { return getPersist(Q_CONFIG, value); }
bool getTickVolLevels(std::array<uint32_t, 2> *value) override { bool getTickVolLevels(std::array<uint32_t, 2> *value) override {

View file

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

View file

@ -33,6 +33,11 @@
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0])) #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
#endif #endif
#ifdef LOG_TAG
#undef LOG_TAG
#define LOG_TAG std::getenv("HAPTIC_NAME")
#endif
namespace aidl { namespace aidl {
namespace android { namespace android {
namespace hardware { namespace hardware {
@ -48,13 +53,16 @@ static constexpr uint32_t WAVEFORM_LONG_VIBRATION_THRESHOLD_MS = 50;
static constexpr uint8_t VOLTAGE_SCALE_MAX = 100; static constexpr uint8_t VOLTAGE_SCALE_MAX = 100;
static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6; // I2C Transaction + DSP Return-From-Standby static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6; // I2C Transaction + DSP Return-From-Standby
static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time static constexpr int8_t MAX_PAUSE_TIMING_ERROR_MS = 1; // ALERT Irq Handling
static constexpr int8_t MAX_PAUSE_TIMING_ERROR_MS = 1; // ALERT Irq Handling
static constexpr uint32_t MAX_TIME_MS = UINT16_MAX; static constexpr uint32_t MAX_TIME_MS = UINT16_MAX;
static constexpr float SETTING_TIME_OVERHEAD = 26; // This time was combined by
// HAL set the effect to
// driver and the kernel
// executes the effect before
// chip play the effect
static constexpr auto ASYNC_COMPLETION_TIMEOUT = std::chrono::milliseconds(100); static constexpr auto ASYNC_COMPLETION_TIMEOUT = std::chrono::milliseconds(100);
static constexpr auto POLLING_TIMEOUT = 20; static constexpr auto POLLING_TIMEOUT = 20;
static constexpr auto POLLING_TIMEOUT_IN_SYNC = 100;
static constexpr int32_t COMPOSE_DELAY_MAX_MS = 10000; static constexpr int32_t COMPOSE_DELAY_MAX_MS = 10000;
/* nsections is 8 bits. Need to preserve 1 section for the first delay before the first effect. */ /* nsections is 8 bits. Need to preserve 1 section for the first delay before the first effect. */
@ -93,12 +101,12 @@ static constexpr float PWLE_BW_MAP_SIZE =
/* /*
* [15] Edge, 0:Falling, 1:Rising * [15] Edge, 0:Falling, 1:Rising
* [14:12] GPI_NUM, 1:GPI1 (with CS40L26A, 1 is the only supported GPI) * [14:12] GPI_NUM, 1:GPI1 (with CS40L26A, 1 is the only supported GPI)
* [8] BANK, 0:ROM, 1:RAM * [8] BANK, 0:RAM, 1:R0M
* [7] USE_BUZZGEN, 0:Not buzzgen, 1:buzzgen * [7] USE_BUZZGEN, 0:Not buzzgen, 1:buzzgen
* [6:0] WAVEFORM_INDEX * [6:0] WAVEFORM_INDEX
* 0x9100 = 1001 0001 0000 0000: Rising + GPI1 + ROM + Not buzzgen * 0x9100 = 1001 0001 0000 0000: Rising + GPI1 + RAM + Not buzzgen
*/ */
static constexpr uint32_t GPIO_TRIGGER_CONFIG = 0x9100; static constexpr uint16_t GPIO_TRIGGER_CONFIG = 0x9100;
static uint16_t amplitudeToScale(float amplitude, float maximum) { static uint16_t amplitudeToScale(float amplitude, float maximum) {
float ratio = 100; /* Unit: % */ float ratio = 100; /* Unit: % */
@ -154,8 +162,6 @@ enum vibe_state {
VIBE_STATE_ASP, VIBE_STATE_ASP,
}; };
std::mutex mActiveId_mutex; // protects mActiveId
static int min(int x, int y) { static int min(int x, int y) {
return x < y ? x : y; return x < y ? x : y;
} }
@ -230,12 +236,24 @@ static int dspmem_chunk_flush(struct dspmem_chunk *ch) {
return dspmem_chunk_write(ch, 24 - ch->cachebits, 0); return dspmem_chunk_write(ch, 24 - ch->cachebits, 0);
} }
Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal) Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> hwCalDefault,
: mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)), mAsyncHandle(std::async([] {})) { std::unique_ptr<HwApi> hwApiDual, std::unique_ptr<HwCal> hwCalDual,
std::unique_ptr<HwGPIO> hwgpio)
: mHwApiDef(std::move(hwApiDefault)),
mHwCalDef(std::move(hwCalDefault)),
mHwApiDual(std::move(hwApiDual)),
mHwCalDual(std::move(hwCalDual)),
mHwGPIO(std::move(hwgpio)),
mAsyncHandle(std::async([] {})) {
int32_t longFrequencyShift; int32_t longFrequencyShift;
std::string caldata{8, '0'}; std::string caldata{8, '0'};
uint32_t calVer; uint32_t calVer;
// ==================Single actuators and dual actuators checking =============================
if ((mHwApiDual != nullptr) && (mHwCalDual != nullptr))
mIsDual = true;
// ==================INPUT Devices== Base =================
const char *inputEventName = std::getenv("INPUT_EVENT_NAME"); const char *inputEventName = std::getenv("INPUT_EVENT_NAME");
const char *inputEventPathName = std::getenv("INPUT_EVENT_PATH"); const char *inputEventPathName = std::getenv("INPUT_EVENT_PATH");
if ((strstr(inputEventName, "cs40l26") != nullptr) || if ((strstr(inputEventName, "cs40l26") != nullptr) ||
@ -248,7 +266,7 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
for (uint8_t retry = 0; retry < 10; retry++) { for (uint8_t retry = 0; retry < 10; retry++) {
ret = glob(inputEventPathName, 0, nullptr, &inputEventPaths); ret = glob(inputEventPathName, 0, nullptr, &inputEventPaths);
if (ret) { if (ret) {
ALOGE("Fail to get input event paths (%d): %s", errno, strerror(errno)); ALOGE("Failed to get input event paths (%d): %s", errno, strerror(errno));
} else { } else {
for (int i = 0; i < inputEventPaths.gl_pathc; i++) { for (int i = 0; i < inputEventPaths.gl_pathc; i++) {
fd = TEMP_FAILURE_RETRY(open(inputEventPaths.gl_pathv[i], O_RDWR)); fd = TEMP_FAILURE_RETRY(open(inputEventPaths.gl_pathv[i], O_RDWR));
@ -278,16 +296,69 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
} }
if (!mInputFd.ok()) { if (!mInputFd.ok()) {
ALOGE("Fail to get an input event with name %s", inputEventName); ALOGE("Failed to get an input event with name %s", inputEventName);
} }
} else { } else {
ALOGE("The input name %s is not cs40l26_input or cs40l26_dual_input", inputEventName); ALOGE("The input name %s is not cs40l26_input or cs40l26_dual_input", inputEventName);
} }
// ==================INPUT Devices== Flip =================
if (mIsDual) {
const char *inputEventNameDual = std::getenv("INPUT_EVENT_NAME_DUAL");
if ((strstr(inputEventNameDual, "cs40l26_dual_input") != nullptr)) {
glob_t inputEventPaths;
int fd = -1;
int ret;
uint32_t val = 0;
char str[20] = {0x00};
for (uint8_t retry = 0; retry < 10; retry++) {
ret = glob(inputEventPathName, 0, nullptr, &inputEventPaths);
if (ret) {
ALOGE("Failed to get flip's input event paths (%d): %s", errno,
strerror(errno));
} else {
for (int i = 0; i < inputEventPaths.gl_pathc; i++) {
fd = TEMP_FAILURE_RETRY(open(inputEventPaths.gl_pathv[i], O_RDWR));
if (fd > 0) {
if (ioctl(fd, EVIOCGBIT(0, sizeof(val)), &val) > 0 &&
(val & (1 << EV_FF)) &&
ioctl(fd, EVIOCGNAME(sizeof(str)), &str) > 0 &&
strstr(str, inputEventNameDual) != nullptr) {
mInputFdDual.reset(fd);
ALOGI("Control %s through %s", inputEventNameDual,
inputEventPaths.gl_pathv[i]);
break;
}
close(fd);
}
}
}
if (ret == 0) {
globfree(&inputEventPaths);
}
if (mInputFdDual.ok()) {
break;
}
sleep(1);
ALOGW("Retry #%d to search in %zu input devices.", retry, inputEventPaths.gl_pathc);
}
if (!mInputFdDual.ok()) {
ALOGE("Failed to get an input event with name %s", inputEventNameDual);
}
ALOGE("HWAPI: %s", std::getenv("HWAPI_PATH_PREFIX"));
} else {
ALOGE("The input name %s is not cs40l26_dual_input", inputEventNameDual);
}
}
// ====================HAL internal effect table== Base ==================================
mFfEffects.resize(WAVEFORM_MAX_INDEX); mFfEffects.resize(WAVEFORM_MAX_INDEX);
mEffectDurations.resize(WAVEFORM_MAX_INDEX); mEffectDurations.resize(WAVEFORM_MAX_INDEX);
mEffectDurations = { mEffectDurations = {
1000, 100, 30, 1000, 300, 130, 150, 500, 100, 15, 20, 1000, 1000, 1000, 1000, 100, 32, 1000, 300, 130, 150, 500, 100, 10, 12, 1000, 1000, 1000,
}; /* 11+3 waveforms. The duration must < UINT16_MAX */ }; /* 11+3 waveforms. The duration must < UINT16_MAX */
uint8_t effectIndex; uint8_t effectIndex;
@ -305,7 +376,7 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
// Bypass the waveform update due to different input name // Bypass the waveform update due to different input name
if ((strstr(inputEventName, "cs40l26") != nullptr) || if ((strstr(inputEventName, "cs40l26") != nullptr) ||
(strstr(inputEventName, "cs40l26_dual_input") != nullptr)) { (strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
if (!mHwApi->setFFEffect( if (!mHwApiDef->setFFEffect(
mInputFd, &mFfEffects[effectIndex], mInputFd, &mFfEffects[effectIndex],
static_cast<uint16_t>(mFfEffects[effectIndex].replay.length))) { static_cast<uint16_t>(mFfEffects[effectIndex].replay.length))) {
ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno)); ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno));
@ -327,45 +398,124 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
} }
} }
if (mHwCal->getF0(&caldata)) { // ====================HAL internal effect table== Flip ==================================
mHwApi->setF0(caldata); if (mIsDual) {
mFfEffectsDual.resize(WAVEFORM_MAX_INDEX);
for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) {
if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
/* Initialize physical waveforms. */
mFfEffectsDual[effectIndex] = {
.type = FF_PERIODIC,
.id = -1,
.replay.length = static_cast<uint16_t>(mEffectDurations[effectIndex]),
.u.periodic.waveform = FF_CUSTOM,
.u.periodic.custom_data = new int16_t[2]{RAM_WVFRM_BANK, effectIndex},
.u.periodic.custom_len = FF_CUSTOM_DATA_LEN,
};
// Bypass the waveform update due to different input name
if ((strstr(inputEventName, "cs40l26") != nullptr) ||
(strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
if (!mHwApiDual->setFFEffect(
mInputFdDual, &mFfEffectsDual[effectIndex],
static_cast<uint16_t>(mFfEffectsDual[effectIndex].replay.length))) {
ALOGE("Failed upload flip's effect %d (%d): %s", effectIndex, errno,
strerror(errno));
}
}
if (mFfEffectsDual[effectIndex].id != effectIndex) {
ALOGW("Unexpected effect index: %d -> %d", effectIndex,
mFfEffectsDual[effectIndex].id);
}
} else {
/* Initiate placeholders for OWT effects. */
mFfEffectsDual[effectIndex] = {
.type = FF_PERIODIC,
.id = -1,
.replay.length = 0,
.u.periodic.waveform = FF_CUSTOM,
.u.periodic.custom_data = nullptr,
.u.periodic.custom_len = 0,
};
}
}
} }
if (mHwCal->getRedc(&caldata)) { // ==============Calibration data checking======================================
mHwApi->setRedc(caldata);
if (mHwCalDef->getF0(&caldata)) {
mHwApiDef->setF0(caldata);
} }
if (mHwCal->getQ(&caldata)) { if (mHwCalDef->getRedc(&caldata)) {
mHwApi->setQ(caldata); mHwApiDef->setRedc(caldata);
}
if (mHwCalDef->getQ(&caldata)) {
mHwApiDef->setQ(caldata);
} }
mHwCal->getLongFrequencyShift(&longFrequencyShift); if (mHwCalDef->getF0SyncOffset(&mF0Offset)) {
if (longFrequencyShift > 0) { ALOGD("Vibrator::Vibrator: F0 offset calculated from both base and flip calibration data: "
mF0Offset = longFrequencyShift * std::pow(2, 14); "%u",
} else if (longFrequencyShift < 0) { mF0Offset);
mF0Offset = std::pow(2, 24) - std::abs(longFrequencyShift) * std::pow(2, 14);
} else { } else {
mF0Offset = 0; 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) { if (calVer == 2) {
mHwCal->getTickVolLevels(&mTickEffectVol); mHwCalDef->getTickVolLevels(&(mTickEffectVol));
mHwCal->getClickVolLevels(&mClickEffectVol); mHwCalDef->getClickVolLevels(&(mClickEffectVol));
mHwCal->getLongVolLevels(&mLongEffectVol); mHwCalDef->getLongVolLevels(&(mLongEffectVol));
} else { } else {
ALOGW("Unsupported calibration version! Using the default calibration value"); ALOGW("Unsupported calibration version! Using the default calibration value");
mHwCal->getTickVolLevels(&mTickEffectVol); mHwCalDef->getTickVolLevels(&(mTickEffectVol));
mHwCal->getClickVolLevels(&mClickEffectVol); mHwCalDef->getClickVolLevels(&(mClickEffectVol));
mHwCal->getLongVolLevels(&mLongEffectVol); mHwCalDef->getLongVolLevels(&(mLongEffectVol));
} }
mHwApi->setF0CompEnable(mHwCal->isF0CompEnabled()); // ================Project specific setting to driver===============================
mHwApi->setRedcCompEnable(mHwCal->isRedcCompEnabled());
mHwApiDef->setF0CompEnable(mHwCalDef->isF0CompEnabled());
mHwApiDef->setRedcCompEnable(mHwCalDef->isRedcCompEnabled());
mHwApiDef->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
if (mIsDual) {
mHwApiDual->setF0CompEnable(mHwCalDual->isF0CompEnabled());
mHwApiDual->setRedcCompEnable(mHwCalDual->isRedcCompEnabled());
mHwApiDual->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
}
// ===============Audio coupled haptics bool init ========
mIsUnderExternalControl = false; mIsUnderExternalControl = false;
mIsChirpEnabled = mHwCal->isChirpEnabled(); // =============== Compose PWLE check =====================================
mIsChirpEnabled = mHwCalDef->isChirpEnabled();
mHwCal->getSupportedPrimitives(&mSupportedPrimitivesBits); mHwCalDef->getSupportedPrimitives(&mSupportedPrimitivesBits);
if (mSupportedPrimitivesBits > 0) { if (mSupportedPrimitivesBits > 0) {
for (auto e : defaultSupportedPrimitives) { for (auto e : defaultSupportedPrimitives) {
if (mSupportedPrimitivesBits & (1 << uint32_t(e))) { if (mSupportedPrimitivesBits & (1 << uint32_t(e))) {
@ -378,7 +528,14 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
} }
mSupportedPrimitives = defaultSupportedPrimitives; mSupportedPrimitives = defaultSupportedPrimitives;
} }
mHwApi->setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US);
mPrimitiveMinScale = {0.0f, 0.01f, 0.11f, 0.23f, 0.0f, 0.25f, 0.02f, 0.03f, 0.16f};
// ====== Get GPIO status and init it ================
mGPIOStatus = mHwGPIO->getGPIO();
if (!mGPIOStatus || !mHwGPIO->initGPIO()) {
ALOGE("Vibrator: GPIO initialization process error");
}
} }
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) { ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
@ -392,7 +549,7 @@ ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
} else { } else {
ALOGE("No haptics ALSA device"); ALOGE("No haptics ALSA device");
} }
if (mHwApi->hasOwtFreeSpace()) { if (mHwApiDef->hasOwtFreeSpace()) {
ret |= IVibrator::CAP_COMPOSE_EFFECTS; ret |= IVibrator::CAP_COMPOSE_EFFECTS;
if (mIsChirpEnabled) { if (mIsChirpEnabled) {
ret |= IVibrator::CAP_FREQUENCY_CONTROL | IVibrator::CAP_COMPOSE_PWLE_EFFECTS; ret |= IVibrator::CAP_FREQUENCY_CONTROL | IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
@ -408,31 +565,48 @@ ndk::ScopedAStatus Vibrator::off() {
const std::scoped_lock<std::mutex> lock(mActiveId_mutex); const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
if (mActiveId >= 0) { if (mActiveId >= 0) {
ALOGV("Off: Stop the active effect: %d", mActiveId); ALOGD("Off: Stop the active effect: %d", mActiveId);
/* Stop the active effect. */ /* Stop the active effect. */
if (!mHwApi->setFFPlay(mInputFd, mActiveId, false)) { if (!mHwApiDef->setFFPlay(mInputFd, mActiveId, false)) {
ALOGE("Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno)); ALOGE("Off: Failed to stop effect %d (%d): %s", mActiveId, errno, strerror(errno));
ret = false; ret = false;
} }
if (mIsDual && (!mHwApiDual->setFFPlay(mInputFdDual, mActiveId, false))) {
ALOGE("Off: Failed to stop flip's effect %d (%d): %s", mActiveId, errno,
strerror(errno));
ret = false;
}
/* Do erase process */
if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) && if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) { (!mHwApiDef->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) {
ALOGE("Failed to clean up the composed effect %d", mActiveId); ALOGE("Off: Failed to clean up the composed effect %d", mActiveId);
ret = false; ret = false;
} }
mHwApi->clearTrigBtn(mInputFd, &mFfEffects[mActiveId], mActiveId); if (mIsDual && (mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApiDual->eraseOwtEffect(mInputFdDual, mActiveId, &mFfEffectsDual))) {
ALOGE("Off: Failed to clean up flip's the composed effect %d", mActiveId);
ret = false;
}
if (!mHwGPIO->setGPIOOutput(false)) {
ALOGE("Off: Failed to reset GPIO(%d): %s", errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
} else { } else {
ALOGV("Off: Vibrator is already off"); ALOGD("Off: Vibrator is already off");
} }
mActiveId = -1;
setGlobalAmplitude(false); setGlobalAmplitude(false);
if (mF0Offset) { if (mF0Offset) {
mHwApi->setF0Offset(0); mHwApiDef->setF0Offset(0);
if (mIsDual && mF0OffsetDual) {
mHwApiDual->setF0Offset(0);
}
} }
if (ret) { if (ret) {
ALOGD("Off: Done.");
mActiveId = -1;
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
} else { } else {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
@ -441,13 +615,8 @@ ndk::ScopedAStatus Vibrator::off() {
ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs, ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
const std::shared_ptr<IVibratorCallback> &callback) { const std::shared_ptr<IVibratorCallback> &callback) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::on"); ATRACE_NAME("Vibrator::on");
ALOGD("Vibrator::on");
if (isBusy()) {
ALOGD("Vibrator::on, isBusy");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (timeoutMs > MAX_TIME_MS) { if (timeoutMs > MAX_TIME_MS) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@ -460,7 +629,10 @@ ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
} }
setGlobalAmplitude(true); setGlobalAmplitude(true);
if (mF0Offset) { if (mF0Offset) {
mHwApi->setF0Offset(mF0Offset); mHwApiDef->setF0Offset(mF0Offset);
if (mIsDual && mF0OffsetDual) {
mHwApiDual->setF0Offset(mF0OffsetDual);
}
} }
return on(timeoutMs, index, nullptr /*ignored*/, callback); return on(timeoutMs, index, nullptr /*ignored*/, callback);
} }
@ -468,8 +640,8 @@ ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength, ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
const std::shared_ptr<IVibratorCallback> &callback, const std::shared_ptr<IVibratorCallback> &callback,
int32_t *_aidl_return) { int32_t *_aidl_return) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::perform"); ATRACE_NAME("Vibrator::perform");
ALOGD("Vibrator::perform");
return performEffect(effect, strength, callback, _aidl_return); return performEffect(effect, strength, callback, _aidl_return);
} }
@ -480,7 +652,6 @@ ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect> *_aidl_retu
} }
ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) { ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::setAmplitude"); ATRACE_NAME("Vibrator::setAmplitude");
if (amplitude <= 0.0f || amplitude > 1.0f) { if (amplitude <= 0.0f || amplitude > 1.0f) {
@ -496,17 +667,12 @@ ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
} }
ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) { ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::setExternalControl"); ATRACE_NAME("Vibrator::setExternalControl");
if (isSynced()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
setGlobalAmplitude(enabled); setGlobalAmplitude(enabled);
if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) { if (mHasHapticAlsaDevice || mConfigHapticAlsaDeviceDone || hasHapticAlsaDevice()) {
if (!mHwApi->setHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) { if (!mHwApiDef->setHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) {
ALOGE("Failed to %s haptic pcm device: %d", (enabled ? "enable" : "disable"), mDevice); ALOGE("Failed to %s haptic pcm device: %d", (enabled ? "enable" : "disable"), mDevice);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
@ -545,8 +711,8 @@ ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
if (!status.isOk()) { if (!status.isOk()) {
return status; return status;
} }
// Please check the overhead time detail in b/261841035
*durationMs = mEffectDurations[effectIndex]; *durationMs = mEffectDurations[effectIndex] + SETTING_TIME_OVERHEAD;
} else { } else {
*durationMs = 0; *durationMs = 0;
} }
@ -555,10 +721,11 @@ ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composite, ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composite,
const std::shared_ptr<IVibratorCallback> &callback) { const std::shared_ptr<IVibratorCallback> &callback) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::compose"); ATRACE_NAME("Vibrator::compose");
ALOGD("Vibrator::compose");
uint16_t size; uint16_t size;
uint16_t nextEffectDelay; uint16_t nextEffectDelay;
uint16_t totalDuration = 0;
auto ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_COMP]{0x00}, auto ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_COMP]{0x00},
FF_CUSTOM_DATA_LEN_MAX_COMP); FF_CUSTOM_DATA_LEN_MAX_COMP);
@ -569,6 +736,7 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
/* Check if there is a wait before the first effect. */ /* Check if there is a wait before the first effect. */
nextEffectDelay = composite.front().delayMs; nextEffectDelay = composite.front().delayMs;
totalDuration += nextEffectDelay;
if (nextEffectDelay > COMPOSE_DELAY_MAX_MS || nextEffectDelay < 0) { if (nextEffectDelay > COMPOSE_DELAY_MAX_MS || nextEffectDelay < 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} else if (nextEffectDelay > 0) { } else if (nextEffectDelay > 0) {
@ -592,7 +760,8 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
auto &e_curr = composite[i_curr]; auto &e_curr = composite[i_curr];
uint32_t effectIndex = 0; uint32_t effectIndex = 0;
uint32_t effectVolLevel = 0; uint32_t effectVolLevel = 0;
if (e_curr.scale < 0.0f || e_curr.scale > 1.0f) { float effectScale = e_curr.scale;
if (effectScale < 0.0f || effectScale > 1.0f) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
@ -602,7 +771,11 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
if (!status.isOk()) { if (!status.isOk()) {
return status; return status;
} }
effectVolLevel = intensityToVolLevel(e_curr.scale, effectIndex); if (effectScale < mPrimitiveMinScale[static_cast<uint32_t>(e_curr.primitive)]) {
effectScale = mPrimitiveMinScale[static_cast<uint32_t>(e_curr.primitive)];
}
effectVolLevel = intensityToVolLevel(effectScale, effectIndex);
totalDuration += mEffectDurations[effectIndex];
} }
/* Fetch the next composite effect delay and fill into the current section */ /* Fetch the next composite effect delay and fill into the current section */
@ -615,6 +788,7 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
nextEffectDelay = delay; nextEffectDelay = delay;
totalDuration += delay;
} }
if (effectIndex == 0 && nextEffectDelay == 0) { if (effectIndex == 0 && nextEffectDelay == 0) {
@ -631,6 +805,10 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
if (header_count == dspmem_chunk_bytes(ch)) { if (header_count == dspmem_chunk_bytes(ch)) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} else { } else {
mFfEffects[WAVEFORM_COMPOSE].replay.length = totalDuration;
if (mIsDual) {
mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = totalDuration;
}
return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, ch, return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, ch,
callback); callback);
} }
@ -660,68 +838,134 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
effectIndex = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE; effectIndex = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE;
uint32_t freeBytes; uint32_t freeBytes;
mHwApi->getOwtFreeSpace(&freeBytes); mHwApiDef->getOwtFreeSpace(&freeBytes);
if (dspmem_chunk_bytes(ch) > freeBytes) { if (dspmem_chunk_bytes(ch) > freeBytes) {
ALOGE("Invalid OWT length: Effect %d: %d > %d!", effectIndex, dspmem_chunk_bytes(ch), ALOGE("Invalid OWT length: Effect %d: %d > %d!", effectIndex, dspmem_chunk_bytes(ch),
freeBytes); freeBytes);
delete ch; delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
int errorStatus; if (mIsDual) {
if (isSynced()) { mHwApiDual->getOwtFreeSpace(&freeBytes);
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex; if (dspmem_chunk_bytes(ch) > freeBytes) {
ALOGE("Invalid OWT length in flip: Effect %d: %d > %d!", effectIndex,
dspmem_chunk_bytes(ch), freeBytes);
delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
} }
if (!mHwApi->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch),
&mFfEffects[effectIndex], &effectIndex, &errorStatus)) { int errorStatus;
if (mGPIOStatus && mIsDual) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
} else {
ALOGD("Not dual haptics HAL and GPIO status fail");
}
if (!mHwApiDef->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch),
&mFfEffects[effectIndex], &effectIndex, &errorStatus)) {
delete ch; delete ch;
ALOGE("Invalid uploadOwtEffect"); ALOGE("Invalid uploadOwtEffect");
return ndk::ScopedAStatus::fromExceptionCode(errorStatus); return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
} }
if (mIsDual && !mHwApiDual->uploadOwtEffect(mInputFdDual, ch->head, dspmem_chunk_bytes(ch),
&mFfEffectsDual[effectIndex], &effectIndex,
&errorStatus)) {
delete ch;
ALOGE("Invalid uploadOwtEffect in flip");
return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
}
delete ch; delete ch;
} else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX || } else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX ||
effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) { effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) {
/* Update duration for long/short vibration. */ /* Update duration for long/short vibration. */
mFfEffects[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs); mFfEffects[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
if (isSynced()) { if (mGPIOStatus && mIsDual) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex; mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
} else {
ALOGD("Not dual haptics HAL and GPIO status fail");
} }
if (!mHwApi->setFFEffect(mInputFd, &mFfEffects[effectIndex], if (!mHwApiDef->setFFEffect(mInputFd, &mFfEffects[effectIndex],
static_cast<uint16_t>(timeoutMs))) { static_cast<uint16_t>(timeoutMs))) {
ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno)); ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
} if (mIsDual) {
{ mFfEffectsDual[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
const std::scoped_lock<std::mutex> lock(mActiveId_mutex); if (!mHwApiDual->setFFEffect(mInputFdDual, &mFfEffectsDual[effectIndex],
mActiveId = effectIndex; static_cast<uint16_t>(timeoutMs))) {
if (isSynced() && ALOGE("Failed to edit flip's effect %d (%d): %s", effectIndex, errno,
(effectIndex == WAVEFORM_CLICK_INDEX || effectIndex == WAVEFORM_LIGHT_TICK_INDEX)) { strerror(errno));
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
if (!mHwApi->setFFEffect(mInputFd, &mFfEffects[effectIndex], mFfEffects[effectIndex].replay.length)) {
ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
} else if (!isSynced()) { }
// /* Play the event now. */ }
if (!mHwApi->setFFPlay(mInputFd, effectIndex, true)) {
{
const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
/* Play the event now. */
mActiveId = effectIndex;
if (!mGPIOStatus) {
ALOGE("GetVibrator: GPIO status error");
// Do playcode to play effect
if (!mHwApiDef->setFFPlay(mInputFd, effectIndex, true)) {
ALOGE("Failed to play effect %d (%d): %s", effectIndex, errno, strerror(errno)); ALOGE("Failed to play effect %d (%d): %s", effectIndex, errno, strerror(errno));
mActiveId = -1;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mIsDual && !mHwApiDual->setFFPlay(mInputFdDual, effectIndex, true)) {
ALOGE("Failed to play flip's effect %d (%d): %s", effectIndex, errno,
strerror(errno));
mActiveId = -1;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
} else {
// Using GPIO to play effect
if ((effectIndex == WAVEFORM_CLICK_INDEX || effectIndex == WAVEFORM_LIGHT_TICK_INDEX)) {
mFfEffects[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
if (!mHwApiDef->setFFEffect(mInputFd, &mFfEffects[effectIndex],
mFfEffects[effectIndex].replay.length)) {
ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (mIsDual) {
mFfEffectsDual[effectIndex].trigger.button = GPIO_TRIGGER_CONFIG | effectIndex;
if (!mHwApiDual->setFFEffect(mInputFdDual, &mFfEffectsDual[effectIndex],
mFfEffectsDual[effectIndex].replay.length)) {
ALOGE("Failed to edit flip's effect %d (%d): %s", effectIndex, errno,
strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
}
if (!mHwGPIO->setGPIOOutput(true)) {
ALOGE("Failed to trigger effect %d (%d) by GPIO: %s", effectIndex, errno,
strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
} }
} }
mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback); mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback);
ALOGD("Vibrator::on, set done.");
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
} }
ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum) { ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum) {
uint16_t scale = amplitudeToScale(amplitude, maximum); uint16_t scale = amplitudeToScale(amplitude, maximum);
if (!mHwApi->setFFGain(mInputFd, scale)) { if (!mHwApiDef->setFFGain(mInputFd, scale)) {
ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno)); ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
if (mIsDual) {
if (!mHwApiDual->setFFGain(mInputFdDual, scale)) {
ALOGE("Failed to set flip's gain to %u (%d): %s", scale, errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
} }
@ -730,6 +974,7 @@ ndk::ScopedAStatus Vibrator::setGlobalAmplitude(bool set) {
if (!set) { if (!set) {
mLongEffectScale = 1.0; // Reset the scale for the later new effect. mLongEffectScale = 1.0; // Reset the scale for the later new effect.
} }
return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX); return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX);
} }
@ -747,7 +992,7 @@ ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t /*id*/) {
ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) { ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
std::string caldata{8, '0'}; std::string caldata{8, '0'};
if (!mHwCal->getF0(&caldata)) { if (!mHwCalDef->getF0(&caldata)) {
ALOGE("Failed to get resonant frequency (%d): %s", errno, strerror(errno)); ALOGE("Failed to get resonant frequency (%d): %s", errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
@ -758,7 +1003,7 @@ ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) { ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
std::string caldata{8, '0'}; std::string caldata{8, '0'};
if (!mHwCal->getQ(&caldata)) { if (!mHwCalDef->getQ(&caldata)) {
ALOGE("Failed to get q factor (%d): %s", errno, strerror(errno)); ALOGE("Failed to get q factor (%d): %s", errno, strerror(errno));
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
} }
@ -1049,37 +1294,6 @@ bool Vibrator::isUnderExternalControl() {
return mIsUnderExternalControl; return mIsUnderExternalControl;
} }
// BnVibratorSync APIs
Status Vibrator::prepareSynced(const sp<IVibratorSyncCallback> &callback) {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::prepareSynced");
if (isBusy() || isSynced() || isUnderExternalControl()) {
ALOGE("Vibrator::prepareSynced, isBusy or isSynced");
return Status::fromExceptionCode(EX_ILLEGAL_STATE);
}
mSyncedCallback = callback;
return Status::ok();
}
Status Vibrator::cancelSynced() {
std::scoped_lock lock(mApiMutex);
ATRACE_NAME("Vibrator::cancelSynced");
if (!isSynced()) {
ALOGE("Vibrator::cancelSynced, isSynced");
return Status::fromExceptionCode(EX_ILLEGAL_STATE);
}
off();
mSyncedCallback = nullptr;
return Status::ok();
}
// BnCInterface APIs // BnCInterface APIs
binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) { binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
@ -1093,29 +1307,38 @@ binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
dprintf(fd, "AIDL:\n"); dprintf(fd, "AIDL:\n");
dprintf(fd, " F0 Offset: %" PRIu32 "\n", mF0Offset); dprintf(fd, " F0 Offset: base: %" PRIu32 " flip: %" PRIu32 "\n", mF0Offset, mF0OffsetDual);
dprintf(fd, " Voltage Levels:\n"); dprintf(fd, " Voltage Levels:\n");
dprintf(fd, " Tick Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mTickEffectVol[0], dprintf(fd, " Tick Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mTickEffectVol[0],
mTickEffectVol[1]); mTickEffectVol[1]);
dprintf(fd, " Click Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mClickEffectVol[0], dprintf(fd, " Click Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mClickEffectVol[0],
mClickEffectVol[1]); mClickEffectVol[1]);
dprintf(fd, " Long Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mLongEffectVol[0], dprintf(fd, " Long Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mLongEffectVol[0],
mLongEffectVol[1]); mLongEffectVol[1]);
dprintf(fd, " FF effect:\n"); dprintf(fd, " FF effect:\n");
dprintf(fd, " Physical waveform:\n"); dprintf(fd, " Physical waveform:\n");
dprintf(fd, "\tId\tIndex\tt ->\tt'\ttrigger button\n"); dprintf(fd, "==== Base ====\n\tId\tIndex\tt ->\tt'\ttrigger button\n");
for (uint8_t effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) { uint8_t effectId;
for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
dprintf(fd, "\t%d\t%d\t%d\t%d\t%X\n", mFfEffects[effectId].id, dprintf(fd, "\t%d\t%d\t%d\t%d\t%X\n", mFfEffects[effectId].id,
mFfEffects[effectId].u.periodic.custom_data[1], mEffectDurations[effectId], mFfEffects[effectId].u.periodic.custom_data[1], mEffectDurations[effectId],
mFfEffects[effectId].replay.length, mFfEffects[effectId].trigger.button); mFfEffects[effectId].replay.length, mFfEffects[effectId].trigger.button);
} }
if (mIsDual) {
dprintf(fd, "==== Flip ====\n\tId\tIndex\tt ->\tt'\ttrigger button\n");
for (effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
dprintf(fd, "\t%d\t%d\t%d\t%d\t%X\n", mFfEffectsDual[effectId].id,
mFfEffectsDual[effectId].u.periodic.custom_data[1], mEffectDurations[effectId],
mFfEffectsDual[effectId].replay.length,
mFfEffectsDual[effectId].trigger.button);
}
}
dprintf(fd, " OWT waveform:\n"); dprintf(fd, "Base: OWT waveform:\n");
dprintf(fd, "\tId\tBytes\tData\ttrigger button\n"); dprintf(fd, "\tId\tBytes\tData\tt\ttrigger button\n");
for (uint8_t effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX; for (effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX; effectId++) {
effectId++) {
uint32_t numBytes = mFfEffects[effectId].u.periodic.custom_len * 2; uint32_t numBytes = mFfEffects[effectId].u.periodic.custom_len * 2;
std::stringstream ss; std::stringstream ss;
ss << " "; ss << " ";
@ -1126,18 +1349,42 @@ binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
i)) i))
<< " "; << " ";
} }
dprintf(fd, "\t%d\t%d\t{%s}\t%X\n", mFfEffects[effectId].id, numBytes, ss.str().c_str(), dprintf(fd, "\t%d\t%d\t{%s}\t%u\t%X\n", mFfEffects[effectId].id, numBytes, ss.str().c_str(),
mFfEffects[effectId].trigger.button); mFfEffectsDual[effectId].replay.length, mFfEffects[effectId].trigger.button);
}
if (mIsDual) {
dprintf(fd, "Flip: OWT waveform:\n");
dprintf(fd, "\tId\tBytes\tData\tt\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%u\t%X\n", mFfEffectsDual[effectId].id, numBytes,
ss.str().c_str(), mFfEffectsDual[effectId].replay.length,
mFfEffectsDual[effectId].trigger.button);
}
} }
dprintf(fd, "\n"); dprintf(fd, "\n");
dprintf(fd, "\n"); dprintf(fd, "\n");
mHwApi->debug(fd); mHwApiDef->debug(fd);
dprintf(fd, "\n"); dprintf(fd, "\n");
mHwCal->debug(fd); mHwCalDef->debug(fd);
if (mIsDual) {
mHwApiDual->debug(fd);
dprintf(fd, "\n");
mHwCalDual->debug(fd);
}
fsync(fd); fsync(fd);
return STATUS_OK; return STATUS_OK;
@ -1148,7 +1395,7 @@ bool Vibrator::hasHapticAlsaDevice() {
// constructor is too early in the boot process and the pcm file contents // constructor is too early in the boot process and the pcm file contents
// are empty. Hence we make the call here once only right before we need to. // are empty. Hence we make the call here once only right before we need to.
if (!mConfigHapticAlsaDeviceDone) { if (!mConfigHapticAlsaDeviceDone) {
if (mHwApi->getHapticAlsaDevice(&mCard, &mDevice)) { if (mHwApiDef->getHapticAlsaDevice(&mCard, &mDevice)) {
mHasHapticAlsaDevice = true; mHasHapticAlsaDevice = true;
mConfigHapticAlsaDeviceDone = true; mConfigHapticAlsaDeviceDone = true;
} else { } else {
@ -1260,6 +1507,10 @@ ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength st
} }
*outTimeMs = timeMs; *outTimeMs = timeMs;
mFfEffects[WAVEFORM_COMPOSE].replay.length = static_cast<uint16_t>(timeMs);
if (mIsDual) {
mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = static_cast<uint16_t>(timeMs);
}
return ndk::ScopedAStatus::ok(); return ndk::ScopedAStatus::ok();
} }
@ -1356,26 +1607,55 @@ ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLev
} }
void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) { void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
ALOGD("Callback status in waitForComplete(): mSync: %d, callBack: %d", isSynced(), ALOGD("waitForComplete: Callback status in waitForComplete(): callBack: %d",
(callback != nullptr)); (callback != nullptr));
if (!mHwApi->pollVibeState(VIBE_STATE_HAPTIC, // Bypass checking flip part's haptic state
(mSyncedCallback) ? POLLING_TIMEOUT_IN_SYNC : POLLING_TIMEOUT)) { if (!mHwApiDef->pollVibeState(VIBE_STATE_HAPTIC, POLLING_TIMEOUT)) {
ALOGV("Fail to get state \"Haptic\""); ALOGD("Failed to get state \"Haptic\"");
} }
mHwApi->pollVibeState(VIBE_STATE_STOPPED); mHwApiDef->pollVibeState(VIBE_STATE_STOPPED);
// Check flip's state after base was done
if (mIsDual) {
mHwApiDual->pollVibeState(VIBE_STATE_STOPPED);
}
ALOGD("waitForComplete: get STOP");
{ {
const std::scoped_lock<std::mutex> lock(mActiveId_mutex); const std::scoped_lock<std::mutex> lock(mActiveId_mutex);
if ((mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) && if (mActiveId >= WAVEFORM_MAX_PHYSICAL_INDEX) {
(!mHwApi->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects))) { if (!mHwApiDef->eraseOwtEffect(mInputFd, mActiveId, &mFfEffects)) {
ALOGE("Failed to clean up the composed effect %d", mActiveId); ALOGE("Failed to clean up the composed effect %d", mActiveId);
} }
if (mActiveId >= 0) { if (mIsDual &&
mHwApi->clearTrigBtn(mInputFd, &mFfEffects[mActiveId], mActiveId); (!mHwApiDual->eraseOwtEffect(mInputFdDual, mActiveId, &mFfEffectsDual))) {
mActiveId = -1; ALOGE("Failed to clean up flip's composed effect %d", mActiveId);
}
} else { } else {
ALOGV("waitForComplete: Vibrator is already off"); ALOGD("waitForComplete: Vibrator is already off");
}
mActiveId = -1;
if (mGPIOStatus && !mHwGPIO->setGPIOOutput(false)) {
ALOGE("waitForComplete: Failed to reset GPIO(%d): %s", errno, strerror(errno));
}
// Do waveform number checking
uint32_t effectCount = WAVEFORM_MAX_PHYSICAL_INDEX;
mHwApiDef->getEffectCount(&effectCount);
if (effectCount > WAVEFORM_MAX_PHYSICAL_INDEX) {
// Forcibly clean all OWT waveforms
if (!mHwApiDef->eraseOwtEffect(mInputFd, WAVEFORM_MAX_INDEX, &mFfEffects)) {
ALOGE("Failed to clean up all base's composed effect");
}
}
if (mIsDual) {
// Forcibly clean all OWT waveforms
effectCount = WAVEFORM_MAX_PHYSICAL_INDEX;
mHwApiDual->getEffectCount(&effectCount);
if ((effectCount > WAVEFORM_MAX_PHYSICAL_INDEX) &&
(!mHwApiDual->eraseOwtEffect(mInputFdDual, WAVEFORM_MAX_INDEX, &mFfEffectsDual))) {
ALOGE("Failed to clean up all flip's composed effect");
}
} }
} }
@ -1385,14 +1665,7 @@ void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
ALOGE("Failed completion callback: %d", ret.getExceptionCode()); ALOGE("Failed completion callback: %d", ret.getExceptionCode());
} }
} }
if (mSyncedCallback) { ALOGD("waitForComplete: Done.");
auto ret = mSyncedCallback->onComplete();
if (!ret.isOk()) {
ALOGE("Failed completion mSyncedCallback: %d", ret.exceptionCode());
}
mSyncedCallback = nullptr;
}
ALOGV("waitForComplete: Done");
} }
uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) { uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
@ -1425,16 +1698,6 @@ uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
return volLevel; return volLevel;
} }
bool Vibrator::isBusy() {
auto timeout = std::chrono::seconds::zero();
auto status = mAsyncHandle.wait_for(timeout);
return status != std::future_status::ready;
}
bool Vibrator::isSynced() {
return (mSyncedCallback != nullptr);
}
} // namespace vibrator } // namespace vibrator
} // namespace hardware } // namespace hardware
} // namespace android } // namespace android

View file

@ -17,7 +17,6 @@
#include <aidl/android/hardware/vibrator/BnVibrator.h> #include <aidl/android/hardware/vibrator/BnVibrator.h>
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
#include <android/hardware/vibrator/BnVibratorSyncCallback.h>
#include <linux/input.h> #include <linux/input.h>
#include <tinyalsa/asoundlib.h> #include <tinyalsa/asoundlib.h>
@ -30,12 +29,22 @@ namespace android {
namespace hardware { namespace hardware {
namespace vibrator { namespace vibrator {
using ::android::sp;
using ::android::binder::Status;
using ::android::hardware::vibrator::IVibratorSyncCallback;
class Vibrator : public BnVibrator { class Vibrator : public BnVibrator {
public: public:
// APIs for interfacing with the GPIO pin.
class HwGPIO {
public:
virtual ~HwGPIO() = default;
// Get the GPIO pin num and address shift information
virtual bool getGPIO() = 0;
// Init the GPIO function
virtual bool initGPIO() = 0;
// Trigger the GPIO pin to synchronize both vibrators's play
virtual bool setGPIOOutput(bool value) = 0;
// Emit diagnostic information to the given file.
virtual void debug(int fd) = 0;
};
// APIs for interfacing with the kernel driver. // APIs for interfacing with the kernel driver.
class HwApi { class HwApi {
public: public:
@ -85,8 +94,6 @@ class Vibrator : public BnVibrator {
int *status) = 0; int *status) = 0;
// Erase OWT waveform // Erase OWT waveform
virtual bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) = 0; virtual bool eraseOwtEffect(int fd, int8_t effectIndex, std::vector<ff_effect> *effect) = 0;
// Erase trigger button information
virtual void clearTrigBtn(int fd, struct ff_effect *effect, int8_t id) = 0;
// Emit diagnostic information to the given file. // Emit diagnostic information to the given file.
virtual void debug(int fd) = 0; virtual void debug(int fd) = 0;
}; };
@ -100,6 +107,9 @@ class Vibrator : public BnVibrator {
// Obtains the LRA resonant frequency to be used for PWLE playback // Obtains the LRA resonant frequency to be used for PWLE playback
// and click compensation. // and click compensation.
virtual bool getF0(std::string *value) = 0; virtual bool getF0(std::string *value) = 0;
// Obtains the offset for actuator that will adjust configured F0 to target
// frequency for dual actuators
virtual bool getF0SyncOffset(uint32_t *value) = 0;
// Obtains the LRA series resistance to be used for click // Obtains the LRA series resistance to be used for click
// compensation. // compensation.
virtual bool getRedc(std::string *value) = 0; virtual bool getRedc(std::string *value) = 0;
@ -126,7 +136,9 @@ class Vibrator : public BnVibrator {
}; };
public: public:
Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal); Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> hwCalDefault,
std::unique_ptr<HwApi> hwApiDual, std::unique_ptr<HwCal> hwCalDual,
std::unique_ptr<HwGPIO> hwgpio);
// BnVibrator APIs // BnVibrator APIs
ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return) override; ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return) override;
@ -160,13 +172,11 @@ class Vibrator : public BnVibrator {
ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite, ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
const std::shared_ptr<IVibratorCallback> &callback) override; const std::shared_ptr<IVibratorCallback> &callback) override;
// BnVibratorSync APIs
Status prepareSynced(const sp<IVibratorSyncCallback> &callback);
Status cancelSynced();
// BnCInterface APIs // BnCInterface APIs
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override; binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time
private: private:
ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, struct dspmem_chunk *ch, ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, struct dspmem_chunk *ch,
const std::shared_ptr<IVibratorCallback> &callback); const std::shared_ptr<IVibratorCallback> &callback);
@ -195,33 +205,37 @@ class Vibrator : public BnVibrator {
bool hasHapticAlsaDevice(); bool hasHapticAlsaDevice();
bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device); bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device);
bool isBusy(); std::unique_ptr<HwApi> mHwApiDef;
bool isSynced(); std::unique_ptr<HwCal> mHwCalDef;
std::unique_ptr<HwApi> mHwApiDual;
std::unique_ptr<HwApi> mHwApi; std::unique_ptr<HwCal> mHwCalDual;
std::unique_ptr<HwCal> mHwCal; std::unique_ptr<HwGPIO> mHwGPIO;
uint32_t mF0Offset; uint32_t mF0Offset;
uint32_t mF0OffsetDual;
std::array<uint32_t, 2> mTickEffectVol; std::array<uint32_t, 2> mTickEffectVol;
std::array<uint32_t, 2> mClickEffectVol; std::array<uint32_t, 2> mClickEffectVol;
std::array<uint32_t, 2> mLongEffectVol; std::array<uint32_t, 2> mLongEffectVol;
std::vector<ff_effect> mFfEffects; std::vector<ff_effect> mFfEffects;
std::vector<ff_effect> mFfEffectsDual;
std::vector<uint32_t> mEffectDurations; std::vector<uint32_t> mEffectDurations;
std::future<void> mAsyncHandle; std::future<void> mAsyncHandle;
::android::base::unique_fd mInputFd; ::android::base::unique_fd mInputFd;
::android::base::unique_fd mInputFdDual;
int8_t mActiveId{-1}; int8_t mActiveId{-1};
struct pcm *mHapticPcm; struct pcm *mHapticPcm;
int mCard; int mCard;
int mDevice; int mDevice;
bool mHasHapticAlsaDevice{false}; bool mHasHapticAlsaDevice{false};
bool mIsUnderExternalControl; bool mIsUnderExternalControl;
float mLongEffectScale = 1.0; float mLongEffectScale{1.0};
bool mIsChirpEnabled; bool mIsChirpEnabled;
uint32_t mSupportedPrimitivesBits = 0x0; uint32_t mSupportedPrimitivesBits = 0x0;
std::vector<CompositePrimitive> mSupportedPrimitives; std::vector<CompositePrimitive> mSupportedPrimitives;
std::vector<float> mPrimitiveMinScale;
bool mConfigHapticAlsaDeviceDone{false}; bool mConfigHapticAlsaDeviceDone{false};
// prevent concurrent execution of IVibrator and IVibratorSync APIs bool mGPIOStatus;
sp<IVibratorSyncCallback> mSyncedCallback; bool mIsDual{false};
std::recursive_mutex mApiMutex; std::mutex mActiveId_mutex; // protects mActiveId
}; };
} // namespace vibrator } // namespace vibrator

View file

@ -1,217 +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 {
using ::android::sp;
using ::android::binder::Status;
using ::android::hardware::vibrator::BnVibratorSyncCallback;
class VibratorSyncCallback : public BnVibratorSyncCallback {
public:
Status onComplete() override {
mPromise.set_value();
return Status::ok();
}
auto getFuture() { return mPromise.get_future(); }
private:
std::promise<void> mPromise;
};
VibratorManager::VibratorManager(std::unique_ptr<HwApi> hwapi,
const std::vector<VibratorTuple> &&vibrators)
: mHwApi(std::move(hwapi)), mVibrators(std::move(vibrators)), mAsyncHandle(std::async([] {})) {
mGPIOStatus = mHwApi->getGPIO();
}
// BnVibratorManager APIs
ndk::ScopedAStatus VibratorManager::getCapabilities(int32_t *_aidl_return) {
ATRACE_NAME("VibratorManager::getCapabilities");
int32_t ret =
IVibratorManager::CAP_SYNC | IVibratorManager::CAP_PREPARE_ON |
IVibratorManager::CAP_PREPARE_PERFORM | IVibratorManager::CAP_PREPARE_COMPOSE |
IVibratorManager::CAP_MIXED_TRIGGER_ON | IVibratorManager::CAP_MIXED_TRIGGER_PERFORM |
IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE | IVibratorManager::CAP_TRIGGER_CALLBACK;
*_aidl_return = ret;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::getVibratorIds(std::vector<int> *_aidl_return) {
ATRACE_NAME("VibratorManager::getVibratorIds");
_aidl_return->resize(mVibrators.size());
std::iota(_aidl_return->begin(), _aidl_return->end(), 0);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::getVibrator(int vibratorId,
std::shared_ptr<IVibrator> *_aidl_return) {
ATRACE_NAME("VibratorManager::getVibrator");
if (!mGPIOStatus) {
ALOGE("GetVibrator: GPIO status error");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (vibratorId >= mVibrators.size()) {
ALOGE("GetVibrator: wrong requested vibrator ID");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
std::tie(*_aidl_return, std::ignore) = mVibrators.at(vibratorId);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::prepareSynced(const std::vector<int32_t> &ids) {
ATRACE_NAME("VibratorManager::prepareSynced");
if (!mGPIOStatus) {
ALOGE("prepareSynced: GPIO status error");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (ids.empty()) {
ALOGE("PrepareSynced: No vibrator could be synced");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (!mSyncContext.empty()) {
ALOGE("PrepareSynced: mSyncContext is not EMPTY!!!");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (isBusy()) {
ALOGE("PrepareSynced: IS BUSY!!!");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
for (auto &id : ids) {
auto &[vib, ext] = mVibrators.at(id);
auto callback = sp<VibratorSyncCallback>::make();
if (ext->prepareSynced(callback).isOk()) {
mSyncContext.emplace_back(id, callback->getFuture());
} else {
cancelSynced();
ALOGV("prepareSynced: Fail: %d", id);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
ALOGV("prepareSynced: Done");
if (mHwApi->initGPIO()) {
return ndk::ScopedAStatus::ok();
} else {
ALOGE("PrepareSynced: GPIO status init fail");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
}
ndk::ScopedAStatus VibratorManager::triggerSynced(
const std::shared_ptr<IVibratorCallback> &callback) {
ATRACE_NAME("VibratorManager::triggerSynced");
ALOGV("TriggerSynced");
if (isBusy()) {
ALOGE("TriggerSynced isBusy");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
mHwApi->setTrigger(true);
doAsync([=]() {
{
std::shared_lock lock(mContextMutex);
for (auto &[id, future] : mSyncContext) {
future.wait();
}
}
{
std::unique_lock lock(mContextMutex);
mSyncContext.clear();
}
if (callback) {
auto ret = callback->onComplete();
if (!ret.isOk()) {
ALOGE("Failed completion callback: %d", ret.getExceptionCode());
}
ALOGD("Callback in MANAGER onComplete()");
}
});
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VibratorManager::cancelSynced() {
ATRACE_NAME("VibratorManager::cancelSynced");
ALOGV("Do cancelSynced");
mHwApi->setTrigger(false);
{
std::shared_lock lock(mContextMutex);
for (auto &[id, future] : mSyncContext) {
auto &[vib, ext] = mVibrators.at(id);
ext->cancelSynced();
}
}
{
std::unique_lock lock(mContextMutex);
mSyncContext.clear();
}
mAsyncHandle.wait();
return ndk::ScopedAStatus::ok();
}
// BnCInterface APIs
binder_status_t VibratorManager::dump(int fd, const char ** /*args*/, uint32_t /*numArgs*/) {
if (fd < 0) {
ALOGE("Called debug() with invalid fd.");
return STATUS_OK;
}
mHwApi->debug(fd);
fsync(fd);
return STATUS_OK;
}
// Private Methods
template <typename Func, typename... Args>
void VibratorManager::doAsync(Func &&func, Args &&...args) {
mAsyncHandle = std::async(func, std::forward<Args>(args)...);
}
bool VibratorManager::isBusy() {
auto timeout = std::chrono::seconds::zero();
auto status = mAsyncHandle.wait_for(timeout);
return status != std::future_status::ready;
}
} // namespace vibrator
} // namespace hardware
} // namespace android
} // namespace aidl

View file

@ -1,83 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <aidl/android/hardware/vibrator/BnVibrator.h>
#include <aidl/android/hardware/vibrator/BnVibratorManager.h>
#include <android/hardware/vibrator/IVibratorSync.h>
#include <future>
#include <shared_mutex>
namespace aidl {
namespace android {
namespace hardware {
namespace vibrator {
using ::android::hardware::vibrator::IVibratorSync;
class VibratorManager : public BnVibratorManager {
public:
// APIs for interfacing with the kernel driver.
class HwApi {
public:
virtual ~HwApi() = default;
// Get the GPIO pin num and address shift information
virtual bool getGPIO() = 0;
// Init the GPIO function
virtual bool initGPIO() = 0;
// Trigger activation of the synchronized vibrators.
virtual bool setTrigger(bool value) = 0;
// Emit diagnostic information to the given file.
virtual void debug(int fd) = 0;
};
using VibratorTuple = std::tuple<std::shared_ptr<IVibrator>, ::android::sp<IVibratorSync>>;
public:
VibratorManager(std::unique_ptr<HwApi> hwapi, const std::vector<VibratorTuple> &&vibrators);
// BnVibratorManager APIs
ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return);
ndk::ScopedAStatus getVibratorIds(std::vector<int> *_aidl_return);
ndk::ScopedAStatus getVibrator(int vibratorId, std::shared_ptr<IVibrator> *_aidl_return);
ndk::ScopedAStatus prepareSynced(const std::vector<int32_t> &ids) override;
ndk::ScopedAStatus triggerSynced(const std::shared_ptr<IVibratorCallback> &callback) override;
ndk::ScopedAStatus cancelSynced() override;
// BnCInterface APIs
binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
private:
template <typename Func, typename... Args>
void doAsync(Func &&func, Args &&...args);
bool isBusy();
private:
using SyncContext = std::tuple<int32_t, std::future<void>>;
const std::unique_ptr<HwApi> mHwApi;
const std::vector<VibratorTuple> mVibrators;
std::vector<SyncContext> mSyncContext;
std::shared_mutex mContextMutex;
std::future<void> mAsyncHandle;
bool mGPIOStatus;
};
} // namespace vibrator
} // namespace hardware
} // namespace android
} // namespace aidl

View file

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

View file

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

View file

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

View file

@ -1,24 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.vibrator;
import android.hardware.vibrator.IVibratorSyncCallback;
interface IVibratorSync {
void prepareSynced(in IVibratorSyncCallback callback);
void cancelSynced();
}

View file

@ -1,21 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.vibrator;
interface IVibratorSyncCallback {
oneway void onComplete();
}

View file

@ -1,47 +0,0 @@
on property:vendor.all.modules.ready=1
wait /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/redc_cal_time_ms
mkdir /mnt/vendor/persist/haptics 0770 system system
chmod 770 /mnt/vendor/persist/haptics
chmod 440 /mnt/vendor/persist/haptics/cs40l26_dual.cal
chown system system /mnt/vendor/persist/haptics
chown system system /mnt/vendor/persist/haptics/cs40l26_dual.cal
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/f0_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/q_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/redc_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/vibe_state
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/num_waves
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_offset
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/owt_free_space
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/redc_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/delay_before_stop_playback_us
enable vendor.vibrator.cs40l26-dual
service vendor.vibrator.cs40l26-dual /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-dual-private
class hal
user system
group system input
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 HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a-dual/
setenv HWAPI_DEBUG_PATHS "
calibration/f0_stored
calibration/redc_stored
calibration/q_stored
default/vibe_state
default/num_waves
default/f0_offset
default/owt_free_space
default/f0_comp_enable
default/redc_comp_enable
default/delay_before_stop_playback_us
"
disabled

View file

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

View file

@ -4,19 +4,32 @@ on property:vendor.all.modules.ready=1
mkdir /mnt/vendor/persist/haptics 0770 system system mkdir /mnt/vendor/persist/haptics 0770 system system
chmod 770 /mnt/vendor/persist/haptics chmod 770 /mnt/vendor/persist/haptics
chmod 440 /mnt/vendor/persist/haptics/cs40l26.cal chmod 440 /mnt/vendor/persist/haptics/cs40l26.cal
chmod 440 /mnt/vendor/persist/haptics/cs40l26_dual.cal
chown system system /mnt/vendor/persist/haptics chown system system /mnt/vendor/persist/haptics
chown system system /mnt/vendor/persist/haptics/cs40l26.cal chown system system /mnt/vendor/persist/haptics/cs40l26.cal
chown system system /mnt/vendor/persist/haptics/cs40l26_dual.cal
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/f0_stored chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/f0_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/f0_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/q_stored chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/q_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/q_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_stored chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/redc_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/vibe_state chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/vibe_state
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/vibe_state
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/num_waves chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/num_waves
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/num_waves
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_offset chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_offset
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_offset
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/owt_free_space chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/owt_free_space
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/owt_free_space
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_comp_enable chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/redc_comp_enable chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/redc_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/redc_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/delay_before_stop_playback_us chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/delay_before_stop_playback_us
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/delay_before_stop_playback_us
chown system system /dev/gpiochip44
enable vendor.vibrator.cs40l26 enable vendor.vibrator.cs40l26
@ -25,12 +38,16 @@ service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service
user system user system
group system input group system input
setenv HAPTIC_NAME Haptics
setenv INPUT_EVENT_NAME cs40l26_input setenv INPUT_EVENT_NAME cs40l26_input
setenv INPUT_EVENT_NAME_DUAL cs40l26_dual_input
setenv INPUT_EVENT_PATH /dev/input/event* setenv INPUT_EVENT_PATH /dev/input/event*
setenv PROPERTY_PREFIX ro.vendor.vibrator.hal. setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal
setenv CALIBRATION_FILEPATH_DUAL /mnt/vendor/persist/haptics/cs40l26_dual.cal
setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a/ setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a/
setenv HWAPI_PATH_PREFIX_DUAL /sys/bus/i2c/devices/i2c-cs40l26a-dual/
setenv HWAPI_DEBUG_PATHS " setenv HWAPI_DEBUG_PATHS "
calibration/f0_stored calibration/f0_stored
calibration/redc_stored calibration/redc_stored
@ -45,3 +62,4 @@ service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service
" "
disabled disabled

View file

@ -1,14 +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 PROPERTY_PREFIX ro.vendor.vibrator.hal.
disabled

View file

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

View file

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

View file

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

View file

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

View file

@ -20,34 +20,60 @@
#include <log/log.h> #include <log/log.h>
#include "Hardware.h" #include "Hardware.h"
#include "VibMgrHwApi.h"
#include "Vibrator.h" #include "Vibrator.h"
#include "VibratorSync.h"
using ::aidl::android::hardware::vibrator::HwApi; using ::aidl::android::hardware::vibrator::HwApi;
using ::aidl::android::hardware::vibrator::HwCal; using ::aidl::android::hardware::vibrator::HwCal;
using ::aidl::android::hardware::vibrator::VibMgrHwApi;
using ::aidl::android::hardware::vibrator::Vibrator; using ::aidl::android::hardware::vibrator::Vibrator;
using ::android::defaultServiceManager; using ::android::defaultServiceManager;
using ::android::ProcessState; using ::android::ProcessState;
using ::android::sp; using ::android::sp;
using ::android::String16; using ::android::String16;
using ::android::hardware::vibrator::VibratorSync;
#if !defined(VIBRATOR_NAME) #if !defined(VIBRATOR_NAME)
#define VIBRATOR_NAME "default" #define VIBRATOR_NAME "default"
#endif #endif
int main() { int main() {
auto svc = ndk::SharedRefBase::make<Vibrator>(std::make_unique<HwApi>(), const char *hwApiPathPrefixDual = std::getenv("HWAPI_PATH_PREFIX_DUAL");
std::make_unique<HwCal>()); const char *calFilePath = std::getenv("CALIBRATION_FILEPATH");
const char *calFilePathDual = std::getenv("CALIBRATION_FILEPATH_DUAL");
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; const auto svcName = std::string() + svc->descriptor + "/" + VIBRATOR_NAME;
auto ext = sp<VibratorSync>::make(svc);
const auto extName = std::stringstream() << ext->descriptor << "/" << VIBRATOR_NAME;
ProcessState::initWithDriver("/dev/vndbinder"); ProcessState::initWithDriver("/dev/vndbinder");
defaultServiceManager()->addService(String16(extName.str().c_str()), ext);
auto svcBinder = svc->asBinder(); auto svcBinder = svc->asBinder();
binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str()); binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str());
LOG_ALWAYS_FATAL_IF(status != STATUS_OK); LOG_ALWAYS_FATAL_IF(status != STATUS_OK);

View file

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

View file

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

View file

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