cs40l26: Align Felix Hal with common HAL

Pull DSPMemChunk related CLs from common HAL.
ag/22087481, ag/22093662

Bug: 322648133
Test: idlcli compose commands
Test: adb shell cmd vibrator_manager synced prebaked 1
Test: adb shell idlcli vibrator composePwle -b \
  a 0.1 300.0 0.1 30.0 1000 \
  a 0.1 30.0 0.1 300.0 500
Test: atest PtsVibratorHalTestSuite \
  PtsHapticsTestCases \
  VibratorHalCs40l26TestSuite \
  VtsHalVibratorManagerTargetTest \
  VtsHalVibratorTargetTest \
  android.os.cts.VibratorTest \
  android.os.cts.VibratorManagerTest \
  android.os.cts.VibrationEffectTest \
  android.os.cts.VibrationAttributesTest \
  android.os.cts.CombinedVibrationTest
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:85299be2adc9e3dec373dc48f321c4fa1930e260)
Merged-In: I32258500ec02c2a8cbfc6d0b37aaea2c83c7c8b4
Change-Id: I32258500ec02c2a8cbfc6d0b37aaea2c83c7c8b4
This commit is contained in:
Ravi Jain 2024-02-26 08:42:35 +00:00 committed by Android Build Coastguard Worker
parent f34dcae450
commit 0e465792b0
4 changed files with 268 additions and 210 deletions

View file

@ -184,7 +184,7 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
*haptic_pcm = NULL; *haptic_pcm = NULL;
return false; 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 { uint32_t *outEffectIndex, int *status) override {
(*effect).u.periodic.custom_len = numBytes / sizeof(uint16_t); (*effect).u.periodic.custom_len = numBytes / sizeof(uint16_t);
delete[] ((*effect).u.periodic.custom_data); delete[] ((*effect).u.periodic.custom_data);
@ -225,6 +225,10 @@ class HwApi : public Vibrator::HwApi, private HwApiBase {
ALOGE("Invalid waveform index for OWT erase: %d", effectIndex); ALOGE("Invalid waveform index for OWT erase: %d", effectIndex);
return false; return false;
} }
if (effect == nullptr || (*effect).empty()) {
ALOGE("Invalid argument effect");
return false;
}
// Turn off the waiting time for SVC init phase to complete since chip // Turn off the waiting time for SVC init phase to complete since chip
// should already under STOP state // should already under STOP state
setMinOnOffInterval(0); setMinOnOffInterval(0);

View file

@ -27,6 +27,8 @@
#include <cmath> #include <cmath>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <memory>
#include <optional>
#include <sstream> #include <sstream>
#ifndef ARRAY_SIZE #ifndef ARRAY_SIZE
@ -85,7 +87,7 @@ static constexpr uint8_t PWLE_AMP_REG_BIT = 0x2;
static constexpr float PWLE_LEVEL_MIN = 0.0; static constexpr float PWLE_LEVEL_MIN = 0.0;
static constexpr float PWLE_LEVEL_MAX = 1.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 CS40L26_PWLE_LEVEL_MAX = 0.9995118;
static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.00; static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.00;
static constexpr float PWLE_FREQUENCY_MIN_HZ = 1.00; static constexpr float PWLE_FREQUENCY_MIN_HZ = 1.00;
@ -157,79 +159,210 @@ enum vibe_state {
VIBE_STATE_ASP, VIBE_STATE_ASP,
}; };
static int min(int x, int y) { class DspMemChunk {
return x < y ? x : y; 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) { bool isEnd() const { return _current == _max; }
if (input < min || input > max) int min(int x, int y) { return x < y ? x : y; }
return -ERANGE;
*output = roundf(input * scale); int write(int nbits, uint32_t val) {
return 0; int nwrite, i;
}
struct dspmem_chunk { nwrite = min(24 - _cachebits, nbits);
uint8_t *head; _cache <<= nwrite;
uint8_t *current; _cache |= val >> (nbits - nwrite);
uint8_t *max; _cachebits += nwrite;
int bytes; nbits -= nwrite;
uint32_t cache; if (_cachebits == 24) {
int cachebits; if (isEnd())
}; return -ENOSPC;
static dspmem_chunk *dspmem_chunk_create(void *data, int size) { _cache &= 0xFFFFFF;
auto ch = new dspmem_chunk{ for (i = 0; i < sizeof(_cache); i++, _cache <<= 8)
.head = reinterpret_cast<uint8_t *>(data), *_current++ = (_cache & 0xFF000000) >> 24;
.current = reinterpret_cast<uint8_t *>(data),
.max = reinterpret_cast<uint8_t *>(data) + size,
};
return ch; bytes += sizeof(_cache);
} _cachebits = 0;
}
static bool dspmem_chunk_end(struct dspmem_chunk *ch) { if (nbits)
return ch->current == ch->max; return write(nbits, val);
}
static int dspmem_chunk_bytes(struct dspmem_chunk *ch) { return 0;
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) int fToU16(float input, uint16_t *output, float scale, float min, float max) {
return dspmem_chunk_write(ch, nbits, val); if (input < min || input > max)
return -ERANGE;
return 0; *output = roundf(input * scale);
}
static int dspmem_chunk_flush(struct dspmem_chunk *ch) {
if (!ch->cachebits)
return 0; 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, Vibrator::Vibrator(std::unique_ptr<HwApi> hwApiDefault, std::unique_ptr<HwCal> hwCalDefault,
std::unique_ptr<HwApi> hwApiDual, std::unique_ptr<HwCal> hwCalDual, std::unique_ptr<HwApi> hwApiDual, std::unique_ptr<HwCal> hwCalDual,
@ -718,9 +851,6 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
uint16_t nextEffectDelay; uint16_t nextEffectDelay;
uint16_t totalDuration = 0; 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()) { if (composite.size() > COMPOSE_SIZE_MAX || composite.empty()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
@ -736,15 +866,13 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
size = composite.size(); size = composite.size();
} }
dspmem_chunk_write(ch, 8, 0); /* Padding */ DspMemChunk ch(WAVEFORM_COMPOSE, FF_CUSTOM_DATA_LEN_MAX_COMP);
dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & size)); /* nsections */ const uint8_t header_count = ch.size();
dspmem_chunk_write(ch, 8, 0); /* repeat */
uint8_t header_count = dspmem_chunk_bytes(ch);
/* Insert 1 section for a wait before the first effect. */ /* Insert 1 section for a wait before the first effect. */
if (nextEffectDelay) { if (nextEffectDelay) {
dspmem_chunk_write(ch, 32, 0); /* amplitude, index, repeat & flags */ ch.constructComposeSegment(0 /*amplitude*/, 0 /*index*/, 0 /*repeat*/, 0 /*flags*/,
dspmem_chunk_write(ch, 16, (uint16_t)(0xFFFF & nextEffectDelay)); /* delay */ nextEffectDelay /*delay*/);
} }
for (uint32_t i_curr = 0, i_next = 1; i_curr < composite.size(); i_curr++, i_next++) { for (uint32_t i_curr = 0, i_next = 1; i_curr < composite.size(); i_curr++, i_next++) {
@ -791,14 +919,16 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & effectVolLevel)); /* amplitude */ ch.constructComposeSegment(effectVolLevel, effectIndex, 0 /*repeat*/, 0 /*flags*/,
dspmem_chunk_write(ch, 8, (uint8_t)(0xFF & effectIndex)); /* index */ nextEffectDelay /*delay*/);
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 == 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); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} else { } else {
// Composition duration should be 0 to allow firmware to play the whole effect // Composition duration should be 0 to allow firmware to play the whole effect
@ -806,12 +936,12 @@ ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect> &composi
if (mIsDual) { if (mIsDual) {
mFfEffectsDual[WAVEFORM_COMPOSE].replay.length = 0; 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); 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) { const std::shared_ptr<IVibratorCallback> &callback) {
ndk::ScopedAStatus status = ndk::ScopedAStatus::ok(); ndk::ScopedAStatus status = ndk::ScopedAStatus::ok();
@ -826,28 +956,28 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
if (ch) { if (ch) {
/* Upload OWT effect. */ /* Upload OWT effect. */
if (ch->head == nullptr) { if (ch->front() == nullptr) {
ALOGE("Invalid OWT bank"); ALOGE("Invalid OWT bank");
delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); 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; uint32_t freeBytes;
mHwApiDef->getOwtFreeSpace(&freeBytes); mHwApiDef->getOwtFreeSpace(&freeBytes);
if (dspmem_chunk_bytes(ch) > freeBytes) { if (ch->size() > freeBytes) {
ALOGE("Invalid OWT length: Effect %d: %d > %d!", effectIndex, dspmem_chunk_bytes(ch), ALOGE("Invalid OWT length: Effect %d: %zu > %d!", effectIndex, ch->size(), freeBytes);
freeBytes);
delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
if (mIsDual) { if (mIsDual) {
mHwApiDual->getOwtFreeSpace(&freeBytes); mHwApiDual->getOwtFreeSpace(&freeBytes);
if (dspmem_chunk_bytes(ch) > freeBytes) { if (ch-> size() > freeBytes) {
ALOGE("Invalid OWT length in flip: Effect %d: %d > %d!", effectIndex, ALOGE("Invalid OWT length in flip: Effect %d: %d > %d!", effectIndex,
dspmem_chunk_bytes(ch), freeBytes); ch-> size(), freeBytes);
delete ch;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
} }
@ -860,20 +990,17 @@ ndk::ScopedAStatus Vibrator::on(uint32_t timeoutMs, uint32_t effectIndex, dspmem
ALOGD("Not dual haptics HAL and GPIO status fail"); ALOGD("Not dual haptics HAL and GPIO status fail");
} }
if (!mHwApiDef->uploadOwtEffect(mInputFd, ch->head, dspmem_chunk_bytes(ch), if (!mHwApiDef->uploadOwtEffect(mInputFd, ch->front(), ch->size(), &mFfEffects[effectIndex],
&mFfEffects[effectIndex], &effectIndex, &errorStatus)) { &effectIndex, &errorStatus)) {
delete ch;
ALOGE("Invalid uploadOwtEffect"); ALOGE("Invalid uploadOwtEffect");
return ndk::ScopedAStatus::fromExceptionCode(errorStatus); return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
} }
if (mIsDual && !mHwApiDual->uploadOwtEffect(mInputFdDual, ch->head, dspmem_chunk_bytes(ch), if (mIsDual && !mHwApiDual->uploadOwtEffect(mInputFdDual, ch->front(), ch->size(),
&mFfEffectsDual[effectIndex], &effectIndex, &mFfEffectsDual[effectIndex], &effectIndex,
&errorStatus)) { &errorStatus)) {
delete ch;
ALOGE("Invalid uploadOwtEffect in flip"); ALOGE("Invalid uploadOwtEffect in flip");
return ndk::ScopedAStatus::fromExceptionCode(errorStatus); return ndk::ScopedAStatus::fromExceptionCode(errorStatus);
} }
delete ch;
} else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX || } else if (effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX ||
effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) { effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX) {
@ -1090,69 +1217,6 @@ static void incrementIndex(int *index) {
*index += 1; *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, ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite,
const std::shared_ptr<IVibratorCallback> &callback) { const std::shared_ptr<IVibratorCallback> &callback) {
ATRACE_NAME("Vibrator::composePwle"); ATRACE_NAME("Vibrator::composePwle");
@ -1177,15 +1241,9 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
float prevEndAmplitude; float prevEndAmplitude;
float prevEndFrequency; float prevEndFrequency;
resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency); resetPreviousEndAmplitudeEndFrequency(&prevEndAmplitude, &prevEndFrequency);
auto ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_PWLE]{0x00}, DspMemChunk ch(WAVEFORM_PWLE, FF_CUSTOM_DATA_LEN_MAX_PWLE);
FF_CUSTOM_DATA_LEN_MAX_PWLE);
bool chirp = false; 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) { for (auto &e : composite) {
switch (e.getTag()) { switch (e.getTag()) {
case PrimitivePwle::active: { case PrimitivePwle::active: {
@ -1215,8 +1273,8 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
if (!((active.startAmplitude == prevEndAmplitude) && if (!((active.startAmplitude == prevEndAmplitude) &&
(active.startFrequency == prevEndFrequency))) { (active.startFrequency == prevEndFrequency))) {
if (constructActiveSegment(ch, 0, active.startAmplitude, active.startFrequency, if (ch.constructActiveSegment(0, active.startAmplitude, active.startFrequency,
false) < 0) { false) < 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
incrementIndex(&segmentIdx); incrementIndex(&segmentIdx);
@ -1225,8 +1283,8 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
if (active.startFrequency != active.endFrequency) { if (active.startFrequency != active.endFrequency) {
chirp = true; chirp = true;
} }
if (constructActiveSegment(ch, active.duration, active.endAmplitude, if (ch.constructActiveSegment(active.duration, active.endAmplitude,
active.endFrequency, chirp) < 0) { active.endFrequency, chirp) < 0) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
incrementIndex(&segmentIdx); incrementIndex(&segmentIdx);
@ -1249,12 +1307,12 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); 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); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
incrementIndex(&segmentIdx); 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); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
incrementIndex(&segmentIdx); incrementIndex(&segmentIdx);
@ -1270,7 +1328,7 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
} }
} }
dspmem_chunk_flush(ch); ch.flush();
/* Update wlength */ /* Update wlength */
totalDuration += MAX_COLD_START_LATENCY_MS; totalDuration += MAX_COLD_START_LATENCY_MS;
@ -1278,12 +1336,19 @@ ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &compo
ALOGE("Total duration is too long (%d)!", totalDuration); ALOGE("Total duration is too long (%d)!", totalDuration);
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); 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 */ /* 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); callback);
} }
@ -1460,7 +1525,7 @@ ndk::ScopedAStatus Vibrator::getSimpleDetails(Effect effect, EffectStrength stre
} }
ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength strength, ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength strength,
uint32_t *outTimeMs, dspmem_chunk *outCh) { uint32_t *outTimeMs, DspMemChunk *outCh) {
ndk::ScopedAStatus status; ndk::ScopedAStatus status;
uint32_t timeMs = 0; uint32_t timeMs = 0;
uint32_t thisEffectIndex; uint32_t thisEffectIndex;
@ -1468,23 +1533,14 @@ ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength st
uint32_t thisVolLevel; uint32_t thisVolLevel;
switch (effect) { switch (effect) {
case Effect::DOUBLE_CLICK: 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, status = getSimpleDetails(Effect::CLICK, strength, &thisEffectIndex, &thisTimeMs,
&thisVolLevel); &thisVolLevel);
if (!status.isOk()) { if (!status.isOk()) {
return status; return status;
} }
timeMs += thisTimeMs; timeMs += thisTimeMs;
outCh->constructComposeSegment(thisVolLevel, thisEffectIndex, 0 /*repeat*/, 0 /*flags*/,
dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisVolLevel)); /* amplitude */ WAVEFORM_DOUBLE_CLICK_SILENCE_MS);
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; timeMs += WAVEFORM_DOUBLE_CLICK_SILENCE_MS + MAX_PAUSE_TIMING_ERROR_MS;
@ -1495,12 +1551,13 @@ ndk::ScopedAStatus Vibrator::getCompoundDetails(Effect effect, EffectStrength st
} }
timeMs += thisTimeMs; timeMs += thisTimeMs;
dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisVolLevel)); /* amplitude */ outCh->constructComposeSegment(thisVolLevel, thisEffectIndex, 0 /*repeat*/, 0 /*flags*/,
dspmem_chunk_write(outCh, 8, (uint8_t)(0xFF & thisEffectIndex)); /* index */ 0 /*delay*/);
dspmem_chunk_write(outCh, 8, 0); /* repeat */ outCh->flush();
dspmem_chunk_write(outCh, 8, 0); /* flags */ if (outCh->updateNSection(2) < 0) {
dspmem_chunk_write(outCh, 16, 0); /* delay */ ALOGE("%s: Failed to update the section count", __func__);
dspmem_chunk_flush(outCh); return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
break; break;
default: default:
@ -1568,7 +1625,7 @@ ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strengt
uint32_t effectIndex; uint32_t effectIndex;
uint32_t timeMs = 0; uint32_t timeMs = 0;
uint32_t volLevel; uint32_t volLevel;
dspmem_chunk *ch = nullptr; std::optional<DspMemChunk> maybeCh;
switch (effect) { switch (effect) {
case Effect::TEXTURE_TICK: case Effect::TEXTURE_TICK:
// fall-through // fall-through
@ -1580,28 +1637,25 @@ ndk::ScopedAStatus Vibrator::performEffect(Effect effect, EffectStrength strengt
status = getSimpleDetails(effect, strength, &effectIndex, &timeMs, &volLevel); status = getSimpleDetails(effect, strength, &effectIndex, &timeMs, &volLevel);
break; break;
case Effect::DOUBLE_CLICK: case Effect::DOUBLE_CLICK:
ch = dspmem_chunk_create(new uint8_t[FF_CUSTOM_DATA_LEN_MAX_COMP]{0x00}, maybeCh.emplace(WAVEFORM_COMPOSE, FF_CUSTOM_DATA_LEN_MAX_COMP);
FF_CUSTOM_DATA_LEN_MAX_COMP); status = getCompoundDetails(effect, strength, &timeMs, &*maybeCh);
status = getCompoundDetails(effect, strength, &timeMs, ch);
volLevel = VOLTAGE_SCALE_MAX; volLevel = VOLTAGE_SCALE_MAX;
break; break;
default: default:
status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); status = ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
break; break;
} }
if (!status.isOk()) { if (status.isOk()) {
goto exit; DspMemChunk *ch = maybeCh ? &*maybeCh : nullptr;
status = performEffect(effectIndex, volLevel, ch, callback);
} }
status = performEffect(effectIndex, volLevel, ch, callback);
exit:
*outTimeMs = timeMs; *outTimeMs = timeMs;
return status; return status;
} }
ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel, ndk::ScopedAStatus Vibrator::performEffect(uint32_t effectIndex, uint32_t volLevel,
dspmem_chunk *ch, const DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback) { const std::shared_ptr<IVibratorCallback> &callback) {
setEffectAmplitude(volLevel, VOLTAGE_SCALE_MAX); 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, virtual bool setHapticPcmAmp(struct pcm **haptic_pcm, bool enable, int card,
int device) = 0; int device) = 0;
// Set OWT waveform for compose or compose PWLE request // 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, struct ff_effect *effect, uint32_t *outEffectIndex,
int *status) = 0; int *status) = 0;
// Erase OWT waveform // Erase OWT waveform
@ -178,7 +178,7 @@ class Vibrator : public BnVibrator {
static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500; // SVC initialization time
private: private:
ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, struct dspmem_chunk *ch, ndk::ScopedAStatus on(uint32_t timeoutMs, uint32_t effectIndex, const class DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback); const std::shared_ptr<IVibratorCallback> &callback);
// set 'amplitude' based on an arbitrary scale determined by 'maximum' // set 'amplitude' based on an arbitrary scale determined by 'maximum'
ndk::ScopedAStatus setEffectAmplitude(float amplitude, float maximum); ndk::ScopedAStatus setEffectAmplitude(float amplitude, float maximum);
@ -189,13 +189,13 @@ class Vibrator : public BnVibrator {
uint32_t *outVolLevel); uint32_t *outVolLevel);
// 'compound' effects are those composed by stringing multiple 'simple' effects // 'compound' effects are those composed by stringing multiple 'simple' effects
ndk::ScopedAStatus getCompoundDetails(Effect effect, EffectStrength strength, 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 getPrimitiveDetails(CompositePrimitive primitive, uint32_t *outEffectIndex);
ndk::ScopedAStatus performEffect(Effect effect, EffectStrength strength, ndk::ScopedAStatus performEffect(Effect effect, EffectStrength strength,
const std::shared_ptr<IVibratorCallback> &callback, const std::shared_ptr<IVibratorCallback> &callback,
int32_t *outTimeMs); int32_t *outTimeMs);
ndk::ScopedAStatus performEffect(uint32_t effectIndex, uint32_t volLevel, ndk::ScopedAStatus performEffect(uint32_t effectIndex, uint32_t volLevel,
struct dspmem_chunk *ch, const class DspMemChunk *ch,
const std::shared_ptr<IVibratorCallback> &callback); const std::shared_ptr<IVibratorCallback> &callback);
ndk::ScopedAStatus setPwle(const std::string &pwleQueue); ndk::ScopedAStatus setPwle(const std::string &pwleQueue);
bool isUnderExternalControl(); bool isUnderExternalControl();

View file

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