From: Seonah Moon Date: Thu, 22 Jul 2021 02:23:14 +0000 (+0900) Subject: ble-gatt: handle pending write data and MTU X-Git-Tag: submit/tizen/20210806.070749~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=742d79dc9a2f19d3ebf7d245276e7f2839f89646;p=platform%2Fcore%2Fapi%2Fvine.git ble-gatt: handle pending write data and MTU Change-Id: I92647460355a7f71900a010e8aa377204c301646 --- diff --git a/packaging/capi-network-vine.spec b/packaging/capi-network-vine.spec index ddff2eb..abef79b 100755 --- a/packaging/capi-network-vine.spec +++ b/packaging/capi-network-vine.spec @@ -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 diff --git a/plugins/ble-gatt/ble-gatt-plugin.cpp b/plugins/ble-gatt/ble-gatt-plugin.cpp index 2fea5e3..af7dbc5 100755 --- a/plugins/ble-gatt/ble-gatt-plugin.cpp +++ b/plugins/ble-gatt/ble-gatt-plugin.cpp @@ -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 *recv_buffer; + VineQueue *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 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->write_buffer = new VineQueue; 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; diff --git a/src/include/vine-data-path-plugin.h b/src/include/vine-data-path-plugin.h index 55b21a9..24a6a23 100755 --- a/src/include/vine-data-path-plugin.h +++ b/src/include/vine-data-path-plugin.h @@ -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 { diff --git a/src/vine-data-path-state.cpp b/src/vine-data-path-state.cpp index c7f1bf7..21907c1 100644 --- a/src/vine-data-path-state.cpp +++ b/src/vine-data-path-state.cpp @@ -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) diff --git a/tool/tool_run.cpp b/tool/tool_run.cpp index 12df100..e8d800f 100755 --- a/tool/tool_run.cpp +++ b/tool/tool_run.cpp @@ -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;