1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "DspMemChunk.h"
18
19 #include <linux/version.h>
20 #include <log/log.h>
21 #include <utils/Trace.h>
22
23 #include <cmath>
24
25 #include "Trace.h"
26
27 namespace aidl {
28 namespace android {
29 namespace hardware {
30 namespace vibrator {
31
32 #ifdef VIBRATOR_TRACE
33 /* Function Trace */
34 #define VFTRACE(...) \
35 ATRACE_NAME(StringPrintf("Vibrator::%s", __func__).c_str()); \
36 auto f_trace_ = std::make_unique<FunctionTrace>("Vibrator", __func__); \
37 __VA_OPT__(f_trace_->addParameter(PREPEND_EACH_ARG_WITH_NAME(__VA_ARGS__))); \
38 f_trace_->save()
39 /* Effect Trace */
40 #define VETRACE(i, s, d, ch) \
41 auto e_trace_ = std::make_unique<EffectTrace>(i, s, d, ch); \
42 e_trace_->save()
43 #else
44 #define VFTRACE(...) ATRACE_NAME(StringPrintf("Vibrator::%s", __func__).c_str())
45 #define VETRACE(...)
46 #endif
47
48 enum WaveformIndex : uint16_t {
49 /* Physical waveform */
50 WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
51 WAVEFORM_RESERVED_INDEX_1 = 1,
52 WAVEFORM_CLICK_INDEX = 2,
53 WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
54 WAVEFORM_THUD_INDEX = 4,
55 WAVEFORM_SPIN_INDEX = 5,
56 WAVEFORM_QUICK_RISE_INDEX = 6,
57 WAVEFORM_SLOW_RISE_INDEX = 7,
58 WAVEFORM_QUICK_FALL_INDEX = 8,
59 WAVEFORM_LIGHT_TICK_INDEX = 9,
60 WAVEFORM_LOW_TICK_INDEX = 10,
61 WAVEFORM_RESERVED_MFG_1,
62 WAVEFORM_RESERVED_MFG_2,
63 WAVEFORM_RESERVED_MFG_3,
64 WAVEFORM_MAX_PHYSICAL_INDEX,
65 /* OWT waveform */
66 WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
67 WAVEFORM_PWLE,
68 /*
69 * Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
70 * #define FF_GAIN 0x60 // 96 in decimal
71 * #define FF_MAX_EFFECTS FF_GAIN
72 */
73 WAVEFORM_MAX_INDEX,
74 };
75
DspMemChunk(uint8_t type,size_t size)76 DspMemChunk::DspMemChunk(uint8_t type, size_t size) : head(new uint8_t[size]{0x00}) {
77 VFTRACE(type, size);
78 waveformType = type;
79 _current = head.get();
80 _max = _current + size;
81
82 if (waveformType == WAVEFORM_COMPOSE) {
83 write(8, 0); /* Padding */
84 write(8, 0); /* nsections placeholder */
85 write(8, 0); /* repeat */
86 } else if (waveformType == WAVEFORM_PWLE) {
87 #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
88 write(16, (PWLE_FTR_BUZZ_BIT | PWLE_FTR_DVL_BIT)
89 << PWLE_HEADER_FTR_SHIFT); /* Feature flag */
90 write(8, PWLE_WT_TYPE); /* type12 */
91 write(24, PWLE_HEADER_WORD_COUNT); /* Header word count */
92 write(24, 0); /* Body word count placeholder */
93 #endif
94 write(24, 0); /* Waveform length placeholder */
95 write(8, 0); /* Repeat */
96 write(12, 0); /* Wait time between repeats */
97 write(8, 0); /* nsections placeholder */
98 } else {
99 ALOGE("%s: Invalid type: %u", __func__, waveformType);
100 }
101 }
102
write(int nbits,uint32_t val)103 int DspMemChunk::write(int nbits, uint32_t val) {
104 VFTRACE(nbits, val);
105 int nwrite;
106
107 nwrite = min(24 - _cachebits, nbits);
108 _cache <<= nwrite;
109 _cache |= val >> (nbits - nwrite);
110 _cachebits += nwrite;
111 nbits -= nwrite;
112
113 if (_cachebits == 24) {
114 if (isEnd())
115 return -ENOSPC;
116
117 _cache &= 0xFFFFFF;
118 for (size_t i = 0; i < sizeof(_cache); i++, _cache <<= 8)
119 *_current++ = (_cache & 0xFF000000) >> 24;
120
121 bytes += sizeof(_cache);
122 _cachebits = 0;
123 }
124
125 if (nbits)
126 return write(nbits, val);
127
128 return 0;
129 }
130
fToU16(float input,uint16_t * output,float scale,float min,float max)131 int DspMemChunk::fToU16(float input, uint16_t *output, float scale, float min, float max) {
132 VFTRACE(input, output, scale, min, max);
133 if (input < min || input > max)
134 return -ERANGE;
135
136 *output = roundf(input * scale);
137 return 0;
138 }
139
constructPwleSegment(uint16_t delay,uint16_t amplitude,uint16_t frequency,uint8_t flags,uint32_t vbemfTarget)140 void DspMemChunk::constructPwleSegment(uint16_t delay, uint16_t amplitude, uint16_t frequency,
141 uint8_t flags, uint32_t vbemfTarget) {
142 VFTRACE(delay, amplitude, frequency, flags, vbemfTarget);
143 write(16, delay);
144 write(12, amplitude);
145 write(12, frequency);
146 /* feature flags to control the chirp, CLAB braking, back EMF amplitude regulation */
147 write(8, (flags | 1) << 4);
148 if (flags & PWLE_AMP_REG_BIT) {
149 write(24, vbemfTarget); /* target back EMF voltage */
150 }
151 }
152
flush()153 int DspMemChunk::flush() {
154 VFTRACE();
155 if (!_cachebits)
156 return 0;
157
158 return write(24 - _cachebits, 0);
159 }
160
constructComposeSegment(uint32_t effectVolLevel,uint32_t effectIndex,uint8_t repeat,uint8_t flags,uint16_t nextEffectDelay)161 int DspMemChunk::constructComposeSegment(uint32_t effectVolLevel, uint32_t effectIndex,
162 uint8_t repeat, uint8_t flags, uint16_t nextEffectDelay) {
163 VFTRACE(effectVolLevel, effectIndex, repeat, flags, nextEffectDelay);
164 if (waveformType != WAVEFORM_COMPOSE) {
165 ALOGE("%s: Invalid type: %d", __func__, waveformType);
166 return -EDOM;
167 }
168 if (effectVolLevel > 100 || effectIndex > WAVEFORM_MAX_PHYSICAL_INDEX) {
169 ALOGE("%s: Invalid argument: %u, %u", __func__, effectVolLevel, effectIndex);
170 return -EINVAL;
171 }
172 write(8, effectVolLevel); /* amplitude */
173 write(8, effectIndex); /* index */
174 write(8, repeat); /* repeat */
175 write(8, flags); /* flags */
176 write(16, nextEffectDelay); /* delay */
177 return 0;
178 }
179
constructActiveSegment(int duration,float amplitude,float frequency,bool chirp)180 int DspMemChunk::constructActiveSegment(int duration, float amplitude, float frequency,
181 bool chirp) {
182 VFTRACE(duration, amplitude, frequency, chirp);
183 uint16_t delay = 0;
184 uint16_t amp = 0;
185 uint16_t freq = 0;
186 uint8_t flags = 0x0;
187 if (waveformType != WAVEFORM_PWLE) {
188 ALOGE("%s: Invalid type: %d", __func__, waveformType);
189 return -EDOM;
190 }
191 if ((fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) ||
192 (fToU16(amplitude, &, 2048, CS40L26_PWLE_LEVEL_MIN, CS40L26_PWLE_LEVEL_MAX) < 0) ||
193 (fToU16(frequency, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ) < 0)) {
194 ALOGE("%s: Invalid argument: %d, %f, %f", __func__, duration, amplitude, frequency);
195 return -ERANGE;
196 }
197 if (chirp) {
198 flags |= PWLE_CHIRP_BIT;
199 }
200 constructPwleSegment(delay, amp, freq, flags, 0 /*ignored*/);
201 return 0;
202 }
203
constructBrakingSegment(int duration,Braking brakingType)204 int DspMemChunk::constructBrakingSegment(int duration, Braking brakingType) {
205 VFTRACE(duration, brakingType);
206 uint16_t delay = 0;
207 uint16_t freq = 0;
208 uint8_t flags = 0x00;
209 if (waveformType != WAVEFORM_PWLE) {
210 ALOGE("%s: Invalid type: %d", __func__, waveformType);
211 return -EDOM;
212 }
213 if (fToU16(duration, &delay, 4, 0.0f, COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) < 0) {
214 ALOGE("%s: Invalid argument: %d", __func__, duration);
215 return -ERANGE;
216 }
217 fToU16(PWLE_FREQUENCY_MIN_HZ, &freq, 4, PWLE_FREQUENCY_MIN_HZ, PWLE_FREQUENCY_MAX_HZ);
218 if (static_cast<std::underlying_type<Braking>::type>(brakingType)) {
219 flags |= PWLE_BRAKE_BIT;
220 }
221
222 constructPwleSegment(delay, 0 /*ignored*/, freq, flags, 0 /*ignored*/);
223 return 0;
224 }
225
updateWLength(uint32_t totalDuration)226 int DspMemChunk::updateWLength(uint32_t totalDuration) {
227 VFTRACE(totalDuration);
228 uint8_t *f = front();
229 if (f == nullptr) {
230 ALOGE("%s: head does not exist!", __func__);
231 return -ENOMEM;
232 }
233 if (waveformType != WAVEFORM_PWLE) {
234 ALOGE("%s: Invalid type: %d", __func__, waveformType);
235 return -EDOM;
236 }
237 if (totalDuration > 0x7FFFF) {
238 ALOGE("%s: Invalid argument: %u", __func__, totalDuration);
239 return -EINVAL;
240 }
241 #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
242 f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE;
243 #endif
244 totalDuration *= 8; /* Unit: 0.125 ms (since wlength played @ 8kHz). */
245 totalDuration |= WT_LEN_CALCD; /* Bit 23 is for WT_LEN_CALCD; Bit 22 is for WT_INDEFINITE. */
246 *(f + 0) = (totalDuration >> 24) & 0xFF;
247 *(f + 1) = (totalDuration >> 16) & 0xFF;
248 *(f + 2) = (totalDuration >> 8) & 0xFF;
249 *(f + 3) = totalDuration & 0xFF;
250 return 0;
251 }
252
updateNSection(int segmentIdx)253 int DspMemChunk::updateNSection(int segmentIdx) {
254 VFTRACE(segmentIdx);
255 uint8_t *f = front();
256 if (f == nullptr) {
257 ALOGE("%s: head does not exist!", __func__);
258 return -ENOMEM;
259 }
260
261 if (waveformType == WAVEFORM_COMPOSE) {
262 if (segmentIdx > COMPOSE_SIZE_MAX + 1 /*1st effect may have a delay*/) {
263 ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
264 return -EINVAL;
265 }
266 *(f + 2) = (0xFF & segmentIdx);
267 } else if (waveformType == WAVEFORM_PWLE) {
268 if (segmentIdx > COMPOSE_PWLE_SIZE_MAX_DEFAULT) {
269 ALOGE("%s: Invalid argument: %d", __func__, segmentIdx);
270 return -EINVAL;
271 }
272 #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
273 f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE;
274 #endif
275 *(f + 7) |= (0xF0 & segmentIdx) >> 4; /* Bit 4 to 7 */
276 *(f + 9) |= (0x0F & segmentIdx) << 4; /* Bit 3 to 0 */
277 } else {
278 ALOGE("%s: Invalid type: %d", __func__, waveformType);
279 return -EDOM;
280 }
281
282 return 0;
283 }
284
updateWCount(int segmentCount)285 int DspMemChunk::updateWCount(int segmentCount) {
286 uint8_t *f = front();
287
288 if (segmentCount > COMPOSE_SIZE_MAX + 1 /*1st effect may have a delay*/) {
289 ALOGE("%s: Invalid argument: %d", __func__, segmentCount);
290 return -EINVAL;
291 }
292 if (f == nullptr) {
293 ALOGE("%s: head does not exist!", __func__);
294 return -ENOMEM;
295 }
296 if (waveformType != WAVEFORM_PWLE) {
297 ALOGE("%s: Invalid type: %d", __func__, waveformType);
298 return -EDOM;
299 }
300
301 #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
302 f += PWLE_HEADER_WORD_COUNT * PWLE_WORD_SIZE;
303 #endif
304 uint32_t dataSize = segmentCount * PWLE_SEGMENT_WORD_COUNT + PWLE_HEADER_WORD_COUNT;
305 *(f + 0) = (dataSize >> 24) & 0xFF;
306 *(f + 1) = (dataSize >> 16) & 0xFF;
307 *(f + 2) = (dataSize >> 8) & 0xFF;
308 *(f + 3) = dataSize & 0xFF;
309
310 return 0;
311 }
312
313 } // namespace vibrator
314 } // namespace hardware
315 } // namespace android
316 } // namespace aidl
317