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