blob: 06d26e0293e887a94b4d09cbc037400d297ad56f [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 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")
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
Michael Bestas886f1142024-05-29 00:50:22 +0300390 snprintf(devicename, sizeof(devicename), "%s/%s", LED_DEVICE, "state");
Rohit Sekharc5949c32023-08-23 15:19:29 +0530391 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");
Michael Bestas82f93c22024-05-29 00:45:13 +0300435 snprintf(value, sizeof(value), "%u\n", timeoutMs);
436 ret = write_value(file, value);
437 if (ret < 0)
438 goto error;
Rohit Sekharc5949c32023-08-23 15:19:29 +0530439
Rohit Sekharc5949c32023-08-23 15:19:29 +0530440 return 0;
441
442error:
443 ALOGE("Failed to turn on vibrator ret: %d\n", ret);
444 return ret;
445}
446
447int LedVibratorDevice::off()
448{
449 char file[PATH_MAX];
450 int ret;
451
Michael Bestas886f1142024-05-29 00:50:22 +0300452 snprintf(file, sizeof(file), "%s/%s", LED_DEVICE, "state");
Rohit Sekharc5949c32023-08-23 15:19:29 +0530453 ret = write_value(file, "0");
Rohit Sekharc5949c32023-08-23 15:19:29 +0530454 return ret;
455}
456
457Vibrator::Vibrator() {
458 struct epoll_event ev;
459
460 epollfd = INVALID_VALUE;
461 pipefd[0] = INVALID_VALUE;
462 pipefd[1] = INVALID_VALUE;
463 inComposition = false;
464
465 if (!ff.mSupportEffects)
466 return;
467
468 if (pipe(pipefd)) {
469 ALOGE("Failed to get pipefd error=%d", errno);
470 return;
471 }
472
473 epollfd = epoll_create1(0);
474 if (epollfd < 0) {
475 ALOGE("Failed to create epoll fd error=%d", errno);
476 goto pipefd_close;
477 }
478
479 ev.events = EPOLLIN;
480 ev.data.fd = pipefd[0];
481
482 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefd[0], &ev) == -1) {
483 ALOGE("Failed to add pipefd to epoll ctl error=%d", errno);
484 goto epollfd_close;
485 }
486
487 return;
488
489epollfd_close:
490 close(epollfd);
491 epollfd = INVALID_VALUE;
492pipefd_close:
493 close(pipefd[0]);
494 close(pipefd[1]);
495 pipefd[0] = INVALID_VALUE;
496 pipefd[1] = INVALID_VALUE;
497}
498
499Vibrator::~Vibrator() {
500 if (epollfd != INVALID_VALUE)
501 close(epollfd);
502 if (pipefd[0] != INVALID_VALUE)
503 close(pipefd[0]);
504 if (pipefd[1] != INVALID_VALUE)
505 close(pipefd[1]);
506}
507
508ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) {
509 *_aidl_return = IVibrator::CAP_ON_CALLBACK;
510
511 if (ledVib.mDetected) {
512 ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
513 return ndk::ScopedAStatus::ok();
514 }
515
516 if (ff.mSupportGain)
517 *_aidl_return |= IVibrator::CAP_AMPLITUDE_CONTROL;
518 if (ff.mSupportEffects) {
519 *_aidl_return |= IVibrator::CAP_PERFORM_CALLBACK;
520 if (access("/sys/class/qcom-haptics/primitive_duration", F_OK) == 0) {
521 *_aidl_return |= IVibrator::CAP_COMPOSE_EFFECTS;
522 }
523 }
524 if (ff.mSupportExternalControl)
525 *_aidl_return |= IVibrator::CAP_EXTERNAL_CONTROL;
526
527 ALOGD("QTI Vibrator reporting capabilities: %d", *_aidl_return);
528 return ndk::ScopedAStatus::ok();
529}
530
531ndk::ScopedAStatus Vibrator::off() {
532 int ret;
533 int composeEven = STOP_COMPOSE;
534
535 ALOGD("QTI Vibrator off");
536 if (ledVib.mDetected)
537 ret = ledVib.off();
538 else
539 ret = ff.off();
540 if (ret != 0)
541 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
542
543 if (inComposition) {
544 ret = write(pipefd[1], &composeEven, sizeof(composeEven));
545 if (ret < 0) {
546 ALOGE("Failed to send STOP_COMPOSE event");
547 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
548 }
549 }
550
551 return ndk::ScopedAStatus::ok();
552}
553
554ndk::ScopedAStatus Vibrator::on(int32_t timeoutMs,
555 const std::shared_ptr<IVibratorCallback>& callback) {
556 int ret;
557
558 ALOGD("Vibrator on for timeoutMs: %d", timeoutMs);
559 if (ledVib.mDetected)
560 ret = ledVib.on(timeoutMs);
561 else
562 ret = ff.on(timeoutMs);
563
564 if (ret != 0)
565 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
566
567 if (callback != nullptr) {
568 std::thread([=] {
569 ALOGD("Starting on on another thread");
570 usleep(timeoutMs * 1000);
571 ALOGD("Notifying on complete");
572 if (!callback->onComplete().isOk()) {
573 ALOGE("Failed to call onComplete");
574 }
575 }).detach();
576 }
577
578 return ndk::ScopedAStatus::ok();
579}
580
581ndk::ScopedAStatus Vibrator::perform(Effect effect, EffectStrength es, const std::shared_ptr<IVibratorCallback>& callback, int32_t* _aidl_return) {
582 long playLengthMs;
583 int ret;
584
585 if (ledVib.mDetected)
586 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
587
588 ALOGD("Vibrator perform effect %d", effect);
589 if (Offload.mEnabled == 1) {
590 if ((effect < Effect::CLICK) ||
591 ((effect > Effect::HEAVY_CLICK) && (effect < Effect::RINGTONE_12)) ||
592 (effect > Effect::RINGTONE_15))
593 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
594 }
595 else {
596 if (effect < Effect::CLICK || effect > Effect::HEAVY_CLICK)
597 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
598 }
599
600 if (es != EffectStrength::LIGHT && es != EffectStrength::MEDIUM && es != EffectStrength::STRONG)
601 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
602
603 ret = ff.playEffect((static_cast<int>(effect)), es, &playLengthMs);
604 if (ret != 0)
605 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
606
607 if (callback != nullptr) {
608 std::thread([=] {
609 ALOGD("Starting perform on another thread");
610 usleep(playLengthMs * 1000);
611 ALOGD("Notifying perform complete");
612 callback->onComplete();
613 }).detach();
614 }
615
616 *_aidl_return = playLengthMs;
617 return ndk::ScopedAStatus::ok();
618}
619
620ndk::ScopedAStatus Vibrator::getSupportedEffects(std::vector<Effect>* _aidl_return) {
621 if (ledVib.mDetected)
622 return ndk::ScopedAStatus::ok();
623
624 if (Offload.mEnabled == 1)
625 *_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
626 Effect::POP, Effect::HEAVY_CLICK, Effect::RINGTONE_12,
627 Effect::RINGTONE_13, Effect::RINGTONE_14, Effect::RINGTONE_15};
628 else
629 *_aidl_return = {Effect::CLICK, Effect::DOUBLE_CLICK, Effect::TICK, Effect::THUD,
630 Effect::POP, Effect::HEAVY_CLICK};
631
632 return ndk::ScopedAStatus::ok();
633}
634
635ndk::ScopedAStatus Vibrator::setAmplitude(float amplitude) {
636 uint8_t tmp;
637 int ret;
638
639 if (ledVib.mDetected)
640 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
641
642 if (!ff.mSupportGain)
643 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
644
645 ALOGD("Vibrator set amplitude: %f", amplitude);
646
647 if (amplitude <= 0.0f || amplitude > 1.0f)
648 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
649
650 if (ff.mInExternalControl)
651 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
652
653 tmp = (uint8_t)(amplitude * 0xff);
654 ret = ff.setAmplitude(tmp);
655 if (ret != 0)
656 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_SERVICE_SPECIFIC));
657
658 return ndk::ScopedAStatus::ok();
659}
660
661ndk::ScopedAStatus Vibrator::setExternalControl(bool enabled) {
662 if (ledVib.mDetected)
663 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
664
665 ALOGD("Vibrator set external control: %d", enabled);
666 if (!ff.mSupportExternalControl)
667 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
668
669 ff.mInExternalControl = enabled;
670 return ndk::ScopedAStatus::ok();
671}
672
673ndk::ScopedAStatus Vibrator::getCompositionDelayMax(int32_t* maxDelayMs) {
674 *maxDelayMs = ComposeDelayMaxMs;
675 return ndk::ScopedAStatus::ok();
676}
677
678ndk::ScopedAStatus Vibrator::getCompositionSizeMax(int32_t* maxSize) {
679 *maxSize = ComposeSizeMax;
680 return ndk::ScopedAStatus::ok();
681}
682
683ndk::ScopedAStatus Vibrator::getSupportedPrimitives(std::vector<CompositePrimitive>* supported) {
684 *supported = {
685 CompositePrimitive::NOOP, CompositePrimitive::CLICK,
686 CompositePrimitive::THUD, CompositePrimitive::SPIN,
687 CompositePrimitive::QUICK_RISE, CompositePrimitive::SLOW_RISE,
688 CompositePrimitive::QUICK_FALL, CompositePrimitive::LIGHT_TICK,
689 CompositePrimitive::LOW_TICK,
690 };
691 return ndk::ScopedAStatus::ok();
692}
693
694static int getPrimitiveDurationFromSysfs(uint32_t primitive_id, int32_t* durationMs) {
695 int count = 0;
696 int fd = 0;
697 int ret = 0;
698 /* the Max primitive id is 32767, so define the size of primitive_buf to 6 */
699 char primitive_buf[6];
700 /* the max primitive_duration is the max value of int32, so define the size to 10 */
701 char primitive_duration[10];
702 char primitive_duration_sysfs[50];
703
704 ret = snprintf(primitive_duration_sysfs, sizeof(primitive_duration_sysfs), "%s%s", HAPTICS_SYSFS, "/primitive_duration");
705 if (ret < 0) {
706 ALOGE("Failed to get primitive duration node, ret = %d\n", ret);
707 return ret;
708 }
709
710 count = snprintf(primitive_buf, sizeof(primitive_buf), "%d%c", primitive_id, '\n');
711 if (count < 0) {
712 ALOGE("Failed to get primitive id, count = %d\n", count);
713 ret = count;
714 return ret;
715 }
716
717 fd = TEMP_FAILURE_RETRY(open(primitive_duration_sysfs, O_RDWR));
718 if (fd < 0) {
719 ALOGE("open %s failed, errno = %d", primitive_duration_sysfs, errno);
720 ret = fd;
721 return ret;
722 }
723
724 ret = TEMP_FAILURE_RETRY(write(fd, primitive_buf, count));
725 if (ret < 0) {
726 ALOGE("write primitive %d failed, errno = %d", primitive_id, errno);
727 goto close_fd;
728 }
729
730 ret = TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET));
731 if (ret < 0) {
732 ALOGE("lseek fd to file head failed, errno = %d", errno);
733 goto close_fd;
734 }
735
736 ret = TEMP_FAILURE_RETRY(read(fd, primitive_duration, sizeof(primitive_duration)));
737 if (ret < 0) {
738 ALOGE("read primitive %d failed, errno = %d", primitive_id, errno);
739 goto close_fd;
740 }
741
742 *durationMs = atoi(primitive_duration);
743 *durationMs /= 1000;
744
745close_fd:
746 ret = TEMP_FAILURE_RETRY(close(fd));
747 if (ret < 0) {
748 ALOGE("close primitive duration device failed, errno = %d", errno);
749 return ret;
750 }
751
752 return ret;
753}
754
755ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
756 int32_t* durationMs) {
757 uint32_t primitive_id = static_cast<uint32_t>(primitive);
758 int ret = 0;
759
760#ifdef USE_EFFECT_STREAM
761 primitive_id |= PRIMITIVE_ID_MASK ;
762 const struct effect_stream *stream;
763 stream = get_effect_stream(primitive_id);
764 if (stream != NULL && stream->play_rate_hz != 0)
765 *durationMs = ((stream->length * 1000) / stream->play_rate_hz) + 1;
766
767 ALOGD("primitive-%d duration is %dms", primitive, *durationMs);
768 return ndk::ScopedAStatus::ok();
769#endif
770
771 ret = getPrimitiveDurationFromSysfs(primitive_id, durationMs);
772 if (ret < 0)
773 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
774
775 ALOGD("primitive-%d duration is %dms", primitive, *durationMs);
776
777 return ndk::ScopedAStatus::ok();
778}
779
780void Vibrator::composePlayThread(Vibrator *vibrator,
781 const std::vector<CompositeEffect>& composite,
782 const std::shared_ptr<IVibratorCallback>& callback){
783 struct epoll_event events;
784 long playLengthMs = 0;
785 int nfd = 0;
786 int status = 0;
787 int ret = 0;
788
789 ALOGD("start a new thread for composeEffect");
790 for (auto& e : composite) {
791 if (e.delayMs) {
792 nfd = epoll_wait(vibrator->epollfd, &events, 1, e.delayMs);
793 if ((nfd == -1) && (errno != EINTR)) {
794 ALOGE("Failed to wait delayMs, error=%d", errno);
795 break;
796 }
797
798 if (nfd > 0) {
799 /* It's supposed that STOP_COMPOSE command is received so quit the composition */
800 ret = read(vibrator->pipefd[0], &status, sizeof(int));
801 if (ret < 0) {
802 ALOGE("Failed to read stop status from pipe(delayMs), status = %d", status);
803 break;
804 }
805 if (status == STOP_COMPOSE)
806 break;
807 }
808 }
809
810 vibrator->ff.playPrimitive((static_cast<int>(e.primitive)), e.scale, &playLengthMs);
811 nfd = epoll_wait(vibrator->epollfd, &events, 1, playLengthMs);
812 if (nfd == -1 && (errno != EINTR)) {
813 ALOGE("Failed to wait sleep playLengthMs, error=%d", errno);
814 break;
815 }
816
817 if (nfd > 0) {
818 /* It's supposed that STOP_COMPOSE command is received so quit the composition */
819 ret = read(vibrator->pipefd[0], &status, sizeof(int));
820 if (ret < 0) {
821 ALOGE("Failed to read stop status from pipe(playLengthMs), status = %d", status);
822 break;
823 }
824 if (status == STOP_COMPOSE)
825 break;
826 }
827 }
828
829 ALOGD("Notifying composite complete, playlength= %ld", playLengthMs);
830 if (callback)
831 callback->onComplete();
832
833 vibrator->inComposition = false;
834}
835
836ndk::ScopedAStatus Vibrator::compose(const std::vector<CompositeEffect>& composite,
837 const std::shared_ptr<IVibratorCallback>& callback) {
838 int status, nfd = 0, durationMs = 0, timeoutMs = 0;
839 struct epoll_event events;
840
841 if (composite.size() > ComposeSizeMax) {
842 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
843 }
844
845 std::vector<CompositePrimitive> supported;
846 getSupportedPrimitives(&supported);
847
848 for (auto& e : composite) {
849 if (e.delayMs > ComposeDelayMaxMs) {
850 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
851 }
852 if (e.scale < 0.0f || e.scale > 1.0f) {
853 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
854 }
855 if (std::find(supported.begin(), supported.end(), e.primitive) == supported.end()) {
856 return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
857 }
858
859 getPrimitiveDuration(e.primitive, &durationMs);
860 timeoutMs += durationMs + e.delayMs;
861 }
862
863 /*
864 * wait for 2 times of the play length timeout to make sure last play has been
865 * terminated successfully.
866 */
867 timeoutMs = (timeoutMs + 10) * 2;
868 /* Stop previous composition if it has not yet been completed */
869 if (inComposition) {
870 ALOGD("Last composePlayThread has not done yet, stop it manually");
871 off();
872
873 while (inComposition && timeoutMs--)
874 usleep(1000);
875
876 if (timeoutMs == 0) {
877 ALOGE("wait for last composePlayThread done timeout");
878 return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
879 }
880
881 /* Read the pipe again to remove any stale data before triggering a new play */
882 nfd = epoll_wait(epollfd, &events, 1, 0);
883 if (nfd == -1 && (errno != EINTR)) {
884 ALOGE("Failed to wait sleep playLengthMs, error=%d", errno);
885 return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
886 }
887 if (nfd > 0)
888 read(pipefd[0], &status, sizeof(int));
889 }
890
891 inComposition = true;
892 composeThread = std::thread(composePlayThread, this, composite, callback);
893 composeThread.detach();
894
895 ALOGD("trigger composition successfully");
896 return ndk::ScopedAStatus::ok();
897}
898
899ndk::ScopedAStatus Vibrator::getSupportedAlwaysOnEffects(std::vector<Effect>* _aidl_return __unused) {
900 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
901}
902
903ndk::ScopedAStatus Vibrator::alwaysOnEnable(int32_t id __unused, Effect effect __unused,
904 EffectStrength strength __unused) {
905 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
906}
907
908ndk::ScopedAStatus Vibrator::alwaysOnDisable(int32_t id __unused) {
909 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
910}
911
912ndk::ScopedAStatus Vibrator::getResonantFrequency(float *resonantFreqHz __unused) {
913 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
914}
915
916ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor __unused) {
917 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
918}
919
920ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz __unused) {
921 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
922}
923
924ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz __unused) {
925 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
926}
927
928ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return __unused) {
929 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
930}
931
932ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs __unused) {
933 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
934}
935
936ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize __unused) {
937 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
938}
939
940ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported __unused) {
941 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
942}
943
944ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite __unused,
945 const std::shared_ptr<IVibratorCallback> &callback __unused) {
946 return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
947}
948
949} // namespace vibrator
950} // namespace hardware
951} // namespace android
952} // namespace aidl
953