ble-gatt: handle pending write data and MTU 97/261597/8
authorSeonah Moon <seonah1.moon@samsung.com>
Thu, 22 Jul 2021 02:23:14 +0000 (11:23 +0900)
committercheoleun moon <chleun.moon@samsung.com>
Fri, 23 Jul 2021 01:45:26 +0000 (01:45 +0000)
Change-Id: I92647460355a7f71900a010e8aa377204c301646

packaging/capi-network-vine.spec
plugins/ble-gatt/ble-gatt-plugin.cpp
src/include/vine-data-path-plugin.h
src/vine-data-path-state.cpp
tool/tool_run.cpp

index ddff2eb2f314e378a5a641787efd92e6de4aaf23..abef79bd1458165dfb3542b8a90dec83871882e4 100755 (executable)
@@ -3,7 +3,7 @@
 %bcond_without use_glib_event_loop
 Name:    capi-network-vine
 Summary: An service discovery framework
-Version: 1.1.5
+Version: 1.1.6
 Release: 0
 Group:   Network & Connectivity/API
 License: Apache-2.0
index 2fea5e32ecc37ea4fd88204687698781ae91de77..af7dbc55e8de385129975dbb51da914c6242bfaa 100755 (executable)
@@ -33,6 +33,7 @@ using namespace std;
 #define VINE_GATT_DESC_UUID "fa87c0d0-afac-11de-8a39-0800200c9a66"
 
 #define BT_ADDRESS_LEN 17
+#define BT_GATT_ATT_DATA_LEN_MAX 512
 
 typedef enum {
        VINE_GATT_ROLE_UNKNOWN = 0,
@@ -47,6 +48,7 @@ typedef struct {
        size_t len;
        //size_t accumulating;
        bool last; // true, single message or last fragment of a message.
+       vine_gatt_s *gatt;
 } gatt_data_s;
 
 typedef struct {
@@ -73,6 +75,8 @@ struct vine_gatt_s {
        } role;
 
        VineQueue<gatt_data_s *> *recv_buffer;
+       VineQueue<gatt_data_s *> *write_buffer;
+       bool ready_to_write;
        char *remote_address;
        void *user; // vine_data_path_h
 };
@@ -90,6 +94,8 @@ static vine_dp_plugin_callbacks g_callbacks = {
 static map<int, struct vine_gatt_s *> g_eventfds;
 
 static vine_gatt_s *_create_gatt(void);
+static int __write_to_client(vine_gatt_s *gatt);
+static int __write_to_server(vine_gatt_s *gatt);
 
 static vine_data_path_error __convert_bt_error_to_data_path_error(int error)
 {
@@ -106,7 +112,7 @@ 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)
+static gatt_data_s *__create_gatt_data(vine_gatt_s *gatt, 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.");
@@ -122,6 +128,7 @@ static gatt_data_s *__create_gatt_data(const char *buf, size_t len, bool last)
        gd->buf = data;
        gd->len = len;
        gd->last = last;
+       gd->gatt = gatt;
        return gd;
 }
 
@@ -131,6 +138,8 @@ static void __destroy_gatt_data(gatt_data_s *data)
 
        if (data->buf)
                free(data->buf);
+       data->buf = NULL;
+       data->gatt = NULL;
        free(data);
 }
 
@@ -226,14 +235,14 @@ static void __gatt_server_write_value_requested_cb(const char *remote_address,
 {
        RET_IF(user_data == NULL, "user_data is NULL");
 
-       VINE_LOGI("Got write value requestment from %s.", remote_address);
+       VINE_LOGI("Got write value request from %s.", remote_address);
 
        vine_gatt_s *gatt = (vine_gatt_s *)user_data;
        vine_gatt_s *client_gatt = __find_client_gatt(gatt, remote_address);
        RET_IF(client_gatt == NULL, "Cannot find a client[%s].", remote_address);
 
        // TODO: Need to handle splited data using offset.
-       gatt_data_s *recv_data = __create_gatt_data(value, len, true);
+       gatt_data_s *recv_data = __create_gatt_data(gatt, value, len, true);
        RET_IF(recv_data == NULL, "recv_data is NULL");
 
        client_gatt->recv_buffer->push(recv_data);
@@ -249,20 +258,41 @@ static void __gatt_server_write_value_requested_cb(const char *remote_address,
                g_callbacks.received_cb(len, client_gatt->user);
 }
 
+static void __notify_write_event(vine_gatt_s *gatt)
+{
+       g_callbacks.pollfd_cb(VINE_DATA_PATH_POLLFD_OP, gatt->eventfd, POLLOUT, gatt->user);
+
+       uint64_t v = 1;
+       if (write(gatt->eventfd, &v, sizeof(v)) == -1)
+               VINE_LOGE("Write error(%d)", errno);
+}
+
 void __gatt_server_noti_state_changed_cb(bool notify, bt_gatt_server_h server,
                bt_gatt_h gatt_handle, void *user_data)
 {
        RET_IF(user_data == NULL, "user_data is NULL.");
        vine_gatt_s *gatt = (vine_gatt_s *)user_data;
 
-       if (notify) {
-               g_callbacks.pollfd_cb(VINE_DATA_PATH_POLLFD_OP, gatt->eventfd, POLLOUT, gatt->user);
+       auto iter = gatt->role.server->client_list->begin();
+       if (iter == gatt->role.server->client_list->end()) {
+               VINE_LOGE("Cannot find accepted client.");
+               return;
+       }
 
-               uint64_t v = 1;
-               if (write(gatt->eventfd, &v, sizeof(v)) == -1)
-                       VINE_LOGE("Write error(%d)", errno);
+       vine_gatt_s *accepted_client = iter->second;
+       if (!accepted_client) {
+               VINE_LOGE("Cannot find accepted client.");
+               return;
        }
+
+       gatt->ready_to_write = notify;
+       accepted_client->ready_to_write = notify;
+
        VINE_LOGI("Noti state changed. notify[%d] characteristic[%p]", notify, gatt_handle);
+       if (notify) {
+               // TODO: This will be changed if BT API supports a remote address here.
+               __notify_write_event(accepted_client);
+       }
 }
 
 void __gatt_server_noti_sent_cb(int result,
@@ -275,11 +305,25 @@ void __gatt_server_noti_sent_cb(int result,
        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;
+       gatt_data_s *data = (gatt_data_s *)user_data;
+       if (!data->gatt)
+               return;
+
+       vine_gatt_s *gatt = data->gatt;
        g_callbacks.written_cb(1, gatt->user); // bytes will be ignored.
+       __destroy_gatt_data(data);
+
+       gatt->ready_to_write = true;
+       __notify_write_event(gatt);
 }
 
 // Client Callbacks
+static void __gatt_client_mtu_changed_cb(bt_gatt_client_h client,
+       const bt_gatt_client_att_mtu_info_s *mtu_info, void *user_data)
+{
+       VINE_LOGI("MTU is changed. %u", mtu_info->mtu);
+}
+
 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)
 {
@@ -301,17 +345,27 @@ void __gatt_client_write_complete_cb(int result, bt_gatt_h gatt_handle, void *us
 
        VINE_LOGI("gatt[%p] completed to send data. result[%d]", gatt_handle, result);
 
-       vine_gatt_s *gatt = (vine_gatt_s *)user_data;
+       gatt_data_s *data = (gatt_data_s *)user_data;
+       if (!data->gatt)
+               return;
+
+       vine_gatt_s *gatt = data->gatt;
+
        g_callbacks.written_cb(1, gatt->user); // bytes will be ignored.
+       __destroy_gatt_data(data);
+
+       gatt->ready_to_write = true;
+       __notify_write_event(gatt);
 }
 
 void __gatt_client_characteristic_value_changed_cb(bt_gatt_h characteristic,
                char *value, int len, void *user_data)
 {
+       VINE_LOGD("+");
        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);
+       gatt_data_s *recv_data =__create_gatt_data(gatt, value, len, true);
        RET_IF(recv_data == NULL, "recv_data is NULL");
 
        gatt->recv_buffer->push(recv_data);
@@ -336,6 +390,14 @@ static int __update_gatt_service_info(const char *remote_address, vine_gatt_s *g
        if (ret != BT_ERROR_NONE || !client)
                return __convert_bt_error_to_data_path_error(ret);
 
+       ret = bt_gatt_client_set_att_mtu_changed_cb(client, __gatt_client_mtu_changed_cb, gatt);
+       if (ret != BT_ERROR_NONE)
+               goto ERR;
+
+       ret = bt_gatt_client_request_att_mtu_change(client, BT_GATT_ATT_DATA_LEN_MAX);
+       if (ret != BT_ERROR_NONE)
+               goto ERR;
+
        ret = bt_gatt_client_set_service_changed_cb(client, __gatt_client_service_changed_cb, gatt);
        if (ret != BT_ERROR_NONE)
                goto ERR;
@@ -403,8 +465,8 @@ static void __handle_connected_client(vine_gatt_s *gatt, const char *address)
        RET_IF(accepted_gatt == NULL, "Failed to create accepted gatt.");
 
        gatt->role.server->client_list->insert(std::make_pair(string(address), accepted_gatt));
-       __invoke_accepted_cb(accepted_gatt, accepted_gatt->user);
        VINE_LOGI("%s is connected.", address);
+       __invoke_accepted_cb(accepted_gatt, accepted_gatt->user);
 }
 
 static void __handle_disconnected_client(vine_gatt_s *gatt, const char *address)
@@ -447,6 +509,7 @@ static void __gatt_connection_state_changed_cb(int result, bool connected,
                        bt_gatt_disconnect(remote_address);
                        result = -1;
                }
+               gatt->ready_to_write = true;
                __invoke_connected_cb(result, gatt->user);
        }
 }
@@ -456,6 +519,13 @@ static vine_gatt_s *_create_gatt(void)
        vine_gatt_s *gatt = (vine_gatt_s *)calloc(1, sizeof(vine_gatt_s));
        RET_VAL_IF(gatt == NULL, NULL, "Out of memory");
 
+       int fd = eventfd(0, 0);
+       if (fd < 0) {
+               VINE_LOGE("cannot get fd for handling event. errno: %d", errno);
+               free(gatt);
+               return NULL;
+       }
+
        if (bt_gatt_set_connection_state_changed_cb(__gatt_connection_state_changed_cb,
                                (void *)gatt) != BT_ERROR_NONE) {
                VINE_LOGE("Failed to register GATT connection state changed callback");
@@ -463,8 +533,10 @@ static vine_gatt_s *_create_gatt(void)
                return NULL;
        }
 
-       gatt->eventfd = eventfd(0, 0);
+       gatt->eventfd = fd;
+       g_eventfds[gatt->eventfd] = gatt;
        gatt->recv_buffer = new VineQueue<gatt_data_s *>;
+       gatt->write_buffer = new VineQueue<gatt_data_s *>;
        return gatt;
 }
 
@@ -474,7 +546,8 @@ static bt_gatt_h _add_gatt_characteristic(bt_gatt_h service, vine_dp_plugin_h ha
        char initial_value[1] = {'0'}; // TODO: will be changed.
        int permissions = BT_GATT_PERMISSION_READ | BT_GATT_PERMISSION_WRITE;
        int properties = BT_GATT_PROPERTY_BROADCAST | BT_GATT_PROPERTY_READ |
-               BT_GATT_PROPERTY_WRITE | BT_GATT_PROPERTY_NOTIFY;
+               BT_GATT_PROPERTY_WRITE
+               | BT_GATT_PROPERTY_INDICATE;
 
        if (bt_gatt_characteristic_create(VINE_GATT_CHAR_UUID,
                                permissions, properties,
@@ -522,34 +595,105 @@ 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)
+static gatt_data_s *__get_pending_write_data(vine_gatt_s *gatt)
 {
-       int ret = bt_gatt_set_value(gatt->characteristic, (const char *)buf, len);
+       if (gatt->write_buffer->empty()) {
+               VINE_LOGI("gatt[%p] write_buffer is empty.", gatt);
+               return NULL;
+       }
+
+       gatt_data_s *data = NULL;
+
+       do {
+               data = gatt->write_buffer->front();
+               gatt->write_buffer->pop();
+       } while(data == nullptr); // TODO: handle infinite loop.
+
+       return data;
+}
+
+static int __write_to_server(vine_gatt_s *gatt)
+{
+       RET_VAL_IF(!gatt->ready_to_write, VINE_DATA_PATH_ERROR_NONE,
+                       "Cannot write yet. The request will be operated later.");
+
+       gatt_data_s *data = __get_pending_write_data(gatt);
+       RET_VAL_IF(data == NULL, VINE_DATA_PATH_ERROR_NO_DATA, "There is no pending data.");
+
+       int ret = bt_gatt_set_value(gatt->characteristic, (const char *)data->buf, data->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);
+                       __gatt_client_write_complete_cb, (void *)data);
        RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
                        "bt_gatt_client_write_value() failed.");
 
+       gatt->ready_to_write = false;
+
        VINE_LOGI("Succeed to send data to server");
+       return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int __push_write_data(vine_gatt_s *gatt, char *buf, size_t len)
+{
+
+       gatt_data_s *data = __create_gatt_data(gatt, buf, len, true);
+       RET_VAL_IF(data == NULL, VINE_DATA_PATH_ERROR_OUT_OF_MEMORY, "out of memory.");
+
+       gatt->write_buffer->push(data);
+       VINE_LOGI("gatt[%p] data[%p]", gatt, data);
 
        return VINE_DATA_PATH_ERROR_NONE;
 }
 
-static int _gatt_write_to_client(vine_gatt_s *gatt, unsigned char *buf, size_t len)
+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);
+       int ret = __push_write_data(gatt, (char *)buf, len);
+       if (ret != VINE_DATA_PATH_ERROR_NONE)
+               return ret;
+
+       ret = __write_to_server(gatt);
+       if (ret != VINE_DATA_PATH_ERROR_NONE)
+               return ret;
+
+       return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int __write_to_client(vine_gatt_s *gatt)
+{
+       RET_VAL_IF(!gatt->ready_to_write, VINE_DATA_PATH_ERROR_NONE,
+                       "Cannot write yet. The request will be operated later.");
+
+       gatt_data_s *data = __get_pending_write_data(gatt);
+       RET_VAL_IF(data == NULL, VINE_DATA_PATH_ERROR_NO_DATA, "There is no pending data.");
+
+       int ret = bt_gatt_set_value(gatt->characteristic, (const char *)data->buf, data->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);
+                       __gatt_server_noti_sent_cb, gatt->remote_address, (void *)data);
        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");
+       gatt->ready_to_write = false;
+
+       VINE_LOGI("Succeed to send data to %s",
+                       gatt->remote_address ? gatt->remote_address : "all");
+
+       return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int _gatt_write_to_client(vine_gatt_s *gatt, unsigned char *buf, size_t len)
+{
+       int ret = __push_write_data(gatt, (char *)buf, len);
+       if (ret != VINE_DATA_PATH_ERROR_NONE)
+               return ret;
+
+       ret = __write_to_client(gatt);
+       if (ret != VINE_DATA_PATH_ERROR_NONE)
+               return ret;
 
        return VINE_DATA_PATH_ERROR_NONE;
 }
@@ -580,6 +724,13 @@ void gatt_process_event(int fd, int events)
        // TODO: handle pending write events.
        vine_gatt_s *gatt = it->second;
        VINE_LOGI("found gatt[%p]", gatt);
+
+       if (gatt->type == VINE_GATT_ROLE_SERVER
+                       || (gatt->type == VINE_GATT_ROLE_CLIENT
+                               && gatt->role.client && gatt->role.client->is_accepted))
+               __write_to_client(gatt);
+       else if (gatt->type == VINE_GATT_ROLE_CLIENT)
+               __write_to_server(gatt);
 }
 
 int gatt_create(vine_dp_plugin_h *handle, void *plugin_data, void *user)
@@ -778,7 +929,8 @@ int gatt_write(vine_dp_plugin_h handle, unsigned char *buf, size_t len)
 {
        RET_VAL_IF(handle == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "handle is NULL");
        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");
+       RET_VAL_IF(len <= 0 || len >= BT_GATT_ATT_DATA_LEN_MAX,
+                       VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "invalid length.");
 
        vine_gatt_s *gatt = (vine_gatt_s *)handle;
        int ret;
index 55b21a9eaea60a9acb53066c7d905f52d75cae37..24a6a230732517deb4b4c1cef1fb7dc4efeee568 100755 (executable)
@@ -29,7 +29,8 @@ typedef enum {
        VINE_DATA_PATH_ERROR_INVALID_PARAMETER,
        VINE_DATA_PATH_ERROR_INVALID_OPERATION,
        VINE_DATA_PATH_ERROR_OPERATION_FAILED,
-       VINE_DATA_PATH_ERROR_OUT_OF_MEMORY
+       VINE_DATA_PATH_ERROR_OUT_OF_MEMORY,
+       VINE_DATA_PATH_ERROR_NO_DATA,
 } vine_data_path_error;
 
 typedef enum {
index c7f1bf7d512b3655de8b0bcaf48a15cf46fd6285..21907c1c88fb87b8c521c2f475a8791c6af6cc11 100644 (file)
@@ -721,8 +721,7 @@ int VineEstablishedState::read(unsigned char *buf, size_t buf_len, size_t *read_
 
 int VineEstablishedState::write(unsigned char *buf, size_t len)
 {
-       __fn.write(__plugin, buf, len);
-       return VINE_ERROR_NONE;
+       return __fn.write(__plugin, buf, len);
 }
 
 void VineEstablishedState::received_cb(size_t bytes)
index 12df1008d10f5049493ca4c81ac12fef1700591c..e8d800fcca96394c2f1c1237b1f25424ea0348b6 100755 (executable)
@@ -24,6 +24,7 @@
 
 #define MAX_EVENTS 10
 #define MAX_READ_LEN 1024
+#define MAX_WRITE_LEN 300
 
 int interrupt_flag;
 
@@ -373,7 +374,7 @@ static int _send_message_from_file(vine_dp_h dp)
        unsigned char *buf;
        long size;
        size_t count;
-       int ret;
+       int ret = VINE_ERROR_NONE;
        FILE *file = fopen(vine_configs.file, "r");
 
        if (file == NULL)
@@ -392,15 +393,19 @@ static int _send_message_from_file(vine_dp_h dp)
        count = fread(buf, sizeof(unsigned char), size, file);
        fclose(file);
 
-       ret = vine_dp_send(dp, buf, count);
-       const char *ret_str;
-       if (ret == VINE_ERROR_NONE) {
-               printf("Sent total %zd bytes.\n", count);
-               ret_str = "Succeeded";
-       } else {
-               ret_str = "Failed";
+       int idx = 0;
+       while (count) {
+               size_t len = count <= MAX_WRITE_LEN ? count : MAX_WRITE_LEN;
+               ret = vine_dp_send(dp, buf + idx, len);
+               if (ret == VINE_ERROR_NONE) {
+                       printf("Sent total %zd bytes.\n", len);
+               } else {
+                       printf("Failed to send a message.\n");
+                       break;
+               }
+               count -= len;
+               idx += len;
        }
-       printf("%s to send a message.\n", ret_str);
        free(buf);
 
        return ret;