diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | hw/haptic/CMakeLists.txt | 25 | ||||
-rw-r--r-- | hw/haptic/gpio.c | 352 |
3 files changed, 378 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 01bda9c..bc89dd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,3 +13,4 @@ ADD_SUBDIRECTORY(hw/usb_gadget) ADD_SUBDIRECTORY(hw/usb_client) ADD_SUBDIRECTORY(hw/usb_cfs_client) ADD_SUBDIRECTORY(hw/thermal) +ADD_SUBDIRECTORY(hw/haptic) diff --git a/hw/haptic/CMakeLists.txt b/hw/haptic/CMakeLists.txt new file mode 100644 index 0000000..f8fac63 --- /dev/null +++ b/hw/haptic/CMakeLists.txt @@ -0,0 +1,25 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(hal-backend-device-haptic C) + +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) + +INCLUDE_DIRECTORIES(../common) + +INCLUDE(FindPkgConfig) +pkg_check_modules(haptic_pkgs REQUIRED + dlog + glib-2.0 + libsyscommon + capi-system-peripheral-io) + +FOREACH(flag ${haptic_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") + +ADD_LIBRARY(${PROJECT_NAME} MODULE gpio.c) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${haptic_pkgs_LDFLAGS}) + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION /hal/lib COMPONENT RuntimeLibraries) diff --git a/hw/haptic/gpio.c b/hw/haptic/gpio.c new file mode 100644 index 0000000..67ba19a --- /dev/null +++ b/hw/haptic/gpio.c @@ -0,0 +1,352 @@ +/* + * 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 <peripheral_io.h> +#include <libsyscommon/list.h> +#include <hal/device/hal-haptic-interface.h> + +#include "common.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 + +#define MAX_LEVEL 2 +#define MAX_INTENSITY 10000 + +static peripheral_i2c_h device_handle = NULL; + +static GList *handle_list; +static int unique_number; + +static bool state = false; +static guint stop_timer = 0; + +static int gpio_haptic_stop_device(int handle); + +static bool find_from_list(int handle) +{ + GList *elem; + + elem = SYS_G_LIST_FIND(handle_list, (gpointer)(long)handle); + if (elem) + return true; + + return false; +} + +static gboolean 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 G_SOURCE_REMOVE; + + 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 = 0; + return G_SOURCE_REMOVE; +} + +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 *handle) +{ + int n; + bool found = false; + GList *elem; + + if (!handle) + return -EINVAL; + + /* if it is the first element */ + n = SYS_G_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 = SYS_G_LIST_FIND(handle_list, (gpointer)(long)unique_number); + if (!elem) + found = true; + } + + /* add info to local list */ + SYS_G_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); + + _D("Handle(%d) is closed and timer deleted.", handle); + SYS_G_LIST_REMOVE(handle_list, (gpointer)(long)handle); + + /* if it is the last element */ + n = SYS_G_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 frequency, int overdrive, int level, int intensity, int priority) +{ + bool found; + int feedback; + int max_level; + + if (level <= 0) + return -EINVAL; + + 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); + + max_level = (level <= MAX_LEVEL)? MAX_LEVEL: level; + if (intensity) + feedback = ((level/max_level) * intensity) / 100; + else + feedback = ((level/max_level) * MAX_INTENSITY) / 100; + + /* 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)feedback); + + /* register timer */ + if (duration) { + //stop_timer = ecore_timer_add(duration/1000.f, gpio_haptic_timer_cb, (void *)(long)handle); + stop_timer = g_timeout_add(duration, gpio_haptic_timer_cb, (void *)(long)handle); + if (!stop_timer) + _E("Failed to add timer callback."); + } + _D("Device handle(%d) %dms", handle, duration); + + return 0; +} + +static int gpio_haptic_stop_device(int 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; + } + } + + /* stop playing */ + peripheral_i2c_write_register_byte(device_handle, DRV2605L_REGISTER_MODE, MODE_INTERNAL_TRIGGER); + + if (stop_timer) { + //ecore_timer_del(stop_timer); + g_source_remove(stop_timer); + stop_timer = 0; + } + + return 0; +} + +static bool is_valid(void) +{ + uint8_t result; + peripheral_i2c_h handle; + + 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; + } + + state = true; + _I("Support gpio haptic device."); + return true; +} + +static int haptic_init(void **data) +{ + hal_backend_haptic_funcs *haptic_funcs; + + haptic_funcs = calloc(1, sizeof(hal_backend_haptic_funcs)); + if (!haptic_funcs) + return -ENOMEM; + + haptic_funcs->get_device_count = gpio_haptic_get_device_count; + haptic_funcs->open_device = gpio_haptic_open_device; + haptic_funcs->close_device = gpio_haptic_close_device; + haptic_funcs->vibrate_monotone = gpio_haptic_vibrate_monotone; + haptic_funcs->stop_device = gpio_haptic_stop_device; + haptic_funcs->is_valid = is_valid; + + *data = (void *)haptic_funcs; + + return 0; +} + +static int haptic_exit(void *data) +{ + if (!data) + return -EINVAL; + + free(data); + return 0; +} + + +hal_backend EXPORT hal_backend_device_haptic_data = { + .name = "haptic", + .vendor = "ARTIK", + .abi_version = HAL_ABI_VERSION_TIZEN_6_5, + .init = haptic_init, + .exit = haptic_exit, +}; |