LE CoC: Add support for L2CAP_LE type socket 15/299415/1 accepted/tizen_6.0_unified tizen_6.0 accepted/tizen/6.0/unified/20231010.005228
authorAyush Garg <ayush.garg@samsung.com>
Tue, 15 Feb 2022 07:35:15 +0000 (13:05 +0530)
committerAnuj Jain <anuj01.jain@samsung.com>
Wed, 27 Sep 2023 10:46:12 +0000 (16:16 +0530)
This patch adds following
- Implement method to create and remove L2CAP_LE socket
- Implement method to listen and connect to L2CAP_LE socket

Change-Id: I7f4f6ba5fe49b6d3fa05bfbc3a1b2bfdd269c6bb
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
Signed-off-by: Anuj Jain <anuj01.jain@samsung.com>
src/adapter.c
src/device.c
src/device.h

index e9ae74b..da49ea0 100644 (file)
@@ -4248,6 +4248,27 @@ static int adapter_le_set_missed_adv_data(uint8_t *p_data, uint8_t data_len,
        return 0;
 }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *adapter_get_psm_l2cap_le(DBusConnection *conn,
+                                               DBusMessage *msg, void *user_data)
+{
+       return get_psm_l2cap_le(conn, msg);
+}
+
+static DBusMessage *adapter_listen_l2cap_le_socket(DBusConnection *conn,
+                                               DBusMessage *msg, void *user_data)
+{
+       return listen_l2cap_le_socket(conn, msg, user_data);
+}
+
+static DBusMessage *adapter_remove_l2cap_le_socket(DBusConnection *conn,
+                                               DBusMessage *msg, void *user_data)
+{
+       return remove_l2cap_le_socket(conn, msg);
+}
+
+#endif
+
 static DBusMessage *adapter_start_custom_discovery(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
@@ -7846,6 +7867,15 @@ static const GDBusMethodTable adapter_methods[] = {
                                set_discovery_filter) },
        { GDBUS_ASYNC_METHOD("StopDiscovery", NULL, NULL, stop_discovery) },
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       { GDBUS_METHOD("GetPSML2capLE",
+                       GDBUS_ARGS({ "path", "o"}),
+                       GDBUS_ARGS({ "psm", "i" }),
+                       adapter_get_psm_l2cap_le) },
+       { GDBUS_METHOD("ListenL2capLESocket",
+                       GDBUS_ARGS({ "path", "o"}, { "psm", "i" }, { "options", "a{sv}" }),
+                       NULL, adapter_listen_l2cap_le_socket) },
+       { GDBUS_METHOD("RemoveL2capLESocket", GDBUS_ARGS({ "path", "o" }),
+                       NULL, adapter_remove_l2cap_le_socket) },
        { GDBUS_METHOD("StartCustomDiscovery",
                        GDBUS_ARGS({ "type", "s" }), NULL,
                        adapter_start_custom_discovery) },
@@ -11744,7 +11774,15 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
        struct btd_device *device;
        static guint id = 0;
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (!strncmp(uuid, L2CAP_LE_UUID_SUBSTR, strlen(L2CAP_LE_UUID_SUBSTR)))
+               device = btd_adapter_find_device(adapter, dst, BDADDR_LE_PUBLIC);
+       else
+               device = btd_adapter_find_device(adapter, dst, BDADDR_BREDR);
+#else
        device = btd_adapter_find_device(adapter, dst, BDADDR_BREDR);
+#endif
+
        if (!device)
                return 0;
 
@@ -11768,7 +11806,12 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
        auth->device = device;
        auth->adapter = adapter;
        auth->id = ++id;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       if (check_for_connection &&
+               strncmp(uuid, L2CAP_LE_UUID_SUBSTR, strlen(L2CAP_LE_UUID_SUBSTR)))
+#else
        if (check_for_connection)
+#endif
                auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth);
        else {
                if (adapter->auth_idle_id == 0)
index 2819c3d..c0cd8a1 100644 (file)
@@ -385,6 +385,44 @@ struct otc_conn_info {
 GSList *otc_connection_list = NULL;
 #endif
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define BT_L2CAP_LE_INTERFACE_NAME "org.bluez.l2cap_le"
+#define BTD_L2CAP_LE_PSM_MAX 0xFFFF
+#define UUID_LEN 37
+
+typedef enum {
+       BT_L2CAP_LE_CLIENT_ROLE = 0x00,
+       BT_L2CAP_LE_SERVER_ROLE,
+} bt_l2cap_le_role_e;
+
+struct l2cap_le_conn_info {
+       struct l2cap_le_profile_info *profile_info;
+       GIOChannel *io;
+       guint io_id;
+       bool connected;
+       const char *dev_path;
+       int psm;
+       guint auth_id;
+       char *auth_uuid;
+};
+
+struct l2cap_le_profile_info {
+       char *name;
+       char *owner;
+       char *path;
+       bt_l2cap_le_role_e role;
+       int psm;
+       BtIOSecLevel sec_level;
+       bool authorize;
+       guint id;
+       struct l2cap_le_conn_info *server;
+       GSList *conn;
+};
+
+GSList *l2cap_le_socket_list = NULL;
+#endif
+
+
 static int device_browse_gatt(struct btd_device *device, DBusMessage *msg);
 static int device_browse_sdp(struct btd_device *device, DBusMessage *msg);
 
@@ -4018,6 +4056,657 @@ static DBusMessage *listen_otc(DBusConnection *conn, DBusMessage *msg,
 }
 #endif
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/********************* L2CAP LE SOCKET IMPLEMENTATION *************************/
+static GSList *find_l2cap_le_profile_info(GSList *list, const char *owner,
+                                               const char *path)
+{
+       GSList *l;
+       for (l = list; l != NULL; l = g_slist_next(l)) {
+               struct l2cap_le_profile_info *info = l->data;
+
+               if (!info)
+                       continue;
+
+               if (g_strcmp0(info->owner, owner))
+                       continue;
+
+               if (!g_strcmp0(info->path, path))
+                       return l;
+       }
+
+       return NULL;
+}
+
+static int parse_l2cap_le_opt(struct l2cap_le_profile_info *info,
+                                               const char *key, DBusMessageIter *value)
+{
+       int type = dbus_message_iter_get_arg_type(value);
+       dbus_bool_t b;
+
+       if (strcasecmp(key, "RequireAuthentication") == 0) {
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(value, &b);
+
+               if (b)
+                       info->sec_level = BT_IO_SEC_MEDIUM;
+               else
+                       info->sec_level = BT_IO_SEC_LOW;
+       } else if (strcasecmp(key, "RequireAuthorization") == 0) {
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return -EINVAL;
+               dbus_message_iter_get_basic(value, &b);
+               info->authorize = b;
+       }
+
+       return 0;
+}
+
+static struct l2cap_le_profile_info *create_l2cap_le_socket(const char *owner,
+                                               const char *path, int psm, DBusMessageIter *opts)
+{
+       struct l2cap_le_profile_info *info = NULL;
+
+       if (psm < 0 || psm > BTD_L2CAP_LE_PSM_MAX)
+               return NULL;
+
+       info = g_malloc0(sizeof(struct l2cap_le_profile_info));
+
+       info->owner = g_strdup(owner);
+       info->path = g_strdup(path);
+       info->psm = psm;
+
+       while (dbus_message_iter_get_arg_type(opts) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter value, entry;
+               const char *key;
+
+               dbus_message_iter_recurse(opts, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (parse_l2cap_le_opt(info, key, &value) < 0)
+                       error("Invalid value for l2cap_le_socket option %s", key);
+
+               dbus_message_iter_next(opts);
+       }
+
+       if (!info->name)
+               info->name = g_strdup_printf("%s%s/%d", owner, path, psm);
+
+       return info;
+}
+
+static void l2cap_le_io_destroy(gpointer p)
+{
+       struct l2cap_le_conn_info *conn = p;
+
+       if (conn->io_id > 0)
+               g_source_remove(conn->io_id);
+
+       if (conn->io) {
+               g_io_channel_shutdown(conn->io, FALSE, NULL);
+               g_io_channel_unref(conn->io);
+       }
+
+       if (conn->auth_id != 0)
+               btd_cancel_authorization(conn->auth_id);
+
+       if (conn->auth_uuid)
+               free(conn->auth_uuid);
+
+       g_free(conn);
+}
+
+static gboolean l2cap_le_disconnected_cb(GIOChannel *chan,
+                                               GIOCondition cond, gpointer user_data)
+{
+       struct l2cap_le_conn_info *conn = user_data;
+       struct l2cap_le_profile_info *info = conn->profile_info;
+
+       DBG(" L2CAP LE socket disconnected role [%s] ",
+                       info->role == BT_L2CAP_LE_SERVER_ROLE ? "server" : "client");
+
+       g_dbus_emit_signal(dbus_conn, conn->dev_path,
+                       DEVICE_INTERFACE, "L2CAPLEDisconnected",
+                       DBUS_TYPE_INVALID);
+
+       conn->connected = false;
+
+       info->conn = g_slist_remove(info->conn, conn);
+       l2cap_le_io_destroy(conn);
+       return FALSE;
+}
+
+static void l2cap_le_connect_cb(GIOChannel *chan, GError *gerr,
+                                               gpointer user_data)
+{
+       struct l2cap_le_conn_info *conn = (struct l2cap_le_conn_info *) user_data;
+       struct l2cap_le_profile_info *info = conn->profile_info;
+       DBusMessage *msg = NULL;
+       DBusMessageIter iter;
+       const char *dev_path;
+       int fd;
+       GError *io_err = NULL;
+       char addr[18];
+
+       if (!bt_io_get(chan, &io_err,
+                               BT_IO_OPT_DEST, addr,
+                               BT_IO_OPT_INVALID)) {
+               if (gerr) {
+                       error("%s failed %s", info->name, gerr->message);
+                       g_error_free(io_err);
+                       io_err = NULL;
+               } else {
+                       error("Unable to get connect data for %s: %s",
+                               info->name, io_err->message);
+                       gerr = io_err;
+               }
+               goto drop;
+       }
+
+       if (gerr != NULL) {
+               error("%s failed to connect to %s: %s", info->name, addr,
+                                                               gerr->message);
+               goto drop;
+       }
+
+       conn->connected = true;
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       if (conn->io_id == 0)
+               conn->io_id = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                       l2cap_le_disconnected_cb, conn);
+
+       msg = dbus_message_new_method_call(info->owner,
+                                                       info->path,
+                                                       BT_L2CAP_LE_INTERFACE_NAME,
+                                                       "NewConnection");
+
+       if (!msg) {
+               error("Unable to create NewConnection call");
+               goto drop;
+       }
+
+       dev_path = conn->dev_path;
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &dev_path);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_UNIX_FD, &fd);
+
+       if (!g_dbus_send_message(dbus_conn, msg)) {
+               error("sending NewConnection failed");
+               dbus_message_unref(msg);
+               goto drop;
+       }
+
+       DBG("L2CAP_LE Connected fd [%d] addr [%s], role [%s]",fd, addr,
+                       info->role == BT_L2CAP_LE_SERVER_ROLE ? "server" : "client");
+       return;
+
+drop:
+       if (io_err)
+               g_error_free(io_err);
+
+       info->conn = g_slist_remove(info->conn, conn);
+       l2cap_le_io_destroy(conn);
+}
+
+static struct l2cap_le_conn_info *create_l2cap_le_conn(
+                                               struct l2cap_le_conn_info *server, GIOChannel *io,
+                                               bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type)
+{
+       struct l2cap_le_conn_info *conn;
+       GIOCondition cond;
+       struct btd_device *device;
+       struct btd_adapter *adapter = adapter_find(src);
+
+       if (!adapter) {
+               error("could not find adapter");
+               return NULL;
+       }
+
+       device = btd_adapter_get_device(adapter, dst, dst_type);
+       if (!device) {
+               error("could not find Device");
+               return NULL;
+       }
+
+       conn = g_new0(struct l2cap_le_conn_info, 1);
+       conn->io = g_io_channel_ref(io);
+       conn->profile_info = server->profile_info;
+       conn->dev_path = device_get_path(device);
+
+       cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+       conn->io_id = g_io_add_watch(io, cond, l2cap_le_disconnected_cb, conn);
+
+       return conn;
+}
+
+static void l2cap_le_auth(DBusError *err, void *user_data)
+{
+       struct l2cap_le_conn_info *conn = user_data;
+       struct l2cap_le_profile_info *info = conn->profile_info;
+       GError *gerr = NULL;
+       char addr[18];
+
+       conn->auth_id = 0;
+
+       bt_io_get(conn->io, &gerr, BT_IO_OPT_DEST, addr, BT_IO_OPT_INVALID);
+       if (gerr != NULL) {
+               error("Unable to get connect data for %s: %s",
+                                               info->name, gerr->message);
+               g_error_free(gerr);
+               goto drop;
+       }
+
+       if (err && dbus_error_is_set(err)) {
+               error("%s rejected %s: %s", info->name, addr, err->message);
+               goto drop;
+       }
+
+       if (!bt_io_accept(conn->io, l2cap_le_connect_cb, conn, NULL, &gerr)) {
+               error("bt_io_accept: %s", gerr->message);
+               g_error_free(gerr);
+               goto drop;
+       }
+
+       DBG("%s authorized to connect to %s", addr, info->name);
+       return;
+
+drop:
+       info->conn = g_slist_remove(info->conn, conn);
+       l2cap_le_io_destroy(conn);
+}
+
+static void l2cap_le_confirm(GIOChannel *io, gpointer user_data)
+{
+       struct l2cap_le_conn_info *server = user_data;
+       struct l2cap_le_profile_info *info = server->profile_info;
+       struct l2cap_le_conn_info *conn;
+       GError *gerr = NULL;
+       uint8_t dst_type;
+       bdaddr_t src, dst;
+       char addr[18];
+       char *uuid = g_malloc0(UUID_LEN * sizeof(char));
+
+       bt_io_get(io, &gerr,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_DEST_TYPE, &dst_type,
+                       BT_IO_OPT_DEST, addr,
+                       BT_IO_OPT_INVALID);
+       if (gerr != NULL) {
+               error("%s failed to get connect data: %s", info->name,
+                                                       gerr->message);
+               g_error_free(gerr);
+               return;
+       }
+
+       DBG("incoming connect from %s for authorization", addr);
+
+       conn = create_l2cap_le_conn(server, io, &src, &dst, dst_type);
+       if (conn == NULL)
+               return;
+
+       /* Use custom uuid of the form "FFFFFFFF-FFFF-FFFF-FFFF-<psm in 12 digits>"
+       *  for authorizing l2cap_le socket using existing flow.
+       *  This custom uuid will be used in the BT-OAL layer to identify the
+       *  l2cap_le socket authorization and extracting psm from it.
+       */
+       snprintf(uuid, UUID_LEN, "%s%012d", L2CAP_LE_UUID_SUBSTR, server->psm);
+       conn->auth_uuid = uuid;
+
+       conn->auth_id = btd_request_authorization(&src, &dst,
+                                       (const char *)conn->auth_uuid, l2cap_le_auth, conn);
+       if (conn->auth_id == 0) {
+               error("%s authorization failure", info->name);
+               l2cap_le_io_destroy(conn);
+               return;
+       }
+
+       info->conn = g_slist_append(info->conn, conn);
+
+       DBG("%s authorizing connection from %s", info->name, addr);
+}
+
+static void l2cap_le_direct_connect(GIOChannel *io, GError *err,
+                                               gpointer user_data)
+{
+       struct l2cap_le_conn_info *server = user_data;
+       struct l2cap_le_profile_info *info = server->profile_info;
+       struct l2cap_le_conn_info *conn;
+       GError *gerr = NULL;
+       uint8_t dst_type;
+       bdaddr_t src, dst;
+
+       bt_io_get(io, &gerr,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_DEST_TYPE, &dst_type,
+                       BT_IO_OPT_INVALID);
+       if (gerr != NULL) {
+               error("%s failed to get connect data: %s", info->name,
+                                                               gerr->message);
+               g_error_free(gerr);
+               return;
+       }
+
+       conn = create_l2cap_le_conn(server, io, &src, &dst, dst_type);
+       if (conn == NULL)
+               return;
+
+       info->conn = g_slist_append(info->conn, conn);
+
+       l2cap_le_connect_cb(io, err, conn);
+}
+
+static void _remove_l2cap_le_socket(struct l2cap_le_profile_info *info)
+{
+       l2cap_le_socket_list = g_slist_remove(l2cap_le_socket_list, info);
+
+       DBG("Removed \"%s\"", info->name);
+
+       if (info->server)
+               l2cap_le_io_destroy((gpointer)info->server);
+       g_slist_free_full(info->conn, l2cap_le_io_destroy);
+
+       g_free(info->name);
+       g_free(info->owner);
+       g_free(info->path);
+
+       g_free(info);
+}
+
+static void l2cap_le_socket_exited(DBusConnection *conn, void *user_data)
+{
+       struct l2cap_le_profile_info *info = user_data;
+
+       DBG("l2cap le server \"%s\" exited", info->name);
+
+       _remove_l2cap_le_socket(info);
+}
+
+static int _connect_l2cap_le(struct btd_device *device,
+                                               struct l2cap_le_profile_info *info)
+{
+       struct btd_adapter *adapter = device_get_adapter(device);
+       const bdaddr_t *src = btd_adapter_get_address(adapter);
+       uint8_t src_type = btd_adapter_get_le_address_type(adapter);
+       const bdaddr_t *dest = device_get_address(device);
+       uint8_t dest_type = device->bdaddr_type;
+       const char *dev_path = device_get_path(device);
+       GError *gerr = NULL;
+       struct l2cap_le_conn_info *conn = NULL;
+
+       conn = g_malloc0(sizeof(struct l2cap_le_conn_info));
+       info->conn = g_slist_append(info->conn, conn);
+
+       conn->io = bt_io_connect(l2cap_le_connect_cb,
+                                       conn, NULL, &gerr,
+                                       BT_IO_OPT_SOURCE_BDADDR, src,
+                                       BT_IO_OPT_SOURCE_TYPE, src_type,
+                                       BT_IO_OPT_DEST_BDADDR, dest,
+                                       BT_IO_OPT_DEST_TYPE, dest_type,
+                                       BT_IO_OPT_PSM, info->psm,
+                                       BT_IO_OPT_SEC_LEVEL, info->sec_level,
+                                       BT_IO_OPT_INVALID);
+
+       if (!conn->io) {
+               error("L2CAP_LE Connect failed : %s", gerr->message);
+               g_error_free(gerr);
+               _remove_l2cap_le_socket(info);
+               return -EIO;
+       }
+
+       info->role = BT_L2CAP_LE_CLIENT_ROLE;
+       conn->dev_path = dev_path;
+       conn->profile_info = info;
+       conn->connected = false;
+       g_io_channel_set_close_on_unref(conn->io, FALSE);
+
+       l2cap_le_socket_list = g_slist_append(l2cap_le_socket_list, info);
+       return 0;
+}
+
+static int _listen_l2cap_le(struct btd_adapter *adapter,
+                                               struct l2cap_le_profile_info *info)
+{
+       const bdaddr_t *src = btd_adapter_get_address(adapter);
+       uint8_t type = btd_adapter_get_le_address_type(adapter);
+       GError *gerr = NULL;
+       struct l2cap_le_conn_info *conn = NULL;
+       BtIOConfirm confirm;
+       BtIOConnect connect;
+       int psm = 0;
+
+       if (info->authorize) {
+               confirm = l2cap_le_confirm;
+               connect = NULL;
+       } else {
+               confirm = NULL;
+               connect = l2cap_le_direct_connect;
+       }
+
+       conn = g_malloc0(sizeof(struct l2cap_le_conn_info));
+       info->server = conn;
+
+       conn->io = bt_io_listen(connect, confirm, (gpointer)conn,
+                                                       NULL, &gerr,
+                                                       BT_IO_OPT_SOURCE_BDADDR, src,
+                                                       BT_IO_OPT_SOURCE_TYPE, type,
+                                                       BT_IO_OPT_PSM, info->psm,
+                                                       BT_IO_OPT_SEC_LEVEL, info->sec_level,
+                                                       BT_IO_OPT_INVALID);
+
+    if (!conn->io) {
+               error("L2cap_LE Listen failed : %s", gerr->message);
+               g_error_free(gerr);
+               _remove_l2cap_le_socket(info);
+               return -EIO;
+    }
+
+       bt_io_get(conn->io, &gerr, BT_IO_OPT_PSM, &psm, BT_IO_OPT_INVALID);
+
+       DBG("L2CAP LE Socket listen to PSM %d successful", psm);
+
+       conn->connected = false;
+       info->role = BT_L2CAP_LE_SERVER_ROLE;
+       conn->psm = psm;
+       conn->profile_info = info;
+       g_io_channel_set_close_on_unref(conn->io, FALSE);
+
+       l2cap_le_socket_list = g_slist_append(l2cap_le_socket_list, info);
+
+       return 0;
+}
+
+static DBusMessage *connect_l2cap_le_socket(DBusConnection *conn,
+                                               DBusMessage *msg, void *user_data)
+{
+       struct btd_device *device = user_data;
+       GSList *l;
+       const char *path, *sender;
+       dbus_int32_t psm;
+       DBusMessageIter args, opts;
+       struct l2cap_le_profile_info *info = NULL;
+
+       if (device == NULL)
+               return btd_error_invalid_args(msg);
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_iter_init(msg, &args);
+       dbus_message_iter_get_basic(&args, &path);
+       dbus_message_iter_next(&args);
+
+       dbus_message_iter_get_basic(&args, &psm);
+       dbus_message_iter_next(&args);
+
+       l = find_l2cap_le_profile_info(l2cap_le_socket_list,  sender, path);
+
+       if (l) {
+               struct l2cap_le_profile_info *info = l->data;
+               if (info->conn) {
+                       struct l2cap_le_conn_info *conn_info = info->conn->data;
+                       if (conn_info && conn_info->connected)
+                               return btd_error_already_connected(msg);
+                       else
+                               return btd_error_busy(msg);
+               } else {
+                       error("Something is wrong!!!");
+                       return btd_error_failed(msg, "ConnectFailed due to some internal error");
+               }
+       }
+
+       if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&args, &opts);
+
+       info = create_l2cap_le_socket(sender, path, psm, &opts);
+       if (!info)
+               return btd_error_invalid_args(msg);
+
+       info->role = BT_L2CAP_LE_CLIENT_ROLE;
+
+       DBG("connect l2cap l2 socket channel %d", (int)psm);
+
+       if (_connect_l2cap_le(device, info) != 0)
+               return btd_error_failed(msg, "ConnectFailed");
+
+       info->id = g_dbus_add_disconnect_watch(conn, sender, l2cap_le_socket_exited,
+                                                       info, NULL);
+
+       return dbus_message_new_method_return(msg);
+}
+
+DBusMessage *listen_l2cap_le_socket(DBusConnection *conn, DBusMessage *msg,
+                                               void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       GSList *l;
+       const char *path, *sender;
+       dbus_int32_t psm;
+       DBusMessageIter args, opts;
+       struct l2cap_le_profile_info *info = NULL;
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_iter_init(msg, &args);
+       dbus_message_iter_get_basic(&args, &path);
+       dbus_message_iter_next(&args);
+
+       dbus_message_iter_get_basic(&args, &psm);
+       dbus_message_iter_next(&args);
+
+       l = find_l2cap_le_profile_info(l2cap_le_socket_list, sender, path);
+
+       if (l)
+               return btd_error_already_exists(msg);
+
+       if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&args, &opts);
+
+       info = create_l2cap_le_socket(sender, path, psm, &opts);
+       if (!info)
+               return btd_error_invalid_args(msg);
+
+       info->role = BT_L2CAP_LE_SERVER_ROLE;
+
+       DBG("listen l2cap_le socket to psm %d", (int)psm);
+
+       if (_listen_l2cap_le(adapter, info) != 0)
+               return btd_error_failed(msg, "ListenFailed");
+
+       info->id = g_dbus_add_disconnect_watch(conn, sender, l2cap_le_socket_exited,
+                                                       info, NULL);
+
+       return dbus_message_new_method_return(msg);
+}
+
+DBusMessage *get_psm_l2cap_le(DBusConnection *conn, DBusMessage *msg)
+{
+       const char *path, *sender;
+       int psm;
+       GSList *l;
+       DBusMessage *reply;
+       DBusMessageIter args;
+       struct l2cap_le_profile_info *info;
+
+       sender = dbus_message_get_sender(msg);
+
+       DBG("sender %s", sender);
+
+       dbus_message_iter_init(msg, &args);
+
+       dbus_message_iter_get_basic(&args, &path);
+       dbus_message_iter_next(&args);
+
+       l = find_l2cap_le_profile_info(l2cap_le_socket_list, sender, path);
+
+       if (!l) {
+               DBG("L2cap LE socket not exist");
+               return btd_error_does_not_exist(msg);
+       }
+
+       info = l->data;
+
+       if(info->server)
+               psm = info->server->psm;
+       else
+               return btd_error_does_not_exist(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return btd_error_failed(msg,
+                                       "Failed to create reply.");
+
+       dbus_message_append_args(reply,
+                       DBUS_TYPE_UINT32, &psm,
+                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+DBusMessage *remove_l2cap_le_socket(DBusConnection *conn, DBusMessage *msg)
+{
+       const char *path, *sender;
+       struct l2cap_le_profile_info *info = NULL;
+       GSList *l;
+
+       sender = dbus_message_get_sender(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       DBG("remove socket sender %s path %s", sender, path);
+
+       l = find_l2cap_le_profile_info(l2cap_le_socket_list, sender, path);
+
+       if (!l) {
+               DBG("L2cap LE socket not exist");
+               return btd_error_does_not_exist(msg);
+       }
+
+       info = l->data;
+
+       g_dbus_remove_watch(conn, info->id);
+       _remove_l2cap_le_socket(info);
+
+       return dbus_message_new_method_return(msg);
+}
+#endif
+
 static DBusMessage *le_set_data_length(
                        DBusConnection *conn, DBusMessage *msg,
                        void *user_data)
@@ -4408,6 +5097,9 @@ static const GDBusMethodTable device_methods[] = {
        { GDBUS_METHOD("ConnectOtc", NULL, NULL, connect_otc) },
        { GDBUS_METHOD("DisconnectOtc", NULL, NULL, disconnect_otc) },
        { GDBUS_METHOD("ListenOtc", NULL, NULL, listen_otc) },
+       { GDBUS_ASYNC_METHOD("ConnectL2capLESocket",
+                       GDBUS_ARGS({ "path", "o"}, { "psm", "i" }, { "options", "a{sv}" }),
+                                               NULL, connect_l2cap_le_socket) },
 #endif
        { GDBUS_ASYNC_METHOD("LESetDataLength",
                        GDBUS_ARGS({"max_tx_octets", "q" },
index 517f7d9..fdd0d60 100644 (file)
@@ -33,6 +33,14 @@ struct btd_device;
 #define DEV_CONN_LE 0x01 /* Only LE*/
 #endif
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define L2CAP_LE_UUID_SUBSTR "FFFFFFFF-FFFF-FFFF-FFFF-"
+DBusMessage *get_psm_l2cap_le(DBusConnection *conn, DBusMessage *msg);
+DBusMessage *remove_l2cap_le_socket(DBusConnection *conn, DBusMessage *msg);
+DBusMessage *listen_l2cap_le_socket(DBusConnection *conn, DBusMessage *msg,
+                                                        void *user_data);
+#endif
+
 struct btd_device *device_create(struct btd_adapter *adapter,
                                const bdaddr_t *address, uint8_t bdaddr_type);
 struct btd_device *device_create_from_storage(struct btd_adapter *adapter,