Create initial commit 45/118345/6 accepted/tizen/unified/20170406.055712 submit/tizen/20170406.012359
authorpr.jung <pr.jung@samsung.com>
Fri, 10 Mar 2017 05:26:01 +0000 (14:26 +0900)
committerpr.jung <pr.jung@samsung.com>
Tue, 4 Apr 2017 05:36:44 +0000 (14:36 +0900)
Change-Id: I55eb8f18f19bcfd6467ed40558a4d6b914085965
Signed-off-by: pr.jung <pr.jung@samsung.com>
34 files changed:
CMakeLists.txt [new file with mode: 0755]
LICENSE [new file with mode: 0644]
packaging/feedbackd.manifest [new file with mode: 0644]
packaging/feedbackd.spec [new file with mode: 0644]
scripts/feedbackd.conf [new file with mode: 0644]
src/README.standard [new file with mode: 0644]
src/core/common.c [new file with mode: 0644]
src/core/common.h [new file with mode: 0644]
src/core/config-parser.c [new file with mode: 0644]
src/core/config-parser.h [new file with mode: 0644]
src/core/dbus.c [new file with mode: 0644]
src/core/dbus.h [new file with mode: 0644]
src/core/device-idler.c [new file with mode: 0644]
src/core/device-idler.h [new file with mode: 0644]
src/core/edbus-handler.c [new file with mode: 0644]
src/core/edbus-handler.h [new file with mode: 0644]
src/core/list.h [new file with mode: 0644]
src/core/log.c [new file with mode: 0644]
src/core/log.h [new file with mode: 0644]
src/core/main.c [new file with mode: 0644]
src/haptic/circle.c [new file with mode: 0644]
src/haptic/emulator.c [new file with mode: 0644]
src/haptic/external.c [new file with mode: 0644]
src/haptic/haptic-mobile.conf [new file with mode: 0644]
src/haptic/haptic-module.h [new file with mode: 0644]
src/haptic/haptic-plugin-intf.h [new file with mode: 0644]
src/haptic/haptic-wearable.conf [new file with mode: 0644]
src/haptic/haptic.c [new file with mode: 0644]
src/haptic/haptic.h [new file with mode: 0644]
src/haptic/standard-mix.c [new file with mode: 0644]
src/haptic/standard-vibcore.c [new file with mode: 0644]
src/haptic/standard-vibcore.h [new file with mode: 0644]
src/haptic/standard.c [new file with mode: 0644]
systemd/feedbackd.service [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..9c80b35
--- /dev/null
@@ -0,0 +1,78 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(feedbackd C)
+
+
+########################################################
+# Deviced CMakeLists.txt
+########################################################
+SET(VERSION 0.1.0)
+
+IF("${PROFILE}" STREQUAL "wearable")
+       IF("${ARCH}" STREQUAL "arm")
+               SET(CIRCLE "on")
+       ENDIF()
+ENDIF()
+
+SET(SRCS
+       src/core/main.c
+       src/core/edbus-handler.c
+       src/core/common.c
+       src/core/config-parser.c
+       src/core/device-idler.c
+       src/core/log.c
+       src/core/dbus.c
+       src/haptic/haptic.c
+       src/haptic/external.c
+       src/haptic/emulator.c
+)
+IF(CIRCLE STREQUAL on)
+       SET(SRCS ${SRCS} src/haptic/circle.c src/haptic/standard-vibcore.c)
+ELSE()
+       IF(STANDARD_MIX STREQUAL on)
+               SET(SRCS ${SRCS} src/haptic/standard-mix.c)
+       ELSE()
+               SET(SRCS ${SRCS} src/haptic/standard.c src/haptic/standard-vibcore.c)
+       ENDIF()
+ENDIF()
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src)
+
+SET(PKG_MODULES
+       ecore
+       edbus
+       dbus-1
+       vconf
+       dlog
+       capi-base-common
+       gio-2.0
+       capi-system-info
+)
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs2 REQUIRED ${PKG_MODULES})
+
+FOREACH(flag ${pkgs2_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -Werror")
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -fno-omit-frame-pointer -finstrument-functions")
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -lrt -fPIE")
+SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie")
+
+ADD_DEFINITIONS("-DDEBUG")
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs2_LDFLAGS} "-ldl" "-lm")
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin)
+
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/scripts/feedbackd.conf DESTINATION /etc/dbus-1/system.d)
+INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/systemd/ DESTINATION lib/systemd/system
+       FILES_MATCHING
+       PATTERN "feedbackd.service")
+
+IF(PROFILE STREQUAL wearable)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/haptic/haptic-wearable.conf DESTINATION /etc/feedbackd)
+ELSE()
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/haptic/haptic-mobile.conf DESTINATION /etc/feedbackd)
+ENDIF()
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..8f17f50
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,204 @@
+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
diff --git a/packaging/feedbackd.manifest b/packaging/feedbackd.manifest
new file mode 100644 (file)
index 0000000..97e8c31
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+       <request>
+               <domain name="_"/>
+       </request>
+</manifest>
diff --git a/packaging/feedbackd.spec b/packaging/feedbackd.spec
new file mode 100644 (file)
index 0000000..32b5c45
--- /dev/null
@@ -0,0 +1,168 @@
+
+#These options are DEACTIVATED by default.
+%bcond_with wayland
+%bcond_with emulator
+
+%define standard_mix off
+
+Name:       feedbackd
+Summary:    Feedbackd
+Version:    1.0.0
+Release:    1
+Group:      System/Management
+License:    Apache-2.0
+Source0:    %{name}-%{version}.tar.gz
+Source1:    feedbackd.manifest
+
+BuildRequires:  cmake
+BuildRequires:  pkgconfig(ecore)
+BuildRequires:  pkgconfig(vconf)
+BuildRequires:  pkgconfig(dlog)
+BuildRequires:  pkgconfig(edbus)
+BuildRequires:  pkgconfig(capi-base-common)
+BuildRequires:  pkgconfig(dbus-1)
+BuildRequires:  pkgconfig(gio-2.0)
+BuildRequires:  pkgconfig(glib-2.0)
+BuildRequires:  pkgconfig(capi-system-info)
+
+Requires(post): /usr/bin/vconftool
+
+Requires:      %{name}-compat = %{version}-%{release}
+Recommends:    %{name}-profile_mobile = %{version}-%{release}
+
+%description
+feedback daemon
+
+%package feedbackd
+Summary:    feedback daemon
+Group:      main
+
+%description feedbackd
+feedback daemon.
+
+# if profile = mobile or common or undefined
+%package profile_mobile
+Summary:       Deviced binaries targeting mobile profile
+Provides:      %{name}-compat = %{version}-%{release}
+Conflicts:     %{name}-profile_wearable
+%description profile_mobile
+Deviced binaries targeting mobile profile. Required by main deviced package
+# if profile = wearable or common or undefined
+%package profile_wearable
+Summary:       Deviced binaries targeting wearable profile
+Provides:      %{name}-compat = %{version}-%{release}
+Conflicts:     %{name}-profile_mobile
+%description profile_wearable
+Deviced binaries targeting wearable profile. Required by main deviced package
+
+%prep
+%setup -q
+
+%if 0%{?tizen_build_devel_mode} == 1
+%define engineer_mode on
+%else
+%define engineer_mode off
+%endif
+
+# Build per profile
+# if profile = mobile or common or undefined
+mkdir -p build_mobile
+pushd build_mobile
+%cmake .. \
+       -DCMAKE_INSTALL_PREFIX=%{_prefix} \
+       -DPROFILE=mobile \
+       -DSTANDARD_MIX=%{standard_mix} \
+       #eol
+popd
+
+# if profile = wearable or common or undefined
+mkdir -p build_wearable
+pushd build_wearable
+%cmake .. \
+       -DCMAKE_INSTALL_PREFIX=%{_prefix} \
+       -DPROFILE=wearable \
+       -DSTANDARD_MIX=%{standard_mix} \
+       #eol
+popd
+
+%build
+cp %{SOURCE1} .
+
+# Build per profile
+# if profile = mobile or common or undefined
+pushd build_mobile
+make %{?jobs:-j%jobs}
+popd
+
+# if profile = wearable or common or undefined
+pushd build_wearable
+make %{?jobs:-j%jobs}
+popd
+
+%install
+rm -rf %{buildroot}
+# Build per profile
+# if profile = mobile or common or undefined
+pushd build_mobile
+%make_install
+mv %{buildroot}%{_bindir}/feedbackd %{buildroot}%{_bindir}/feedbackd.mobile
+popd
+
+# if profile = wearable or common or undefined
+pushd build_wearable
+%make_install
+mv %{buildroot}%{_bindir}/feedbackd %{buildroot}%{_bindir}/feedbackd.wearable
+popd
+
+%install_service multi-user.target.wants feedbackd.service
+
+%post profile_mobile
+mv %{_bindir}/feedbackd.mobile %{_bindir}/feedbackd
+mv %{_sysconfdir}/feedbackd/haptic-mobile.conf %{_sysconfdir}/feedbackd/haptic.conf 
+
+systemctl daemon-reload
+if [ "$1" == "1" ]; then
+    systemctl restart feedbackd.service
+fi
+
+%preun profile_mobile
+mv %{_bindir}/feedbackd %{_bindir}/feedbackd.mobile
+
+if [ "$1" == "0" ]; then
+    systemctl stop feedbackd.service
+fi
+
+%post profile_wearable
+mv %{_bindir}/feedbackd.wearable %{_bindir}/feedbackd
+mv %{_sysconfdir}/feedbackd/haptic-wearable.conf %{_sysconfdir}/feedbackd/haptic.conf 
+
+systemctl daemon-reload
+if [ "$1" == "1" ]; then
+    systemctl restart feedbackd.service
+fi
+
+%preun profile_wearable
+mv %{_bindir}/feedbackd %{_bindir}/feedbackd.wearable
+
+if [ "$1" == "0" ]; then
+    systemctl stop feedbackd.service
+fi
+
+%files -n feedbackd 
+%manifest %{name}.manifest
+%license LICENSE
+%config %{_sysconfdir}/dbus-1/system.d/feedbackd.conf
+%{_unitdir}/feedbackd.service
+%{_unitdir}/multi-user.target.wants/feedbackd.service
+
+%files profile_mobile
+%license LICENSE
+%manifest %{name}.manifest
+%config %{_sysconfdir}/feedbackd/haptic-mobile.conf
+%{_bindir}/feedbackd.mobile
+
+%files profile_wearable
+%license LICENSE
+%manifest %{name}.manifest
+%config %{_sysconfdir}/feedbackd/haptic-wearable.conf
+%{_bindir}/feedbackd.wearable
diff --git a/scripts/feedbackd.conf b/scripts/feedbackd.conf
new file mode 100644 (file)
index 0000000..5b70ebb
--- /dev/null
@@ -0,0 +1,19 @@
+<!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>
diff --git a/src/README.standard b/src/README.standard
new file mode 100644 (file)
index 0000000..52cf000
--- /dev/null
@@ -0,0 +1,13 @@
+Standard Force Feedback Information
+
+1. Max effect id is 16.
+   It depends on force feedback driver.
+
+2. Do not increase effect id in case of repeated requests before previous effect finishs.
+   Standard Force Feedback does not reset the effect id on ff_set_effect function.
+   If the effect id is reseted as -1,
+   effect id will be increased until removing the effect from device by ioctl(EVIOCRMFF).
+   In this case Standard Force Feedback can not remove the previous effect id.
+
+3. Do not support to mix each effect.
+   If the request is occurred on the same time, it plays effect as per the last request.
diff --git a/src/core/common.c b/src/core/common.c
new file mode 100644 (file)
index 0000000..4bd4be8
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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;
+}
diff --git a/src/core/common.h b/src/core/common.h
new file mode 100644 (file)
index 0000000..9978bf4
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __FEEDBACKD_COMMON_H__
+#define __FEEDBACKD_COMMON_H__
+
+#include <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__ */
+
diff --git a/src/core/config-parser.c b/src/core/config-parser.c
new file mode 100644 (file)
index 0000000..ca88496
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2013 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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;
+}
+
diff --git a/src/core/config-parser.h b/src/core/config-parser.h
new file mode 100644 (file)
index 0000000..71fa379
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2013 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __FEEDBACKD_CONFIG_PARSER_H__
+#define __FEEDBACKD_CONFIG_PARSER_H__
+
+#define MATCH(a, b)            (!strncmp(a, b, strlen(a)))
+#define SET_CONF(a, b)         (a = (b > 0.0 ? b : a))
+
+struct parse_result {
+       char *section;
+       char *name;
+       char *value;
+};
+
+/**
+ * @brief Parse config file and call callback\n
+ * @param[in] file_name conf file.
+ * @param[in] cb cb is called when conf file is parsed line by line.
+ * @param[in] user_data user data is passed to cb.
+ * @return 0 on success, negative if failed
+ */
+int config_parse(const char *file_name, int cb(struct parse_result *result,
+       void *user_data), void *user_data);
+
+#endif
+
diff --git a/src/core/dbus.c b/src/core/dbus.c
new file mode 100644 (file)
index 0000000..c8845f0
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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, &param[i]);
+                       break;
+               case 'a':
+                       ++ch;
+                       switch (*ch) {
+                       case 'y':
+                               ++i;
+                               dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &arr);
+                               byte = (struct dbus_byte *)param[i];
+                               dbus_message_iter_append_fixed_array(&arr, DBUS_TYPE_BYTE, &(byte->data), byte->size);
+                               dbus_message_iter_close_container(iter, &arr);
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+DBusMessage *dbus_method_sync_with_reply(const char *dest, const char *path,
+               const char *interface, const char *method,
+               const char *sig, char *param[])
+{
+       DBusConnection *conn;
+       DBusMessage *msg;
+       DBusMessageIter iter;
+       DBusMessage *reply;
+       DBusError err;
+       int r;
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (!conn) {
+               _E("dbus_bus_get error");
+               return NULL;
+       }
+
+       msg = dbus_message_new_method_call(dest, path, interface, method);
+       if (!msg) {
+               _E("dbus_message_new_method_call(%s:%s-%s)",
+                       path, interface, method);
+               return NULL;
+       }
+
+       dbus_message_iter_init_append(msg, &iter);
+       r = append_variant(&iter, sig, param);
+       if (r < 0) {
+               _E("append_variant error(%d) %s %s:%s-%s",
+                       r, dest, path, interface, method);
+               dbus_message_unref(msg);
+               return NULL;
+       }
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, DBUS_REPLY_TIMEOUT, &err);
+       if (!reply) {
+               _E("dbus_connection_send error(No reply) %s %s:%s-%s",
+                       dest, path, interface, method);
+       }
+
+       if (dbus_error_is_set(&err)) {
+               _E("dbus_connection_send error(%s:%s) %s %s:%s-%s",
+                       err.name, err.message, dest, path, interface, method);
+               dbus_error_free(&err);
+               reply = NULL;
+       }
+
+       dbus_message_unref(msg);
+       return reply;
+}
+
+static void __CONSTRUCTOR__ dbus_init(void)
+{
+       dbus_threads_init_default();
+}
diff --git a/src/core/dbus.h b/src/core/dbus.h
new file mode 100644 (file)
index 0000000..4fb1436
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __DBUS_H__
+#define __DBUS_H__
+
+#include <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
diff --git a/src/core/device-idler.c b/src/core/device-idler.c
new file mode 100644 (file)
index 0000000..2d58b63
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2015 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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;
+}
diff --git a/src/core/device-idler.h b/src/core/device-idler.h
new file mode 100644 (file)
index 0000000..264cee3
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2015 - 2017 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __FEEDBACKD_DEVICE_IDLER_H__
+#define __FEEDBACKD_DEVICE_IDLER_H__
+
+/*
+ * To allow for callbacks to be called when the daemon is idle state.
+ */
+
+int add_idle_request(void (*func)(void *data), void *data);
+
+#endif /* __FEEDBACKD_DEVICE_IDLER_H__ */
diff --git a/src/core/edbus-handler.c b/src/core/edbus-handler.c
new file mode 100644 (file)
index 0000000..b1f62d3
--- /dev/null
@@ -0,0 +1,837 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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();
+}
diff --git a/src/core/edbus-handler.h b/src/core/edbus-handler.h
new file mode 100644 (file)
index 0000000..b1ec0ed
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __FEEDBACKD_EDBUS_HANDLE_H__
+#define __FEEDBACKD_EDBUS_HANDLE_H__
+
+#include <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__ */
diff --git a/src/core/list.h b/src/core/list.h
new file mode 100644 (file)
index 0000000..1ef40b4
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FEEDBACKD_LIST_H__
+#define __FEEDBACKD_LIST_H__
+
+#include <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
diff --git a/src/core/log.c b/src/core/log.c
new file mode 100644 (file)
index 0000000..7b9a583
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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
diff --git a/src/core/log.h b/src/core/log.h
new file mode 100644 (file)
index 0000000..87b5481
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __FEEDBACKD_LOG_H__
+#define __FEEDBACKD_LOG_H__
+
+#include <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__ */
diff --git a/src/core/main.c b/src/core/main.c
new file mode 100644 (file)
index 0000000..8ae1fb9
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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;
+}
diff --git a/src/haptic/circle.c b/src/haptic/circle.c
new file mode 100644 (file)
index 0000000..d78ddba
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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)
diff --git a/src/haptic/emulator.c b/src/haptic/emulator.c
new file mode 100644 (file)
index 0000000..db5492a
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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)
diff --git a/src/haptic/external.c b/src/haptic/external.c
new file mode 100644 (file)
index 0000000..32eb6b3
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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)
diff --git a/src/haptic/haptic-mobile.conf b/src/haptic/haptic-mobile.conf
new file mode 100644 (file)
index 0000000..84271f1
--- /dev/null
@@ -0,0 +1,24 @@
+[Haptic]
+# level
+#   how much does the vibration level to subdivide.
+#   The max value of vibration level fixed at 100.
+#   The min value of vibration level fixed at 0.
+level=6
+
+[level0]
+value=0
+
+[level1]
+value=20
+
+[level2]
+value=40
+
+[level3]
+value=60
+
+[level4]
+value=80
+
+[level5]
+value=100
diff --git a/src/haptic/haptic-module.h b/src/haptic/haptic-module.h
new file mode 100644 (file)
index 0000000..9b50bd1
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * deviced
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __FEEDBACKD_HAPTIC_MODULE_H__
+#define __FEEDBACKD_HAPTIC_MODULE_H__
+
+/**
+ * @brief Enumerations of device id for the Haptic Module API.
+ * @details We support two motors now.
+ */
+typedef enum {
+       HAPTIC_MODULE_DEVICE_0 = 0x0,             /**< 1st motor */
+       HAPTIC_MODULE_DEVICE_1 = 0x1,             /**< 2nd motor */
+       HAPTIC_MODULE_DEVICE_ALL = 0x4,           /**< both of them */
+} haptic_module_device;
+
+/**
+ * @brief Enumerations of priority level for the Haptic Module API.
+ */
+typedef enum {
+       HAPTIC_MODULE_PRIORITY_MIN = 0,        /**< Minimum effect priority for developers (default) */
+       HAPTIC_MODULE_PRIORITY_MIDDLE,         /**< Maximum effect priority for developers */
+       HAPTIC_MODULE_PRIORITY_HIGH,           /**< Maximum effect priority for OEMs */
+} haptic_module_priority;
+
+/**
+ * @brief Enumerations of feedback level for the Haptic Module API.
+ * @details Haptic level means vibration power (intensity).
+ */
+typedef enum {
+       HAPTIC_MODULE_FEEDBACK_MIN = 0,
+       HAPTIC_MODULE_FEEDBACK_MAX = 100,
+} haptic_module_feedback;
+
+/**
+ * @brief Enumerations of unlimited duration for the Haptic Module API.
+ */
+typedef enum {
+       HAPTIC_MODULE_DURATION_UNLIMITED = 0x7FFFFFFF,
+} haptic_module_duration;
+
+/**
+ * @brief Enumerations of iteration count for the Haptic Module API.
+ */
+typedef enum {
+       HAPTIC_MODULE_ITERATION_ONCE = 1,
+       HAPTIC_MODULE_ITERATION_INFINITE = 256,
+} haptic_module_iteration;
+
+/**
+ * @brief Enumerations of effect or device state for the Haptic Module API.
+ */
+typedef enum {
+       HAPTIC_MODULE_STATE_STOP = 0,
+       HAPTIC_MODULE_STATE_PLAYING,
+} haptic_module_state;
+
+/* Error and Return value codes */
+#define HAPTIC_MODULE_ERROR_NONE                                                0
+#define HAPTIC_MODULE_NOT_INITIALIZED                                          -1
+#define HAPTIC_MODULE_ALREADY_INITIALIZED                                      -2
+#define HAPTIC_MODULE_INVALID_ARGUMENT                                         -3
+#define HAPTIC_MODULE_OPERATION_FAILED                                         -4
+#define HAPTIC_MODULE_NOT_SUPPORTED                                            -5
+
+/**
+ * @par Description:
+ *      effect element for haptic module.
+ */
+typedef struct {
+       int haptic_duration;           /**< Start time of the effect element in millisecond */
+       int haptic_level;        /**< Duration of the effect element in millisecond */
+} haptic_module_effect_element;
+
+#endif  /* __FEEDBACKD_HAPTIC_MODULE_H__ */
diff --git a/src/haptic/haptic-plugin-intf.h b/src/haptic/haptic-plugin-intf.h
new file mode 100644 (file)
index 0000000..c507397
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * deviced
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __FEEDBACKD_HAPTIC_PLUGIN_INTF_H__
+#define __FEEDBACKD_HAPTIC_PLUGIN_INTF_H__
+
+#include "haptic-module.h"
+
+struct haptic_plugin_ops {
+       int (*get_device_count) (int*);
+       int (*open_device) (int, int*);
+       int (*close_device) (int);
+       int (*vibrate_monotone) (int, int, int, int, int*);
+       int (*vibrate_buffer) (int, const unsigned char*, int, int, int, int*);
+       int (*vibrate_effect) (int, const char*, int, int);
+       int (*is_supported) (const char*);
+       int (*stop_device) (int);
+       int (*get_device_state) (int, int*);
+       int (*create_effect) (unsigned char*, int, haptic_module_effect_element*, int);
+       int (*get_buffer_duration) (int, const unsigned char*, int*);
+       int (*convert_binary) (const unsigned char*, int, const char*);
+};
+
+const struct haptic_plugin_ops *get_haptic_plugin_interface();
+
+#endif /* __FEEDBACKD_HAPTIC_PLUGIN_INTF_H__ */
diff --git a/src/haptic/haptic-wearable.conf b/src/haptic/haptic-wearable.conf
new file mode 100644 (file)
index 0000000..6e52318
--- /dev/null
@@ -0,0 +1,15 @@
+[Haptic]
+# level
+#   how much does the vibration level to subdivide.
+#   The max value of vibration level fixed at 100.
+#   The min value of vibration level fixed at 0.
+level=3
+
+[level0]
+value=0
+
+[level1]
+value=58
+
+[level2]
+value=100
diff --git a/src/haptic/haptic.c b/src/haptic/haptic.c
new file mode 100644 (file)
index 0000000..1b78083
--- /dev/null
@@ -0,0 +1,974 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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;
+}
diff --git a/src/haptic/haptic.h b/src/haptic/haptic.h
new file mode 100644 (file)
index 0000000..26056a5
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __FEEDBACKD_HAPTIC_H__
+#define __FEEDBACKD_HAPTIC_H__
+
+#include <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__ */
diff --git a/src/haptic/standard-mix.c b/src/haptic/standard-mix.c
new file mode 100644 (file)
index 0000000..40bda76
--- /dev/null
@@ -0,0 +1,1085 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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)
diff --git a/src/haptic/standard-vibcore.c b/src/haptic/standard-vibcore.c
new file mode 100644 (file)
index 0000000..e80d145
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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;
+}
+
diff --git a/src/haptic/standard-vibcore.h b/src/haptic/standard-vibcore.h
new file mode 100644 (file)
index 0000000..9f792b1
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __FEEDBACKD_STANDARD_VIBCORE_H__
+#define __FEEDBACKD_STANDARD_VIBCORE_H__
+
+typedef int (*t_vibrate_monotone)(int device_handle, int duration, int feedback, int priority, int *effect_handle);
+
+int standard_config_parse();
+int standard_is_supported(const char *pattern);
+int standard_vibrate_effect(int device_handle, const char *pattern, int feedback, int priority);
+int standard_set_vib_function(t_vibrate_monotone func);
+int standard_vibrate_close();
+
+#endif  /* __FEEDBACKD_STANDARD_VIBCORE_H__ */
diff --git a/src/haptic/standard.c b/src/haptic/standard.c
new file mode 100644 (file)
index 0000000..008bdcd
--- /dev/null
@@ -0,0 +1,692 @@
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <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)
diff --git a/systemd/feedbackd.service b/systemd/feedbackd.service
new file mode 100644 (file)
index 0000000..9b0f42b
--- /dev/null
@@ -0,0 +1,18 @@
+[Unit]
+Description=System Vibrator Daemon
+After=deviced.service
+
+[Service]
+BusName=org.tizen.system.vibrator
+SmackProcessLabel=System
+ExecStart=/usr/bin/feedbackd
+Restart=always
+RestartSec=0
+KillSignal=SIGUSR1
+CapabilityBoundingSet=~CAP_MAC_ADMIN
+CapabilityBoundingSet=~CAP_MAC_OVERRIDE
+User=system_fw
+Group=system_fw
+
+[Install]
+WantedBy=multi-user.target