summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2022-02-14 17:31:17 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2022-02-14 17:31:17 +0000
commit8e0f3aaf0924ef1641e968869b69bddd9047fac0 (patch)
treea78cbc4595fe61b048e828fcfe8f02053279ada8
parent6a53c9f84db035e2ff06c58ee697b75d207d44d8 (diff)
parentbcf55fa84f13157dd3795a4e646aedae02c3e6af (diff)
downloadpixel-sam_222710654.tar.gz
Merge "Merge sc-v2-dev-plus-aosp-without-vendor@8084891" into stage-aosp-mastersam_222710654
-rw-r--r--fastboot/Android.bp10
-rw-r--r--fastboot/Fastboot.cpp72
-rw-r--r--mm/device_gki.mk4
-rw-r--r--mm/fstab.zram.2g1
-rw-r--r--mm/fstab.zram.3g1
-rw-r--r--mm/pixel-mm-gki.rc6
-rw-r--r--pixelstats/SysfsCollector.cpp2
-rw-r--r--pixelstats/UeventListener.cpp17
-rw-r--r--pixelstats/WirelessChargeStats.cpp4
-rw-r--r--pixelstats/include/pixelstats/UeventListener.h3
-rw-r--r--power-libperfmgr/aidl/Power.cpp5
-rw-r--r--power-libperfmgr/aidl/PowerExt.cpp4
-rw-r--r--power-libperfmgr/aidl/PowerHintSession.cpp39
-rw-r--r--power-libperfmgr/disp-power/InteractionHandler.cpp2
-rw-r--r--power-libperfmgr/libperfmgr/HintManager.cc5
-rw-r--r--power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h6
-rw-r--r--power-libperfmgr/libperfmgr/include/perfmgr/RequestGroup.h4
-rw-r--r--thermal/Thermal.cpp9
-rw-r--r--vibrator/common/HardwareBase.h57
-rw-r--r--vibrator/cs40l25/Android.bp93
-rw-r--r--vibrator/cs40l25/Hardware.h3
-rw-r--r--vibrator/cs40l25/Vibrator.cpp50
-rw-r--r--vibrator/cs40l25/Vibrator.h4
-rw-r--r--vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-dual.rc4
-rw-r--r--vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-specific-revision-dual.rc78
-rw-r--r--vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-specific-revision.rc75
-rw-r--r--vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc2
-rw-r--r--vibrator/cs40l25/device-stereo-specific-revision.mk6
-rw-r--r--vibrator/cs40l25/device-stereo.mk1
-rw-r--r--vibrator/cs40l25/tests/mocks.h1
-rw-r--r--vibrator/cs40l25/tests/test-hwapi.cpp3
-rw-r--r--vibrator/cs40l26/Android.bp170
-rw-r--r--vibrator/cs40l26/Hardware.h135
-rw-r--r--vibrator/cs40l26/Vibrator.cpp1365
-rw-r--r--vibrator/cs40l26/Vibrator.h190
-rw-r--r--vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc40
-rw-r--r--vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.xml7
-rw-r--r--vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dvt-dual.rc40
-rw-r--r--vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dvt.rc40
-rw-r--r--vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-evt-dual.rc40
-rw-r--r--vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-evt.rc40
-rw-r--r--vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc40
-rw-r--r--vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.xml7
-rw-r--r--vibrator/cs40l26/device-stereo-dvt.mk3
-rw-r--r--vibrator/cs40l26/device-stereo-evt.mk3
-rw-r--r--vibrator/cs40l26/device-stereo.mk6
-rw-r--r--vibrator/cs40l26/device.mk6
-rw-r--r--vibrator/cs40l26/service.cpp55
48 files changed, 2676 insertions, 82 deletions
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index c40b69f7..dd2f80da 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -26,13 +26,15 @@ cc_library {
relative_install_path: "hw",
export_include_dirs: ["include"],
shared_libs: [
- "liblog",
+ "android.hardware.fastboot@1.0",
+ "android.hardware.fastboot@1.1",
"libbase",
+ "libcutils",
+ "libext4_utils",
+ "libfs_mgr",
"libhidlbase",
+ "liblog",
"libutils",
- "libcutils",
- "android.hardware.fastboot@1.0",
- "android.hardware.fastboot@1.1",
],
static_libs: [
"libnos_for_fastboot",
diff --git a/fastboot/Fastboot.cpp b/fastboot/Fastboot.cpp
index 93435d69..922334b4 100644
--- a/fastboot/Fastboot.cpp
+++ b/fastboot/Fastboot.cpp
@@ -19,11 +19,19 @@
#include <string>
#include <unordered_map>
#include <vector>
+#include <map>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+
+// FS headers
+#include <ext4_utils/wipe.h>
+#include <fs_mgr.h>
+#include <fs_mgr/roots.h>
+
+// Nugget headers
#include <app_nugget.h>
#include <nos/NuggetClient.h>
#include <nos/debug.h>
@@ -122,7 +130,50 @@ Return<void> Fastboot::doOemCommand(const ::android::hardware::hidl_string& oemC
return Void();
}
+static android::fs_mgr::Fstab fstab;
+enum WipeVolumeStatus {
+ WIPE_OK = 0,
+ VOL_FSTAB,
+ VOL_UNKNOWN,
+ VOL_MOUNTED,
+ VOL_BLK_DEV_OPEN,
+ WIPE_ERROR_MAX = 0xffffffff,
+};
+std::map<enum WipeVolumeStatus, std::string> wipe_vol_ret_msg{
+ {WIPE_OK, ""},
+ {VOL_FSTAB, "Unknown FS table"},
+ {VOL_UNKNOWN, "Unknown volume"},
+ {VOL_MOUNTED, "Fail to unmount volume"},
+ {VOL_BLK_DEV_OPEN, "Fail to open block device"},
+ {WIPE_ERROR_MAX, "Unknown wipe error"}};
+
+enum WipeVolumeStatus wipe_volume(const std::string &volume) {
+ if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
+ return VOL_FSTAB;
+ }
+ const fs_mgr::FstabEntry *v = android::fs_mgr::GetEntryForPath(&fstab, volume);
+ if (v == nullptr) {
+ return VOL_UNKNOWN;
+ }
+ if (android::fs_mgr::EnsurePathUnmounted(&fstab, volume) != true) {
+ return VOL_MOUNTED;
+ }
+
+ int fd = open(v->blk_device.c_str(), O_WRONLY | O_CREAT, 0644);
+ if (fd == -1) {
+ return VOL_BLK_DEV_OPEN;
+ }
+ wipe_block_device(fd, get_block_device_size(fd));
+ close(fd);
+
+ return WIPE_OK;
+}
+
Return<void> Fastboot::doOemSpecificErase(V1_1::IFastboot::doOemSpecificErase_cb _hidl_cb) {
+ // Erase metadata partition along with userdata partition.
+ // Keep erasing Titan M even if failing on this case.
+ auto wipe_status = wipe_volume("/metadata");
+
// Connect to Titan M
::nos::NuggetClient client;
client.Open();
@@ -136,16 +187,27 @@ Return<void> Fastboot::doOemSpecificErase(V1_1::IFastboot::doOemSpecificErase_cb
std::vector<uint8_t> magic(sizeof(magicValue));
memcpy(magic.data(), &magicValue, sizeof(magicValue));
const uint8_t retry_count = 5;
+ uint32_t nugget_status;
for(uint8_t i = 0; i < retry_count; i++) {
- const uint32_t status
- = client.CallApp(APP_ID_NUGGET, NUGGET_PARAM_NUKE_FROM_ORBIT, magic, nullptr);
- if (status == APP_SUCCESS) {
- _hidl_cb({ Status::SUCCESS, "" });
+ nugget_status = client.CallApp(APP_ID_NUGGET, NUGGET_PARAM_NUKE_FROM_ORBIT, magic, nullptr);
+ if (nugget_status == APP_SUCCESS && wipe_status == WIPE_OK) {
+ _hidl_cb({Status::SUCCESS, wipe_vol_ret_msg[wipe_status]});
return Void();
}
}
- _hidl_cb({ Status::FAILURE_UNKNOWN, "Titan M user data wipe failed" });
+ // Return exactly what happened
+ if (nugget_status != APP_SUCCESS && wipe_status != WIPE_OK) {
+ _hidl_cb({Status::FAILURE_UNKNOWN, "Fail on wiping metadata and Titan M user data"});
+ } else if (nugget_status != APP_SUCCESS) {
+ _hidl_cb({Status::FAILURE_UNKNOWN, "Titan M user data wipe failed"});
+ } else {
+ if (wipe_vol_ret_msg.find(wipe_status) != wipe_vol_ret_msg.end())
+ _hidl_cb({Status::FAILURE_UNKNOWN, wipe_vol_ret_msg[wipe_status]});
+ else // Should not reach here, but handle it anyway
+ _hidl_cb({Status::FAILURE_UNKNOWN, "Unknown failure"});
+ }
+
return Void();
}
diff --git a/mm/device_gki.mk b/mm/device_gki.mk
index 5d068e6c..c0bff728 100644
--- a/mm/device_gki.mk
+++ b/mm/device_gki.mk
@@ -1,5 +1,7 @@
PRODUCT_COPY_FILES += \
- hardware/google/pixel/mm/pixel-mm-gki.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/pixel-mm-gki.rc
+ hardware/google/pixel/mm/pixel-mm-gki.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/pixel-mm-gki.rc \
+ hardware/google/pixel/mm/fstab.zram.2g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.2g \
+ hardware/google/pixel/mm/fstab.zram.3g:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.zram.3g
ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
PRODUCT_PACKAGES += \
diff --git a/mm/fstab.zram.2g b/mm/fstab.zram.2g
new file mode 100644
index 00000000..aa59346c
--- /dev/null
+++ b/mm/fstab.zram.2g
@@ -0,0 +1 @@
+/dev/block/zram0 none swap defaults zramsize=2147483648,max_comp_streams=8,zram_backingdev_size=512M
diff --git a/mm/fstab.zram.3g b/mm/fstab.zram.3g
new file mode 100644
index 00000000..b087adf1
--- /dev/null
+++ b/mm/fstab.zram.3g
@@ -0,0 +1 @@
+/dev/block/zram0 none swap defaults zramsize=3221225472,max_comp_streams=8,zram_backingdev_size=512M
diff --git a/mm/pixel-mm-gki.rc b/mm/pixel-mm-gki.rc
index 0566ef46..e5ab7f75 100644
--- a/mm/pixel-mm-gki.rc
+++ b/mm/pixel-mm-gki.rc
@@ -19,5 +19,11 @@ on init
# khugepaged tuning
write /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs 60000
+on property:persist.vendor.zram.size=*
+ setprop vendor.zram.size ${persist.vendor.zram.size}
+
+on property:sys.boot_completed=1
+ swapon_all /vendor/etc/fstab.zram.${vendor.zram.size}
+
on property:sys.boot_completed=1
chmod 444 /sys/kernel/debug/page_owner
diff --git a/pixelstats/SysfsCollector.cpp b/pixelstats/SysfsCollector.cpp
index f2fdeea8..f814119f 100644
--- a/pixelstats/SysfsCollector.cpp
+++ b/pixelstats/SysfsCollector.cpp
@@ -894,7 +894,6 @@ void SysfsCollector::logPerDay() {
logSpeechDspStat(stats_client);
logUFSLifetime(stats_client);
logUFSErrorStats(stats_client);
- logZramStats(stats_client);
logSpeakerHealthStats(stats_client);
mm_metrics_reporter_.logCmaStatus(stats_client);
mm_metrics_reporter_.logPixelMmMetricsPerDay(stats_client);
@@ -907,6 +906,7 @@ void SysfsCollector::logPerHour() {
return;
}
mm_metrics_reporter_.logPixelMmMetricsPerHour(stats_client);
+ logZramStats(stats_client);
if (kPowerMitigationStatsPath != nullptr && strlen(kPowerMitigationStatsPath) > 0)
mitigation_stats_reporter_.logMitigationStatsPerHour(stats_client,
kPowerMitigationStatsPath);
diff --git a/pixelstats/UeventListener.cpp b/pixelstats/UeventListener.cpp
index 05283013..f8dec476 100644
--- a/pixelstats/UeventListener.cpp
+++ b/pixelstats/UeventListener.cpp
@@ -82,6 +82,7 @@ constexpr int32_t VID_MASK = 0xffff;
constexpr int32_t VID_GOOGLE = 0x18d1;
constexpr int32_t PID_OFFSET = 2;
constexpr int32_t PID_LENGTH = 4;
+constexpr uint32_t PID_P30 = 0x4f05;
bool UeventListener::ReadFileToInt(const std::string &path, int *val) {
return ReadFileToInt(path.c_str(), val);
@@ -449,13 +450,15 @@ void UeventListener::ReportTypeCPartnerId(const std::shared_ptr<IStats> &stats_c
return;
}
- // Upload data only for chargers
- if (((vid >> PRODUCT_TYPE_OFFSET) & PRODUCT_TYPE_MASK) != PRODUCT_TYPE_CHARGER) {
+ // Upload data only for Google VID
+ if ((VID_MASK & vid) != VID_GOOGLE) {
return;
}
- // Upload data only for Google VID
- if ((VID_MASK & vid) != VID_GOOGLE) {
+ // Upload data only for chargers unless for P30 PID where the product type
+ // isn't set to charger.
+ if ((((vid >> PRODUCT_TYPE_OFFSET) & PRODUCT_TYPE_MASK) != PRODUCT_TYPE_CHARGER) &&
+ (pid != PID_P30)) {
return;
}
@@ -541,7 +544,7 @@ bool UeventListener::ProcessUevent() {
devpath = cp;
} else if (!strncmp(cp, "SUBSYSTEM=", strlen("SUBSYSTEM="))) {
subsystem = cp;
- } else if (!strncmp(cp, "DEVTYPE=typec_partner", strlen("DEVTYPE=typec_partner"))) {
+ } else if (!strncmp(cp, kTypeCPartnerUevent.c_str(), kTypeCPartnerUevent.size())) {
collect_partner_id = true;
} else if (!strncmp(cp, "POWER_SUPPLY_NAME=wireless",
strlen("POWER_SUPPLY_NAME=wireless"))) {
@@ -589,6 +592,7 @@ UeventListener::UeventListener(const std::string audio_uevent, const std::string
kBatterySSOCPath(ssoc_details_path),
kUsbPortOverheatPath(overheat_path),
kChargeMetricsPath(charge_metrics_path),
+ kTypeCPartnerUevent(typec_partner_uevent_default),
kTypeCPartnerVidPath(typec_partner_vid_path),
kTypeCPartnerPidPath(typec_partner_pid_path),
kWirelessChargerPtmcUevent(""),
@@ -605,6 +609,9 @@ UeventListener::UeventListener(const struct UeventPaths &uevents_paths)
kChargeMetricsPath((uevents_paths.ChargeMetricsPath == nullptr)
? charge_metrics_path_default
: uevents_paths.ChargeMetricsPath),
+ kTypeCPartnerUevent((uevents_paths.TypeCPartnerUevent == nullptr)
+ ? typec_partner_uevent_default
+ : uevents_paths.TypeCPartnerUevent),
kTypeCPartnerVidPath((uevents_paths.TypeCPartnerVidPath == nullptr)
? typec_partner_vid_path_default
: uevents_paths.TypeCPartnerVidPath),
diff --git a/pixelstats/WirelessChargeStats.cpp b/pixelstats/WirelessChargeStats.cpp
index 6fd34c4a..79d62089 100644
--- a/pixelstats/WirelessChargeStats.cpp
+++ b/pixelstats/WirelessChargeStats.cpp
@@ -43,6 +43,10 @@ int WirelessChargeStats::TranslateSysModeToAtomValue(const int sys_mode) {
return PixelAtoms::ChargeStats::ADAPTER_TYPE_WPC_EPP;
case 3:
return PixelAtoms::ChargeStats::ADAPTER_TYPE_WPC_L7;
+ case 0xe0:
+ return PixelAtoms::ChargeStats::ADAPTER_TYPE_DL;
+ case 0xa0:
+ return PixelAtoms::ChargeStats::ADAPTER_TYPE_L7;
default:
return PixelAtoms::ChargeStats::ADAPTER_TYPE_WLC;
}
diff --git a/pixelstats/include/pixelstats/UeventListener.h b/pixelstats/include/pixelstats/UeventListener.h
index cd701e08..6505b02f 100644
--- a/pixelstats/include/pixelstats/UeventListener.h
+++ b/pixelstats/include/pixelstats/UeventListener.h
@@ -48,6 +48,7 @@ class UeventListener {
const char *const SsocDetailsPath;
const char *const OverheatPath;
const char *const ChargeMetricsPath;
+ const char *const TypeCPartnerUevent;
const char *const TypeCPartnerVidPath;
const char *const TypeCPartnerPidPath;
const char *const WirelessChargerPtmcUevent;
@@ -63,6 +64,7 @@ class UeventListener {
"/sys/class/typec/port0-partner/identity/id_header";
constexpr static const char *const typec_partner_pid_path_default =
"/sys/class/typec/port0-partner/identity/product";
+ constexpr static const char *const typec_partner_uevent_default = "DEVTYPE=typec_partner";
UeventListener(const std::string audio_uevent, const std::string ssoc_details_path = "",
const std::string overheat_path = overheat_path_default,
@@ -99,6 +101,7 @@ class UeventListener {
const std::string kBatterySSOCPath;
const std::string kUsbPortOverheatPath;
const std::string kChargeMetricsPath;
+ const std::string kTypeCPartnerUevent;
const std::string kTypeCPartnerVidPath;
const std::string kTypeCPartnerPidPath;
const std::string kWirelessChargerPtmcUevent;
diff --git a/power-libperfmgr/aidl/Power.cpp b/power-libperfmgr/aidl/Power.cpp
index f232ff4d..86076719 100644
--- a/power-libperfmgr/aidl/Power.cpp
+++ b/power-libperfmgr/aidl/Power.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#define LOG_TAG "powerhal-libperfmgr"
#include "Power.h"
@@ -26,10 +25,8 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <cutils/properties.h>
#include <utils/Log.h>
-#include <utils/Trace.h>
#include "PowerHintSession.h"
#include "PowerSessionManager.h"
@@ -97,7 +94,6 @@ Power::Power(std::shared_ptr<HintManager> hm, std::shared_ptr<DisplayLowPower> d
ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
LOG(DEBUG) << "Power setMode: " << toString(type) << " to: " << enabled;
- ATRACE_INT(toString(type).c_str(), enabled);
PowerSessionManager::getInstance()->updateHintMode(toString(type), enabled);
switch (type) {
case Mode::LOW_POWER:
@@ -196,7 +192,6 @@ ndk::ScopedAStatus Power::isModeSupported(Mode type, bool *_aidl_return) {
ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
LOG(DEBUG) << "Power setBoost: " << toString(type) << " duration: " << durationMs;
- ATRACE_INT(toString(type).c_str(), durationMs);
switch (type) {
case Boost::INTERACTION:
if (mVRModeOn || mSustainedPerfModeOn) {
diff --git a/power-libperfmgr/aidl/PowerExt.cpp b/power-libperfmgr/aidl/PowerExt.cpp
index 7fded31b..9481e4c8 100644
--- a/power-libperfmgr/aidl/PowerExt.cpp
+++ b/power-libperfmgr/aidl/PowerExt.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#define LOG_TAG "android.hardware.power-service.pixel.ext-libperfmgr"
#include "PowerExt.h"
@@ -29,7 +28,6 @@
#include <android-base/strings.h>
#include <utils/Log.h>
-#include <utils/Trace.h>
namespace aidl {
namespace google {
@@ -40,7 +38,6 @@ namespace pixel {
ndk::ScopedAStatus PowerExt::setMode(const std::string &mode, bool enabled) {
LOG(DEBUG) << "PowerExt setMode: " << mode << " to: " << enabled;
- ATRACE_INT(mode.c_str(), enabled);
if (enabled) {
mHintManager->DoHint(mode);
@@ -61,7 +58,6 @@ ndk::ScopedAStatus PowerExt::isModeSupported(const std::string &mode, bool *_aid
ndk::ScopedAStatus PowerExt::setBoost(const std::string &boost, int32_t durationMs) {
LOG(DEBUG) << "PowerExt setBoost: " << boost << " duration: " << durationMs;
- ATRACE_INT(boost.c_str(), durationMs);
if (durationMs > 0) {
mHintManager->DoHint(boost, std::chrono::milliseconds(durationMs));
diff --git a/power-libperfmgr/aidl/PowerHintSession.cpp b/power-libperfmgr/aidl/PowerHintSession.cpp
index 7a39dab0..359fcc3c 100644
--- a/power-libperfmgr/aidl/PowerHintSession.cpp
+++ b/power-libperfmgr/aidl/PowerHintSession.cpp
@@ -95,8 +95,8 @@ static double getDoubleProperty(const char *prop, double value) {
return value;
}
-static double sPidPOver = getDoubleProperty(kPowerHalAdpfPidPOver, 5.0);
-static double sPidPUnder = getDoubleProperty(kPowerHalAdpfPidPUnder, 3.0);
+static double sPidPOver = getDoubleProperty(kPowerHalAdpfPidPOver, 2.0);
+static double sPidPUnder = getDoubleProperty(kPowerHalAdpfPidPUnder, 1.0);
static double sPidI = getDoubleProperty(kPowerHalAdpfPidI, 0.001);
static double sPidDOver = getDoubleProperty(kPowerHalAdpfPidDOver, 500.0);
static double sPidDUnder = getDoubleProperty(kPowerHalAdpfPidDUnder, 0.0);
@@ -113,12 +113,12 @@ static const int64_t sPidIHighLimit =
static const int64_t sPidILowLimit =
(sPidI == 0) ? 0
: static_cast<int64_t>(::android::base::GetIntProperty<int64_t>(
- kPowerHalAdpfPidILowLimit, -120) /
+ kPowerHalAdpfPidILowLimit, -30) /
sPidI);
static const int32_t sUclampMinHighLimit =
- ::android::base::GetUintProperty<uint32_t>(kPowerHalAdpfUclampMinHighLimit, 512);
+ ::android::base::GetUintProperty<uint32_t>(kPowerHalAdpfUclampMinHighLimit, 384);
static const int32_t sUclampMinLowLimit =
- ::android::base::GetUintProperty<uint32_t>(kPowerHalAdpfUclampMinLowLimit, 0);
+ ::android::base::GetUintProperty<uint32_t>(kPowerHalAdpfUclampMinLowLimit, 2);
static const uint32_t sUclampMinGranularity =
::android::base::GetUintProperty<uint32_t>(kPowerHalAdpfUclampMinGranularity, 5);
static const int64_t sStaleTimeFactor =
@@ -146,6 +146,8 @@ PowerHintSession::PowerHintSession(int32_t tgid, int32_t uid, const std::vector<
ATRACE_INT(sz.c_str(), (int64_t)mDescriptor->duration.count());
sz = StringPrintf("adpf.%s-active", idstr.c_str());
ATRACE_INT(sz.c_str(), mDescriptor->is_active.load());
+ sz = StringPrintf("adpf.%s-stale", idstr.c_str());
+ ATRACE_INT(sz.c_str(), isStale());
}
PowerSessionManager::getInstance()->addPowerSession(this);
// init boost
@@ -286,12 +288,13 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
if (PowerHintMonitor::getInstance()->isRunning() && isStale()) {
+ mDescriptor->integral_error = std::max(sPidIInit, mDescriptor->integral_error);
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
- std::string sz = StringPrintf("adpf.%s-stale", idstr.c_str());
+ std::string sz = StringPrintf("adpf.%s-wakeup", idstr.c_str());
+ ATRACE_INT(sz.c_str(), mDescriptor->integral_error);
ATRACE_INT(sz.c_str(), 0);
}
- mDescriptor->integral_error = std::max(sPidIInit, mDescriptor->integral_error);
}
int64_t targetDurationNanos = (int64_t)mDescriptor->duration.count();
int64_t length = actualDurations.size();
@@ -325,6 +328,15 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
}
mDescriptor->previous_error = error;
}
+ if (ATRACE_ENABLED()) {
+ const std::string idstr = getIdString();
+ std::string sz = StringPrintf("adpf.%s-err", idstr.c_str());
+ ATRACE_INT(sz.c_str(), err_sum / (length - p_start));
+ sz = StringPrintf("adpf.%s-integral", idstr.c_str());
+ ATRACE_INT(sz.c_str(), mDescriptor->integral_error);
+ sz = StringPrintf("adpf.%s-derivative", idstr.c_str());
+ ATRACE_INT(sz.c_str(), derivative_sum / dt / (length - d_start));
+ }
int64_t pOut = static_cast<int64_t>((err_sum > 0 ? sPidPOver : sPidPUnder) * err_sum /
(length - p_start));
int64_t iOut = static_cast<int64_t>(sPidI * mDescriptor->integral_error);
@@ -332,6 +344,7 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
derivative_sum / dt / (length - d_start));
int64_t output = pOut + iOut + dOut;
+
if (ATRACE_ENABLED()) {
const std::string idstr = getIdString();
std::string sz = StringPrintf("adpf.%s-actl_last", idstr.c_str());
@@ -350,6 +363,10 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
ATRACE_INT(sz.c_str(), dOut);
sz = StringPrintf("adpf.%s-pid.output", idstr.c_str());
ATRACE_INT(sz.c_str(), output);
+ sz = StringPrintf("adpf.%s-stale", idstr.c_str());
+ ATRACE_INT(sz.c_str(), isStale());
+ sz = StringPrintf("adpf.%s-pid.overtime", idstr.c_str());
+ ATRACE_INT(sz.c_str(), err_sum > 0);
}
mDescriptor->update_count++;
@@ -357,8 +374,7 @@ ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
/* apply to all the threads in the group */
if (output != 0) {
- int next_min =
- std::min(sUclampMinHighLimit, mDescriptor->current_min + static_cast<int>(output));
+ int next_min = std::min(sUclampMinHighLimit, static_cast<int>(output));
next_min = std::max(sUclampMinLowLimit, next_min);
if (std::abs(mDescriptor->current_min - next_min) > sUclampMinGranularity) {
setUclamp(next_min);
@@ -429,6 +445,11 @@ void PowerHintSession::StaleHandler::updateStaleTimer() {
duration_cast<nanoseconds>(next - now).count(), this, NULL);
mIsMonitoringStale.store(true);
}
+ if (ATRACE_ENABLED()) {
+ const std::string idstr = mSession->getIdString();
+ std::string sz = StringPrintf("adpf.%s-stale", idstr.c_str());
+ ATRACE_INT(sz.c_str(), 0);
+ }
}
}
diff --git a/power-libperfmgr/disp-power/InteractionHandler.cpp b/power-libperfmgr/disp-power/InteractionHandler.cpp
index f6cd0e9a..bfdffa79 100644
--- a/power-libperfmgr/disp-power/InteractionHandler.cpp
+++ b/power-libperfmgr/disp-power/InteractionHandler.cpp
@@ -133,7 +133,6 @@ void InteractionHandler::PerfLock() {
if (!mHintManager->DoHint("INTERACTION")) {
ALOGE("%s: do hint INTERACTION failed", __func__);
}
- ATRACE_INT("interaction_lock", 1);
}
void InteractionHandler::PerfRel() {
@@ -141,7 +140,6 @@ void InteractionHandler::PerfRel() {
if (!mHintManager->EndHint("INTERACTION")) {
ALOGE("%s: end hint INTERACTION failed", __func__);
}
- ATRACE_INT("interaction_lock", 0);
}
void InteractionHandler::Acquire(int32_t duration) {
diff --git a/power-libperfmgr/libperfmgr/HintManager.cc b/power-libperfmgr/libperfmgr/HintManager.cc
index b25ce45e..bbfce08c 100644
--- a/power-libperfmgr/libperfmgr/HintManager.cc
+++ b/power-libperfmgr/libperfmgr/HintManager.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
#define LOG_TAG "libperfmgr"
#include "perfmgr/HintManager.h"
@@ -23,6 +24,7 @@
#include <android-base/stringprintf.h>
#include <json/reader.h>
#include <json/value.h>
+#include <utils/Trace.h>
#include <inttypes.h>
#include <algorithm>
@@ -86,6 +88,8 @@ void HintManager::DoHintStatus(const std::string &hint_type, std::chrono::millis
std::lock_guard<std::mutex> lock(actions_.at(hint_type).status->mutex);
actions_.at(hint_type).status->stats.count.fetch_add(1);
auto now = std::chrono::steady_clock::now();
+ ATRACE_INT(hint_type.c_str(), (timeout_ms == kMilliSecondZero) ? std::numeric_limits<int>::max()
+ : timeout_ms.count());
if (now > actions_.at(hint_type).status->end_time) {
actions_.at(hint_type).status->stats.duration_ms.fetch_add(
std::chrono::duration_cast<std::chrono::milliseconds>(
@@ -102,6 +106,7 @@ void HintManager::EndHintStatus(const std::string &hint_type) {
std::lock_guard<std::mutex> lock(actions_.at(hint_type).status->mutex);
// Update HintStats if the hint ends earlier than expected end_time
auto now = std::chrono::steady_clock::now();
+ ATRACE_INT(hint_type.c_str(), 0);
if (now < actions_.at(hint_type).status->end_time) {
actions_.at(hint_type).status->stats.duration_ms.fetch_add(
std::chrono::duration_cast<std::chrono::milliseconds>(
diff --git a/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h b/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h
index 747e5c44..8943c98c 100644
--- a/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h
+++ b/power-libperfmgr/libperfmgr/include/perfmgr/HintManager.h
@@ -41,7 +41,7 @@ struct HintStats {
struct HintStatus {
const std::chrono::milliseconds max_timeout;
HintStatus() : max_timeout(std::chrono::milliseconds(0)) {}
- HintStatus(std::chrono::milliseconds max_timeout)
+ explicit HintStatus(std::chrono::milliseconds max_timeout)
: max_timeout(max_timeout),
start_time(std::chrono::steady_clock::time_point::min()),
end_time(std::chrono::steady_clock::time_point::min()) {}
@@ -58,7 +58,7 @@ struct HintStatus {
enum class HintActionType { Node, DoHint, EndHint, MaskHint };
struct HintAction {
- HintAction(HintActionType t, std::string v) : type(t), value(v) {}
+ HintAction(HintActionType t, const std::string &v) : type(t), value(v) {}
HintActionType type;
std::string value;
};
@@ -80,7 +80,7 @@ struct Hint {
class HintManager {
public:
HintManager(sp<NodeLooperThread> nm, const std::unordered_map<std::string, Hint> &actions)
- : nm_(std::move(nm)), actions_(std::move(actions)) {}
+ : nm_(std::move(nm)), actions_(actions) {}
~HintManager() {
if (nm_.get() != nullptr) nm_->Stop();
}
diff --git a/power-libperfmgr/libperfmgr/include/perfmgr/RequestGroup.h b/power-libperfmgr/libperfmgr/include/perfmgr/RequestGroup.h
index 698a8216..f3035887 100644
--- a/power-libperfmgr/libperfmgr/include/perfmgr/RequestGroup.h
+++ b/power-libperfmgr/libperfmgr/include/perfmgr/RequestGroup.h
@@ -36,8 +36,8 @@ using ReqTime = std::chrono::time_point<std::chrono::steady_clock>;
// that hint.
class RequestGroup {
public:
- RequestGroup(std::string request_value) // NOLINT(runtime/explicit)
- : request_value_(std::move(request_value)) {}
+ RequestGroup(const std::string &request_value) // NOLINT(runtime/explicit)
+ : request_value_(request_value) {}
// Remove expired request in the map and return true when request_map_ is
// not empty, false when request_map_ is empty; also update expire_time with
diff --git a/thermal/Thermal.cpp b/thermal/Thermal.cpp
index b63ca2c2..ec7d3fab 100644
--- a/thermal/Thermal.cpp
+++ b/thermal/Thermal.cpp
@@ -263,10 +263,13 @@ void Thermal::sendThermalChangedCallback(const Temperature_2_0 &t) {
[&](const CallbackSetting &c) {
if (!c.is_filter_type || t.type == c.type) {
Return<void> ret = c.callback->notifyThrottling(t);
- return !ret.isOk();
+ if (!ret.isOk()) {
+ LOG(ERROR) << "a Thermal callback is dead, removed from "
+ "callback list.";
+ return true;
+ }
+ return false;
}
- LOG(ERROR)
- << "a Thermal callback is dead, removed from callback list.";
return false;
}),
callbacks_.end());
diff --git a/vibrator/common/HardwareBase.h b/vibrator/common/HardwareBase.h
index 7612fb35..214c0565 100644
--- a/vibrator/common/HardwareBase.h
+++ b/vibrator/common/HardwareBase.h
@@ -71,10 +71,14 @@ class HwApiBase {
template <typename T>
bool get(T *value, std::istream *stream);
template <typename T>
+ bool getStr(T *value, std::istream *stream);
+ template <typename T>
bool set(const T &value, std::ostream *stream);
template <typename T>
bool poll(const T &value, std::istream *stream);
template <typename T>
+ bool pollStr(const T &value, std::istream *stream, const int32_t timeout = -1);
+ template <typename T>
void record(const char *func, const T &value, const std::ios *stream);
private:
@@ -109,6 +113,24 @@ bool HwApiBase::get(T *value, std::istream *stream) {
}
template <typename T>
+bool HwApiBase::getStr(T *value, std::istream *stream) {
+ ATRACE_NAME("HwApi::getStr");
+ std::scoped_lock ioLock{mIoMutex};
+ bool ret;
+ stream->seekg(0);
+ utils::unpack(*stream, value);
+ if (!(ret = !!*stream)) {
+ ALOGE("Failed to read %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno));
+ }
+ if (!(ret = stream->eof())) {
+ ALOGE("Invalid %s !", mNames[stream].c_str());
+ }
+ stream->clear();
+ HWAPI_RECORD(*value, stream);
+ return ret;
+}
+
+template <typename T>
bool HwApiBase::set(const T &value, std::ostream *stream) {
ATRACE_NAME("HwApi::set");
using utils::operator<<;
@@ -149,6 +171,41 @@ bool HwApiBase::poll(const T &value, std::istream *stream) {
}
template <typename T>
+bool HwApiBase::pollStr(const T &value, std::istream *stream, const int32_t timeoutMs) {
+ ATRACE_NAME("HwApi::pollStr");
+ auto path = mPathPrefix + mNames[stream];
+ unique_fd fileFd{::open(path.c_str(), O_RDONLY)};
+ unique_fd epollFd{epoll_create(1)};
+ epoll_event event = {
+ .events = EPOLLPRI | EPOLLET,
+ };
+ T actual;
+ bool ret;
+ int epollRet;
+
+ if (timeoutMs < -1) {
+ ALOGE("Invalid polling timeout!");
+ return false;
+ }
+
+ if (epoll_ctl(epollFd, EPOLL_CTL_ADD, fileFd, &event)) {
+ ALOGE("Failed to poll string %s (%d): %s", mNames[stream].c_str(), errno, strerror(errno));
+ return false;
+ }
+
+ while ((ret = getStr(&actual, stream)) && (actual != value)) {
+ epollRet = epoll_wait(epollFd, &event, 1, timeoutMs);
+ if (epollRet <= 0) {
+ ALOGE("Polling error or timeout! (%d)", epollRet);
+ return false;
+ }
+ }
+
+ HWAPI_RECORD(value, stream);
+ return ret;
+}
+
+template <typename T>
void HwApiBase::record(const char *func, const T &value, const std::ios *stream) {
std::lock_guard<std::mutex> lock(mRecordsMutex);
mRecords.emplace_back(std::make_unique<Record<T>>(func, value, stream));
diff --git a/vibrator/cs40l25/Android.bp b/vibrator/cs40l25/Android.bp
index 6db96f44..80436f73 100644
--- a/vibrator/cs40l25/Android.bp
+++ b/vibrator/cs40l25/Android.bp
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2017 The Android Open Source Project
+// 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.
@@ -17,11 +17,46 @@ package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
+soong_config_module_type {
+ name: "haptics_feature_cc_defaults",
+ module_type: "cc_defaults",
+ config_namespace: "haptics",
+ variables: [
+ "actuator_model",
+ ],
+ properties: ["cflags"],
+}
+
+soong_config_string_variable {
+ name: "actuator_model",
+ values: [
+ "luxshare_ict_081545",
+ "luxshare_ict_lt_xlra1906d",
+ ],
+}
+
+haptics_feature_cc_defaults {
+ name: "haptics_feature_defaults",
+ soong_config_variables: {
+ actuator_model: {
+ luxshare_ict_081545: {
+ cflags: [
+ "-DLUXSHARE_ICT_081545",
+ ],
+ },
+ luxshare_ict_lt_xlra1906d: {
+ cflags: [
+ "-DLUXSHARE_ICT_LT_XLRA1906D",
+ ],
+ },
+ },
+ },
+}
+
cc_defaults {
name: "android.hardware.vibrator-defaults.cs40l25",
cflags: [
"-DATRACE_TAG=(ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)",
- "-DLOG_TAG=\"android.hardware.vibrator-cs40l25\"",
],
shared_libs: [
"libbinder",
@@ -50,7 +85,10 @@ cc_defaults {
cc_library_shared {
name: "android.hardware.vibrator-impl.cs40l25",
- defaults: ["VibratorHalCs40l25BinaryDefaults"],
+ defaults: [
+ "VibratorHalCs40l25BinaryDefaults",
+ "haptics_feature_defaults",
+ ],
srcs: ["Vibrator.cpp"],
include_dirs: [
"external/tinyalsa/include",
@@ -78,6 +116,9 @@ cc_binary {
"libcutils",
"libtinyalsa",
],
+ cflags: [
+ "-DLOG_TAG=\"android.hardware.vibrator-cs40l25\"",
+ ],
proprietary: true,
}
@@ -95,6 +136,50 @@ cc_binary {
"libcutils",
"libtinyalsa",
],
- cflags: ["-DVIBRATOR_NAME=\"dual\""],
+ cflags: [
+ "-DVIBRATOR_NAME=\"dual\"",
+ "-DLOG_TAG=\"android.hardware.vibrator-cs40l25-dual\"",
+ ],
+ proprietary: true,
+}
+
+cc_binary {
+ name: "android.hardware.vibrator-service.cs40l25-specific-revision",
+ defaults: ["VibratorHalCs40l25BinaryDefaults"],
+ init_rc: ["android.hardware.vibrator-service.cs40l25-specific-revision.rc"],
+ vintf_fragments: ["android.hardware.vibrator-service.cs40l25.xml"],
+ srcs: ["service.cpp"],
+ include_dirs: [
+ "external/tinyalsa/include"
+ ],
+ shared_libs: [
+ "android.hardware.vibrator-impl.cs40l25",
+ "libcutils",
+ "libtinyalsa",
+ ],
+ cflags: [
+ "-DLOG_TAG=\"android.hardware.vibrator-cs40l25\"",
+ ],
+ proprietary: true,
+}
+
+cc_binary {
+ name: "android.hardware.vibrator-service.cs40l25-specific-revision-dual",
+ defaults: ["VibratorHalCs40l25BinaryDefaults"],
+ init_rc: ["android.hardware.vibrator-service.cs40l25-specific-revision-dual.rc"],
+ vintf_fragments: ["android.hardware.vibrator-service.cs40l25-dual.xml"],
+ srcs: ["service.cpp"],
+ include_dirs: [
+ "external/tinyalsa/include"
+ ],
+ shared_libs: [
+ "android.hardware.vibrator-impl.cs40l25",
+ "libcutils",
+ "libtinyalsa",
+ ],
+ cflags: [
+ "-DVIBRATOR_NAME=\"dual\"",
+ "-DLOG_TAG=\"android.hardware.vibrator-cs40l25-dual\"",
+ ],
proprietary: true,
}
diff --git a/vibrator/cs40l25/Hardware.h b/vibrator/cs40l25/Hardware.h
index f74fe931..d1883e53 100644
--- a/vibrator/cs40l25/Hardware.h
+++ b/vibrator/cs40l25/Hardware.h
@@ -48,7 +48,6 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
open("device/clab_enable", &mClabEnable);
open("device/available_pwle_segments", &mAvailablePwleSegments);
open("device/pwle", &mPwle);
- open("device/pwle_ramp_down", &mPwleRampDown);
}
bool setF0(uint32_t value) override { return set(value, &mF0); }
@@ -77,7 +76,6 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
bool getAvailablePwleSegments(uint32_t *value) override { return get(value, &mAvailablePwleSegments); }
bool hasPwle() override { return has(mPwle); }
bool setPwle(std::string value) override { return set(value, &mPwle); }
- bool setPwleRampDown(uint32_t value) override { return set(value, &mPwleRampDown); }
void debug(int fd) override { HwApiBase::debug(fd); }
private:
@@ -103,7 +101,6 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
std::ofstream mClabEnable;
std::ifstream mAvailablePwleSegments;
std::ofstream mPwle;
- std::ofstream mPwleRampDown;
};
class HwCal : public Vibrator::HwCal, private HwCalBase {
diff --git a/vibrator/cs40l25/Vibrator.cpp b/vibrator/cs40l25/Vibrator.cpp
index 8110cb37..002fccf9 100644
--- a/vibrator/cs40l25/Vibrator.cpp
+++ b/vibrator/cs40l25/Vibrator.cpp
@@ -101,8 +101,6 @@ static constexpr float PWLE_FREQUENCY_MIN_HZ = 0.25;
static constexpr float PWLE_FREQUENCY_MAX_HZ = 1023.75;
static constexpr float PWLE_BW_MAP_SIZE =
1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
-static constexpr float RAMP_DOWN_CONSTANT = 1048.576;
-static constexpr float RAMP_DOWN_TIME_MS = 50.0;
static struct pcm_config haptic_nohost_config = {
.channels = 1,
@@ -119,13 +117,31 @@ static uint8_t amplitudeToScale(float amplitude, float maximum) {
// Discrete points of frequency:max_level pairs as recommended by the document
// [R4O6] Max. Allowable Chirp Levels (go/r4o6-max-chirp-levels) around resonant frequency
+#if defined(LUXSHARE_ICT_081545)
static std::map<float, float> discretePwleMaxLevels = {{120.0, 0.4}, {130.0, 0.31}, {140.0, 0.14},
{145.0, 0.09}, {150.0, 0.15}, {160.0, 0.35},
{170.0, 0.4}};
+// Discrete points of frequency:max_level pairs as recommended by the document
+// [P7] Max. Allowable Chirp Levels (go/p7-max-chirp-levels) around resonant frequency
+#elif defined(LUXSHARE_ICT_LT_XLRA1906D)
+static std::map<float, float> discretePwleMaxLevels = {{145.0, 0.38}, {150.0, 0.35}, {160.0, 0.35},
+ {170.0, 0.15}, {180.0, 0.35}, {190.0, 0.35},
+ {200.0, 0.38}};
+#else
+static std::map<float, float> discretePwleMaxLevels = {};
+#endif
// Initialize all limits to 0.4 according to the document [R4O6] Max. Allowable Chirp Levels
// (go/r4o6-max-chirp-levels)
+#if defined(LUXSHARE_ICT_081545)
std::vector<float> pwleMaxLevelLimitMap(PWLE_BW_MAP_SIZE, 0.4);
+// Initialize all limits to 0.38 according to the document [P7] Max. Allowable Chirp Levels
+// (go/p7-max-chirp-levels)
+#elif defined(LUXSHARE_ICT_LT_XLRA1906D)
+std::vector<float> pwleMaxLevelLimitMap(PWLE_BW_MAP_SIZE, 0.38);
+#else
+std::vector<float> pwleMaxLevelLimitMap(PWLE_BW_MAP_SIZE, 1.0);
+#endif
void Vibrator::createPwleMaxLevelLimitMap() {
int32_t capabilities;
@@ -133,6 +149,17 @@ void Vibrator::createPwleMaxLevelLimitMap() {
if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
std::map<float, float>::iterator itr0, itr1;
+ if (discretePwleMaxLevels.empty()) {
+ return;
+ }
+ if (discretePwleMaxLevels.size() == 1) {
+ itr0 = discretePwleMaxLevels.begin();
+ float pwleMaxLevelLimitMapIdx =
+ (itr0->first - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ;
+ pwleMaxLevelLimitMap[pwleMaxLevelLimitMapIdx] = itr0->second;
+ return;
+ }
+
itr0 = discretePwleMaxLevels.begin();
itr1 = std::next(itr0, 1);
@@ -142,7 +169,7 @@ void Vibrator::createPwleMaxLevelLimitMap() {
float x1 = itr1->first;
float y1 = itr1->second;
float pwleMaxLevelLimitMapIdx =
- (itr0->first - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ;
+ (itr0->first - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ;
for (float xp = x0; xp < (x1 + PWLE_FREQUENCY_RESOLUTION_HZ);
xp += PWLE_FREQUENCY_RESOLUTION_HZ) {
@@ -235,7 +262,6 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
createPwleMaxLevelLimitMap();
mIsUnderExternalControl = false;
- setPwleRampDown();
}
ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
@@ -1150,22 +1176,6 @@ fail:
return false;
}
-void Vibrator::setPwleRampDown() {
- // The formula for calculating the ramp down coefficient to be written into
- // pwle_ramp_down is as follows:
- // Crd = 1048.576 / Trd
- // where Trd is the desired ramp down time in seconds
- // pwle_ramp_down accepts only 24 bit integers values
-
- const float seconds = RAMP_DOWN_TIME_MS / 1000;
- const auto ramp_down_coefficient = static_cast<uint32_t>(RAMP_DOWN_CONSTANT / seconds);
-
- if (!mHwApi->setPwleRampDown(ramp_down_coefficient)) {
- ALOGE("Failed to write \"%d\" to pwle_ramp_down (%d): %s", ramp_down_coefficient, errno,
- strerror(errno));
- }
-}
-
} // namespace vibrator
} // namespace hardware
} // namespace android
diff --git a/vibrator/cs40l25/Vibrator.h b/vibrator/cs40l25/Vibrator.h
index 88f7abd8..08a115e4 100644
--- a/vibrator/cs40l25/Vibrator.h
+++ b/vibrator/cs40l25/Vibrator.h
@@ -101,9 +101,6 @@ class Vibrator : public BnVibrator {
// Specifies piecewise-linear specifications to generate complex
// waveforms.
virtual bool setPwle(std::string value) = 0;
- // Specifies the coefficient required for a ramp down when a waveform
- // ends
- virtual bool setPwleRampDown(uint32_t value) = 0;
// Emit diagnostic information to the given file.
virtual void debug(int fd) = 0;
};
@@ -203,7 +200,6 @@ class Vibrator : public BnVibrator {
bool hasHapticAlsaDevice();
bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device);
void createPwleMaxLevelLimitMap();
- void setPwleRampDown();
std::unique_ptr<HwApi> mHwApi;
std::unique_ptr<HwCal> mHwCal;
diff --git a/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-dual.rc b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-dual.rc
index 7e9ff688..f9ed341c 100644
--- a/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-dual.rc
+++ b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-dual.rc
@@ -13,6 +13,7 @@ on boot
chown system system /sys/class/leds/vibrator_1/state
chown system system /sys/class/leds/vibrator_1/device/asp_enable
+ chown system system /sys/class/leds/vibrator_1/device/available_pwle_segments
chown system system /sys/class/leds/vibrator_1/device/clab_enable
chown system system /sys/class/leds/vibrator_1/device/comp_enable
chown system system /sys/class/leds/vibrator_1/device/cp_dig_scale
@@ -35,6 +36,7 @@ on boot
chown system system /sys/class/leds/vibrator_1/device/heartbeat
chown system system /sys/class/leds/vibrator_1/device/hw_reset
chown system system /sys/class/leds/vibrator_1/device/num_waves
+ chown system system /sys/class/leds/vibrator_1/device/pwle
chown system system /sys/class/leds/vibrator_1/device/q_stored
chown system system /sys/class/leds/vibrator_1/device/redc_comp_enable
chown system system /sys/class/leds/vibrator_1/device/redc_stored
@@ -56,6 +58,7 @@ service vendor.vibrator.cs40l25-dual /vendor/bin/hw/android.hardware.vibrator-se
setenv HWAPI_PATH_PREFIX /sys/class/leds/vibrator_1/
setenv HWAPI_DEBUG_PATHS "
device/asp_enable
+ device/available_pwle_segments
device/clab_enable
device/f0_stored
device/f0_offset
@@ -66,6 +69,7 @@ service vendor.vibrator.cs40l25-dual /vendor/bin/hw/android.hardware.vibrator-se
device/gpio1_rise_index
device/heartbeat
device/num_waves
+ device/pwle
device/q_stored
device/redc_stored
state
diff --git a/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-specific-revision-dual.rc b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-specific-revision-dual.rc
new file mode 100644
index 00000000..8b7cd72f
--- /dev/null
+++ b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-specific-revision-dual.rc
@@ -0,0 +1,78 @@
+on boot && property:ro.boot.revision=PROTO1.0
+ wait /sys/class/leds/vibrator_1/device
+
+ mkdir /mnt/vendor/persist/haptics 0770 system system
+ chmod 770 /mnt/vendor/persist/haptics
+ chmod 440 /mnt/vendor/persist/haptics/cs40l25a_dual.cal
+ chown system system /mnt/vendor/persist/haptics
+ chown system system /mnt/vendor/persist/haptics/cs40l25a_dual.cal
+
+ chown system system /sys/class/leds/vibrator_1/activate
+ chown system system /sys/class/leds/vibrator_1/brightness
+ chown system system /sys/class/leds/vibrator_1/duration
+ chown system system /sys/class/leds/vibrator_1/state
+
+ chown system system /sys/class/leds/vibrator_1/device/asp_enable
+ chown system system /sys/class/leds/vibrator_1/device/available_pwle_segments
+ chown system system /sys/class/leds/vibrator_1/device/clab_enable
+ chown system system /sys/class/leds/vibrator_1/device/comp_enable
+ chown system system /sys/class/leds/vibrator_1/device/cp_dig_scale
+ chown system system /sys/class/leds/vibrator_1/device/cp_trigger_duration
+ chown system system /sys/class/leds/vibrator_1/device/cp_trigger_index
+ chown system system /sys/class/leds/vibrator_1/device/cp_trigger_q_sub
+ chown system system /sys/class/leds/vibrator_1/device/cp_trigger_queue
+ chown system system /sys/class/leds/vibrator_1/device/dig_scale
+ chown system system /sys/class/leds/vibrator_1/device/exc_enable
+ chown system system /sys/class/leds/vibrator_1/device/f0_stored
+ chown system system /sys/class/leds/vibrator_1/device/f0_offset
+ chown system system /sys/class/leds/vibrator_1/device/fw_rev
+ chown system system /sys/class/leds/vibrator_1/device/gpio1_enable
+ chown system system /sys/class/leds/vibrator_1/device/gpio1_fall_dig_scale
+ chown system system /sys/class/leds/vibrator_1/device/gpio1_fall_index
+ chown system system /sys/class/leds/vibrator_1/device/gpio1_rise_dig_scale
+ chown system system /sys/class/leds/vibrator_1/device/gpio1_rise_index
+ chown system system /sys/class/leds/vibrator_1/device/gpio_event
+ chown system system /sys/class/leds/vibrator_1/device/gpio_trigger
+ chown system system /sys/class/leds/vibrator_1/device/heartbeat
+ chown system system /sys/class/leds/vibrator_1/device/hw_reset
+ chown system system /sys/class/leds/vibrator_1/device/num_waves
+ chown system system /sys/class/leds/vibrator_1/device/pwle
+ chown system system /sys/class/leds/vibrator_1/device/q_stored
+ chown system system /sys/class/leds/vibrator_1/device/redc_comp_enable
+ chown system system /sys/class/leds/vibrator_1/device/redc_stored
+ chown system system /sys/class/leds/vibrator_1/device/standby_timeout
+ chown system system /sys/class/leds/vibrator_1/device/vbatt_max
+ chown system system /sys/class/leds/vibrator_1/device/vbatt_min
+ chown system system /sys/class/leds/vibrator_1/device/vibe_state
+
+ enable vendor.vibrator.cs40l25-dual
+
+service vendor.vibrator.cs40l25-dual /vendor/bin/hw/android.hardware.vibrator-service.cs40l25-specific-revision-dual
+ class hal
+ user system
+ group system
+
+ setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
+ setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l25a_dual.cal
+
+ setenv HWAPI_PATH_PREFIX /sys/class/leds/vibrator_1/
+ setenv HWAPI_DEBUG_PATHS "
+ device/asp_enable
+ device/available_pwle_segments
+ device/clab_enable
+ device/f0_stored
+ device/f0_offset
+ device/fw_rev
+ device/gpio1_fall_dig_scale
+ device/gpio1_fall_index
+ device/gpio1_rise_dig_scale
+ device/gpio1_rise_index
+ device/heartbeat
+ device/num_waves
+ device/pwle
+ device/q_stored
+ device/redc_stored
+ state
+ "
+
+ disabled
diff --git a/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-specific-revision.rc b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-specific-revision.rc
new file mode 100644
index 00000000..29adb894
--- /dev/null
+++ b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25-specific-revision.rc
@@ -0,0 +1,75 @@
+on boot && property:ro.boot.revision=PROTO1.0
+ wait /sys/class/leds/vibrator/device
+
+ mkdir /mnt/vendor/persist/haptics 0770 system system
+ chmod 770 /mnt/vendor/persist/haptics
+ chmod 440 /mnt/vendor/persist/haptics/cs40l25a.cal
+ chown system system /mnt/vendor/persist/haptics
+ chown system system /mnt/vendor/persist/haptics/cs40l25a.cal
+
+ chown system system /sys/class/leds/vibrator/activate
+ chown system system /sys/class/leds/vibrator/brightness
+ chown system system /sys/class/leds/vibrator/duration
+ chown system system /sys/class/leds/vibrator/state
+
+ chown system system /sys/class/leds/vibrator/device/asp_enable
+ chown system system /sys/class/leds/vibrator/device/available_pwle_segments
+ chown system system /sys/class/leds/vibrator/device/clab_enable
+ chown system system /sys/class/leds/vibrator/device/comp_enable
+ chown system system /sys/class/leds/vibrator/device/cp_dig_scale
+ chown system system /sys/class/leds/vibrator/device/cp_trigger_duration
+ chown system system /sys/class/leds/vibrator/device/cp_trigger_index
+ chown system system /sys/class/leds/vibrator/device/cp_trigger_q_sub
+ chown system system /sys/class/leds/vibrator/device/cp_trigger_queue
+ chown system system /sys/class/leds/vibrator/device/dig_scale
+ chown system system /sys/class/leds/vibrator/device/exc_enable
+ chown system system /sys/class/leds/vibrator/device/f0_stored
+ chown system system /sys/class/leds/vibrator/device/f0_offset
+ chown system system /sys/class/leds/vibrator/device/fw_rev
+ chown system system /sys/class/leds/vibrator/device/gpio1_fall_dig_scale
+ chown system system /sys/class/leds/vibrator/device/gpio1_fall_index
+ chown system system /sys/class/leds/vibrator/device/gpio1_rise_dig_scale
+ chown system system /sys/class/leds/vibrator/device/gpio1_rise_index
+ chown system system /sys/class/leds/vibrator/device/heartbeat
+ chown system system /sys/class/leds/vibrator/device/hw_reset
+ chown system system /sys/class/leds/vibrator/device/num_waves
+ chown system system /sys/class/leds/vibrator/device/pwle
+ chown system system /sys/class/leds/vibrator/device/q_stored
+ chown system system /sys/class/leds/vibrator/device/redc_comp_enable
+ chown system system /sys/class/leds/vibrator/device/redc_stored
+ chown system system /sys/class/leds/vibrator/device/standby_timeout
+ chown system system /sys/class/leds/vibrator/device/vbatt_max
+ chown system system /sys/class/leds/vibrator/device/vbatt_min
+ chown system system /sys/class/leds/vibrator/device/vibe_state
+
+ enable vendor.vibrator.cs40l25
+
+service vendor.vibrator.cs40l25 /vendor/bin/hw/android.hardware.vibrator-service.cs40l25-specific-revision
+ class hal
+ user system
+ group system
+
+ setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
+ setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l25a.cal
+
+ setenv HWAPI_PATH_PREFIX /sys/class/leds/vibrator/
+ setenv HWAPI_DEBUG_PATHS "
+ device/asp_enable
+ device/available_pwle_segments
+ device/clab_enable
+ device/f0_stored
+ device/f0_offset
+ device/fw_rev
+ device/gpio1_fall_dig_scale
+ device/gpio1_fall_index
+ device/gpio1_rise_dig_scale
+ device/gpio1_rise_index
+ device/heartbeat
+ device/num_waves
+ device/pwle
+ device/q_stored
+ device/redc_stored
+ state
+ "
+
+ disabled
diff --git a/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc
index f373d31f..3f28d81f 100644
--- a/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc
+++ b/vibrator/cs40l25/android.hardware.vibrator-service.cs40l25.rc
@@ -34,7 +34,6 @@ on boot
chown system system /sys/class/leds/vibrator/device/hw_reset
chown system system /sys/class/leds/vibrator/device/num_waves
chown system system /sys/class/leds/vibrator/device/pwle
- chown system system /sys/class/leds/vibrator/device/pwle_ramp_down
chown system system /sys/class/leds/vibrator/device/q_stored
chown system system /sys/class/leds/vibrator/device/redc_comp_enable
chown system system /sys/class/leds/vibrator/device/redc_stored
@@ -68,7 +67,6 @@ service vendor.vibrator.cs40l25 /vendor/bin/hw/android.hardware.vibrator-service
device/heartbeat
device/num_waves
device/pwle
- device/pwle_ramp_down
device/q_stored
device/redc_stored
state
diff --git a/vibrator/cs40l25/device-stereo-specific-revision.mk b/vibrator/cs40l25/device-stereo-specific-revision.mk
new file mode 100644
index 00000000..485de0cb
--- /dev/null
+++ b/vibrator/cs40l25/device-stereo-specific-revision.mk
@@ -0,0 +1,6 @@
+PRODUCT_PACKAGES += \
+ android.hardware.vibrator-service.cs40l25-specific-revision \
+ android.hardware.vibrator-service.cs40l25-specific-revision-dual \
+
+BOARD_SEPOLICY_DIRS += \
+ hardware/google/pixel-sepolicy/vibrator/cs40l25 \
diff --git a/vibrator/cs40l25/device-stereo.mk b/vibrator/cs40l25/device-stereo.mk
index fe69b635..8a030974 100644
--- a/vibrator/cs40l25/device-stereo.mk
+++ b/vibrator/cs40l25/device-stereo.mk
@@ -3,5 +3,4 @@ PRODUCT_PACKAGES += \
android.hardware.vibrator-service.cs40l25-dual \
BOARD_SEPOLICY_DIRS += \
- hardware/google/pixel-sepolicy/vibrator/common \
hardware/google/pixel-sepolicy/vibrator/cs40l25 \
diff --git a/vibrator/cs40l25/tests/mocks.h b/vibrator/cs40l25/tests/mocks.h
index 8f2c672e..2c69e27b 100644
--- a/vibrator/cs40l25/tests/mocks.h
+++ b/vibrator/cs40l25/tests/mocks.h
@@ -49,7 +49,6 @@ class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
MOCK_METHOD1(getAvailablePwleSegments, bool(uint32_t *value));
MOCK_METHOD0(hasPwle, bool());
MOCK_METHOD1(setPwle, bool(std::string value));
- MOCK_METHOD1(setPwleRampDown, bool(uint32_t value));
MOCK_METHOD1(debug, void(int fd));
~MockApi() override { destructor(); };
diff --git a/vibrator/cs40l25/tests/test-hwapi.cpp b/vibrator/cs40l25/tests/test-hwapi.cpp
index a339207b..7f2ae01b 100644
--- a/vibrator/cs40l25/tests/test-hwapi.cpp
+++ b/vibrator/cs40l25/tests/test-hwapi.cpp
@@ -55,7 +55,6 @@ class HwApiTest : public Test {
"device/num_waves",
"device/available_pwle_segments",
"device/pwle",
- "device/pwle_ramp_down",
};
public:
@@ -320,8 +319,6 @@ INSTANTIATE_TEST_CASE_P(
&Vibrator::HwApi::setGpioRiseIndex),
SetUint32Test::MakeParam("device/gpio1_rise_dig_scale",
&Vibrator::HwApi::setGpioRiseScale),
- SetUint32Test::MakeParam("device/pwle_ramp_down",
- &Vibrator::HwApi::setPwleRampDown),
}),
SetUint32Test::PrintParam);
diff --git a/vibrator/cs40l26/Android.bp b/vibrator/cs40l26/Android.bp
new file mode 100644
index 00000000..df78ae8e
--- /dev/null
+++ b/vibrator/cs40l26/Android.bp
@@ -0,0 +1,170 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+ name: "android.hardware.vibrator-defaults.cs40l26",
+ cflags: [
+ "-DATRACE_TAG=(ATRACE_TAG_VIBRATOR | ATRACE_TAG_HAL)",
+ "-DLOG_TAG=\"android.hardware.vibrator-cs40l26\"",
+ ],
+ shared_libs: [
+ "libbinder",
+ ],
+}
+
+cc_defaults {
+ name: "VibratorHalCs40l26BinaryDefaults",
+ defaults: [
+ "PixelVibratorBinaryDefaults",
+ "android.hardware.vibrator-defaults.cs40l26",
+ ],
+}
+
+cc_defaults {
+ name: "VibratorHalCs40l26TestDefaults",
+ defaults: [
+ "PixelVibratorTestDefaults",
+ "android.hardware.vibrator-defaults.cs40l26",
+ ],
+ shared_libs: ["android.hardware.vibrator-impl.cs40l26"],
+ include_dirs: [
+ "external/tinyalsa/include",
+ ],
+}
+
+cc_library_shared {
+ name: "android.hardware.vibrator-impl.cs40l26",
+ defaults: ["VibratorHalCs40l26BinaryDefaults"],
+ srcs: ["Vibrator.cpp"],
+ include_dirs: [
+ "external/tinyalsa/include",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libtinyalsa",
+ ],
+ export_include_dirs: ["."],
+ vendor_available: true,
+ visibility: [":__subpackages__"],
+}
+
+cc_binary {
+ name: "android.hardware.vibrator-service.cs40l26",
+ defaults: ["VibratorHalCs40l26BinaryDefaults"],
+ init_rc: ["android.hardware.vibrator-service.cs40l26.rc"],
+ vintf_fragments: ["android.hardware.vibrator-service.cs40l26.xml"],
+ srcs: ["service.cpp"],
+ include_dirs: [
+ "external/tinyalsa/include"
+ ],
+ shared_libs: [
+ "android.hardware.vibrator-impl.cs40l26",
+ "libcutils",
+ "libtinyalsa",
+ ],
+ proprietary: true,
+}
+
+cc_binary {
+ name: "android.hardware.vibrator-service.cs40l26-dual",
+ defaults: ["VibratorHalCs40l26BinaryDefaults"],
+ init_rc: ["android.hardware.vibrator-service.cs40l26-dual.rc"],
+ vintf_fragments: ["android.hardware.vibrator-service.cs40l26-dual.xml"],
+ srcs: ["service.cpp"],
+ include_dirs: [
+ "external/tinyalsa/include"
+ ],
+ shared_libs: [
+ "android.hardware.vibrator-impl.cs40l26",
+ "libcutils",
+ "libtinyalsa",
+ ],
+ cflags: ["-DVIBRATOR_NAME=\"dual\""],
+ proprietary: true,
+}
+
+cc_binary {
+ name: "android.hardware.vibrator-service.cs40l26-evt",
+ defaults: ["VibratorHalCs40l26BinaryDefaults"],
+ init_rc: ["android.hardware.vibrator-service.cs40l26-evt.rc"],
+ vintf_fragments: ["android.hardware.vibrator-service.cs40l26.xml"],
+ srcs: ["service.cpp"],
+ include_dirs: [
+ "external/tinyalsa/include"
+ ],
+ shared_libs: [
+ "android.hardware.vibrator-impl.cs40l26",
+ "libcutils",
+ "libtinyalsa",
+ ],
+ proprietary: true,
+}
+
+cc_binary {
+ name: "android.hardware.vibrator-service.cs40l26-evt-dual",
+ defaults: ["VibratorHalCs40l26BinaryDefaults"],
+ init_rc: ["android.hardware.vibrator-service.cs40l26-evt-dual.rc"],
+ vintf_fragments: ["android.hardware.vibrator-service.cs40l26-dual.xml"],
+ srcs: ["service.cpp"],
+ include_dirs: [
+ "external/tinyalsa/include"
+ ],
+ shared_libs: [
+ "android.hardware.vibrator-impl.cs40l26",
+ "libcutils",
+ "libtinyalsa",
+ ],
+ cflags: ["-DVIBRATOR_NAME=\"dual\""],
+ proprietary: true,
+}
+
+cc_binary {
+ name: "android.hardware.vibrator-service.cs40l26-dvt",
+ defaults: ["VibratorHalCs40l26BinaryDefaults"],
+ init_rc: ["android.hardware.vibrator-service.cs40l26-dvt.rc"],
+ vintf_fragments: ["android.hardware.vibrator-service.cs40l26.xml"],
+ srcs: ["service.cpp"],
+ include_dirs: [
+ "external/tinyalsa/include"
+ ],
+ shared_libs: [
+ "android.hardware.vibrator-impl.cs40l26",
+ "libcutils",
+ "libtinyalsa",
+ ],
+ proprietary: true,
+}
+
+cc_binary {
+ name: "android.hardware.vibrator-service.cs40l26-dvt-dual",
+ defaults: ["VibratorHalCs40l26BinaryDefaults"],
+ init_rc: ["android.hardware.vibrator-service.cs40l26-dvt-dual.rc"],
+ vintf_fragments: ["android.hardware.vibrator-service.cs40l26-dual.xml"],
+ srcs: ["service.cpp"],
+ include_dirs: [
+ "external/tinyalsa/include"
+ ],
+ shared_libs: [
+ "android.hardware.vibrator-impl.cs40l26",
+ "libcutils",
+ "libtinyalsa",
+ ],
+ cflags: ["-DVIBRATOR_NAME=\"dual\""],
+ proprietary: true,
+}
diff --git a/vibrator/cs40l26/Hardware.h b/vibrator/cs40l26/Hardware.h
new file mode 100644
index 00000000..a555874e
--- /dev/null
+++ b/vibrator/cs40l26/Hardware.h
@@ -0,0 +1,135 @@
+/*
+ * 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 "HardwareBase.h"
+#include "Vibrator.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+class HwApi : public Vibrator::HwApi, private HwApiBase {
+ public:
+ HwApi() {
+ open("calibration/f0_stored", &mF0);
+ open("default/f0_offset", &mF0Offset);
+ open("calibration/redc_stored", &mRedc);
+ open("calibration/q_stored", &mQ);
+ open("default/vibe_state", &mVibeState);
+ open("default/num_waves", &mEffectCount);
+ open("default/owt_free_space", &mOwtFreeSpace);
+ }
+
+ bool setF0(std::string value) override { return set(value, &mF0); }
+ bool setF0Offset(uint32_t value) override { return set(value, &mF0Offset); }
+ bool setRedc(std::string value) override { return set(value, &mRedc); }
+ bool setQ(std::string value) override { return set(value, &mQ); }
+ bool getEffectCount(uint32_t *value) override { return getStr(value, &mEffectCount); }
+ bool pollVibeState(std::string value, int32_t timeoutMs) override {
+ return pollStr(value, &mVibeState, timeoutMs);
+ }
+ bool setClabEnable(bool value) override { return set(value, &mClabEnable); }
+ bool getAvailablePwleSegments(uint32_t *value) override {
+ return getStr(value, &mAvailablePwleSegments);
+ }
+ bool setPwle(std::string value) override { return set(value, &mPwle); }
+ bool hasOwtFreeSpace() override { return has(mOwtFreeSpace); }
+ bool getOwtFreeSpace(uint32_t *value) override { return get(value, &mOwtFreeSpace); }
+ void debug(int fd) override { HwApiBase::debug(fd); }
+
+ private:
+ std::ofstream mF0;
+ std::ofstream mF0Offset;
+ std::ofstream mRedc;
+ std::ofstream mQ;
+ std::ifstream mEffectCount;
+ std::ifstream mVibeState;
+ std::ofstream mClabEnable;
+ std::ifstream mAvailablePwleSegments;
+ std::ofstream mPwle;
+ std::ifstream mOwtFreeSpace;
+};
+
+class HwCal : public Vibrator::HwCal, private HwCalBase {
+ private:
+ static constexpr char VERSION[] = "version";
+ static constexpr char F0_CONFIG[] = "f0_measured";
+ static constexpr char REDC_CONFIG[] = "redc_measured";
+ static constexpr char Q_CONFIG[] = "q_measured";
+ static constexpr char TICK_VOLTAGES_CONFIG[] = "v_tick";
+ static constexpr char CLICK_VOLTAGES_CONFIG[] = "v_click";
+ static constexpr char LONG_VOLTAGES_CONFIG[] = "v_long";
+
+ static constexpr uint32_t VERSION_DEFAULT = 2;
+ static constexpr int32_t DEFAULT_FREQUENCY_SHIFT = 0;
+ static constexpr std::array<uint32_t, 2> V_TICK_DEFAULT = {1, 100};
+ static constexpr std::array<uint32_t, 2> V_CTICK_DEFAULT = {1, 100};
+ static constexpr std::array<uint32_t, 2> V_LONG_DEFAULT = {1, 100};
+
+ public:
+ HwCal() {}
+
+ bool getVersion(uint32_t *value) override {
+ if (getPersist(VERSION, value)) {
+ return true;
+ }
+ *value = VERSION_DEFAULT;
+ return true;
+ }
+ bool getLongFrequencyShift(int32_t *value) override {
+ return getProperty("long.frequency.shift", value, DEFAULT_FREQUENCY_SHIFT);
+ }
+ bool getF0(std::string *value) override { return getPersist(F0_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 getTickVolLevels(std::array<uint32_t, 2> *value) override {
+ if (getPersist(TICK_VOLTAGES_CONFIG, value)) {
+ return true;
+ }
+ *value = V_TICK_DEFAULT;
+ return true;
+ }
+ bool getClickVolLevels(std::array<uint32_t, 2> *value) override {
+ if (getPersist(CLICK_VOLTAGES_CONFIG, value)) {
+ return true;
+ }
+ *value = V_CTICK_DEFAULT;
+ return true;
+ }
+ bool getLongVolLevels(std::array<uint32_t, 2> *value) override {
+ if (getPersist(LONG_VOLTAGES_CONFIG, value)) {
+ return true;
+ }
+ *value = V_LONG_DEFAULT;
+ return true;
+ }
+ bool isChirpEnabled() override {
+ bool value;
+ getProperty("chirp.enabled", &value, false);
+ return value;
+ }
+ bool getSupportedPrimitives(uint32_t *value) override {
+ return getProperty("supported_primitives", value, (uint32_t)0);
+ }
+ void debug(int fd) override { HwCalBase::debug(fd); }
+};
+
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/vibrator/cs40l26/Vibrator.cpp b/vibrator/cs40l26/Vibrator.cpp
new file mode 100644
index 00000000..9d7954e8
--- /dev/null
+++ b/vibrator/cs40l26/Vibrator.cpp
@@ -0,0 +1,1365 @@
+/*
+ * 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 "Vibrator.h"
+
+#include <glob.h>
+#include <hardware/hardware.h>
+#include <hardware/vibrator.h>
+#include <linux/input.h>
+#include <log/log.h>
+#include <stdio.h>
+#include <utils/Trace.h>
+
+#include <cinttypes>
+#include <cmath>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
+#endif
+
+#define PROC_SND_PCM "/proc/asound/pcm"
+#define HAPTIC_PCM_DEVICE_SYMBOL "haptic nohost playback"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+static constexpr uint8_t FF_CUSTOM_DATA_LEN = 2;
+static constexpr uint16_t FF_CUSTOM_DATA_LEN_MAX_COMP = 2044; // (COMPOSE_SIZE_MAX + 1) * 8 + 4
+
+static constexpr uint32_t WAVEFORM_DOUBLE_CLICK_SILENCE_MS = 100;
+
+static constexpr uint32_t WAVEFORM_LONG_VIBRATION_THRESHOLD_MS = 50;
+
+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_PAUSE_TIMING_ERROR_MS = 1; // ALERT Irq Handling
+static constexpr uint32_t MAX_TIME_MS = UINT16_MAX;
+
+static constexpr auto ASYNC_COMPLETION_TIMEOUT = std::chrono::milliseconds(100);
+static constexpr auto POLLING_TIMEOUT = 20;
+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. */
+static constexpr int32_t COMPOSE_SIZE_MAX = 254;
+static constexpr int32_t COMPOSE_PWLE_SIZE_MAX_DEFAULT = 127;
+
+// Measured resonant frequency, f0_measured, is represented by Q10.14 fixed
+// point format on cs40l26 devices. The expression to calculate f0 is:
+// f0 = f0_measured / 2^Q14_BIT_SHIFT
+// See the LRA Calibration Support documentation for more details.
+static constexpr int32_t Q14_BIT_SHIFT = 14;
+
+// Measured Q factor, q_measured, is represented by Q8.16 fixed
+// point format on cs40l26 devices. The expression to calculate q is:
+// q = q_measured / 2^Q16_BIT_SHIFT
+// See the LRA Calibration Support documentation for more details.
+static constexpr int32_t Q16_BIT_SHIFT = 16;
+
+static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383;
+
+static constexpr float PWLE_LEVEL_MIN = 0.0;
+static constexpr float PWLE_LEVEL_MAX = 1.0;
+static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 0.25;
+static constexpr float PWLE_FREQUENCY_MIN_HZ = 0.25;
+static constexpr float PWLE_FREQUENCY_MAX_HZ = 1023.75;
+static constexpr float PWLE_BW_MAP_SIZE =
+ 1 + ((PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ);
+
+static struct pcm_config haptic_nohost_config = {
+ .channels = 1,
+ .rate = 48000,
+ .period_size = 80,
+ .period_count = 2,
+ .format = PCM_FORMAT_S16_LE,
+};
+
+static uint16_t amplitudeToScale(float amplitude, float maximum) {
+ float ratio = 100; /* Unit: % */
+ if (maximum != 0)
+ ratio = amplitude / maximum * 100;
+
+ if (maximum == 0 || ratio > 100)
+ ratio = 100;
+
+ return std::round(ratio);
+}
+
+enum class AlwaysOnId : uint32_t {
+ GPIO_RISE,
+ GPIO_FALL,
+};
+
+enum WaveformBankID : uint8_t {
+ RAM_WVFRM_BANK,
+ ROM_WVFRM_BANK,
+ OWT_WVFRM_BANK,
+};
+
+enum WaveformIndex : uint16_t {
+ /* Physical waveform */
+ WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
+ WAVEFORM_RESERVED_INDEX_1 = 1,
+ WAVEFORM_CLICK_INDEX = 2,
+ WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
+ WAVEFORM_THUD_INDEX = 4,
+ WAVEFORM_SPIN_INDEX = 5,
+ WAVEFORM_QUICK_RISE_INDEX = 6,
+ WAVEFORM_SLOW_RISE_INDEX = 7,
+ WAVEFORM_QUICK_FALL_INDEX = 8,
+ WAVEFORM_LIGHT_TICK_INDEX = 9,
+ WAVEFORM_LOW_TICK_INDEX = 10,
+ WAVEFORM_RESERVED_MFG_1,
+ WAVEFORM_RESERVED_MFG_2,
+ WAVEFORM_RESERVED_MFG_3,
+ WAVEFORM_MAX_PHYSICAL_INDEX,
+ /* OWT waveform */
+ WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
+ WAVEFORM_PWLE,
+ /*
+ * Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
+ * #define FF_GAIN 0x60 // 96 in decimal
+ * #define FF_MAX_EFFECTS FF_GAIN
+ */
+ WAVEFORM_MAX_INDEX,
+};
+
+std::vector<CompositePrimitive> defaultSupportedPrimitives = {
+ ndk::enum_range<CompositePrimitive>().begin(), ndk::enum_range<CompositePrimitive>().end()};
+
+static int min(int x, int y) {
+ return x < y ? x : y;
+}
+
+struct dspmem_chunk {
+ uint8_t *head;
+ uint8_t *current;
+ uint8_t *max;
+ int bytes;
+
+ uint32_t cache;
+ int cachebits;
+};
+
+static dspmem_chunk *dspmem_chunk_create(void *data, int size) {
+ auto ch = new dspmem_chunk{
+ .head = reinterpret_cast<uint8_t *>(data),
+ .current = reinterpret_cast<uint8_t *>(data),
+ .max = reinterpret_cast<uint8_t *>(data) + size,
+ };
+
+ return ch;
+}
+
+static bool dspmem_chunk_end(struct dspmem_chunk *ch) {
+ return ch->current == ch->max;
+}
+
+static int dspmem_chunk_bytes(struct dspmem_chunk *ch) {
+ return ch->bytes;
+}
+
+static int dspmem_chunk_write(struct dspmem_chunk *ch, int nbits, uint32_t val) {
+ int nwrite, i;
+
+ nwrite = min(24 - ch->cachebits, nbits);
+ ch->cache <<= nwrite;
+ ch->cache |= val >> (nbits - nwrite);
+ ch->cachebits += nwrite;
+ nbits -= nwrite;
+
+ if (ch->cachebits == 24) {
+ if (dspmem_chunk_end(ch))
+ return -ENOSPC;
+
+ ch->cache &= 0xFFFFFF;
+ for (i = 0; i < sizeof(ch->cache); i++, ch->cache <<= 8)
+ *ch->current++ = (ch->cache & 0xFF000000) >> 24;
+
+ ch->bytes += sizeof(ch->cache);
+ ch->cachebits = 0;
+ }
+
+ if (nbits)
+ return dspmem_chunk_write(ch, nbits, val);
+
+ return 0;
+}
+
+static int dspmem_chunk_flush(struct dspmem_chunk *ch) {
+ if (!ch->cachebits)
+ return 0;
+
+ return dspmem_chunk_write(ch, 24 - ch->cachebits, 0);
+}
+
+std::vector<struct ff_effect> mFfEffects;
+
+Vibrator::Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal)
+ : mHwApi(std::move(hwapi)), mHwCal(std::move(hwcal)), mAsyncHandle(std::async([] {})) {
+ int32_t longFrequencyShift;
+ std::string caldata{8, '0'};
+ uint32_t calVer;
+
+ glob_t inputEventPaths;
+ if (glob("/dev/input/event*", 0, nullptr, &inputEventPaths)) {
+ ALOGE("Fail to get input event paths (%d): %s", errno, strerror(errno));
+ return;
+ }
+
+ int fd = -1;
+ uint32_t val = 0;
+ char str[20] = {0x00};
+ const char *inputEventName = std::getenv("INPUT_EVENT_NAME");
+ for (uint8_t retry = 0; retry < 3; retry++) {
+ 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 &&
+ strcmp(str, inputEventName) == 0) {
+ mInputFd.reset(fd);
+ ALOGI("Control %s through %s", inputEventName, inputEventPaths.gl_pathv[i]);
+ break;
+ }
+ close(fd);
+ }
+ }
+ if (mInputFd.ok()) {
+ break;
+ }
+
+ sleep(1);
+ ALOGW("Retry to search the input");
+ }
+ globfree(&inputEventPaths);
+ if (!mInputFd.ok()) {
+ ALOGE("Fail to get an input event with name %s", inputEventName);
+ return;
+ }
+
+ /*
+ * Create custom effects for all physical waveforms.
+ * 1. Set the initial duration for the corresponding RAM waveform.
+ * 2. Write to the force feedback driver. If the waveform firmware is not loaded,
+ * retry at most 10 times and then abort the constructor.
+ * 3. Store the effect ID.
+ */
+ uint8_t retry = 0;
+ mFfEffects.resize(WAVEFORM_MAX_INDEX);
+ mEffectDurations.resize(WAVEFORM_MAX_INDEX);
+ mEffectDurations = {
+ 1000, 100, 30, 1000, 300, 130, 150, 500, 100, 15, 20, 1000, 1000, 1000,
+ }; /* 11+3 waveforms. The duration must < UINT16_MAX */
+
+ uint8_t effectIndex = 0;
+
+ for (effectIndex = 0; effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX; effectIndex++) {
+ mFfEffects[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,
+ };
+
+ while (true) {
+ if (ioctl(mInputFd, EVIOCSFF, &mFfEffects[effectIndex]) < 0) {
+ ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno));
+
+ if (retry < 10) {
+ sleep(1);
+ ALOGW("Retry #%u", ++retry);
+ continue;
+ } else {
+ ALOGE("Retried but the initialization was failed!");
+ return;
+ }
+ }
+ mFfEffects[effectIndex].id = effectIndex;
+ break;
+ }
+ }
+ if (effectIndex != WAVEFORM_MAX_PHYSICAL_INDEX) {
+ ALOGE("Incomplete effect initialization!");
+ return;
+ }
+
+ /* Initiate placeholders for OWT effects. */
+ for (effectIndex = WAVEFORM_MAX_PHYSICAL_INDEX; effectIndex < WAVEFORM_MAX_INDEX;
+ effectIndex++) {
+ mFfEffects[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->getF0(&caldata)) {
+ mHwApi->setF0(caldata);
+ }
+ if (mHwCal->getRedc(&caldata)) {
+ mHwApi->setRedc(caldata);
+ }
+ if (mHwCal->getQ(&caldata)) {
+ mHwApi->setQ(caldata);
+ }
+
+ mHwCal->getLongFrequencyShift(&longFrequencyShift);
+ if (longFrequencyShift > 0) {
+ mF0Offset = longFrequencyShift * std::pow(2, 14);
+ } else if (longFrequencyShift < 0) {
+ mF0Offset = std::pow(2, 24) - std::abs(longFrequencyShift) * std::pow(2, 14);
+ } else {
+ mF0Offset = 0;
+ }
+
+ mHwCal->getVersion(&calVer);
+ if (calVer == 2) {
+ mHwCal->getTickVolLevels(&mTickEffectVol);
+ mHwCal->getClickVolLevels(&mClickEffectVol);
+ mHwCal->getLongVolLevels(&mLongEffectVol);
+ } else {
+ ALOGW("Unsupported calibration version!");
+ }
+
+ mIsUnderExternalControl = false;
+
+ mIsChirpEnabled = mHwCal->isChirpEnabled();
+
+ mHwCal->getSupportedPrimitives(&mSupportedPrimitivesBits);
+ if (mSupportedPrimitivesBits > 0) {
+ for (auto e : defaultSupportedPrimitives) {
+ if (mSupportedPrimitivesBits & (1 << uint32_t(e))) {
+ mSupportedPrimitives.emplace_back(e);
+ }
+ }
+ } else {
+ for (auto e : defaultSupportedPrimitives) {
+ mSupportedPrimitivesBits |= (1 << uint32_t(e));
+ }
+ mSupportedPrimitives = defaultSupportedPrimitives;
+ }
+}
+
+ndk::ScopedAStatus Vibrator::getCapabilities(int32_t *_aidl_return) {
+ ATRACE_NAME("Vibrator::getCapabilities");
+
+ int32_t ret = IVibrator::CAP_ON_CALLBACK | IVibrator::CAP_PERFORM_CALLBACK |
+ IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_ALWAYS_ON_CONTROL |
+ IVibrator::CAP_GET_RESONANT_FREQUENCY | IVibrator::CAP_GET_Q_FACTOR;
+ if (hasHapticAlsaDevice()) {
+ ret |= IVibrator::CAP_EXTERNAL_CONTROL;
+ }
+ if (mHwApi->hasOwtFreeSpace()) {
+ ret |= IVibrator::CAP_COMPOSE_EFFECTS;
+
+ if (mIsChirpEnabled) {
+ ret |= IVibrator::CAP_FREQUENCY_CONTROL | IVibrator::CAP_COMPOSE_PWLE_EFFECTS;
+ }
+ }
+ *_aidl_return = ret;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::off() {
+ ATRACE_NAME("Vibrator::off");
+ if (mActiveId < 0) {
+ return ndk::ScopedAStatus::ok();
+ }
+
+ struct input_event play = {
+ .type = EV_FF,
+ .code = static_cast<uint16_t>(mActiveId),
+ .value = 0,
+ };
+
+ if (write(mInputFd, (const void *)&play, sizeof(play)) != sizeof(play)) {
+ ALOGE("Failed to stop effect %d (%d): %s", play.code, errno, strerror(errno));
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ mActiveId = -1;
+ setGlobalAmplitude(false);
+ mHwApi->setF0Offset(0);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
+ const std::shared_ptr<IVibratorCallback> &callback) {
+ ATRACE_NAME("Vibrator::on");
+ if (timeoutMs > MAX_TIME_MS) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ const uint16_t index = (timeoutMs < WAVEFORM_LONG_VIBRATION_THRESHOLD_MS)
+ ? WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX
+ : WAVEFORM_LONG_VIBRATION_EFFECT_INDEX;
+ if (MAX_COLD_START_LATENCY_MS <= MAX_TIME_MS - timeoutMs) {
+ timeoutMs += MAX_COLD_START_LATENCY_MS;
+ }
+ setGlobalAmplitude(true);
+ mHwApi->setF0Offset(mF0Offset);
+ return on(timeoutMs, index, callback);
+}
+
+ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength strength,
+ const std::shared_ptr<IVibratorCallback> &callback,
+ int32_t *_aidl_return) {
+ ATRACE_NAME("Vibrator::perform");
+ return performEffect(effect, strength, callback, _aidl_return);
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect> *_aidl_return) {
+ *_aidl_return = {Effect::TEXTURE_TICK, Effect::TICK, Effect::CLICK, Effect::HEAVY_CLICK,
+ Effect::DOUBLE_CLICK};
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
+ ATRACE_NAME("Vibrator::setAmplitude");
+ if (amplitude <= 0.0f || amplitude > 1.0f) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ mLongEffectScale = amplitude;
+ if (!isUnderExternalControl()) {
+ return setGlobalAmplitude(true);
+ } else {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+}
+
+ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
+ ATRACE_NAME("Vibrator::setExternalControl");
+ setGlobalAmplitude(enabled);
+
+ if (mHasHapticAlsaDevice) {
+ if (!enableHapticPcmAmp(&mHapticPcm, enabled, mCard, mDevice)) {
+ ALOGE("Failed to %s haptic pcm device: %d", (enabled ? "enable" : "disable"), mDevice);
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ }
+
+ mIsUnderExternalControl = enabled;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t *maxDelayMs) {
+ ATRACE_NAME("Vibrator::getCompositionDelayMax");
+ *maxDelayMs = COMPOSE_DELAY_MAX_MS;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t *maxSize) {
+ ATRACE_NAME("Vibrator::getCompositionSizeMax");
+ *maxSize = COMPOSE_SIZE_MAX;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive> *supported) {
+ *supported = mSupportedPrimitives;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
+ int32_t *durationMs) {
+ ndk::ScopedAStatus status;
+ uint32_t effectIndex;
+ if (primitive != CompositePrimitive::NOOP) {
+ status = getPrimitiveDetails(primitive, &effectIndex);
+ if (!status.isOk()) {
+ return status;
+ }
+
+ *durationMs = mEffectDurations[effectIndex];
+ } else {
+ *durationMs = 0;
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composite,
+ const std::shared_ptr<IVibratorCallback> &callback) {
+ ATRACE_NAME("Vibrator::compose");
+ uint16_t size;
+ uint16_t nextEffectDelay;
+
+ auto ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_COMP]{0x00},
+ FF_CUSTOM_DATA_LEN_MAX_COMP);
+
+ if (composite.size() > COMPOSE_SIZE_MAX || composite.empty()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ /* Check if there is a wait before the first effect. */
+ nextEffectDelay = composite.front().delayMs;
+ if (nextEffectDelay > COMPOSE_DELAY_MAX_MS || nextEffectDelay < 0) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ } else if (nextEffectDelay > 0) {
+ size = composite.size() + 1;
+ } else {
+ size = composite.size();
+ }
+
+ dspmem_chunk_write(ch, 8, 0); /* Padding */
+ dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & size)); /* nsections */
+ dspmem_chunk_write(ch, 8, 0); /* repeat */
+ uint8_t header_count = ch->bytes;
+
+ /* Insert 1 section for a wait before the first effect. */
+ if (nextEffectDelay) {
+ dspmem_chunk_write(ch, 32, 0); /* amplitude, index, repeat & flags */
+ dspmem_chunk_write(ch, 16, (uint16_t)(0xFFFF & nextEffectDelay)); /* delay */
+ }
+
+ for (uint32_t i_curr = 0, i_next = 1; i_curr < composite.size(); i_curr++, i_next++) {
+ auto &e_curr = composite[i_curr];
+ uint32_t effectIndex = 0;
+ uint32_t effectVolLevel = 0;
+ if (e_curr.scale < 0.0f || e_curr.scale > 1.0f) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ if (e_curr.primitive != CompositePrimitive::NOOP) {
+ ndk::ScopedAStatus status;
+ status = getPrimitiveDetails(e_curr.primitive, &effectIndex);
+ if (!status.isOk()) {
+ return status;
+ }
+ effectVolLevel = intensityToVolLevel(e_curr.scale, effectIndex);
+ }
+
+ /* Fetch the next composite effect delay and fill into the current section */
+ nextEffectDelay = 0;
+ if (i_next < composite.size()) {
+ auto &e_next = composite[i_next];
+ int32_t delay = e_next.delayMs;
+
+ if (delay > COMPOSE_DELAY_MAX_MS || delay < 0) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ nextEffectDelay = delay;
+ }
+
+ if (effectIndex == 0 && nextEffectDelay == 0) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & effectVolLevel)); /* amplitude */
+ dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & effectIndex)); /* index */
+ dspmem_chunk_write(ch, 8, 0); /* repeat */
+ dspmem_chunk_write(ch, 8, 0); /* flags */
+ dspmem_chunk_write(ch, 16, (uint16_t)(0xFFFF & nextEffectDelay)); /* delay */
+ }
+ dspmem_chunk_flush(ch);
+ if (header_count == ch->bytes) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ } else {
+ return performEffect(0 /*ignored*/, 0 /*ignored*/, ch, callback);
+ }
+}
+
+ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex,
+ const std::shared_ptr<IVibratorCallback> &callback) {
+ if (effectIndex > WAVEFORM_MAX_INDEX) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (mAsyncHandle.wait_for(ASYNC_COMPLETION_TIMEOUT) != std::future_status::ready) {
+ ALOGE("Previous vibration pending.");
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ /* Update duration for long/short vibration. */
+ if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX ||
+ effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) {
+ mFfEffects[effectIndex].replay.length = static_cast<uint16_t>(timeoutMs);
+ if (ioctl(mInputFd, EVIOCSFF, &mFfEffects[effectIndex]) < 0) {
+ ALOGE("Failed to edit effect %d (%d): %s", effectIndex, errno, strerror(errno));
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ }
+
+ /* Play the event now. */
+ struct input_event play = {
+ .type = EV_FF,
+ .code = static_cast<uint16_t>(effectIndex),
+ .value = 1,
+ };
+ if (write(mInputFd, (const void *)&play, sizeof(play)) != sizeof(play)) {
+ ALOGE("Failed to play effect %d (%d): %s", play.code, errno, strerror(errno));
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ mActiveId = play.code;
+
+ mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::setEffectAmplitude(float amplitude, float maximum) {
+ uint16_t scale = amplitudeToScale(amplitude, maximum);
+ struct input_event gain = {
+ .type = EV_FF,
+ .code = FF_GAIN,
+ .value = scale,
+ };
+ if (write(mInputFd, (const void *)&gain, sizeof(gain)) != sizeof(gain)) {
+ ALOGE("Failed to set the gain to %u (%d): %s", scale, errno, strerror(errno));
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::setGlobalAmplitude(bool set) {
+ uint8_t amplitude = set ? roundf(mLongEffectScale * mLongEffectVol[1]) : VOLTAGE_SCALE_MAX;
+ if (!set) {
+ mLongEffectScale = 1.0; // Reset the scale for the later new effect.
+ }
+ return setEffectAmplitude(amplitude, VOLTAGE_SCALE_MAX);
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect> *_aidl_return) {
+ *_aidl_return = {Effect::TEXTURE_TICK, Effect::TICK, Effect::CLICK, Effect::HEAVY_CLICK};
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) {
+ ndk::ScopedAStatus status;
+ uint32_t effectIndex;
+ uint32_t timeMs;
+ uint32_t volLevel;
+ uint16_t scale;
+ status = getSimpleDetails(effect, strength, &effectIndex, &timeMs, &volLevel);
+ if (!status.isOk()) {
+ return status;
+ }
+
+ scale = amplitudeToScale(volLevel, VOLTAGE_SCALE_MAX);
+
+ switch (static_cast<AlwaysOnId>(id)) {
+ case AlwaysOnId::GPIO_RISE:
+ // mHwApi->setGpioRiseIndex(effectIndex);
+ // mHwApi->setGpioRiseScale(scale);
+ return ndk::ScopedAStatus::ok();
+ case AlwaysOnId::GPIO_FALL:
+ // mHwApi->setGpioFallIndex(effectIndex);
+ // mHwApi->setGpioFallScale(scale);
+ return ndk::ScopedAStatus::ok();
+ }
+
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id) {
+ switch (static_cast<AlwaysOnId>(id)) {
+ case AlwaysOnId::GPIO_RISE:
+ // mHwApi->setGpioRiseIndex(0);
+ return ndk::ScopedAStatus::ok();
+ case AlwaysOnId::GPIO_FALL:
+ // mHwApi->setGpioFallIndex(0);
+ return ndk::ScopedAStatus::ok();
+ }
+
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz) {
+ std::string caldata{8, '0'};
+ if (!mHwCal->getF0(&caldata)) {
+ ALOGE("Failed to get resonant frequency (%d): %s", errno, strerror(errno));
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ *resonantFreqHz = static_cast<float>(std::stoul(caldata, nullptr, 16)) / (1 << Q14_BIT_SHIFT);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) {
+ std::string caldata{8, '0'};
+ if (!mHwCal->getQ(&caldata)) {
+ ALOGE("Failed to get q factor (%d): %s", errno, strerror(errno));
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ *qFactor = static_cast<float>(std::stoul(caldata, nullptr, 16)) / (1 << Q16_BIT_SHIFT);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) {
+ int32_t capabilities;
+ Vibrator::getCapabilities(&capabilities);
+ if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+ *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ;
+ return ndk::ScopedAStatus::ok();
+ } else {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+}
+
+ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) {
+ int32_t capabilities;
+ Vibrator::getCapabilities(&capabilities);
+ if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+ *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ;
+ return ndk::ScopedAStatus::ok();
+ } else {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+}
+
+ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) {
+ // TODO(b/170919640): complete implementation
+ int32_t capabilities;
+ Vibrator::getCapabilities(&capabilities);
+ if (capabilities & IVibrator::CAP_FREQUENCY_CONTROL) {
+ std::vector<float> bandwidthAmplitudeMap(PWLE_BW_MAP_SIZE, 1.0);
+ *_aidl_return = bandwidthAmplitudeMap;
+ return ndk::ScopedAStatus::ok();
+ } else {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+}
+
+ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) {
+ int32_t capabilities;
+ Vibrator::getCapabilities(&capabilities);
+ if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+ *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS;
+ return ndk::ScopedAStatus::ok();
+ } else {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+}
+
+ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) {
+ int32_t capabilities;
+ Vibrator::getCapabilities(&capabilities);
+ if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+ *maxSize = COMPOSE_PWLE_SIZE_MAX_DEFAULT;
+ return ndk::ScopedAStatus::ok();
+ } else {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+}
+
+ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) {
+ int32_t capabilities;
+ Vibrator::getCapabilities(&capabilities);
+ if (capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) {
+ *supported = {
+ Braking::NONE,
+ Braking::CLAB,
+ };
+ return ndk::ScopedAStatus::ok();
+ } else {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+}
+
+ndk::ScopedAStatus Vibrator::setPwle(const std::string &pwleQueue) {
+ if (!mHwApi->setPwle(pwleQueue)) {
+ ALOGE("Failed to write \"%s\" to pwle (%d): %s", pwleQueue.c_str(), errno, strerror(errno));
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+static void resetPreviousEndAmplitudeEndFrequency(float *prevEndAmplitude,
+ float *prevEndFrequency) {
+ const float reset = -1.0;
+ *prevEndAmplitude = reset;
+ *prevEndFrequency = reset;
+}
+
+static void incrementIndex(int *index) {
+ *index += 1;
+}
+
+static void constructActiveDefaults(std::ostringstream &pwleBuilder, const int &segmentIdx) {
+ pwleBuilder << ",C" << segmentIdx << ":1";
+ pwleBuilder << ",B" << segmentIdx << ":0";
+ pwleBuilder << ",AR" << segmentIdx << ":0";
+ pwleBuilder << ",V" << segmentIdx << ":0";
+}
+
+static void constructActiveSegment(std::ostringstream &pwleBuilder, const int &segmentIdx,
+ int duration, float amplitude, float frequency) {
+ pwleBuilder << ",T" << segmentIdx << ":" << duration;
+ pwleBuilder << ",L" << segmentIdx << ":" << amplitude;
+ pwleBuilder << ",F" << segmentIdx << ":" << frequency;
+ constructActiveDefaults(pwleBuilder, segmentIdx);
+}
+
+static void constructBrakingSegment(std::ostringstream &pwleBuilder, const int &segmentIdx,
+ int duration, Braking brakingType) {
+ pwleBuilder << ",T" << segmentIdx << ":" << duration;
+ pwleBuilder << ",L" << segmentIdx << ":" << 0;
+ pwleBuilder << ",F" << segmentIdx << ":" << PWLE_FREQUENCY_MIN_HZ;
+ pwleBuilder << ",C" << segmentIdx << ":0";
+ pwleBuilder << ",B" << segmentIdx << ":"
+ << static_cast<std::underlying_type<Braking>::type>(brakingType);
+ pwleBuilder << ",AR" << segmentIdx << ":0";
+ pwleBuilder << ",V" << segmentIdx << ":0";
+}
+
+ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
+ const std::shared_ptr<IVibratorCallback> &callback) {
+ ATRACE_NAME("Vibrator::composePwle");
+ std::ostringstream pwleBuilder;
+ std::string pwleQueue;
+ int32_t capabilities;
+ Vibrator::getCapabilities(&capabilities);
+ if ((capabilities & IVibrator::CAP_COMPOSE_PWLE_EFFECTS) == 0) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+
+ if (composite.size() <= 0 || composite.size() > compositionSizeMax) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ float prevEndAmplitude;
+ float prevEndFrequency;
+ resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency);
+
+ int segmentIdx = 0;
+ uint32_t totalDuration = 0;
+
+ pwleBuilder << "S:0,WF:4,RP:0,WT:0";
+
+ for (auto &e : composite) {
+ switch (e.getTag()) {
+ case PrimitivePwle::active: {
+ auto active = e.get<PrimitivePwle::active>();
+ if (active.duration < 0 ||
+ active.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (active.startAmplitude < PWLE_LEVEL_MIN ||
+ active.startAmplitude > PWLE_LEVEL_MAX ||
+ active.endAmplitude < PWLE_LEVEL_MIN || active.endAmplitude > PWLE_LEVEL_MAX) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (active.startFrequency < PWLE_FREQUENCY_MIN_HZ ||
+ active.startFrequency > PWLE_FREQUENCY_MAX_HZ ||
+ active.endFrequency < PWLE_FREQUENCY_MIN_HZ ||
+ active.endFrequency > PWLE_FREQUENCY_MAX_HZ) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ if (!((active.startAmplitude == prevEndAmplitude) &&
+ (active.startFrequency == prevEndFrequency))) {
+ constructActiveSegment(pwleBuilder, segmentIdx, 0, active.startAmplitude,
+ active.startFrequency);
+ incrementIndex(&segmentIdx);
+ }
+
+ constructActiveSegment(pwleBuilder, segmentIdx, active.duration,
+ active.endAmplitude, active.endFrequency);
+ incrementIndex(&segmentIdx);
+
+ prevEndAmplitude = active.endAmplitude;
+ prevEndFrequency = active.endFrequency;
+ totalDuration += active.duration;
+ break;
+ }
+ case PrimitivePwle::braking: {
+ auto braking = e.get<PrimitivePwle::braking>();
+ if (braking.braking > Braking::CLAB) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ constructBrakingSegment(pwleBuilder, segmentIdx, 0, braking.braking);
+ incrementIndex(&segmentIdx);
+
+ constructBrakingSegment(pwleBuilder, segmentIdx, braking.duration, braking.braking);
+ incrementIndex(&segmentIdx);
+
+ resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency);
+ totalDuration += braking.duration;
+ break;
+ }
+ }
+ }
+
+ pwleQueue = pwleBuilder.str();
+ ALOGD("composePwle queue: (%s)", pwleQueue.c_str());
+
+ ndk::ScopedAStatus status = setPwle(pwleQueue);
+ if (!status.isOk()) {
+ ALOGE("Failed to write pwle queue");
+ return status;
+ }
+
+ // mHwApi->setEffectIndex(WAVEFORM_UNSAVED_TRIGGER_QUEUE_INDEX);
+
+ totalDuration += MAX_COLD_START_LATENCY_MS;
+ // mHwApi->setDuration(MAX_TIME_MS);
+
+ // mHwApi->setActivate(1);
+
+ mAsyncHandle = std::async(&Vibrator::waitForComplete, this, callback);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+bool Vibrator::isUnderExternalControl() {
+ return mIsUnderExternalControl;
+}
+
+binder_status_t Vibrator::dump(int fd, const char **args, uint32_t numArgs) {
+ if (fd < 0) {
+ ALOGE("Called debug() with invalid fd.");
+ return STATUS_OK;
+ }
+
+ (void)args;
+ (void)numArgs;
+
+ dprintf(fd, "AIDL:\n");
+
+ dprintf(fd, " F0 Offset: %" PRIu32 "\n", mF0Offset);
+
+ dprintf(fd, " Voltage Levels:\n");
+ dprintf(fd, " Tick Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mTickEffectVol[0],
+ mTickEffectVol[1]);
+ dprintf(fd, " Click Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mClickEffectVol[0],
+ mClickEffectVol[1]);
+ dprintf(fd, " Long Effect Min: %" PRIu32 " Max: %" PRIu32 "\n", mLongEffectVol[0],
+ mLongEffectVol[1]);
+
+ dprintf(fd, " FF effect:\n");
+ dprintf(fd, " Physical waveform:\n");
+ dprintf(fd, "\tId\tIndex\tt ->\tt'\n");
+ for (uint8_t effectId = 0; effectId < WAVEFORM_MAX_PHYSICAL_INDEX; effectId++) {
+ dprintf(fd, "\t%d\t%d\t%d\t%d\n", mFfEffects[effectId].id,
+ mFfEffects[effectId].u.periodic.custom_data[1], mEffectDurations[effectId],
+ mFfEffects[effectId].replay.length);
+ }
+ dprintf(fd, " OWT waveform:\n");
+ dprintf(fd, "\tId\tBytes\tData\n");
+ for (uint8_t effectId = WAVEFORM_MAX_PHYSICAL_INDEX; effectId < WAVEFORM_MAX_INDEX;
+ effectId++) {
+ uint32_t numBytes = mFfEffects[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 *>(mFfEffects[effectId].u.periodic.custom_data) +
+ i))
+ << " ";
+ }
+ dprintf(fd, "\t%d\t%d\t{%s}\n", mFfEffects[effectId].id, numBytes, ss.str().c_str());
+ }
+
+ dprintf(fd, "\n");
+ dprintf(fd, "\n");
+
+ mHwApi->debug(fd);
+
+ dprintf(fd, "\n");
+
+ mHwCal->debug(fd);
+
+ fsync(fd);
+ return STATUS_OK;
+}
+
+bool Vibrator::findHapticAlsaDevice(int *card, int *device) {
+ std::string line;
+ std::ifstream myfile(PROC_SND_PCM);
+ if (myfile.is_open()) {
+ while (getline(myfile, line)) {
+ if (line.find(HAPTIC_PCM_DEVICE_SYMBOL) != std::string::npos) {
+ std::stringstream ss(line);
+ std::string currentToken;
+ std::getline(ss, currentToken, ':');
+ sscanf(currentToken.c_str(), "%d-%d", card, device);
+ return true;
+ }
+ }
+ myfile.close();
+ } else {
+ ALOGE("Failed to read file: %s", PROC_SND_PCM);
+ }
+ return false;
+}
+
+bool Vibrator::hasHapticAlsaDevice() {
+ // We need to call findHapticAlsaDevice once only. Calling in the
+ // 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.
+ static bool configHapticAlsaDeviceDone = false;
+ if (!configHapticAlsaDeviceDone) {
+ if (findHapticAlsaDevice(&mCard, &mDevice)) {
+ mHasHapticAlsaDevice = true;
+ configHapticAlsaDeviceDone = true;
+ } else {
+ ALOGE("Haptic ALSA device not supported");
+ }
+ }
+ return mHasHapticAlsaDevice;
+}
+
+bool Vibrator::enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device) {
+ int ret = 0;
+
+ if (enable) {
+ *haptic_pcm = pcm_open(card, device, PCM_OUT, &haptic_nohost_config);
+ if (!pcm_is_ready(*haptic_pcm)) {
+ ALOGE("cannot open pcm_out driver: %s", pcm_get_error(*haptic_pcm));
+ goto fail;
+ }
+
+ ret = pcm_prepare(*haptic_pcm);
+ if (ret < 0) {
+ ALOGE("cannot prepare haptic_pcm: %s", pcm_get_error(*haptic_pcm));
+ goto fail;
+ }
+
+ ret = pcm_start(*haptic_pcm);
+ if (ret < 0) {
+ ALOGE("cannot start haptic_pcm: %s", pcm_get_error(*haptic_pcm));
+ goto fail;
+ }
+
+ return true;
+ } else {
+ if (*haptic_pcm) {
+ pcm_close(*haptic_pcm);
+ *haptic_pcm = NULL;
+ }
+ return true;
+ }
+
+fail:
+ pcm_close(*haptic_pcm);
+ *haptic_pcm = NULL;
+ return false;
+}
+
+ndk::ScopedAStatus Vibrator::getSimpleDetails(Effect effect, EffectStrength strength,
+ uint32_t *outEffectIndex, uint32_t *outTimeMs,
+ uint32_t *outVolLevel) {
+ uint32_t effectIndex;
+ uint32_t timeMs;
+ float intensity;
+ uint32_t volLevel;
+ switch (strength) {
+ case EffectStrength::LIGHT:
+ intensity = 0.5f;
+ break;
+ case EffectStrength::MEDIUM:
+ intensity = 0.7f;
+ break;
+ case EffectStrength::STRONG:
+ intensity = 1.0f;
+ break;
+ default:
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+
+ switch (effect) {
+ case Effect::TEXTURE_TICK:
+ effectIndex = WAVEFORM_LIGHT_TICK_INDEX;
+ intensity *= 0.5f;
+ break;
+ case Effect::TICK:
+ effectIndex = WAVEFORM_CLICK_INDEX;
+ intensity *= 0.5f;
+ break;
+ case Effect::CLICK:
+ effectIndex = WAVEFORM_CLICK_INDEX;
+ intensity *= 0.7f;
+ break;
+ case Effect::HEAVY_CLICK:
+ effectIndex = WAVEFORM_CLICK_INDEX;
+ intensity *= 1.0f;
+ break;
+ default:
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+
+ volLevel = intensityToVolLevel(intensity, effectIndex);
+ timeMs = mEffectDurations[effectIndex] + MAX_COLD_START_LATENCY_MS;
+
+ *outEffectIndex = effectIndex;
+ *outTimeMs = timeMs;
+ *outVolLevel = volLevel;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength strength,
+ uint32_t *outTimeMs, dspmem_chunk *outCh) {
+ ndk::ScopedAStatus status;
+ uint32_t timeMs = 0;
+ uint32_t thisEffectIndex;
+ uint32_t thisTimeMs;
+ uint32_t thisVolLevel;
+ switch (effect) {
+ case Effect::DOUBLE_CLICK:
+ dspmem_chunk_write(outCh, 8, 0); /* Padding */
+ dspmem_chunk_write(outCh, 8, 2); /* nsections */
+ dspmem_chunk_write(outCh, 8, 0); /* repeat */
+
+ status = getSimpleDetails(Effect::CLICK, strength, &thisEffectIndex, &thisTimeMs,
+ &thisVolLevel);
+ if (!status.isOk()) {
+ return status;
+ }
+ timeMs += thisTimeMs;
+
+ dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisVolLevel)); /* amplitude */
+ dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisEffectIndex)); /* index */
+ dspmem_chunk_write(outCh, 8, 0); /* repeat */
+ dspmem_chunk_write(outCh, 8, 0); /* flags */
+ dspmem_chunk_write(outCh, 16,
+ (uint16_t)(0xFFFF & WAVEFORM_DOUBLE_CLICK_SILENCE_MS)); /* delay */
+
+ timeMs += WAVEFORM_DOUBLE_CLICK_SILENCE_MS + MAX_PAUSE_TIMING_ERROR_MS;
+
+ status = getSimpleDetails(Effect::HEAVY_CLICK, strength, &thisEffectIndex, &thisTimeMs,
+ &thisVolLevel);
+ if (!status.isOk()) {
+ return status;
+ }
+ timeMs += thisTimeMs;
+
+ dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisVolLevel)); /* amplitude */
+ dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisEffectIndex)); /* index */
+ dspmem_chunk_write(outCh, 8, 0); /* repeat */
+ dspmem_chunk_write(outCh, 8, 0); /* flags */
+ dspmem_chunk_write(outCh, 16, 0); /* delay */
+ dspmem_chunk_flush(outCh);
+
+ break;
+ default:
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+
+ *outTimeMs = timeMs;
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::getPrimitiveDetails(CompositePrimitive primitive,
+ uint32_t *outEffectIndex) {
+ uint32_t effectIndex;
+ uint32_t primitiveBit = 1 << int32_t(primitive);
+ if ((primitiveBit & mSupportedPrimitivesBits) == 0x0) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+
+ switch (primitive) {
+ case CompositePrimitive::NOOP:
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ case CompositePrimitive::CLICK:
+ effectIndex = WAVEFORM_CLICK_INDEX;
+ break;
+ case CompositePrimitive::THUD:
+ effectIndex = WAVEFORM_THUD_INDEX;
+ break;
+ case CompositePrimitive::SPIN:
+ effectIndex = WAVEFORM_SPIN_INDEX;
+ break;
+ case CompositePrimitive::QUICK_RISE:
+ effectIndex = WAVEFORM_QUICK_RISE_INDEX;
+ break;
+ case CompositePrimitive::SLOW_RISE:
+ effectIndex = WAVEFORM_SLOW_RISE_INDEX;
+ break;
+ case CompositePrimitive::QUICK_FALL:
+ effectIndex = WAVEFORM_QUICK_FALL_INDEX;
+ break;
+ case CompositePrimitive::LIGHT_TICK:
+ effectIndex = WAVEFORM_LIGHT_TICK_INDEX;
+ break;
+ case CompositePrimitive::LOW_TICK:
+ effectIndex = WAVEFORM_LOW_TICK_INDEX;
+ break;
+ default:
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+
+ *outEffectIndex = effectIndex;
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::uploadOwtEffect(uint8_t *owtData, uint32_t numBytes,
+ uint32_t *outEffectIndex) {
+ if (owtData == nullptr) {
+ ALOGE("Invalid waveform bank");
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ bool isPwle = (*reinterpret_cast<uint16_t *>(owtData) != 0x0000);
+ WaveformIndex targetId = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE;
+
+ /* Erase the created OWT waveform. */
+ bool isCreated = (mFfEffects[targetId].id != -1);
+ if (isCreated && ioctl(mInputFd, EVIOCRMFF, mFfEffects[targetId].id) < 0) {
+ ALOGW("Failed to erase effect %d(%d) (%d): %s", targetId, mFfEffects[targetId].id, errno,
+ strerror(errno));
+ mFfEffects[targetId].id = -1;
+ }
+
+ uint32_t freeBytes;
+ mHwApi->getOwtFreeSpace(&freeBytes);
+ if (numBytes > freeBytes) {
+ ALOGE("Effect %d length: %d > %d!", targetId, numBytes, freeBytes);
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+
+ /* Create a new OWT waveform to update the PWLE or composite effect. */
+ mFfEffects[targetId].id = -1; /* New Effect */
+ mFfEffects[targetId].u.periodic.custom_len = numBytes / 2; /* # of 16-bit elements */
+ delete[](mFfEffects[targetId].u.periodic.custom_data);
+ mFfEffects[targetId].u.periodic.custom_data =
+ new int16_t[mFfEffects[targetId].u.periodic.custom_len]{0x0000};
+ if (mFfEffects[targetId].u.periodic.custom_data == nullptr) {
+ ALOGE("Failed to allocate memory for custom data\n");
+ return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+ }
+ memcpy(mFfEffects[targetId].u.periodic.custom_data, owtData, numBytes);
+
+ if (ioctl(mInputFd, EVIOCSFF, &mFfEffects[targetId]) < 0) {
+ ALOGE("Failed to upload effect %d (%d): %s", targetId, errno, strerror(errno));
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ *outEffectIndex = mFfEffects[targetId].id;
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strength,
+ const std::shared_ptr<IVibratorCallback> &callback,
+ int32_t *outTimeMs) {
+ ndk::ScopedAStatus status;
+ uint32_t effectIndex;
+ uint32_t timeMs = 0;
+ uint32_t volLevel;
+ dspmem_chunk *ch = nullptr;
+
+ switch (effect) {
+ case Effect::TEXTURE_TICK:
+ // fall-through
+ case Effect::TICK:
+ // fall-through
+ case Effect::CLICK:
+ // fall-through
+ case Effect::HEAVY_CLICK:
+ status = getSimpleDetails(effect, strength, &effectIndex, &timeMs, &volLevel);
+ break;
+ case Effect::DOUBLE_CLICK:
+ ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_COMP]{0x00},
+ FF_CUSTOM_DATA_LEN_MAX_COMP);
+ status = getCompoundDetails(effect, strength, &timeMs, ch);
+ break;
+ default:
+ status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ break;
+ }
+ if (!status.isOk()) {
+ goto exit;
+ }
+
+ status = performEffect(static_cast<uint16_t>(effectIndex), volLevel, ch, callback);
+
+exit:
+ *outTimeMs = timeMs;
+ return status;
+}
+
+ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel,
+ dspmem_chunk *ch,
+ const std::shared_ptr<IVibratorCallback> &callback) {
+ if (ch) {
+ ndk::ScopedAStatus status = uploadOwtEffect(ch->head, dspmem_chunk_bytes(ch), &effectIndex);
+ delete ch;
+ if (!status.isOk()) {
+ return status;
+ }
+ setEffectAmplitude(VOLTAGE_SCALE_MAX, VOLTAGE_SCALE_MAX);
+ } else {
+ setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX);
+ }
+
+ return on(MAX_TIME_MS, effectIndex, callback);
+}
+
+void Vibrator::waitForComplete(std::shared_ptr<IVibratorCallback> &&callback) {
+ if (!mHwApi->pollVibeState("Vibe state: Haptic\n", POLLING_TIMEOUT)) {
+ ALOGE("Fail to get state \"Haptic\"");
+ }
+ mHwApi->pollVibeState("Vibe state: Stopped\n");
+
+ if (callback) {
+ auto ret = callback->onComplete();
+ if (!ret.isOk()) {
+ ALOGE("Failed completion callback: %d", ret.getExceptionCode());
+ }
+ }
+}
+
+uint32_t Vibrator::intensityToVolLevel(float intensity, uint32_t effectIndex) {
+ uint32_t volLevel;
+ auto calc = [](float intst, std::array<uint32_t, 2> v) -> uint32_t {
+ return std::lround(intst * (v[1] - v[0])) + v[0];
+ };
+
+ switch (effectIndex) {
+ case WAVEFORM_LIGHT_TICK_INDEX:
+ volLevel = calc(intensity, mTickEffectVol);
+ break;
+ case WAVEFORM_QUICK_RISE_INDEX:
+ // fall-through
+ case WAVEFORM_QUICK_FALL_INDEX:
+ volLevel = calc(intensity, mLongEffectVol);
+ break;
+ case WAVEFORM_CLICK_INDEX:
+ // fall-through
+ case WAVEFORM_THUD_INDEX:
+ // fall-through
+ case WAVEFORM_SPIN_INDEX:
+ // fall-through
+ case WAVEFORM_SLOW_RISE_INDEX:
+ // fall-through
+ default:
+ volLevel = calc(intensity, mClickEffectVol);
+ break;
+ }
+ return volLevel;
+}
+
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/vibrator/cs40l26/Vibrator.h b/vibrator/cs40l26/Vibrator.h
new file mode 100644
index 00000000..dc7c42b0
--- /dev/null
+++ b/vibrator/cs40l26/Vibrator.h
@@ -0,0 +1,190 @@
+/*
+ * 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 <android-base/unique_fd.h>
+#include <tinyalsa/asoundlib.h>
+
+#include <array>
+#include <fstream>
+#include <future>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace vibrator {
+
+class Vibrator : public BnVibrator {
+ public:
+ // APIs for interfacing with the kernel driver.
+ class HwApi {
+ public:
+ virtual ~HwApi() = default;
+ // Stores the LRA resonant frequency to be used for PWLE playback
+ // and click compensation.
+ virtual bool setF0(std::string value) = 0;
+ // Stores the frequency offset for long vibrations.
+ virtual bool setF0Offset(uint32_t value) = 0;
+ // Stores the LRA series resistance to be used for click
+ // compensation.
+ virtual bool setRedc(std::string value) = 0;
+ // Stores the LRA Q factor to be used for Q-dependent waveform
+ // selection.
+ virtual bool setQ(std::string value) = 0;
+ // Reports the number of effect waveforms loaded in firmware.
+ virtual bool getEffectCount(uint32_t *value) = 0;
+ // Blocks until vibrator reaches desired state
+ // ("Vibe state: Haptic" means enabled).
+ // ("Vibe state: Stopped" means disabled).
+ virtual bool pollVibeState(std::string value, int32_t timeoutMs = -1) = 0;
+ // Enables/disables closed-loop active braking.
+ virtual bool setClabEnable(bool value) = 0;
+ // Reports the number of available PWLE segments.
+ virtual bool getAvailablePwleSegments(uint32_t *value) = 0;
+ // Specifies piecewise-linear specifications to generate complex
+ // waveforms.
+ virtual bool setPwle(std::string value) = 0;
+ // Reports whether getOwtFreeSpace() is supported.
+ virtual bool hasOwtFreeSpace() = 0;
+ // Reports the available OWT bytes.
+ virtual bool getOwtFreeSpace(uint32_t *value) = 0;
+ // Emit diagnostic information to the given file.
+ virtual void debug(int fd) = 0;
+ };
+
+ // APIs for obtaining calibration/configuration data from persistent memory.
+ class HwCal {
+ public:
+ virtual ~HwCal() = default;
+ // Obtain the calibration version
+ virtual bool getVersion(uint32_t *value) = 0;
+ // Obtains the LRA resonant frequency to be used for PWLE playback
+ // and click compensation.
+ virtual bool getF0(std::string *value) = 0;
+ // Obtains the LRA series resistance to be used for click
+ // compensation.
+ virtual bool getRedc(std::string *value) = 0;
+ // Obtains the LRA Q factor to be used for Q-dependent waveform
+ // selection.
+ virtual bool getQ(std::string *value) = 0;
+ // Obtains frequency shift for long vibrations.
+ virtual bool getLongFrequencyShift(int32_t *value) = 0;
+ // Obtains the v0/v1(min/max) voltage levels to be applied for
+ // tick/click/long in units of 1%.
+ virtual bool getTickVolLevels(std::array<uint32_t, 2> *value) = 0;
+ virtual bool getClickVolLevels(std::array<uint32_t, 2> *value) = 0;
+ virtual bool getLongVolLevels(std::array<uint32_t, 2> *value) = 0;
+ // Checks if the chirp feature is enabled.
+ virtual bool isChirpEnabled() = 0;
+ // Obtains the supported primitive effects.
+ virtual bool getSupportedPrimitives(uint32_t *value) = 0;
+ // Emit diagnostic information to the given file.
+ virtual void debug(int fd) = 0;
+ };
+
+ public:
+ Vibrator(std::unique_ptr<HwApi> hwapi, std::unique_ptr<HwCal> hwcal);
+
+ ndk::ScopedAStatus getCapabilities(int32_t *_aidl_return) override;
+ ndk::ScopedAStatus off() override;
+ ndk::ScopedAStatus on(int32_t timeoutMs,
+ const std::shared_ptr<IVibratorCallback> &callback) override;
+ ndk::ScopedAStatus perform(Effect effect, EffectStrength strength,
+ const std::shared_ptr<IVibratorCallback> &callback,
+ int32_t *_aidl_return) override;
+ ndk::ScopedAStatus getSupportedEffects(std::vector<Effect> *_aidl_return) override;
+ ndk::ScopedAStatus setAmplitude(float amplitude) override;
+ ndk::ScopedAStatus setExternalControl(bool enabled) override;
+ ndk::ScopedAStatus getCompositionDelayMax(int32_t *maxDelayMs);
+ ndk::ScopedAStatus getCompositionSizeMax(int32_t *maxSize);
+ ndk::ScopedAStatus getSupportedPrimitives(std::vector<CompositePrimitive> *supported) override;
+ ndk::ScopedAStatus getPrimitiveDuration(CompositePrimitive primitive,
+ int32_t *durationMs) override;
+ ndk::ScopedAStatus compose(const std::vector<CompositeEffect> &composite,
+ const std::shared_ptr<IVibratorCallback> &callback) override;
+ ndk::ScopedAStatus getSupportedAlwaysOnEffects(std::vector<Effect> *_aidl_return) override;
+ ndk::ScopedAStatus alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) override;
+ ndk::ScopedAStatus alwaysOnDisable(int32_t id) override;
+ ndk::ScopedAStatus getResonantFrequency(float *resonantFreqHz) override;
+ ndk::ScopedAStatus getQFactor(float *qFactor) override;
+ ndk::ScopedAStatus getFrequencyResolution(float *freqResolutionHz) override;
+ ndk::ScopedAStatus getFrequencyMinimum(float *freqMinimumHz) override;
+ ndk::ScopedAStatus getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) override;
+ ndk::ScopedAStatus getPwlePrimitiveDurationMax(int32_t *durationMs) override;
+ ndk::ScopedAStatus getPwleCompositionSizeMax(int32_t *maxSize) override;
+ ndk::ScopedAStatus getSupportedBraking(std::vector<Braking> *supported) override;
+ ndk::ScopedAStatus composePwle(const std::vector<PrimitivePwle> &composite,
+ const std::shared_ptr<IVibratorCallback> &callback) override;
+
+ binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
+
+ private:
+ ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex,
+ const std::shared_ptr<IVibratorCallback> &callback);
+ // set 'amplitude' based on an arbitrary scale determined by 'maximum'
+ ndk::ScopedAStatus setEffectAmplitude(float amplitude, float maximum);
+ ndk::ScopedAStatus setGlobalAmplitude(bool set);
+ // 'simple' effects are those precompiled and loaded into the controller
+ ndk::ScopedAStatus getSimpleDetails(Effect effect, EffectStrength strength,
+ uint32_t *outEffectIndex, uint32_t *outTimeMs,
+ uint32_t *outVolLevel);
+ // 'compound' effects are those composed by stringing multiple 'simple' effects
+ ndk::ScopedAStatus getCompoundDetails(Effect effect, EffectStrength strength,
+ uint32_t *outTimeMs, struct dspmem_chunk *outCh);
+ ndk::ScopedAStatus getPrimitiveDetails(CompositePrimitive primitive, uint32_t *outEffectIndex);
+ ndk::ScopedAStatus uploadOwtEffect(uint8_t *owtData, uint32_t num_bytes,
+ uint32_t *outEffectIndex);
+ ndk::ScopedAStatus performEffect(Effect effect, EffectStrength strength,
+ const std::shared_ptr<IVibratorCallback> &callback,
+ int32_t *outTimeMs);
+ ndk::ScopedAStatus performEffect(uint32_t effectIndex, uint32_t volLevel,
+ struct dspmem_chunk *ch,
+ const std::shared_ptr<IVibratorCallback> &callback);
+ ndk::ScopedAStatus setPwle(const std::string &pwleQueue);
+ bool isUnderExternalControl();
+ void waitForComplete(std::shared_ptr<IVibratorCallback> &&callback);
+ uint32_t intensityToVolLevel(float intensity, uint32_t effectIndex);
+ bool findHapticAlsaDevice(int *card, int *device);
+ bool hasHapticAlsaDevice();
+ bool enableHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card, int device);
+
+ std::unique_ptr<HwApi> mHwApi;
+ std::unique_ptr<HwCal> mHwCal;
+ uint32_t mF0Offset;
+ std::array<uint32_t, 2> mTickEffectVol;
+ std::array<uint32_t, 2> mClickEffectVol;
+ std::array<uint32_t, 2> mLongEffectVol;
+ std::vector<uint32_t> mEffectDurations;
+ std::future<void> mAsyncHandle;
+ int32_t compositionSizeMax;
+ ::android::base::unique_fd mInputFd;
+ int8_t mActiveId{-1};
+ struct pcm *mHapticPcm;
+ int mCard;
+ int mDevice;
+ bool mHasHapticAlsaDevice;
+ bool mIsUnderExternalControl;
+ float mLongEffectScale = 1.0;
+ bool mIsChirpEnabled;
+ uint32_t mSupportedPrimitivesBits = 0x0;
+ std::vector<CompositePrimitive> mSupportedPrimitives;
+};
+
+} // namespace vibrator
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc
new file mode 100644
index 00000000..539a83e8
--- /dev/null
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.rc
@@ -0,0 +1,40 @@
+on boot && 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
+
+ enable vendor.vibrator.cs40l26-dual
+
+service vendor.vibrator.cs40l26-dual /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-dual
+ class hal
+ user system
+ group system input
+
+ setenv INPUT_EVENT_NAME cs40l26_dual_input
+ 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
+ "
+
+ disabled
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.xml b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.xml
new file mode 100644
index 00000000..1bd3e7e8
--- /dev/null
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dual.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.vibrator</name>
+ <version>2</version>
+ <fqname>IVibrator/dual</fqname>
+ </hal>
+</manifest>
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dvt-dual.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dvt-dual.rc
new file mode 100644
index 00000000..c897f061
--- /dev/null
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dvt-dual.rc
@@ -0,0 +1,40 @@
+on boot && property:vendor.all.modules.ready=1 && property:ro.boot.revision=DVT1.0
+ 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
+
+ enable vendor.vibrator.cs40l26-dual
+
+service vendor.vibrator.cs40l26-dual /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-dvt-dual
+ class hal
+ user system
+ group system input
+
+ setenv INPUT_EVENT_NAME cs40l26_dual_input
+ 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
+ "
+
+ disabled
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dvt.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dvt.rc
new file mode 100644
index 00000000..c591bc45
--- /dev/null
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-dvt.rc
@@ -0,0 +1,40 @@
+on boot && property:vendor.all.modules.ready=1 && property:ro.boot.revision=DVT1.0
+ wait /sys/bus/i2c/devices/i2c-cs40l26a/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.cal
+ chown system system /mnt/vendor/persist/haptics
+ chown system system /mnt/vendor/persist/haptics/cs40l26.cal
+
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a/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/redc_stored
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a/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/f0_offset
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/owt_free_space
+
+ enable vendor.vibrator.cs40l26
+
+service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-dvt
+ class hal
+ user system
+ group system input
+
+ setenv INPUT_EVENT_NAME cs40l26_input
+ setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
+ setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal
+
+ setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a/
+ 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
+ "
+
+ disabled
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-evt-dual.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-evt-dual.rc
new file mode 100644
index 00000000..4f7fe788
--- /dev/null
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-evt-dual.rc
@@ -0,0 +1,40 @@
+on boot && property:vendor.all.modules.ready=1 && property:ro.boot.revision=EVT1.0
+ 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
+
+ enable vendor.vibrator.cs40l26-evt-dual
+
+service vendor.vibrator.cs40l26-evt-dual /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-evt-dual
+ class hal
+ user system
+ group system input
+
+ setenv INPUT_EVENT_NAME cs40l26_dual_input
+ 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
+ "
+
+ disabled
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-evt.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-evt.rc
new file mode 100644
index 00000000..d521ce90
--- /dev/null
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26-evt.rc
@@ -0,0 +1,40 @@
+on boot && property:vendor.all.modules.ready=1 && property:ro.boot.revision=EVT1.0
+ wait /sys/bus/i2c/devices/i2c-cs40l26a/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.cal
+ chown system system /mnt/vendor/persist/haptics
+ chown system system /mnt/vendor/persist/haptics/cs40l26.cal
+
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a/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/redc_stored
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a/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/f0_offset
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/owt_free_space
+
+ enable vendor.vibrator.cs40l26-evt
+
+service vendor.vibrator.cs40l26-evt /vendor/bin/hw/android.hardware.vibrator-service.cs40l26-evt
+ class hal
+ user system
+ group system input
+
+ setenv INPUT_EVENT_NAME cs40l26_input
+ setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
+ setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal
+
+ setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a/
+ 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
+ "
+
+ disabled
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc
new file mode 100644
index 00000000..dbb2c417
--- /dev/null
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.rc
@@ -0,0 +1,40 @@
+on boot && property:vendor.all.modules.ready=1
+ wait /sys/bus/i2c/devices/i2c-cs40l26a/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.cal
+ chown system system /mnt/vendor/persist/haptics
+ chown system system /mnt/vendor/persist/haptics/cs40l26.cal
+
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a/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/redc_stored
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a/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/f0_offset
+ chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/owt_free_space
+
+ enable vendor.vibrator.cs40l26
+
+service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service.cs40l26
+ class hal
+ user system
+ group system input
+
+ setenv INPUT_EVENT_NAME cs40l26_input
+ setenv PROPERTY_PREFIX ro.vendor.vibrator.hal.
+ setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal
+
+ setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a/
+ 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
+ "
+
+ disabled
diff --git a/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.xml b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.xml
new file mode 100644
index 00000000..4db8f8c5
--- /dev/null
+++ b/vibrator/cs40l26/android.hardware.vibrator-service.cs40l26.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.vibrator</name>
+ <version>2</version>
+ <fqname>IVibrator/default</fqname>
+ </hal>
+</manifest>
diff --git a/vibrator/cs40l26/device-stereo-dvt.mk b/vibrator/cs40l26/device-stereo-dvt.mk
new file mode 100644
index 00000000..21da45d9
--- /dev/null
+++ b/vibrator/cs40l26/device-stereo-dvt.mk
@@ -0,0 +1,3 @@
+PRODUCT_PACKAGES += \
+ android.hardware.vibrator-service.cs40l26-dvt \
+ android.hardware.vibrator-service.cs40l26-dvt-dual \
diff --git a/vibrator/cs40l26/device-stereo-evt.mk b/vibrator/cs40l26/device-stereo-evt.mk
new file mode 100644
index 00000000..e04282c7
--- /dev/null
+++ b/vibrator/cs40l26/device-stereo-evt.mk
@@ -0,0 +1,3 @@
+PRODUCT_PACKAGES += \
+ android.hardware.vibrator-service.cs40l26-evt \
+ android.hardware.vibrator-service.cs40l26-evt-dual \
diff --git a/vibrator/cs40l26/device-stereo.mk b/vibrator/cs40l26/device-stereo.mk
new file mode 100644
index 00000000..44d2b92c
--- /dev/null
+++ b/vibrator/cs40l26/device-stereo.mk
@@ -0,0 +1,6 @@
+PRODUCT_PACKAGES += \
+ android.hardware.vibrator-service.cs40l26 \
+ android.hardware.vibrator-service.cs40l26-dual \
+
+BOARD_SEPOLICY_DIRS += \
+ hardware/google/pixel-sepolicy/vibrator/cs40l26 \
diff --git a/vibrator/cs40l26/device.mk b/vibrator/cs40l26/device.mk
new file mode 100644
index 00000000..5a3dd4f8
--- /dev/null
+++ b/vibrator/cs40l26/device.mk
@@ -0,0 +1,6 @@
+PRODUCT_PACKAGES += \
+ android.hardware.vibrator-service.cs40l26
+
+BOARD_SEPOLICY_DIRS += \
+ hardware/google/pixel-sepolicy/vibrator/common \
+ hardware/google/pixel-sepolicy/vibrator/cs40l26
diff --git a/vibrator/cs40l26/service.cpp b/vibrator/cs40l26/service.cpp
new file mode 100644
index 00000000..27173d9f
--- /dev/null
+++ b/vibrator/cs40l26/service.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 "Hardware.h"
+#include "Vibrator.h"
+
+using ::aidl::android::hardware::vibrator::HwApi;
+using ::aidl::android::hardware::vibrator::HwCal;
+using ::aidl::android::hardware::vibrator::Vibrator;
+using ::android::defaultServiceManager;
+using ::android::ProcessState;
+using ::android::sp;
+using ::android::String16;
+
+#if !defined(VIBRATOR_NAME)
+#define VIBRATOR_NAME "default"
+#endif
+
+int main() {
+ auto svc = ndk::SharedRefBase::make<Vibrator>(std::make_unique<HwApi>(),
+ std::make_unique<HwCal>());
+ const auto svcName = std::string() + svc->descriptor + "/" + VIBRATOR_NAME;
+
+ ProcessState::initWithDriver("/dev/vndbinder");
+
+ auto svcBinder = svc->asBinder();
+ binder_status_t status = AServiceManager_addService(svcBinder.get(), svcName.c_str());
+ LOG_ALWAYS_FATAL_IF(status != STATUS_OK);
+
+ ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ ProcessState::self()->startThreadPool();
+
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ ABinderProcess_joinThreadPool();
+
+ return EXIT_FAILURE; // should not reach
+}