BLE Gatt Client Socket write and notify implementation
[platform/core/connectivity/bluetooth-frwk.git] / bt-api / bt-gatt-client.c
index c0f5620..8f79ce3 100755 (executable)
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdint.h>
+#include <gio/gunixfdlist.h>
 
 #include "bt-common.h"
 #include "bt-event-handler.h"
@@ -31,6 +32,8 @@
 #ifdef TIZEN_GATT_CLIENT
 #include "bluetooth-gatt-client-api.h"
 #include <arpa/inet.h>
+static GSList *gatt_characteristic_notify_list;
+static GSList *gatt_characteristic_write_list = NULL;;
 #endif
 
 #define GATT_DEFAULT_TIMEOUT  (6 * 1000) // Dependent on supervision timeout 6 sec
@@ -1849,8 +1852,7 @@ static void __bt_fill_service_handle_informations(bt_services_browse_info_t *pro
        }
 }
 
-static void __bt_fill_char_handle_informations(bt_char_browse_info_t *props,
-               bt_gatt_service_property_t *service)
+static void __bt_fill_char_handle_informations(bt_char_browse_info_t *props, bt_gatt_service_property_t *service)
 {
        int count;
        char uuid_string[BLUETOOTH_UUID_STRING_MAX];
@@ -2147,6 +2149,130 @@ BT_EXPORT_API int bluetooth_gatt_client_get_char_descriptor_property(
                as an asyn function and leave every thing else in the callback */
 }
 
+
+static gboolean  bluetooth_gatt_client_notify_channel_watch_cb(GIOChannel *gio,
+                                       GIOCondition cond, gpointer data)
+{
+       bt_gatt_characteristic_notify_info_t *chr_info = (bt_gatt_characteristic_notify_info_t *)data;
+
+       BT_INFO(" FD io NOTIFICATION recived\n");
+
+       if (!chr_info) {
+               BT_ERR("char INFO nort recieved");
+               return FALSE;
+       }
+       if (cond & G_IO_IN) {
+               GIOStatus status = G_IO_STATUS_NORMAL;
+               GError *err = NULL;
+               char *buffer = NULL;
+               gsize len = 0;
+               bt_event_info_t *event_info;
+
+               buffer = g_malloc0(chr_info->mtu + 1);
+
+               status = g_io_channel_read_chars(gio, buffer,
+                               chr_info->mtu, &len, &err);
+               if (status != G_IO_STATUS_NORMAL) {
+                       BT_ERR("IO Channel read is failed with %d", status);
+                       g_free(buffer);
+                       if (err) {
+                               BT_ERR("IO Channel read error [%s]", err->message);
+                               if (status == G_IO_STATUS_ERROR) {
+                                       BT_ERR("cond : %d", cond);
+                                       g_error_free(err);
+                                       g_io_channel_shutdown(gio, TRUE, NULL);
+                                       g_io_channel_unref(gio);
+
+                                       gatt_characteristic_notify_list = g_slist_remove(gatt_characteristic_notify_list, chr_info);
+                                       g_free(chr_info);
+                                       return FALSE;
+                               }
+                               g_error_free(err);
+                       }
+                       return FALSE;
+               }
+
+               if (len > 0) {
+
+                       bt_gatt_notify_req_t char_val;
+                       BT_INFO("FD io sending  value changed %s %d \ni", buffer, len);
+
+                       char_val.val = g_malloc0(len + 1);
+
+                       memcpy(char_val.UUID, chr_info->UUID, 16);
+                       memcpy(char_val.val, buffer, len);
+                       char_val.len = len;
+                       memcpy(char_val.adress, chr_info->adress, 18);
+
+                       event_info = _bt_event_get_cb_data(BT_GATT_CLIENT_EVENT);
+
+                       if (event_info) {
+
+                               _bt_common_event_cb(BLUETOOTH_EVENT_GATT_CHAR_VAL_CHANGED,
+                                                       BLUETOOTH_ERROR_NONE, &char_val,
+                                               event_info->cb, event_info->user_data);
+                       } else {
+                               BT_ERR("eventinfo failed");
+                       }
+
+                       g_free(char_val.val);
+
+               }
+               g_free(buffer);
+
+               return TRUE;
+       }
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+               BT_ERR("Error : GIOCondition %d, [%s]", cond, chr_info->UUID);
+               g_io_channel_shutdown(gio, TRUE, NULL);
+               g_io_channel_unref(gio);
+
+               gatt_characteristic_notify_list = g_slist_remove(gatt_characteristic_notify_list, chr_info);
+               g_free(chr_info);
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static bt_gatt_characteristic_notify_info_t * bluetooth_gatt_client_get_characteristic_notify_info(unsigned  char *handle , int id)
+{
+       GSList *l;
+
+       for (l = gatt_characteristic_notify_list; l != NULL; l = l->next) {
+               bt_gatt_characteristic_notify_info_t *info = l->data;
+               if (memcmp(info->UUID, handle, 16) == 0 && info->id == id)
+                       return info;
+       }
+       return NULL;
+}
+
+static bt_gatt_characteristic_notify_info_t *  bluetooth_gatt_client_create_watch_io(int fd, int id, int mtu, char * address, unsigned char *uuid)
+{
+       GIOChannel *channel;
+       bt_gatt_characteristic_notify_info_t *chr_info;
+
+       chr_info = g_malloc0(sizeof(bt_gatt_characteristic_notify_info_t));
+       chr_info->notify_fd = fd;
+       chr_info->id = id;
+       chr_info->mtu = mtu;
+       g_strlcpy(chr_info->adress, address, 18);
+       memcpy(chr_info->UUID, uuid, 16);
+
+       channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL);
+       g_io_add_watch(channel, (G_IO_IN | G_IO_ERR | G_IO_HUP),
+                        bluetooth_gatt_client_notify_channel_watch_cb, chr_info);
+
+       return chr_info;
+
+}
+
 BT_EXPORT_API int bluetooth_gatt_client_watch_characteristics(
                        const char *address,
                        bt_gatt_handle_property_t *service_handle,
@@ -2156,12 +2282,24 @@ BT_EXPORT_API int bluetooth_gatt_client_watch_characteristics(
 {
        int result = BLUETOOTH_ERROR_NONE;
        bluetooth_gatt_client_char_prop_info_t param;
+       bt_gatt_characteristic_notify_info_t *chr_info;
+
        BT_DBG("+");
 
        BT_CHECK_PARAMETER(address, return);
        BT_CHECK_PARAMETER(service_handle, return);
        BT_CHECK_PARAMETER(char_handle, return);
 
+       chr_info = bluetooth_gatt_client_get_characteristic_notify_info(char_handle->uuid , char_handle->instance_id);
+       if (chr_info) {
+               BT_INFO("Already CCCD enabled. fd %d", chr_info->notify_fd);
+
+               if (!is_notify)
+                         close(chr_info->notify_fd);
+
+                       return result;
+       }
+
        /* ASync Function, result expected in callback from bt-service */
 
        BT_INIT_PARAMS();
@@ -2182,9 +2320,46 @@ BT_EXPORT_API int bluetooth_gatt_client_watch_characteristics(
        g_array_append_vals(in_param2, &client_id, sizeof(int));
        g_array_append_vals(in_param3, &is_notify, sizeof(gboolean));
 
-       result = _bt_send_request(BT_BLUEZ_SERVICE,
+
+       GUnixFDList *out_fd_list = NULL;
+
+       result  = _bt_send_request_with_unix_fd_list(BT_BLUEZ_SERVICE, BT_GATT_WATCH_CHARACTERISTIC,
+                                                       in_param1, in_param2, in_param3, in_param4, NULL, &out_param, &out_fd_list);
+       BT_DBG("result: %x", result);
+
+       if (result != BLUETOOTH_ERROR_NONE) {
+               BT_ERR("Fail to send request");
+               return result;
+       } else if (NULL == out_fd_list) {
+               BT_ERR("out_fd_list is NULL");
+
+       } else {
+
+               int *fd_list_array;
+               int len = 0;
+               int mtu;
+               int fd = -1;;
+
+               if (!out_fd_list)
+                       return BLUETOOTH_ERROR_INTERNAL;
+
+               fd_list_array = g_unix_fd_list_steal_fds(out_fd_list, &len);
+               BT_INFO("Num fds in fd_list is : %d, fd_list[0]: %d", len, fd_list_array[0]);
+               fd = fd_list_array[0];
+               mtu =  g_array_index(out_param, int, 0);
+
+               chr_info = bluetooth_gatt_client_create_watch_io(fd, char_handle->instance_id, mtu, (char *)address, char_handle->uuid);
+
+               gatt_characteristic_notify_list = g_slist_append(gatt_characteristic_notify_list, chr_info);
+
+               g_free(fd_list_array);
+               g_object_unref(out_fd_list);
+
+       }
+
+       /*result = _bt_send_request(BT_BLUEZ_SERVICE,
                        BT_GATT_WATCH_CHARACTERISTIC,
-                       in_param1, in_param2, in_param3, in_param4, &out_param);
+                       in_param1, in_param2, in_param3, in_param4, &out_param);*/
 
        BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
 
@@ -2287,6 +2462,85 @@ BT_EXPORT_API int bluetooth_gatt_client_read_descriptor_value(
        return result;
 }
 
+
+static bt_gatt_characteristic_write_info_t *  bluetooth_gatt_client_get_characteristic_fd(unsigned  char *handle, int id)
+{
+       GSList *l;
+       char  str[37];
+       _bt_convert_uuid_type_to_string(str, handle);
+       BT_INFO("request found UUID [%s], sid [ %d]", str, id);
+       for (l = gatt_characteristic_write_list; l != NULL; l = l->next) {
+               bt_gatt_characteristic_write_info_t *info = l->data;
+               _bt_convert_uuid_type_to_string(str, info->UUID);
+               BT_INFO("UUID [%s], sid [ %d]" , str, info->id);
+               if (memcmp(info->UUID, handle, 16) ==  0 &&  info->id == id)
+                       return info;
+       }
+       return NULL;
+}
+
+static gboolean  bluetooth_gatt_client_write_channel_watch_cb(GIOChannel *gio,
+                                       GIOCondition cond, gpointer data)
+{
+       bt_gatt_characteristic_write_info_t *chr_info = (bt_gatt_characteristic_write_info_t *)data;
+
+       if (!chr_info)
+               return FALSE;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+               BT_ERR("Error : GIOCondition %d, [%s]", cond, chr_info->UUID);
+               g_io_channel_shutdown(gio, TRUE, NULL);
+               g_io_channel_unref(gio);
+
+               gatt_characteristic_write_list = g_slist_remove(gatt_characteristic_write_list, chr_info);
+               g_free(chr_info);
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int  bluetooth_gatt_client_write_characteristics_value_to_fd(
+                        int fd, const guint8 *value, int length, int mtu,
+                       gpointer user_data)
+{
+               int written;
+               int att_result = BLUETOOTH_ERROR_NONE;
+               BT_CHECK_PARAMETER(value, return);
+               written = write(fd, value, length);
+               if (written != length) {
+                       att_result = BLUETOOTH_ERROR_INTERNAL;
+                       BT_INFO("write data failed  %d is ", written);
+               } else
+                       BT_INFO("write data %s is sucess ", value);
+
+               return att_result;
+}
+
+static void  bluetooth_gatt_client_create_write_io_channel(int fd, unsigned char * uuid, int id, int mtu)
+{
+       bt_gatt_characteristic_write_info_t *chr_info;
+       GIOChannel *channel;
+
+       chr_info = g_malloc0(sizeof(bt_gatt_characteristic_write_info_t));
+       chr_info->write_fd = fd;
+       chr_info->id = id;
+       chr_info->mtu = mtu;
+
+       memcpy(chr_info->UUID, uuid, 16);
+       channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL);
+       g_io_add_watch(channel, (G_IO_ERR | G_IO_HUP | G_IO_NVAL),
+                        bluetooth_gatt_client_write_channel_watch_cb, chr_info);
+
+       gatt_characteristic_write_list = g_slist_append(gatt_characteristic_write_list, chr_info);
+
+}
+
 BT_EXPORT_API int bluetooth_gatt_client_write_characteristic_value_by_type(
                const char *address,
                bt_gatt_handle_property_t *service_handle,
@@ -2325,10 +2579,75 @@ BT_EXPORT_API int bluetooth_gatt_client_write_characteristic_value_by_type(
        g_array_append_vals(in_param2, data, sizeof(bluetooth_gatt_att_data_t));
        g_array_append_vals(in_param3, &write_type, sizeof(bluetooth_gatt_write_type_e));
 
-       result = _bt_send_request_async(BT_BLUEZ_SERVICE,
+       if (write_type == BLUETOOTH_GATT_TYPE_WRITE_NO_RESPONSE) {
+               int fd = -1;
+               int mtu = 0;
+               bt_gatt_characteristic_write_info_t *info;
+               info =  bluetooth_gatt_client_get_characteristic_fd(char_handle->uuid, service_handle->instance_id);
+
+               if (info) {
+                       fd = info->write_fd;
+                       mtu = info->mtu;
+               }
+
+               if (fd < 0) {
+
+                       GUnixFDList *out_fd_list = NULL;
+
+                       result  = _bt_send_request_with_unix_fd_list(BT_BLUEZ_SERVICE, BT_GATT_ACQUIRE_WRITE,
+                                       in_param1, in_param2, in_param3, in_param4, NULL, &out_param, &out_fd_list);
+                       BT_DBG("result: %x", result);
+
+                       mtu =  g_array_index(out_param, int, 0);
+
+                       if (result != BLUETOOTH_ERROR_NONE) {
+                                       BT_ERR("Fail to send request");
+                                       BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
+                                       return result;
+                       } else if (NULL == out_fd_list) {
+                               BT_ERR("out_fd_list is NULL");
+                               return BLUETOOTH_ERROR_INTERNAL;
+                       } else {
+                               int *fd_list_array;
+                               int len = 0;
+
+                               if (!out_fd_list) {
+                                       BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);
+                                       return BLUETOOTH_ERROR_INTERNAL;
+                               }
+
+                               fd_list_array = g_unix_fd_list_steal_fds(out_fd_list, &len);
+                               BT_INFO("Num fds in fd_list is : %d, fd_list[0]: %d", len, fd_list_array[0]);
+                               fd = fd_list_array[0];
+
+                               g_free(fd_list_array);
+                               g_object_unref(out_fd_list);
+                       }
+                       BT_INFO("Acquired characteristic fd %d --------------- mtu %d,    ", fd, mtu);
+
+                       if (fd > -1) {
+
+                               bluetooth_gatt_client_create_write_io_channel(fd, char_handle->uuid, service_handle->instance_id, mtu);
+
+                               result =  bluetooth_gatt_client_write_characteristics_value_to_fd(fd, data->data, data->length, mtu, NULL);
+
+                       } else {
+                               BT_INFO(" characteristic info  FD is invalid\n");
+                               goto done;
+                       }
+
+               } else {
+                       BT_INFO("Acquired characteristic fd %d --------------- mtu %d,    ", fd, mtu);
+                       result =  bluetooth_gatt_client_write_characteristics_value_to_fd(fd, data->data, data->length, mtu, NULL);
+               }
+
+       } else {
+done:
+               result = _bt_send_request_async(BT_BLUEZ_SERVICE,
                        BT_GATT_WRITE_CHARACTERISTIC_VALUE_BY_TYPE,
                        in_param1, in_param2, in_param3, in_param4,
                        user_info->cb, user_info->user_data);
+       }
 
        BT_FREE_PARAMS(in_param1, in_param2, in_param3, in_param4, out_param);