summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAyush Garg <ayush.garg@samsung.com>2022-02-15 13:05:15 +0530
committerAnuj Jain <anuj01.jain@samsung.com>2023-09-27 14:29:51 +0530
commit11b1de93b1baaa9af62a59249aa532a76049101b (patch)
tree5119d8a4dc0b2dad219dcb226836ee4c13a9e9c7
parentcd5c5b9f23851c82da1ef9714425419dd88ed55a (diff)
downloadbluez-11b1de93b1baaa9af62a59249aa532a76049101b.tar.gz
bluez-11b1de93b1baaa9af62a59249aa532a76049101b.tar.bz2
bluez-11b1de93b1baaa9af62a59249aa532a76049101b.zip
LE CoC: Add support for L2CAP_LE type socketaccepted/tizen/6.5/unified/20231010.011950
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>
-rw-r--r--src/adapter.c43
-rw-r--r--src/device.c692
-rw-r--r--src/device.h8
3 files changed, 743 insertions, 0 deletions
diff --git a/src/adapter.c b/src/adapter.c
index 3502dc8c..4164a1ca 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -4333,6 +4333,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)
{
@@ -7995,6 +8016,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) },
@@ -12354,7 +12384,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;
@@ -12378,7 +12416,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)
diff --git a/src/device.c b/src/device.c
index 37acc5d2..f3e8aa1a 100644
--- a/src/device.c
+++ b/src/device.c
@@ -410,6 +410,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);
@@ -4186,6 +4224,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)
@@ -4576,6 +5265,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" },
diff --git a/src/device.h b/src/device.h
index 71c4981f..f749ed58 100644
--- a/src/device.h
+++ b/src/device.h
@@ -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,