Implement Long Write Operations for Tizen TV 42/252142/4
authorAyush Garg <ayush.garg@samsung.com>
Sat, 23 Jan 2021 09:20:06 +0000 (14:50 +0530)
committerAyush Garg <ayush.garg@samsung.com>
Wed, 27 Jan 2021 13:56:59 +0000 (19:26 +0530)
In Tizen Platform, For long write operations, bluez handles
all the ATT_Prepare_Write_Req packets and sends the complete
data to the BT-Service when ATT_EXEC_WRITE_REQ is received.

Where as in Tizen TV, Bluedroid works in a different manner.
It sends every ATT_Prepare_Write_Req packet to bt-service using
OAL_EVENT_GATTS_REQUEST_WRITE event with flag is_prep=1 and thus,
allows upper layers to handle long write operation on its own.

This change will perform all the below operations(in case of bluedroid)
at bt-service level which bluez performs for GATT long write:
1. Appending all the Prepare_Write_Req data in a list and send
   prepare_write_resp.
2. Passing the complete data to the capi when
   OAL_EVENT_GATTS_EXEC_REQUEST_WRITE event comes.

Thus this change will help in maintaining uniformity at CAPI level
irrespective of Bluez or Bluedroid.

Change-Id: Iae005dc3643d54783dbd3ddd8c1dc70a1684be99
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
bt-oal/include/oal-event.h
bt-service/services/bt-service-event-receiver.c
bt-service/services/gatt/bt-service-gatt.c

index 87ca441..672d7fd 100644 (file)
@@ -160,6 +160,7 @@ extern "C" {
        EVENT(OAL_EVENT_GATTS_DISCONNECTION_COMPLETED)      /* gatts DisConnection completed */\
        EVENT(OAL_EVENT_GATTS_REQUEST_READ)                 /* gatts Request Read from client */\
        EVENT(OAL_EVENT_GATTS_REQUEST_WRITE)                /* gatts Request Write from client */\
+       EVENT(OAL_EVENT_GATTS_EXEC_REQUEST_WRITE)                /* gatts Exec Request Write from client */\
        EVENT(OAL_EVENT_GATTS_RESPONSE_CONFIRMED)           /* gatts Response confirmation */\
        EVENT(OAL_EVENT_GATTS_IND_CONFIRM)                  /* gatts Indiction confirmation from remote client */\
        EVENT(OAL_EVENT_GATTS_NOTIFICATION)                 /* gatts Notification from remote client */\
@@ -431,6 +432,15 @@ typedef struct {
        uint8_t is_prep;
 } event_gatts_srvc_write_attr_t;
 
+#ifdef TIZEN_BLUEDROID_PORTING
+typedef struct {
+       bt_address_t address;
+       int conn_id;
+       int trans_id;
+       int exec_write;
+} event_gatts_srvc_exec_write_attr_t;
+#endif
+
 typedef struct {
        int hndl;
        oal_status_t status;
index 78334d7..3ddd545 100644 (file)
@@ -369,6 +369,7 @@ static gboolean __bt_handle_oal_events(gpointer data)
        case OAL_EVENT_GATTS_DISCONNECTION_COMPLETED:           /* gatts Disconnection completed */\
        case OAL_EVENT_GATTS_REQUEST_READ:                      /* gatts Request Read from client */\
        case OAL_EVENT_GATTS_REQUEST_WRITE:                     /* gatts Request Write from client */\
+       case OAL_EVENT_GATTS_EXEC_REQUEST_WRITE:                /* gatts Exec Request Write from client */\
        case OAL_EVENT_GATTS_IND_CONFIRM:                       /* gatts Indicate confirmation from remote client */\
        case OAL_EVENT_GATTS_REQUEST_ACQUIRE_WRITE:
        case OAL_EVENT_GATTS_REQUEST_ACQUIRE_NOTIFY:
index 9944e26..5b834cb 100644 (file)
 #define NUM_UUID 20
 #define UUID_MAX_LEN 50
 
+#ifdef TIZEN_BLUEDROID_PORTING
+typedef struct {
+       int connection_id;
+       int request_id;
+       char *device_address;
+       char *value;
+       int handle;
+       int offset;
+       int length;
+       bluetooth_gatt_att_request_type_e request_type;
+       int prep_request_count;
+} bt_gatt_prep_write_data_t;
+
+static GSList *g_pending_write_list = NULL;
+#endif
+
 #define BDADDR_ANY   (&(bluetooth_device_address_t) {{0, 0, 0, 0, 0, 0} })
 
 static char uuid_list[NUM_UUID][BT_UUID_STRING_MAX] = {"0000b00b-0000-0000-f065-080080fa49b5", /* Used by BLEAPP */
@@ -1703,6 +1719,282 @@ static void __bt_handle_gatt_server_acquire_notify_requested(event_gatts_srvc_ac
                                 param);
 }
 
+#ifdef TIZEN_BLUEDROID_PORTING
+static bt_gatt_prep_write_data_t* __bt_create_prep_write_data(event_gatts_srvc_write_attr_t *event)
+{
+       bluetooth_device_address_t dev_addr;
+       char *addr;
+       bt_gatt_prep_write_data_t *prep_data = NULL;
+
+       prep_data = g_malloc0(sizeof(bt_gatt_prep_write_data_t));
+       prep_data->connection_id = event->attr_trans.conn_id;
+       prep_data->request_id = event->attr_trans.trans_id;
+       prep_data->handle = event->attr_trans.attr_handle;
+       prep_data->request_type = BLUETOOTH_GATT_REQUEST_TYPE_WRITE;
+       memcpy(dev_addr.addr, event->address.addr, 6);
+       addr = g_malloc0(BT_ADDRESS_STRING_SIZE);
+       _bt_convert_addr_type_to_string(addr,
+               (unsigned char *)dev_addr.addr);
+       prep_data->device_address = addr;
+       prep_data->offset = event->attr_trans.offset;
+       prep_data->length = event->length;
+       prep_data->value = g_memdup(&event->value[0], event->length);
+
+       return prep_data;
+}
+
+static int __bt_gatt_server_send_long_write_response(bt_gatt_prep_write_data_t *prep_data, int resp_status, int auth_req)
+{
+       int ret = OAL_STATUS_SUCCESS;
+       oal_gatt_response_t response;
+
+       memset(&response, 0x00, sizeof(oal_gatt_response_t));
+
+       BT_INFO("GATT Server Write Res Connection ID: [%d]", prep_data->connection_id);
+       BT_INFO("GATT Server Write Res Transaction ID:[%d]", prep_data->request_id);
+       BT_INFO("GATT Server Write Res Attribute Handle: [%d]", prep_data->handle);
+       BT_INFO("GATT Server Write Res Attribute Offset: [%d]", prep_data->offset);
+       BT_INFO("GATT Server Write Res value length [%d]", prep_data->length);
+
+       response.handle = prep_data->handle;
+       response.attr_value.auth_req = auth_req;
+       response.attr_value.handle = prep_data->handle;
+       response.attr_value.offset = prep_data->offset;
+       response.attr_value.len = prep_data->length;
+       memcpy(&response.attr_value.value, &prep_data->value[0], prep_data->length);
+
+       ret = gatts_send_response(prep_data->connection_id, prep_data->request_id,
+                       resp_status, &response);
+       return ret;
+}
+
+static bt_gatt_prep_write_data_t* __bt_find_prep_write_data_from_request_id(int request_id)
+{
+       GSList *l;
+       bt_gatt_prep_write_data_t *prep_data = NULL;
+
+       for (l = g_pending_write_list; l != NULL; l = g_slist_next(l)) {
+               prep_data = (bt_gatt_prep_write_data_t*)l->data;
+               if (prep_data && (prep_data->request_id == request_id) &&
+                       (prep_data->request_type == BLUETOOTH_GATT_REQUEST_TYPE_WRITE)) {
+                       BT_INFO("prep_data found for request id [%d]", request_id);
+                       return prep_data;
+               }
+       }
+       BT_INFO("prep_data not found for request [%d]", request_id);
+       return NULL;
+}
+
+static bt_gatt_prep_write_data_t* __bt_find_exec_write_req(int conn_id)
+{
+       GSList *l;
+       bt_gatt_prep_write_data_t *prep_data = NULL;
+
+       for (l = g_pending_write_list; l != NULL; l = g_slist_next(l)) {
+               prep_data = (bt_gatt_prep_write_data_t*)l->data;
+               if (prep_data && (prep_data->request_type == BLUETOOTH_GATT_REQUEST_TYPE_EXEC_WRITE)
+                               && (prep_data->connection_id == conn_id)) {
+                       BT_INFO("Exec request found");
+                       return prep_data;
+               }
+       }
+       BT_INFO("Exec request not found");
+       return NULL;
+}
+
+static int __bt_get_prep_request_count(int conn_id)
+{
+       int count = 0;
+       GSList *l;
+       bt_gatt_prep_write_data_t *prep_data = NULL;
+
+       for (l = g_pending_write_list; l != NULL; l = g_slist_next(l)) {
+               prep_data = (bt_gatt_prep_write_data_t*)l->data;
+               if (prep_data && (prep_data->connection_id == conn_id) &&
+                       (prep_data->request_type == BLUETOOTH_GATT_REQUEST_TYPE_WRITE))
+                       count++;
+       }
+       return count;
+}
+
+static bt_gatt_prep_write_data_t* __bt_find_prep_write_data_from_handle(int conn_id, int handle)
+{
+       GSList *l;
+       bt_gatt_prep_write_data_t *prep_data = NULL;
+       bt_gatt_prep_write_data_t *last_prep_data = NULL;
+
+       for (l = g_pending_write_list; l != NULL; l = g_slist_next(l)) {
+               prep_data = (bt_gatt_prep_write_data_t*)l->data;
+               if (prep_data && (prep_data->connection_id == conn_id) && (prep_data->handle == handle)) {
+                       BT_INFO("prep_data entry found for handle [%d]", handle);
+                       last_prep_data = prep_data;
+               }
+       }
+
+       if (!last_prep_data)
+               BT_INFO("prep_data entry not found for handle [%d]", handle);
+
+       return last_prep_data;
+}
+
+static void __bt_gatt_server_send_prep_write_req(int conn_id)
+{
+       int result = BLUETOOTH_ERROR_NONE;
+       GSList *l;
+       bt_gatt_prep_write_data_t *prep_data = NULL;
+
+       for (l = g_pending_write_list; l != NULL; l = g_slist_next(l)) {
+               prep_data = (bt_gatt_prep_write_data_t*)l->data;
+               if (prep_data && (prep_data->connection_id == conn_id) &&
+                       (prep_data->request_type == BLUETOOTH_GATT_REQUEST_TYPE_WRITE)) {
+                       BT_INFO("sending prep_req, req_id=%d", prep_data->request_id);
+                       GVariant *data = NULL;
+                       GVariant *param = NULL;
+                       data = g_variant_new_from_data(
+                               G_VARIANT_TYPE_BYTESTRING,
+                               prep_data->value,
+                               prep_data->length,
+                               TRUE, NULL, NULL);
+
+                       param = g_variant_new("(iiiiiibbsn@ay)", result,
+                               prep_data->connection_id,
+                               prep_data->request_id,
+                               prep_data->handle,
+                               prep_data->offset,
+                               prep_data->length,
+                               1,
+                               0,
+                               prep_data->device_address,
+                               prep_data->length,
+                               data);
+
+                       _bt_send_event(BT_GATT_SERVER_EVENT,
+                               BLUETOOTH_EVENT_GATT_SERVER_VALUE_CHANGED,
+                               param);
+               }
+       }
+}
+
+static void __bt_remove_all_prep_write_req(int conn_id)
+{
+       GSList *l;
+       bt_gatt_prep_write_data_t *prep_data = NULL;
+
+       BT_INFO("Removing all req for conn_id %d", conn_id);
+       for (l = g_pending_write_list; l != NULL;) {
+               prep_data = (bt_gatt_prep_write_data_t*)l->data;
+               l = g_slist_next(l);
+               if (prep_data && (prep_data->connection_id == conn_id)) {
+                       BT_INFO("Removing req for req_id %d", prep_data->request_id);
+                       g_pending_write_list = g_slist_remove(g_pending_write_list, prep_data);
+                       g_free(prep_data->value);
+                       g_free(prep_data->device_address);
+                       g_free(prep_data);
+                       prep_data = NULL;
+               }
+       }
+}
+
+static bool __bt_update_prep_write_data(bt_gatt_prep_write_data_t *prep_data, int offset,
+                                       int length, char *value)
+{
+       char *val;
+       int len;
+
+       if (!length)
+               return true;
+
+       len = prep_data->length + length;
+       val = g_realloc(prep_data->value, len);
+       if (!val)
+               return false;
+
+       memcpy(val + prep_data->length, value, length);
+       prep_data->value = val;
+       prep_data->length = len;
+
+       BT_INFO("updated prep_data->length %d, prep_data->req_id %d", prep_data->length, prep_data->request_id);
+       return true;
+}
+
+static bool __bt_handle_gatt_server_prepare_write_response(int *res,
+                       bluetooth_gatt_server_response_params_t *param)
+{
+       bt_gatt_prep_write_data_t *prep_data = NULL;
+       bt_gatt_prep_write_data_t *exec_data = NULL;
+       int conn_id = -1;
+
+       /* Search for matching Request in prepare write List */
+       prep_data = __bt_find_prep_write_data_from_request_id(param->request_id);
+
+       if (!prep_data)
+               return false;
+
+       conn_id = prep_data->connection_id;
+       exec_data = __bt_find_exec_write_req(conn_id);
+
+       if (!exec_data) {
+               BT_ERR("Oops, Something weird has happened!!!");
+               *res =  BLUETOOTH_ERROR_INTERNAL;
+               __bt_remove_all_prep_write_req(conn_id);
+       } else {
+               // remove pending write request from the list
+               BT_INFO("Removing prending write request, request id = %d", prep_data->request_id);
+               g_pending_write_list = g_slist_remove(g_pending_write_list, prep_data);
+               g_free(prep_data->value);
+               g_free(prep_data->device_address);
+               g_free(prep_data);
+
+               exec_data->prep_request_count--;
+               if (param->response_status || !exec_data->prep_request_count) {
+                       BT_INFO("Sending exec response with status = %d", param->response_status);
+                       ret = __bt_gatt_server_send_long_write_response(exec_data, param->response_status, param->auth_req);
+                       if (ret != OAL_STATUS_SUCCESS) {
+                               BT_ERR("ret: %d", ret);
+                               *res = BLUETOOTH_ERROR_INTERNAL;
+                       }
+                       __bt_remove_all_prep_write_req(conn_id);
+               }
+       }
+       return true;
+}
+
+static void __bt_handle_gatt_server_prepare_write_requested(event_gatts_srvc_write_attr_t *event)
+{
+       bt_gatt_prep_write_data_t *pdata = NULL;
+       bt_gatt_prep_write_data_t *prep_data = NULL;
+       int ret;
+       int resp_status = BLUETOOTH_ATT_ERROR_NONE;
+
+       prep_data = __bt_create_prep_write_data(event);
+
+       /* Find if the req node for that attribute already exists */
+       pdata = __bt_find_prep_write_data_from_handle(prep_data->connection_id, prep_data->handle);
+
+       if (!pdata || (prep_data->offset != (pdata->length + pdata->offset))) {
+               BT_INFO("prep_write_req node doestn't exist or data is not in continuation, offset=%d", prep_data->offset);
+               pdata = prep_data;
+               g_pending_write_list = g_slist_append(g_pending_write_list, (gpointer)pdata);
+               BT_INFO("Send prep_write_response");
+               ret = __bt_gatt_server_send_long_write_response(prep_data, resp_status, 0);
+       } else {
+               /* Update the data and offset in attribute node */
+               if (!(__bt_update_prep_write_data(pdata, prep_data->offset, prep_data->length, prep_data->value))) {
+                       BT_ERR("prep_data couldnot be updated");
+                       resp_status = BLUETOOTH_ATT_ERROR_INSUFFICIENT_RESOURCES;
+               }
+               BT_INFO("Send prep_write_response");
+               ret = __bt_gatt_server_send_long_write_response(prep_data, resp_status, 0);
+               g_free(prep_data->device_address);
+               g_free(prep_data->value);
+               g_free(prep_data);
+       }
+
+       if (ret != OAL_STATUS_SUCCESS)
+               BT_ERR("ret: %d", ret);
+}
+#endif
+
 static void __bt_handle_gatt_server_write_requested(event_gatts_srvc_write_attr_t *event)
 {
        char *address;
@@ -1732,6 +2024,13 @@ static void __bt_handle_gatt_server_write_requested(event_gatts_srvc_write_attr_
                return;
        }
 
+#ifdef TIZEN_BLUEDROID_PORTING
+       if (event->is_prep) {
+               BT_INFO("receive prepare_write request");
+               return __bt_handle_gatt_server_prepare_write_requested(event);
+       }
+#endif
+
        need_resp = event->need_rsp;
        is_prepare_write = event->is_prep;
 
@@ -1782,6 +2081,64 @@ static void __bt_handle_gatt_server_write_requested(event_gatts_srvc_write_attr_
        g_free(write_val);
 }
 
+#ifdef TIZEN_BLUEDROID_PORTING
+static void __bt_handle_gatt_server_exec_write_requested(event_gatts_srvc_exec_write_attr_t *event)
+{
+       char *address;
+       bluetooth_device_address_t dev_addr;
+       int ret;
+       bt_gatt_prep_write_data_t *exec_data = NULL;
+       int resp_status = BLUETOOTH_ATT_ERROR_NONE;
+       BT_INFO("GATT Server Execute Write Requested");
+
+       memcpy(dev_addr.addr, event->address.addr, 6);
+       address = g_malloc0(BT_ADDRESS_STRING_SIZE);
+       _bt_convert_addr_type_to_string(address,
+                       (unsigned char *)dev_addr.addr);
+
+       BT_INFO("GATT Server Exec Write Req Connection ID: [%d]", event->conn_id);
+       BT_INFO("GATT Server Exec Write Req Transaction ID:[%d]", event->trans_id);
+       BT_INFO("GATT Server Exec Write Req Exec Write: [%d]", event->exec_write);
+
+       // prepare exec response data
+       exec_data = g_malloc0(sizeof(bt_gatt_prep_write_data_t));
+       exec_data->connection_id = event->conn_id;
+       exec_data->request_id = event->trans_id;
+       exec_data->device_address = address;
+       exec_data->request_type = BLUETOOTH_GATT_REQUEST_TYPE_EXEC_WRITE;
+       exec_data->prep_request_count = __bt_get_prep_request_count(exec_data->connection_id);
+
+       if ((exec_data->prep_request_count != 1) || !event->exec_write) {
+               if (!event->exec_write) {
+                       BT_INFO("Cancelling all prepared writes, removing all pending entries");
+                       __bt_remove_all_prep_write_req(event->conn_id);
+               } else if (exec_data->prep_request_count > 1) {
+                       /* TODO: Handle reliable-write session */
+                       BT_INFO("This may be reliable write session. Not yet supported!!!, prep_request_count =%d",
+                                       exec_data->prep_request_count);
+                       resp_status = BLUETOOTH_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+                       __bt_remove_all_prep_write_req(event->conn_id);
+               }
+
+               BT_INFO("Send exec response");
+               // Made response and send it.
+               ret = __bt_gatt_server_send_long_write_response(exec_data, resp_status, 0);
+               if (ret != OAL_STATUS_SUCCESS)
+                       BT_ERR("ret: %d", ret);
+
+               g_free(exec_data->device_address);
+               g_free(exec_data);
+               return;
+       }
+
+       BT_INFO("Write all pending prepared values");
+       __bt_gatt_server_send_prep_write_req(exec_data->connection_id);
+
+       // Add exec request in the queue.
+       g_pending_write_list = g_slist_append(g_pending_write_list, (gpointer)exec_data);
+}
+#endif
+
 static void __bt_handle_gatt_server_read_requested(event_gatts_srvc_read_attr_t *event)
 {
        char *address = g_malloc0(BT_ADDRESS_STRING_SIZE);
@@ -1998,6 +2355,13 @@ static void __bt_gatt_event_handler(int event_type, gpointer event_data)
                __bt_handle_gatt_server_write_requested((event_gatts_srvc_write_attr_t *)event_data);
                break;
        }
+#ifdef TIZEN_BLUEDROID_PORTING
+       case OAL_EVENT_GATTS_EXEC_REQUEST_WRITE: {
+               BT_INFO("OAL Event: GATT Server Exec Write Request");
+               __bt_handle_gatt_server_exec_write_requested((event_gatts_srvc_exec_write_attr_t *)event_data);
+               break;
+       }
+#endif
        case OAL_EVENT_GATTS_REQUEST_ACQUIRE_WRITE: {
                BT_INFO("OAL Event: GATT Server Acquire  Write Request");
                __bt_handle_gatt_server_acquire_write_requested((event_gatts_srvc_acquire_attr_t*)event_data);
@@ -2272,14 +2636,21 @@ int _bt_gatt_server_send_response(char *sender, bluetooth_gatt_att_data_t *data,
        BT_CHECK_PARAMETER(param, return);
        struct gatt_server_req_info *req_info = NULL;
        int ret = OAL_STATUS_SUCCESS;
-
+#ifdef TIZEN_BLUEDROID_PORTING
+       int res = BLUETOOTH_ERROR_NONE;
+#endif
        oal_gatt_response_t response;
 
-       BT_DBG("GATT Server Response: Req Type [%d] req_id [%d] status [%d] auth_req [%d] offset[%d] data len[%d]",
+       BT_INFO("GATT Server Response: Req Type [%d] req_id [%d] status [%d] auth_req [%d] offset[%d] data len[%d]",
                        param->req_type, param->request_id,
                        param->response_status, param->auth_req,
                        data->offset, data->length);
 
+#ifdef TIZEN_BLUEDROID_PORTING
+       if (__bt_handle_gatt_server_prepare_write_response(&res, param))
+               return res;
+#endif
+
        /* Search for matching Request in List */
        req_info = __bt_gatt_server_find_request_info(param->request_id, param->req_type);
        if (!req_info) {