--- /dev/null
+/*
+ * feedbackd
+ *
+ * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <glib.h>
+#include <Ecore.h>
+
+#include "core/log.h"
+#include "haptic.h"
+#include "peripheral_io.h"
+#include "peripheral_internal.h"
+#include "standard-vibcore.h"
+
+#define GPIO_I2C_BUS_INDEX 1
+#define MAX_HAPIC 1
+#define DRV2605L_DEFAULT_ADDR 0x5A
+
+#define START_CMD_CODE 1
+#define STOP_CMD_CODE 0
+
+#define DRV2605L_REGISTER_STATUS 0x00
+#define DRV2605L_REGISTER_MODE 0x01
+#define DRV2605L_REGISTER_RTPINPUT 0x02
+#define DRV2605L_REGISTER_LIBRARY 0x03
+#define DRV2605L_REGISTER_WAVESEQ1 0x04
+#define DRV2605L_REGISTER_WAVESEQ2 0x05
+#define DRV2605L_REGISTER_WAVESEQ3 0x06
+#define DRV2605L_REGISTER_WAVESEQ4 0x07
+#define DRV2605L_REGISTER_WAVESEQ5 0x08
+#define DRV2605L_REGISTER_WAVESEQ6 0x09
+#define DRV2605L_REGISTER_WAVESEQ7 0x0A
+#define DRV2605L_REGISTER_WAVESEQ8 0x0B
+#define DRV2605L_REGISTER_GO 0x0C
+
+/**
+ * Mode register
+ */
+#define MODE_INTERNAL_TRIGGER 0
+#define MODE_EXTERNAL_TRIGGER_EDGE 1
+#define MODE_EXTERNAL_TRIGGER_LEVEL 2
+#define MODE_PWM_INPUT_ANALOG_INPUT 3
+#define MODE_AUDIO_TO_VIBE 4
+#define MODE_REALTIME_PLAYBACK 5
+#define MODE_DIAGNOSTICS 6
+#define MODE_AUTO_CALIBRATION 7
+/**
+ * Library register
+ */
+#define EMPTY_LIBRARY 0
+#define TS2200_LIBRARY_A 1
+#define TS2200_LIBRARY_B 2
+#define TS2200_LIBRARY_C 3
+#define TS2200_LIBRARY_D 4
+#define TS2200_LIBRARY_E 5
+#define LRA_LIBRARY_E 6
+#define TS2200_LIBRARY_F 7
+
+static peripheral_i2c_h device_handle = NULL;
+
+static dd_list *handle_list;
+static int unique_number;
+
+static bool state = false;
+//static bool is_playing = false;
+static Ecore_Timer *stop_timer = NULL;
+
+static int gpio_haptic_stop_device(int 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;
+}
+
+/* START: Haptic Module APIs */
+static Eina_Bool gpio_haptic_timer_cb(void *data)
+{
+ int handle = (int)(long)data;
+ bool found;
+
+ _I("stop vibration by timer");
+
+ found = find_from_list(handle);
+ if (!found)
+ return ECORE_CALLBACK_CANCEL;
+
+ if (device_handle == NULL) {
+ if (peripheral_i2c_open(GPIO_I2C_BUS_INDEX, DRV2605L_DEFAULT_ADDR, &device_handle) < PERIPHERAL_ERROR_NONE) {
+ _E("Failed to open I2C");
+ return -EIO;
+ }
+ }
+
+ /* stop playing */
+ peripheral_i2c_write_register_byte(device_handle, DRV2605L_REGISTER_MODE, MODE_INTERNAL_TRIGGER);
+
+ stop_timer = NULL;
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static int gpio_haptic_get_device_count(int *count)
+{
+ _I("HAL: The max number of DRV2605L is %d", (int)MAX_HAPIC);
+ if (count)
+ *count = MAX_HAPIC;
+
+ return 0;
+}
+
+/**
+ * Only one handle
+ * return device_handle
+ */
+static int gpio_haptic_open_device(int device_index, int *handle)
+{
+ int n;
+ bool found = false;
+ dd_list *elem;
+
+ if (!handle)
+ return -EINVAL;
+
+ /* if it is the first element */
+ n = DD_LIST_LENGTH(handle_list);
+ if (n == 0) {
+ _I("Peripheral Device Open");
+ if (device_handle == NULL) {
+ if (peripheral_i2c_open(GPIO_I2C_BUS_INDEX, DRV2605L_DEFAULT_ADDR, &device_handle) < PERIPHERAL_ERROR_NONE) {
+ _E("Failed to open I2C");
+ return -EIO;
+ }
+ }
+ }
+
+ 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);
+
+ *handle = unique_number;
+ return 0;
+}
+
+static int gpio_haptic_close_device(int handle)
+{
+ int r, n;
+ bool found;
+
+ found = find_from_list(handle);
+ if (!found)
+ return -EINVAL;
+
+ if (device_handle == NULL) {
+ if (peripheral_i2c_open(GPIO_I2C_BUS_INDEX, DRV2605L_DEFAULT_ADDR, &device_handle) < PERIPHERAL_ERROR_NONE) {
+ _E("Failed to open I2C");
+ return -EIO;
+ }
+ }
+
+ /* stop vibration */
+ r = gpio_haptic_stop_device(handle);
+ if (r < 0)
+ _I("already stopped or failed to stop effect : %d", r);
+
+ /* unregister existing timer */
+ if (r >= 0) {
+ if (stop_timer) {
+ ecore_timer_del(stop_timer);
+ stop_timer = NULL;
+ }
+ }
+
+ standard_vibrate_close();
+
+ _D("handle %d is closed and timer deleted", handle);
+ DD_LIST_REMOVE(handle_list, (gpointer)(long)handle);
+
+ /* if it is the last element */
+ n = DD_LIST_LENGTH(handle_list);
+ if (n == 0) {
+ _I("Peripheral Device Close");
+ if (device_handle != NULL) {
+ if (peripheral_i2c_close(device_handle) < PERIPHERAL_ERROR_NONE) {
+ _E("Failed to close peripheral I2C");
+ return -EIO;
+ }
+ device_handle = NULL;
+ }
+ }
+ return 0;
+}
+
+static int gpio_haptic_vibrate_monotone(int handle, int duration, int level, int priority, int *e_handle)
+{
+ bool found;
+
+ found = find_from_list(handle);
+ if (!found)
+ return -EINVAL;
+
+ if (device_handle == NULL) {
+ if (peripheral_i2c_open(GPIO_I2C_BUS_INDEX, DRV2605L_DEFAULT_ADDR, &device_handle) < PERIPHERAL_ERROR_NONE) {
+ _E("Failed to open I2C");
+ return -EIO;
+ }
+ }
+
+ /* Zero(0) is the infinitely vibration value */
+ if (duration == HAPTIC_MODULE_DURATION_UNLIMITED)
+ duration = 0;
+
+ if (stop_timer)
+ gpio_haptic_stop_device(handle);
+
+ /* play vibration */
+ peripheral_i2c_write_register_byte(device_handle, DRV2605L_REGISTER_LIBRARY, TS2200_LIBRARY_A);
+ /* continuously vibrate*/
+ peripheral_i2c_write_register_byte(device_handle, DRV2605L_REGISTER_MODE, MODE_REALTIME_PLAYBACK);
+ peripheral_i2c_write_register_byte(device_handle, DRV2605L_REGISTER_RTPINPUT, (uint8_t)level);
+
+ /* register timer */
+ if (duration) {
+ stop_timer = ecore_timer_add(duration/1000.f, gpio_haptic_timer_cb, (void *)(long)handle);
+ if (!stop_timer)
+ _E("Failed to add timer callback");
+ }
+ _D("device handle %d %dms", handle, duration);
+
+ if (e_handle)
+ *e_handle = handle;
+
+ return 0;
+}
+
+static int gpio_haptic_stop_device(int handle)
+{
+ bool found;
+
+ found = find_from_list(handle);
+ if (!found)
+ return -EINVAL;
+
+ if (cur_h_data.handle > 0 && cur_h_data.handle != handle) {
+ _E("Only same handle can stop current vibration");
+ return -EPERM;
+ }
+
+ /* Remove duration_timer for vibrate_effect */
+ standard_vibrate_close();
+
+ if (device_handle == NULL) {
+ if (peripheral_i2c_open(GPIO_I2C_BUS_INDEX, DRV2605L_DEFAULT_ADDR, &device_handle) < PERIPHERAL_ERROR_NONE) {
+ _E("Failed to open I2C");
+ return -EIO;
+ }
+ }
+
+ /* stop playing */
+ peripheral_i2c_write_register_byte(device_handle, DRV2605L_REGISTER_MODE, MODE_INTERNAL_TRIGGER);
+
+ if (stop_timer) {
+ ecore_timer_del(stop_timer);
+ stop_timer = NULL;
+ }
+
+ return 0;
+}
+
+static int gpio_haptic_get_device_state(int index, int *effect_state)
+{
+ if (!effect_state)
+ return -EINVAL;
+
+ *effect_state = state;
+ return 0;
+}
+
+static int gpio_haptic_vibrate_buffer(int handle, const unsigned char *vibe_buffer, int iteration, int feedback, int priority, int *effect_handle)
+{
+ _E("Not support feature");
+ return -EACCES;
+}
+
+static int gpio_haptic_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 gpio_haptic_get_buffer_duration(int handle, const unsigned char *vibe_buffer, int *buffer_duration)
+{
+ _E("Not support feature");
+ return -EACCES;
+}
+
+static int gpio_haptic_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 = gpio_haptic_get_device_count,
+ .open_device = gpio_haptic_open_device,
+ .close_device = gpio_haptic_close_device,
+ .vibrate_monotone = gpio_haptic_vibrate_monotone,
+ .vibrate_buffer = gpio_haptic_vibrate_buffer,
+ .vibrate_effect = standard_vibrate_effect,
+ .is_supported = standard_is_supported,
+ .stop_device = gpio_haptic_stop_device,
+ .get_device_state = gpio_haptic_get_device_state,
+ .create_effect = gpio_haptic_create_effect,
+ .get_buffer_duration = gpio_haptic_get_buffer_duration,
+ .convert_binary = gpio_haptic_convert_binary,
+};
+
+static bool is_valid(void)
+{
+ uint8_t result;
+ peripheral_i2c_h handle;
+ int ret;
+
+ if (peripheral_i2c_open(GPIO_I2C_BUS_INDEX, DRV2605L_DEFAULT_ADDR, &handle) < PERIPHERAL_ERROR_NONE) {
+ _E("Failed to open I2C");
+ state = false;
+ return false;
+ }
+ if(peripheral_i2c_read_register_byte(handle, DRV2605L_REGISTER_STATUS, &result) < PERIPHERAL_ERROR_NONE) {
+ _E("Failed to read peripheral I2C");
+ if (peripheral_i2c_close(handle) < PERIPHERAL_ERROR_NONE)
+ _E("Failed to close peripheral I2C");
+ state = false;
+ return false;
+ }
+ if (peripheral_i2c_close(handle) < PERIPHERAL_ERROR_NONE) {
+ _E("Failed to close peripheral I2C");
+ 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 gpio haptic device");
+ return true;
+}
+
+static const struct haptic_plugin_ops *load(void)
+{
+ _I("gpio haptic device module loaded");
+ standard_set_vib_function(&gpio_haptic_vibrate_monotone);
+ return &default_plugin;
+}
+
+static const struct haptic_ops gpio_haptic_ops = {
+ .is_valid = is_valid,
+ .load = load,
+};
+
+HAPTIC_OPS_REGISTER(&gpio_haptic_ops)