blob: 289fd7f2351f66d3b2aa103c22be2d315d461a75 [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
Rohit Sekhara0be89d2023-08-23 15:24:06 +053035#define LOG_TAG "vendor.qti.vibrator.FP5"
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
Rohit Sekhareca6ed52023-08-23 15:29:42 +053083static const char LED_DEVICE[] = "/sys/class/leds/vibrator_1";
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 Sekharfeda64e2023-08-23 15:28:25 +0530141 && strcmp(name, "aw-haptic-hv")
Rohit Sekharc5949c32023-08-23 15:19:29 +0530142 && strcmp(name, "aw8624_haptic")
143 && strcmp(name, "aw8695_haptic")
144 && strcmp(name, "aw8697_haptic")
145 && strcmp(name, "awinic_haptic")) {
146 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:
183 mSupportExternalControl = true;
184 break;
185 default:
186 mSupportExternalControl = false;
187 break;
188 }
189 break;
190 }
191
192 close(fd);
193 }
194
195 closedir(dp);
196}
197
198/** Play vibration
199 *
200 * @param effectId: ID of the predefined effect will be played. If effectId is valid
201 * (non-negative value), the timeoutMs value will be ignored, and the
202 * real playing length will be set in param@playLengtMs and returned
203 * to VibratorService. If effectId is invalid, value in param@timeoutMs
204 * will be used as the play length for playing a constant effect.
205 * @param timeoutMs: playing length, non-zero means playing, zero means stop playing.
206 * @param playLengthMs: the playing length in ms unit which will be returned to
207 * VibratorService if the request is playing a predefined effect.
208 * The custom_data in periodic is reused for returning the playLengthMs
209 * from kernel space to userspace if the pattern is defined in kernel
210 * driver. It's been defined with following format:
211 * <effect-ID, play-time-in-seconds, play-time-in-milliseconds>.
212 * The effect-ID is used for passing down the predefined effect to
213 * kernel driver, and the rest two parameters are used for returning
214 * back the real playing length from kernel driver.
215 */
216int InputFFDevice::play(int effectId, uint32_t timeoutMs, long *playLengthMs) {
217 struct ff_effect effect;
218 struct input_event play;
219 int16_t data[CUSTOM_DATA_LEN] = {0, 0, 0};
220 int ret;
221#ifdef USE_EFFECT_STREAM
222 const struct effect_stream *stream;
223#endif
224
225 /* For QMAA compliance, return OK even if vibrator device doesn't exist */
226 if (mVibraFd == INVALID_VALUE) {
227 if (playLengthMs != NULL)
228 *playLengthMs = 0;
229 return 0;
230 }
231
232 if (timeoutMs != 0) {
233 if (mCurrAppId != INVALID_VALUE) {
234 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
235 if (ret == -1) {
236 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
237 goto errout;
238 }
239 mCurrAppId = INVALID_VALUE;
240 }
241
242 memset(&effect, 0, sizeof(effect));
243 if (effectId != INVALID_VALUE) {
244 data[0] = effectId;
245 effect.type = FF_PERIODIC;
246 effect.u.periodic.waveform = FF_CUSTOM;
247 effect.u.periodic.magnitude = mCurrMagnitude;
248 effect.u.periodic.custom_data = data;
249 effect.u.periodic.custom_len = sizeof(int16_t) * CUSTOM_DATA_LEN;
250#ifdef USE_EFFECT_STREAM
251 stream = get_effect_stream(effectId);
252 if (stream != NULL) {
253 effect.u.periodic.custom_data = (int16_t *)stream;
254 effect.u.periodic.custom_len = sizeof(*stream);
255 }
256#endif
257 } else {
258 effect.type = FF_CONSTANT;
259 effect.u.constant.level = mCurrMagnitude;
260 effect.replay.length = timeoutMs;
261 }
262
263 effect.id = mCurrAppId;
264 effect.replay.delay = 0;
265
266 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCSFF, &effect));
267 if (ret == -1) {
268 ALOGE("ioctl EVIOCSFF failed, errno = %d", -errno);
269 goto errout;
270 }
271
272 mCurrAppId = effect.id;
273 if (effectId != INVALID_VALUE && playLengthMs != NULL) {
274 *playLengthMs = data[1] * 1000 + data[2];
275#ifdef USE_EFFECT_STREAM
276 if (stream != NULL && stream->play_rate_hz != 0)
277 *playLengthMs = ((stream->length * 1000) / stream->play_rate_hz) + 1;
278#endif
279 }
280
281 play.value = 1;
282 play.type = EV_FF;
283 play.code = mCurrAppId;
284 play.time.tv_sec = 0;
285 play.time.tv_usec = 0;
286 ret = TEMP_FAILURE_RETRY(write(mVibraFd, (const void*)&play, sizeof(play)));
287 if (ret == -1) {
288 ALOGE("write failed, errno = %d\n", -errno);
289 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
290 if (ret == -1)
291 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
292 goto errout;
293 }
294 } else if (mCurrAppId != INVALID_VALUE) {
295 ret = TEMP_FAILURE_RETRY(ioctl(mVibraFd, EVIOCRMFF, mCurrAppId));
296 if (ret == -1) {
297 ALOGE("ioctl EVIOCRMFF failed, errno = %d", -errno);
298 goto errout;
299 }
300 mCurrAppId = INVALID_VALUE;
301 }
302 return 0;
303
304errout:
305 mCurrAppId = INVALID_VALUE;
306 return ret;
307}
308
309int InputFFDevice::on(int32_t timeoutMs) {
310 return play(INVALID_VALUE, timeoutMs, NULL);
311}
312
313int InputFFDevice::off() {
314 return play(INVALID_VALUE, 0, NULL);
315}
316
317int InputFFDevice::setAmplitude(uint8_t amplitude) {
318 int tmp, ret;
319 struct input_event ie;
320
321 /* For QMAA compliance, return OK even if vibrator device doesn't exist */
322 if (mVibraFd == INVALID_VALUE)
323 return 0;
324
325 tmp = amplitude * (STRONG_MAGNITUDE - LIGHT_MAGNITUDE) / 255;
326 tmp += LIGHT_MAGNITUDE;
327 ie.type = EV_FF;
328 ie.code = FF_GAIN;
329 ie.value = tmp;
330
331 ret = TEMP_FAILURE_RETRY(write(mVibraFd, &ie, sizeof(ie)));
332 if (ret == -1) {
333 ALOGE("write FF_GAIN failed, errno = %d", -errno);
334 return ret;
335 }
336
337 mCurrMagnitude = tmp;
338 return 0;
339}
340
341int InputFFDevice::playEffect(int effectId, EffectStrength es, long *playLengthMs) {
342 if (effectId > MAX_PATTERN_ID) {
343 ALOGE("effect id %d exceeds %d", effectId, MAX_PATTERN_ID);
344 return -1;
345 }
346
347 switch (es) {
348 case EffectStrength::LIGHT:
349 mCurrMagnitude = LIGHT_MAGNITUDE;
350 break;
351 case EffectStrength::MEDIUM:
352 mCurrMagnitude = MEDIUM_MAGNITUDE;
353 break;
354 case EffectStrength::STRONG:
355 mCurrMagnitude = STRONG_MAGNITUDE;
356 break;
357 default:
358 return -1;
359 }
360
361 return play(effectId, INVALID_VALUE, playLengthMs);
362}
363
364int InputFFDevice::playPrimitive(int primitiveId, float amplitude, long *playLengthMs) {
365 int8_t tmp;
366 int ret = 0;
367
368 if (primitiveId > MAX_PATTERN_ID) {
369 ALOGE("primitive id %d exceeds %d", primitiveId, MAX_PATTERN_ID);
370 return -1;
371 }
372
373 primitiveId |= PRIMITIVE_ID_MASK;
374 tmp = (uint8_t)(amplitude * 0xff);
375 mCurrMagnitude = tmp * (STRONG_MAGNITUDE - LIGHT_MAGNITUDE) / 255;
376 mCurrMagnitude += LIGHT_MAGNITUDE;
377
378 ret = play(primitiveId, INVALID_VALUE, playLengthMs);
379 if (ret != 0)
380 ALOGE("Failed to play primitive %d", primitiveId);
381
382 return ret;
383}
384
385LedVibratorDevice::LedVibratorDevice() {
386 char devicename[PATH_MAX];
387 int fd;
388
389 mDetected = false;
390
391 snprintf(devicename, sizeof(devicename), "%s/%s", LED_DEVICE, "activate");
392 fd = TEMP_FAILURE_RETRY(open(devicename, O_RDWR));
393 if (fd < 0) {
394 ALOGE("open %s failed, errno = %d", devicename, errno);
395 return;
396 }
397
398 mDetected = true;
399}
400
401int LedVibratorDevice::write_value(const char *file, const char *value) {
402 int fd;
403 int ret;
404
405 fd = TEMP_FAILURE_RETRY(open(file, O_WRONLY));
406 if (fd < 0) {
407 ALOGE("open %s failed, errno = %d", file, errno);
408 return -errno;
409 }
410
411 ret = TEMP_FAILURE_RETRY(write(fd, value, strlen(value) + 1));
412 if (ret == -1) {
413 ret = -errno;
414 } else if (ret != strlen(value) + 1) {
415 /* even though EAGAIN is an errno value that could be set
416 by write() in some cases, none of them apply here. So, this return
417 value can be clearly identified when debugging and suggests the
418 caller that it may try to call vibrator_on() again */
419 ret = -EAGAIN;
420 } else {
421 ret = 0;
422 }
423
424 errno = 0;
425 close(fd);
426
427 return ret;
428}
429
430int LedVibratorDevice::on(int32_t timeoutMs) {
431 char file[PATH_MAX];
432 char value[32];
433 int ret;
434
435 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "state");
436 ret = write_value(file, "1");
437 if (ret < 0)
438 goto error;
439
440 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "duration");
441 snprintf(value, sizeof(value), "%u\n", timeoutMs);
442 ret = write_value(file, value);
443 if (ret < 0)
444 goto error;
445
446 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate");
447 ret = write_value(file, "1");
448 if (ret < 0)
449 goto error;
450
451 return 0;
452
453error:
454 ALOGE("Failed to turn on vibrator ret: %d\n", ret);
455 return ret;
456}
457
458int LedVibratorDevice::off()
459{
460 char file[PATH_MAX];
461 int ret;
462
463 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "activate");
464 ret = write_value(file, "0");
465 return ret;
466}
467
468Vibrator::Vibrator() {
469 struct epoll_event ev;
470
471 epollfd = INVALID_VALUE;
472 pipefd[0] = INVALID_VALUE;
473 pipefd[1] = INVALID_VALUE;
474 inComposition = false;
475
476 if (!ff.mSupportEffects)
477 return;
478
479 if (pipe(pipefd)) {
480 ALOGE("Failed to get pipefd error=%d", errno);
481 return;
482 }
483
484 epollfd = epoll_create1(0);
485 if (epollfd < 0) {
486 ALOGE("Failed to create epoll fd error=%d", errno);
487 goto pipefd_close;
488 }
489
490 ev.events = EPOLLIN;
491 ev.data.fd = pipefd[0];
492
493 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefd[0], &ev) == -1) {
494 ALOGE("Failed to add pipefd to epoll ctl error=%d", errno);
495 goto epollfd_close;
496 }
497
498 return;
499
500epollfd_close:
501 close(epollfd);
502 epollfd = INVALID_VALUE;
503pipefd_close:
504 close(pipefd[0]);
505 close(pipefd[1]);
506 pipefd[0] = INVALID_VALUE;
507 pipefd[1] = INVALID_VALUE;
508}
509
510Vibrator::~Vibrator() {
511 if (epollfd != INVALID_VALUE)
512 close(epollfd);
513 if (pipefd[0] != INVALID_VALUE)
514 close(pipefd[0]);
515 if (pipefd[1] != INVALID_VALUE)
516 close(pipefd[1]);
517}
518
519ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
520 *_aidl_return = IVibrator::CAP_ON_CALLBACK;
521
522 if (ledVib.mDetected) {
523 ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
524 return ndk::ScopedAStatus::ok();
525 }
526
527 if (ff.mSupportGain)
528 *_aidl_return |= IVibrator::CAP_AMPLITUDE_CONTROL;
529 if (ff.mSupportEffects) {
530 *_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK;
531 if (access("/sys/class/qcom-haptics/primitive_duration", F_OK) == 0) {
532 *_aidl_return |= IVibrator::CAP_COMPOSE_EFFECTS;
533 }
534 }
535 if (ff.mSupportExternalControl)
536 *_aidl_return |= IVibrator::CAP_EXTERNAL_CONTROL;
537
538 ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
539 return ndk::ScopedAStatus::ok();
540}
541
542ndk::ScopedAStatus Vibrator::off() {
543 int ret;
544 int composeEven = STOP_COMPOSE;
545
546 ALOGD("QTI Vibrator off");
547 if (ledVib.mDetected)
548 ret = ledVib.off();
549 else
550 ret = ff.off();
551 if (ret != 0)
552 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
553
554 if (inComposition) {
555 ret = write(pipefd[1], &composeEven, sizeof(composeEven));
556 if (ret < 0) {
557 ALOGE("Failed to send STOP_COMPOSE event");
558 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
559 }
560 }
561
562 return ndk::ScopedAStatus::ok();
563}
564
565ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
566 const std::shared_ptr<IVibratorCallback>& callback) {
567 int ret;
568
569 ALOGD("Vibrator on for timeoutMs: %d", timeoutMs);
570 if (ledVib.mDetected)
571 ret = ledVib.on(timeoutMs);
572 else
573 ret = ff.on(timeoutMs);
574
575 if (ret != 0)
576 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
577
578 if (callback != nullptr) {
579 std::thread([=] {
580 ALOGD("Starting on on another thread");
581 usleep(timeoutMs * 1000);
582 ALOGD("Notifying on complete");
583 if (!callback->onComplete().isOk()) {
584 ALOGE("Failed to call onComplete");
585 }
586 }).detach();
587 }
588
589 return ndk::ScopedAStatus::ok();
590}
591
592ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength es, const std::shared_ptr<IVibratorCallback>& callback, int32_t* _aidl_return) {
593 long playLengthMs;
594 int ret;
595
596 if (ledVib.mDetected)
597 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
598
599 ALOGD("Vibrator perform effect %d", effect);
600 if (Offload.mEnabled == 1) {
601 if ((effect < Effect::CLICK) ||
602 ((effect > Effect::HEAVY_CLICK) && (effect < Effect::RINGTONE_12)) ||
603 (effect > Effect::RINGTONE_15))
604 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
605 }
606 else {
607 if (effect < Effect::CLICK || effect > Effect::HEAVY_CLICK)
608 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
609 }
610
611 if (es != EffectStrength::LIGHT && es != EffectStrength::MEDIUM && es != EffectStrength::STRONG)
612 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
613
614 ret = ff.playEffect((static_cast<int>(effect)), es, &playLengthMs);
615 if (ret != 0)
616 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
617
618 if (callback != nullptr) {
619 std::thread([=] {
620 ALOGD("Starting perform on another thread");
621 usleep(playLengthMs * 1000);
622 ALOGD("Notifying perform complete");
623 callback->onComplete();
624 }).detach();
625 }
626
627 *_aidl_return = playLengthMs;
628 return ndk::ScopedAStatus::ok();
629}
630
631ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
632 if (ledVib.mDetected)
633 return ndk::ScopedAStatus::ok();
634
635 if (Offload.mEnabled == 1)
636 *_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
637 Effect::POP, Effect::HEAVY_CLICK, Effect::RINGTONE_12,
638 Effect::RINGTONE_13, Effect::RINGTONE_14, Effect::RINGTONE_15};
639 else
640 *_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
641 Effect::POP, Effect::HEAVY_CLICK};
642
643 return ndk::ScopedAStatus::ok();
644}
645
646ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
647 uint8_t tmp;
648 int ret;
649
650 if (ledVib.mDetected)
651 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
652
653 if (!ff.mSupportGain)
654 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
655
656 ALOGD("Vibrator set amplitude: %f", amplitude);
657
658 if (amplitude <= 0.0f || amplitude > 1.0f)
659 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
660
661 if (ff.mInExternalControl)
662 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
663
664 tmp = (uint8_t)(amplitude * 0xff);
665 ret = ff.setAmplitude(tmp);
666 if (ret != 0)
667 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
668
669 return ndk::ScopedAStatus::ok();
670}
671
672ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
673 if (ledVib.mDetected)
674 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
675
676 ALOGD("Vibrator set external control: %d", enabled);
677 if (!ff.mSupportExternalControl)
678 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
679
680 ff.mInExternalControl = enabled;
681 return ndk::ScopedAStatus::ok();
682}
683
684ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) {
685 *maxDelayMs = ComposeDelayMaxMs;
686 return ndk::ScopedAStatus::ok();
687}
688
689ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) {
690 *maxSize = ComposeSizeMax;
691 return ndk::ScopedAStatus::ok();
692}
693
694ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
695 *supported = {
696 CompositePrimitive::NOOP, CompositePrimitive::CLICK,
697 CompositePrimitive::THUD, CompositePrimitive::SPIN,
698 CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
699 CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
700 CompositePrimitive::LOW_TICK,
701 };
702 return ndk::ScopedAStatus::ok();
703}
704
705static int getPrimitiveDurationFromSysfs(uint32_t primitive_id, int32_t* durationMs) {
706 int count = 0;
707 int fd = 0;
708 int ret = 0;
709 /* the Max primitive id is 32767, so define the size of primitive_buf to 6 */
710 char primitive_buf[6];
711 /* the max primitive_duration is the max value of int32, so define the size to 10 */
712 char primitive_duration[10];
713 char primitive_duration_sysfs[50];
714
715 ret = snprintf(primitive_duration_sysfs, sizeof(primitive_duration_sysfs), "%s%s", HAPTICS_SYSFS, "/primitive_duration");
716 if (ret < 0) {
717 ALOGE("Failed to get primitive duration node, ret = %d\n", ret);
718 return ret;
719 }
720
721 count = snprintf(primitive_buf, sizeof(primitive_buf), "%d%c", primitive_id, '\n');
722 if (count < 0) {
723 ALOGE("Failed to get primitive id, count = %d\n", count);
724 ret = count;
725 return ret;
726 }
727
728 fd = TEMP_FAILURE_RETRY(open(primitive_duration_sysfs, O_RDWR));
729 if (fd < 0) {
730 ALOGE("open %s failed, errno = %d", primitive_duration_sysfs, errno);
731 ret = fd;
732 return ret;
733 }
734
735 ret = TEMP_FAILURE_RETRY(write(fd, primitive_buf, count));
736 if (ret < 0) {
737 ALOGE("write primitive %d failed, errno = %d", primitive_id, errno);
738 goto close_fd;
739 }
740
741 ret = TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET));
742 if (ret < 0) {
743 ALOGE("lseek fd to file head failed, errno = %d", errno);
744 goto close_fd;
745 }
746
747 ret = TEMP_FAILURE_RETRY(read(fd, primitive_duration, sizeof(primitive_duration)));
748 if (ret < 0) {
749 ALOGE("read primitive %d failed, errno = %d", primitive_id, errno);
750 goto close_fd;
751 }
752
753 *durationMs = atoi(primitive_duration);
754 *durationMs /= 1000;
755
756close_fd:
757 ret = TEMP_FAILURE_RETRY(close(fd));
758 if (ret < 0) {
759 ALOGE("close primitive duration device failed, errno = %d", errno);
760 return ret;
761 }
762
763 return ret;
764}
765
766ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
767 int32_t* durationMs) {
768 uint32_t primitive_id = static_cast<uint32_t>(primitive);
769 int ret = 0;
770
771#ifdef USE_EFFECT_STREAM
772 primitive_id |= PRIMITIVE_ID_MASK ;
773 const struct effect_stream *stream;
774 stream = get_effect_stream(primitive_id);
775 if (stream != NULL && stream->play_rate_hz != 0)
776 *durationMs = ((stream->length * 1000) / stream->play_rate_hz) + 1;
777
778 ALOGD("primitive-%d duration is %dms", primitive, *durationMs);
779 return ndk::ScopedAStatus::ok();
780#endif
781
782 ret = getPrimitiveDurationFromSysfs(primitive_id, durationMs);
783 if (ret < 0)
784 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
785
786 ALOGD("primitive-%d duration is %dms", primitive, *durationMs);
787
788 return ndk::ScopedAStatus::ok();
789}
790
791void Vibrator::composePlayThread(Vibrator *vibrator,
792 const std::vector<CompositeEffect>& composite,
793 const std::shared_ptr<IVibratorCallback>& callback){
794 struct epoll_event events;
795 long playLengthMs = 0;
796 int nfd = 0;
797 int status = 0;
798 int ret = 0;
799
800 ALOGD("start a new thread for composeEffect");
801 for (auto& e : composite) {
802 if (e.delayMs) {
803 nfd = epoll_wait(vibrator->epollfd, &events, 1, e.delayMs);
804 if ((nfd == -1) && (errno != EINTR)) {
805 ALOGE("Failed to wait delayMs, error=%d", errno);
806 break;
807 }
808
809 if (nfd > 0) {
810 /* It's supposed that STOP_COMPOSE command is received so quit the composition */
811 ret = read(vibrator->pipefd[0], &status, sizeof(int));
812 if (ret < 0) {
813 ALOGE("Failed to read stop status from pipe(delayMs), status = %d", status);
814 break;
815 }
816 if (status == STOP_COMPOSE)
817 break;
818 }
819 }
820
821 vibrator->ff.playPrimitive((static_cast<int>(e.primitive)), e.scale, &playLengthMs);
822 nfd = epoll_wait(vibrator->epollfd, &events, 1, playLengthMs);
823 if (nfd == -1 && (errno != EINTR)) {
824 ALOGE("Failed to wait sleep playLengthMs, error=%d", errno);
825 break;
826 }
827
828 if (nfd > 0) {
829 /* It's supposed that STOP_COMPOSE command is received so quit the composition */
830 ret = read(vibrator->pipefd[0], &status, sizeof(int));
831 if (ret < 0) {
832 ALOGE("Failed to read stop status from pipe(playLengthMs), status = %d", status);
833 break;
834 }
835 if (status == STOP_COMPOSE)
836 break;
837 }
838 }
839
840 ALOGD("Notifying composite complete, playlength= %ld", playLengthMs);
841 if (callback)
842 callback->onComplete();
843
844 vibrator->inComposition = false;
845}
846
847ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
848 const std::shared_ptr<IVibratorCallback>& callback) {
849 int status, nfd = 0, durationMs = 0, timeoutMs = 0;
850 struct epoll_event events;
851
852 if (composite.size() > ComposeSizeMax) {
853 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
854 }
855
856 std::vector<CompositePrimitive> supported;
857 getSupportedPrimitives(&supported);
858
859 for (auto& e : composite) {
860 if (e.delayMs > ComposeDelayMaxMs) {
861 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
862 }
863 if (e.scale < 0.0f || e.scale > 1.0f) {
864 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
865 }
866 if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
867 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
868 }
869
870 getPrimitiveDuration(e.primitive, &durationMs);
871 timeoutMs += durationMs + e.delayMs;
872 }
873
874 /*
875 * wait for 2 times of the play length timeout to make sure last play has been
876 * terminated successfully.
877 */
878 timeoutMs = (timeoutMs + 10) * 2;
879 /* Stop previous composition if it has not yet been completed */
880 if (inComposition) {
881 ALOGD("Last composePlayThread has not done yet, stop it manually");
882 off();
883
884 while (inComposition && timeoutMs--)
885 usleep(1000);
886
887 if (timeoutMs == 0) {
888 ALOGE("wait for last composePlayThread done timeout");
889 return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
890 }
891
892 /* Read the pipe again to remove any stale data before triggering a new play */
893 nfd = epoll_wait(epollfd, &events, 1, 0);
894 if (nfd == -1 && (errno != EINTR)) {
895 ALOGE("Failed to wait sleep playLengthMs, error=%d", errno);
896 return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
897 }
898 if (nfd > 0)
899 read(pipefd[0], &status, sizeof(int));
900 }
901
902 inComposition = true;
903 composeThread = std::thread(composePlayThread, this, composite, callback);
904 composeThread.detach();
905
906 ALOGD("trigger composition successfully");
907 return ndk::ScopedAStatus::ok();
908}
909
910ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return __unused) {
911 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
912}
913
914ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id __unused, Effect effect __unused,
915 EffectStrength strength __unused) {
916 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
917}
918
919ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id __unused) {
920 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
921}
922
923ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz __unused) {
924 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
925}
926
927ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor __unused) {
928 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
929}
930
931ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz __unused) {
932 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
933}
934
935ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz __unused) {
936 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
937}
938
939ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return __unused) {
940 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
941}
942
943ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs __unused) {
944 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
945}
946
947ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize __unused) {
948 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
949}
950
951ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported __unused) {
952 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
953}
954
955ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite __unused,
956 const std::shared_ptr<IVibratorCallback> &callback __unused) {
957 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
958}
959
960} // namespace vibrator
961} // namespace hardware
962} // namespace android
963} // namespace aidl
964