From 9a6ce676cc7de421411592d386f50e3f562edec7 Mon Sep 17 00:00:00 2001 From: "pr.jung" Date: Fri, 10 Mar 2017 14:26:01 +0900 Subject: [PATCH] Create initial commit Change-Id: I55eb8f18f19bcfd6467ed40558a4d6b914085965 Signed-off-by: pr.jung --- CMakeLists.txt | 78 +++ LICENSE | 204 ++++++++ packaging/feedbackd.manifest | 5 + packaging/feedbackd.spec | 168 ++++++ scripts/feedbackd.conf | 19 + src/README.standard | 13 + src/core/common.c | 104 ++++ src/core/common.h | 66 +++ src/core/config-parser.c | 127 +++++ src/core/config-parser.h | 43 ++ src/core/dbus.c | 144 ++++++ src/core/dbus.h | 85 +++ src/core/device-idler.c | 102 ++++ src/core/device-idler.h | 29 ++ src/core/edbus-handler.c | 837 ++++++++++++++++++++++++++++++ src/core/edbus-handler.h | 51 ++ src/core/list.h | 60 +++ src/core/log.c | 39 ++ src/core/log.h | 48 ++ src/core/main.c | 65 +++ src/haptic/circle.c | 323 ++++++++++++ src/haptic/emulator.c | 196 +++++++ src/haptic/external.c | 99 ++++ src/haptic/haptic-mobile.conf | 24 + src/haptic/haptic-module.h | 91 ++++ src/haptic/haptic-plugin-intf.h | 42 ++ src/haptic/haptic-wearable.conf | 15 + src/haptic/haptic.c | 974 +++++++++++++++++++++++++++++++++++ src/haptic/haptic.h | 56 ++ src/haptic/standard-mix.c | 1085 +++++++++++++++++++++++++++++++++++++++ src/haptic/standard-vibcore.c | 276 ++++++++++ src/haptic/standard-vibcore.h | 31 ++ src/haptic/standard.c | 692 +++++++++++++++++++++++++ systemd/feedbackd.service | 18 + 34 files changed, 6209 insertions(+) create mode 100755 CMakeLists.txt create mode 100644 LICENSE create mode 100644 packaging/feedbackd.manifest create mode 100644 packaging/feedbackd.spec create mode 100644 scripts/feedbackd.conf create mode 100644 src/README.standard create mode 100644 src/core/common.c create mode 100644 src/core/common.h create mode 100644 src/core/config-parser.c create mode 100644 src/core/config-parser.h create mode 100644 src/core/dbus.c create mode 100644 src/core/dbus.h create mode 100644 src/core/device-idler.c create mode 100644 src/core/device-idler.h create mode 100644 src/core/edbus-handler.c create mode 100644 src/core/edbus-handler.h create mode 100644 src/core/list.h create mode 100644 src/core/log.c create mode 100644 src/core/log.h create mode 100644 src/core/main.c create mode 100644 src/haptic/circle.c create mode 100644 src/haptic/emulator.c create mode 100644 src/haptic/external.c create mode 100644 src/haptic/haptic-mobile.conf create mode 100644 src/haptic/haptic-module.h create mode 100644 src/haptic/haptic-plugin-intf.h create mode 100644 src/haptic/haptic-wearable.conf create mode 100644 src/haptic/haptic.c create mode 100644 src/haptic/haptic.h create mode 100644 src/haptic/standard-mix.c create mode 100644 src/haptic/standard-vibcore.c create mode 100644 src/haptic/standard-vibcore.h create mode 100644 src/haptic/standard.c create mode 100644 systemd/feedbackd.service diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..9c80b35 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,78 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(feedbackd C) + + +######################################################## +# Deviced CMakeLists.txt +######################################################## +SET(VERSION 0.1.0) + +IF("${PROFILE}" STREQUAL "wearable") + IF("${ARCH}" STREQUAL "arm") + SET(CIRCLE "on") + ENDIF() +ENDIF() + +SET(SRCS + src/core/main.c + src/core/edbus-handler.c + src/core/common.c + src/core/config-parser.c + src/core/device-idler.c + src/core/log.c + src/core/dbus.c + src/haptic/haptic.c + src/haptic/external.c + src/haptic/emulator.c +) +IF(CIRCLE STREQUAL on) + SET(SRCS ${SRCS} src/haptic/circle.c src/haptic/standard-vibcore.c) +ELSE() + IF(STANDARD_MIX STREQUAL on) + SET(SRCS ${SRCS} src/haptic/standard-mix.c) + ELSE() + SET(SRCS ${SRCS} src/haptic/standard.c src/haptic/standard-vibcore.c) + ENDIF() +ENDIF() + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src) + +SET(PKG_MODULES + ecore + edbus + dbus-1 + vconf + dlog + capi-base-common + gio-2.0 + capi-system-info +) + +INCLUDE(FindPkgConfig) +pkg_check_modules(pkgs2 REQUIRED ${PKG_MODULES}) + +FOREACH(flag ${pkgs2_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -Werror") +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -fno-omit-frame-pointer -finstrument-functions") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -lrt -fPIE") +SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie") + +ADD_DEFINITIONS("-DDEBUG") + +ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs2_LDFLAGS} "-ldl" "-lm") +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin) + +INSTALL(FILES ${CMAKE_SOURCE_DIR}/scripts/feedbackd.conf DESTINATION /etc/dbus-1/system.d) +INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/systemd/ DESTINATION lib/systemd/system + FILES_MATCHING + PATTERN "feedbackd.service") + +IF(PROFILE STREQUAL wearable) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/haptic/haptic-wearable.conf DESTINATION /etc/feedbackd) +ELSE() +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/haptic/haptic-mobile.conf DESTINATION /etc/feedbackd) +ENDIF() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8f17f50 --- /dev/null +++ b/LICENSE @@ -0,0 +1,204 @@ +Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/packaging/feedbackd.manifest b/packaging/feedbackd.manifest new file mode 100644 index 0000000..97e8c31 --- /dev/null +++ b/packaging/feedbackd.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/feedbackd.spec b/packaging/feedbackd.spec new file mode 100644 index 0000000..32b5c45 --- /dev/null +++ b/packaging/feedbackd.spec @@ -0,0 +1,168 @@ + +#These options are DEACTIVATED by default. +%bcond_with wayland +%bcond_with emulator + +%define standard_mix off + +Name: feedbackd +Summary: Feedbackd +Version: 1.0.0 +Release: 1 +Group: System/Management +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +Source1: feedbackd.manifest + +BuildRequires: cmake +BuildRequires: pkgconfig(ecore) +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(edbus) +BuildRequires: pkgconfig(capi-base-common) +BuildRequires: pkgconfig(dbus-1) +BuildRequires: pkgconfig(gio-2.0) +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(capi-system-info) + +Requires(post): /usr/bin/vconftool + +Requires: %{name}-compat = %{version}-%{release} +Recommends: %{name}-profile_mobile = %{version}-%{release} + +%description +feedback daemon + +%package feedbackd +Summary: feedback daemon +Group: main + +%description feedbackd +feedback daemon. + +# if profile = mobile or common or undefined +%package profile_mobile +Summary: Deviced binaries targeting mobile profile +Provides: %{name}-compat = %{version}-%{release} +Conflicts: %{name}-profile_wearable +%description profile_mobile +Deviced binaries targeting mobile profile. Required by main deviced package +# if profile = wearable or common or undefined +%package profile_wearable +Summary: Deviced binaries targeting wearable profile +Provides: %{name}-compat = %{version}-%{release} +Conflicts: %{name}-profile_mobile +%description profile_wearable +Deviced binaries targeting wearable profile. Required by main deviced package + +%prep +%setup -q + +%if 0%{?tizen_build_devel_mode} == 1 +%define engineer_mode on +%else +%define engineer_mode off +%endif + +# Build per profile +# if profile = mobile or common or undefined +mkdir -p build_mobile +pushd build_mobile +%cmake .. \ + -DCMAKE_INSTALL_PREFIX=%{_prefix} \ + -DPROFILE=mobile \ + -DSTANDARD_MIX=%{standard_mix} \ + #eol +popd + +# if profile = wearable or common or undefined +mkdir -p build_wearable +pushd build_wearable +%cmake .. \ + -DCMAKE_INSTALL_PREFIX=%{_prefix} \ + -DPROFILE=wearable \ + -DSTANDARD_MIX=%{standard_mix} \ + #eol +popd + +%build +cp %{SOURCE1} . + +# Build per profile +# if profile = mobile or common or undefined +pushd build_mobile +make %{?jobs:-j%jobs} +popd + +# if profile = wearable or common or undefined +pushd build_wearable +make %{?jobs:-j%jobs} +popd + +%install +rm -rf %{buildroot} +# Build per profile +# if profile = mobile or common or undefined +pushd build_mobile +%make_install +mv %{buildroot}%{_bindir}/feedbackd %{buildroot}%{_bindir}/feedbackd.mobile +popd + +# if profile = wearable or common or undefined +pushd build_wearable +%make_install +mv %{buildroot}%{_bindir}/feedbackd %{buildroot}%{_bindir}/feedbackd.wearable +popd + +%install_service multi-user.target.wants feedbackd.service + +%post profile_mobile +mv %{_bindir}/feedbackd.mobile %{_bindir}/feedbackd +mv %{_sysconfdir}/feedbackd/haptic-mobile.conf %{_sysconfdir}/feedbackd/haptic.conf + +systemctl daemon-reload +if [ "$1" == "1" ]; then + systemctl restart feedbackd.service +fi + +%preun profile_mobile +mv %{_bindir}/feedbackd %{_bindir}/feedbackd.mobile + +if [ "$1" == "0" ]; then + systemctl stop feedbackd.service +fi + +%post profile_wearable +mv %{_bindir}/feedbackd.wearable %{_bindir}/feedbackd +mv %{_sysconfdir}/feedbackd/haptic-wearable.conf %{_sysconfdir}/feedbackd/haptic.conf + +systemctl daemon-reload +if [ "$1" == "1" ]; then + systemctl restart feedbackd.service +fi + +%preun profile_wearable +mv %{_bindir}/feedbackd %{_bindir}/feedbackd.wearable + +if [ "$1" == "0" ]; then + systemctl stop feedbackd.service +fi + +%files -n feedbackd +%manifest %{name}.manifest +%license LICENSE +%config %{_sysconfdir}/dbus-1/system.d/feedbackd.conf +%{_unitdir}/feedbackd.service +%{_unitdir}/multi-user.target.wants/feedbackd.service + +%files profile_mobile +%license LICENSE +%manifest %{name}.manifest +%config %{_sysconfdir}/feedbackd/haptic-mobile.conf +%{_bindir}/feedbackd.mobile + +%files profile_wearable +%license LICENSE +%manifest %{name}.manifest +%config %{_sysconfdir}/feedbackd/haptic-wearable.conf +%{_bindir}/feedbackd.wearable diff --git a/scripts/feedbackd.conf b/scripts/feedbackd.conf new file mode 100644 index 0000000..5b70ebb --- /dev/null +++ b/scripts/feedbackd.conf @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/src/README.standard b/src/README.standard new file mode 100644 index 0000000..52cf000 --- /dev/null +++ b/src/README.standard @@ -0,0 +1,13 @@ +Standard Force Feedback Information + +1. Max effect id is 16. + It depends on force feedback driver. + +2. Do not increase effect id in case of repeated requests before previous effect finishs. + Standard Force Feedback does not reset the effect id on ff_set_effect function. + If the effect id is reseted as -1, + effect id will be increased until removing the effect from device by ioctl(EVIOCRMFF). + In this case Standard Force Feedback can not remove the previous effect id. + +3. Do not support to mix each effect. + If the request is occurred on the same time, it plays effect as per the last request. diff --git a/src/core/common.c b/src/core/common.c new file mode 100644 index 0000000..4bd4be8 --- /dev/null +++ b/src/core/common.c @@ -0,0 +1,104 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "common.h" + + +int get_cmdline_name(pid_t pid, char *cmdline, size_t cmdline_size) +{ + int fd, ret; + char buf[PATH_MAX + 1]; + char *filename; + + snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); + fd = open(buf, O_RDONLY); + if (fd < 0) { + errno = ESRCH; + return -1; + } + + ret = read(fd, buf, PATH_MAX); + close(fd); + if (ret < 0) + return -1; + + buf[PATH_MAX] = '\0'; + + filename = strrchr(buf, '/'); + if (filename == NULL) + filename = buf; + else + filename = filename + 1; + + if (cmdline_size < strlen(filename) + 1) { + errno = EOVERFLOW; + return -1; + } + + strncpy(cmdline, filename, cmdline_size - 1); + cmdline[cmdline_size - 1] = '\0'; + return 0; +} + +#define MODEL_NAME "http://tizen.org/system/model_name" +#define MODEL_EMULATOR "Emulator" + +bool is_emulator(void) +{ + int ret; + char *model_name = NULL; + static bool emul = false; + static int set = 0; + + if (set) + return emul; + + ret = system_info_get_platform_string(MODEL_NAME, &model_name); + if (ret < 0) { + _E("Cannot get model name(%d)", ret); + return emul; + } + + if (!strncmp(MODEL_EMULATOR, model_name, strlen(model_name) + 1)) + emul = true; + + set = 1; + free(model_name); + + return emul; +} diff --git a/src/core/common.h b/src/core/common.h new file mode 100644 index 0000000..9978bf4 --- /dev/null +++ b/src/core/common.h @@ -0,0 +1,66 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __FEEDBACKD_COMMON_H__ +#define __FEEDBACKD_COMMON_H__ + +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0])) +#define BITS_PER_LONG (sizeof(long) * 8) +#define OFFSET(x) ((x) & (BITS_PER_LONG - 1)) +#define BIT(x) (1UL << OFFSET(x)) +#define LONG(x) ((x) / BITS_PER_LONG) +#define test_bit(bit, array) ((array[LONG(bit)] >> OFFSET(bit)) & 1) + +#ifndef __CONSTRUCTOR__ +#define __CONSTRUCTOR__ __attribute__ ((constructor)) +#endif + +#ifndef __DESTRUCTOR__ +#define __DESTRUCTOR__ __attribute__ ((destructor)) +#endif + +#ifndef __WEAK__ +#define __WEAK__ __attribute__ ((weak)) +#endif + +#define NANO_SECOND_MULTIPLIER 1000000 /* 1ms = 1,000,000 nsec */ + +#ifndef safe_free +#define safe_free(x) safe_free_memory((void**)&(x)) +#endif + +static inline void safe_free_memory(void** mem) +{ + if (mem && *mem) { + free(*mem); + *mem = NULL; + } +} + +int get_cmdline_name(pid_t pid, char *cmdline, size_t cmdline_size); +bool is_emulator(void); + +#endif /* __FEEDBACKD_COMMON_H__ */ + diff --git a/src/core/config-parser.c b/src/core/config-parser.c new file mode 100644 index 0000000..ca88496 --- /dev/null +++ b/src/core/config-parser.c @@ -0,0 +1,127 @@ +/* + * feedbackd + * + * Copyright (c) 2013 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "log.h" +#include "config-parser.h" + +#define MAX_LINE 512 +#define MAX_SECTION 64 +#define WHITESPACE " \t" +#define NEWLINE "\n\r" +#define COMMENT '#' + +static inline char *trim_str(char *s) +{ + char *t; + /* left trim */ + s += strspn(s, WHITESPACE); + + /* right trim */ + for (t = strchr(s, 0); t > s; t--) + if (!strchr(WHITESPACE, t[-1])) + break; + *t = 0; + return s; +} + +int config_parse(const char *file_name, int cb(struct parse_result *result, + void *user_data), void *user_data) +{ + FILE *f = NULL; + struct parse_result result; + /* use stack for parsing */ + char line[MAX_LINE]; + char section[MAX_SECTION]; + char *start, *end, *name, *value; + int lineno = 0, ret = 0; + + if (!file_name || !cb) { + ret = -EINVAL; + goto error; + } + + /* open conf file */ + f = fopen(file_name, "r"); + if (!f) { + _E("Failed to open file %s", file_name); + ret = -EIO; + goto error; + } + + /* parsing line by line */ + while (fgets(line, MAX_LINE, f) != NULL) { + lineno++; + + start = line; + start[strcspn(start, NEWLINE)] = '\0'; + start = trim_str(start); + + if (*start == COMMENT) { + continue; + } else if (*start == '[') { + /* parse section */ + end = strchr(start, ']'); + if (!end || *end != ']') { + ret = -EBADMSG; + goto error; + } + + *end = '\0'; + strncpy(section, start + 1, sizeof(section)); + section[MAX_SECTION-1] = '\0'; + } else if (*start) { + /* parse name & value */ + end = strchr(start, '='); + if (!end || *end != '=') { + ret = -EBADMSG; + goto error; + } + *end = '\0'; + name = trim_str(start); + value = trim_str(end + 1); + end = strchr(value, COMMENT); + if (end && *end == COMMENT) { + *end = '\0'; + value = trim_str(value); + } + + result.section = section; + result.name = name; + result.value = value; + /* callback with parse result */ + ret = cb(&result, user_data); + if (ret < 0) { + ret = -EBADMSG; + goto error; + } + } + } + _D("Success to load %s", file_name); + fclose(f); + return 0; + +error: + if (f) + fclose(f); + _E("Failed to read %s:%d!", file_name, lineno); + return ret; +} + diff --git a/src/core/config-parser.h b/src/core/config-parser.h new file mode 100644 index 0000000..71fa379 --- /dev/null +++ b/src/core/config-parser.h @@ -0,0 +1,43 @@ +/* + * feedbackd + * + * Copyright (c) 2013 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __FEEDBACKD_CONFIG_PARSER_H__ +#define __FEEDBACKD_CONFIG_PARSER_H__ + +#define MATCH(a, b) (!strncmp(a, b, strlen(a))) +#define SET_CONF(a, b) (a = (b > 0.0 ? b : a)) + +struct parse_result { + char *section; + char *name; + char *value; +}; + +/** + * @brief Parse config file and call callback\n + * @param[in] file_name conf file. + * @param[in] cb cb is called when conf file is parsed line by line. + * @param[in] user_data user data is passed to cb. + * @return 0 on success, negative if failed + */ +int config_parse(const char *file_name, int cb(struct parse_result *result, + void *user_data), void *user_data); + +#endif + diff --git a/src/core/dbus.c b/src/core/dbus.c new file mode 100644 index 0000000..c8845f0 --- /dev/null +++ b/src/core/dbus.c @@ -0,0 +1,144 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include + +#include "core/log.h" +#include "core/common.h" +#include "dbus.h" + +/* -1 is a default timeout value, it's converted to 25*1000 internally. */ +#define DBUS_REPLY_TIMEOUT (-1) + +int append_variant(DBusMessageIter *iter, const char *sig, char *param[]) +{ + char *ch; + int i; + int int_type; + dbus_bool_t bool_type; + uint64_t int64_type; + DBusMessageIter arr; + struct dbus_byte *byte; + + if (!sig || !param) + return 0; + + for (ch = (char *)sig, i = 0; *ch != '\0'; ++i, ++ch) { + switch (*ch) { + case 'b': + bool_type = (atoi(param[i])) ? TRUE : FALSE; + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &bool_type); + break; + case 'i': + int_type = atoi(param[i]); + dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &int_type); + break; + case 'u': + int_type = strtoul(param[i], NULL, 10); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &int_type); + break; + case 't': + int64_type = atoll(param[i]); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &int64_type); + break; + case 's': + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, ¶m[i]); + break; + case 'a': + ++ch; + switch (*ch) { + case 'y': + ++i; + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &arr); + byte = (struct dbus_byte *)param[i]; + dbus_message_iter_append_fixed_array(&arr, DBUS_TYPE_BYTE, &(byte->data), byte->size); + dbus_message_iter_close_container(iter, &arr); + break; + default: + break; + } + break; + default: + return -EINVAL; + } + } + + return 0; +} + +DBusMessage *dbus_method_sync_with_reply(const char *dest, const char *path, + const char *interface, const char *method, + const char *sig, char *param[]) +{ + DBusConnection *conn; + DBusMessage *msg; + DBusMessageIter iter; + DBusMessage *reply; + DBusError err; + int r; + + conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); + if (!conn) { + _E("dbus_bus_get error"); + return NULL; + } + + msg = dbus_message_new_method_call(dest, path, interface, method); + if (!msg) { + _E("dbus_message_new_method_call(%s:%s-%s)", + path, interface, method); + return NULL; + } + + dbus_message_iter_init_append(msg, &iter); + r = append_variant(&iter, sig, param); + if (r < 0) { + _E("append_variant error(%d) %s %s:%s-%s", + r, dest, path, interface, method); + dbus_message_unref(msg); + return NULL; + } + + dbus_error_init(&err); + + reply = dbus_connection_send_with_reply_and_block(conn, msg, DBUS_REPLY_TIMEOUT, &err); + if (!reply) { + _E("dbus_connection_send error(No reply) %s %s:%s-%s", + dest, path, interface, method); + } + + if (dbus_error_is_set(&err)) { + _E("dbus_connection_send error(%s:%s) %s %s:%s-%s", + err.name, err.message, dest, path, interface, method); + dbus_error_free(&err); + reply = NULL; + } + + dbus_message_unref(msg); + return reply; +} + +static void __CONSTRUCTOR__ dbus_init(void) +{ + dbus_threads_init_default(); +} diff --git a/src/core/dbus.h b/src/core/dbus.h new file mode 100644 index 0000000..4fb1436 --- /dev/null +++ b/src/core/dbus.h @@ -0,0 +1,85 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __DBUS_H__ +#define __DBUS_H__ + +#include +#include + +/* + * Template + * +#define XXX_BUS_NAME "org.tizen.system.XXX" +#define XXX_OBJECT_PATH "/Org/Tizen/System/XXX" +#define XXX_INTERFACE_NAME XXX_BUS_NAME +#define XXX_PATH_YYY XXX_OBJECT_PATH"/YYY" +#define XXX_INTERFACE_YYY XXX_INTERFACE_NAME".YYY" +#define XXX_SIGNAL_ZZZ "ZZZ" +#define XXX_METHOD_ZZZ "ZZZ" + */ + +/* + * DBus daemon + */ +#define DBUS_BUS_NAME "org.freedesktop.DBus" +#define DBUS_OBJECT_PATH "/org/freedesktop/DBus" +#define DBUS_INTERFACE_NAME DBUS_BUS_NAME + +/* + * Device daemon + */ +#define DEVICED_BUS_NAME "org.tizen.system.deviced" +#define DEVICED_OBJECT_PATH "/Org/Tizen/System/DeviceD" +#define DEVICED_INTERFACE_NAME DEVICED_BUS_NAME +/* Core service: get/set device status operations about device */ +#define DEVICED_PATH_CORE DEVICED_OBJECT_PATH"/Core" +#define DEVICED_INTERFACE_CORE DEVICED_INTERFACE_NAME".core" +/* Poweroff service: get power off status operations about Poweroff */ +#define DEVICED_PATH_POWEROFF DEVICED_OBJECT_PATH"/PowerOff" +#define DEVICED_INTERFACE_POWEROFF DEVICED_INTERFACE_NAME".PowerOff" +/* Key service: operations about key */ +#define DEVICED_PATH_KEY DEVICED_OBJECT_PATH"/Key" +#define DEVICED_INTERFACE_KEY DEVICED_INTERFACE_NAME".Key" + +/* + * Vibrator daemon + */ +#define VIBRATOR_BUS_NAME "org.tizen.system.vibrator" +#define VIBRATOR_OBJECT_PATH "/Org/Tizen/System/Vibrator" +#define VIBRATOR_INTERFACE_NAME VIBRATOR_BUS_NAME +/* Core service: get/set device status operations about device */ +#define VIBRATOR_PATH_CORE VIBRATOR_OBJECT_PATH"/Core" +#define VIBRATOR_INTERFACE_CORE VIBRATOR_INTERFACE_NAME".core" + +#define VIBRATOR_PATH_HAPTIC VIBRATOR_OBJECT_PATH"/Haptic" +#define VIBRATOR_INTERFACE_HAPTIC VIBRATOR_INTERFACE_NAME".haptic" + +struct dbus_byte { + const unsigned char *data; + int size; +}; + +int append_variant(DBusMessageIter *iter, const char *sig, char *param[]); + +DBusMessage *dbus_method_sync_with_reply(const char *dest, const char *path, + const char *interface, const char *method, + const char *sig, char *param[]); + +#endif diff --git a/src/core/device-idler.c b/src/core/device-idler.c new file mode 100644 index 0000000..2d58b63 --- /dev/null +++ b/src/core/device-idler.c @@ -0,0 +1,102 @@ +/* + * feedbackd + * + * Copyright (c) 2015 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +#include "log.h" + +struct device_request { + void (*func)(void *data); + void *data; +}; + +static GQueue req_queue = G_QUEUE_INIT; +static Ecore_Idler *idler; + +static int free_request(struct device_request *req) +{ + if (!req) + return -EINVAL; + + free(req); + return 0; +} + +static Eina_Bool idler_cb(void *data) +{ + struct device_request *req; + + req = g_queue_pop_head(&req_queue); + if (req) { + if (req->func) + req->func(req->data); + free_request(req); + } + + if (g_queue_is_empty(&req_queue)) { + idler = NULL; + return ECORE_CALLBACK_CANCEL; + } + + return ECORE_CALLBACK_RENEW; +} + +static void process_next_request_in_idle(void) +{ + if (g_queue_is_empty(&req_queue)) + return; + + if (idler) + return; + + idler = ecore_idler_add(idler_cb, NULL); + /** + * if idler is null, + * it means whole system might be an abnormal state. + * so it just prints out error log. + */ + if (!idler) + _E("fail to add request to idler"); +} + +int add_idle_request(void (*func)(void *data), void *data) +{ + struct device_request *req; + + if (!func) { + _E("invalid argumet : func(NULL)"); + return -EINVAL; + } + + req = calloc(1, sizeof(struct device_request)); + if (!req) { + _E("fail to allocate request : %d", errno); + return -errno; + } + + req->func = func; + req->data = data; + + g_queue_push_tail(&req_queue, req); + process_next_request_in_idle(); + + return 0; +} diff --git a/src/core/device-idler.h b/src/core/device-idler.h new file mode 100644 index 0000000..264cee3 --- /dev/null +++ b/src/core/device-idler.h @@ -0,0 +1,29 @@ +/* + * feedbackd + * + * Copyright (c) 2015 - 2017 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __FEEDBACKD_DEVICE_IDLER_H__ +#define __FEEDBACKD_DEVICE_IDLER_H__ + +/* + * To allow for callbacks to be called when the daemon is idle state. + */ + +int add_idle_request(void (*func)(void *data), void *data); + +#endif /* __FEEDBACKD_DEVICE_IDLER_H__ */ diff --git a/src/core/edbus-handler.c b/src/core/edbus-handler.c new file mode 100644 index 0000000..b1f62d3 --- /dev/null +++ b/src/core/edbus-handler.c @@ -0,0 +1,837 @@ +/* + * feedbackd + * + * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + +#include "core/log.h" +#include "core/common.h" +#include "core/device-idler.h" +#include "core/list.h" +#include "edbus-handler.h" + +#define EDBUS_INIT_RETRY_COUNT 5 +#define NAME_OWNER_CHANGED "NameOwnerChanged" +#define NAME_OWNER_MATCH "type='signal',sender='org.freedesktop.DBus'," \ + "path='/org/freedesktop/DBus',interface='org.freedesktop.DBus'," \ + "member='NameOwnerChanged',arg0='%s'" + +/* -1 is a default timeout value, it's converted to 25*1000 internally. */ +#define DBUS_REPLY_TIMEOUT (-1) +#define RETRY_MAX 5 + +struct edbus_list { + char *signal_name; + E_DBus_Signal_Handler *handler; +}; + +static struct edbus_object { + char *path; + char *interface; + E_DBus_Object *obj; + E_DBus_Interface *iface; +} edbus_objects[] = { + { VIBRATOR_PATH_CORE, VIBRATOR_INTERFACE_CORE, NULL, NULL }, + /* Add new object & interface here*/ +}; + +struct watch_func_info { + bool deleted; + void (*func)(const char *sender, void *data); + void *data; +}; + +struct watch_info { + bool deleted; + char *sender; + dd_list *func_list; +}; + +static dd_list *edbus_object_list; +static dd_list *edbus_owner_list; +static dd_list *edbus_handler_list; +static dd_list *edbus_watch_list; +static int edbus_init_val; +static DBusConnection *conn; +static E_DBus_Connection *edbus_conn; +static DBusPendingCall *edbus_request_name; + +static DBusHandlerResult message_filter(DBusConnection *connection, + DBusMessage *message, void *data); +static int register_edbus_interface(struct edbus_object *object) +{ + if (!object) { + _E("object is invalid value!"); + return -1; + } + + object->obj = e_dbus_object_add(edbus_conn, object->path, NULL); + if (!object->obj) { + _E("fail to add edbus obj"); + return -1; + } + + object->iface = e_dbus_interface_new(object->interface); + if (!object->iface) { + _E("fail to add edbus interface"); + return -1; + } + + e_dbus_object_interface_attach(object->obj, object->iface); + + return 0; +} + +E_DBus_Interface *get_edbus_interface(const char *path) +{ + struct edbus_object *obj; + dd_list *elem; + int i; + + for (i = 0; i < ARRAY_SIZE(edbus_objects); i++) + if (!strcmp(path, edbus_objects[i].path)) + return edbus_objects[i].iface; + + /* find matched obj */ + DD_LIST_FOREACH(edbus_object_list, elem, obj) { + if (strncmp(obj->path, path, strlen(obj->path)) == 0) + return obj->iface; + } + + return NULL; +} + +static void unregister_edbus_signal_handle(void) +{ + dd_list *tmp, *next; + struct edbus_list *entry; + + DD_LIST_FOREACH_SAFE(edbus_handler_list, tmp, next, entry) { + if (!entry->handler) + continue; + e_dbus_signal_handler_del(edbus_conn, entry->handler); + DD_LIST_REMOVE(edbus_handler_list, entry); + free(entry->signal_name); + free(entry); + } +} + +int register_edbus_signal_handler(const char *path, const char *interface, + const char *name, E_DBus_Signal_Cb cb) +{ + dd_list *tmp; + struct edbus_list *entry; + E_DBus_Signal_Handler *handler; + + DD_LIST_FOREACH(edbus_handler_list, tmp, entry) { + if (strncmp(entry->signal_name, name, strlen(name)) == 0) + return -EEXIST; + } + + handler = e_dbus_signal_handler_add(edbus_conn, NULL, path, + interface, name, cb, NULL); + + if (!handler) { + _E("fail to add edbus handler"); + return -ENOMEM; + } + + entry = malloc(sizeof(struct edbus_list)); + + if (!entry) { + e_dbus_signal_handler_del(edbus_conn, handler); + _E("Malloc failed"); + return -ENOMEM; + } + + entry->signal_name = strndup(name, strlen(name)); + + if (!entry->signal_name) { + _E("Malloc failed"); + e_dbus_signal_handler_del(edbus_conn, handler); + free(entry); + return -ENOMEM; + } + + entry->handler = handler; + DD_LIST_PREPEND(edbus_handler_list, entry); + if (!edbus_handler_list) { + _E("dd_list_prepend failed"); + e_dbus_signal_handler_del(edbus_conn, handler); + free(entry->signal_name); + free(entry); + return -ENOMEM; + } + return 0; +} + +int unregister_edbus_signal_handler(const char *path, const char *interface, + const char *name) +{ + dd_list *tmp, *next; + struct edbus_list *entry; + + DD_LIST_FOREACH_SAFE(edbus_handler_list, tmp, next, entry) { + if (strncmp(entry->signal_name, name, strlen(name) + 1) == 0) { + e_dbus_signal_handler_del(edbus_conn, entry->handler); + DD_LIST_REMOVE(edbus_handler_list, entry); + free(entry->signal_name); + free(entry); + return 0; + } + } + + return -1; +} + +int broadcast_edbus_signal(const char *path, const char *interface, + const char *name, const char *sig, char *param[]) +{ + DBusMessage *msg; + DBusMessageIter iter; + int r; + + msg = dbus_message_new_signal(path, interface, name); + if (!msg) { + _E("fail to allocate new %s.%s signal", interface, name); + return -EPERM; + } + + dbus_message_iter_init_append(msg, &iter); + r = append_variant(&iter, sig, param); + if (r < 0) { + _E("append_variant error(%d)", r); + return -EPERM; + } + + r = dbus_connection_send(conn, msg, NULL); + dbus_message_unref(msg); + + if (r != TRUE) { + _E("dbus_connection_send error(%s:%s-%s)", + path, interface, name); + return -ECOMM; + } + + return 0; +} + +static void print_watch_item(void) +{ + struct watch_info *watch; + struct watch_func_info *finfo; + dd_list *n; + dd_list *e; + + DD_LIST_FOREACH(edbus_watch_list, n, watch) { + _D("watch sender : %s, deleted : %d", + watch->sender, watch->deleted); + DD_LIST_FOREACH(watch->func_list, e, finfo) + _D("\tfunc : %p, deleted : %d", + finfo->func, finfo->deleted); + } +} + +static bool get_valid_watch_item(void) +{ + struct watch_info *watch; + dd_list *elem; + + DD_LIST_FOREACH(edbus_watch_list, elem, watch) { + if (!watch->deleted) + return true; + } + + return false; +} + +static void watch_idler_cb(void *data) +{ + struct watch_info *watch; + struct watch_func_info *finfo; + dd_list *n; + dd_list *next; + dd_list *elem; + dd_list *enext; + char match[256]; + + DD_LIST_FOREACH_SAFE(edbus_watch_list, n, next, watch) { + if (!watch->deleted) + continue; + + /* remove dbus match */ + snprintf(match, sizeof(match), NAME_OWNER_MATCH, watch->sender); + dbus_bus_remove_match(conn, match, NULL); + + _I("%s is not watched by dbus!", watch->sender); + + /* remove watch func list */ + DD_LIST_FOREACH_SAFE(watch->func_list, elem, enext, finfo) + free(finfo); + + /* remove watch item */ + DD_LIST_FREE_LIST(watch->func_list); + DD_LIST_REMOVE_LIST(edbus_watch_list, n); + free(watch->sender); + free(watch); + } + + /* if the last request, remove message filter */ + if (!get_valid_watch_item()) + dbus_connection_remove_filter(conn, message_filter, NULL); +} + +static DBusHandlerResult message_filter(DBusConnection *connection, + DBusMessage *message, void *data) +{ + int ret; + const char *iface, *member; + const char *sender; + struct watch_info *watch; + struct watch_func_info *finfo; + dd_list *n; + int len; + + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + iface = dbus_message_get_interface(message); + member = dbus_message_get_member(message); + + if (strncmp(iface, DBUS_INTERFACE_DBUS, + sizeof(DBUS_INTERFACE_DBUS))) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (strncmp(member, NAME_OWNER_CHANGED, + sizeof(NAME_OWNER_CHANGED))) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + ret = dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &sender, + DBUS_TYPE_INVALID); + if (!ret) { + _E("no message"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + len = strlen(sender) + 1; + DD_LIST_FOREACH(edbus_watch_list, n, watch) { + if (!watch->deleted && + !strncmp(watch->sender, sender, len)) + break; + } + + if (!watch) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + DD_LIST_FOREACH(watch->func_list, n, finfo) { + if (!finfo->deleted && + finfo->func) + finfo->func(watch->sender, finfo->data); + } + + /* no interest in this item anymore */ + watch->deleted = true; + + print_watch_item(); + add_idle_request(watch_idler_cb, NULL); + return DBUS_HANDLER_RESULT_HANDLED; +} + +static struct watch_info *get_matched_watch_item(const char *sender) +{ + int len; + dd_list *n; + struct watch_info *watch; + + if (!sender) + return NULL; + + len = strlen(sender) + 1; + /* check the sender&type is already registered */ + DD_LIST_FOREACH(edbus_watch_list, n, watch) { + if (!watch->deleted && + !strncmp(watch->sender, sender, len)) + return watch; + } + + return NULL; +} + +static struct watch_info *add_watch_item(const char *sender) +{ + DBusError err; + struct watch_info *watch; + char match[256]; + int ret; + + if (!sender) + return NULL; + + watch = calloc(1, sizeof(struct watch_info)); + if (!watch) + return NULL; + + watch->sender = strdup(sender); + if (!watch->sender) + goto out; + + dbus_error_init(&err); + /* add name owner changed match string */ + snprintf(match, sizeof(match), NAME_OWNER_MATCH, watch->sender); + dbus_bus_add_match(conn, match, &err); + + if (dbus_error_is_set(&err)) { + _E("fail to add match for %s [%s:%s]", + sender, err.name, err.message); + dbus_error_free(&err); + goto out; + } + + /* if the first request, add message filter */ + if (!get_valid_watch_item()) { + ret = dbus_connection_add_filter(conn, + message_filter, NULL, NULL); + if (!ret) { + _E("fail to add message filter!"); + dbus_bus_remove_match(conn, match, NULL); + goto out; + } + _I("success to add message filter!"); + } + + /* Add watch to watch list */ + DD_LIST_APPEND(edbus_watch_list, watch); + _I("%s is watched by dbus!", sender); + return watch; + +out: + if (watch) { + free(watch->sender); + free(watch); + } + + return NULL; +} + +int register_edbus_watch(const char *sender, + void (*func)(const char *sender, void *data), void *data) +{ + struct watch_info *watch; + struct watch_func_info *finfo; + dd_list *elem; + bool isnew = false; + + if (!sender || !func) { + _E("invalid argument : sender(NULL) || func(NULL)"); + return -EINVAL; + } + + watch = get_matched_watch_item(sender); + if (!watch) { + /* create new watch item */ + watch = add_watch_item(sender); + if (!watch) { + _E("fail to add watch item"); + return -EPERM; + } + isnew = true; + } + + /* find the same callback */ + DD_LIST_FOREACH(watch->func_list, elem, finfo) { + if (finfo->func == func) { + _E("there is already the same callback"); + goto out; + } + } + + finfo = calloc(1, sizeof(struct watch_func_info)); + if (!finfo) { + _E("fail to allocate watch func info"); + goto out; + } + + finfo->func = func; + finfo->data = data; + + /* add callback function to the watch list */ + DD_LIST_APPEND(watch->func_list, finfo); + + _I("register watch func(%p) of %s", func, sender); + return 0; +out: + if (isnew) + watch->deleted = true; + + return -EPERM; +} + +int unregister_edbus_watch(const char *sender, + void (*func)(const char *sender, void *data)) +{ + struct watch_info *watch; + struct watch_func_info *finfo; + dd_list *elem; + bool matched = false; + + if (!sender || !func) { + _E("invalid argument : sender(NULL) || func(NULL)"); + return -EINVAL; + } + + watch = get_matched_watch_item(sender); + if (!watch) { + _E("fail to get matched watch item"); + return -ENODEV; + } + + /* check the no interest function */ + DD_LIST_FOREACH(watch->func_list, elem, finfo) { + if (finfo->func == func) + finfo->deleted = true; + if (!finfo->deleted) + matched = true; + } + + /* if it is the last item */ + if (!matched) + watch->deleted = true; + + _I("unregister watch func(%p) of %s", func, sender); + return 0; +} + +static void unregister_edbus_watch_all(void) +{ + dd_list *n, *next; + struct watch_info *watch; + + DD_LIST_FOREACH_SAFE(edbus_watch_list, n, next, watch) + watch->deleted = true; + + add_idle_request(watch_idler_cb, NULL); +} + +static int register_method(E_DBus_Interface *iface, + const struct edbus_method *edbus_methods, int size) +{ + int ret; + int i; + + assert(iface); + assert(edbus_methods); + + for (i = 0; i < size; i++) { + ret = e_dbus_interface_method_add(iface, + edbus_methods[i].member, + edbus_methods[i].signature, + edbus_methods[i].reply_signature, + edbus_methods[i].func); + if (!ret) { + _E("fail to add method %s!", edbus_methods[i].member); + return -EINVAL; + } + } + + return 0; +} + +int register_edbus_interface_and_method(const char *path, + const char *interface, + const struct edbus_method *edbus_methods, int size) +{ + struct edbus_object *obj; + dd_list *elem; + int ret; + + if (!path || !interface || !edbus_methods || size < 1) { + _E("invalid parameter"); + return -EINVAL; + } + + /* find matched obj */ + DD_LIST_FOREACH(edbus_object_list, elem, obj) { + if (strncmp(obj->path, path, strlen(obj->path)) == 0 && + strncmp(obj->interface, interface, strlen(obj->interface)) == 0) { + _I("found matched item : obj(%p)", obj); + break; + } + } + + /* if there is no matched obj */ + if (!obj) { + obj = malloc(sizeof(struct edbus_object)); + if (!obj) { + _E("fail to allocate %s interface", path); + return -ENOMEM; + } + + obj->path = strdup(path); + obj->interface = strdup(interface); + + ret = register_edbus_interface(obj); + if (ret < 0) { + _E("fail to register %s interface(%d)", obj->path, ret); + free(obj->path); + free(obj->interface); + free(obj); + return ret; + } + + DD_LIST_APPEND(edbus_object_list, obj); + } + + ret = register_method(obj->iface, edbus_methods, size); + if (ret < 0) { + _E("fail to register %s method(%d)", obj->path, ret); + return ret; + } + + return 0; +} + +int unregister_edbus_interface_all(void) +{ + struct edbus_object *obj; + dd_list *elem, *n; + + DD_LIST_FOREACH_SAFE(edbus_object_list, elem, n, obj) { + DD_LIST_REMOVE(edbus_object_list, obj); + free(obj->path); + free(obj->interface); + free(obj); + } + + return 0; +} + +int register_edbus_method(const char *path, const struct edbus_method *edbus_methods, int size) +{ + E_DBus_Interface *iface; + int ret; + + if (!path || !edbus_methods || size < 1) { + _E("invalid parameter"); + return -EINVAL; + } + + iface = get_edbus_interface(path); + if (!iface) { + _E("fail to get edbus interface!"); + return -ENODEV; + } + + ret = register_method(iface, edbus_methods, size); + if (ret < 0) { + _E("fail to register %s method(%d)", path, ret); + return ret; + } + + return 0; +} + +static void request_name_cb(void *data, DBusMessage *msg, DBusError *error) +{ + DBusError err; + unsigned int val; + int r; + + if (!msg) { + _D("invalid DBusMessage!"); + return; + } + + dbus_error_init(&err); + r = dbus_message_get_args(msg, &err, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID); + if (!r) { + _E("no message : [%s:%s]", err.name, err.message); + dbus_error_free(&err); + return; + } + + _I("Request Name reply : %d", val); +} + +static void check_owner_name(void) +{ + DBusError err; + DBusMessage *msg; + DBusMessageIter iter; + char *pa[1]; + char exe_name[PATH_MAX]; + char *entry; + dd_list *n; + int pid; + + DD_LIST_FOREACH(edbus_owner_list, n, entry) { + pa[0] = entry; + msg = dbus_method_sync_with_reply(E_DBUS_FDO_BUS, + E_DBUS_FDO_PATH, + E_DBUS_FDO_INTERFACE, + "GetConnectionUnixProcessID", "s", pa); + + if (!msg) { + _E("invalid DBusMessage!"); + return; + } + + dbus_error_init(&err); + dbus_message_iter_init(msg, &iter); + + dbus_message_iter_get_basic(&iter, &pid); + if (get_cmdline_name(pid, exe_name, PATH_MAX) != 0) + goto out; + _I("%s(%d)", exe_name, pid); + +out: + dbus_message_unref(msg); + dbus_error_free(&err); + } +} + +static void check_owner_list(void) +{ + DBusError err; + DBusMessage *msg; + DBusMessageIter array, item; + char *pa[1]; + char *name; + char *entry; + + pa[0] = VIBRATOR_BUS_NAME; + msg = dbus_method_sync_with_reply(E_DBUS_FDO_BUS, + E_DBUS_FDO_PATH, + E_DBUS_FDO_INTERFACE, + "ListQueuedOwners", "s", pa); + + if (!msg) { + _E("invalid DBusMessage!"); + return; + } + + dbus_error_init(&err); + dbus_message_iter_init(msg, &array); + + if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) + goto out; + dbus_message_iter_recurse(&array, &item); + while (dbus_message_iter_get_arg_type(&item) == DBUS_TYPE_STRING) { + dbus_message_iter_get_basic(&item, &name); + entry = strndup(name, strlen(name)); + DD_LIST_APPEND(edbus_owner_list, entry); + if (!edbus_owner_list) { + _E("append failed"); + free(entry); + goto out; + } + dbus_message_iter_next(&item); + } + +out: + dbus_message_unref(msg); + dbus_error_free(&err); +} + +void edbus_init(void *data) +{ + DBusError error; + int retry = 0; + int i, ret; + + dbus_threads_init_default(); + dbus_error_init(&error); + + do { + edbus_init_val = e_dbus_init(); + if (edbus_init_val) + break; + if (retry == EDBUS_INIT_RETRY_COUNT) { + _E("fail to init edbus"); + return; + } + retry++; + } while (retry <= EDBUS_INIT_RETRY_COUNT); + + retry = 0; + do { + conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (conn) + break; + if (retry == EDBUS_INIT_RETRY_COUNT) { + _E("fail to get dbus"); + goto out1; + } + retry++; + } while (retry <= EDBUS_INIT_RETRY_COUNT); + + retry = 0; + do { + edbus_conn = e_dbus_connection_setup(conn); + if (edbus_conn) + break; + if (retry == EDBUS_INIT_RETRY_COUNT) { + _E("fail to get edbus"); + goto out2; + } + retry++; + } while (retry <= EDBUS_INIT_RETRY_COUNT); + + retry = 0; + do { + edbus_request_name = e_dbus_request_name(edbus_conn, VIBRATOR_BUS_NAME, + DBUS_NAME_FLAG_REPLACE_EXISTING, request_name_cb, NULL); + if (edbus_request_name) + break; + if (retry == EDBUS_INIT_RETRY_COUNT) { + _E("fail to request edbus name"); + goto out3; + } + retry++; + } while (retry <= EDBUS_INIT_RETRY_COUNT); + + for (i = 0; i < ARRAY_SIZE(edbus_objects); i++) { + ret = register_edbus_interface(&edbus_objects[i]); + if (ret < 0) { + _E("fail to add obj & interface for %s", + edbus_objects[i].interface); + return; + } + _D("add new obj for %s", edbus_objects[i].interface); + } + check_owner_list(); + check_owner_name(); + return; + +out3: + e_dbus_connection_close(edbus_conn); +out2: + dbus_connection_set_exit_on_disconnect(conn, FALSE); +out1: + e_dbus_shutdown(); +} + +void edbus_exit(void *data) +{ + unregister_edbus_signal_handle(); + unregister_edbus_watch_all(); + unregister_edbus_interface_all(); + e_dbus_connection_close(edbus_conn); + e_dbus_shutdown(); +} diff --git a/src/core/edbus-handler.h b/src/core/edbus-handler.h new file mode 100644 index 0000000..b1ec0ed --- /dev/null +++ b/src/core/edbus-handler.h @@ -0,0 +1,51 @@ +/* + * feedbackd + * + * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __FEEDBACKD_EDBUS_HANDLE_H__ +#define __FEEDBACKD_EDBUS_HANDLE_H__ + +#include +#include "core/dbus.h" + +struct edbus_method { + const char *member; + const char *signature; + const char *reply_signature; + E_DBus_Method_Cb func; +}; +int register_edbus_interface_and_method(const char *path, + const char *interface, + const struct edbus_method *edbus_methods, int size); +int register_edbus_method(const char *path, const struct edbus_method *edbus_methods, int size); +int register_edbus_signal_handler(const char *path, const char *interface, + const char *name, E_DBus_Signal_Cb cb); +int unregister_edbus_signal_handler(const char *path, const char *interface, + const char *name); +E_DBus_Interface *get_edbus_interface(const char *path); +int broadcast_edbus_signal(const char *path, const char *interface, + const char *name, const char *sig, char *param[]); +int register_edbus_watch(const char *sender, + void (*func)(const char *sender, void *data), void *data); +int unregister_edbus_watch(const char *sender, + void (*func)(const char *sender, void *data)); + +void edbus_init(void *data); +void edbus_exit(void *data); + +#endif /* __FEEDBACKD_EDBUS_HANDLE_H__ */ diff --git a/src/core/list.h b/src/core/list.h new file mode 100644 index 0000000..1ef40b4 --- /dev/null +++ b/src/core/list.h @@ -0,0 +1,60 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FEEDBACKD_LIST_H__ +#define __FEEDBACKD_LIST_H__ + +#include +typedef GList dd_list; + +#define DD_LIST_PREPEND(a, b) \ + a = g_list_prepend(a, (gpointer)b) +#define DD_LIST_APPEND(a, b) \ + a = g_list_append(a, (gpointer)b) +#define DD_LIST_REMOVE(a, b) \ + a = g_list_remove(a, (gpointer)b) +#define DD_LIST_REMOVE_LIST(a, b) \ + a = g_list_delete_link(a, b) +#define DD_LIST_LENGTH(a) \ + g_list_length(a) +#define DD_LIST_NTH(a, b) \ + g_list_nth_data(a, b) +#define DD_LIST_FIND(a, b) \ + g_list_find(a, (gpointer)b) +#define DD_LIST_FREE_LIST(a) \ + g_list_free(a) +#define DD_LIST_FOREACH(head, elem, node) \ + for (elem = head, node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = elem->next, node = NULL) +#define DD_LIST_FOREACH_SAFE(head, elem, elem_next, node) \ + for (elem = head, elem_next = g_list_next(elem), node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = elem_next, elem_next = g_list_next(elem), node = NULL) +#define DD_LIST_REVERSE_FOREACH(head, elem, node) \ + for (elem = g_list_last(head), node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = g_list_previous(elem), node = NULL) +#define DD_LIST_REVERSE_FOREACH_SAFE(head, elem, elem_next, node) \ + for (elem = g_list_last(head), elem_next = g_list_previous(elem), node = NULL; \ + elem && ((node = elem->data) != NULL); \ + elem = elem_next, elem_next = g_list_previous(elem), node = NULL) +#define DD_LIST_NEXT(a) \ + g_list_next(a) + +#endif diff --git a/src/core/log.c b/src/core/log.c new file mode 100644 index 0000000..7b9a583 --- /dev/null +++ b/src/core/log.c @@ -0,0 +1,39 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "log.h" + +#ifdef DEBUG +void __cyg_profile_func_enter(void *, void *) + __attribute__ ((no_instrument_function)); +void __cyg_profile_func_exit(void *, void *) + __attribute__ ((no_instrument_function)); + +int g_trace_depth = -2; + +void __cyg_profile_func_enter(void *func, void *caller) +{ + g_trace_depth++; +} + +void __cyg_profile_func_exit(void *func, void *caller) +{ + g_trace_depth--; +} +#endif diff --git a/src/core/log.h b/src/core/log.h new file mode 100644 index 0000000..87b5481 --- /dev/null +++ b/src/core/log.h @@ -0,0 +1,48 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __FEEDBACKD_LOG_H__ +#define __FEEDBACKD_LOG_H__ + +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "FEEDBACKD" + +#define _D(fmt, arg...) \ + do { SLOGD(fmt, ##arg); } while (0) +#define _I(fmt, arg...) \ + do { SLOGI(fmt, ##arg); } while (0) +#define _W(fmt, arg...) \ + do { SLOGW(fmt, ##arg); } while (0) +#define _E(fmt, arg...) \ + do { SLOGE(fmt, ##arg); } while (0) +#define _SD(fmt, arg...) \ + do { SECURE_SLOGD(fmt, ##arg); } while (0) +#define _SI(fmt, arg...) \ + do { SECURE_SLOGI(fmt, ##arg); } while (0) +#define _SW(fmt, arg...) \ + do { SECURE_SLOGW(fmt, ##arg); } while (0) +#define _SE(fmt, arg...) \ + do { SECURE_SLOGE(fmt, ##arg); } while (0) + +#endif /* __FEEDBACKD_LOG_H__ */ diff --git a/src/core/main.c b/src/core/main.c new file mode 100644 index 0000000..8ae1fb9 --- /dev/null +++ b/src/core/main.c @@ -0,0 +1,65 @@ +/* + * feedbackd + * + * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +#include "core/log.h" +#include "core/common.h" +#include "core/edbus-handler.h" +#include "core/dbus.h" +#include "haptic/haptic.h" + +static void sig_quit(int signo) +{ + _D("received SIGTERM signal %d", signo); +} + +static void sig_usr1(int signo) +{ + _D("received SIGUSR1 signal %d, feedbackd'll be finished!", signo); + + ecore_main_loop_quit(); +} + +int main(int argc, char **argv) +{ + int ret; + + ecore_init(); + edbus_init(NULL); + ret = haptic_probe(); + if (ret != 0) { + _E("[haptic] probe fail"); + return ret; + } + haptic_init(); + + signal(SIGTERM, sig_quit); + signal(SIGUSR1, sig_usr1); + + ecore_main_loop_begin(); + + _D("[haptic] deinitialize"); + haptic_exit(); + edbus_exit(NULL); + ecore_shutdown(); + return 0; +} diff --git a/src/haptic/circle.c b/src/haptic/circle.c new file mode 100644 index 0000000..d78ddba --- /dev/null +++ b/src/haptic/circle.c @@ -0,0 +1,323 @@ +/* + * feedbackd + * + * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/log.h" +#include "core/list.h" +#include "haptic.h" +#include "standard-vibcore.h" + +#define CIRCLE_ON_PATH "/sys/class/sec/motor/motor_on" +#define CIRCLE_OFF_PATH "/sys/class/sec/motor/motor_off" + +static int fd_play = -1, fd_stop = -1; +static dd_list *handle_list; +static Ecore_Timer *stop_timer; +static int unique_number; +static bool state = false; + +static int stop_device(int device_handle); +static bool find_from_list(int handle) +{ + dd_list *elem; + + elem = DD_LIST_FIND(handle_list, (gpointer)(long)handle); + if (elem) + return true; + + return false; +} + +static Eina_Bool timer_cb(void *data) +{ + int device_handle = (int)(long)data; + _I("stop vibration by timer"); + + /* stop previous vibration */ + stop_device(device_handle); + + return ECORE_CALLBACK_CANCEL; +} + +static int get_device_count(int *count) +{ + /* suppose there is just one haptic device */ + if (count) + *count = 1; + + return 0; +} + +static int open_device(int device_index, int *device_handle) +{ + int n; + bool found = false; + dd_list *elem; + + if (!device_handle) + return -EINVAL; + + /* if it is the first element */ + n = DD_LIST_LENGTH(handle_list); + if (n == 0) { + _I("Open"); + if (fd_play < 0) { + fd_play = open(CIRCLE_ON_PATH, O_RDONLY); + if (fd_play < 0) { + _E("Failed to open %s : %d", CIRCLE_ON_PATH, errno); + return -errno; + } + } + if (fd_stop < 0) { + fd_stop = open(CIRCLE_OFF_PATH, O_RDONLY); + if (fd_stop < 0) { + _E("Failed to open %s : %d", CIRCLE_OFF_PATH, errno); + return -errno; + } + } + } + + if (unique_number == INT_MAX) + unique_number = 0; + + while (found != true) { + ++unique_number; + elem = DD_LIST_FIND(handle_list, (gpointer)(long)unique_number); + if (!elem) + found = true; + } + + /* add info to local list */ + DD_LIST_APPEND(handle_list, (gpointer)(long)unique_number); + + *device_handle = unique_number; + return 0; +} + +static int close_device(int device_handle) +{ + int r, n; + bool found; + + found = find_from_list(device_handle); + if (!found) + return -EINVAL; + + if (fd_stop < 0) { + fd_stop = open(CIRCLE_OFF_PATH, O_RDONLY); + if (fd_stop < 0) + return -ENODEV; + } + + /* stop vibration */ + r = stop_device(device_handle); + if (r < 0) + _I("already stopped or failed to stop effect : %d", r); + + /* unregister existing timer */ + if (r >= 0) { + _D("device handle %d is closed and timer deleted", device_handle); + if (stop_timer) { + ecore_timer_del(stop_timer); + stop_timer = NULL; + } + } + + standard_vibrate_close(); + + DD_LIST_REMOVE(handle_list, (gpointer)(long)device_handle); + + /* if it is the last element */ + n = DD_LIST_LENGTH(handle_list); + if (n == 0) { + _I("Close"); + if (fd_play > 0) { + close(fd_play); + fd_play = -1; + } + if (fd_stop > 0) { + close(fd_stop); + fd_stop = -1; + } + } + + return 0; +} + +static int vibrate_monotone(int device_handle, int duration, int feedback, int priority, int *effect_handle) +{ + int ret; + char buf[8]; + bool found; + + found = find_from_list(device_handle); + if (!found) + return -EINVAL; + + if (fd_play < 0) { + fd_play = open(CIRCLE_ON_PATH, O_RDONLY); + if (fd_play < 0) + return -ENODEV; + } + + /* Zero(0) is the infinitely vibration value */ + if (duration == HAPTIC_MODULE_DURATION_UNLIMITED) + duration = 0; + + if (stop_timer) + stop_device(device_handle); + + /* play vibration */ + ret = read(fd_play, buf, 8); + if (ret < 0) { + _E("failed to play"); + return -errno; + } + + /* register timer */ + if (duration) { + stop_timer = ecore_timer_add(duration/1000.f, timer_cb, (void *)(long)device_handle); + if (!stop_timer) + _E("Failed to add timer callback"); + } + + _D("device handle %d %dms", device_handle, duration); + + return 0; +} + +static int vibrate_buffer(int device_handle, const unsigned char *vibe_buffer, int iteration, int feedback, int priority, int *effect_handle) +{ + _E("Not support feature"); + return -EACCES; +} + +static int stop_device(int device_handle) +{ + int ret; + char buf[8]; + bool found; + + found = find_from_list(device_handle); + if (!found) + return -EINVAL; + + if (fd_stop < 0) { + fd_stop = open(CIRCLE_OFF_PATH, O_RDONLY); + if (fd_stop < 0) + return -ENODEV; + } + ret = read(fd_stop, buf, 8); + if (ret < 0) { + _E("failed to stop"); + return -errno; + } + if (stop_timer) { + ecore_timer_del(stop_timer); + stop_timer = NULL; + } + + return 0; +} + +static int get_device_state(int device_index, int *effect_state) +{ + if (!effect_state) + return -EINVAL; + + *effect_state = state; + return 0; +} + +static int create_effect(unsigned char *vibe_buffer, int max_bufsize, haptic_module_effect_element *elem_arr, int max_elemcnt) +{ + _E("Not support feature"); + return -EACCES; +} + +static int get_buffer_duration(int device_handle, const unsigned char *vibe_buffer, int *buffer_duration) +{ + _E("Not support feature"); + return -EACCES; +} + +static int convert_binary(const unsigned char *vibe_buffer, int max_bufsize, const char *file_path) +{ + _E("Not support feature"); + return -EACCES; +} +/* END: Haptic Module APIs */ + +static const struct haptic_plugin_ops default_plugin = { + .get_device_count = get_device_count, + .open_device = open_device, + .close_device = close_device, + .vibrate_monotone = vibrate_monotone, + .vibrate_buffer = vibrate_buffer, + .vibrate_effect = standard_vibrate_effect, + .is_supported = standard_is_supported, + .stop_device = stop_device, + .get_device_state = get_device_state, + .create_effect = create_effect, + .get_buffer_duration = get_buffer_duration, + .convert_binary = convert_binary, +}; + +static bool is_valid(void) +{ + int ret; + + if ((access(CIRCLE_ON_PATH, R_OK) != 0) || + (access(CIRCLE_OFF_PATH, R_OK) != 0)) { + _E("Do not support wearable haptic device"); + state = false; + return false; + } + + ret = standard_config_parse(); + if (ret < 0) + _E("failed to load standard vibration configuration file : %d", ret); + + state = true; + _I("Support wearable haptic device"); + return true; +} + +static const struct haptic_plugin_ops *load(void) +{ + standard_set_vib_function(&vibrate_monotone); + return &default_plugin; +} + +static const struct haptic_ops std_ops = { + .type = HAPTIC_STANDARD, + .is_valid = is_valid, + .load = load, +}; + +HAPTIC_OPS_REGISTER(&std_ops) diff --git a/src/haptic/emulator.c b/src/haptic/emulator.c new file mode 100644 index 0000000..db5492a --- /dev/null +++ b/src/haptic/emulator.c @@ -0,0 +1,196 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + +#include "core/log.h" +#include "core/list.h" +#include "haptic.h" + +#define DEFAULT_EFFECT_HANDLE 0xFFFA + +static dd_list *handle_list; +static int unique_number = 0; + +/* START: Haptic Module APIs */ +static int get_device_count(int *count) +{ + if (count) + *count = 1; + + return 0; +} + +static int open_device(int device_index, int *device_handle) +{ + dd_list *elem; + int handle; + bool found = false; + + if (unique_number == INT_MAX) + unique_number = 0; + + while (found != true) { + ++unique_number; + elem = DD_LIST_FIND(handle_list, (gpointer)(long)unique_number); + if (!elem) + found = true; + } + handle = unique_number; + DD_LIST_APPEND(handle_list, (gpointer)(long)handle); + + if (device_handle) + *device_handle = handle; + + return 0; +} + +static int close_device(int device_handle) +{ + DD_LIST_REMOVE(handle_list, (gpointer)(long)device_handle); + + return 0; +} + +static int vibrate_monotone(int device_handle, int duration, int feedback, int priority, int *effect_handle) +{ + dd_list *elem; + + elem = DD_LIST_FIND(handle_list, (gpointer)(long)device_handle); + if (!elem) + return -EINVAL; + + if (effect_handle) + *effect_handle = DEFAULT_EFFECT_HANDLE; + + return 0; +} + +static int vibrate_buffer(int device_handle, const unsigned char *vibe_buffer, int iteration, int feedback, int priority, int *effect_handle) +{ + dd_list *elem; + + elem = DD_LIST_FIND(handle_list, (gpointer)(long)device_handle); + if (!elem) + return -EINVAL; + + if (effect_handle) + *effect_handle = DEFAULT_EFFECT_HANDLE; + + return 0; +} + +static int vibrate_effect(int device_handle, const char *pattern, int feedback, int priority) +{ + dd_list *elem; + + elem = DD_LIST_FIND(handle_list, (gpointer)(long)device_handle); + if (!elem) + return -EINVAL; + + return 0; +} + +static int stop_device(int device_handle) +{ + dd_list *elem; + + elem = DD_LIST_FIND(handle_list, (gpointer)(long)device_handle); + if (!elem) + return -EINVAL; + + return 0; +} + +static int is_supported(const char *pattern) +{ + return 0; +} + +static int get_device_state(int device_index, int *effect_state) +{ + if (effect_state) + *effect_state = 0; + + return 0; +} + +static int create_effect(unsigned char *vibe_buffer, int max_bufsize, haptic_module_effect_element *elem_arr, int max_elemcnt) +{ + _E("Not support feature"); + return -EACCES; +} + +static int get_buffer_duration(int device_handle, const unsigned char *vibe_buffer, int *buffer_duration) +{ + dd_list *elem; + + elem = DD_LIST_FIND(handle_list, (gpointer)(long)device_handle); + if (!elem) + return -EINVAL; + + _E("Not support feature"); + return -EACCES; +} + +static int convert_binary(const unsigned char *vibe_buffer, int max_bufsize, const char *file_path) +{ + _E("Not support feature"); + return -EACCES; +} +/* END: Haptic Module APIs */ + +static const struct haptic_plugin_ops default_plugin = { + .get_device_count = get_device_count, + .open_device = open_device, + .close_device = close_device, + .vibrate_monotone = vibrate_monotone, + .vibrate_buffer = vibrate_buffer, + .vibrate_effect = vibrate_effect, + .is_supported = is_supported, + .stop_device = stop_device, + .get_device_state = get_device_state, + .create_effect = create_effect, + .get_buffer_duration = get_buffer_duration, + .convert_binary = convert_binary, +}; + +static bool is_valid(void) +{ + if (is_emulator()) { + _I("Support emulator haptic device"); + return true; + } + + _E("Do not support emulator haptic device"); + return false; +} + +static const struct haptic_plugin_ops *load(void) +{ + return &default_plugin; +} + +static const struct haptic_ops emul_ops = { + .is_valid = is_valid, + .load = load, +}; + +HAPTIC_OPS_REGISTER(&emul_ops) diff --git a/src/haptic/external.c b/src/haptic/external.c new file mode 100644 index 0000000..32eb6b3 --- /dev/null +++ b/src/haptic/external.c @@ -0,0 +1,99 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include + +#include "core/log.h" +#include "haptic.h" + +#define HAPTIC_MODULE_PATH "/usr/lib/libhaptic-module.so" + +/* Haptic Plugin Interface */ +static void *dlopen_handle; +static const struct haptic_plugin_ops *plugin_intf; + +static bool is_valid(void) +{ + struct stat buf; + const struct haptic_plugin_ops *(*get_haptic_plugin_interface) () = NULL; + + if (stat(HAPTIC_MODULE_PATH, &buf)) { + _E("file(%s) is not presents", HAPTIC_MODULE_PATH); + goto error; + } + + dlopen_handle = dlopen(HAPTIC_MODULE_PATH, RTLD_NOW); + if (!dlopen_handle) { + _E("dlopen failed"); + goto error; + } + + get_haptic_plugin_interface = dlsym(dlopen_handle, "get_haptic_plugin_interface"); + if (!get_haptic_plugin_interface) { + _E("dlsym failed"); + goto error; + } + + plugin_intf = get_haptic_plugin_interface(); + if (!plugin_intf) { + _E("get_haptic_plugin_interface() failed"); + goto error; + } + + _I("Support external haptic device"); + return true; + +error: + if (dlopen_handle) { + dlclose(dlopen_handle); + dlopen_handle = NULL; + } + + _I("Do not support external haptic device"); + return false; +} + +static const struct haptic_plugin_ops *load(void) +{ + return plugin_intf; +} + +static void release(void) +{ + if (dlopen_handle) { + dlclose(dlopen_handle); + dlopen_handle = NULL; + } + + plugin_intf = NULL; +} + +static const struct haptic_ops ext_ops = { + .type = HAPTIC_EXTERNAL, + .is_valid = is_valid, + .load = load, + .release = release, +}; + +HAPTIC_OPS_REGISTER(&ext_ops) diff --git a/src/haptic/haptic-mobile.conf b/src/haptic/haptic-mobile.conf new file mode 100644 index 0000000..84271f1 --- /dev/null +++ b/src/haptic/haptic-mobile.conf @@ -0,0 +1,24 @@ +[Haptic] +# level +# how much does the vibration level to subdivide. +# The max value of vibration level fixed at 100. +# The min value of vibration level fixed at 0. +level=6 + +[level0] +value=0 + +[level1] +value=20 + +[level2] +value=40 + +[level3] +value=60 + +[level4] +value=80 + +[level5] +value=100 diff --git a/src/haptic/haptic-module.h b/src/haptic/haptic-module.h new file mode 100644 index 0000000..9b50bd1 --- /dev/null +++ b/src/haptic/haptic-module.h @@ -0,0 +1,91 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __FEEDBACKD_HAPTIC_MODULE_H__ +#define __FEEDBACKD_HAPTIC_MODULE_H__ + +/** + * @brief Enumerations of device id for the Haptic Module API. + * @details We support two motors now. + */ +typedef enum { + HAPTIC_MODULE_DEVICE_0 = 0x0, /**< 1st motor */ + HAPTIC_MODULE_DEVICE_1 = 0x1, /**< 2nd motor */ + HAPTIC_MODULE_DEVICE_ALL = 0x4, /**< both of them */ +} haptic_module_device; + +/** + * @brief Enumerations of priority level for the Haptic Module API. + */ +typedef enum { + HAPTIC_MODULE_PRIORITY_MIN = 0, /**< Minimum effect priority for developers (default) */ + HAPTIC_MODULE_PRIORITY_MIDDLE, /**< Maximum effect priority for developers */ + HAPTIC_MODULE_PRIORITY_HIGH, /**< Maximum effect priority for OEMs */ +} haptic_module_priority; + +/** + * @brief Enumerations of feedback level for the Haptic Module API. + * @details Haptic level means vibration power (intensity). + */ +typedef enum { + HAPTIC_MODULE_FEEDBACK_MIN = 0, + HAPTIC_MODULE_FEEDBACK_MAX = 100, +} haptic_module_feedback; + +/** + * @brief Enumerations of unlimited duration for the Haptic Module API. + */ +typedef enum { + HAPTIC_MODULE_DURATION_UNLIMITED = 0x7FFFFFFF, +} haptic_module_duration; + +/** + * @brief Enumerations of iteration count for the Haptic Module API. + */ +typedef enum { + HAPTIC_MODULE_ITERATION_ONCE = 1, + HAPTIC_MODULE_ITERATION_INFINITE = 256, +} haptic_module_iteration; + +/** + * @brief Enumerations of effect or device state for the Haptic Module API. + */ +typedef enum { + HAPTIC_MODULE_STATE_STOP = 0, + HAPTIC_MODULE_STATE_PLAYING, +} haptic_module_state; + +/* Error and Return value codes */ +#define HAPTIC_MODULE_ERROR_NONE 0 +#define HAPTIC_MODULE_NOT_INITIALIZED -1 +#define HAPTIC_MODULE_ALREADY_INITIALIZED -2 +#define HAPTIC_MODULE_INVALID_ARGUMENT -3 +#define HAPTIC_MODULE_OPERATION_FAILED -4 +#define HAPTIC_MODULE_NOT_SUPPORTED -5 + +/** + * @par Description: + * effect element for haptic module. + */ +typedef struct { + int haptic_duration; /**< Start time of the effect element in millisecond */ + int haptic_level; /**< Duration of the effect element in millisecond */ +} haptic_module_effect_element; + +#endif /* __FEEDBACKD_HAPTIC_MODULE_H__ */ diff --git a/src/haptic/haptic-plugin-intf.h b/src/haptic/haptic-plugin-intf.h new file mode 100644 index 0000000..c507397 --- /dev/null +++ b/src/haptic/haptic-plugin-intf.h @@ -0,0 +1,42 @@ +/* + * deviced + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __FEEDBACKD_HAPTIC_PLUGIN_INTF_H__ +#define __FEEDBACKD_HAPTIC_PLUGIN_INTF_H__ + +#include "haptic-module.h" + +struct haptic_plugin_ops { + int (*get_device_count) (int*); + int (*open_device) (int, int*); + int (*close_device) (int); + int (*vibrate_monotone) (int, int, int, int, int*); + int (*vibrate_buffer) (int, const unsigned char*, int, int, int, int*); + int (*vibrate_effect) (int, const char*, int, int); + int (*is_supported) (const char*); + int (*stop_device) (int); + int (*get_device_state) (int, int*); + int (*create_effect) (unsigned char*, int, haptic_module_effect_element*, int); + int (*get_buffer_duration) (int, const unsigned char*, int*); + int (*convert_binary) (const unsigned char*, int, const char*); +}; + +const struct haptic_plugin_ops *get_haptic_plugin_interface(); + +#endif /* __FEEDBACKD_HAPTIC_PLUGIN_INTF_H__ */ diff --git a/src/haptic/haptic-wearable.conf b/src/haptic/haptic-wearable.conf new file mode 100644 index 0000000..6e52318 --- /dev/null +++ b/src/haptic/haptic-wearable.conf @@ -0,0 +1,15 @@ +[Haptic] +# level +# how much does the vibration level to subdivide. +# The max value of vibration level fixed at 100. +# The min value of vibration level fixed at 0. +level=3 + +[level0] +value=0 + +[level1] +value=58 + +[level2] +value=100 diff --git a/src/haptic/haptic.c b/src/haptic/haptic.c new file mode 100644 index 0000000..1b78083 --- /dev/null +++ b/src/haptic/haptic.c @@ -0,0 +1,974 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include + +#include "core/log.h" +#include "core/list.h" +#include "core/common.h" +#include "core/device-idler.h" +#include "core/edbus-handler.h" +#include "core/config-parser.h" +#include "haptic.h" + +#define HAPTIC_CONF_PATH "/etc/feedbackd/haptic.conf" +#define SIGNAL_CHANGE_HARDKEY "ChangeHardkey" +#define SIGNAL_POWEROFF_STATE "ChangeState" +#define SIGNAL_VIBRATOR_INITIATED "InitiateVibrator" + +/* hardkey vibration variable */ +#define HARDKEY_VIB_ITERATION 1 +#define HARDKEY_VIB_FEEDBACK 3 +#define HARDKEY_VIB_PRIORITY 2 +#define HARDKEY_VIB_DURATION 30 +#define HAPTIC_FEEDBACK_STEP 20 +#define DEFAULT_FEEDBACK_LEVEL 3 + +/* power on, power off vibration variable */ +#define POWER_ON_VIB_DURATION 300 +#define POWER_OFF_VIB_DURATION 300 +#define POWER_VIB_FEEDBACK 100 + +#define MAX_EFFECT_BUFFER (64*1024) + +#ifndef VCONFKEY_RECORDER_STATE +#define VCONFKEY_RECORDER_STATE "memory/recorder/state" +#define VCONFKEY_RECORDER_STATE_RECORDING 2 +#endif + +#define CHECK_VALID_OPS(ops, r) ((ops) ? true : !(r = -ENODEV)) +#define RETRY_CNT 3 + +struct haptic_info { + char *sender; + dd_list *handle_list; +}; + +struct vibrate_effect_info { + unsigned int handle; + char *pattern; + int level; + int priority; +}; + +/* for playing */ +static int g_handle; + +/* haptic operation variable */ +static dd_list *h_head; +static dd_list *haptic_handle_list; +static const struct haptic_plugin_ops *h_ops; +static enum haptic_type h_type; +static bool haptic_disabled; + +struct haptic_config { + int level; + int *level_arr; + int sound_capture; +}; + +static struct haptic_config haptic_conf; + +static int haptic_start(void); +static int haptic_stop(void); +static int haptic_internal_init(void); +static int remove_haptic_info(struct haptic_info *info); + +void add_haptic(const struct haptic_ops *ops) +{ + DD_LIST_APPEND(h_head, (void *)ops); +} + +void remove_haptic(const struct haptic_ops *ops) +{ + DD_LIST_REMOVE(h_head, (void *)ops); +} + +static int haptic_module_load(void) +{ + struct haptic_ops *ops; + dd_list *elem; + int r; + + /* find valid plugin */ + DD_LIST_FOREACH(h_head, elem, ops) { + if (ops->is_valid && ops->is_valid()) { + if (ops->load) + h_ops = ops->load(); + h_type = ops->type; + break; + } + } + + if (!CHECK_VALID_OPS(h_ops, r)) { + _E("Can't find the valid haptic device"); + return r; + } + + /* solution bug + * we do not use internal vibration except power off. + * if the last handle is closed during the playing of vibration, + * solution makes unlimited vibration. + * so we need at least one handle. */ + haptic_internal_init(); + + return 0; +} + +static int convert_magnitude_by_conf(int level) +{ + int i, step; + + assert(level >= 0 && level <= 100); + + step = 100 / (haptic_conf.level-1); + for (i = 0; i < haptic_conf.level; ++i) { + if (level <= i*step) { + _D("level changed : %d -> %d", level, haptic_conf.level_arr[i]); + return haptic_conf.level_arr[i]; + } + } + + _D("play default level"); + return DEFAULT_FEEDBACK_LEVEL * HAPTIC_FEEDBACK_STEP; +} + +static DBusMessage *edbus_get_count(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int ret, val; + + if (!CHECK_VALID_OPS(h_ops, ret)) + goto exit; + + ret = h_ops->get_device_count(&val); + if (ret >= 0) + ret = val; + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static void haptic_name_owner_changed(const char *sender, void *data) +{ + dd_list *n; + struct haptic_info *info = data; + int handle; + + _I("%s (sender:%s)", __func__, sender); + + if (!info) + return; + + for (n = info->handle_list; n; n = n->next) { + handle = (int)(long)n->data; + h_ops->stop_device(handle); + h_ops->close_device(handle); + } + + remove_haptic_info(info); +} + +static struct haptic_info *add_haptic_info(const char *sender) +{ + struct haptic_info *info; + + assert(sender); + + info = calloc(1, sizeof(struct haptic_info)); + if (!info) + return NULL; + + info->sender = strdup(sender); + DD_LIST_APPEND(haptic_handle_list, info); + + register_edbus_watch(sender, haptic_name_owner_changed, info); + + return info; +} + +static int remove_haptic_info(struct haptic_info *info) +{ + assert(info); + + unregister_edbus_watch(info->sender, haptic_name_owner_changed); + + DD_LIST_REMOVE(haptic_handle_list, info); + DD_LIST_FREE_LIST(info->handle_list); + free(info->sender); + free(info); + + return 0; +} + +static struct haptic_info *get_matched_haptic_info(const char *sender) +{ + dd_list *n; + struct haptic_info *info; + int len; + + assert(sender); + + len = strlen(sender) + 1; + DD_LIST_FOREACH(haptic_handle_list, n, info) { + if (!strncmp(info->sender, sender, len)) + return info; + } + + return NULL; +} + +static DBusMessage *edbus_open_device(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int index, handle, ret; + struct haptic_info *info; + const char *sender; + + /* Load haptic module before booting done */ + if (!CHECK_VALID_OPS(h_ops, ret)) { + ret = haptic_module_load(); + if (ret < 0) + goto exit; + } + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) { + ret = -EINVAL; + goto exit; + } + + ret = h_ops->open_device(index, &handle); + if (ret < 0) + goto exit; + + + sender = dbus_message_get_sender(msg); + if (!sender) { + ret = -EPERM; + h_ops->close_device(handle); + goto exit; + } + + info = get_matched_haptic_info(sender); + if (!info) { + info = add_haptic_info(sender); + if (!info) { + _E("fail to create haptic information"); + ret = -EPERM; + h_ops->close_device(handle); + goto exit; + } + } + + DD_LIST_APPEND(info->handle_list, (gpointer)(long)handle); + + ret = handle; + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *edbus_close_device(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + unsigned int handle; + int ret; + struct haptic_info *info; + const char *sender; + int cnt; + + if (!CHECK_VALID_OPS(h_ops, ret)) + goto exit; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID)) { + ret = -EINVAL; + goto exit; + } + + sender = dbus_message_get_sender(msg); + if (!sender) { + _E("fail to get sender from dbus message"); + ret = -EPERM; + goto exit; + } + + ret = h_ops->close_device(handle); + if (ret < 0) + goto exit; + + info = get_matched_haptic_info(sender); + if (!info) { + _E("fail to find the matched haptic info."); + goto exit; + } + + DD_LIST_REMOVE(info->handle_list, (gpointer)(long)handle); + cnt = DD_LIST_LENGTH(info->handle_list); + if (cnt == 0) + remove_haptic_info(info); + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *edbus_vibrate_monotone(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + unsigned int handle; + int duration, level, priority, e_handle, ret = 0; + + if (!CHECK_VALID_OPS(h_ops, ret)) + goto exit; + + if (haptic_disabled) + goto exit; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INT32, &duration, + DBUS_TYPE_INT32, &level, + DBUS_TYPE_INT32, &priority, DBUS_TYPE_INVALID)) { + ret = -EINVAL; + goto exit; + } + + /* convert as per conf value */ + level = convert_magnitude_by_conf(level); + if (level < 0) { + ret = -EINVAL; + goto exit; + } + + ret = h_ops->vibrate_monotone(handle, duration, level, priority, &e_handle); + if (ret >= 0) + ret = e_handle; + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; + +} + +static DBusMessage *edbus_vibrate_buffer(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + unsigned int handle; + unsigned char *data; + int size, iteration, level, priority, e_handle, ret = 0; + + if (!CHECK_VALID_OPS(h_ops, ret)) + goto exit; + + if (haptic_disabled) + goto exit; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, &size, + DBUS_TYPE_INT32, &iteration, + DBUS_TYPE_INT32, &level, + DBUS_TYPE_INT32, &priority, DBUS_TYPE_INVALID)) { + ret = -EINVAL; + goto exit; + } + + /* convert as per conf value */ + level = convert_magnitude_by_conf(level); + if (level < 0) { + ret = -EINVAL; + goto exit; + } + + ret = h_ops->vibrate_buffer(handle, data, iteration, level, priority, &e_handle); + if (ret >= 0) + ret = e_handle; + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static void vibrate_effect_idler_cb(void *data) +{ + struct vibrate_effect_info *vibrate_info = (struct vibrate_effect_info *)data; + + h_ops->vibrate_effect(vibrate_info->handle, vibrate_info->pattern, + vibrate_info->level, vibrate_info->priority); + free(vibrate_info->pattern); + free(vibrate_info); +} + +static DBusMessage *edbus_vibrate_effect(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + struct vibrate_effect_info *vibrate_info; + unsigned int handle; + char *pattern; + int level, priority, ret = 0; + + if (!CHECK_VALID_OPS(h_ops, ret)) + goto exit; + + if (haptic_disabled) + goto exit; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_STRING, &pattern, + DBUS_TYPE_INT32, &level, + DBUS_TYPE_INT32, &priority, DBUS_TYPE_INVALID)) { + ret = -EINVAL; + goto exit; + } + + /* convert as per conf value */ + level = convert_magnitude_by_conf(level); + if (level < 0) { + ret = -EINVAL; + goto exit; + } + + vibrate_info = calloc(1, sizeof(struct vibrate_effect_info)); + if (!vibrate_info) { + _E("failed to allocate memory for vibrate_info"); + ret = -errno; + goto exit; + } + vibrate_info->handle = handle; + vibrate_info->pattern = strdup(pattern); + if (!vibrate_info->pattern) { + _E("failed to allocate memory for pattern"); + ret = -errno; + free(vibrate_info); + goto exit; + } + vibrate_info->level = level; + vibrate_info->priority = priority; + + ret = add_idle_request(vibrate_effect_idler_cb, (void *)vibrate_info); + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *edbus_stop_device(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + unsigned int handle; + int ret = 0; + + if (!CHECK_VALID_OPS(h_ops, ret)) + goto exit; + + if (haptic_disabled) + goto exit; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID)) { + ret = -EINVAL; + goto exit; + } + + ret = h_ops->stop_device(handle); + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *edbus_get_state(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + int index, state, ret; + + if (!CHECK_VALID_OPS(h_ops, ret)) + goto exit; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) { + ret = -EINVAL; + goto exit; + } + + ret = h_ops->get_device_state(index, &state); + if (ret >= 0) + ret = state; + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *edbus_create_effect(E_DBus_Object *obj, DBusMessage *msg) +{ + static unsigned char data[MAX_EFFECT_BUFFER]; + static unsigned char *p = data; + DBusMessageIter iter, arr; + DBusMessage *reply; + haptic_module_effect_element *elem_arr; + int i, size, cnt, ret, bufsize = sizeof(data); + + if (!CHECK_VALID_OPS(h_ops, ret)) + goto exit; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &bufsize, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &elem_arr, &size, + DBUS_TYPE_INT32, &cnt, DBUS_TYPE_INVALID)) { + ret = -EINVAL; + goto exit; + } + + if (bufsize > MAX_EFFECT_BUFFER) { + ret = -ENOMEM; + goto exit; + } + + for (i = 0; i < cnt; ++i) + _D("[%2d] %d %d", i, elem_arr[i].haptic_duration, elem_arr[i].haptic_level); + + memset(data, 0, MAX_EFFECT_BUFFER); + ret = h_ops->create_effect(data, bufsize, elem_arr, cnt); + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &arr); + dbus_message_iter_append_fixed_array(&arr, DBUS_TYPE_BYTE, &p, bufsize); + dbus_message_iter_close_container(&iter, &arr); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *edbus_get_duration(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + unsigned int handle; + unsigned char *data; + int size, duration, ret; + + if (!CHECK_VALID_OPS(h_ops, ret)) + goto exit; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, &size, + DBUS_TYPE_INVALID)) { + ret = -EINVAL; + goto exit; + } + + ret = h_ops->get_buffer_duration(handle, data, &duration); + if (ret >= 0) + ret = duration; + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *edbus_save_binary(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + unsigned char *data; + char *path; + int size, ret; + + if (!CHECK_VALID_OPS(h_ops, ret)) + goto exit; + + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, &size, + DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) { + ret = -EINVAL; + goto exit; + } + + _D("file path : %s", path); + ret = h_ops->convert_binary(data, size, path); + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static DBusMessage *edbus_show_handle_list(E_DBus_Object *obj, DBusMessage *msg) +{ + dd_list *n; + dd_list *elem; + struct haptic_info *info; + + _D("sender handle"); + DD_LIST_FOREACH(haptic_handle_list, n, info) { + for (elem = info->handle_list; elem; elem = elem->next) + _D("%s %d", info->sender, (int)(long)elem->data); + } + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *edbus_pattern_is_supported(E_DBus_Object *obj, DBusMessage *msg) +{ + DBusMessageIter iter; + DBusMessage *reply; + char *data; + int ret = 0; + + if (!CHECK_VALID_OPS(h_ops, ret)) + goto exit; + + if (haptic_disabled) + goto exit; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &data, + DBUS_TYPE_INVALID)) { + ret = -EINVAL; + goto exit; + } + + ret = h_ops->is_supported(data); + + _I("%s is supported : %d", data, ret); + +exit: + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + return reply; +} + +static int haptic_internal_init(void) +{ + int r; + if (!CHECK_VALID_OPS(h_ops, r)) + return r; + return h_ops->open_device(HAPTIC_MODULE_DEVICE_ALL, &g_handle); +} + +static int haptic_internal_exit(void) +{ + int r; + if (!CHECK_VALID_OPS(h_ops, r)) + return r; + return h_ops->close_device(g_handle); +} + +static void haptic_hardkey_changed_cb(void *data, DBusMessage *msg) +{ + int level, status, e_handle, ret; + + if (!CHECK_VALID_OPS(h_ops, ret)) { + ret = haptic_module_load(); + if (ret < 0) + return; + } + + if (!g_handle) + haptic_internal_init(); + + /* if haptic is stopped, do not play vibration */ + if (haptic_disabled) + return; + + if (vconf_get_bool(VCONFKEY_SETAPPL_VIBRATION_STATUS_BOOL, &status) < 0) { + _E("fail to get VCONFKEY_SETAPPL_VIBRATION_STATUS_BOOL"); + status = 1; + } + + /* when turn off haptic feedback option */ + if (!status) + return; + + ret = vconf_get_int(VCONFKEY_SETAPPL_TOUCH_FEEDBACK_VIBRATION_LEVEL_INT, &level); + if (ret < 0) { + _E("fail to get VCONFKEY_SETAPPL_TOUCH_FEEDBACK_VIBRATION_LEVEL_INT"); + level = HARDKEY_VIB_FEEDBACK; + } + + ret = h_ops->vibrate_monotone(g_handle, HARDKEY_VIB_DURATION, + level*HAPTIC_FEEDBACK_STEP, HARDKEY_VIB_PRIORITY, &e_handle); + if (ret < 0) + _E("fail to vibrate buffer : %d", ret); + + return; +} + +static void haptic_poweroff_cb(void *data, DBusMessage *msg) +{ + int e_handle, ret; + struct timespec time = {0,}; + + if (!CHECK_VALID_OPS(h_ops, ret)) { + ret = haptic_module_load(); + if (ret < 0) + return; + } + + if (!g_handle) + haptic_internal_init(); + + /* power off vibration */ + ret = h_ops->vibrate_monotone(g_handle, POWER_OFF_VIB_DURATION, + POWER_VIB_FEEDBACK, HARDKEY_VIB_PRIORITY, &e_handle); + if (ret < 0) { + _E("fail to vibrate_monotone : %d", ret); + return; + } + + /* sleep for vibration */ + time.tv_nsec = POWER_OFF_VIB_DURATION * NANO_SECOND_MULTIPLIER; + nanosleep(&time, NULL); + return; +} + +static void sound_capturing_cb(keynode_t *key, void *data) +{ + int status; + + status = vconf_keynode_get_int(key); + + /* if sound capture is in use, this value is 1(true). */ + if (status == VCONFKEY_RECORDER_STATE_RECORDING) + haptic_stop(); + else + haptic_start(); +} + +static int parse_section(struct parse_result *result, void *user_data, int index) +{ + struct haptic_config *conf = (struct haptic_config *)user_data; + + if (!result) + return 0; + + if (!result->section || !result->name || !result->value) + return 0; + + if (MATCH(result->name, "sound_capture")) { + conf->sound_capture = atoi(result->value); + } else if (MATCH(result->name, "level")) { + conf->level = atoi(result->value); + if (conf->level < 0 || conf->level >= INT_MAX - 1) { + _E("You must set level with positive number in integer range"); + return -EINVAL; + } + conf->level_arr = calloc(sizeof(int), conf->level); + if (!conf->level_arr) { + _E("failed to allocate memory for level"); + return -errno; + } + } else if (MATCH(result->name, "value")) { + if (index < 0) + return -EINVAL; + conf->level_arr[index] = atoi(result->value); + } + + return 0; +} + +static int haptic_load_config(struct parse_result *result, void *user_data) +{ + struct haptic_config *conf = (struct haptic_config *)user_data; + char name[NAME_MAX]; + int ret; + static int index; + + if (!result) + return 0; + + if (!result->section || !result->name || !result->value) + return 0; + + /* Parsing 'Haptic' section */ + if (MATCH(result->section, "Haptic")) { + ret = parse_section(result, user_data, -1); + if (ret < 0) { + _E("failed to parse [Haptic] section : %d", ret); + return ret; + } + goto out; + } + + /* Parsing 'Level' section */ + for (index = 0; index < conf->level; ++index) { + snprintf(name, sizeof(name), "level%d", index); + if (MATCH(result->section, name)) { + ret = parse_section(result, user_data, index); + if (ret < 0) { + _E("failed to parse [level] section : %d", ret); + return ret; + } + goto out; + } + } + +out: + return 0; +} + +static const struct edbus_method edbus_methods[] = { + { "GetCount", NULL, "i", edbus_get_count }, + { "OpenDevice", "i", "i", edbus_open_device }, + { "CloseDevice", "u", "i", edbus_close_device }, + { "StopDevice", "u", "i", edbus_stop_device }, + { "VibrateMonotone", "uiii", "i", edbus_vibrate_monotone }, + { "VibrateBuffer", "uayiii", "i", edbus_vibrate_buffer }, + { "VibrateEffect", "usii", "i", edbus_vibrate_effect }, + { "GetState", "i", "i", edbus_get_state }, + { "GetDuration", "uay", "i", edbus_get_duration }, + { "CreateEffect", "iayi", "ayi", edbus_create_effect }, + { "SaveBinary", "ays", "i", edbus_save_binary }, + { "ShowHandleList", NULL, NULL, edbus_show_handle_list }, + { "IsSupported", "s", "i", edbus_pattern_is_supported }, + /* Add methods here */ +}; + +int haptic_probe(void) +{ + /** + * load haptic module. + * if there is no haptic module, + * feedbackd does not activate a haptic interface. + */ + return haptic_module_load(); +} + +void haptic_init(void) +{ + int r; + + /* get haptic data from configuration file */ + r = config_parse(HAPTIC_CONF_PATH, haptic_load_config, &haptic_conf); + if (r < 0) { + _E("failed to load configuration file(%s) : %d", HAPTIC_CONF_PATH, r); + safe_free(haptic_conf.level_arr); + } + + /* init dbus interface */ + r = register_edbus_interface_and_method(VIBRATOR_PATH_HAPTIC, + VIBRATOR_INTERFACE_HAPTIC, + edbus_methods, ARRAY_SIZE(edbus_methods)); + if (r < 0) + _E("fail to init edbus interface and method(%d)", r); + + /* register notifier for below each event */ + register_edbus_signal_handler(DEVICED_PATH_KEY, + DEVICED_INTERFACE_KEY, + SIGNAL_CHANGE_HARDKEY, + haptic_hardkey_changed_cb); + + register_edbus_signal_handler(DEVICED_PATH_POWEROFF, + DEVICED_INTERFACE_POWEROFF, + SIGNAL_POWEROFF_STATE, + haptic_poweroff_cb); + + broadcast_edbus_signal(VIBRATOR_PATH_HAPTIC, + VIBRATOR_INTERFACE_HAPTIC, + SIGNAL_VIBRATOR_INITIATED, + "", NULL); + + /* add watch for sound capturing value */ + if (haptic_conf.sound_capture) + vconf_notify_key_changed(VCONFKEY_RECORDER_STATE, sound_capturing_cb, NULL); +} + +void haptic_exit(void) +{ + struct haptic_ops *ops; + dd_list *elem; + int r; + + /* remove watch */ + if (haptic_conf.sound_capture) + vconf_ignore_key_changed(VCONFKEY_RECORDER_STATE, sound_capturing_cb); + + /* unregister notifier for below each event */ + unregister_edbus_signal_handler(DEVICED_PATH_KEY, + DEVICED_INTERFACE_KEY, + SIGNAL_CHANGE_HARDKEY); + + unregister_edbus_signal_handler(DEVICED_PATH_POWEROFF, + DEVICED_INTERFACE_POWEROFF, + SIGNAL_POWEROFF_STATE); + + /* release haptic data memory */ + safe_free(haptic_conf.level_arr); + + if (!CHECK_VALID_OPS(h_ops, r)) + return; + + /* haptic exit for feedbackd */ + haptic_internal_exit(); + + /* release plugin */ + DD_LIST_FOREACH(h_head, elem, ops) { + if (ops->is_valid && ops->is_valid()) { + if (ops->release) + ops->release(); + h_ops = NULL; + break; + } + } +} + +static int haptic_start(void) +{ + _I("start"); + haptic_disabled = false; + return 0; +} + +static int haptic_stop(void) +{ + _I("stop"); + haptic_disabled = true; + return 0; +} diff --git a/src/haptic/haptic.h b/src/haptic/haptic.h new file mode 100644 index 0000000..26056a5 --- /dev/null +++ b/src/haptic/haptic.h @@ -0,0 +1,56 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __FEEDBACKD_HAPTIC_H__ +#define __FEEDBACKD_HAPTIC_H__ + +#include +#include "core/common.h" +#include "haptic-plugin-intf.h" + +#define HAPTIC_OPS_REGISTER(dev) \ +static void __CONSTRUCTOR__ module_init(void) \ +{ \ + add_haptic(dev); \ +} \ +static void __DESTRUCTOR__ module_exit(void) \ +{ \ + remove_haptic(dev); \ +} + +enum haptic_type { + HAPTIC_STANDARD, + HAPTIC_EXTERNAL, +}; + +struct haptic_ops { + enum haptic_type type; + bool (*is_valid)(void); + const struct haptic_plugin_ops *(*load)(void); + void (*release)(void); +}; + +void add_haptic(const struct haptic_ops *ops); +void remove_haptic(const struct haptic_ops *ops); + +int haptic_probe(void); +void haptic_init(void); +void haptic_exit(void); + +#endif /* __FEEDBACKD_HAPTIC_H__ */ diff --git a/src/haptic/standard-mix.c b/src/haptic/standard-mix.c new file mode 100644 index 0000000..40bda76 --- /dev/null +++ b/src/haptic/standard-mix.c @@ -0,0 +1,1085 @@ +/* + * feedbackd + * + * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/log.h" +#include "core/list.h" +#include "core/config-parser.h" +#include "haptic.h" + +#define MAX_MAGNITUDE 0xFFFF +#define PERIODIC_MAX_MAGNITUDE 0x7FFF /* 0.5 * MAX_MAGNITUDE */ +#define RUMBLE_MAX_MAGNITUDE 0xFFFF + +#define DEV_INPUT "/dev/input" +#define EVENT "event" + +#define BITS_PER_LONG (sizeof(long) * 8) +#define OFF(x) ((x)%BITS_PER_LONG) +#define BIT(x) (1UL<> OFF(bit)) & 1) + +#define MAX_DATA 16 +#define FF_INFO_MAGIC 0xDEADFEED + +#define VIBRATION_CONF_PATH "/usr/share/feedback/vibration_mix.conf" +#define VIBRATION_DURATION_CONF_PATH "/usr/share/feedback/vibration_duration.conf" +#define VIBRATION_WAITING_CONF_PATH "/usr/share/feedback/vibration_waiting.conf" + +struct ff_info_header { + unsigned int magic; + int iteration; + int ff_info_data_count; +}; + +struct ff_info_data { + int type;/* play, stop etc */ + int magnitude; /* strength */ + int length; /* in ms for stop, play*/ +}; + +struct ff_info_buffer { + struct ff_info_header header; + struct ff_info_data data[MAX_DATA]; +}; + +struct ff_info { + int handle; + Ecore_Timer *timer; + struct ff_effect effect; + struct ff_info_buffer *ffinfobuffer; + int currentindex; +}; + +struct vibration_table { + char *pattern; + char *duration; + char *waiting; +}; + +struct vibration_config { + char *pattern; + int *data; + int data_len; +}; + +struct haptic_data { + unsigned int handle; + int *duration_config; + int *waiting_config; + int level; + int priority; + int duration_len; + int waiting_len; + int index; + bool stop; +}; + +static int ff_fd; +static dd_list *ff_list; +static dd_list *handle_list; +static dd_list *vib_conf_list; +static dd_list *vib_duration_conf_list; +static dd_list *vib_waiting_conf_list; +static Ecore_Timer *duration_timer; +static char ff_path[PATH_MAX]; +static int unique_number; + +struct ff_info *read_from_list(int handle) +{ + struct ff_info *temp; + dd_list *elem; + + DD_LIST_FOREACH(ff_list, elem, temp) { + if (temp->handle == handle) + return temp; + } + return NULL; +} + +static bool check_valid_handle(struct ff_info *info) +{ + struct ff_info *temp; + dd_list *elem; + + DD_LIST_FOREACH(ff_list, elem, temp) { + if (temp == info) + break; + } + + if (!temp) + return false; + return true; +} + +static bool check_fd(int *fd) +{ + int ffd; + + if (*fd > 0) + return true; + + ffd = open(ff_path, O_RDWR); + if (ffd <= 0) + return false; + + *fd = ffd; + return true; +} + +static int ff_stop(int fd, struct ff_effect *effect); +static Eina_Bool timer_cb(void *data) +{ + struct ff_info *info = (struct ff_info *)data; + + if (!info) + return ECORE_CALLBACK_CANCEL; + + if (!check_valid_handle(info)) + return ECORE_CALLBACK_CANCEL; + + _I("stop vibration by timer : id(%d)", info->effect.id); + + /* stop previous vibration */ + ff_stop(ff_fd, &info->effect); + + /* reset timer */ + info->timer = NULL; + + return ECORE_CALLBACK_CANCEL; +} + +static int ff_find_device(void) +{ + DIR *dir; + struct dirent *dent; + char ev_path[PATH_MAX]; + unsigned long features[1+FF_MAX/sizeof(unsigned long)]; + int fd, ret; + + dir = opendir(DEV_INPUT); + if (!dir) + return -errno; + + while (1) { + dent = readdir(dir); + if (dent == NULL) + break; + + if (dent->d_type == DT_DIR || + !strstr(dent->d_name, "event")) + continue; + + snprintf(ev_path, sizeof(ev_path), "%s/%s", DEV_INPUT, dent->d_name); + + fd = open(ev_path, O_RDWR); + if (fd < 0) + continue; + + /* get force feedback device */ + memset(features, 0, sizeof(features)); + ret = ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features); + if (ret == -1) { + close(fd); + continue; + } + + if (test_bit(FF_CONSTANT, features)) + _D("%s type : constant", ev_path); + if (test_bit(FF_PERIODIC, features)) + _D("%s type : periodic", ev_path); + if (test_bit(FF_SPRING, features)) + _D("%s type : spring", ev_path); + if (test_bit(FF_FRICTION, features)) + _D("%s type : friction", ev_path); + if (test_bit(FF_RUMBLE, features)) + _D("%s type : rumble", ev_path); + + if (test_bit(FF_RUMBLE, features)) { + memcpy(ff_path, ev_path, strlen(ev_path)); + close(fd); + closedir(dir); + return 0; + } + + close(fd); + } + + closedir(dir); + return -1; +} + +static int ff_init_effect(struct ff_effect *effect) +{ + if (!effect) + return -EINVAL; + + /*Only rumble supported as of now*/ + effect->type = FF_RUMBLE; + effect->replay.length = 0; + effect->replay.delay = 10; + effect->id = -1; + effect->u.rumble.strong_magnitude = 0x8000; + effect->u.rumble.weak_magnitude = 0xc000; + + return 0; +} + +static int ff_set_effect(struct ff_effect *effect, int length, int level) +{ + double magnitude; + + if (!effect) + return -EINVAL; + + magnitude = (double)level/HAPTIC_MODULE_FEEDBACK_MAX; + magnitude *= RUMBLE_MAX_MAGNITUDE; + + _I("info : magnitude(%d) length(%d)", (int)magnitude, length); + + /* set member variables in effect struct */ + effect->u.rumble.strong_magnitude = (int)magnitude; + effect->replay.length = length; /* length millisecond */ + + return 0; +} + +static int ff_play(int fd, struct ff_effect *effect) +{ + struct input_event play; + int ret; + + if (fd < 0 || !effect) { + if (fd < 0) + _E("fail to check fd"); + else + _E("fail to check effect"); + return -EINVAL; + } + + /* upload an effect */ + if (ioctl(fd, EVIOCSFF, effect) == -1) { + _E("fail to ioctl"); + return -errno; + } + + /* play vibration*/ + play.type = EV_FF; + play.code = effect->id; + play.value = 1; /* 1 : PLAY, 0 : STOP */ + + ret = write(fd, (const void *)&play, sizeof(play)); + if (ret == -1) { + _E("fail to write"); + return -errno; + } + + return 0; +} + +static int ff_stop(int fd, struct ff_effect *effect) +{ + struct input_event stop; + int ret; + + if (fd < 0) + return -EINVAL; + + /* Stop vibration */ + stop.type = EV_FF; + stop.code = effect->id; + stop.value = 0; /* 1 : PLAY, 0 : STOP */ + ret = write(fd, (const void *)&stop, sizeof(stop)); + if (ret == -1) + return -errno; + + /* removing an effect from the device */ + if (ioctl(fd, EVIOCRMFF, effect->id) == -1) + return -errno; + + /* reset effect id */ + effect->id = -1; + + return 0; +} + +/* START: Haptic Module APIs */ +static int get_device_count(int *count) +{ + /* suppose there is just one haptic device */ + if (count) + *count = 1; + + return 0; +} + +static int open_device(int device_index, int *device_handle) +{ + struct ff_info *info; + int n; + bool found = false; + dd_list *elem; + + if (!device_handle) + return -EINVAL; + + /* if it is the first element */ + n = DD_LIST_LENGTH(ff_list); + if (n == 0 && !ff_fd) { + _I("First element: open ff driver"); + /* open ff driver */ + ff_fd = open(ff_path, O_RDWR); + if (!ff_fd) { + _E("Failed to open %s : %d", ff_path, errno); + return -errno; + } + } + + /* allocate memory */ + info = calloc(sizeof(struct ff_info), 1); + if (!info) { + _E("Failed to allocate memory : %d", errno); + return -errno; + } + + /* initialize ff_effect structure */ + ff_init_effect(&info->effect); + + if (unique_number == INT_MAX) + unique_number = 0; + + while (found != true) { + ++unique_number; + elem = DD_LIST_FIND(handle_list, (gpointer)(long)unique_number); + if (!elem) + found = true; + } + + info->handle = unique_number; + + /* add info to local list */ + DD_LIST_APPEND(ff_list, info); + DD_LIST_APPEND(handle_list, (gpointer)(long)info->handle); + + *device_handle = info->handle; + return 0; +} + +static int close_device(int device_handle) +{ + struct ff_info *info; + int r, n; + + info = read_from_list(device_handle); + if (!info) + return -EINVAL; + + if (!check_valid_handle(info)) + return -EINVAL; + + if (!check_fd(&ff_fd)) + return -ENODEV; + + /* stop vibration */ + r = ff_stop(ff_fd, &info->effect); + if (r < 0) + _I("already stopped or failed to stop effect : %d", r); + + /* unregister existing timer */ + if (r >= 0 && info->timer) { + _D("device handle %d is closed and timer deleted", device_handle); + ecore_timer_del(info->timer); + info->timer = NULL; + } + + DD_LIST_REMOVE(handle_list, (gpointer)(long)info->handle); + + safe_free(info->ffinfobuffer); + /* remove info from local list */ + DD_LIST_REMOVE(ff_list, info); + safe_free(info); + + /* if it is the last element */ + n = DD_LIST_LENGTH(ff_list); + if (n == 0 && ff_fd) { + _I("Last element: close ff driver"); + /* close ff driver */ + close(ff_fd); + ff_fd = 0; + } + + return 0; +} + +static int vibrate_monotone(int device_handle, int duration, int feedback, int priority, int *effect_handle) +{ + struct ff_info *info; + int ret; + + info = read_from_list(device_handle); + if (!info) { + _E("fail to check list"); + return -EINVAL; + } + + if (!check_valid_handle(info)) { + _E("fail to check handle"); + return -EINVAL; + } + + if (!check_fd(&ff_fd)) + return -ENODEV; + + /* Zero(0) is the infinitely vibration value */ + if (duration == HAPTIC_MODULE_DURATION_UNLIMITED) + duration = 0; + + /* unregister existing timer */ + if (info->timer) { + ff_stop(ff_fd, &info->effect); + ecore_timer_del(info->timer); + info->timer = NULL; + } + + /* set effect as per arguments */ + ff_init_effect(&info->effect); + ret = ff_set_effect(&info->effect, duration, feedback); + if (ret < 0) { + _E("failed to set effect(duration:%d, feedback:%d) : %d", + duration, feedback, ret); + return ret; + } + + /* play effect as per arguments */ + ret = ff_play(ff_fd, &info->effect); + if (ret < 0) { + _E("failed to play haptic effect(fd:%d id:%d) : %d", + ff_fd, info->effect.id, ret); + return ret; + } + + /* register timer */ + if (duration) { + info->timer = ecore_timer_add(duration/1000.f, timer_cb, info); + if (!info->timer) + _E("Failed to add timer callback"); + } + + _D("device handle %d effect id : %d %dms", device_handle, info->effect.id, duration); + if (effect_handle) + *effect_handle = info->effect.id; + + return 0; +} + +static Eina_Bool _buffer_play(void *cbdata) +{ + struct ff_info *info = (struct ff_info *)cbdata; + struct ff_info_header *header = &info->ffinfobuffer->header; + struct ff_info_data *data = info->ffinfobuffer->data; + int index = info->currentindex; + int play_type = (index < header->ff_info_data_count) ? data[index].type : 0; + int length = (index < header->ff_info_data_count) ? data[index].length : 1; + int ret; + + ff_set_effect(&info->effect, length, 1); + if (play_type != 0) { + _D("Going to play for %d ms", length); + ret = ff_play(ff_fd, &info->effect); + if (ret < 0) + _D("Failed to play the effect %d", ret); + } else { + _D("Going to stop for %d ms", length); + ret = ff_stop(ff_fd, &info->effect); + if (ret < 0) + _D("Failed to stop the effect %d", ret); + } + + if (info->currentindex < header->ff_info_data_count) { + info->currentindex++; + info->timer = ecore_timer_add(length/1000.0f, _buffer_play, info); + } else { + --header->iteration; + if (header->iteration > 0) { + info->currentindex = 0; + info->timer = ecore_timer_add(0.0, _buffer_play, info); + } else + info->timer = NULL; + } + + return ECORE_CALLBACK_CANCEL; +} + +static void print_buffer(const unsigned char *vibe_buffer) +{ + struct ff_info_buffer fb; + int i = 0; + memcpy(&fb.header, vibe_buffer, sizeof(struct ff_info_header)); + memcpy(&fb.data, (unsigned char *)vibe_buffer+sizeof(struct ff_info_header), + sizeof(struct ff_info_data) * fb.header.ff_info_data_count); + _D("\nMagic %x\niteration %d\ncount %d\n", fb.header.magic, + fb.header.iteration, fb.header.ff_info_data_count); + + for (i = 0; i < fb.header.ff_info_data_count; i++) + _D("type %d\nmagn 0x%x\nlen %d\n", fb.data[i].type, + fb.data[i].magnitude, fb.data[i].length); +} + +static int vibrate_custom_buffer(int device_handle, const unsigned char *vibe_buffer, int iteration, int feedback, int priority, int *effect_handle) +{ + struct ff_info *info; + struct ff_info_header *header; + struct ff_info_data *data; + + info = read_from_list(device_handle); + if (!info) + return -EINVAL; + + if (!check_valid_handle(info)) + return -EINVAL; + + if (!check_fd(&ff_fd)) + return -ENODEV; + + if (!info->ffinfobuffer) + info->ffinfobuffer = (struct ff_info_buffer *)calloc(sizeof(struct ff_info_buffer), 1); + if (!info->ffinfobuffer) + return -ENOMEM; + + header = &info->ffinfobuffer->header; + data = info->ffinfobuffer->data; + + memcpy(header, vibe_buffer, sizeof(struct ff_info_header)); + if (header->ff_info_data_count < 0 || header->ff_info_data_count > MAX_DATA) + return -EINVAL; + + memcpy(data, vibe_buffer+sizeof(struct ff_info_header), sizeof(struct ff_info_data) * header->ff_info_data_count); + + info->currentindex = 0; + if (info->timer) + ecore_timer_del(info->timer); + + if (header->iteration > 0) + _buffer_play(info); + + return 0; +} + +static Eina_Bool haptic_duration_play(void *data) +{ + struct haptic_data *h_data; + double time; + int ret = 0; + int index; + unsigned int v_handle; + int level; + int priority; + int duration; + + + if (!data) + return ECORE_CALLBACK_CANCEL; + + if (duration_timer) { + ecore_timer_del(duration_timer); + duration_timer = NULL; + } + + h_data = (struct haptic_data *)data; + if (h_data->stop) { + h_data->stop = false; + free(h_data); + return ECORE_CALLBACK_CANCEL; + } + + index = h_data->index; + v_handle = h_data->handle; + level = h_data->level; + priority = h_data->priority; + + if (!h_data->duration_config[index]) { + free(h_data); + return ECORE_CALLBACK_CANCEL; + } + + duration = h_data->duration_config[index]; + + h_data->index++; + if (h_data->index > h_data->duration_len - 1) + free(h_data); + else { + if (h_data->index > h_data->waiting_len - 1) + time = duration; + else + time = duration + h_data->waiting_config[index]; + + duration_timer = ecore_timer_add(time/1000.0f, haptic_duration_play, (void *)h_data); + _D("timer: %d", time); + } + + _D("duration: %d", duration); + + ret = vibrate_monotone(v_handle, duration, level, priority, NULL); + if (ret != 0) { + _D("auto stop vibration"); + if (h_data) + h_data->stop = true; + } + return ECORE_CALLBACK_CANCEL; +} + +static int vibrate_buffer(int device_handle, const unsigned char *vibe_buffer, int iteration, int feedback, int priority, int *effect_handle) +{ + int magic = 0; + + if (!device_handle) + return -EINVAL; + + if (vibe_buffer) + magic = *(int *)vibe_buffer; + + if (magic == FF_INFO_MAGIC) { + print_buffer(vibe_buffer); + return vibrate_custom_buffer(device_handle, vibe_buffer, iteration, feedback, priority, effect_handle); + } else + return vibrate_monotone(device_handle, 300, feedback, priority, effect_handle); +} + +static int vibrate_effect(int device_handle, const char *pattern, int feedback, int priority) +{ + dd_list *elem1, *elem2; + struct vibration_table *conf_table; + struct vibration_config *conf_duration, *conf_waiting; + struct haptic_data *data; + char *duration = NULL, *waiting = NULL; + int len1, len2; + + if (!device_handle) + return -EINVAL; + + if (!pattern) + return -EINVAL; + + len1 = strlen(pattern) + 1; + DD_LIST_FOREACH(vib_conf_list, elem1, conf_table) { + if (!conf_table->pattern) + continue; + if (strncmp(conf_table->pattern, pattern, len1)) + continue; + duration = conf_table->duration; + waiting = conf_table->waiting; + break; + } + + if (!duration) + return -ENOTSUP; + + len1 = strlen(duration) + 1; + if (!waiting) + len2 = 0; + else + len2 = strlen(waiting) + 1; + DD_LIST_FOREACH(vib_duration_conf_list, elem1, conf_duration) { + if (!conf_duration->pattern) + continue; + if (strncmp(conf_duration->pattern, duration, len1)) + continue; + + data = (struct haptic_data *)malloc(sizeof(struct haptic_data)); + if (!data) { + _E("fail to alloc"); + return -ENOMEM; + } + data->duration_len = 0; + data->waiting_len = 0; + + DD_LIST_FOREACH(vib_waiting_conf_list, elem2, conf_waiting) { + if (!waiting) + break; + if (!conf_waiting->pattern) + continue; + if (strncmp(conf_waiting->pattern, waiting, len2)) + continue; + data->waiting_config = conf_waiting->data; + data->waiting_len = conf_waiting->data_len; + break; + } + data->handle = device_handle; + data->level = feedback; + data->priority = priority; + data->duration_config = conf_duration->data; + data->duration_len = conf_duration->data_len; + data->index = 0; + + haptic_duration_play((void *)data); + break; + } + + return 0; +} + +static int is_supported(const char *pattern) +{ + dd_list *elem; + struct vibration_table *conf; + int ret = 0; + int len; + + if (!pattern) + return -EINVAL; + + len = strlen(pattern) + 1; + DD_LIST_FOREACH(vib_conf_list, elem, conf) { + if (!conf->pattern) + continue; + if (!strncmp(conf->pattern, pattern, len)) { + ret = true; + break; + } + } + + return ret; +} + +static int stop_device(int device_handle) +{ + struct ff_info *info; + int r; + + info = read_from_list(device_handle); + if (!info) + return -EINVAL; + + if (!check_valid_handle(info)) + return -EINVAL; + + if (!check_fd(&ff_fd)) + return -ENODEV; + + /* stop effect */ + r = ff_stop(ff_fd, &info->effect); + if (r < 0) + _E("failed to stop effect(id:%d) : %d", info->effect.id, r); + + /* unregister existing timer */ + if (r >= 0 && info->timer) { + ecore_timer_del(info->timer); + info->timer = NULL; + } + + return 0; +} + +static int get_device_state(int device_index, int *effect_state) +{ + struct ff_info *info; + dd_list *elem; + int status = false; + + if (!effect_state) + return -EINVAL; + + /* suppose there is just one haptic device */ + DD_LIST_FOREACH(ff_list, elem, info) { + if (info->effect.id >= 0) { + status = true; + break; + } + } + + *effect_state = status; + return 0; +} + +static int create_effect(unsigned char *vibe_buffer, int max_bufsize, haptic_module_effect_element *elem_arr, int max_elemcnt) +{ + _E("Not supported feature"); + return -EACCES; +} + +static int get_buffer_duration(int device_handle, const unsigned char *vibe_buffer, int *buffer_duration) +{ + _E("Not supported feature"); + return -EACCES; +} + +static int convert_binary(const unsigned char *vibe_buffer, int max_bufsize, const char *file_path) +{ + _E("Not supported feature"); + return -EACCES; +} +/* END: Haptic Module APIs */ + +static const struct haptic_plugin_ops default_plugin = { + .get_device_count = get_device_count, + .open_device = open_device, + .close_device = close_device, + .vibrate_monotone = vibrate_monotone, + .vibrate_buffer = vibrate_buffer, + .vibrate_effect = vibrate_effect, + .is_supported = is_supported, + .stop_device = stop_device, + .get_device_state = get_device_state, + .create_effect = create_effect, + .get_buffer_duration = get_buffer_duration, + .convert_binary = convert_binary, +}; + +static int vibration_duration_load_config(struct parse_result *result, void *user_data) +{ + struct vibration_config *conf; + char *value; + char *check; + int count = 0; + int len; + int i; + + if (!result) + return 0; + + if (!MATCH(result->section, "Vibration")) + return 0; + + + if (!result->name || !result->value) + return 0; + + conf = (struct vibration_config *)calloc(1, sizeof(struct vibration_config)); + if (!conf) { + _E("fail to alloc"); + return -ENOMEM; + } + + conf->pattern = strdup(result->name); + if (!conf->pattern) + _E("fail to copy %s pattern data", result->name); + + value = result->value; + + if (!value) + len = 0; + else + len = strlen(value); + + if (len == 0) { + DD_LIST_APPEND(vib_duration_conf_list, conf); + return 0; + } + + check = strchr(value, ','); + while (check != NULL) { + count++; + check = strchr(check + 1, ','); + } + + int *duration = (int *)malloc((count + 1) * sizeof(int)); + for (i = 0; i <= count; i++) { + duration[i] = 0; + check = strchr(value, ','); + if (check) { + *check = '\0'; + duration[i] = strtol(value, NULL, 10); + value = check + 1; + } else { + duration[i] = strtol(value, NULL, 10); + break; + } + if (duration[i] == 0) + break; + } + conf->data = duration; + conf->data_len = count + 1; + + DD_LIST_APPEND(vib_duration_conf_list, conf); + + return 0; +} + +static int vibration_waiting_load_config(struct parse_result *result, void *user_data) +{ + struct vibration_config *conf; + char *value; + char *check; + int count = 0; + int len; + int i; + + if (!result) + return 0; + + if (!MATCH(result->section, "Vibration")) + return 0; + + + if (!result->name || !result->value) + return 0; + + conf = (struct vibration_config *)calloc(1, sizeof(struct vibration_config)); + if (!conf) { + _E("fail to alloc"); + return -ENOMEM; + } + + conf->pattern = strdup(result->name); + if (!conf->pattern) + _E("fail to copy %s pattern data", result->name); + + value = result->value; + + if (!value) + len = 0; + else + len = strlen(value); + + if (len == 0) { + DD_LIST_APPEND(vib_waiting_conf_list, conf); + return 0; + } + + check = strchr(value, ','); + while (check != NULL) { + count++; + check = strchr(check + 1, ','); + } + + int *waiting = (int *)malloc((count + 1) * sizeof(int)); + for (i = 0; i <= count; i++) { + waiting[i] = 0; + check = strchr(value, ','); + if (check) { + *check = '\0'; + waiting[i] = strtol(value, NULL, 10); + value = check + 1; + } else { + waiting[i] = strtol(value, NULL, 10); + break; + } + if (waiting[i] == 0) + break; + } + conf->data = waiting; + conf->data_len = count + 1; + + DD_LIST_APPEND(vib_waiting_conf_list, conf); + + return 0; +} + +static int vibration_table_load_config(struct parse_result *result, void *user_data) +{ + struct vibration_table *conf; + char *value; + char *check; + int len; + + if (!result) + return 0; + + if (!MATCH(result->section, "Vibration")) + return 0; + + + if (!result->name || !result->value) + return 0; + + conf = (struct vibration_table *)calloc(1, sizeof(struct vibration_table)); + if (!conf) { + _E("fail to alloc"); + return -ENOMEM; + } + + conf->pattern = strdup(result->name); + if (!conf->pattern) + _E("fail to copy %s pattern data", result->name); + + value = result->value; + + if (!value) + len = 0; + else + len = strlen(value); + + if (len == 0) { + DD_LIST_APPEND(vib_conf_list, conf); + return 0; + } + + check = strchr(value, ','); + + if (check) { + *check = '\0'; + conf->duration = strdup(value); + value = check + 1; + conf->waiting = strdup(value); + } else + conf->duration = strdup(value); + + DD_LIST_APPEND(vib_conf_list, conf); + + return 0; +} + +static bool is_valid(void) +{ + int ret; + + ret = ff_find_device(); + if (ret < 0) { + _E("Do not support standard haptic device"); + return false; + } + ret = config_parse(VIBRATION_CONF_PATH, vibration_table_load_config, NULL); + if (ret < 0) + _E("failed to load configuration file(%s) : %d", VIBRATION_DURATION_CONF_PATH, ret); + ret = config_parse(VIBRATION_DURATION_CONF_PATH, vibration_duration_load_config, NULL); + if (ret < 0) + _E("failed to load configuration file(%s) : %d", VIBRATION_DURATION_CONF_PATH, ret); + ret = config_parse(VIBRATION_WAITING_CONF_PATH, vibration_waiting_load_config, NULL); + if (ret < 0) + _E("failed to load configuration file(%s) : %d", VIBRATION_WAITING_CONF_PATH, ret); + + _I("Support standard haptic device"); + return true; +} + +static const struct haptic_plugin_ops *load(void) +{ + return &default_plugin; +} + +static const struct haptic_ops std_ops = { + .type = HAPTIC_STANDARD, + .is_valid = is_valid, + .load = load, +}; + +HAPTIC_OPS_REGISTER(&std_ops) diff --git a/src/haptic/standard-vibcore.c b/src/haptic/standard-vibcore.c new file mode 100644 index 0000000..e80d145 --- /dev/null +++ b/src/haptic/standard-vibcore.c @@ -0,0 +1,276 @@ +/* + * feedbackd + * + * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +#include "core/log.h" +#include "core/list.h" +#include "core/config-parser.h" +#include "standard-vibcore.h" + +#define VIBRATION_CONF_PATH "/usr/share/feedback/vibration.conf" + +struct vibration_config { + char *pattern; + dd_list *data; +}; + +struct duration_data { + int duration; + int wait; +}; + +struct haptic_data { + dd_list *vibration_data; + unsigned int handle; + int level; + int priority; + bool stop; +}; + +static dd_list *vib_conf_list; +static Ecore_Timer *duration_timer; + +static t_vibrate_monotone real_vibrate_monotone; + +static int vibration_load_config(struct parse_result *result, void *user_data) +{ + struct vibration_config *conf; + struct duration_data *data; + char *value; + char *check; + int len; + + if (!result) + return 0; + + if (!MATCH(result->section, "Vibration")) + return 0; + + + if (!result->name || !result->value) + return 0; + + conf = (struct vibration_config *)calloc(1, sizeof(struct vibration_config)); + if (!conf) { + _E("fail to alloc"); + return -ENOMEM; + } + + conf->pattern = strdup(result->name); + if (!conf->pattern) + _E("fail to copy %s pattern data", result->name); + + value = result->value; + + if (!value) + len = 0; + else + len = strlen(value); + + if (len == 0) { + data = (struct duration_data *)malloc(sizeof(struct duration_data)); + if (!data) { + _E("not enough memory"); + free(conf->pattern); + free(conf); + return -ENOMEM; + } + data->duration = 0; + data->wait = 0; + + DD_LIST_APPEND(conf->data, data); + DD_LIST_APPEND(vib_conf_list, conf); + return 0; + } + + do { + data = (struct duration_data *)malloc(sizeof(struct duration_data)); + if (!data) { + _E("not enough memory"); + free(conf->pattern); + free(conf); + return -ENOMEM; + } + data->duration = 0; + data->wait = 0; + + check = strchr(value, 'D'); + if (check) { + *check = '\0'; + data->duration = strtol(value, NULL, 10); + if (!value) + len = len - 1; + else + len = len - strlen(value) - 1; + value = check + 1; + } + check = strchr(value, 'W'); + if (check) { + *check = '\0'; + data->wait = strtol(value, NULL, 10); + if (!value) + len = len - 1; + else + len = len - strlen(value) - 1; + value = check + 1; + } + DD_LIST_APPEND(conf->data, data); + if (data->duration == 0 && data->wait == 0) + break; + } while (value && len > 0); + + DD_LIST_APPEND(vib_conf_list, conf); + + return 0; +} + +int standard_config_parse() +{ + return config_parse(VIBRATION_CONF_PATH, vibration_load_config, NULL); +} + +int standard_is_supported(const char *pattern) +{ + dd_list *elem; + struct vibration_config *conf; + size_t len; + int ret; + + if (!pattern) + return -EINVAL; + + len = strlen(pattern) + 1; + ret = 0; + DD_LIST_FOREACH(vib_conf_list, elem, conf) { + if (!conf->pattern) + continue; + if (!strncmp(conf->pattern, pattern, len)) { + ret = true; + break; + } + } + + return ret; +} + +static Eina_Bool haptic_duration_play(void *data) +{ + dd_list *head, *n, *next; + struct haptic_data *h_data; + struct duration_data *node; + int ret = 0; + + if (!data) + goto out; + + if (duration_timer) { + ecore_timer_del(duration_timer); + duration_timer = NULL; + } + + h_data = (struct haptic_data *)data; + if (h_data->stop) { + h_data->stop = false; + free(h_data); + goto out; + } + + head = h_data->vibration_data; + DD_LIST_FOREACH_SAFE(head, n, next, node) { + _D("Play: %dms and Wait: %dms", node->duration, node->wait); + if (!node->duration) { + free(h_data); + break; + } + + if (node->wait && next) { + h_data->vibration_data = next; + duration_timer = ecore_timer_add((node->duration + node->wait)/1000.0f, haptic_duration_play, (void *)h_data); + } + + ret = real_vibrate_monotone(h_data->handle, node->duration, h_data->level, h_data->priority, NULL); + if (!next) { + free(h_data); + goto out; + } + break; + } + if (ret != 0) { + _D("auto stop vibration"); + if (h_data) + h_data->stop = true; + } +out: + return ECORE_CALLBACK_CANCEL; +} + +int standard_set_vib_function(t_vibrate_monotone func) +{ + real_vibrate_monotone = func; + return 0; +} + +int standard_vibrate_effect(int device_handle, const char *pattern, int feedback, int priority) +{ + dd_list *elem; + struct vibration_config *conf; + struct haptic_data *data; + size_t len; + + if (device_handle < 0) + return -EINVAL; + + len = strlen(pattern) + 1; + DD_LIST_FOREACH(vib_conf_list, elem, conf) { + if (!conf->pattern) + continue; + if (strncmp(conf->pattern, pattern, len)) + continue; + + data = (struct haptic_data *)malloc(sizeof(struct haptic_data)); + if (!data) { + _E("fail to alloc"); + return -ENOMEM; + } + data->vibration_data = conf->data; + data->handle = device_handle; + data->level = feedback; + data->priority = priority; + data->stop = false; + _D("Play %s", conf->pattern); + haptic_duration_play((void *)data); + break; + } + + return 0; +} + +int standard_vibrate_close() +{ + if (duration_timer) { + ecore_timer_del(duration_timer); + duration_timer = NULL; + } + + return 0; +} + diff --git a/src/haptic/standard-vibcore.h b/src/haptic/standard-vibcore.h new file mode 100644 index 0000000..9f792b1 --- /dev/null +++ b/src/haptic/standard-vibcore.h @@ -0,0 +1,31 @@ +/* + * feedbackd + * + * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __FEEDBACKD_STANDARD_VIBCORE_H__ +#define __FEEDBACKD_STANDARD_VIBCORE_H__ + +typedef int (*t_vibrate_monotone)(int device_handle, int duration, int feedback, int priority, int *effect_handle); + +int standard_config_parse(); +int standard_is_supported(const char *pattern); +int standard_vibrate_effect(int device_handle, const char *pattern, int feedback, int priority); +int standard_set_vib_function(t_vibrate_monotone func); +int standard_vibrate_close(); + +#endif /* __FEEDBACKD_STANDARD_VIBCORE_H__ */ diff --git a/src/haptic/standard.c b/src/haptic/standard.c new file mode 100644 index 0000000..008bdcd --- /dev/null +++ b/src/haptic/standard.c @@ -0,0 +1,692 @@ +/* + * feedbackd + * + * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/log.h" +#include "core/list.h" +#include "haptic.h" +#include "standard-vibcore.h" + +#define MAX_MAGNITUDE 0xFFFF +#define PERIODIC_MAX_MAGNITUDE 0x7FFF /* 0.5 * MAX_MAGNITUDE */ +#define RUMBLE_MAX_MAGNITUDE 0xFFFF + +#define DEV_INPUT "/dev/input" +#define EVENT "event" + +#define MAX_DATA 16 +#define FF_INFO_MAGIC 0xDEADFEED + +struct ff_info_header { + unsigned int magic; + int iteration; + int ff_info_data_count; +}; + +struct ff_info_data { + int type;/* play, stop etc */ + int magnitude; /* strength */ + int length; /* in ms for stop, play*/ +}; + +struct ff_info_buffer { + struct ff_info_header header; + struct ff_info_data data[MAX_DATA]; +}; + +struct ff_info { + int handle; + Ecore_Timer *timer; + struct ff_effect effect; + struct ff_info_buffer *ffinfobuffer; + int currentindex; +}; + +static int ff_fd; +static dd_list *ff_list; +static dd_list *handle_list; +static char ff_path[PATH_MAX]; +static int unique_number; + +struct ff_info *read_from_list(int handle) +{ + struct ff_info *temp; + dd_list *elem; + + DD_LIST_FOREACH(ff_list, elem, temp) { + if (temp->handle == handle) + return temp; + } + return NULL; +} + +static bool check_valid_handle(struct ff_info *info) +{ + struct ff_info *temp; + dd_list *elem; + + DD_LIST_FOREACH(ff_list, elem, temp) { + if (temp == info) + break; + } + + if (!temp) + return false; + return true; +} + +static bool check_fd(int *fd) +{ + int ffd; + + if (*fd > 0) + return true; + + ffd = open(ff_path, O_RDWR); + if (ffd <= 0) + return false; + + *fd = ffd; + return true; +} + +static int ff_stop(int fd, struct ff_effect *effect); +static Eina_Bool timer_cb(void *data) +{ + struct ff_info *info = (struct ff_info *)data; + + if (!info) + return ECORE_CALLBACK_CANCEL; + + if (!check_valid_handle(info)) + return ECORE_CALLBACK_CANCEL; + + _I("stop vibration by timer : id(%d)", info->effect.id); + + /* stop previous vibration */ + ff_stop(ff_fd, &info->effect); + + /* reset timer */ + info->timer = NULL; + + return ECORE_CALLBACK_CANCEL; +} + +static int ff_find_device(void) +{ + DIR *dir; + struct dirent *dent; + char ev_path[PATH_MAX]; + unsigned long features[1+FF_MAX/sizeof(unsigned long)]; + int fd, ret; + + dir = opendir(DEV_INPUT); + if (!dir) + return -errno; + + while (1) { + dent = readdir(dir); + if (dent == NULL) + break; + + if (dent->d_type == DT_DIR || + !strstr(dent->d_name, "event")) + continue; + + snprintf(ev_path, sizeof(ev_path), "%s/%s", DEV_INPUT, dent->d_name); + + fd = open(ev_path, O_RDWR); + if (fd < 0) + continue; + + /* get force feedback device */ + memset(features, 0, sizeof(features)); + ret = ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features); + if (ret == -1) { + close(fd); + continue; + } + + if (test_bit(FF_CONSTANT, features)) + _D("%s type : constant", ev_path); + if (test_bit(FF_PERIODIC, features)) + _D("%s type : periodic", ev_path); + if (test_bit(FF_SPRING, features)) + _D("%s type : spring", ev_path); + if (test_bit(FF_FRICTION, features)) + _D("%s type : friction", ev_path); + if (test_bit(FF_RUMBLE, features)) + _D("%s type : rumble", ev_path); + + if (test_bit(FF_RUMBLE, features)) { + memcpy(ff_path, ev_path, strlen(ev_path)); + close(fd); + closedir(dir); + return 0; + } + + close(fd); + } + + closedir(dir); + return -1; +} + +static int ff_init_effect(struct ff_effect *effect) +{ + if (!effect) + return -EINVAL; + + /*Only rumble supported as of now*/ + effect->type = FF_RUMBLE; + effect->replay.length = 0; + effect->replay.delay = 10; + effect->id = -1; + effect->u.rumble.strong_magnitude = 0x8000; + effect->u.rumble.weak_magnitude = 0xc000; + + return 0; +} + +static int ff_set_effect(struct ff_effect *effect, int length, int level) +{ + double magnitude; + + if (!effect) + return -EINVAL; + + magnitude = (double)level/HAPTIC_MODULE_FEEDBACK_MAX; + magnitude *= RUMBLE_MAX_MAGNITUDE; + + _I("info : magnitude(%d) length(%d)", (int)magnitude, length); + + /* set member variables in effect struct */ + effect->u.rumble.strong_magnitude = (int)magnitude; + effect->replay.length = length; /* length millisecond */ + + return 0; +} + +static int ff_play(int fd, struct ff_effect *effect) +{ + struct input_event play; + int ret; + + if (fd < 0 || !effect) { + if (fd < 0) + _E("fail to check fd"); + else + _E("fail to check effect"); + return -EINVAL; + } + + /* upload an effect */ + if (ioctl(fd, EVIOCSFF, effect) == -1) { + _E("fail to ioctl"); + return -errno; + } + + /* play vibration*/ + play.type = EV_FF; + play.code = effect->id; + play.value = 1; /* 1 : PLAY, 0 : STOP */ + + ret = write(fd, (const void *)&play, sizeof(play)); + if (ret == -1) { + _E("fail to write"); + return -errno; + } + + return 0; +} + +static int ff_stop(int fd, struct ff_effect *effect) +{ + struct input_event stop; + int ret; + + if (fd < 0) + return -EINVAL; + + /* Stop vibration */ + stop.type = EV_FF; + stop.code = effect->id; + stop.value = 0; /* 1 : PLAY, 0 : STOP */ + ret = write(fd, (const void *)&stop, sizeof(stop)); + if (ret == -1) + return -errno; + + /* removing an effect from the device */ + if (ioctl(fd, EVIOCRMFF, effect->id) == -1) + return -errno; + + /* reset effect id */ + effect->id = -1; + + return 0; +} + +/* START: Haptic Module APIs */ +static int get_device_count(int *count) +{ + /* suppose there is just one haptic device */ + if (count) + *count = 1; + + return 0; +} + +static int open_device(int device_index, int *device_handle) +{ + struct ff_info *info; + int n; + bool found = false; + dd_list *elem; + + if (!device_handle) + return -EINVAL; + + /* if it is the first element */ + n = DD_LIST_LENGTH(ff_list); + if (n == 0 && !ff_fd) { + _I("First element: open ff driver"); + /* open ff driver */ + ff_fd = open(ff_path, O_RDWR); + if (!ff_fd) { + _E("Failed to open %s : %d", ff_path, errno); + return -errno; + } + } + + /* allocate memory */ + info = calloc(sizeof(struct ff_info), 1); + if (!info) { + _E("Failed to allocate memory : %d", errno); + return -errno; + } + + /* initialize ff_effect structure */ + ff_init_effect(&info->effect); + + if (unique_number == INT_MAX) + unique_number = 0; + + while (found != true) { + ++unique_number; + elem = DD_LIST_FIND(handle_list, (gpointer)(long)unique_number); + if (!elem) + found = true; + } + + info->handle = unique_number; + + /* add info to local list */ + DD_LIST_APPEND(ff_list, info); + DD_LIST_APPEND(handle_list, (gpointer)(long)info->handle); + + *device_handle = info->handle; + return 0; +} + +static int close_device(int device_handle) +{ + struct ff_info *info; + int r, n; + + info = read_from_list(device_handle); + if (!info) + return -EINVAL; + + if (!check_valid_handle(info)) + return -EINVAL; + + if (!check_fd(&ff_fd)) + return -ENODEV; + + /* stop vibration */ + r = ff_stop(ff_fd, &info->effect); + if (r < 0) + _I("already stopped or failed to stop effect : %d", r); + + /* unregister existing timer */ + if (r >= 0 && info->timer) { + _D("device handle %d is closed and timer deleted", device_handle); + ecore_timer_del(info->timer); + info->timer = NULL; + } + + standard_vibrate_close(); + + DD_LIST_REMOVE(handle_list, (gpointer)(long)info->handle); + + safe_free(info->ffinfobuffer); + /* remove info from local list */ + DD_LIST_REMOVE(ff_list, info); + safe_free(info); + + /* if it is the last element */ + n = DD_LIST_LENGTH(ff_list); + if (n == 0 && ff_fd) { + _I("Last element: close ff driver"); + /* close ff driver */ + close(ff_fd); + ff_fd = 0; + } + + return 0; +} + +static int vibrate_monotone(int device_handle, int duration, int feedback, int priority, int *effect_handle) +{ + struct ff_info *info; + int ret; + + info = read_from_list(device_handle); + if (!info) { + _E("fail to check list"); + return -EINVAL; + } + + if (!check_valid_handle(info)) { + _E("fail to check handle"); + return -EINVAL; + } + + if (!check_fd(&ff_fd)) + return -ENODEV; + + /* Zero(0) is the infinitely vibration value */ + if (duration == HAPTIC_MODULE_DURATION_UNLIMITED) + duration = 0; + + /* unregister existing timer */ + if (info->timer) { + ff_stop(ff_fd, &info->effect); + ecore_timer_del(info->timer); + info->timer = NULL; + } + + /* set effect as per arguments */ + ff_init_effect(&info->effect); + ret = ff_set_effect(&info->effect, duration, feedback); + if (ret < 0) { + _E("failed to set effect(duration:%d, feedback:%d) : %d", + duration, feedback, ret); + return ret; + } + + /* play effect as per arguments */ + ret = ff_play(ff_fd, &info->effect); + if (ret < 0) { + _E("failed to play haptic effect(fd:%d id:%d) : %d", + ff_fd, info->effect.id, ret); + return ret; + } + + /* register timer */ + if (duration) { + info->timer = ecore_timer_add(duration/1000.f, timer_cb, info); + if (!info->timer) + _E("Failed to add timer callback"); + } + + _D("device handle %d effect id : %d %dms", device_handle, info->effect.id, duration); + if (effect_handle) + *effect_handle = info->effect.id; + + return 0; +} + +static Eina_Bool _buffer_play(void *cbdata) +{ + struct ff_info *info = (struct ff_info *)cbdata; + struct ff_info_header *header = &info->ffinfobuffer->header; + struct ff_info_data *data = info->ffinfobuffer->data; + int index = info->currentindex; + int play_type = (index < header->ff_info_data_count) ? data[index].type : 0; + int length = (index < header->ff_info_data_count) ? data[index].length : 1; + int ret; + + ff_set_effect(&info->effect, length, 1); + if (play_type != 0) { + _D("Going to play for %d ms", length); + ret = ff_play(ff_fd, &info->effect); + if (ret < 0) + _D("Failed to play the effect %d", ret); + } else { + _D("Going to stop for %d ms", length); + ret = ff_stop(ff_fd, &info->effect); + if (ret < 0) + _D("Failed to stop the effect %d", ret); + } + + if (info->currentindex < header->ff_info_data_count) { + info->currentindex++; + info->timer = ecore_timer_add(length/1000.0f, _buffer_play, info); + } else { + --header->iteration; + if (header->iteration > 0) { + info->currentindex = 0; + info->timer = ecore_timer_add(0.0, _buffer_play, info); + } else + info->timer = NULL; + } + + return ECORE_CALLBACK_CANCEL; +} + +static void print_buffer(const unsigned char *vibe_buffer) +{ + struct ff_info_buffer fb; + int i = 0; + memcpy(&fb.header, vibe_buffer, sizeof(struct ff_info_header)); + memcpy(&fb.data, (unsigned char *)vibe_buffer+sizeof(struct ff_info_header), + sizeof(struct ff_info_data) * fb.header.ff_info_data_count); + _D("\nMagic %x\niteration %d\ncount %d\n", fb.header.magic, + fb.header.iteration, fb.header.ff_info_data_count); + + for (i = 0; i < fb.header.ff_info_data_count; i++) + _D("type %d\nmagn 0x%x\nlen %d\n", fb.data[i].type, + fb.data[i].magnitude, fb.data[i].length); +} + +static int vibrate_custom_buffer(int device_handle, const unsigned char *vibe_buffer, int iteration, int feedback, int priority, int *effect_handle) +{ + struct ff_info *info; + struct ff_info_header *header; + struct ff_info_data *data; + + info = read_from_list(device_handle); + if (!info) + return -EINVAL; + + if (!check_valid_handle(info)) + return -EINVAL; + + if (!check_fd(&ff_fd)) + return -ENODEV; + + if (!info->ffinfobuffer) + info->ffinfobuffer = (struct ff_info_buffer *)calloc(sizeof(struct ff_info_buffer), 1); + if (!info->ffinfobuffer) + return -ENOMEM; + + header = &info->ffinfobuffer->header; + data = info->ffinfobuffer->data; + + memcpy(header, vibe_buffer, sizeof(struct ff_info_header)); + if (header->ff_info_data_count < 0 || header->ff_info_data_count > MAX_DATA) + return -EINVAL; + + memcpy(data, vibe_buffer+sizeof(struct ff_info_header), sizeof(struct ff_info_data) * header->ff_info_data_count); + + info->currentindex = 0; + if (info->timer) + ecore_timer_del(info->timer); + + if (header->iteration > 0) + _buffer_play(info); + + return 0; +} + +static int vibrate_buffer(int device_handle, const unsigned char *vibe_buffer, int iteration, int feedback, int priority, int *effect_handle) +{ + int magic = 0; + + if (!device_handle) + return -EINVAL; + + if (vibe_buffer) + magic = *(int *)vibe_buffer; + + if (magic == FF_INFO_MAGIC) { + print_buffer(vibe_buffer); + return vibrate_custom_buffer(device_handle, vibe_buffer, iteration, feedback, priority, effect_handle); + } else + return vibrate_monotone(device_handle, 300, feedback, priority, effect_handle); +} + +static int stop_device(int device_handle) +{ + struct ff_info *info; + int r; + + info = read_from_list(device_handle); + if (!info) + return -EINVAL; + + if (!check_valid_handle(info)) + return -EINVAL; + + if (!check_fd(&ff_fd)) + return -ENODEV; + + /* stop effect */ + r = ff_stop(ff_fd, &info->effect); + if (r < 0) + _E("failed to stop effect(id:%d) : %d", info->effect.id, r); + + /* unregister existing timer */ + if (r >= 0 && info->timer) { + ecore_timer_del(info->timer); + info->timer = NULL; + } + + return 0; +} + +static int get_device_state(int device_index, int *effect_state) +{ + struct ff_info *info; + dd_list *elem; + int status = false; + + if (!effect_state) + return -EINVAL; + + /* suppose there is just one haptic device */ + DD_LIST_FOREACH(ff_list, elem, info) { + if (info->effect.id >= 0) { + status = true; + break; + } + } + + *effect_state = status; + return 0; +} + +static int create_effect(unsigned char *vibe_buffer, int max_bufsize, haptic_module_effect_element *elem_arr, int max_elemcnt) +{ + _E("Not support feature"); + return -EACCES; +} + +static int get_buffer_duration(int device_handle, const unsigned char *vibe_buffer, int *buffer_duration) +{ + _E("Not support feature"); + return -EACCES; +} + +static int convert_binary(const unsigned char *vibe_buffer, int max_bufsize, const char *file_path) +{ + _E("Not support feature"); + return -EACCES; +} +/* END: Haptic Module APIs */ + +static const struct haptic_plugin_ops default_plugin = { + .get_device_count = get_device_count, + .open_device = open_device, + .close_device = close_device, + .vibrate_monotone = vibrate_monotone, + .vibrate_buffer = vibrate_buffer, + .vibrate_effect = standard_vibrate_effect, + .is_supported = standard_is_supported, + .stop_device = stop_device, + .get_device_state = get_device_state, + .create_effect = create_effect, + .get_buffer_duration = get_buffer_duration, + .convert_binary = convert_binary, +}; + +static bool is_valid(void) +{ + int ret; + + ret = ff_find_device(); + if (ret < 0) { + _E("Do not support standard haptic device"); + return false; + } + + ret = standard_config_parse(); + if (ret < 0) + _E("failed to load standard vibration configuration file : %d", ret); + + _I("Support standard haptic device"); + return true; +} + +static const struct haptic_plugin_ops *load(void) +{ + standard_set_vib_function(&vibrate_monotone); + return &default_plugin; +} + +static const struct haptic_ops std_ops = { + .type = HAPTIC_STANDARD, + .is_valid = is_valid, + .load = load, +}; + +HAPTIC_OPS_REGISTER(&std_ops) diff --git a/systemd/feedbackd.service b/systemd/feedbackd.service new file mode 100644 index 0000000..9b0f42b --- /dev/null +++ b/systemd/feedbackd.service @@ -0,0 +1,18 @@ +[Unit] +Description=System Vibrator Daemon +After=deviced.service + +[Service] +BusName=org.tizen.system.vibrator +SmackProcessLabel=System +ExecStart=/usr/bin/feedbackd +Restart=always +RestartSec=0 +KillSignal=SIGUSR1 +CapabilityBoundingSet=~CAP_MAC_ADMIN +CapabilityBoundingSet=~CAP_MAC_OVERRIDE +User=system_fw +Group=system_fw + +[Install] +WantedBy=multi-user.target -- 2.7.4