Handling le connection interval
[platform/core/connectivity/bluetooth-frwk.git] / bt-service / bt-service-device.c
index 3a50a4e..49bc041 100644 (file)
@@ -90,11 +90,28 @@ typedef struct {
        gboolean auto_connect;
 } bt_pending_le_conn_info_s;
 
+typedef struct {
+       char *address;
+       float interval_min;
+       float interval_max;
+       GSList *senders;
+} bt_connected_le_dev_t;
+
+typedef struct {
+       char *sender;
+       float interval_min;
+       float interval_max;
+       guint16 latency;
+       guint16 time_out;
+       float key;
+} bt_le_conn_param_t;
+
 gboolean is_device_creating;
 bt_funcion_data_t *bonding_info;
 bt_funcion_data_t *searching_info;
 bt_funcion_data_t *att_mtu_req_info;
 
+static GSList *le_connected_dev_list = NULL;
 static GSList *pin_info_list = NULL;
 static bt_pending_le_conn_info_s *pending_le_conn_info = NULL;
 static guint pending_le_conn_timer_id = 0;
@@ -2315,59 +2332,24 @@ int _bt_get_rssi_strength(bluetooth_device_address_t *bd_addr,
        return ret;
 }
 
-int _bt_le_conn_update(unsigned char *device_address,
+static int __bt_le_set_conn_parameter(const char *address,
                                float interval_min, float interval_max,
                                guint16 latency, guint16 time_out)
 {
-       char address[BT_ADDRESS_STRING_SIZE] = { 0 };
        gchar *device_path = NULL;
        GError *error = NULL;
        GDBusProxy *device_proxy = NULL;
        GDBusConnection *conn;
        GVariant *reply;
        guint32 min, max, to;
-       guint32 min_supervision_to;
        int ret = BLUETOOTH_ERROR_NONE;
 
-       BT_CHECK_PARAMETER(device_address, return);
+       BT_CHECK_PARAMETER(address, return);
 
        BT_INFO("Min interval: %f, Max interval: %f, Latency: %u, Supervision timeout: %u",
                        interval_min, interval_max, latency, time_out);
 
-       if (interval_min > interval_max ||
-                       interval_min < BT_LE_CONN_INTERVAL_MIN ||
-                       interval_max > BT_LE_CONN_INTERVAL_MAX) {
-               ret = BLUETOOTH_ERROR_INVALID_PARAM;
-               goto fail;
-       }
-
-       if (time_out < BT_LE_CONN_SUPER_TO_MIN ||
-                       time_out > BT_LE_CONN_SUPER_TO_MAX) {
-               ret = BLUETOOTH_ERROR_INVALID_PARAM;
-               goto fail;
-       }
-
-       if (latency > BT_LE_CONN_SLAVE_LATENCY_MAX) {
-               ret = BLUETOOTH_ERROR_INVALID_PARAM;
-               goto fail;
-       }
-
-       /*
-        * The Supervision_Timeout in milliseconds shall be larger than
-        * (1 + Conn_Latency) * Conn_Interval_Max * 2,
-        * where Conn_Interval_Max is given in milliseconds.
-        */
-       min_supervision_to = (1 + latency) * interval_max * 2;
-       if (time_out <= min_supervision_to) {
-               ret = BLUETOOTH_ERROR_INVALID_PARAM;
-               goto fail;
-       }
-
-       _bt_convert_addr_type_to_string(address, device_address);
-
-       BT_DBG("Remote device address: %s", address);
-
-       device_path = _bt_get_device_object_path(address);
+       device_path = _bt_get_device_object_path((char *)address);
 
        if (device_path == NULL) {
                BT_ERR("device_path NULL");
@@ -2418,6 +2400,306 @@ fail:
        return ret;
 }
 
+static void __bt_le_conn_param_free(void *data)
+{
+       bt_le_conn_param_t *param = (bt_le_conn_param_t *)data;
+
+       BT_DBG("%s", param->sender);
+       g_free(param->sender);
+       g_free(param);
+}
+
+static gint __bt_compare_le_conn_param_key(gpointer *a, gpointer *b)
+{
+       bt_le_conn_param_t *parama = (bt_le_conn_param_t *)a;
+       bt_le_conn_param_t *paramb = (bt_le_conn_param_t *)b;
+
+       return parama->key > paramb->key;
+}
+
+static bt_connected_le_dev_t *__bt_get_le_connected_dev_info(const char *address)
+{
+       GSList *l = NULL;
+       bt_connected_le_dev_t *dev;
+
+       if (!address)
+               return NULL;
+
+       for (l = le_connected_dev_list; l; l = g_slist_next(l)) {
+               dev = l->data;
+
+               if (g_strcmp0(dev->address, address) == 0)
+                       return dev;
+       }
+       return NULL;
+}
+
+static bt_le_conn_param_t *__bt_get_le_conn_param_info(bt_connected_le_dev_t *dev, const char *sender)
+{
+       GSList *l = NULL;
+       bt_le_conn_param_t *param = NULL;
+
+       if (!dev || !sender)
+               return NULL;
+
+       for (l = dev->senders; l; l = g_slist_next(l)) {
+               param = l->data;
+               if (g_strcmp0(param->sender, sender) == 0)
+                       return param;
+       }
+
+       return NULL;
+}
+
+int _bt_add_le_conn_param_info(const char *address, const char *sender,
+                       float interval_min, float interval_max, guint16 latency, guint16 time_out)
+{
+       bt_connected_le_dev_t *dev = NULL;
+       bt_le_conn_param_t *param = NULL;
+       bt_le_conn_param_t *data = NULL;
+
+       if (!address || !sender)
+               return BLUETOOTH_ERROR_INVALID_PARAM;
+
+       dev = __bt_get_le_connected_dev_info(address);
+       if (!dev)
+               return BLUETOOTH_ERROR_INTERNAL;
+
+       param = __bt_get_le_conn_param_info(dev, sender);
+
+       data = g_malloc0(sizeof(bt_le_conn_param_t));
+       data->sender = g_strdup(sender);
+       data->interval_min = interval_min;
+       data->interval_max = interval_max;
+       data->latency = latency;
+       data->time_out = time_out;
+       data->key = interval_min + (interval_max - interval_min)/2;
+
+       if (param == NULL) {
+               BT_DBG("Add param %s %s %f %f", address, sender, interval_min, interval_max);
+               dev->senders = g_slist_append(dev->senders, data);
+       } else {
+               BT_DBG("Update param %s %s %f %f", address, sender, interval_min, interval_max);
+               dev->senders = g_slist_remove(dev->senders, param);
+               g_free(param->sender);
+               g_free(param);
+               dev->senders = g_slist_append(dev->senders, data);
+       }
+
+       /* Sorting. First element have the minimum interval */
+       dev->senders = g_slist_sort(dev->senders,
+                                       (GCompareFunc)__bt_compare_le_conn_param_key);
+
+       return BLUETOOTH_ERROR_NONE;
+}
+
+int _bt_remove_le_conn_param_info(const char *address, const char *sender, gboolean *is_removed)
+{
+       bt_connected_le_dev_t *dev = NULL;
+       bt_le_conn_param_t *param = NULL;
+
+       if (!address || !sender)
+               return BLUETOOTH_ERROR_INVALID_PARAM;
+
+       dev = __bt_get_le_connected_dev_info(address);
+       if (!dev)
+               return BLUETOOTH_ERROR_INTERNAL;
+
+       param = __bt_get_le_conn_param_info(dev, sender);
+       if (param) {
+               BT_DBG("Remove param %s %s ", address, sender);
+               dev->senders = g_slist_remove(dev->senders, param);
+               g_free(param->sender);
+               g_free(param);
+               *is_removed = TRUE;
+       } else
+               *is_removed = FALSE;
+
+       return BLUETOOTH_ERROR_NONE;
+}
+
+int _bt_remove_all_le_conn_param_info(const char *sender)
+{
+       GSList *l = NULL;
+       bt_connected_le_dev_t *dev = NULL;
+       bt_le_conn_param_t *param = NULL;
+       gboolean is_removed = FALSE;
+       char *sender_new = NULL;
+       unsigned char addr[BLUETOOTH_ADDRESS_LENGTH];
+       int ret = BLUETOOTH_ERROR_NONE;
+
+       if (!sender)
+               return BLUETOOTH_ERROR_INVALID_PARAM;
+
+       for (l = le_connected_dev_list; l; l = g_slist_next(l)) {
+               dev = l->data;
+               _bt_remove_le_conn_param_info(dev->address, sender, &is_removed);
+
+               if (is_removed) {
+                       BT_INFO("Sender terminated. Update le conn interval [senders %d]",
+                                               g_slist_length(dev->senders));
+                       if (g_slist_length(dev->senders) > 0) {
+                               param = dev->senders->data;
+                               BT_DBG("dev %f %f, param %f %f", dev->interval_min, dev->interval_max,
+                                               param->interval_min, param->interval_max);
+
+                               if (dev->interval_min != param->interval_min ||
+                                       dev->interval_max != param->interval_max) {
+                                       sender_new = g_strdup(param->sender);
+
+                                       _bt_convert_addr_string_to_type(addr, dev->address);
+                                       ret = _bt_le_conn_update(sender_new, addr,
+                                                               param->interval_min,  param->interval_max,
+                                                               param->latency, param->time_out);
+                                       g_free(sender_new);
+
+                                       if (ret != BLUETOOTH_ERROR_NONE)
+                                               BT_ERR("Unable to set le connection parameter");
+                               }
+                       } else {
+                               BT_INFO("Set the default interval");
+
+                               bluetooth_le_connection_param_t param = { 0 };
+                                _bt_get_le_connection_parameter(
+                                                       BLUETOOTH_LE_CONNECTION_MODE_LOW_POWER,
+                                                       &param);
+
+                               ret = __bt_le_set_conn_parameter(dev->address,
+                                                               param.interval_min, param.interval_max,
+                                                               param.latency, param.timeout);
+                               if (ret == BLUETOOTH_ERROR_NONE) {
+                                       dev->interval_min = param.interval_min;
+                                       dev->interval_max = param.interval_max;
+                               }
+                       }
+               }
+       }
+
+       return ret;
+}
+
+void _bt_add_le_connected_dev_info(const char *address)
+{
+       bt_connected_le_dev_t *dev = NULL;
+
+       if (!address)
+               return;
+
+       dev = g_malloc0(sizeof(bt_connected_le_dev_t));
+       dev->address = g_strdup(address);
+
+       le_connected_dev_list = g_slist_append(le_connected_dev_list, dev);
+
+       return;
+}
+
+void _bt_remove_le_connected_dev_info(const char *address)
+{
+       bt_connected_le_dev_t *dev = NULL;
+
+       if (!address)
+               return;
+
+       dev = __bt_get_le_connected_dev_info(address);
+       if (!dev)
+               return;
+
+       g_slist_free_full(dev->senders, __bt_le_conn_param_free);
+       le_connected_dev_list = g_slist_remove(le_connected_dev_list, dev);
+       g_free(dev->address);
+       g_free(dev);
+
+       return;
+}
+
+int _bt_le_conn_update(const char *sender,
+                               unsigned char *device_address,
+                               float interval_min, float interval_max,
+                               guint16 latency, guint16 time_out)
+{
+       char address[BT_ADDRESS_STRING_SIZE] = { 0 };
+       guint32 min_supervision_to;
+       bt_connected_le_dev_t *dev = NULL;
+       bt_le_conn_param_t *param = NULL;
+       gboolean is_removed = FALSE;
+       int ret = BLUETOOTH_ERROR_NONE;
+
+       BT_CHECK_PARAMETER(device_address, return);
+
+       BT_INFO("Sender %s, Min interval: %f, Max interval: %f, Latency: %u, Supervision timeout: %u",
+                       sender, interval_min, interval_max, latency, time_out);
+
+       if (interval_min > interval_max ||
+                       interval_min < BT_LE_CONN_INTERVAL_MIN ||
+                       interval_max > BT_LE_CONN_INTERVAL_MAX) {
+               ret = BLUETOOTH_ERROR_INVALID_PARAM;
+               goto fail;
+       }
+
+       if (time_out < BT_LE_CONN_SUPER_TO_MIN ||
+                       time_out > BT_LE_CONN_SUPER_TO_MAX) {
+               ret = BLUETOOTH_ERROR_INVALID_PARAM;
+               goto fail;
+       }
+
+       if (latency > BT_LE_CONN_SLAVE_LATENCY_MAX) {
+               ret = BLUETOOTH_ERROR_INVALID_PARAM;
+               goto fail;
+       }
+
+       /*
+        * The Supervision_Timeout in milliseconds shall be larger than
+        * (1 + Conn_Latency) * Conn_Interval_Max * 2,
+        * where Conn_Interval_Max is given in milliseconds.
+        */
+       min_supervision_to = (1 + latency) * interval_max * 2;
+       if (time_out <= min_supervision_to) {
+               ret = BLUETOOTH_ERROR_INVALID_PARAM;
+               goto fail;
+       }
+
+       _bt_convert_addr_type_to_string(address, device_address);
+       BT_DBG("Remote device address: %s", address);
+
+       _bt_add_le_conn_param_info(address, sender, interval_min, interval_max, 0, 2000);
+
+       dev = __bt_get_le_connected_dev_info(address);
+       if (dev == NULL) {
+               ret = BLUETOOTH_ERROR_NOT_CONNECTED;
+               goto fail;
+       }
+
+       if (g_slist_length(dev->senders) == 1)
+               goto update;
+       else {
+               param = dev->senders->data;
+
+               BT_DBG("dev %f, param %f, input %f", dev->interval_min, param->interval_min, interval_min);
+
+               if (dev->interval_min == param->interval_min && dev->interval_max == param->interval_max) {
+                       BT_DBG("Skip due to same interval");
+                       return ret;
+               }
+
+               interval_min = param->interval_min;
+               interval_max = param->interval_max;
+       }
+
+update:
+       ret = __bt_le_set_conn_parameter(address, interval_min, interval_max, latency, time_out);
+
+       if (ret != BLUETOOTH_ERROR_NONE) {
+               _bt_remove_le_conn_param_info(address, sender, &is_removed);
+               return ret;
+       }
+
+       dev->interval_min = interval_min;
+       dev->interval_max = interval_max;
+
+fail:
+       return ret;
+}
+
 int _bt_set_pin_code(bluetooth_device_address_t *device_address,
                                bluetooth_device_pin_code_t *pin_code)
 {