ELSEIF(DRIVER STREQUAL emulator)
SET(SRCS ${SRCS} src/haptic/emulator.c)
ELSEIF(DRIVER STREQUAL gpio)
- SET(SRCS ${SRCS} src/haptic/gpio_haptic.c src/haptic/standard-vibcore.c)
+ SET(SRCS ${SRCS} src/haptic/gpio_haptic.c)
ELSEIF(DRIVER STREQUAL standard)
- SET(SRCS ${SRCS} src/haptic/standard.c src/haptic/standard-vibcore.c)
+ SET(SRCS ${SRCS} src/haptic/standard.c)
ELSEIF(DRIVER STREQUAL circle)
- SET(SRCS ${SRCS} src/haptic/circle.c src/haptic/standard-vibcore.c)
+ SET(SRCS ${SRCS} src/haptic/circle.c)
ENDIF()
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src)
#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"
return -EPERM;
}
- /* Remove duration_timer for vibrate_effect */
- standard_vibrate_close();
-
if (fd_stop < 0) {
fd_stop = open(CIRCLE_OFF_PATH, O_RDONLY);
if (fd_stop < 0) {
.get_device_count = get_device_count,
.open_device = open_device,
.close_device = close_device,
- .vibrate_monotone = standard_vibrate_monotone,
- .vibrate_effect = standard_vibrate_effect,
- .is_supported = standard_is_supported,
+ .vibrate_monotone = vibrate_monotone,
.stop_device = stop_device,
};
return false;
}
- standard_config_parse();
-
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;
}
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;
return 0;
}
-
-static int is_supported(const char *pattern)
-{
- return 0;
-}
/* END: Haptic Module APIs */
static const struct haptic_plugin_ops default_plugin = {
.open_device = open_device,
.close_device = close_device,
.vibrate_monotone = vibrate_monotone,
- .vibrate_effect = vibrate_effect,
- .is_supported = is_supported,
.stop_device = stop_device,
};
#include "core/log.h"
#include "haptic.h"
#include "peripheral_io.h"
-#include "standard-vibcore.h"
#define GPIO_I2C_BUS_INDEX 1
#define MAX_HAPIC 1
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");
.get_device_count = gpio_haptic_get_device_count,
.open_device = gpio_haptic_open_device,
.close_device = gpio_haptic_close_device,
- .vibrate_monotone = standard_vibrate_monotone,
- .vibrate_effect = standard_vibrate_effect,
- .is_supported = standard_is_supported,
+ .vibrate_monotone = gpio_haptic_vibrate_monotone,
.stop_device = gpio_haptic_stop_device,
};
return false;
}
- standard_config_parse();
-
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;
}
int (*open_device) (int, int*);
int (*close_device) (int);
int (*vibrate_monotone) (int, int, int, int);
- int (*vibrate_effect) (int, const char*, int, int);
- int (*is_supported) (const char*);
int (*stop_device) (int);
};
#include "core/config-parser.h"
#include "haptic.h"
+#define FEEDBACK_BASE_PATH "/usr/share/feedback/"
+#define STANDARD_FILE_PATH FEEDBACK_BASE_PATH"vibration/"
+#define VIBRATION_CONF_PATH FEEDBACK_BASE_PATH"vibration.conf"
+
#define HAPTIC_CONF_PATH "/etc/feedbackd/haptic.conf"
#define SIGNAL_CHANGE_HARDKEY "ChangeHardkey"
#define SIGNAL_POWEROFF_STATE "ChangeState"
#define CHECK_VALID_OPS(ops, r) ((ops) ? true : !(r = -ENODEV))
#define RETRY_CNT 3
+#define INTENSITY_BASE_RATE (10000)
+#define VALUE_MAX_LEN 10
+#define VIB_LOCK_TIMEOUT_MAX (300000) /* 5minutes */
+
+/*
+ 1,A_W or A,A_W or 250D250W250D750W
+
+ 85,10000,
+ 90,0,
+ 105,10000,
+ 0,end
+*/
+struct vibration_config {
+ char *pattern; /* pattern name */
+ char *standard; /* assigned standard pattern name */
+ dd_list *data; /* duration_data list */
+ unsigned int pattern_duration;
+ int unlimit;
+};
+
+struct duration_data {
+ int duration;
+ int intensity;
+ int wait;
+};
+
enum poweroff_type {
POWER_OFF_NONE = 0,
POWER_OFF_POPUP,
/* for playing */
static int g_handle;
+/* pattern configuration list */
+static dd_list *vib_conf_list;
+
+guint duration_timer;
+
/* haptic operation variable */
static dd_list *h_head;
static dd_list *haptic_handle_list;
DD_LIST_REMOVE(h_head, (void *)ops);
}
+static int insert_conf_data(dd_list **conf_data, struct duration_data *update)
+{
+ struct duration_data *data;
+
+ data = (struct duration_data *)calloc(1, sizeof(struct duration_data));
+ if (!data) {
+ _E("not enough memory");
+ return -ENOMEM;
+ }
+ memcpy(data, update, sizeof(struct duration_data));
+ /* insert vibration pattern data */
+ // to debug :
+ // _D("%dD%dI%dF%dO%dW", data->duration, data->intensity, data->frequency, data->overdriving, data->wait);
+ DD_LIST_APPEND(*conf_data, data);
+ return 0;
+}
+
+static void get_pattern_property(char **iter, char property, int *value)
+{
+ char *check;
+ unsigned long int val;
+
+ check = strchr(*iter, property);
+ if (!check)
+ return;
+
+ *check = '\0';
+ val = strtoul(*iter, NULL, 10);
+ if (errno == EINVAL || errno == ERANGE) {
+ val = 0;
+ _E("Failed to get value of %s: %d", *iter, errno);
+ }
+ if (val > VIB_LOCK_TIMEOUT_MAX)
+ val = VIB_LOCK_TIMEOUT_MAX;
+
+ *value = (int)val;
+
+ *iter = check + 1;
+}
+
+/* [A]xxxDxxxIxxxFxxxOxxxW format */
+static int insert_raw_data_format(dd_list **conf_data, char *value)
+{
+ struct duration_data update = {0, };
+ char *iter;
+ char *end;
+ int pattern_duration = 0;
+
+ if (!value)
+ return insert_conf_data(conf_data, &update);
+ if (!conf_data) {
+ _E("Invalid parameter: Configuration list is null");
+ return -EINVAL;
+ }
+
+ iter = value;
+ end = iter + strlen(iter);
+ while (iter < end) {
+ memset(&update, 0, sizeof(struct duration_data));
+
+ get_pattern_property(&iter, 'D', &update.duration);
+ get_pattern_property(&iter, 'I', &update.intensity);
+ if (update.intensity > INTENSITY_BASE_RATE)
+ update.intensity = INTENSITY_BASE_RATE;
+ get_pattern_property(&iter, 'W', &update.wait);
+
+ if (update.duration == 0 && update.wait == 0) {
+ _D("Pattern duration is zero.");
+ break;
+ }
+
+ pattern_duration += (update.duration + update.wait);
+ if (pattern_duration > VIB_LOCK_TIMEOUT_MAX) {
+ _D("Max pattern duration");
+ pattern_duration = VIB_LOCK_TIMEOUT_MAX;
+ }
+
+ if (insert_conf_data(conf_data, &update) < 0)
+ return -EINVAL;
+ }
+
+ return pattern_duration;
+}
+
+static int get_config_data(int count, bool *packed, char *val, unsigned int *pattern_duration, struct duration_data *update)
+{
+ static int duration = 0;
+ int value;
+
+ if (count > 2 || count <= 0) {
+ _E("Invalid parameter: count is invalid");
+ return -EINVAL;
+ }
+
+ if (!packed || !val || !pattern_duration || !update) {
+ _E("Invalid parameter: %p %p %p %p", packed, val, pattern_duration, update);
+ return -EINVAL;
+ }
+
+ get_pattern_property(&val, 'C', &value);
+
+ if (count == 1) {
+ duration = value;
+ *pattern_duration += duration;
+ if (*pattern_duration > VIB_LOCK_TIMEOUT_MAX) {
+ _D("Max pattern duration");
+ *pattern_duration = VIB_LOCK_TIMEOUT_MAX;
+
+ }
+ return count;
+ }
+
+ if (value > INTENSITY_BASE_RATE)
+ value = INTENSITY_BASE_RATE;
+ if (*packed == false) {
+ update->duration = duration;
+ update->intensity = value;
+ *packed = true;
+ return 0;
+ }
+ if (value == 0)
+ update->wait = duration;
+ else
+ update->wait = 0;
+ *packed = false;
+ duration = 0;
+
+ return -1;
+}
+
+/*
+ duration, intensity, frequency, overdriving
+ waiting duration, intensity=0, frequency, overdriving
+ 85,10000,
+ 90,0,
+ 105,10000,
+ 0,end
+*/
+static int load_standard_format(const char *pattern)
+{
+ struct duration_data update = {0, };
+ bool packed = false;
+ struct vibration_config *conf;
+ int ret = 0, count = 0, end = 2;
+ int index = 0;
+ int fd;
+ char path[PATH_MAX], elem, val[VALUE_MAX_LEN] = {0, };
+
+ snprintf(path, sizeof(path), STANDARD_FILE_PATH"%s", pattern);
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -ENOENT;
+
+ conf = (struct vibration_config *)calloc(1, sizeof(struct vibration_config));
+ if (!conf) {
+ _E("fail to alloc");
+ ret = -errno;
+ goto error_out;
+ }
+
+ conf->pattern = strdup(pattern);
+ if (!conf->pattern) {
+ _E("fail to copy %s pattern data", pattern);
+ ret = -errno;
+ goto error_out;
+ }
+
+ /* make feedback pattern(xDxIxFxOxW) format from d,i,f,o, */
+ while (read(fd, &elem, 1) != 0) {
+ if (end == 0) {
+ if (insert_conf_data(&conf->data, &update) < 0)
+ goto error_out;
+ break;
+ }
+ if (elem == 'e' || elem == 'n' || elem == 'd') {
+ end--;
+ continue;
+ }
+ if (elem == '\n') {
+ count = 0;
+ continue;
+ }
+ if (elem == ',') {
+ count++;
+ val[index] = 'C';
+ index = 0;
+
+ ret = get_config_data(count, &packed, val, &(conf->pattern_duration), &update);
+ if (ret < 0) {
+ if (ret == -EINVAL)
+ break;
+ if (insert_conf_data(&conf->data, &update) < 0)
+ goto error_out;
+ memset(&update, 0, sizeof(struct duration_data));
+ } else
+ count = ret;
+ } else {
+ if (index < (VALUE_MAX_LEN - 2)) /* Temporal limit */
+ val[index++] = elem;
+ else
+ _E("Pattern %s is out of bound: %s", pattern, val);
+ }
+ }
+ close(fd);
+ DD_LIST_APPEND(vib_conf_list, conf);
+ return ret;
+
+error_out:
+ if (fd >= 0)
+ close(fd);
+ if (conf) {
+ if (conf->pattern)
+ free(conf->pattern);
+ free(conf);
+ }
+ return -ENOENT;
+}
+
+static int vibration_load_config(struct parse_result *result, void *user_data)
+{
+ struct vibration_config *conf;
+ char *value;
+ char *check;
+ int len;
+ int duration;
+
+ 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_duration = 0;
+ conf->pattern = strdup(result->name);
+ if (!conf->pattern) {
+ _E("fail to copy %s pattern data", result->name);
+ goto error_out;
+ }
+
+ value = result->value;
+ len = strlen(value);
+
+ if (len == 0) {
+ if (insert_raw_data_format(&conf->data, NULL) < 0)
+ goto error_out;
+ DD_LIST_APPEND(vib_conf_list, conf);
+ return 0;
+ }
+
+ /* Load Standard Pattern Name */
+ /* value: 1,A_W or A,A_W */
+ if ((check = strchr(value, ','))) {
+ *check = '\0';
+ if (strncmp(value, "A", 1) == 0)
+ conf->unlimit = 1;
+ value = check + 1;
+ conf->standard = strdup(value);
+ if (!conf->standard) {
+ _E("fail to copy standard name");
+ goto error_out;
+ }
+ DD_LIST_APPEND(vib_conf_list, conf);
+ return 0;
+ }
+
+ /* value : A100D or 100D0W or 250D250W250D750W */
+ /* Load Vibration Pattern Data */
+ check = strchr(value, 'A');
+ if (check) {
+ *check = '\0';
+ conf->unlimit = 1;
+ if (!value)
+ len = len - 1;
+ else
+ len = len - strlen(value) - 1;
+ value = check + 1;
+ }
+ duration = insert_raw_data_format(&conf->data, value);
+ if (duration < 0) {
+ conf->pattern_duration = 0;
+ goto error_out;
+ } else
+ conf->pattern_duration = duration;
+ DD_LIST_APPEND(vib_conf_list, conf);
+
+ return 0;
+
+error_out:
+ if (conf) {
+ if (conf->pattern)
+ free(conf->pattern);
+ if (conf->standard)
+ free(conf->standard);
+ }
+ return -ENOMEM;
+}
+
+static void load_standard_vibration_patterns(void)
+{
+ DIR *dir;
+ struct dirent *dent;
+ int ret;
+
+ dir = opendir(STANDARD_FILE_PATH);
+ if (!dir) {
+ _E("Failed to load %s Use default value!", STANDARD_FILE_PATH);
+ return;
+ }
+ while ((dent = readdir(dir))) {
+ if (dent->d_type == DT_DIR)
+ continue;
+ ret = load_standard_format(dent->d_name);
+ if (ret < 0)
+ _E("Failed to parse %s: %d", dent->d_name, ret);
+ }
+ closedir(dir);
+ _D("Success to load %s", STANDARD_FILE_PATH);
+}
+
+void pattern_config_parse(void)
+{
+ int ret;
+
+ ret = config_parse(VIBRATION_CONF_PATH, vibration_load_config, NULL);
+ if (ret < 0)
+ _E("Failed to load %s, %d Use default value!", VIBRATION_CONF_PATH, ret);
+
+ load_standard_vibration_patterns();
+}
+
static int haptic_module_load(void)
{
struct haptic_ops *ops;
dd_list *elem;
int r;
+ pattern_config_parse();
/* find valid plugin */
DD_LIST_FOREACH(h_head, elem, ops) {
if (ops->is_valid && ops->is_valid()) {
return g_variant_new("(i)", ret);
}
+int clear_current_data(void)
+{
+ cur_h_data.handle = INVALID_HANDLE;
+ cur_h_data.priority = PRIORITY_MIN;
+
+ if (duration_timer) {
+ _I("Remove duration_timer");
+ g_source_remove(duration_timer);
+ duration_timer = 0;
+ }
+
+ return 0;
+}
+
GVariant *hdbus_close_device(GDBusConnection *conn,
const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
if (ret < 0)
goto exit;
+ if (cur_h_data.handle == handle) {
+ /* Remove duration_timer for vibrate_effect */
+ clear_current_data();
+ }
+
info = get_matched_haptic_info(sender);
if (!info) {
_E("fail to find the matched haptic info.");
static void vibrate_monotone_idler_cb(void *data)
{
struct vibrate_monotone_info *vibrate_info;
+ int ret;
if (!data)
return;
vibrate_info = (struct vibrate_monotone_info *)data;
- h_ops->vibrate_monotone(vibrate_info->handle, vibrate_info->duration,
- vibrate_info->level, vibrate_info->priority);
+ if (vibrate_info->priority < cur_h_data.priority) {
+ _I("Handle %d skip low priority(pre:%d now:%d)", vibrate_info->handle, cur_h_data.priority, vibrate_info->priority);
+ free(vibrate_info);
+ return;
+ }
+
+ cur_h_data.vibration_data = NULL;
+ cur_h_data.handle = vibrate_info->handle;
+ cur_h_data.level = vibrate_info->level;
+ cur_h_data.priority = vibrate_info->priority;
+ cur_h_data.stop = false;
+ cur_h_data.unlimit = false;
+
+ ret = device_power_request_lock(POWER_LOCK_CPU, vibrate_info->duration);
+ if (ret != DEVICE_ERROR_NONE)
+ _E("Failed to request power lock");
+ h_ops->vibrate_monotone(cur_h_data.handle, vibrate_info->duration, cur_h_data.level, cur_h_data.priority);
+
free(vibrate_info);
}
return g_variant_new("(i)", ret);
}
+static gboolean haptic_duration_play(void *data)
+{
+ dd_list *head, *n, *next;
+ struct duration_data *node;
+ int level;
+ int ret = 0;
+
+ if (duration_timer) {
+ g_source_remove(duration_timer);
+ duration_timer = 0;
+ }
+
+ if (!data) {
+ if (cur_h_data.unlimit) /* In case of unlimit pattern, do not stop */
+ head = cur_h_data.vibration_data;
+ else {
+ cur_h_data.handle = INVALID_HANDLE;
+ cur_h_data.priority = PRIORITY_MIN;
+ goto out;
+ }
+ } else
+ head = (dd_list *)data;
+
+ if (cur_h_data.stop) {
+ _I("Stop currunt vibration");
+ cur_h_data.stop = false;
+ cur_h_data.handle = INVALID_HANDLE;
+ cur_h_data.priority = PRIORITY_MIN;
+ goto out;
+ }
+
+ DD_LIST_FOREACH_SAFE(head, n, next, node) {
+ _D("Handle %d play: %dms and Wait: %dms %s type",
+ cur_h_data.handle, node->duration, node->wait,
+ cur_h_data.unlimit ? "Unlimit" : "Once");
+ if ((node->duration + node->wait) <= 0) {
+ if (!cur_h_data.unlimit) {
+ cur_h_data.handle = INVALID_HANDLE;
+ cur_h_data.priority = PRIORITY_MIN;
+ break;
+ } else {
+ next = cur_h_data.vibration_data;
+ continue;
+ }
+ }
+
+ if (node->intensity)
+ level = (cur_h_data.level * node->intensity) / INTENSITY_BASE_RATE;
+ else
+ level = cur_h_data.level;
+
+ duration_timer = g_timeout_add((node->duration + node->wait), haptic_duration_play, (void *)next);
+
+ ret = h_ops->vibrate_monotone(cur_h_data.handle, node->duration, level, cur_h_data.priority);
+
+ break;
+ }
+ if (ret != 0) {
+ _D("auto stop vibration");
+ cur_h_data.stop = true;
+ }
+out:
+ return G_SOURCE_REMOVE;
+}
+
static void vibrate_effect_idler_cb(void *data)
{
struct vibrate_effect_info *vibrate_info;
+ dd_list *elem;
+ struct vibration_config *conf;
+ char pattern[PATH_MAX];
+ int ret;
+ int unlimit = 0;
if (!data)
return;
vibrate_info = (struct vibrate_effect_info *)data;
- h_ops->vibrate_effect(vibrate_info->handle, vibrate_info->pattern,
- vibrate_info->level, vibrate_info->priority);
+ if (!(vibrate_info->pattern)) {
+ free(vibrate_info);
+ return;
+ }
+
+ /* Same or higher priority pattern should be played */
+ if (vibrate_info->priority < cur_h_data.priority) {
+ _I("Handle %d skip low priority(pre:%d now:%d)", vibrate_info->handle, cur_h_data.priority, vibrate_info->priority);
+ goto out;
+ }
+
+ snprintf(pattern, sizeof(pattern), "%s", vibrate_info->pattern);
+ DD_LIST_FOREACH(vib_conf_list, elem, conf) {
+ if (!conf->pattern)
+ continue;
+ if (strcmp(conf->pattern, pattern))
+ continue;
+ if (conf->standard) {
+ unlimit = conf->unlimit;
+ snprintf(pattern, sizeof(pattern), "%s", conf->standard);
+ continue;
+ }
+
+ if (unlimit)
+ cur_h_data.unlimit = unlimit;
+ else
+ cur_h_data.unlimit = conf->unlimit;
+ cur_h_data.vibration_data = conf->data;
+ cur_h_data.handle = vibrate_info->handle;
+ cur_h_data.level = vibrate_info->level;
+ cur_h_data.priority = vibrate_info->priority;
+ cur_h_data.stop = false;
+ _I("Handle %d play %s pri %d %s", cur_h_data.handle, conf->pattern, cur_h_data.priority,
+ cur_h_data.unlimit ? "Unlimit" : "Once");
+
+ if (conf->pattern_duration <= 0)
+ break;
+
+ if (!cur_h_data.unlimit && conf->pattern_duration > 0) {
+ ret = device_power_request_lock(POWER_LOCK_CPU, (int)conf->pattern_duration);
+ if (ret != DEVICE_ERROR_NONE)
+ _E("Failed to request power lock");
+ }
+ haptic_duration_play((void *)cur_h_data.vibration_data);
+ goto out;
+ }
+ _E("Handle %d %s is not supported", vibrate_info->handle, pattern);
+
+out:
free(vibrate_info->pattern);
free(vibrate_info);
}
ret = h_ops->stop_device(handle);
+ /* Remove duration_timer for vibrate_effect */
+ clear_current_data();
+
exit:
return g_variant_new("(i)", ret);
}
return g_variant_new_tuple(NULL, 0);
}
+int pattern_is_supported(const char *pattern)
+{
+ dd_list *elem;
+ struct vibration_config *conf;
+ int ret;
+
+ if (!pattern)
+ return -EINVAL;
+
+ ret = 0;
+ DD_LIST_FOREACH(vib_conf_list, elem, conf) {
+ if (!conf->pattern)
+ continue;
+ if (!strcmp(conf->pattern, pattern)) {
+ ret = true;
+ break;
+ }
+ }
+
+ return ret;
+}
+
GVariant *hdbus_pattern_is_supported(GDBusConnection *conn,
const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
g_variant_get(param, "(s)", &data);
- ret = h_ops->is_supported(data);
+ ret = pattern_is_supported(data);
_I("%s is supported : %d", data, ret);
+++ /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 <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdbool.h>
-
-#include "core/log.h"
-#include "core/list.h"
-#include "core/config-parser.h"
-#include "haptic.h"
-#include "standard-vibcore.h"
-
-#define FEEDBACK_BASE_PATH "/usr/share/feedback/"
-#define STANDARD_FILE_PATH FEEDBACK_BASE_PATH"vibration/"
-#define VIBRATION_CONF_PATH FEEDBACK_BASE_PATH"vibration.conf"
-
-#define INTENSITY_BASE_RATE (10000)
-#define VALUE_MAX_LEN 10
-#define VIB_LOCK_TIMEOUT_MAX (300000) /* 5minutes */
-
-/*
- 1,A_W or A,A_W or 250D250W250D750W
-
- 85,10000,
- 90,0,
- 105,10000,
- 0,end
-*/
-struct vibration_config {
- char *pattern; /* pattern name */
- char *standard; /* assigned standard pattern name */
- dd_list *data; /* duration_data list */
- unsigned int pattern_duration;
- int unlimit;
-};
-
-struct duration_data {
- int duration;
- int intensity;
- int wait;
-};
-
-static dd_list *vib_conf_list;
-static guint duration_timer;
-
-static t_vibrate_monotone real_vibrate_monotone;
-
-static int insert_conf_data(dd_list **conf_data, struct duration_data *update)
-{
- struct duration_data *data;
-
- data = (struct duration_data *)calloc(1, sizeof(struct duration_data));
- if (!data) {
- _E("not enough memory");
- return -ENOMEM;
- }
- memcpy(data, update, sizeof(struct duration_data));
- /* insert vibration pattern data */
- // to debug :
- // _D("%dD%dI%dF%dO%dW", data->duration, data->intensity, data->frequency, data->overdriving, data->wait);
- DD_LIST_APPEND(*conf_data, data);
- return 0;
-}
-
-static void get_pattern_property(char **iter, char property, int *value)
-{
- char *check;
- unsigned long int val;
-
- check = strchr(*iter, property);
- if (!check)
- return;
-
- *check = '\0';
- val = strtoul(*iter, NULL, 10);
- if (errno == EINVAL || errno == ERANGE) {
- val = 0;
- _E("Failed to get value of %s: %d", *iter, errno);
- }
- if (val > VIB_LOCK_TIMEOUT_MAX)
- val = VIB_LOCK_TIMEOUT_MAX;
-
- *value = (int)val;
-
- *iter = check + 1;
-}
-
-/* [A]xxxDxxxIxxxFxxxOxxxW format */
-static int insert_raw_data_format(dd_list **conf_data, char *value)
-{
- struct duration_data update = {0, };
- char *iter;
- char *end;
- int pattern_duration = 0;
-
- if (!value)
- return insert_conf_data(conf_data, &update);
- if (!conf_data) {
- _E("Invalid parameter: Configuration list is null");
- return -EINVAL;
- }
-
- iter = value;
- end = iter + strlen(iter);
- while (iter < end) {
- memset(&update, 0, sizeof(struct duration_data));
-
- get_pattern_property(&iter, 'D', &update.duration);
- get_pattern_property(&iter, 'I', &update.intensity);
- if (update.intensity > INTENSITY_BASE_RATE)
- update.intensity = INTENSITY_BASE_RATE;
- get_pattern_property(&iter, 'W', &update.wait);
-
- if (update.duration == 0 && update.wait == 0) {
- _D("Pattern duration is zero.");
- break;
- }
-
- pattern_duration += (update.duration + update.wait);
- if (pattern_duration > VIB_LOCK_TIMEOUT_MAX) {
- _D("Max pattern duration");
- pattern_duration = VIB_LOCK_TIMEOUT_MAX;
- }
-
- if (insert_conf_data(conf_data, &update) < 0)
- return -EINVAL;
- }
-
- return pattern_duration;
-}
-
-static int get_config_data(int count, bool *packed, char *val, unsigned int *pattern_duration, struct duration_data *update)
-{
- static int duration = 0;
- int value;
-
- if (count > 2 || count <= 0) {
- _E("Invalid parameter: count is invalid");
- return -EINVAL;
- }
-
- if (!packed || !val || !pattern_duration || !update) {
- _E("Invalid parameter: %p %p %p %p", packed, val, pattern_duration, update);
- return -EINVAL;
- }
-
- get_pattern_property(&val, 'C', &value);
-
- if (count == 1) {
- duration = value;
- *pattern_duration += duration;
- if (*pattern_duration > VIB_LOCK_TIMEOUT_MAX) {
- _D("Max pattern duration");
- *pattern_duration = VIB_LOCK_TIMEOUT_MAX;
-
- }
- return count;
- }
-
- if (value > INTENSITY_BASE_RATE)
- value = INTENSITY_BASE_RATE;
- if (*packed == false) {
- update->duration = duration;
- update->intensity = value;
- *packed = true;
- return 0;
- }
- if (value == 0)
- update->wait = duration;
- else
- update->wait = 0;
- *packed = false;
- duration = 0;
-
- return -1;
-}
-
-/*
- duration, intensity, frequency, overdriving
- waiting duration, intensity=0, frequency, overdriving
- 85,10000,
- 90,0,
- 105,10000,
- 0,end
-*/
-static int load_standard_format(const char *pattern)
-{
- struct duration_data update = {0, };
- bool packed = false;
- struct vibration_config *conf;
- int ret = 0, count = 0, end = 2;
- int index = 0;
- int fd;
- char path[PATH_MAX], elem, val[VALUE_MAX_LEN] = {0, };
-
- snprintf(path, sizeof(path), STANDARD_FILE_PATH"%s", pattern);
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return -ENOENT;
-
- conf = (struct vibration_config *)calloc(1, sizeof(struct vibration_config));
- if (!conf) {
- _E("fail to alloc");
- ret = -errno;
- goto error_out;
- }
-
- conf->pattern = strdup(pattern);
- if (!conf->pattern) {
- _E("fail to copy %s pattern data", pattern);
- ret = -errno;
- goto error_out;
- }
-
- /* make feedback pattern(xDxIxFxOxW) format from d,i,f,o, */
- while (read(fd, &elem, 1) != 0) {
- if (end == 0) {
- if (insert_conf_data(&conf->data, &update) < 0)
- goto error_out;
- break;
- }
- if (elem == 'e' || elem == 'n' || elem == 'd') {
- end--;
- continue;
- }
- if (elem == '\n') {
- count = 0;
- continue;
- }
- if (elem == ',') {
- count++;
- val[index] = 'C';
- index = 0;
-
- ret = get_config_data(count, &packed, val, &(conf->pattern_duration), &update);
- if (ret < 0) {
- if (ret == -EINVAL)
- break;
- if (insert_conf_data(&conf->data, &update) < 0)
- goto error_out;
- memset(&update, 0, sizeof(struct duration_data));
- } else
- count = ret;
- } else {
- if (index < (VALUE_MAX_LEN - 2)) /* Temporal limit */
- val[index++] = elem;
- else
- _E("Pattern %s is out of bound: %s", pattern, val);
- }
- }
- close(fd);
- DD_LIST_APPEND(vib_conf_list, conf);
- return ret;
-
-error_out:
- if (fd >= 0)
- close(fd);
- if (conf) {
- if (conf->pattern)
- free(conf->pattern);
- free(conf);
- }
- return -ENOENT;
-}
-
-static int vibration_load_config(struct parse_result *result, void *user_data)
-{
- struct vibration_config *conf;
- char *value;
- char *check;
- int len;
- int duration;
-
- 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_duration = 0;
- conf->pattern = strdup(result->name);
- if (!conf->pattern) {
- _E("fail to copy %s pattern data", result->name);
- goto error_out;
- }
-
- value = result->value;
- len = strlen(value);
-
- if (len == 0) {
- if (insert_raw_data_format(&conf->data, NULL) < 0)
- goto error_out;
- DD_LIST_APPEND(vib_conf_list, conf);
- return 0;
- }
-
- /* Load Standard Pattern Name */
- /* value: 1,A_W or A,A_W */
- if ((check = strchr(value, ','))) {
- *check = '\0';
- if (strncmp(value, "A", 1) == 0)
- conf->unlimit = 1;
- value = check + 1;
- conf->standard = strdup(value);
- if (!conf->standard) {
- _E("fail to copy standard name");
- goto error_out;
- }
- DD_LIST_APPEND(vib_conf_list, conf);
- return 0;
- }
-
- /* value : A100D or 100D0W or 250D250W250D750W */
- /* Load Vibration Pattern Data */
- check = strchr(value, 'A');
- if (check) {
- *check = '\0';
- conf->unlimit = 1;
- if (!value)
- len = len - 1;
- else
- len = len - strlen(value) - 1;
- value = check + 1;
- }
- duration = insert_raw_data_format(&conf->data, value);
- if (duration < 0) {
- conf->pattern_duration = 0;
- goto error_out;
- } else
- conf->pattern_duration = duration;
- DD_LIST_APPEND(vib_conf_list, conf);
-
- return 0;
-
-error_out:
- if (conf) {
- if (conf->pattern)
- free(conf->pattern);
- if (conf->standard)
- free(conf->standard);
- }
- return -ENOMEM;
-}
-
-static void load_standard_vibration_patterns(void)
-{
- DIR *dir;
- struct dirent *dent;
- int ret;
-
- dir = opendir(STANDARD_FILE_PATH);
- if (!dir) {
- _E("Failed to load %s Use default value!", STANDARD_FILE_PATH);
- return;
- }
- while ((dent = readdir(dir))) {
- if (dent->d_type == DT_DIR)
- continue;
- ret = load_standard_format(dent->d_name);
- if (ret < 0)
- _E("Failed to parse %s: %d", dent->d_name, ret);
- }
- closedir(dir);
- _D("Success to load %s", STANDARD_FILE_PATH);
-}
-
-void standard_config_parse(void)
-{
- int ret;
-
- ret = config_parse(VIBRATION_CONF_PATH, vibration_load_config, NULL);
- if (ret < 0)
- _E("Failed to load %s, %d Use default value!", VIBRATION_CONF_PATH, ret);
-
- load_standard_vibration_patterns();
-}
-
-int standard_is_supported(const char *pattern)
-{
- dd_list *elem;
- struct vibration_config *conf;
- int ret;
-
- if (!pattern)
- return -EINVAL;
-
- ret = 0;
- DD_LIST_FOREACH(vib_conf_list, elem, conf) {
- if (!conf->pattern)
- continue;
- if (!strcmp(conf->pattern, pattern)) {
- ret = true;
- break;
- }
- }
-
- return ret;
-}
-
-static gboolean haptic_duration_play(void *data)
-{
- dd_list *head, *n, *next;
- struct duration_data *node;
- int level;
- int ret = 0;
-
- if (duration_timer) {
- g_source_remove(duration_timer);
- duration_timer = 0;
- }
-
- if (!data) {
- if (cur_h_data.unlimit) /* In case of unlimit pattern, do not stop */
- head = cur_h_data.vibration_data;
- else {
- cur_h_data.handle = INVALID_HANDLE;
- cur_h_data.priority = PRIORITY_MIN;
- goto out;
- }
- } else
- head = (dd_list *)data;
-
- if (cur_h_data.stop) {
- _I("Stop currunt vibration");
- cur_h_data.stop = false;
- cur_h_data.handle = INVALID_HANDLE;
- cur_h_data.priority = PRIORITY_MIN;
- goto out;
- }
-
- DD_LIST_FOREACH_SAFE(head, n, next, node) {
- _D("Handle %d play: %dms and Wait: %dms %s type",
- cur_h_data.handle, node->duration, node->wait,
- cur_h_data.unlimit ? "Unlimit" : "Once");
- if ((node->duration + node->wait) <= 0) {
- if (!cur_h_data.unlimit) {
- cur_h_data.handle = INVALID_HANDLE;
- cur_h_data.priority = PRIORITY_MIN;
- break;
- } else {
- next = cur_h_data.vibration_data;
- continue;
- }
- }
-
- if (node->intensity)
- level = (cur_h_data.level * node->intensity) / INTENSITY_BASE_RATE;
- else
- level = cur_h_data.level;
-
- duration_timer = g_timeout_add((node->duration + node->wait), haptic_duration_play, (void *)next);
-
- ret = real_vibrate_monotone(cur_h_data.handle, node->duration, level, cur_h_data.priority);
-
- break;
- }
- if (ret != 0) {
- _D("auto stop vibration");
- cur_h_data.stop = true;
- }
-out:
- return G_SOURCE_REMOVE;
-}
-
-int standard_set_vib_function(t_vibrate_monotone func)
-{
- real_vibrate_monotone = func;
- return 0;
-}
-
-int standard_vibrate_monotone(int device_handle, int duration, int feedback, int priority)
-{
- int ret;
-
- if (priority < cur_h_data.priority) {
- _I("Handle %d skip low priority(pre:%d now:%d)", device_handle, cur_h_data.priority, priority);
- return 0;
- }
-
- cur_h_data.vibration_data = NULL;
- cur_h_data.handle = device_handle;
- cur_h_data.level = feedback;
- cur_h_data.priority = priority;
- cur_h_data.stop = false;
- cur_h_data.unlimit = false;
-
- ret = device_power_request_lock(POWER_LOCK_CPU, duration);
- if (ret != DEVICE_ERROR_NONE)
- _E("Failed to request power lock");
- real_vibrate_monotone(cur_h_data.handle, duration, cur_h_data.level, cur_h_data.priority);
-
- return 0;
-}
-
-int standard_vibrate_effect(int device_handle, const char *requested_pattern, int feedback, int priority)
-{
- dd_list *elem;
- struct vibration_config *conf;
- char pattern[PATH_MAX];
- int ret;
- int unlimit = 0;
-
- if (device_handle < 0 || !requested_pattern)
- return -EINVAL;
-
- /* Same or higher priority pattern should be played */
- if (priority < cur_h_data.priority) {
- _I("Handle %d skip low priority(pre:%d now:%d)", device_handle, cur_h_data.priority, priority);
- return 0;
- }
-
- snprintf(pattern, sizeof(pattern), "%s", requested_pattern);
- DD_LIST_FOREACH(vib_conf_list, elem, conf) {
- if (!conf->pattern)
- continue;
- if (strcmp(conf->pattern, pattern))
- continue;
- if (conf->standard) {
- unlimit = conf->unlimit;
- snprintf(pattern, sizeof(pattern), "%s", conf->standard);
- continue;
- }
-
- if (unlimit)
- cur_h_data.unlimit = unlimit;
- else
- cur_h_data.unlimit = conf->unlimit;
- cur_h_data.vibration_data = conf->data;
- cur_h_data.handle = device_handle;
- cur_h_data.level = feedback;
- cur_h_data.priority = priority;
- cur_h_data.stop = false;
- _I("Handle %d play %s pri %d %s", cur_h_data.handle, conf->pattern, cur_h_data.priority,
- cur_h_data.unlimit ? "Unlimit" : "Once");
-
- if (conf->pattern_duration <= 0)
- break;
-
- if (!cur_h_data.unlimit && conf->pattern_duration > 0) {
- ret = device_power_request_lock(POWER_LOCK_CPU, (int)conf->pattern_duration);
- if (ret != DEVICE_ERROR_NONE)
- _E("Failed to request power lock");
- }
- haptic_duration_play((void *)cur_h_data.vibration_data);
- return 0;
- }
- _E("Handld %d %s is not supported", device_handle, pattern);
-
- return 0;
-}
-
-int standard_vibrate_close(void)
-{
- cur_h_data.handle = INVALID_HANDLE;
- cur_h_data.priority = PRIORITY_MIN;
-
- if (duration_timer) {
- _I("Remove duration_timer");
- g_source_remove(duration_timer);
- duration_timer = 0;
- }
-
- return 0;
-}
-
+++ /dev/null
-/*
- * feedbackd
- *
- * Copyright (c) 2016 - 2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the License);
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef __FEEDBACKD_STANDARD_VIBCORE_H__
-#define __FEEDBACKD_STANDARD_VIBCORE_H__
-
-typedef int (*t_vibrate_monotone)(int device_handle, int duration, int feedback, int priority);
-
-void standard_config_parse(void);
-int standard_is_supported(const char *pattern);
-int standard_vibrate_monotone(int device_handle, int duration, int feedback, int priority);
-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(void);
-
-#endif /* __FEEDBACKD_STANDARD_VIBCORE_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 */
return -EPERM;
}
- /* Remove duration_timer for vibrate_effect */
- standard_vibrate_close();
-
/* stop effect */
r = ff_stop(ff_fd, &info->effect);
if (r < 0)
.get_device_count = get_device_count,
.open_device = open_device,
.close_device = close_device,
- .vibrate_monotone = standard_vibrate_monotone,
- .vibrate_effect = standard_vibrate_effect,
- .is_supported = standard_is_supported,
+ .vibrate_monotone = vibrate_monotone,
.stop_device = stop_device,
};
return false;
}
- standard_config_parse();
-
_I("Support standard haptic device");
return true;
}
static const struct haptic_plugin_ops *load(void)
{
- standard_set_vib_function(&vibrate_monotone);
return &default_plugin;
}