Merge Android 14 QPR3 to AOSP main

Bug: 346855327
Merged-In: I4e10554e6f7abbac2e06953c68bfba6f7ffe77d3
Change-Id: I53bf20ef8d2ccf02642adeaadc0d2b2f77a0dd24
This commit is contained in:
Xin Li 2024-06-13 10:48:14 -07:00
commit 2a16a2c557
23 changed files with 483 additions and 332 deletions

View file

@ -93,7 +93,7 @@
samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>
<mixPort name="incall capture" role="sink">
<mixPort name="incall capture" role="sink" maxActiveCount="2" maxOpenCount="2">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_MONO"/>

View file

@ -92,7 +92,7 @@
samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>
<mixPort name="incall capture" role="sink">
<mixPort name="incall capture" role="sink" maxActiveCount="2" maxOpenCount="2">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_MONO"/>

View file

@ -88,7 +88,7 @@
samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>
<mixPort name="incall capture" role="sink">
<mixPort name="incall capture" role="sink" maxActiveCount="2" maxOpenCount="2">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_MONO"/>

View file

@ -93,7 +93,7 @@
samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>
<mixPort name="incall capture" role="sink">
<mixPort name="incall capture" role="sink" maxActiveCount="2" maxOpenCount="2">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_MONO"/>

View file

@ -18,6 +18,7 @@ on fs
chown system system /sys/class/backlight/panel1-backlight/dimming_on
chown system system /sys/class/backlight/panel1-backlight/hbm_mode
chown system system /sys/class/backlight/panel1-backlight/local_hbm_mode
chown system system /sys/class/backlight/panel1-backlight/allow_wakeup_by_state_change
chown system system /sys/devices/platform/exynos-drm/secondary-panel/gamma
chown system system /sys/devices/platform/exynos-drm/secondary-panel/min_vrefresh
chown system system /sys/devices/platform/exynos-drm/secondary-panel/idle_delay_ms

View file

@ -21,6 +21,15 @@ TARGET_KERNEL_DIR ?= device/google/felix-kernel
TARGET_BOARD_KERNEL_HEADERS := device/google/felix-kernel/kernel-headers
TARGET_RECOVERY_DEFAULT_ROTATION := ROTATION_RIGHT
ifdef RELEASE_GOOGLE_FELIX_KERNEL_VERSION
TARGET_LINUX_KERNEL_VERSION := $(RELEASE_GOOGLE_FELIX_KERNEL_VERSION)
endif
ifdef RELEASE_GOOGLE_FELIX_KERNEL_DIR
TARGET_KERNEL_DIR := $(RELEASE_GOOGLE_FELIX_KERNEL_DIR)
TARGET_BOARD_KERNEL_HEADERS := $(RELEASE_GOOGLE_FELIX_KERNEL_DIR)/kernel-headers
endif
$(call inherit-product-if-exists, vendor/google_devices/felix/prebuilts/device-vendor-felix.mk)
$(call inherit-product-if-exists, vendor/google_devices/gs201/prebuilts/device-vendor.mk)
$(call inherit-product-if-exists, vendor/google_devices/gs201/proprietary/device-vendor.mk)
@ -211,7 +220,7 @@ PRODUCT_SOONG_NAMESPACES += \
# Increment the SVN for any official public releases
PRODUCT_VENDOR_PROPERTIES += \
ro.vendor.build.svn=37
ro.vendor.build.svn=51
# Vibrator HAL
PRODUCT_VENDOR_PROPERTIES +=\
@ -257,7 +266,7 @@ PRODUCT_PACKAGES += \
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts
ifneq (,$(filter AP1%,$(RELEASE_PLATFORM_VERSION)))
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts/trusty/24Q1
else ifneq (,$(filter AP2%,$(RELEASE_PLATFORM_VERSION)))
else ifneq (,$(filter AP2% AP3%,$(RELEASE_PLATFORM_VERSION)))
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts/trusty/24Q2
else
PRODUCT_SOONG_NAMESPACES += vendor/google_devices/felix/prebuilts/trusty/trunk
@ -395,3 +404,12 @@ PRODUCT_VENDOR_PROPERTIES += \
PRODUCT_PRODUCT_PROPERTIES += \
ro.quick_start.oem_id=00e0 \
ro.quick_start.device_id=felix
# Set support hide display cutout feature
PRODUCT_PRODUCT_PROPERTIES += \
ro.support_hide_display_cutout=true
PRODUCT_PACKAGES += \
NoCutoutOverlay \
AvoidAppsInCutoutOverlay

View file

@ -23,7 +23,7 @@ BOARD_KERNEL_CMDLINE += swiotlb=noforce
RELEASE_GOOGLE_PRODUCT_RADIO_DIR := $(RELEASE_GOOGLE_FELIX_RADIO_DIR)
ifneq (,$(filter AP1%,$(RELEASE_PLATFORM_VERSION)))
RELEASE_GOOGLE_PRODUCT_BOOTLOADER_DIR := bootloader/24Q1
else ifneq (,$(filter AP2%,$(RELEASE_PLATFORM_VERSION)))
else ifneq (,$(filter AP2% AP3%,$(RELEASE_PLATFORM_VERSION)))
RELEASE_GOOGLE_PRODUCT_BOOTLOADER_DIR := bootloader/24Q2
else
RELEASE_GOOGLE_PRODUCT_BOOTLOADER_DIR := bootloader/trunk

View file

@ -78,8 +78,17 @@
m 589.2,66.53 a 49.5,49.5 0 0 1 -49.5,49.5 49.5,49.5 0 0 1 -49.5,-49.5 49.5,49.5 0 0 1 49.5,-49.5 49.5,49.5 0 0 1 49.5,49.5 z
</string>
<!-- Camera 0 is the front camera -->
<!-- 1 is the logical id of the front camera -->
<string translatable="false" name="config_protectedCameraId">1</string>
<!-- 5 is the physical id of the outer screen front camera -->
<string translatable="false" name="config_protectedPhysicalCameraId">5</string>
<!-- The properties of the face auth front camera in pixels -->
<integer-array name="config_face_auth_props">
<item>540</item> <!-- sensorLocationX -->
<item>66</item> <!-- sensorLocationY -->
<item>50</item> <!--sensorRadius -->
</integer-array>
<!-- Comma-separated list of packages to exclude from camera protection. In our case,
ignore the gaze detection package -->

View file

@ -35,7 +35,7 @@
SuplVersion="2"
SuplMinorVersion="0"
SuplOtdoaCapable="true"
SuplOtdoaCapable2="true"
SuplOtdoaCapable2="false"
SuplGlonassCapable = "true"
SuplGalileoCapable = "true"
SuplBdsCapable = "true"
@ -60,7 +60,7 @@
CpLppeUseAgnssLocForEmptyDbh="true"
CpLppHighAccuracyShapeMode="1"
ReAidingOnHotStart="false"
ReAidingIntervalSec="1200"
ReAidingIntervalSec="3600"
RuntimeSwLteFilterEnable="true"
PpsDevice="/sys/class/pps/pps0/assert_elapsed"
SensorsMask="0x244"
@ -93,6 +93,8 @@
MinGpsWeekNumber="2216"
OnChipAccMask="50"
EnableB1C="false"
RTICacheTimeoutSec="3600"
/>
<gll_features

View file

@ -34,7 +34,7 @@
SuplVersion="2"
SuplMinorVersion="0"
SuplOtdoaCapable="true"
SuplOtdoaCapable2="true"
SuplOtdoaCapable2="false"
SuplGlonassCapable = "true"
SuplGalileoCapable = "true"
SuplBdsCapable = "true"
@ -59,7 +59,7 @@
CpLppeUseAgnssLocForEmptyDbh="true"
CpLppHighAccuracyShapeMode="1"
ReAidingOnHotStart="false"
ReAidingIntervalSec="1200"
ReAidingIntervalSec="3600"
RuntimeSwLteFilterEnable="true"
PpsDevice="/sys/class/pps/pps0/assert_elapsed"
SensorsMask="0x244"
@ -92,6 +92,8 @@
MinGpsWeekNumber="2216"
OnChipAccMask="50"
EnableB1C="false"
RTICacheTimeoutSec="3600"
/>
<gll_features

View file

@ -8,7 +8,7 @@ NFC_DEBUG_ENABLED=0
###############################################################################
# Vendor specific mode to enable FW (RF & SWP) traces.
STNFC_FW_DEBUG_ENABLED=0
STNFC_FW_DEBUG_ENABLED=1
###############################################################################
# File used for NFA storage

View file

@ -784,6 +784,17 @@
"Type": "DoHint",
"Value": "LAUNCH_EXTEND"
},
{
"PowerHint": "LAUNCH",
"Type": "DoHint",
"Value": "LAUNCH_PMU"
},
{
"PowerHint": "LAUNCH_PMU",
"Node": "PMU_POLL",
"Duration": 3000,
"Value": "0"
},
{
"PowerHint": "LAUNCH_EXTEND",
"Node": "CPUBigClusterMaxFreq",
@ -939,6 +950,12 @@
"Duration": 1000,
"Value": "4-7"
},
{
"PowerHint": "CAMERA_LAUNCH",
"Node": "PMU_POLL",
"Duration": 1000,
"Value": "0"
},
{
"PowerHint": "CAMERA_LAUNCH_EXTENDED",
"Node": "CPUBigClusterMaxFreq",
@ -963,6 +980,12 @@
"Duration": 2000,
"Value": "0"
},
{
"PowerHint": "CAMERA_LAUNCH_EXTENDED",
"Node": "PMU_POLL",
"Duration": 2000,
"Value": "0"
},
{
"PowerHint": "CAMERA_THERMAL_CPU_THROTTLE",
"Node": "CPUBigClusterMaxFreq",
@ -984,21 +1007,33 @@
{
"PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE",
"Node": "CPUBigClusterMaxFreq",
"Duration": 1000,
"Duration": 2500,
"Value": "1826000"
},
{
"PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE",
"Node": "CPUMidClusterMaxFreq",
"Duration": 1000,
"Duration": 2500,
"Value": "1491000"
},
{
"PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE",
"Node": "CPULittleClusterMaxFreq",
"Duration": 1000,
"Duration": 2500,
"Value": "1401000"
},
{
"PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE",
"Node": "PMU_POLL",
"Duration": 2500,
"Value": "0"
},
{
"PowerHint": "CAMERA_CAPTURE_CPU_THROTTLE",
"Node": "EM_Profile",
"Duration": 2500,
"Value": "default"
},
{
"PowerHint": "CAMERA_SHOT",
"Node": "MemFreq",
@ -1467,12 +1502,6 @@
"Duration": 0,
"Value": "1"
},
{
"PowerHint": "CAMERA_STREAMING_STANDARD",
"Node": "CDPreferIdle",
"Duration": 0,
"Value": "0"
},
{
"PowerHint": "CAMERA_STREAMING_STANDARD",
"Node": "PMU_POLL",

View file

@ -38,36 +38,50 @@ using aidl::android::hardware::power::stats::PowerStatsEnergyConsumer;
void addDisplay(std::shared_ptr<PowerStats> p) {
// Add display residency stats for inner display
std::vector<std::string> inner_states = {
"Off",
"LP: 1840x2208@1",
"LP: 1840x2208@30",
"On: 1840x2208@1",
"On: 1840x2208@10",
"On: 1840x2208@60",
"On: 1840x2208@120",
"HBM: 1840x2208@60",
"HBM: 1840x2208@120"};
struct stat primaryBuffer;
if (!stat("/sys/class/drm/card0/device/primary-panel/time_in_state", &primaryBuffer)) {
// time_in_state exists
addDisplayMrrByEntity(p, "Inner Display", "/sys/class/drm/card0/device/primary-panel/");
} else {
// time_in_state doesn't exist
std::vector<std::string> inner_states = {
"Off",
"LP: 1840x2208@1",
"LP: 1840x2208@30",
"On: 1840x2208@1",
"On: 1840x2208@10",
"On: 1840x2208@60",
"On: 1840x2208@120",
"HBM: 1840x2208@60",
"HBM: 1840x2208@120"};
p->addStateResidencyDataProvider(std::make_unique<DisplayStateResidencyDataProvider>(
"Inner Display",
"/sys/class/backlight/panel0-backlight/state",
inner_states));
p->addStateResidencyDataProvider(std::make_unique<DisplayStateResidencyDataProvider>(
"Inner Display",
"/sys/class/backlight/panel0-backlight/state",
inner_states));
}
// Add display residency stats for outer display
std::vector<std::string> outer_states = {
"Off",
"LP: 1080x2092@30",
"On: 1080x2092@10",
"On: 1080x2092@60",
"On: 1080x2092@120",
"HBM: 1080x2092@60",
"HBM: 1080x2092@120"};
struct stat secondaryBuffer;
if (!stat("/sys/class/drm/card0/device/secondary-panel/time_in_state", &secondaryBuffer)) {
// time_in_state exists
addDisplayMrrByEntity(p, "Outer Display", "/sys/class/drm/card0/device/secondary-panel/");
} else {
// time_in_state doesn't exist
std::vector<std::string> outer_states = {
"Off",
"LP: 1080x2092@30",
"On: 1080x2092@10",
"On: 1080x2092@60",
"On: 1080x2092@120",
"HBM: 1080x2092@60",
"HBM: 1080x2092@120"};
p->addStateResidencyDataProvider(std::make_unique<DisplayStateResidencyDataProvider>(
"Outer Display",
"/sys/class/backlight/panel1-backlight/state",
outer_states));
p->addStateResidencyDataProvider(std::make_unique<DisplayStateResidencyDataProvider>(
"Outer Display",
"/sys/class/backlight/panel1-backlight/state",
outer_states));
}
// Add display energy consumer
p->addEnergyConsumer(PowerStatsEnergyConsumer::createMeterConsumer(
@ -142,15 +156,7 @@ void addGPU(std::shared_ptr<PowerStats> p) {
}
std::string getNfcPath() {
struct stat buffer;
int size = 128;
char path[size];
for (int i = 0; i < 10; i++) {
std::snprintf(path, size,
"/sys/devices/platform/10970000.hsi2c/i2c-%d/i2c-st21nfc/power_stats", i);
if (!stat(path, &buffer)) break;
}
return std::string(path);
return std::string("/sys/devices/platform/10970000.hsi2c/i2c-8/8-0008/power_stats");
}
int main() {

View file

@ -32,4 +32,5 @@
<item>103</item>
<item>103</item>
</integer-array>
<bool name="nfc_observe_mode_supported">true</bool>
</resources>

View file

@ -167,4 +167,12 @@
regulatory approval (for example, FCC pre-approval is required according to "594280 D01
Software Configuration Control v02r01").-->
<bool translatable="false" name ="config_wifiUpdateCountryCodeFromScanResultGeneric">true</bool>
<!-- Boolean indicating performing a partial initial scan is enabled -->
<bool translatable="false" name="config_wifiEnablePartialInitialScan">true</bool>
<!-- Configure the max number of new channels to add into the initial partial scan list per network.
If equals to 0, it means there's no limit on the max number of channels to include per network.-->
<integer translatable="false" name="config_wifiInitialPartialScanMaxNewChannelsPerNetwork">3</integer>
</resources>

View file

@ -11,7 +11,6 @@
system_ext/lib64/libmediaadaptor.so \
system_ext/priv-app/ShannonRcs/ShannonRcs.apk \
system_ext/priv-app/ShannonIms/ShannonIms.apk \
system_ext/priv-app/PixelQualifiedNetworksService/PixelQualifiedNetworksService.apk \
system_ext/priv-app/UwbVendorService/UwbVendorService.apk \
"
;;

View file

@ -22,22 +22,6 @@ ifneq ($(filter felix,$(TARGET_DEVICE)),)
endif
ifneq ($(filter felix,$(TARGET_DEVICE)),)
include $(CLEAR_VARS)
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_MODULE := PixelQualifiedNetworksService
LOCAL_MODULE_TAGS := optional
LOCAL_BUILT_MODULE_STEM := package.apk
LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MODULE_OWNER := samsung
LOCAL_MODULE_CLASS := APPS
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_CERTIFICATE := platform
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../COPYRIGHT $(LOCAL_PATH)/../LICENSE
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_MODULE := ShannonIms

View file

@ -17,7 +17,6 @@ PRODUCT_SOONG_NAMESPACES += \
# AOSP packages required by the blobs
PRODUCT_PACKAGES := \
PixelQualifiedNetworksService \
ShannonIms \
ShannonRcs \
UwbVendorService \

View file

@ -104,18 +104,25 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
.code = FF_GAIN,
.value = value,
};
if (value > 100) {
ALOGE("Invalid gain");
return false;
}
if (write(fd, (const void *)&gain, sizeof(gain)) != sizeof(gain)) {
return false;
}
return true;
}
bool setFFEffect(int fd, struct ff_effect *effect, uint16_t timeoutMs) override {
if (((*effect).replay.length != timeoutMs) || (ioctl(fd, EVIOCSFF, effect) < 0)) {
if (effect == nullptr) {
ALOGE("Invalid ff_effect");
return false;
}
if (ioctl(fd, EVIOCSFF, effect) < 0) {
ALOGE("setFFEffect fail");
return false;
} else {
return true;
}
return true;
}
bool setFFPlay(int fd, int8_t index, bool value) override {
struct input_event play = {
@ -184,16 +191,19 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
*haptic_pcm = NULL;
return false;
}
bool uploadOwtEffect(int fd, uint8_t *owtData, uint32_t numBytes, struct ff_effect *effect,
bool uploadOwtEffect(int fd, const uint8_t *owtData, const uint32_t numBytes, struct ff_effect *effect,
uint32_t *outEffectIndex, int *status) override {
(*effect).u.periodic.custom_len = numBytes / sizeof(uint16_t);
delete[] ((*effect).u.periodic.custom_data);
(*effect).u.periodic.custom_data = new int16_t[(*effect).u.periodic.custom_len]{0x0000};
if ((*effect).u.periodic.custom_data == nullptr) {
ALOGE("Failed to allocate memory for custom data\n");
if (owtData == nullptr || effect == nullptr || outEffectIndex == nullptr) {
ALOGE("Invalid argument owtData, ff_effect or outEffectIndex");
*status = EX_NULL_POINTER;
return false;
}
if (status == nullptr) {
ALOGE("Invalid argument status");
return false;
}
(*effect).u.periodic.custom_len = numBytes / sizeof(uint16_t);
memcpy((*effect).u.periodic.custom_data, owtData, numBytes);
if ((*effect).id != -1) {
@ -204,7 +214,6 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
(*effect).id = -1;
if (ioctl(fd, EVIOCSFF, effect) < 0) {
ALOGE("Failed to upload effect %d (%d): %s", *outEffectIndex, errno, strerror(errno));
delete[] ((*effect).u.periodic.custom_data);
*status = EX_ILLEGAL_STATE;
return false;
}
@ -225,6 +234,10 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
ALOGE("Invalid waveform index for OWT erase: %d", effectIndex);
return false;
}
if (effect == nullptr || (*effect).empty()) {
ALOGE("Invalid argument effect");
return false;
}
// Turn off the waiting time for SVC init phase to complete since chip
// should already under STOP state
setMinOnOffInterval(0);

View file

@ -27,6 +27,8 @@
#include <cmath>
#include <fstream>
#include <iostream>
#include <memory>
#include <optional>
#include <sstream>
#ifndef ARRAY_SIZE
@ -42,7 +44,6 @@ 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 uint16_t FF_CUSTOM_DATA_LEN_MAX_PWLE = 2302;
@ -85,7 +86,7 @@ static constexpr uint8_t PWLE_AMP_REG_BIT = 0x2;
static constexpr float PWLE_LEVEL_MIN = 0.0;
static constexpr float PWLE_LEVEL_MAX = 1.0;
static constexpr float CS40L26_PWLE_LEVEL_MIX = -1.0;
static constexpr float CS40L26_PWLE_LEVEL_MIN = -1.0;
static constexpr float CS40L26_PWLE_LEVEL_MAX = 0.9995118;
static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.00;
static constexpr float PWLE_FREQUENCY_MIN_HZ = 1.00;
@ -157,79 +158,210 @@ enum vibe_state {
VIBE_STATE_ASP,
};
static int min(int x, int y) {
return x < y ? x : y;
}
class DspMemChunk {
private:
std::unique_ptr<uint8_t[]> head;
size_t bytes = 0;
uint8_t waveformType;
uint8_t *_current;
const uint8_t *_max;
uint32_t _cache = 0;
int _cachebits = 0;
static int floatToUint16(float input, uint16_t *output, float scale, float min, float max) {
if (input < min || input > max)
return -ERANGE;
bool isEnd() const { return _current == _max; }
int min(int x, int y) { return x < y ? x : y; }
*output = roundf(input * scale);
return 0;
}
int write(int nbits, uint32_t val) {
int nwrite, i;
struct dspmem_chunk {
uint8_t *head;
uint8_t *current;
uint8_t *max;
int bytes;
nwrite = min(24 - _cachebits, nbits);
_cache <<= nwrite;
_cache |= val >> (nbits - nwrite);
_cachebits += nwrite;
nbits -= nwrite;
uint32_t cache;
int cachebits;
};
if (_cachebits == 24) {
if (isEnd())
return -ENOSPC;
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,
};
_cache &= 0xFFFFFF;
for (i = 0; i < sizeof(_cache); i++, _cache <<= 8)
*_current++ = (_cache & 0xFF000000) >> 24;
return ch;
}
bytes += sizeof(_cache);
_cachebits = 0;
}
static bool dspmem_chunk_end(struct dspmem_chunk *ch) {
return ch->current == ch->max;
}
if (nbits)
return write(nbits, val);
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;
return 0;
}
if (nbits)
return dspmem_chunk_write(ch, nbits, val);
int fToU16(float input, uint16_t *output, float scale, float min, float max) {
if (input < min || input > max)
return -ERANGE;
return 0;
}
static int dspmem_chunk_flush(struct dspmem_chunk *ch) {
if (!ch->cachebits)
*output = roundf(input * scale);
return 0;
}
return dspmem_chunk_write(ch, 24 - ch->cachebits, 0);
}
void constructPwleSegment(uint16_t delay, uint16_t amplitude, uint16_t frequency, uint8_t flags,
uint32_t vbemfTarget = 0) {
write(16, delay);
write(12, amplitude);
write(12, frequency);
/* feature flags to control the chirp, CLAB braking, back EMF amplitude regulation */
write(8, (flags | 1) << 4);
if (flags & PWLE_AMP_REG_BIT) {
write(24, vbemfTarget); /* target back EMF voltage */
}
}
public:
uint8_t *front() const { return head.get(); }
uint8_t type() const { return waveformType; }
size_t size() const { return bytes; }
DspMemChunk(uint8_t type, size_t size) : head(new uint8_t[size]{0x00}) {
waveformType = type;
_current = head.get();
_max = _current + size;
if (waveformType == WAVEFORM_COMPOSE) {
write(8, 0); /* Padding */
write(8, 0); /* nsections placeholder */
write(8, 0); /* repeat */
} else if (waveformType == WAVEFORM_PWLE) {
write(24, 0); /* Waveform length placeholder */
write(8, 0); /* Repeat */
write(12, 0); /* Wait time between repeats */
write(8, 0); /* nsections placeholder */
} else {
ALOGE("%s: Invalid type: %u", __func__, waveformType);
}
}
int flush() {
if (!_cachebits)
return 0;
return write(24 - _cachebits, 0);
}
int constructComposeSegment(uint32_t effectVolLevel, uint32_t effectIndex, uint8_t repeat,
uint8_t flags, uint16_t nextEffectDelay) {
if (waveformType != WAVEFORM_COMPOSE) {
ALOGE("%s: Invalid type: %d", __func__, waveformType);
return -EDOM;
}
if (effectVolLevel > 100 || effectIndex > WAVEFORM_MAX_PHYSICAL_INDEX) {
ALOGE("%s: Invalid argument: %u, %u", __func__, effectVolLevel, effectIndex);
return -EINVAL;
}
write(8, effectVolLevel); /* amplitude */
write(8, effectIndex); /* index */
write(8, repeat); /* repeat */
write(8, flags); /* flags */
write(16, nextEffectDelay); /* delay */
return 0;
}
int constructActiveSegment(int duration, float amplitude, float frequency, bool chirp) {
uint16_t delay = 0;
uint16_t amp = 0;
uint16_t freq = 0;
uint8_t flags = 0x0;
if (waveformType != WAVEFORM_PWLE) {
ALOGE("%s: Invalid type: %d", __func__, waveformType);
return -EDOM;
}
if ((fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) ||
(fToU16(amplitude, &amp, 2048, CS40L26_PWLE_LEVEL_MIN, CS40L26_PWLE_LEVEL_MAX) < 0) ||
(fToU16(frequency, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ) < 0)) {
ALOGE("%s: Invalid argument: %d, %f, %f", __func__, duration, amplitude, frequency);
return -ERANGE;
}
if (chirp) {
flags |= PWLE_CHIRP_BIT;
}
constructPwleSegment(delay, amp, freq, flags, 0 /*ignored*/);
return 0;
}
int constructBrakingSegment(int duration, Braking brakingType) {
uint16_t delay = 0;
uint16_t freq = 0;
uint8_t flags = 0x00;
if (waveformType != WAVEFORM_PWLE) {
ALOGE("%s: Invalid type: %d", __func__, waveformType);
return -EDOM;
}
if (fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) {
ALOGE("%s: Invalid argument: %d", __func__, duration);
return -ERANGE;
}
fToU16(PWLE_FREQUENCY_MIN_HZ, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ);
if (static_cast<std::underlying_type<Braking>::type>(brakingType)) {
flags |= PWLE_BRAKE_BIT;
}
constructPwleSegment(delay, 0 /*ignored*/, freq, flags, 0 /*ignored*/);
return 0;
}
int updateWLength(uint32_t totalDuration) {
uint8_t *f = front();
if (f == nullptr) {
ALOGE("%s: head does not exist!", __func__);
return -ENOMEM;
}
if (waveformType != WAVEFORM_PWLE) {
ALOGE("%s: Invalid type: %d", __func__, waveformType);
return -EDOM;
}
if (totalDuration > 0x7FFFF) {
ALOGE("%s: Invalid argument: %u", __func__, totalDuration);
return -EINVAL;
}
totalDuration *= 8; /* Unit: 0.125 ms (since wlength played @ 8kHz). */
totalDuration |=
WT_LEN_CALCD; /* Bit 23 is for WT_LEN_CALCD; Bit 22 is for WT_INDEFINITE. */
*(f + 0) = (totalDuration >> 24) & 0xFF;
*(f + 1) = (totalDuration >> 16) & 0xFF;
*(f + 2) = (totalDuration >> 8) & 0xFF;
*(f + 3) = totalDuration & 0xFF;
return 0;
}
int updateNSection(int segmentIdx) {
uint8_t *f = front();
if (f == nullptr) {
ALOGE("%s: head does not exist!", __func__);
return -ENOMEM;
}
if (waveformType == WAVEFORM_COMPOSE) {
if (segmentIdx > COMPOSE_SIZE_MAX + 1 /*1st effect may have a delay*/) {
ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
return -EINVAL;
}
*(f + 2) = (0xFF & segmentIdx);
} else if (waveformType == WAVEFORM_PWLE) {
if (segmentIdx > COMPOSE_PWLE_SIZE_MAX_DEFAULT) {
ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
return -EINVAL;
}
*(f + 7) |= (0xF0 & segmentIdx) >> 4; /* Bit 4 to 7 */
*(f + 9) |= (0x0F & segmentIdx) << 4; /* Bit 3 to 0 */
} else {
ALOGE("%s: Invalid type: %d", __func__, waveformType);
return -EDOM;
}
return 0;
}
};
Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> hwCalDefault,
std::unique_ptr<HwApi> hwApiDual, std::unique_ptr<HwCal> hwCalDual,
@ -355,25 +487,32 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> h
mEffectDurations = {
1000, 100, 12, 1000, 300, 130, 150, 500, 100, 5, 12, 1000, 1000, 1000,
}; /* 11+3 waveforms. The duration must < UINT16_MAX */
mEffectCustomData.reserve(WAVEFORM_MAX_INDEX);
uint8_t effectIndex;
uint16_t numBytes = 0;
for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) {
if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
/* Initialize physical waveforms. */
mEffectCustomData.push_back({RAM_WVFRM_BANK, effectIndex});
mFfEffects[effectIndex] = {
.type = FF_PERIODIC,
.id = -1,
.replay.length = static_cast<uint16_t>(mEffectDurations[effectIndex]),
// Length == 0 to allow firmware control of the duration
.replay.length = 0,
.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,
.u.periodic.custom_data = mEffectCustomData[effectIndex].data(),
.u.periodic.custom_len =
static_cast<uint32_t>(mEffectCustomData[effectIndex].size()),
};
// Bypass the waveform update due to different input name
if ((strstr(inputEventName, "cs40l26") != nullptr) ||
(strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
// Let the firmware control the playback duration to avoid
// cutting any effect that is played short
if (!mHwApiDef->setFFEffect(
mInputFd, &mFfEffects[effectIndex],
static_cast<uint16_t>(mFfEffects[effectIndex].replay.length))) {
mEffectDurations[effectIndex])) {
ALOGE("Failed upload effect %d (%d): %s", effectIndex, errno, strerror(errno));
}
}
@ -382,12 +521,16 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> h
}
} else {
/* Initiate placeholders for OWT effects. */
numBytes = effectIndex == WAVEFORM_COMPOSE ? FF_CUSTOM_DATA_LEN_MAX_COMP
: FF_CUSTOM_DATA_LEN_MAX_PWLE;
std::vector<int16_t> tempVec(numBytes, 0);
mEffectCustomData.push_back(std::move(tempVec));
mFfEffects[effectIndex] = {
.type = FF_PERIODIC,
.id = -1,
.replay.length = 0,
.u.periodic.waveform = FF_CUSTOM,
.u.periodic.custom_data = nullptr,
.u.periodic.custom_data = mEffectCustomData[effectIndex].data(),
.u.periodic.custom_len = 0,
};
}
@ -396,24 +539,30 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> h
// ====================HAL internal effect table== Flip ==================================
if (mIsDual) {
mFfEffectsDual.resize(WAVEFORM_MAX_INDEX);
mEffectCustomDataDual.reserve(WAVEFORM_MAX_INDEX);
for (effectIndex = 0; effectIndex < WAVEFORM_MAX_INDEX; effectIndex++) {
if (effectIndex < WAVEFORM_MAX_PHYSICAL_INDEX) {
/* Initialize physical waveforms. */
mEffectCustomDataDual.push_back({RAM_WVFRM_BANK, effectIndex});
mFfEffectsDual[effectIndex] = {
.type = FF_PERIODIC,
.id = -1,
.replay.length = static_cast<uint16_t>(mEffectDurations[effectIndex]),
// Length == 0 to allow firmware control of the duration
.replay.length = 0,
.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,
.u.periodic.custom_data = mEffectCustomDataDual[effectIndex].data(),
.u.periodic.custom_len =
static_cast<uint32_t>(mEffectCustomDataDual[effectIndex].size()),
};
// Bypass the waveform update due to different input name
if ((strstr(inputEventName, "cs40l26") != nullptr) ||
(strstr(inputEventName, "cs40l26_dual_input") != nullptr)) {
// Let the firmware control the playback duration to avoid
// cutting any effect that is played short
if (!mHwApiDual->setFFEffect(
mInputFdDual, &mFfEffectsDual[effectIndex],
static_cast<uint16_t>(mFfEffectsDual[effectIndex].replay.length))) {
mEffectDurations[effectIndex])) {
ALOGE("Failed upload flip's effect %d (%d): %s", effectIndex, errno,
strerror(errno));
}
@ -424,12 +573,16 @@ Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> h
}
} else {
/* Initiate placeholders for OWT effects. */
numBytes = effectIndex == WAVEFORM_COMPOSE ? FF_CUSTOM_DATA_LEN_MAX_COMP
: FF_CUSTOM_DATA_LEN_MAX_PWLE;
std::vector<int16_t> tempVec(numBytes, 0);
mEffectCustomDataDual.push_back(std::move(tempVec));
mFfEffectsDual[effectIndex] = {
.type = FF_PERIODIC,
.id = -1,
.replay.length = 0,
.u.periodic.waveform = FF_CUSTOM,
.u.periodic.custom_data = nullptr,
.u.periodic.custom_data = mEffectCustomDataDual[effectIndex].data(),
.u.periodic.custom_len = 0,
};
}
@ -712,9 +865,6 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
uint16_t nextEffectDelay;
uint16_t totalDuration = 0;
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);
}
@ -730,15 +880,13 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
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 = dspmem_chunk_bytes(ch);
DspMemChunk ch(WAVEFORM_COMPOSE, FF_CUSTOM_DATA_LEN_MAX_COMP);
const uint8_t header_count = ch.size();
/* 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 */
ch.constructComposeSegment(0 /*amplitude*/, 0 /*index*/, 0 /*repeat*/, 0 /*flags*/,
nextEffectDelay /*delay*/);
}
for (uint32_t i_curr = 0, i_next = 1; i_curr < composite.size(); i_curr++, i_next++) {
@ -785,26 +933,29 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
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 */
ch.constructComposeSegment(effectVolLevel, effectIndex, 0 /*repeat*/, 0 /*flags*/,
nextEffectDelay /*delay*/);
}
dspmem_chunk_flush(ch);
if (header_count == dspmem_chunk_bytes(ch)) {
ch.flush();
if (ch.updateNSection(size) < 0) {
ALOGE("%s: Failed to update the section count", __func__);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (header_count == ch.size()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} else {
mFfEffects[WAVEFORM_COMPOSE].replay.length = totalDuration;
// Composition duration should be 0 to allow firmware to play the whole effect
mFfEffects[WAVEFORM_COMPOSE].replay.length = 0;
if (mIsDual) {
mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = totalDuration;
mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = 0;
}
return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, ch,
return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, &ch,
callback);
}
}
ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem_chunk *ch,
ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, const DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback) {
ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
@ -819,28 +970,28 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
if (ch) {
/* Upload OWT effect. */
if (ch->head == nullptr) {
if (ch->front() == nullptr) {
ALOGE("Invalid OWT bank");
delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
bool isPwle = (*reinterpret_cast<uint16_t *>(ch->head) != 0x0000);
effectIndex = isPwle ? WAVEFORM_PWLE : WAVEFORM_COMPOSE;
if (ch->type() != WAVEFORM_PWLE && ch->type() != WAVEFORM_COMPOSE) {
ALOGE("Invalid OWT type");
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
effectIndex = ch->type();
uint32_t freeBytes;
mHwApiDef->getOwtFreeSpace(&freeBytes);
if (dspmem_chunk_bytes(ch) > freeBytes) {
ALOGE("Invalid OWT length: Effect %d: %d > %d!", effectIndex, dspmem_chunk_bytes(ch),
freeBytes);
delete ch;
if (ch->size() > freeBytes) {
ALOGE("Invalid OWT length: Effect %d: %zu > %d!", effectIndex, ch->size(), freeBytes);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (mIsDual) {
mHwApiDual->getOwtFreeSpace(&freeBytes);
if (dspmem_chunk_bytes(ch) > freeBytes) {
if (ch-> size() > freeBytes) {
ALOGE("Invalid OWT length in flip: Effect %d: %d > %d!", effectIndex,
dspmem_chunk_bytes(ch), freeBytes);
delete ch;
ch-> size(), freeBytes);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
@ -853,20 +1004,17 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
ALOGD("Not dual haptics HAL and GPIO status fail");
}
if (!mHwApiDef->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch),
&mFfEffects[effectIndex], &effectIndex, &errorStatus)) {
delete ch;
if (!mHwApiDef->uploadOwtEffect(mInputFd, ch->front(), ch->size(), &mFfEffects[effectIndex],
&effectIndex, &errorStatus)) {
ALOGE("Invalid uploadOwtEffect");
return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
}
if (mIsDual && !mHwApiDual->uploadOwtEffect(mInputFdDual, ch->head, dspmem_chunk_bytes(ch),
if (mIsDual && !mHwApiDual->uploadOwtEffect(mInputFdDual, ch->front(), ch->size(),
&mFfEffectsDual[effectIndex], &effectIndex,
&errorStatus)) {
delete ch;
ALOGE("Invalid uploadOwtEffect in flip");
return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
}
delete ch;
} else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX ||
effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) {
@ -1083,69 +1231,6 @@ static void incrementIndex(int *index) {
*index += 1;
}
static void constructPwleSegment(dspmem_chunk *ch, uint16_t delay, uint16_t amplitude,
uint16_t frequency, uint8_t flags, uint32_t vbemfTarget = 0) {
dspmem_chunk_write(ch, 16, delay);
dspmem_chunk_write(ch, 12, amplitude);
dspmem_chunk_write(ch, 12, frequency);
/* feature flags to control the chirp, CLAB braking, back EMF amplitude regulation */
dspmem_chunk_write(ch, 8, (flags | 1) << 4);
if (flags & PWLE_AMP_REG_BIT) {
dspmem_chunk_write(ch, 24, vbemfTarget); /* target back EMF voltage */
}
}
static int constructActiveSegment(dspmem_chunk *ch, int duration, float amplitude, float frequency,
bool chirp) {
uint16_t delay = 0;
uint16_t amp = 0;
uint16_t freq = 0;
uint8_t flags = 0x0;
if ((floatToUint16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) ||
(floatToUint16(amplitude, &amp, 2048, CS40L26_PWLE_LEVEL_MIX, CS40L26_PWLE_LEVEL_MAX) <
0) ||
(floatToUint16(frequency, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ) < 0)) {
ALOGE("Invalid argument: %d, %f, %f", duration, amplitude, frequency);
return -ERANGE;
}
if (chirp) {
flags |= PWLE_CHIRP_BIT;
}
constructPwleSegment(ch, delay, amp, freq, flags, 0 /*ignored*/);
return 0;
}
static int constructBrakingSegment(dspmem_chunk *ch, int duration, Braking brakingType) {
uint16_t delay = 0;
uint16_t freq = 0;
uint8_t flags = 0x00;
if (floatToUint16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) {
ALOGE("Invalid argument: %d", duration);
return -ERANGE;
}
floatToUint16(PWLE_FREQUENCY_MIN_HZ, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ);
if (static_cast<std::underlying_type<Braking>::type>(brakingType)) {
flags |= PWLE_BRAKE_BIT;
}
constructPwleSegment(ch, delay, 0 /*ignored*/, freq, flags, 0 /*ignored*/);
return 0;
}
static void updateWLength(dspmem_chunk *ch, uint32_t totalDuration) {
totalDuration *= 8; /* Unit: 0.125 ms (since wlength played @ 8kHz). */
totalDuration |= WT_LEN_CALCD; /* Bit 23 is for WT_LEN_CALCD; Bit 22 is for WT_INDEFINITE. */
*(ch->head + 0) = (totalDuration >> 24) & 0xFF;
*(ch->head + 1) = (totalDuration >> 16) & 0xFF;
*(ch->head + 2) = (totalDuration >> 8) & 0xFF;
*(ch->head + 3) = totalDuration & 0xFF;
}
static void updateNSection(dspmem_chunk *ch, int segmentIdx) {
*(ch->head + 7) |= (0xF0 & segmentIdx) >> 4; /* Bit 4 to 7 */
*(ch->head + 9) |= (0x0F & segmentIdx) << 4; /* Bit 3 to 0 */
}
ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
const std::shared_ptr<IVibratorCallback> &callback) {
ATRACE_NAME("Vibrator::composePwle");
@ -1170,15 +1255,9 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
float prevEndAmplitude;
float prevEndFrequency;
resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency);
auto ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_PWLE]{0x00},
FF_CUSTOM_DATA_LEN_MAX_PWLE);
DspMemChunk ch(WAVEFORM_PWLE, FF_CUSTOM_DATA_LEN_MAX_PWLE);
bool chirp = false;
dspmem_chunk_write(ch, 24, 0x000000); /* Waveform length placeholder */
dspmem_chunk_write(ch, 8, 0); /* Repeat */
dspmem_chunk_write(ch, 12, 0); /* Wait time between repeats */
dspmem_chunk_write(ch, 8, 0x00); /* nsections placeholder */
for (auto &e : composite) {
switch (e.getTag()) {
case PrimitivePwle::active: {
@ -1208,8 +1287,8 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
if (!((active.startAmplitude == prevEndAmplitude) &&
(active.startFrequency == prevEndFrequency))) {
if (constructActiveSegment(ch, 0, active.startAmplitude, active.startFrequency,
false) < 0) {
if (ch.constructActiveSegment(0, active.startAmplitude, active.startFrequency,
false) < 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
incrementIndex(&segmentIdx);
@ -1218,8 +1297,8 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
if (active.startFrequency != active.endFrequency) {
chirp = true;
}
if (constructActiveSegment(ch, active.duration, active.endAmplitude,
active.endFrequency, chirp) < 0) {
if (ch.constructActiveSegment(active.duration, active.endAmplitude,
active.endFrequency, chirp) < 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
incrementIndex(&segmentIdx);
@ -1242,12 +1321,12 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (constructBrakingSegment(ch, 0, braking.braking) < 0) {
if (ch.constructBrakingSegment(0, braking.braking) < 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
incrementIndex(&segmentIdx);
if (constructBrakingSegment(ch, braking.duration, braking.braking) < 0) {
if (ch.constructBrakingSegment(braking.duration, braking.braking) < 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
incrementIndex(&segmentIdx);
@ -1263,7 +1342,7 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
}
dspmem_chunk_flush(ch);
ch.flush();
/* Update wlength */
totalDuration += MAX_COLD_START_LATENCY_MS;
@ -1271,12 +1350,19 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
ALOGE("Total duration is too long (%d)!", totalDuration);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
updateWLength(ch, totalDuration);
if (ch.updateWLength(totalDuration) < 0) {
ALOGE("%s: Failed to update the waveform length length", __func__);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
/* Update nsections */
updateNSection(ch, segmentIdx);
if (ch.updateNSection(segmentIdx) < 0) {
ALOGE("%s: Failed to update the section count", __func__);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, ch,
return performEffect(WAVEFORM_MAX_INDEX /*ignored*/, VOLTAGE_SCALE_MAX /*ignored*/, &ch,
callback);
}
@ -1453,7 +1539,7 @@ ndk::ScopedAStatus Vibrator::getSimpleDetails(Effect effect, EffectStrength stre
}
ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength strength,
uint32_t *outTimeMs, dspmem_chunk *outCh) {
uint32_t *outTimeMs, DspMemChunk *outCh) {
ndk::ScopedAStatus status;
uint32_t timeMs = 0;
uint32_t thisEffectIndex;
@ -1461,23 +1547,14 @@ ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength st
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 */
outCh->constructComposeSegment(thisVolLevel, thisEffectIndex, 0 /*repeat*/, 0 /*flags*/,
WAVEFORM_DOUBLE_CLICK_SILENCE_MS);
timeMs += WAVEFORM_DOUBLE_CLICK_SILENCE_MS + MAX_PAUSE_TIMING_ERROR_MS;
@ -1488,12 +1565,13 @@ ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength st
}
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);
outCh->constructComposeSegment(thisVolLevel, thisEffectIndex, 0 /*repeat*/, 0 /*flags*/,
0 /*delay*/);
outCh->flush();
if (outCh->updateNSection(2) < 0) {
ALOGE("%s: Failed to update the section count", __func__);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
break;
default:
@ -1501,9 +1579,10 @@ ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength st
}
*outTimeMs = timeMs;
mFfEffects[WAVEFORM_COMPOSE].replay.length = static_cast<uint16_t>(timeMs);
// Compositions should have 0 duration
mFfEffects[WAVEFORM_COMPOSE].replay.length = 0;
if (mIsDual) {
mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = static_cast<uint16_t>(timeMs);
mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = 0;
}
return ndk::ScopedAStatus::ok();
@ -1560,7 +1639,7 @@ ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strengt
uint32_t effectIndex;
uint32_t timeMs = 0;
uint32_t volLevel;
dspmem_chunk *ch = nullptr;
std::optional<DspMemChunk> maybeCh;
switch (effect) {
case Effect::TEXTURE_TICK:
// fall-through
@ -1572,28 +1651,25 @@ ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strengt
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);
maybeCh.emplace(WAVEFORM_COMPOSE, FF_CUSTOM_DATA_LEN_MAX_COMP);
status = getCompoundDetails(effect, strength, &timeMs, &*maybeCh);
volLevel = VOLTAGE_SCALE_MAX;
break;
default:
status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
break;
}
if (!status.isOk()) {
goto exit;
if (status.isOk()) {
DspMemChunk *ch = maybeCh ? &*maybeCh : nullptr;
status = performEffect(effectIndex, volLevel, ch, callback);
}
status = performEffect(effectIndex, volLevel, ch, callback);
exit:
*outTimeMs = timeMs;
return status;
}
ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel,
dspmem_chunk *ch,
const DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback) {
setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX);

View file

@ -89,7 +89,7 @@ class Vibrator : public BnVibrator {
virtual bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card,
int device) = 0;
// Set OWT waveform for compose or compose PWLE request
virtual bool uploadOwtEffect(int fd, uint8_t *owtData, uint32_t numBytes,
virtual bool uploadOwtEffect(int fd, const uint8_t *owtData, const uint32_t numBytes,
struct ff_effect *effect, uint32_t *outEffectIndex,
int *status) = 0;
// Erase OWT waveform
@ -178,7 +178,7 @@ class Vibrator : public BnVibrator {
static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time
private:
ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, struct dspmem_chunk *ch,
ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, const class DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback);
// set 'amplitude' based on an arbitrary scale determined by 'maximum'
ndk::ScopedAStatus setEffectAmplitude(float amplitude, float maximum);
@ -189,13 +189,13 @@ class Vibrator : public BnVibrator {
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);
uint32_t *outTimeMs, class DspMemChunk *outCh);
ndk::ScopedAStatus getPrimitiveDetails(CompositePrimitive primitive, 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 class DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback);
ndk::ScopedAStatus setPwle(const std::string &pwleQueue);
bool isUnderExternalControl();
@ -218,6 +218,8 @@ class Vibrator : public BnVibrator {
std::vector<ff_effect> mFfEffects;
std::vector<ff_effect> mFfEffectsDual;
std::vector<uint32_t> mEffectDurations;
std::vector<std::vector<int16_t>> mEffectCustomData;
std::vector<std::vector<int16_t>> mEffectCustomDataDual;
std::future<void> mAsyncHandle;
::android::base::unique_fd mInputFd;
::android::base::unique_fd mInputFdDual;

View file

@ -1,5 +1,5 @@
on property:vendor.all.modules.ready=1
wait /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_cal_time_ms
wait /sys/bus/i2c/devices/15-0043/calibration/redc_cal_time_ms
mkdir /mnt/vendor/persist/haptics 0770 system system
chmod 770 /mnt/vendor/persist/haptics
@ -9,26 +9,28 @@ on property:vendor.all.modules.ready=1
chown system system /mnt/vendor/persist/haptics/cs40l26.cal
chown system system /mnt/vendor/persist/haptics/cs40l26_dual.cal
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/f0_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/f0_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/q_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/q_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/calibration/redc_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/calibration/redc_stored
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/vibe_state
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/vibe_state
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/num_waves
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/num_waves
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_offset
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_offset
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/owt_free_space
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/owt_free_space
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/f0_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/f0_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/redc_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/redc_comp_enable
chown system system /sys/bus/i2c/devices/i2c-cs40l26a/default/delay_before_stop_playback_us
chown system system /sys/bus/i2c/devices/i2c-cs40l26a-dual/default/delay_before_stop_playback_us
chown system system /sys/bus/i2c/devices/15-0043/calibration/f0_stored
chown system system /sys/bus/i2c/devices/15-0043/calibration/q_stored
chown system system /sys/bus/i2c/devices/15-0043/calibration/redc_stored
chown system system /sys/bus/i2c/devices/15-0043/default/vibe_state
chown system system /sys/bus/i2c/devices/15-0043/default/num_waves
chown system system /sys/bus/i2c/devices/15-0043/default/f0_offset
chown system system /sys/bus/i2c/devices/15-0043/default/owt_free_space
chown system system /sys/bus/i2c/devices/15-0043/default/f0_comp_enable
chown system system /sys/bus/i2c/devices/15-0043/default/redc_comp_enable
chown system system /sys/bus/i2c/devices/15-0043/default/delay_before_stop_playback_us
chown system system /sys/bus/i2c/devices/15-0042/calibration/f0_stored
chown system system /sys/bus/i2c/devices/15-0042/calibration/q_stored
chown system system /sys/bus/i2c/devices/15-0042/calibration/redc_stored
chown system system /sys/bus/i2c/devices/15-0042/default/vibe_state
chown system system /sys/bus/i2c/devices/15-0042/default/num_waves
chown system system /sys/bus/i2c/devices/15-0042/default/f0_offset
chown system system /sys/bus/i2c/devices/15-0042/default/owt_free_space
chown system system /sys/bus/i2c/devices/15-0042/default/f0_comp_enable
chown system system /sys/bus/i2c/devices/15-0042/default/redc_comp_enable
chown system system /sys/bus/i2c/devices/15-0042/default/delay_before_stop_playback_us
chown system system /dev/gpiochip44
enable vendor.vibrator.cs40l26
@ -46,8 +48,8 @@ service vendor.vibrator.cs40l26 /vendor/bin/hw/android.hardware.vibrator-service
setenv CALIBRATION_FILEPATH /mnt/vendor/persist/haptics/cs40l26.cal
setenv CALIBRATION_FILEPATH_DUAL /mnt/vendor/persist/haptics/cs40l26_dual.cal
setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/i2c-cs40l26a/
setenv HWAPI_PATH_PREFIX_DUAL /sys/bus/i2c/devices/i2c-cs40l26a-dual/
setenv HWAPI_PATH_PREFIX /sys/bus/i2c/devices/15-0043/
setenv HWAPI_PATH_PREFIX_DUAL /sys/bus/i2c/devices/15-0042/
setenv HWAPI_DEBUG_PATHS "
calibration/f0_stored
calibration/redc_stored

View file

@ -51,7 +51,7 @@ class MockApi : public ::aidl::android::hardware::vibrator::Vibrator::HwApi {
MOCK_METHOD2(getHapticAlsaDevice, bool(int *card, int *device));
MOCK_METHOD4(setHapticPcmAmp, bool(struct pcm **haptic_pcm, bool enable, int card, int device));
MOCK_METHOD6(uploadOwtEffect,
bool(int fd, uint8_t *owtData, uint32_t numBytes, struct ff_effect *effect,
bool(int fd, const uint8_t *owtData, const uint32_t numBytes, struct ff_effect *effect,
uint32_t *outEffectIndex, int *status));
MOCK_METHOD3(eraseOwtEffect, bool(int fd, int8_t effectIndex, std::vector<ff_effect> *effect));
MOCK_METHOD1(debug, void(int fd));