blob: 04bc256d5670e2629eb174de3e0e006b1024c821 [file] [log] [blame]
Rohit Sekharc5949c32023-08-23 15:19:29 +05301/*
2 * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
3 * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials provided
13 * with the distribution.
14 * * Neither the name of The Linux Foundation nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
19 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
28 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Changes from Qualcomm Innovation Center are provided under the following license:
31 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
32 * SPDX-License-Identifier: BSD-3-Clause-Clear
33 */
34
Michael Bestas9e3d0d62024-05-28 03:47:26 +030035#define LOG_TAG "vendor.qti.vibrator.otter"
Rohit Sekharc5949c32023-08-23 15:19:29 +053036
37#include <cutils/properties.h>
38#include <dirent.h>
39#include <inttypes.h>
40#include <linux/input.h>
41#include <log/log.h>
42#include <string.h>
43#include <unistd.h>
44#include <bits/epoll_event.h>
45#include <sys/ioctl.h>
46#include <sys/epoll.h>
47#include <sys/poll.h>
48#include <thread>
49
50#include "include/Vibrator.h"
51#ifdef USE_EFFECT_STREAM
52#include "effect.h"
53#endif
54
55namespace aidl {
56namespace android {
57namespace hardware {
58namespace vibrator {
59
60#define STRONG_MAGNITUDE 0x7fff
61#define MEDIUM_MAGNITUDE 0x5fff
62#define LIGHT_MAGNITUDE 0x3fff
63#define INVALID_VALUE -1
64#define CUSTOM_DATA_LEN 3
65#define NAME_BUF_SIZE 32
66#define PRIMITIVE_ID_MASK 0x8000
67#define MAX_PATTERN_ID 32767
68
69#define MSM_CPU_LAHAINA 415
70#define APQ_CPU_LAHAINA 439
71#define MSM_CPU_SHIMA 450
72#define MSM_CPU_SM8325 501
73#define APQ_CPU_SM8325P 502
74#define MSM_CPU_YUPIK 475
75#define MSM_CPU_CAPE 530
76#define APQ_CPU_CAPE 531
77#define MSM_CPU_TARO 457
78#define MSM_CPU_TARO_LTE 552
79#define MSM_CPU_KALAMA 519
80
81#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8)))
82
Michael Bestas82f93c22024-05-29 00:45:13 +030083static const char LED_DEVICE[] = "/sys/class/leds/vibrator";
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")
144 && strcmp(name, "awinic_haptic")) {
145 ALOGD("not a supported haptics device\n");
146 close(fd);
147 continue;
148 }
149
150 ALOGI("%s is detected at %s\n", name, devicename);
151 ret = TEMP_FAILURE_RETRY(ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffBitmask)), ffBitmask));
152 if (ret == -1) {
153 ALOGE("ioctl failed, errno = %d", errno);
154 close(fd);
155 continue;
156 }
157
158 if (test_bit(FF_CONSTANT, ffBitmask) ||
159 test_bit(FF_PERIODIC, ffBitmask)) {
160 mVibraFd = fd;
161 if (test_bit(FF_CUSTOM, ffBitmask))
162 mSupportEffects = true;
163 if (test_bit(FF_GAIN, ffBitmask))
164 mSupportGain = true;
165
166 if (soc <= 0 && (fp = fopen("/sys/devices/soc0/soc_id", "r")) != NULL) {
167 fscanf(fp, "%u", &soc);
168 fclose(fp);
169 }
170 switch (soc) {
171 case MSM_CPU_LAHAINA:
172 case APQ_CPU_LAHAINA:
173 case MSM_CPU_SHIMA:
174 case MSM_CPU_SM8325:
175 case APQ_CPU_SM8325P:
176 case MSM_CPU_TARO:
177 case MSM_CPU_TARO_LTE:
178 case MSM_CPU_YUPIK:
179 case MSM_CPU_CAPE:
180 case APQ_CPU_CAPE:
181 case MSM_CPU_KALAMA:
182 mSupportExternalControl = true;
183 break;
184 default:
185 mSupportExternalControl = false;
186 break;
187 }
188 break;
189 }
190
191 close(fd);
192 }
193
194 closedir(dp);
195}
196
197/** Play vibration
198 *
199 * @param effectId: ID of the predefined effect will be played. If effectId is valid
200 * (non-negative value), the timeoutMs value will be ignored, and the
201 * real playing length will be set in param@playLengtMs and returned
202 * to VibratorService. If effectId is invalid, value in param@timeoutMs
203 * will be used as the play length for playing a constant effect.
204 * @param timeoutMs: playing length, non-zero means playing, zero means stop playing.
205 * @param playLengthMs: the playing length in ms unit which will be returned to
206 * VibratorService if the request is playing a predefined effect.
207 * The custom_data in periodic is reused for returning the playLengthMs
208 * from kernel space to userspace if the pattern is defined in kernel
209 * driver. It's been defined with following format:
210 * <effect-ID, play-time-in-seconds, play-time-in-milliseconds>.
211 * The effect-ID is used for passing down the predefined effect to
212 * kernel driver, and the rest two parameters are used for returning
213 * back the real playing length from kernel driver.
214 */
215int InputFFDevice::play(int effectId, uint32_t timeoutMs, long *playLengthMs) {
216 struct ff_effect effect;
217 struct input_event play;
218 int16_t data[CUSTOM_DATA_LEN] = {0, 0, 0};
219 int ret;
220#ifdef USE_EFFECT_STREAM
221 const struct effect_stream *stream;
222#endif
223
224 /* For QMAA compliance, return OK even if vibrator device doesn't exist */
225 if (mVibraFd == INVALID_VALUE) {
226 if (playLengthMs != NULL)
227 *playLengthMs = 0;
Michael Bestas4072cc12024-01-02 19:29:16 +0200228 return 0;
Rohit Sekharc5949c32023-08-23 15:19:29 +0530229 }
230
231 if (timeoutMs != 0) {
232 if (mCurrAppId != INVALID_VALUE) {
233 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
234 if (ret == -1) {
235 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
236 goto errout;
237 }
238 mCurrAppId = INVALID_VALUE;
239 }
240
241 memset(&effect, 0, sizeof(effect));
242 if (effectId != INVALID_VALUE) {
243 data[0] = effectId;
244 effect.type = FF_PERIODIC;
245 effect.u.periodic.waveform = FF_CUSTOM;
246 effect.u.periodic.magnitude = mCurrMagnitude;
247 effect.u.periodic.custom_data = data;
248 effect.u.periodic.custom_len = sizeof(int16_t) * CUSTOM_DATA_LEN;
249#ifdef USE_EFFECT_STREAM
250 stream = get_effect_stream(effectId);
251 if (stream != NULL) {
252 effect.u.periodic.custom_data = (int16_t *)stream;
253 effect.u.periodic.custom_len = sizeof(*stream);
254 }
255#endif
256 } else {
257 effect.type = FF_CONSTANT;
258 effect.u.constant.level = mCurrMagnitude;
259 effect.replay.length = timeoutMs;
260 }
261
262 effect.id = mCurrAppId;
263 effect.replay.delay = 0;
264
265 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCSFF, &effect));
266 if (ret == -1) {
267 ALOGE("ioctl EVIOCSFF failed, errno = %d", -errno);
268 goto errout;
269 }
270
271 mCurrAppId = effect.id;
272 if (effectId != INVALID_VALUE && playLengthMs != NULL) {
273 *playLengthMs = data[1] * 1000 + data[2];
274#ifdef USE_EFFECT_STREAM
275 if (stream != NULL && stream->play_rate_hz != 0)
276 *playLengthMs = ((stream->length * 1000) / stream->play_rate_hz) + 1;
277#endif
278 }
279
280 play.value = 1;
281 play.type = EV_FF;
282 play.code = mCurrAppId;
283 play.time.tv_sec = 0;
284 play.time.tv_usec = 0;
285 ret = TEMP_FAILURE_RETRY(write(mVibraFd, (const void*)&play, sizeof(play)));
286 if (ret == -1) {
287 ALOGE("write failed, errno = %d\n", -errno);
288 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
289 if (ret == -1)
290 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
291 goto errout;
292 }
293 } else if (mCurrAppId != INVALID_VALUE) {
294 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
295 if (ret == -1) {
296 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
297 goto errout;
298 }
299 mCurrAppId = INVALID_VALUE;
300 }
301 return 0;
302
303errout:
304 mCurrAppId = INVALID_VALUE;
305 return ret;
306}
307
308int InputFFDevice::on(int32_t timeoutMs) {
309 return play(INVALID_VALUE, timeoutMs, NULL);
310}
311
312int InputFFDevice::off() {
313 return play(INVALID_VALUE, 0, NULL);
314}
315
316int InputFFDevice::setAmplitude(uint8_t amplitude) {
317 int tmp, ret;
318 struct input_event ie;
319
320 /* For QMAA compliance, return OK even if vibrator device doesn't exist */
321 if (mVibraFd == INVALID_VALUE)
322 return 0;
323
324 tmp = amplitude * (STRONG_MAGNITUDE - LIGHT_MAGNITUDE) / 255;
325 tmp += LIGHT_MAGNITUDE;
326 ie.type = EV_FF;
327 ie.code = FF_GAIN;
328 ie.value = tmp;
329
330 ret = TEMP_FAILURE_RETRY(write(mVibraFd, &ie, sizeof(ie)));
331 if (ret == -1) {
332 ALOGE("write FF_GAIN failed, errno = %d", -errno);
333 return ret;
334 }
335
336 mCurrMagnitude = tmp;
337 return 0;
338}
339
340int InputFFDevice::playEffect(int effectId, EffectStrength es, long *playLengthMs) {
341 if (effectId > MAX_PATTERN_ID) {
342 ALOGE("effect id %d exceeds %d", effectId, MAX_PATTERN_ID);
343 return -1;
344 }
345
346 switch (es) {
347 case EffectStrength::LIGHT:
348 mCurrMagnitude = LIGHT_MAGNITUDE;
349 break;
350 case EffectStrength::MEDIUM:
351 mCurrMagnitude = MEDIUM_MAGNITUDE;
352 break;
353 case EffectStrength::STRONG:
354 mCurrMagnitude = STRONG_MAGNITUDE;
355 break;
356 default:
357 return -1;
358 }
359
360 return play(effectId, INVALID_VALUE, playLengthMs);
361}
362
363int InputFFDevice::playPrimitive(int primitiveId, float amplitude, long *playLengthMs) {
364 int8_t tmp;
365 int ret = 0;
366
367 if (primitiveId > MAX_PATTERN_ID) {
368 ALOGE("primitive id %d exceeds %d", primitiveId, MAX_PATTERN_ID);
369 return -1;
370 }
371
372 primitiveId |= PRIMITIVE_ID_MASK;
373 tmp = (uint8_t)(amplitude * 0xff);
374 mCurrMagnitude = tmp * (STRONG_MAGNITUDE - LIGHT_MAGNITUDE) / 255;
375 mCurrMagnitude += LIGHT_MAGNITUDE;
376
377 ret = play(primitiveId, INVALID_VALUE, playLengthMs);
378 if (ret != 0)
379 ALOGE("Failed to play primitive %d", primitiveId);
380
381 return ret;
382}
383
384LedVibratorDevice::LedVibratorDevice() {
385 char devicename[PATH_MAX];
386 int fd;
387
388 mDetected = false;
389
390 snprintf(devicename, sizeof(devicename), "%s/%s", LED_DEVICE, "activate");
391 fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR));
392 if (fd < 0) {
393 ALOGE("open %s failed, errno = %d", devicename, errno);
394 return;
395 }
396
397 mDetected = true;
398}
399
400int LedVibratorDevice::write_value(const char *file, const char *value) {
401 int fd;
402 int ret;
403
404 fd = TEMP_FAILURE_RETRY(open(file, O_WRONLY));
405 if (fd < 0) {
406 ALOGE("open %s failed, errno = %d", file, errno);
407 return -errno;
408 }
409
410 ret = TEMP_FAILURE_RETRY(write(fd, value, strlen(value) + 1));
411 if (ret == -1) {
412 ret = -errno;
413 } else if (ret != strlen(value) + 1) {
414 /* even though EAGAIN is an errno value that could be set
415 by write() in some cases, none of them apply here. So, this return
416 value can be clearly identified when debugging and suggests the
417 caller that it may try to call vibrator_on() again */
418 ret = -EAGAIN;
419 } else {
420 ret = 0;
421 }
422
423 errno = 0;
424 close(fd);
425
426 return ret;
427}
428
429int LedVibratorDevice::on(int32_t timeoutMs) {
430 char file[PATH_MAX];
431 char value[32];
432 int ret;
433
Michael Bestas82f93c22024-05-29 00:45:13 +0300434 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "state");
435 ret = write_value(file, "1");
Rohit Sekharc5949c32023-08-23 15:19:29 +0530436 if (ret < 0)
437 goto error;
438
Michael Bestas82f93c22024-05-29 00:45:13 +0300439 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "duration");
440 snprintf(value, sizeof(value), "%u\n", timeoutMs);
441 ret = write_value(file, value);
442 if (ret < 0)
443 goto error;
Rohit Sekharc5949c32023-08-23 15:19:29 +0530444
Michael Bestas82f93c22024-05-29 00:45:13 +0300445 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate");
446 ret = write_value(file, "1");
447 if (ret < 0)
448 goto error;
Rohit Sekharc5949c32023-08-23 15:19:29 +0530449
450 return 0;
451
452error:
453 ALOGE("Failed to turn on vibrator ret: %d\n", ret);
454 return ret;
455}
456
457int LedVibratorDevice::off()
458{
459 char file[PATH_MAX];
460 int ret;
461
462 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate");
463 ret = write_value(file, "0");
Rohit Sekharc5949c32023-08-23 15:19:29 +0530464 return ret;
465}
466
467Vibrator::Vibrator() {
468 struct epoll_event ev;
469
470 epollfd = INVALID_VALUE;
471 pipefd[0] = INVALID_VALUE;
472 pipefd[1] = INVALID_VALUE;
473 inComposition = false;
474
475 if (!ff.mSupportEffects)
476 return;
477
478 if (pipe(pipefd)) {
479 ALOGE("Failed to get pipefd error=%d", errno);
480 return;
481 }
482
483 epollfd = epoll_create1(0);
484 if (epollfd < 0) {
485 ALOGE("Failed to create epoll fd error=%d", errno);
486 goto pipefd_close;
487 }
488
489 ev.events = EPOLLIN;
490 ev.data.fd = pipefd[0];
491
492 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefd[0], &ev) == -1) {
493 ALOGE("Failed to add pipefd to epoll ctl error=%d", errno);
494 goto epollfd_close;
495 }
496
497 return;
498
499epollfd_close:
500 close(epollfd);
501 epollfd = INVALID_VALUE;
502pipefd_close:
503 close(pipefd[0]);
504 close(pipefd[1]);
505 pipefd[0] = INVALID_VALUE;
506 pipefd[1] = INVALID_VALUE;
507}
508
509Vibrator::~Vibrator() {
510 if (epollfd != INVALID_VALUE)
511 close(epollfd);
512 if (pipefd[0] != INVALID_VALUE)
513 close(pipefd[0]);
514 if (pipefd[1] != INVALID_VALUE)
515 close(pipefd[1]);
516}
517
518ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
519 *_aidl_return = IVibrator::CAP_ON_CALLBACK;
520
521 if (ledVib.mDetected) {
522 ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
523 return ndk::ScopedAStatus::ok();
524 }
525
526 if (ff.mSupportGain)
527 *_aidl_return |= IVibrator::CAP_AMPLITUDE_CONTROL;
528 if (ff.mSupportEffects) {
529 *_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK;
530 if (access("/sys/class/qcom-haptics/primitive_duration", F_OK) == 0) {
531 *_aidl_return |= IVibrator::CAP_COMPOSE_EFFECTS;
532 }
533 }
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 }
834 if (status == STOP_COMPOSE)
835 break;
836 }
837 }
838
839 ALOGD("Notifying composite complete, playlength= %ld", playLengthMs);
840 if (callback)
841 callback->onComplete();
842
843 vibrator->inComposition = false;
844}
845
846ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
847 const std::shared_ptr<IVibratorCallback>& callback) {
848 int status, nfd = 0, durationMs = 0, timeoutMs = 0;
849 struct epoll_event events;
850
851 if (composite.size() > ComposeSizeMax) {
852 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
853 }
854
855 std::vector<CompositePrimitive> supported;
856 getSupportedPrimitives(&supported);
857
858 for (auto& e : composite) {
859 if (e.delayMs > ComposeDelayMaxMs) {
860 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
861 }
862 if (e.scale < 0.0f || e.scale > 1.0f) {
863 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
864 }
865 if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
866 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
867 }
868
869 getPrimitiveDuration(e.primitive, &durationMs);
870 timeoutMs += durationMs + e.delayMs;
871 }
872
873 /*
874 * wait for 2 times of the play length timeout to make sure last play has been
875 * terminated successfully.
876 */
877 timeoutMs = (timeoutMs + 10) * 2;
878 /* Stop previous composition if it has not yet been completed */
879 if (inComposition) {
880 ALOGD("Last composePlayThread has not done yet, stop it manually");
881 off();
882
883 while (inComposition && timeoutMs--)
884 usleep(1000);
885
886 if (timeoutMs == 0) {
887 ALOGE("wait for last composePlayThread done timeout");
888 return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
889 }
890
891 /* Read the pipe again to remove any stale data before triggering a new play */
892 nfd = epoll_wait(epollfd, &events, 1, 0);
893 if (nfd == -1 && (errno != EINTR)) {
894 ALOGE("Failed to wait sleep playLengthMs, error=%d", errno);
895 return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
896 }
897 if (nfd > 0)
898 read(pipefd[0], &status, sizeof(int));
899 }
900
901 inComposition = true;
902 composeThread = std::thread(composePlayThread, this, composite, callback);
903 composeThread.detach();
904
905 ALOGD("trigger composition successfully");
906 return ndk::ScopedAStatus::ok();
907}
908
909ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return __unused) {
910 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
911}
912
913ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id __unused, Effect effect __unused,
914 EffectStrength strength __unused) {
915 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
916}
917
918ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id __unused) {
919 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
920}
921
922ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz __unused) {
923 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
924}
925
926ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor __unused) {
927 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
928}
929
930ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz __unused) {
931 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
932}
933
934ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz __unused) {
935 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
936}
937
938ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return __unused) {
939 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
940}
941
942ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs __unused) {
943 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
944}
945
946ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize __unused) {
947 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
948}
949
950ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported __unused) {
951 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
952}
953
954ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite __unused,
955 const std::shared_ptr<IVibratorCallback> &callback __unused) {
956 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
957}
958
959} // namespace vibrator
960} // namespace hardware
961} // namespace android
962} // namespace aidl
963