From 4ac0ef65a7ce8c8770d705141f4c8e2b07538615 Mon Sep 17 00:00:00 2001 From: Ayush Garg Date: Tue, 15 Feb 2022 13:05:15 +0530 Subject: [PATCH] LE CoC: Add support for L2CAP_LE type socket 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 Signed-off-by: Anuj Jain --- src/adapter.c | 43 ++++ src/device.c | 692 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/device.h | 8 + 3 files changed, 743 insertions(+) diff --git a/src/adapter.c b/src/adapter.c index e9ae74b..da49ea0 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -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) diff --git a/src/device.c b/src/device.c index 2819c3d..c0cd8a1 100644 --- a/src/device.c +++ b/src/device.c @@ -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-" + * 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" }, diff --git a/src/device.h b/src/device.h index 517f7d9..fdd0d60 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, -- 2.7.4