#include "vine-data-path-plugin.h"
#include "vine-log.h"
#include "vine-utils.h"
+#include "vine-queue.h"
using namespace std;
} vine_gatt_role_e;
typedef struct {
+ char *buf;
+ size_t len;
+ //size_t accumulating;
+ bool last; // true, single message or last fragment of a message.
+} gatt_data_s;
+
+typedef struct {
bt_gatt_h service;
bt_gatt_h characteristic;
bt_gatt_h descriptor;
bt_gatt_client_h client;
} role;
- char *remote_address;
+ VineQueue<gatt_data_s *> *recv_buffer;
+
+ char *remote_address; // this is used for VINE_GATT_ROLE_CLIENT only.
bool is_accepted;
void *user; // vine_data_path_h
} vine_gatt_s;
}
}
+static gatt_data_s *__create_gatt_data(const char *buf, size_t len, bool last)
+{
+ gatt_data_s *gd = (gatt_data_s *)calloc(1, sizeof(gatt_data_s));
+ RET_VAL_IF(!gd, NULL, "Out of memory.");
+
+ char *data = (char *)calloc(len, sizeof(char));
+ if (!data) {
+ VINE_LOGE("Out of memory.");
+ free(gd);
+ return NULL;
+ }
+
+ memcpy(data, buf, len);
+ gd->buf = data;
+ gd->len = len;
+ gd->last = last;
+ return gd;
+}
+
+static void __destroy_gatt_data(gatt_data_s *data)
+{
+ RET_IF(!data, "data is NULL");
+
+ if (data->buf)
+ free(data->buf);
+ free(data);
+}
+
static void __invoke_accepted_cb(const char *remote_address, vine_gatt_s *gatt, void *user_data)
{
if (!g_callbacks.accepted_cb)
vine_gatt_s *accepted_gatt = _create_gatt();
RET_IF(accepted_gatt == NULL, "Failed to create accepted_gatt.");
+ accepted_gatt->type = VINE_GATT_ROLE_CLIENT;
accepted_gatt->remote_address = STRDUP(remote_address);
// refer to server's gatt handles.
int offset, void *user_data)
{
// TODO: Send response.
+ // TODO: Save data
+ // Is this function needed for VINE?
}
static void __gatt_server_write_value_requested_cb(const char *remote_address,
bt_gatt_h gatt_handle, bool response_needed, int offset,
const char *value, int len, void *user_data)
{
+ RET_IF(user_data == NULL, "user_data is NULL");
+
VINE_LOGI("Got write value requestment from %s.", remote_address);
- // TODO: Send response.
+ vine_gatt_s *gatt = (vine_gatt_s *)user_data;
+
+ // TODO: Need to handle splited data using offset.
+ gatt_data_s *recv_data = __create_gatt_data(value, len, true);
+ RET_IF(recv_data == NULL, "recv_data is NULL");
+
+ gatt->recv_buffer->push(recv_data);
+
+ VINE_LOGI("Write value requested. offset[%d] len[%d] reponse_needed[%d]",
+ offset, len, response_needed);
+
+ if (bt_gatt_server_send_response(request_id,
+ BT_GATT_REQUEST_TYPE_WRITE, offset, BT_ERROR_NONE, NULL, 0) < 0)
+ VINE_LOGE("bt_gatt_server_send_response() is failed.");
+
+ if (g_callbacks.received_cb)
+ g_callbacks.received_cb(len, gatt->user);
}
void __gatt_server_noti_state_changed_cb(bool notify, bt_gatt_server_h server,
VINE_LOGI("Noti state changed. notify[%d] characteristic[%p]", notify, gatt_handle);
}
+void __gatt_server_noti_sent_cb(int result,
+ const char *remote_address, bt_gatt_server_h server, bt_gatt_h characteristic,
+ bool completed, void *user_data)
+{
+ RET_IF(user_data == NULL, "user_data is NULL");
+ RET_IF(g_callbacks.written_cb == NULL, "written_cb() is NULL");
+
+ VINE_LOGI("Send data to %s is %s. server[%p] completed[%d]",
+ remote_address, result == 0 ? "success" : "failed", server, completed);
+
+ vine_gatt_s *gatt = (vine_gatt_s *)user_data;
+ g_callbacks.written_cb(1, gatt->user); // bytes will be ignored.
+}
+
// Client Callbacks
static void __gatt_client_service_changed_cb(bt_gatt_client_h client,
bt_gatt_client_service_change_type_e type, const char *uuid, void *user_data)
type == BT_GATT_CLIENT_SERVICE_ADDED ? "added." : "removed." );
}
+void __gatt_client_write_complete_cb(int result, bt_gatt_h gatt_handle, void *user_data)
+{
+ RET_IF(user_data == NULL, "user_data is NULL");
+ RET_IF(g_callbacks.written_cb == NULL, "written_cb() is NULL");
+
+ VINE_LOGI("gatt[%p] completed to send data. result[%d]", gatt_handle, result);
+
+ vine_gatt_s *gatt = (vine_gatt_s *)user_data;
+ g_callbacks.written_cb(1, gatt->user); // bytes will be ignored.
+}
+
+void __gatt_client_characteristic_value_changed_cb(bt_gatt_h characteristic,
+ char *value, int len, void *user_data)
+{
+ RET_IF(user_data == NULL, "user_data is NULL");
+
+ vine_gatt_s *gatt = (vine_gatt_s *)user_data;
+ gatt_data_s *recv_data = __create_gatt_data(value, len, true);
+ RET_IF(recv_data == NULL, "recv_data is NULL");
+
+ gatt->recv_buffer->push(recv_data);
+
+ VINE_LOGD("recv_data[%p] is pushed to recv_buffer. length[%zd]", recv_data, len);
+ if (g_callbacks.received_cb)
+ g_callbacks.received_cb(len, gatt->user);
+}
+
static int __update_gatt_service_info(const char *remote_address, vine_gatt_s *gatt)
{
RET_VAL_IF(gatt == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "gatt is NULL");
if (ret != BT_ERROR_NONE)
goto ERR;
+ ret = bt_gatt_client_set_characteristic_value_changed_cb(characteristic,
+ __gatt_client_characteristic_value_changed_cb, gatt);
+ if (ret != BT_ERROR_NONE)
+ goto ERR;
+
gatt->role.client = client;
gatt->service = service;
gatt->characteristic = characteristic;
free(gatt);
return NULL;
}
+
+ gatt->recv_buffer = new VineQueue<gatt_data_s *>;
return gatt;
}
return descriptor;
}
+static int _gatt_write_to_server(vine_gatt_s *gatt, unsigned char *buf, size_t len)
+{
+ int ret = bt_gatt_set_value(gatt->characteristic, (const char *)buf, len);
+ RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
+ "bt_gatt_set_value() failed.");
+
+ ret = bt_gatt_client_write_value(gatt->characteristic,
+ __gatt_client_write_complete_cb, (void *)gatt);
+ RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
+ "bt_gatt_client_write_value() failed.");
+
+ VINE_LOGI("Succeed to send data to server");
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int _gatt_write_to_client(vine_gatt_s *gatt, unsigned char *buf, size_t len)
+{
+ int ret = bt_gatt_set_value(gatt->characteristic, (const char *)buf, len);
+ RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
+ "bt_gatt_set_value() failed.");
+
+ ret = bt_gatt_server_notify_characteristic_changed_value(gatt->characteristic,
+ __gatt_server_noti_sent_cb, gatt->remote_address, (void *)gatt);
+ RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
+ "bt_gatt_server_notify_characteristic_changed_value() failed.");
+
+ VINE_LOGI("Succeed to send data to %s", gatt->remote_address ? gatt->remote_address : "all");
+
+ return VINE_DATA_PATH_ERROR_NONE;
+}
+
int gatt_init(void)
{
return VINE_DATA_PATH_ERROR_NONE;
RET_VAL_IF(handle == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "handle is NULL");
RET_VAL_IF(user == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "user is NULL");
- vine_gatt_s *gatt = _create_gatt();
+ vine_gatt_s *gatt = plugin_data ? (vine_gatt_s *)plugin_data :_create_gatt();
RET_VAL_IF(gatt == NULL, VINE_DATA_PATH_ERROR_OPERATION_FAILED, "Failed to create GATT handle");
gatt->user = user;
VINE_LOGI("Succeeded to start GATT server.");
+ // TODO
//if (g_callbacks.opened_cb)
// g_callbacks.opened_cb(VINE_DATA_PATH_ERROR_NONE, -1, gatt->user);
return VINE_DATA_PATH_ERROR_NONE;
int gatt_read(vine_dp_plugin_h handle, unsigned char *buf, size_t len)
{
- return VINE_DATA_PATH_ERROR_NONE;
+ RET_VAL_IF(handle == NULL, 0, "plugin handle is NULL");
+ RET_VAL_IF(buf == NULL, 0, "buf is NULL");
+ RET_VAL_IF(len == 0, 0, "len is 0");
+
+ vine_gatt_s *gatt = (vine_gatt_s *)handle;
+ gatt_data_s *gd = NULL;
+ size_t bytes = 0;
+
+ if (!gatt->recv_buffer)
+ return bytes;
+
+ unsigned char *ptr = buf;
+ size_t read_len = 0;
+ while (!gatt->recv_buffer->empty() && len > 0) {
+ gd = gatt->recv_buffer->front();
+ if (gd == nullptr) {
+ gatt->recv_buffer->pop();
+ return bytes;
+ }
+
+ ptr += read_len;
+ read_len = gd->len > len ? len : gd->len;
+ memcpy(ptr, gd->buf, read_len);
+ len -= read_len;
+ bytes += read_len;
+
+ if (read_len >= gd->len) {
+ gatt->recv_buffer->pop();
+ bool last = gd->last;
+ __destroy_gatt_data(gd);
+ gd = NULL;
+ if (last)
+ break;
+ } else if (read_len < gd->len) { // requeue
+ size_t remained_len = gd->len - read_len;
+ char *remained_data = (char *)calloc(remained_len, sizeof(char));
+ memcpy(remained_data, gd->buf + read_len, remained_len);
+ free(gd->buf);
+ gd->buf = remained_data;
+ gd->len = remained_len;
+ return bytes;
+ }
+ }
+ return bytes;
}
int gatt_write(vine_dp_plugin_h handle, unsigned char *buf, size_t len)
RET_VAL_IF(buf == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "buf is NULL");
RET_VAL_IF(len <= 0, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "len is less than 0");
- // TODO
- // vine_gatt_s *gatt = (vine_gatt_s *)handle;
+ vine_gatt_s *gatt = (vine_gatt_s *)handle;
+ int ret;
- return VINE_DATA_PATH_ERROR_NONE;
+ switch (gatt->type) {
+ case VINE_GATT_ROLE_SERVER:
+ ret =_gatt_write_to_client(gatt, buf, len);
+ break;
+ case VINE_GATT_ROLE_CLIENT:
+ ret = gatt->is_accepted ? _gatt_write_to_client(gatt, buf, len)
+ : _gatt_write_to_server(gatt, buf, len);
+ break;
+ case VINE_GATT_ROLE_UNKNOWN:
+ default:
+ ret = VINE_DATA_PATH_ERROR_INVALID_OPERATION;
+ break;
+ }
+
+ return ret;
}
int gatt_close(vine_dp_plugin_h handle)