--- /dev/null
+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()
--- /dev/null
+Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.\r
+
+ 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.
+\r
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>
--- /dev/null
+
+#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
--- /dev/null
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy user="root">
+ <allow own="org.tizen.system.vibrator"/>
+ <allow send_destination="org.tizen.system.vibrator"/>
+ </policy>
+
+ <policy context="default">
+ <allow send_destination="org.tizen.system.vibrator"/>
+
+ <deny send_destination="org.tizen.system.vibrator"
+ send_interface="org.tizen.system.vibrator.haptic"/>
+
+ <check send_destination="org.tizen.system.vibrator"
+ send_interface="org.tizen.system.vibrator.haptic"
+ privilege="http://tizen.org/privilege/haptic"/>
+ </policy>
+</busconfig>
--- /dev/null
+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.
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <poll.h>
+#include <mntent.h>
+#include <system_info.h>
+
+#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;
+}
--- /dev/null
+/*
+ * 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 <Ecore.h>
+#include <stdio.h>
+#include <error.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#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__ */
+
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <errno.h>
+#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;
+}
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+
+#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();
+}
--- /dev/null
+/*
+ * 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 <dbus/dbus.h>
+#include <stdarg.h>
+
+/*
+ * 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
--- /dev/null
+/*
+ * 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 <Ecore.h>
+#include <glib.h>
+#include <errno.h>
+
+#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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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 <stdbool.h>
+#include <assert.h>
+
+#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();
+}
--- /dev/null
+/*
+ * 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 <E_DBus.h>
+#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__ */
--- /dev/null
+/*
+ * 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 <glib.h>
+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
--- /dev/null
+/*
+ * 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 <stdio.h>
+#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
--- /dev/null
+/*
+ * 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 <dlog.h>
+
+#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__ */
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <fcntl.h>
+#include <sys/reboot.h>
+
+#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;
+}
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <Ecore.h>
+#include <sys/types.h>
+
+#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)
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <errno.h>
+
+#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)
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdbool.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#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)
--- /dev/null
+[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
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+[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
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdbool.h>
+#include <dlfcn.h>
+#include <assert.h>
+#include <vconf.h>
+#include <time.h>
+
+#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;
+}
--- /dev/null
+/*
+ * 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 <stdbool.h>
+#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__ */
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <linux/input.h>
+#include <Ecore.h>
+
+#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(x))
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define test_bit(bit, array) ((array[LONG(bit)] >> 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)
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include <stdbool.h>
+#include <Ecore.h>
+
+#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;
+}
+
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <linux/input.h>
+#include <Ecore.h>
+
+#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)
--- /dev/null
+[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