Implement read/write for BLE GATT 09/261009/2
authorSeonah Moon <seonah1.moon@samsung.com>
Thu, 8 Jul 2021 08:47:04 +0000 (17:47 +0900)
committerSeonah Moon <seonah1.moon@samsung.com>
Thu, 8 Jul 2021 08:54:22 +0000 (17:54 +0900)
Change-Id: Id018c74de7bac482d3d9bd59fca6ab61598a9cb2

plugins/ble-gatt/ble-gatt-plugin.cpp
src/include/vine-data-path-state.h
src/vine-data-path.cpp

index c8a3292..e428706 100755 (executable)
@@ -20,6 +20,7 @@
 #include "vine-data-path-plugin.h"
 #include "vine-log.h"
 #include "vine-utils.h"
+#include "vine-queue.h"
 
 using namespace std;
 
@@ -37,6 +38,13 @@ typedef enum {
 } 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;
@@ -47,7 +55,9 @@ typedef struct {
                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;
@@ -79,6 +89,34 @@ static vine_data_path_error __convert_bt_error_to_data_path_error(int error)
        }
 }
 
+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)
@@ -87,6 +125,7 @@ static void __invoke_accepted_cb(const char *remote_address, vine_gatt_s *gatt,
        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.
@@ -121,6 +160,8 @@ static void __gatt_server_read_value_requested_cb(
         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,
@@ -128,9 +169,27 @@ 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,
@@ -139,6 +198,20 @@ 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)
@@ -147,6 +220,33 @@ static void __gatt_client_service_changed_cb(bt_gatt_client_h client,
                        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");
@@ -174,6 +274,11 @@ static int __update_gatt_service_info(const char *remote_address, vine_gatt_s *g
        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;
@@ -224,6 +329,8 @@ static vine_gatt_s *_create_gatt(void)
                free(gatt);
                return NULL;
        }
+
+       gatt->recv_buffer = new VineQueue<gatt_data_s *>;
        return gatt;
 }
 
@@ -281,6 +388,38 @@ static bt_gatt_h _add_gatt_descriptor(bt_gatt_h characteristic)
        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;
@@ -305,7 +444,7 @@ int gatt_create(vine_dp_plugin_h *handle, void *plugin_data, void *user)
        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;
@@ -402,6 +541,7 @@ int gatt_open(vine_dp_plugin_h handle, int addr_family,
 
        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;
@@ -445,7 +585,50 @@ int gatt_connect(vine_dp_plugin_h handle, int addr_family,
 
 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)
@@ -454,10 +637,24 @@ 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)
index 7ac17ba..c20b1a5 100644 (file)
@@ -27,7 +27,7 @@
 // It will be removed when vine-data-path-state is changed.
 typedef void *vine_data_path_h;
 
-#define AUTH_CONNECTED_TIMEOUT_MS 5000
+#define AUTH_CONNECTED_TIMEOUT_MS 15000
 #define AUTH_REQUEST_TIMEOUT_MS 5000
 #define AUTH_RESPONSE_TIMEOUT_MS 5000
 
index e2fa7f5..c0aac6e 100755 (executable)
@@ -284,7 +284,7 @@ static void __accepted_cb(vine_dp_addr_family_e addr_family, char *addr,
 
        vine_data_path_s *listen_dp = (vine_data_path_s *)user_data;
 
-       VINE_LOGE("listen_dp[%p], security[%p]", listen_dp, listen_dp->security);
+       VINE_LOGI("listen_dp[%p], security[%p]", listen_dp, listen_dp->security);
        vine_data_path_s *connected_dp = _vine_data_path_create(listen_dp->method,
                        __convert_addr_family(addr_family),
                        addr, port, listen_dp->security, plugin_data, listen_dp->event_queue);