• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &amp, 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