blob: abecdc92541e92f19adef1c8a0bf5771d7e0d9b8 [file] [log] [blame]
Rohit Sekharc5949c32023-08-23 15:19:29 +05301/*
2 * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
Rohit Sekharc5949c32023-08-23 15:19:29 +05303 *
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 * Changes from Qualcomm Innovation Center are provided under the following license:
Michael Bestas4618b562024-09-26 02:28:02 +030030 * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
Rohit Sekharc5949c32023-08-23 15:19:29 +053031 * SPDX-License-Identifier: BSD-3-Clause-Clear
32 */
33
Michael Bestas9e3d0d62024-05-28 03:47:26 +030034#define LOG_TAG "vendor.qti.vibrator.otter"
Rohit Sekharc5949c32023-08-23 15:19:29 +053035
36#include <cutils/properties.h>
37#include <dirent.h>
38#include <inttypes.h>
39#include <linux/input.h>
40#include <log/log.h>
41#include <string.h>
42#include <unistd.h>
43#include <bits/epoll_event.h>
44#include <sys/ioctl.h>
45#include <sys/epoll.h>
46#include <sys/poll.h>
47#include <thread>
48
49#include "include/Vibrator.h"
50#ifdef USE_EFFECT_STREAM
51#include "effect.h"
52#endif
53
54namespace aidl {
55namespace android {
56namespace hardware {
57namespace vibrator {
58
59#define STRONG_MAGNITUDE 0x7fff
60#define MEDIUM_MAGNITUDE 0x5fff
61#define LIGHT_MAGNITUDE 0x3fff
62#define INVALID_VALUE -1
63#define CUSTOM_DATA_LEN 3
64#define NAME_BUF_SIZE 32
65#define PRIMITIVE_ID_MASK 0x8000
66#define MAX_PATTERN_ID 32767
67
68#define MSM_CPU_LAHAINA 415
69#define APQ_CPU_LAHAINA 439
70#define MSM_CPU_SHIMA 450
71#define MSM_CPU_SM8325 501
72#define APQ_CPU_SM8325P 502
73#define MSM_CPU_YUPIK 475
74#define MSM_CPU_CAPE 530
75#define APQ_CPU_CAPE 531
76#define MSM_CPU_TARO 457
77#define MSM_CPU_TARO_LTE 552
78#define MSM_CPU_KALAMA 519
Michael Bestas4618b562024-09-26 02:28:02 +030079#define MSM_CPU_PINEAPPLE 557
Rohit Sekharc5949c32023-08-23 15:19:29 +053080
81#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8)))
82
Michael Bestas886f1142024-05-29 00:50:22 +030083static const char LED_DEVICE[] = "/sys/class/leds/drv26xx_haptic";
Rohit Sekharc5949c32023-08-23 15:19:29 +053084static const char HAPTICS_SYSFS[] = "/sys/class/qcom-haptics";
85
86static constexpr int32_t ComposeDelayMaxMs = 1000;
87static constexpr int32_t ComposeSizeMax = 256;
88
89enum composeEvent {
90 STOP_COMPOSE = 0,
91};
92
93InputFFDevice::InputFFDevice()
94{
95 DIR *dp;
96 FILE *fp = NULL;
97 struct dirent *dir;
98 uint8_t ffBitmask[FF_CNT / 8];
99 char devicename[PATH_MAX];
100 const char *INPUT_DIR = "/dev/input/";
101 char name[NAME_BUF_SIZE];
102 int fd, ret;
103 int soc = property_get_int32("ro.vendor.qti.soc_id", -1);
104
105 mVibraFd = INVALID_VALUE;
106 mSupportGain = false;
107 mSupportEffects = false;
108 mSupportExternalControl = false;
109 mCurrAppId = INVALID_VALUE;
110 mCurrMagnitude = 0x7fff;
111 mInExternalControl = false;
112
113 dp = opendir(INPUT_DIR);
114 if (!dp) {
115 ALOGE("open %s failed, errno = %d", INPUT_DIR, errno);
116 return;
117 }
118
119 memset(ffBitmask, 0, sizeof(ffBitmask));
120 while ((dir = readdir(dp)) != NULL){
121 if (dir->d_name[0] == '.' &&
122 (dir->d_name[1] == '\0' ||
123 (dir->d_name[1] == '.' && dir->d_name[2] == '\0')))
124 continue;
125
126 snprintf(devicename, PATH_MAX, "%s%s", INPUT_DIR, dir->d_name);
127 fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR));
128 if (fd < 0) {
129 ALOGE("open %s failed, errno = %d", devicename, errno);
130 continue;
131 }
132
133 ret = TEMP_FAILURE_RETRY(ioctl(fd, EVIOCGNAME(sizeof(name)), name));
134 if (ret == -1) {
135 ALOGE("get input device name %s failed, errno = %d\n", devicename, errno);
136 close(fd);
137 continue;
138 }
139
140 if (strcmp(name, "qcom-hv-haptics") && strcmp(name, "qti-haptics")
Rohit Sekharc5949c32023-08-23 15:19:29 +0530141 && strcmp(name, "aw8624_haptic")
142 && strcmp(name, "aw8695_haptic")
143 && strcmp(name, "aw8697_haptic")
Michael Bestas4618b562024-09-26 02:28:02 +0300144 && strcmp(name, "awinic_haptic")
145 && strcmp(name, "drv2624:haptics")) {
Rohit Sekharc5949c32023-08-23 15:19:29 +0530146 ALOGD("not a supported haptics device\n");
147 close(fd);
148 continue;
149 }
150
151 ALOGI("%s is detected at %s\n", name, devicename);
152 ret = TEMP_FAILURE_RETRY(ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffBitmask)), ffBitmask));
153 if (ret == -1) {
154 ALOGE("ioctl failed, errno = %d", errno);
155 close(fd);
156 continue;
157 }
158
159 if (test_bit(FF_CONSTANT, ffBitmask) ||
160 test_bit(FF_PERIODIC, ffBitmask)) {
161 mVibraFd = fd;
162 if (test_bit(FF_CUSTOM, ffBitmask))
163 mSupportEffects = true;
164 if (test_bit(FF_GAIN, ffBitmask))
165 mSupportGain = true;
166
167 if (soc <= 0 && (fp = fopen("/sys/devices/soc0/soc_id", "r")) != NULL) {
168 fscanf(fp, "%u", &soc);
169 fclose(fp);
170 }
171 switch (soc) {
172 case MSM_CPU_LAHAINA:
173 case APQ_CPU_LAHAINA:
174 case MSM_CPU_SHIMA:
175 case MSM_CPU_SM8325:
176 case APQ_CPU_SM8325P:
177 case MSM_CPU_TARO:
178 case MSM_CPU_TARO_LTE:
179 case MSM_CPU_YUPIK:
180 case MSM_CPU_CAPE:
181 case APQ_CPU_CAPE:
182 case MSM_CPU_KALAMA:
Michael Bestas4618b562024-09-26 02:28:02 +0300183 case MSM_CPU_PINEAPPLE:
Rohit Sekharc5949c32023-08-23 15:19:29 +0530184 mSupportExternalControl = true;
185 break;
186 default:
187 mSupportExternalControl = false;
188 break;
189 }
190 break;
191 }
192
193 close(fd);
194 }
195
196 closedir(dp);
197}
198
199/** Play vibration
200 *
201 * @param effectId: ID of the predefined effect will be played. If effectId is valid
202 * (non-negative value), the timeoutMs value will be ignored, and the
203 * real playing length will be set in param@playLengtMs and returned
204 * to VibratorService. If effectId is invalid, value in param@timeoutMs
205 * will be used as the play length for playing a constant effect.
206 * @param timeoutMs: playing length, non-zero means playing, zero means stop playing.
207 * @param playLengthMs: the playing length in ms unit which will be returned to
208 * VibratorService if the request is playing a predefined effect.
209 * The custom_data in periodic is reused for returning the playLengthMs
210 * from kernel space to userspace if the pattern is defined in kernel
211 * driver. It's been defined with following format:
212 * <effect-ID, play-time-in-seconds, play-time-in-milliseconds>.
213 * The effect-ID is used for passing down the predefined effect to
214 * kernel driver, and the rest two parameters are used for returning
215 * back the real playing length from kernel driver.
216 */
217int InputFFDevice::play(int effectId, uint32_t timeoutMs, long *playLengthMs) {
218 struct ff_effect effect;
219 struct input_event play;
220 int16_t data[CUSTOM_DATA_LEN] = {0, 0, 0};
221 int ret;
222#ifdef USE_EFFECT_STREAM
223 const struct effect_stream *stream;
224#endif
225
Michael Bestas4618b562024-09-26 02:28:02 +0300226 mtx.lock();
Rohit Sekharc5949c32023-08-23 15:19:29 +0530227 /* For QMAA compliance, return OK even if vibrator device doesn't exist */
228 if (mVibraFd == INVALID_VALUE) {
229 if (playLengthMs != NULL)
230 *playLengthMs = 0;
Michael Bestas4618b562024-09-26 02:28:02 +0300231 mtx.unlock();
Michael Bestas4072cc12024-01-02 19:29:16 +0200232 return 0;
Rohit Sekharc5949c32023-08-23 15:19:29 +0530233 }
234
235 if (timeoutMs != 0) {
236 if (mCurrAppId != INVALID_VALUE) {
237 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
238 if (ret == -1) {
239 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
240 goto errout;
241 }
242 mCurrAppId = INVALID_VALUE;
243 }
244
245 memset(&effect, 0, sizeof(effect));
246 if (effectId != INVALID_VALUE) {
247 data[0] = effectId;
248 effect.type = FF_PERIODIC;
249 effect.u.periodic.waveform = FF_CUSTOM;
250 effect.u.periodic.magnitude = mCurrMagnitude;
251 effect.u.periodic.custom_data = data;
252 effect.u.periodic.custom_len = sizeof(int16_t) * CUSTOM_DATA_LEN;
253#ifdef USE_EFFECT_STREAM
254 stream = get_effect_stream(effectId);
255 if (stream != NULL) {
256 effect.u.periodic.custom_data = (int16_t *)stream;
257 effect.u.periodic.custom_len = sizeof(*stream);
258 }
259#endif
260 } else {
261 effect.type = FF_CONSTANT;
262 effect.u.constant.level = mCurrMagnitude;
263 effect.replay.length = timeoutMs;
264 }
265
266 effect.id = mCurrAppId;
267 effect.replay.delay = 0;
268
269 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCSFF, &effect));
270 if (ret == -1) {
271 ALOGE("ioctl EVIOCSFF failed, errno = %d", -errno);
272 goto errout;
273 }
274
275 mCurrAppId = effect.id;
276 if (effectId != INVALID_VALUE && playLengthMs != NULL) {
277 *playLengthMs = data[1] * 1000 + data[2];
278#ifdef USE_EFFECT_STREAM
279 if (stream != NULL && stream->play_rate_hz != 0)
280 *playLengthMs = ((stream->length * 1000) / stream->play_rate_hz) + 1;
281#endif
282 }
283
284 play.value = 1;
285 play.type = EV_FF;
286 play.code = mCurrAppId;
287 play.time.tv_sec = 0;
288 play.time.tv_usec = 0;
289 ret = TEMP_FAILURE_RETRY(write(mVibraFd, (const void*)&play, sizeof(play)));
290 if (ret == -1) {
291 ALOGE("write failed, errno = %d\n", -errno);
292 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
293 if (ret == -1)
294 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
295 goto errout;
296 }
297 } else if (mCurrAppId != INVALID_VALUE) {
298 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
299 if (ret == -1) {
300 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
301 goto errout;
302 }
303 mCurrAppId = INVALID_VALUE;
304 }
Michael Bestas4618b562024-09-26 02:28:02 +0300305 mtx.unlock();
Rohit Sekharc5949c32023-08-23 15:19:29 +0530306 return 0;
307
308errout:
309 mCurrAppId = INVALID_VALUE;
Michael Bestas4618b562024-09-26 02:28:02 +0300310 mtx.unlock();
Rohit Sekharc5949c32023-08-23 15:19:29 +0530311 return ret;
312}
313
314int InputFFDevice::on(int32_t timeoutMs) {
315 return play(INVALID_VALUE, timeoutMs, NULL);
316}
317
318int InputFFDevice::off() {
319 return play(INVALID_VALUE, 0, NULL);
320}
321
322int InputFFDevice::setAmplitude(uint8_t amplitude) {
323 int tmp, ret;
324 struct input_event ie;
325
326 /* For QMAA compliance, return OK even if vibrator device doesn't exist */
327 if (mVibraFd == INVALID_VALUE)
328 return 0;
329
330 tmp = amplitude * (STRONG_MAGNITUDE - LIGHT_MAGNITUDE) / 255;
331 tmp += LIGHT_MAGNITUDE;
332 ie.type = EV_FF;
333 ie.code = FF_GAIN;
334 ie.value = tmp;
335
336 ret = TEMP_FAILURE_RETRY(write(mVibraFd, &ie, sizeof(ie)));
337 if (ret == -1) {
338 ALOGE("write FF_GAIN failed, errno = %d", -errno);
339 return ret;
340 }
341
342 mCurrMagnitude = tmp;
343 return 0;
344}
345
346int InputFFDevice::playEffect(int effectId, EffectStrength es, long *playLengthMs) {
347 if (effectId > MAX_PATTERN_ID) {
348 ALOGE("effect id %d exceeds %d", effectId, MAX_PATTERN_ID);
349 return -1;
350 }
351
352 switch (es) {
353 case EffectStrength::LIGHT:
354 mCurrMagnitude = LIGHT_MAGNITUDE;
355 break;
356 case EffectStrength::MEDIUM:
357 mCurrMagnitude = MEDIUM_MAGNITUDE;
358 break;
359 case EffectStrength::STRONG:
360 mCurrMagnitude = STRONG_MAGNITUDE;
361 break;
362 default:
363 return -1;
364 }
365
366 return play(effectId, INVALID_VALUE, playLengthMs);
367}
368
369int InputFFDevice::playPrimitive(int primitiveId, float amplitude, long *playLengthMs) {
370 int8_t tmp;
371 int ret = 0;
372
373 if (primitiveId > MAX_PATTERN_ID) {
374 ALOGE("primitive id %d exceeds %d", primitiveId, MAX_PATTERN_ID);
375 return -1;
376 }
377
378 primitiveId |= PRIMITIVE_ID_MASK;
379 tmp = (uint8_t)(amplitude * 0xff);
380 mCurrMagnitude = tmp * (STRONG_MAGNITUDE - LIGHT_MAGNITUDE) / 255;
381 mCurrMagnitude += LIGHT_MAGNITUDE;
382
383 ret = play(primitiveId, INVALID_VALUE, playLengthMs);
384 if (ret != 0)
385 ALOGE("Failed to play primitive %d", primitiveId);
386
387 return ret;
388}
389
390LedVibratorDevice::LedVibratorDevice() {
391 char devicename[PATH_MAX];
392 int fd;
393
394 mDetected = false;
395
Michael Bestas886f1142024-05-29 00:50:22 +0300396 snprintf(devicename, sizeof(devicename), "%s/%s", LED_DEVICE, "state");
Rohit Sekharc5949c32023-08-23 15:19:29 +0530397 fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR));
398 if (fd < 0) {
399 ALOGE("open %s failed, errno = %d", devicename, errno);
400 return;
401 }
402
403 mDetected = true;
404}
405
406int LedVibratorDevice::write_value(const char *file, const char *value) {
407 int fd;
408 int ret;
409
410 fd = TEMP_FAILURE_RETRY(open(file, O_WRONLY));
411 if (fd < 0) {
412 ALOGE("open %s failed, errno = %d", file, errno);
413 return -errno;
414 }
415
416 ret = TEMP_FAILURE_RETRY(write(fd, value, strlen(value) + 1));
417 if (ret == -1) {
418 ret = -errno;
419 } else if (ret != strlen(value) + 1) {
420 /* even though EAGAIN is an errno value that could be set
421 by write() in some cases, none of them apply here. So, this return
422 value can be clearly identified when debugging and suggests the
423 caller that it may try to call vibrator_on() again */
424 ret = -EAGAIN;
425 } else {
426 ret = 0;
427 }
428
429 errno = 0;
430 close(fd);
431
432 return ret;
433}
434
435int LedVibratorDevice::on(int32_t timeoutMs) {
436 char file[PATH_MAX];
437 char value[32];
438 int ret;
439
Michael Bestas82f93c22024-05-29 00:45:13 +0300440 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "state");
Michael Bestas82f93c22024-05-29 00:45:13 +0300441 snprintf(value, sizeof(value), "%u\n", timeoutMs);
442 ret = write_value(file, value);
443 if (ret < 0)
444 goto error;
Rohit Sekharc5949c32023-08-23 15:19:29 +0530445
Rohit Sekharc5949c32023-08-23 15:19:29 +0530446 return 0;
447
448error:
449 ALOGE("Failed to turn on vibrator ret: %d\n", ret);
450 return ret;
451}
452
453int LedVibratorDevice::off()
454{
455 char file[PATH_MAX];
456 int ret;
457
Michael Bestas886f1142024-05-29 00:50:22 +0300458 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "state");
Rohit Sekharc5949c32023-08-23 15:19:29 +0530459 ret = write_value(file, "0");
Rohit Sekharc5949c32023-08-23 15:19:29 +0530460 return ret;
461}
462
463Vibrator::Vibrator() {
464 struct epoll_event ev;
465
466 epollfd = INVALID_VALUE;
467 pipefd[0] = INVALID_VALUE;
468 pipefd[1] = INVALID_VALUE;
469 inComposition = false;
470
471 if (!ff.mSupportEffects)
472 return;
473
474 if (pipe(pipefd)) {
475 ALOGE("Failed to get pipefd error=%d", errno);
476 return;
477 }
478
479 epollfd = epoll_create1(0);
480 if (epollfd < 0) {
481 ALOGE("Failed to create epoll fd error=%d", errno);
482 goto pipefd_close;
483 }
484
485 ev.events = EPOLLIN;
486 ev.data.fd = pipefd[0];
487
488 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefd[0], &ev) == -1) {
489 ALOGE("Failed to add pipefd to epoll ctl error=%d", errno);
490 goto epollfd_close;
491 }
492
493 return;
494
495epollfd_close:
496 close(epollfd);
497 epollfd = INVALID_VALUE;
498pipefd_close:
499 close(pipefd[0]);
500 close(pipefd[1]);
501 pipefd[0] = INVALID_VALUE;
502 pipefd[1] = INVALID_VALUE;
503}
504
505Vibrator::~Vibrator() {
506 if (epollfd != INVALID_VALUE)
507 close(epollfd);
508 if (pipefd[0] != INVALID_VALUE)
509 close(pipefd[0]);
510 if (pipefd[1] != INVALID_VALUE)
511 close(pipefd[1]);
512}
513
Michael Bestas4618b562024-09-26 02:28:02 +0300514static int getPrimitiveDurationFromSysfs(uint32_t primitive_id, int32_t* durationMs);
515
Rohit Sekharc5949c32023-08-23 15:19:29 +0530516ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
517 *_aidl_return = IVibrator::CAP_ON_CALLBACK;
518
519 if (ledVib.mDetected) {
520 ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
521 return ndk::ScopedAStatus::ok();
522 }
523
524 if (ff.mSupportGain)
525 *_aidl_return |= IVibrator::CAP_AMPLITUDE_CONTROL;
526 if (ff.mSupportEffects) {
Michael Bestas4618b562024-09-26 02:28:02 +0300527 *_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK;
528 int32_t primitiveDuration = 0;
529 uint32_t primitiveId = static_cast<uint32_t>(CompositePrimitive::CLICK);
530 getPrimitiveDurationFromSysfs(primitiveId, &primitiveDuration);
531 if (primitiveDuration != 0)
Rohit Sekharc5949c32023-08-23 15:19:29 +0530532 *_aidl_return |= IVibrator::CAP_COMPOSE_EFFECTS;
Rohit Sekharc5949c32023-08-23 15:19:29 +0530533 }
534 if (ff.mSupportExternalControl)
535 *_aidl_return |= IVibrator::CAP_EXTERNAL_CONTROL;
536
537 ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
538 return ndk::ScopedAStatus::ok();
539}
540
541ndk::ScopedAStatus Vibrator::off() {
542 int ret;
543 int composeEven = STOP_COMPOSE;
544
545 ALOGD("QTI Vibrator off");
546 if (ledVib.mDetected)
547 ret = ledVib.off();
548 else
549 ret = ff.off();
550 if (ret != 0)
551 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
552
553 if (inComposition) {
554 ret = write(pipefd[1], &composeEven, sizeof(composeEven));
555 if (ret < 0) {
556 ALOGE("Failed to send STOP_COMPOSE event");
557 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
558 }
559 }
560
561 return ndk::ScopedAStatus::ok();
562}
563
564ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
565 const std::shared_ptr<IVibratorCallback>& callback) {
566 int ret;
567
568 ALOGD("Vibrator on for timeoutMs: %d", timeoutMs);
569 if (ledVib.mDetected)
570 ret = ledVib.on(timeoutMs);
571 else
572 ret = ff.on(timeoutMs);
573
574 if (ret != 0)
575 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
576
577 if (callback != nullptr) {
578 std::thread([=] {
579 ALOGD("Starting on on another thread");
580 usleep(timeoutMs * 1000);
581 ALOGD("Notifying on complete");
582 if (!callback->onComplete().isOk()) {
583 ALOGE("Failed to call onComplete");
584 }
585 }).detach();
586 }
587
588 return ndk::ScopedAStatus::ok();
589}
590
591ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength es, const std::shared_ptr<IVibratorCallback>& callback, int32_t* _aidl_return) {
592 long playLengthMs;
593 int ret;
594
595 if (ledVib.mDetected)
596 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
597
598 ALOGD("Vibrator perform effect %d", effect);
599 if (Offload.mEnabled == 1) {
600 if ((effect < Effect::CLICK) ||
601 ((effect > Effect::HEAVY_CLICK) && (effect < Effect::RINGTONE_12)) ||
602 (effect > Effect::RINGTONE_15))
603 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
604 }
605 else {
606 if (effect < Effect::CLICK || effect > Effect::HEAVY_CLICK)
607 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
608 }
609
610 if (es != EffectStrength::LIGHT && es != EffectStrength::MEDIUM && es != EffectStrength::STRONG)
611 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
612
613 ret = ff.playEffect((static_cast<int>(effect)), es, &playLengthMs);
614 if (ret != 0)
615 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
616
617 if (callback != nullptr) {
618 std::thread([=] {
619 ALOGD("Starting perform on another thread");
620 usleep(playLengthMs * 1000);
621 ALOGD("Notifying perform complete");
622 callback->onComplete();
623 }).detach();
624 }
625
626 *_aidl_return = playLengthMs;
627 return ndk::ScopedAStatus::ok();
628}
629
630ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
631 if (ledVib.mDetected)
632 return ndk::ScopedAStatus::ok();
633
634 if (Offload.mEnabled == 1)
635 *_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
636 Effect::POP, Effect::HEAVY_CLICK, Effect::RINGTONE_12,
637 Effect::RINGTONE_13, Effect::RINGTONE_14, Effect::RINGTONE_15};
638 else
639 *_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
640 Effect::POP, Effect::HEAVY_CLICK};
641
642 return ndk::ScopedAStatus::ok();
643}
644
645ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
646 uint8_t tmp;
647 int ret;
648
649 if (ledVib.mDetected)
650 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
651
652 if (!ff.mSupportGain)
653 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
654
655 ALOGD("Vibrator set amplitude: %f", amplitude);
656
657 if (amplitude <= 0.0f || amplitude > 1.0f)
658 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
659
660 if (ff.mInExternalControl)
661 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
662
663 tmp = (uint8_t)(amplitude * 0xff);
664 ret = ff.setAmplitude(tmp);
665 if (ret != 0)
666 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
667
668 return ndk::ScopedAStatus::ok();
669}
670
671ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
672 if (ledVib.mDetected)
673 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
674
675 ALOGD("Vibrator set external control: %d", enabled);
676 if (!ff.mSupportExternalControl)
677 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
678
679 ff.mInExternalControl = enabled;
680 return ndk::ScopedAStatus::ok();
681}
682
683ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) {
684 *maxDelayMs = ComposeDelayMaxMs;
685 return ndk::ScopedAStatus::ok();
686}
687
688ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) {
689 *maxSize = ComposeSizeMax;
690 return ndk::ScopedAStatus::ok();
691}
692
693ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
694 *supported = {
695 CompositePrimitive::NOOP, CompositePrimitive::CLICK,
696 CompositePrimitive::THUD, CompositePrimitive::SPIN,
697 CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
698 CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
699 CompositePrimitive::LOW_TICK,
700 };
701 return ndk::ScopedAStatus::ok();
702}
703
704static int getPrimitiveDurationFromSysfs(uint32_t primitive_id, int32_t* durationMs) {
705 int count = 0;
706 int fd = 0;
707 int ret = 0;
708 /* the Max primitive id is 32767, so define the size of primitive_buf to 6 */
709 char primitive_buf[6];
710 /* the max primitive_duration is the max value of int32, so define the size to 10 */
711 char primitive_duration[10];
712 char primitive_duration_sysfs[50];
713
714 ret = snprintf(primitive_duration_sysfs, sizeof(primitive_duration_sysfs), "%s%s", HAPTICS_SYSFS, "/primitive_duration");
715 if (ret < 0) {
716 ALOGE("Failed to get primitive duration node, ret = %d\n", ret);
717 return ret;
718 }
719
720 count = snprintf(primitive_buf, sizeof(primitive_buf), "%d%c", primitive_id, '\n');
721 if (count < 0) {
722 ALOGE("Failed to get primitive id, count = %d\n", count);
723 ret = count;
724 return ret;
725 }
726
727 fd = TEMP_FAILURE_RETRY(open(primitive_duration_sysfs, O_RDWR));
728 if (fd < 0) {
729 ALOGE("open %s failed, errno = %d", primitive_duration_sysfs, errno);
730 ret = fd;
731 return ret;
732 }
733
734 ret = TEMP_FAILURE_RETRY(write(fd, primitive_buf, count));
735 if (ret < 0) {
736 ALOGE("write primitive %d failed, errno = %d", primitive_id, errno);
737 goto close_fd;
738 }
739
740 ret = TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET));
741 if (ret < 0) {
742 ALOGE("lseek fd to file head failed, errno = %d", errno);
743 goto close_fd;
744 }
745
746 ret = TEMP_FAILURE_RETRY(read(fd, primitive_duration, sizeof(primitive_duration)));
747 if (ret < 0) {
748 ALOGE("read primitive %d failed, errno = %d", primitive_id, errno);
749 goto close_fd;
750 }
751
752 *durationMs = atoi(primitive_duration);
753 *durationMs /= 1000;
754
755close_fd:
756 ret = TEMP_FAILURE_RETRY(close(fd));
757 if (ret < 0) {
758 ALOGE("close primitive duration device failed, errno = %d", errno);
759 return ret;
760 }
761
762 return ret;
763}
764
765ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
766 int32_t* durationMs) {
767 uint32_t primitive_id = static_cast<uint32_t>(primitive);
768 int ret = 0;
769
770#ifdef USE_EFFECT_STREAM
771 primitive_id |= PRIMITIVE_ID_MASK ;
772 const struct effect_stream *stream;
773 stream = get_effect_stream(primitive_id);
774 if (stream != NULL && stream->play_rate_hz != 0)
775 *durationMs = ((stream->length * 1000) / stream->play_rate_hz) + 1;
776
777 ALOGD("primitive-%d duration is %dms", primitive, *durationMs);
778 return ndk::ScopedAStatus::ok();
779#endif
780
781 ret = getPrimitiveDurationFromSysfs(primitive_id, durationMs);
782 if (ret < 0)
783 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
784
785 ALOGD("primitive-%d duration is %dms", primitive, *durationMs);
786
787 return ndk::ScopedAStatus::ok();
788}
789
790void Vibrator::composePlayThread(Vibrator *vibrator,
791 const std::vector<CompositeEffect>& composite,
792 const std::shared_ptr<IVibratorCallback>& callback){
793 struct epoll_event events;
794 long playLengthMs = 0;
795 int nfd = 0;
796 int status = 0;
797 int ret = 0;
798
799 ALOGD("start a new thread for composeEffect");
800 for (auto& e : composite) {
801 if (e.delayMs) {
802 nfd = epoll_wait(vibrator->epollfd, &events, 1, e.delayMs);
803 if ((nfd == -1) && (errno != EINTR)) {
804 ALOGE("Failed to wait delayMs, error=%d", errno);
805 break;
806 }
807
808 if (nfd > 0) {
809 /* It's supposed that STOP_COMPOSE command is received so quit the composition */
810 ret = read(vibrator->pipefd[0], &status, sizeof(int));
811 if (ret < 0) {
812 ALOGE("Failed to read stop status from pipe(delayMs), status = %d", status);
813 break;
814 }
815 if (status == STOP_COMPOSE)
816 break;
817 }
818 }
819
820 vibrator->ff.playPrimitive((static_cast<int>(e.primitive)), e.scale, &playLengthMs);
821 nfd = epoll_wait(vibrator->epollfd, &events, 1, playLengthMs);
822 if (nfd == -1 && (errno != EINTR)) {
823 ALOGE("Failed to wait sleep playLengthMs, error=%d", errno);
824 break;
825 }
826
827 if (nfd > 0) {
828 /* It's supposed that STOP_COMPOSE command is received so quit the composition */
829 ret = read(vibrator->pipefd[0], &status, sizeof(int));
830 if (ret < 0) {
831 ALOGE("Failed to read stop status from pipe(playLengthMs), status = %d", status);
832 break;
833 }
Michael Bestas4618b562024-09-26 02:28:02 +0300834 if (status == STOP_COMPOSE) {
835
836 /*
837 * There is a corner case that the off() command could be executed in
838 * main thread before the primitive play is triggered in the child thread,
839 * such as, when playing a very short primitive effect while the system is
840 * pretty busy (one example is enabling all kernel console log after executed
841 * "echo Y > /sys/module/printk/parameters/ignore_loglevel"), the child thread
842 * may not be able to schedule out for running before the main thread times out
843 * on the primitive duration and sent the off() command, there won't be any
844 * off() command coming again to stop the primitive effect after it's triggered.
845 *
846 * However, the primitive could be played out and stopped automatically but the
847 * haptics driver does expect an explicit off() command to restore HW/SW logic
848 * after that, so call it here. It would result a redundant off() command in
849 * normal case but it won't do any harm because it would be ignored and not sent
850 * to haptics driver because of an invalid mCurrAppId. It would also result in the
851 * primitive effect to stop immediately right after it's triggered in such
852 * corner case. But considering the main thread has stopped it before off() is
853 * called here, take this as a limitation and it is expected not playing the
854 * vibration out.
855 */
856
857 vibrator->ff.off();
Rohit Sekharc5949c32023-08-23 15:19:29 +0530858 break;
Michael Bestas4618b562024-09-26 02:28:02 +0300859 }
Rohit Sekharc5949c32023-08-23 15:19:29 +0530860 }
861 }
862
863 ALOGD("Notifying composite complete, playlength= %ld", playLengthMs);
864 if (callback)
865 callback->onComplete();
866
867 vibrator->inComposition = false;
868}
869
870ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
871 const std::shared_ptr<IVibratorCallback>& callback) {
872 int status, nfd = 0, durationMs = 0, timeoutMs = 0;
873 struct epoll_event events;
874
875 if (composite.size() > ComposeSizeMax) {
876 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
877 }
878
879 std::vector<CompositePrimitive> supported;
880 getSupportedPrimitives(&supported);
881
882 for (auto& e : composite) {
883 if (e.delayMs > ComposeDelayMaxMs) {
884 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
885 }
886 if (e.scale < 0.0f || e.scale > 1.0f) {
887 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
888 }
889 if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
890 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
891 }
892
893 getPrimitiveDuration(e.primitive, &durationMs);
894 timeoutMs += durationMs + e.delayMs;
895 }
896
897 /*
898 * wait for 2 times of the play length timeout to make sure last play has been
899 * terminated successfully.
900 */
901 timeoutMs = (timeoutMs + 10) * 2;
902 /* Stop previous composition if it has not yet been completed */
903 if (inComposition) {
904 ALOGD("Last composePlayThread has not done yet, stop it manually");
905 off();
906
907 while (inComposition && timeoutMs--)
908 usleep(1000);
909
910 if (timeoutMs == 0) {
911 ALOGE("wait for last composePlayThread done timeout");
912 return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
913 }
Michael Bestas4618b562024-09-26 02:28:02 +0300914 }
Rohit Sekharc5949c32023-08-23 15:19:29 +0530915
Michael Bestas4618b562024-09-26 02:28:02 +0300916 /* Read the pipe again to remove any stale data before triggering a new play */
917 nfd = epoll_wait(epollfd, &events, 1, 0);
918 if (nfd == -1 && (errno != EINTR)) {
919 ALOGE("Failed to wait sleep playLengthMs, error=%d", errno);
920 return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
921 }
922 if (nfd > 0) {
923 ALOGD("A stale event is cached in the pipe, remove it");
924 read(pipefd[0], &status, sizeof(int));
Rohit Sekharc5949c32023-08-23 15:19:29 +0530925 }
926
927 inComposition = true;
928 composeThread = std::thread(composePlayThread, this, composite, callback);
929 composeThread.detach();
930
931 ALOGD("trigger composition successfully");
932 return ndk::ScopedAStatus::ok();
933}
934
935ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return __unused) {
936 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
937}
938
939ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id __unused, Effect effect __unused,
940 EffectStrength strength __unused) {
941 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
942}
943
944ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id __unused) {
945 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
946}
947
948ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz __unused) {
949 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
950}
951
952ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor __unused) {
953 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
954}
955
956ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz __unused) {
957 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
958}
959
960ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz __unused) {
961 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
962}
963
964ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return __unused) {
965 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
966}
967
968ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs __unused) {
969 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
970}
971
972ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize __unused) {
973 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
974}
975
976ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported __unused) {
977 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
978}
979
980ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite __unused,
981 const std::shared_ptr<IVibratorCallback> &callback __unused) {
982 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
983}
984
985} // namespace vibrator
986} // namespace hardware
987} // namespace android
988} // namespace aidl
989