blob: ec78ee5bfdc96c38f4d59fc88a584c338fafdb8b [file] [log] [blame]
merothh5ce005f2021-11-13 19:15:30 +05301/*
2 * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
merothh66682a52021-11-13 19:20:17 +053030#define LOG_TAG "vendor.qti.vibrator.FP4"
merothh5ce005f2021-11-13 19:15:30 +053031
32#include <cutils/properties.h>
33#include <dirent.h>
34#include <inttypes.h>
35#include <linux/input.h>
36#include <log/log.h>
37#include <string.h>
38#include <sys/ioctl.h>
39#include <thread>
40
41#include "include/Vibrator.h"
42#ifdef USE_EFFECT_STREAM
43#include "effect.h"
44#endif
45
46namespace aidl {
47namespace android {
48namespace hardware {
49namespace vibrator {
50
51#define STRONG_MAGNITUDE 0x7fff
52#define MEDIUM_MAGNITUDE 0x5fff
53#define LIGHT_MAGNITUDE 0x3fff
54#define INVALID_VALUE -1
55#define CUSTOM_DATA_LEN 3
56#define NAME_BUF_SIZE 32
57
58#define MSM_CPU_LAHAINA 415
59#define APQ_CPU_LAHAINA 439
60#define MSM_CPU_SHIMA 450
61#define MSM_CPU_SM8325 501
62#define APQ_CPU_SM8325P 502
63#define MSM_CPU_YUPIK 475
64
65#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8)))
66
merothhad035ea2021-11-13 22:56:57 +053067static const char LED_DEVICE[] = "/sys/class/leds/vibrator_aw8695";
merothh5ce005f2021-11-13 19:15:30 +053068
69InputFFDevice::InputFFDevice()
70{
71 DIR *dp;
72 FILE *fp = NULL;
73 struct dirent *dir;
74 uint8_t ffBitmask[FF_CNT / 8];
75 char devicename[PATH_MAX];
76 const char *INPUT_DIR = "/dev/input/";
77 char name[NAME_BUF_SIZE];
78 int fd, ret;
79 int soc = property_get_int32("ro.vendor.qti.soc_id", -1);
80
81 mVibraFd = INVALID_VALUE;
82 mSupportGain = false;
83 mSupportEffects = false;
84 mSupportExternalControl = false;
85 mCurrAppId = INVALID_VALUE;
86 mCurrMagnitude = 0x7fff;
87 mInExternalControl = false;
88
89 dp = opendir(INPUT_DIR);
90 if (!dp) {
91 ALOGE("open %s failed, errno = %d", INPUT_DIR, errno);
92 return;
93 }
94
95 memset(ffBitmask, 0, sizeof(ffBitmask));
96 while ((dir = readdir(dp)) != NULL){
97 if (dir->d_name[0] == '.' &&
98 (dir->d_name[1] == '\0' ||
99 (dir->d_name[1] == '.' && dir->d_name[2] == '\0')))
100 continue;
101
102 snprintf(devicename, PATH_MAX, "%s%s", INPUT_DIR, dir->d_name);
103 fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR));
104 if (fd < 0) {
105 ALOGE("open %s failed, errno = %d", devicename, errno);
106 continue;
107 }
108
109 ret = TEMP_FAILURE_RETRY(ioctl(fd, EVIOCGNAME(sizeof(name)), name));
110 if (ret == -1) {
111 ALOGE("get input device name %s failed, errno = %d\n", devicename, errno);
112 close(fd);
113 continue;
114 }
115
116 if (strcmp(name, "qcom-hv-haptics") && strcmp(name, "qti-haptics")) {
117 ALOGD("not a qcom/qti haptics device\n");
118 close(fd);
119 continue;
120 }
121
122 ALOGI("%s is detected at %s\n", name, devicename);
123 ret = TEMP_FAILURE_RETRY(ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffBitmask)), ffBitmask));
124 if (ret == -1) {
125 ALOGE("ioctl failed, errno = %d", errno);
126 close(fd);
127 continue;
128 }
129
130 if (test_bit(FF_CONSTANT, ffBitmask) ||
131 test_bit(FF_PERIODIC, ffBitmask)) {
132 mVibraFd = fd;
133 if (test_bit(FF_CUSTOM, ffBitmask))
134 mSupportEffects = true;
135 if (test_bit(FF_GAIN, ffBitmask))
136 mSupportGain = true;
137
138 if (soc <= 0 && (fp = fopen("/sys/devices/soc0/soc_id", "r")) != NULL) {
139 fscanf(fp, "%u", &soc);
140 fclose(fp);
141 }
142 switch (soc) {
143 case MSM_CPU_LAHAINA:
144 case APQ_CPU_LAHAINA:
145 case MSM_CPU_SHIMA:
146 case MSM_CPU_SM8325:
147 case APQ_CPU_SM8325P:
148 case MSM_CPU_YUPIK:
149 mSupportExternalControl = true;
150 break;
151 default:
152 mSupportExternalControl = false;
153 break;
154 }
155 break;
156 }
157
158 close(fd);
159 }
160
161 closedir(dp);
162}
163
164/** Play vibration
165 *
166 * @param effectId: ID of the predefined effect will be played. If effectId is valid
167 * (non-negative value), the timeoutMs value will be ignored, and the
168 * real playing length will be set in param@playLengtMs and returned
169 * to VibratorService. If effectId is invalid, value in param@timeoutMs
170 * will be used as the play length for playing a constant effect.
171 * @param timeoutMs: playing length, non-zero means playing, zero means stop playing.
172 * @param playLengthMs: the playing length in ms unit which will be returned to
173 * VibratorService if the request is playing a predefined effect.
174 * The custom_data in periodic is reused for returning the playLengthMs
175 * from kernel space to userspace if the pattern is defined in kernel
176 * driver. It's been defined with following format:
177 * <effect-ID, play-time-in-seconds, play-time-in-milliseconds>.
178 * The effect-ID is used for passing down the predefined effect to
179 * kernel driver, and the rest two parameters are used for returning
180 * back the real playing length from kernel driver.
181 */
182int InputFFDevice::play(int effectId, uint32_t timeoutMs, long *playLengthMs) {
183 struct ff_effect effect;
184 struct input_event play;
185 int16_t data[CUSTOM_DATA_LEN] = {0, 0, 0};
186 int ret;
187#ifdef USE_EFFECT_STREAM
188 const struct effect_stream *stream;
189#endif
190
191 /* For QMAA compliance, return OK even if vibrator device doesn't exist */
192 if (mVibraFd == INVALID_VALUE) {
193 if (playLengthMs != NULL)
194 *playLengthMs = 0;
195 return 0;
196 }
197
198 if (timeoutMs != 0) {
199 if (mCurrAppId != INVALID_VALUE) {
200 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
201 if (ret == -1) {
202 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
203 goto errout;
204 }
205 mCurrAppId = INVALID_VALUE;
206 }
207
208 memset(&effect, 0, sizeof(effect));
209 if (effectId != INVALID_VALUE) {
210 data[0] = effectId;
211 effect.type = FF_PERIODIC;
212 effect.u.periodic.waveform = FF_CUSTOM;
213 effect.u.periodic.magnitude = mCurrMagnitude;
214 effect.u.periodic.custom_data = data;
215 effect.u.periodic.custom_len = sizeof(int16_t) * CUSTOM_DATA_LEN;
216#ifdef USE_EFFECT_STREAM
217 stream = get_effect_stream(effectId);
218 if (stream != NULL) {
219 effect.u.periodic.custom_data = (int16_t *)stream;
220 effect.u.periodic.custom_len = sizeof(*stream);
221 }
222#endif
223 } else {
224 effect.type = FF_CONSTANT;
225 effect.u.constant.level = mCurrMagnitude;
226 effect.replay.length = timeoutMs;
227 }
228
229 effect.id = mCurrAppId;
230 effect.replay.delay = 0;
231
232 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCSFF, &effect));
233 if (ret == -1) {
234 ALOGE("ioctl EVIOCSFF failed, errno = %d", -errno);
235 goto errout;
236 }
237
238 mCurrAppId = effect.id;
239 if (effectId != INVALID_VALUE && playLengthMs != NULL) {
240 *playLengthMs = data[1] * 1000 + data[2];
241#ifdef USE_EFFECT_STREAM
242 if (stream != NULL && stream->play_rate_hz != 0)
243 *playLengthMs = ((stream->length * 1000) / stream->play_rate_hz) + 1;
244#endif
245 }
246
247 play.value = 1;
248 play.type = EV_FF;
249 play.code = mCurrAppId;
250 play.time.tv_sec = 0;
251 play.time.tv_usec = 0;
252 ret = TEMP_FAILURE_RETRY(write(mVibraFd, (const void*)&play, sizeof(play)));
253 if (ret == -1) {
254 ALOGE("write failed, errno = %d\n", -errno);
255 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
256 if (ret == -1)
257 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
258 goto errout;
259 }
260 } else if (mCurrAppId != INVALID_VALUE) {
261 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
262 if (ret == -1) {
263 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
264 goto errout;
265 }
266 mCurrAppId = INVALID_VALUE;
267 }
268 return 0;
269
270errout:
271 mCurrAppId = INVALID_VALUE;
272 return ret;
273}
274
275int InputFFDevice::on(int32_t timeoutMs) {
276 return play(INVALID_VALUE, timeoutMs, NULL);
277}
278
279int InputFFDevice::off() {
280 return play(INVALID_VALUE, 0, NULL);
281}
282
283int InputFFDevice::setAmplitude(uint8_t amplitude) {
284 int tmp, ret;
285 struct input_event ie;
286
287 /* For QMAA compliance, return OK even if vibrator device doesn't exist */
288 if (mVibraFd == INVALID_VALUE)
289 return 0;
290
291 tmp = amplitude * (STRONG_MAGNITUDE - LIGHT_MAGNITUDE) / 255;
292 tmp += LIGHT_MAGNITUDE;
293 ie.type = EV_FF;
294 ie.code = FF_GAIN;
295 ie.value = tmp;
296
297 ret = TEMP_FAILURE_RETRY(write(mVibraFd, &ie, sizeof(ie)));
298 if (ret == -1) {
299 ALOGE("write FF_GAIN failed, errno = %d", -errno);
300 return ret;
301 }
302
303 mCurrMagnitude = tmp;
304 return 0;
305}
306
307int InputFFDevice::playEffect(int effectId, EffectStrength es, long *playLengthMs) {
308 switch (es) {
309 case EffectStrength::LIGHT:
310 mCurrMagnitude = LIGHT_MAGNITUDE;
311 break;
312 case EffectStrength::MEDIUM:
313 mCurrMagnitude = MEDIUM_MAGNITUDE;
314 break;
315 case EffectStrength::STRONG:
316 mCurrMagnitude = STRONG_MAGNITUDE;
317 break;
318 default:
319 return -1;
320 }
321
322 return play(effectId, INVALID_VALUE, playLengthMs);
323}
324
325LedVibratorDevice::LedVibratorDevice() {
326 char devicename[PATH_MAX];
327 int fd;
328
329 mDetected = false;
330
331 snprintf(devicename, sizeof(devicename), "%s/%s", LED_DEVICE, "activate");
332 fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR));
333 if (fd < 0) {
334 ALOGE("open %s failed, errno = %d", devicename, errno);
335 return;
336 }
337
338 mDetected = true;
339}
340
341int LedVibratorDevice::write_value(const char *file, const char *value) {
342 int fd;
343 int ret;
344
345 fd = TEMP_FAILURE_RETRY(open(file, O_WRONLY));
346 if (fd < 0) {
347 ALOGE("open %s failed, errno = %d", file, errno);
348 return -errno;
349 }
350
351 ret = TEMP_FAILURE_RETRY(write(fd, value, strlen(value) + 1));
352 if (ret == -1) {
353 ret = -errno;
354 } else if (ret != strlen(value) + 1) {
355 /* even though EAGAIN is an errno value that could be set
356 by write() in some cases, none of them apply here. So, this return
357 value can be clearly identified when debugging and suggests the
358 caller that it may try to call vibrator_on() again */
359 ret = -EAGAIN;
360 } else {
361 ret = 0;
362 }
363
364 errno = 0;
365 close(fd);
366
367 return ret;
368}
369
370int LedVibratorDevice::on(int32_t timeoutMs) {
371 char file[PATH_MAX];
372 char value[32];
373 int ret;
374
375 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "state");
376 ret = write_value(file, "1");
377 if (ret < 0)
378 goto error;
379
380 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "duration");
381 snprintf(value, sizeof(value), "%u\n", timeoutMs);
382 ret = write_value(file, value);
383 if (ret < 0)
384 goto error;
385
386 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate");
387 ret = write_value(file, "1");
388 if (ret < 0)
389 goto error;
390
391 return 0;
392
393error:
394 ALOGE("Failed to turn on vibrator ret: %d\n", ret);
395 return ret;
396}
397
398int LedVibratorDevice::off()
399{
400 char file[PATH_MAX];
401 int ret;
402
403 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate");
404 ret = write_value(file, "0");
405 return ret;
406}
407
408ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
409 *_aidl_return = IVibrator::CAP_ON_CALLBACK;
410
411 if (ledVib.mDetected) {
412 *_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK;
413 ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
414 return ndk::ScopedAStatus::ok();
415 }
416
417 if (ff.mSupportGain)
418 *_aidl_return |= IVibrator::CAP_AMPLITUDE_CONTROL;
419 if (ff.mSupportEffects)
420 *_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK;
421 if (ff.mSupportExternalControl)
422 *_aidl_return |= IVibrator::CAP_EXTERNAL_CONTROL;
423
424 ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
425 return ndk::ScopedAStatus::ok();
426}
427
428ndk::ScopedAStatus Vibrator::off() {
429 int ret;
430
431 ALOGD("QTI Vibrator off");
432 if (ledVib.mDetected)
433 ret = ledVib.off();
434 else
435 ret = ff.off();
436 if (ret != 0)
437 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
438
439 return ndk::ScopedAStatus::ok();
440}
441
442ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
443 const std::shared_ptr<IVibratorCallback>& callback) {
444 int ret;
445
446 ALOGD("Vibrator on for timeoutMs: %d", timeoutMs);
447 if (ledVib.mDetected)
448 ret = ledVib.on(timeoutMs);
449 else
450 ret = ff.on(timeoutMs);
451
452 if (ret != 0)
453 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
454
455 if (callback != nullptr) {
456 std::thread([=] {
457 ALOGD("Starting on on another thread");
458 usleep(timeoutMs * 1000);
459 ALOGD("Notifying on complete");
460 if (!callback->onComplete().isOk()) {
461 ALOGE("Failed to call onComplete");
462 }
463 }).detach();
464 }
465
466 return ndk::ScopedAStatus::ok();
467}
468
469ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength es, const std::shared_ptr<IVibratorCallback>& callback, int32_t* _aidl_return) {
470 long playLengthMs;
471 int ret;
472
473 if (ledVib.mDetected)
474 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
475
476 ALOGD("Vibrator perform effect %d", effect);
477
478 if (effect < Effect::CLICK ||
479 effect > Effect::HEAVY_CLICK)
480 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
481
482 if (es != EffectStrength::LIGHT && es != EffectStrength::MEDIUM && es != EffectStrength::STRONG)
483 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
484
485 ret = ff.playEffect((static_cast<int>(effect)), es, &playLengthMs);
486 if (ret != 0)
487 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
488
489 if (callback != nullptr) {
490 std::thread([=] {
491 ALOGD("Starting perform on another thread");
492 usleep(playLengthMs * 1000);
493 ALOGD("Notifying perform complete");
494 callback->onComplete();
495 }).detach();
496 }
497
498 *_aidl_return = playLengthMs;
499 return ndk::ScopedAStatus::ok();
500}
501
502ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
503 if (ledVib.mDetected)
504 return ndk::ScopedAStatus::ok();
505
506 *_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
507 Effect::POP, Effect::HEAVY_CLICK};
508
509 return ndk::ScopedAStatus::ok();
510}
511
512ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
513 uint8_t tmp;
514 int ret;
515
516 if (ledVib.mDetected)
517 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
518
519 ALOGD("Vibrator set amplitude: %f", amplitude);
520
521 if (amplitude <= 0.0f || amplitude > 1.0f)
522 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
523
524 if (ff.mInExternalControl)
525 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
526
527 tmp = (uint8_t)(amplitude * 0xff);
528 ret = ff.setAmplitude(tmp);
529 if (ret != 0)
530 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
531
532 return ndk::ScopedAStatus::ok();
533}
534
535ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
536 if (ledVib.mDetected)
537 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
538
539 ALOGD("Vibrator set external control: %d", enabled);
540 if (!ff.mSupportExternalControl)
541 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
542
543 ff.mInExternalControl = enabled;
544 return ndk::ScopedAStatus::ok();
545}
546
547ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs __unused) {
548 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
549}
550
551ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize __unused) {
552 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
553}
554
555ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported __unused) {
556 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
557}
558
559ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive __unused,
560 int32_t* durationMs __unused) {
561 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
562}
563
564ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite __unused,
565 const std::shared_ptr<IVibratorCallback>& callback __unused) {
566 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
567}
568
569ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return __unused) {
570 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
571}
572
573ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id __unused, Effect effect __unused,
574 EffectStrength strength __unused) {
575 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
576}
577
578ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id __unused) {
579 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
580}
581
582} // namespace vibrator
583} // namespace hardware
584} // namespace android
585} // namespace aidl
586