From 67284854dc4bdbf9ab6ad23a5d4bf5a43af12a77 Mon Sep 17 00:00:00 2001 From: Ayush Garg Date: Mon, 21 Feb 2022 13:13:45 +0530 Subject: [PATCH] LE CoC: Add HAL implementation for L2CAP_LE type socket This patch adds following - Implement HAL interface to create and remove L2CAP_LE Socket - Implement HAL interface to listen and connect to L2CAP_LE Socket - Support for L2CAP_LE socket authorization Change-Id: Ifd4297f77a327ded5b7f51a3bc56c72f1416c9f1 Signed-off-by: Ayush Garg --- bt-oal/bluez_hal/CMakeLists.txt | 1 + bt-oal/bluez_hal/src/bt-hal-agent.c | 10 + bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.c | 305 +++++++ bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.h | 16 + .../bluez_hal/src/bt-hal-l2cap-le-dbus-handler.c | 918 +++++++++++++++++++++ .../bluez_hal/src/bt-hal-l2cap-le-dbus-handler.h | 45 + bt-oal/bluez_hal/src/bt-hal-socket.c | 33 +- bt-oal/hardware/bt_sock.h | 3 +- 8 files changed, 1323 insertions(+), 8 deletions(-) create mode 100644 bt-oal/bluez_hal/src/bt-hal-l2cap-le-dbus-handler.c create mode 100644 bt-oal/bluez_hal/src/bt-hal-l2cap-le-dbus-handler.h diff --git a/bt-oal/bluez_hal/CMakeLists.txt b/bt-oal/bluez_hal/CMakeLists.txt index e388be9..56d1833 100644 --- a/bt-oal/bluez_hal/CMakeLists.txt +++ b/bt-oal/bluez_hal/CMakeLists.txt @@ -39,6 +39,7 @@ SET(SRCS ./src/bt-hal-mesh-dbus-handler.c ./src/bt-hal-tds.c ./src/bt-hal-tds-dbus-handler.c +./src/bt-hal-l2cap-le-dbus-handler.c ) SET(PREFIX ${CMAKE_INSTALL_PREFIX}) diff --git a/bt-oal/bluez_hal/src/bt-hal-agent.c b/bt-oal/bluez_hal/src/bt-hal-agent.c index d9ac786..65f6446 100644 --- a/bt-oal/bluez_hal/src/bt-hal-agent.c +++ b/bt-oal/bluez_hal/src/bt-hal-agent.c @@ -50,6 +50,7 @@ #include "bt-hal-adapter-dbus-handler.h" #include "bt-hal-rfcomm-dbus-handler.h" +#include "bt-hal-l2cap-le-dbus-handler.h" #include "bt-hal-device-dbus-handler.h" #include "bt-hal-event-receiver.h" @@ -848,6 +849,15 @@ static gboolean __bt_hal_authorize_request(GapAgentPrivate *agent, GDBusProxy *d #endif #endif + if (_is_l2cap_le_server_uuid(uuid)) { + INFO("sending l2cap_le event for authorization"); + /* Here, it is a custom uuid of the form "FFFFFFFF-FFFF-FFFF-FFFF-". + * It will be used in FRWK-API layer to extract psm from it. + */ + __bt_hal_send_rfcomm_authorize_request_event(address, uuid, NULL, NULL, -1); + goto done; + } + if (trust || !headed_plugin_info->plugin_headed_enabled) { INFO("Trusted or Headless device, so authorize\n"); gap_agent_reply_authorize(agent, diff --git a/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.c b/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.c index adc2151..66340c6 100644 --- a/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.c +++ b/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.c @@ -85,6 +85,17 @@ static const gchar rfcomm_agent_xml[] = " " ""; +static GDBusNodeInfo *new_l2cap_le_conn_node; +static const gchar l2cap_le_agent_xml[] = +"" +" " +" " +" " +" " +" " +" " +""; + static GDBusConnection *__bt_hal_init_session_conn(void) { if (session_conn == NULL) @@ -1517,6 +1528,251 @@ static void __hal_new_connection_method(GDBusConnection *connection, } } +int _bt_hal_connect_l2cap_le(char *address, + bt_hal_l2cap_le_profile_info_t *info, void *cb, gpointer func_data) +{ + GVariantBuilder *option_builder; + char *object_path; + GDBusProxy *proxy; + GDBusConnection *conn; + GDBusProxy *adapter_proxy; + GError *error = NULL; + GVariant *result; + + DBG("+"); + conn = _bt_hal_get_system_gconn(); + if (conn == NULL) + return BT_HAL_ERROR_INTERNAL; + + object_path = _bt_hal_get_device_object_path(address); + if (object_path == NULL) { + ERR("No searched device"); + + adapter_proxy = _bt_hal_get_adapter_proxy(); + if (adapter_proxy == NULL) + return BT_HAL_ERROR_INTERNAL; + + result = g_dbus_proxy_call_sync(adapter_proxy, "CreateDevice", + g_variant_new("(s)", address), G_DBUS_CALL_FLAGS_NONE, + -1, NULL, &error); + + if (!result) { + if (error) { + ERR("CreateDevice Fail: %s", error->message); + g_error_free(error); + } + } else + g_variant_unref(result); + + object_path = _bt_hal_get_device_object_path(address); + } + + if (object_path == NULL) { + INFO("Not able to find device object"); + return BT_HAL_ERROR_INTERNAL; + } + + proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, + NULL, BT_HAL_BLUEZ_NAME, + object_path, BT_HAL_DEVICE_INTERFACE, NULL, NULL); + g_free(object_path); + + if (proxy == NULL) { + INFO("proxy is null"); + return BT_HAL_ERROR_INTERNAL; + } + + option_builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + if (info->authentication) + g_variant_builder_add(option_builder, "{sv}", + "RequireAuthentication", + g_variant_new_boolean(TRUE)); + + if (info->authorization) + g_variant_builder_add(option_builder, "{sv}", + "RequireAuthorization", + g_variant_new_boolean(TRUE)); + + g_dbus_proxy_call(proxy, "ConnectL2capLESocket", + g_variant_new("(oia{sv})", info->obj_path, + info->psm, option_builder), + G_DBUS_CALL_FLAGS_NONE, BT_HAL_MAX_DBUS_TIMEOUT, NULL, + (GAsyncReadyCallback)cb, func_data); + + g_variant_builder_unref(option_builder); + + DBG("-"); + return BT_HAL_ERROR_NONE; +} + +int _bt_hal_listen_l2cap_le(bt_hal_l2cap_le_profile_info_t *info) +{ + GVariantBuilder *option_builder; + GVariant *ret; + GDBusProxy *proxy; + GError *err = NULL; + int result = BT_STATUS_SUCCESS; + + DBG("+"); + proxy = _bt_hal_get_adapter_proxy(); + if (proxy == NULL) { + ERR("Getting adapter profile proxy failed"); + return BT_STATUS_FAIL; + } + + option_builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + if (info->authentication) + g_variant_builder_add(option_builder, "{sv}", + "RequireAuthentication", + g_variant_new_boolean(TRUE)); + + if (info->authorization) + g_variant_builder_add(option_builder, "{sv}", + "RequireAuthorization", + g_variant_new_boolean(TRUE)); + + ret = g_dbus_proxy_call_sync(proxy, "ListenL2capLESocket", + g_variant_new("(oia{sv})", info->obj_path, info->psm, + option_builder), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &err); + + if (err) { + ERR("Listen L2cap_LE failed: %s", err->message); + + if (g_strrstr(err->message, BT_HAL_ACCESS_DENIED_MSG)) + result = BT_STATUS_AUTH_REJECTED; + else + result = BT_STATUS_FAIL; + + g_clear_error(&err); + } + + g_variant_builder_unref(option_builder); + + if (ret) + g_variant_unref(ret); + + DBG("-"); + return result; +} + +void _bt_hal_remove_l2cap_le_socket(char *path) +{ + GVariant *ret; + GDBusProxy *proxy; + GError *err = NULL; + + proxy = _bt_hal_get_adapter_proxy(); + if (proxy == NULL) { + ERR("Getting adapter profile proxy failed"); + return; + } + + ret = g_dbus_proxy_call_sync(proxy, "RemoveL2capLESocket", + g_variant_new("(o)", path), + G_DBUS_CALL_FLAGS_NONE, -1, + NULL, &err); + if (err) { + ERR("Remove socket l2cap le failed : %s", err->message); + g_clear_error(&err); + } + + if (ret) + g_variant_unref(ret); + + return; +} + +int _bt_hal_get_psm_l2cap_le(bt_hal_l2cap_le_profile_info_t *info, uint32_t *psm) +{ + GVariant *ret; + GDBusProxy *proxy; + GError *err = NULL; + int result = BT_STATUS_SUCCESS; + + proxy = _bt_hal_get_adapter_proxy(); + if (proxy == NULL) { + ERR("Getting adapter profile proxy failed"); + return BT_STATUS_FAIL; + } + + ret = g_dbus_proxy_call_sync(proxy, "GetPSML2capLE", + g_variant_new("(o)", info->obj_path), + G_DBUS_CALL_FLAGS_NONE, -1, + NULL, &err); + if (err) { + ERR("Get PSM L2cap_LE failed: %s", err->message); + result = BT_STATUS_FAIL; + g_clear_error(&err); + return result; + } + + g_variant_get(ret, "(u)", psm); + INFO("PSM: %d", *psm); + + if (ret) + g_variant_unref(ret); + + return result; +} + +static void __hal_l2cap_le_new_connection_method(GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + INFO("method %s", method_name); + if (g_strcmp0(method_name, "NewConnection") == 0) { + INFO("L2cap_LE New connection method"); + int index; + int fd; + GUnixFDList *fd_list; + GDBusMessage *msg; + char *obj_path; + bt_bdaddr_t remote_addr1; + char addr[BT_HAL_ADDRESS_STRING_SIZE]; + bt_hal_new_connection_cb cb = user_data; + + g_variant_get(parameters, "(oh)", &obj_path, &index); + + msg = g_dbus_method_invocation_get_message(invocation); + fd_list = g_dbus_message_get_unix_fd_list(msg); + if (fd_list == NULL) { + ERR("No fd in message"); + GQuark quark = g_quark_from_string("l2cap_le-app"); + GError *err = g_error_new(quark, 0, "No fd in message"); + g_dbus_method_invocation_return_gerror(invocation, err); + g_error_free(err); + return; + } + + fd = g_unix_fd_list_get(fd_list, index, NULL); + if (fd == -1) { + ERR("Invalid fd return"); + GQuark quark = g_quark_from_string("l2cap_le-app"); + GError *err = g_error_new(quark, 0, "Invalid FD return"); + g_dbus_method_invocation_return_gerror(invocation, err); + g_error_free(err); + return; + } + + _bt_hal_convert_device_path_to_address(obj_path, addr); + _bt_hal_convert_addr_string_to_type(remote_addr1.address, (const char *)addr); + INFO("Object path %s, fd: %d, address %s", obj_path, fd, addr); + + g_dbus_method_invocation_return_value(invocation, NULL); + + if (cb) + cb(object_path, fd, &remote_addr1); + else + close(fd); + } +} + static GDBusNodeInfo *_bt_hal_get_gdbus_node(const gchar *xml_data) { if (bus_id == 0) { @@ -1543,6 +1799,12 @@ static const GDBusInterfaceVTable method_table = { NULL, }; +static const GDBusInterfaceVTable l2cap_le_method_table = { + __hal_l2cap_le_new_connection_method, + NULL, + NULL, +}; + int _bt_hal_register_new_gdbus_object(const char *path, bt_hal_new_connection_cb cb) { GDBusConnection *gconn; @@ -1585,6 +1847,49 @@ void _bt_hal_unregister_gdbus_object(int object_id) g_dbus_connection_unregister_object(gconn, object_id); } +int _bt_hal_register_new_l2cap_le_gdbus_object(const char *path, + bt_hal_new_connection_cb cb) +{ + GDBusConnection *gconn; + int id; + GError *error = NULL; + + gconn = _bt_hal_get_system_gconn(); + if (gconn == NULL) + return -1; + + if (new_l2cap_le_conn_node == NULL) + new_l2cap_le_conn_node = _bt_hal_get_gdbus_node(l2cap_le_agent_xml); + + if (new_l2cap_le_conn_node == NULL) + return -1; + + id = g_dbus_connection_register_object(gconn, path, + new_l2cap_le_conn_node->interfaces[0], + &l2cap_le_method_table, + cb, NULL, &error); + if (id == 0) { + ERR("Failed to register: %s", error->message); + g_error_free(error); + return -1; + } + + INFO("L2CAP_LE NEW CONNECTION ID %d", id); + + return id; +} + +void _bt_hal_unregister_l2cap_le_gdbus_object(int object_id) +{ + GDBusConnection *gconn; + + gconn = _bt_hal_get_system_gconn(); + if (gconn == NULL) + return; + + g_dbus_connection_unregister_object(gconn, object_id); +} + int _bt_hal_discover_services(char *address, char *uuid, void *cb, gpointer func_data) { char *object_path; diff --git a/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.h b/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.h index 283d317..c858338 100644 --- a/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.h +++ b/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.h @@ -429,6 +429,13 @@ extern "C" { char *service; } bt_hal_register_profile_info_t; + typedef struct { + char *obj_path; + int psm; + gboolean authentication; + gboolean authorization; + } bt_hal_l2cap_le_profile_info_t; + typedef int (*bt_hal_new_connection_cb) (const char *path, int fd, bt_bdaddr_t *address); GDBusProxy *_bt_hal_get_adapter_proxy(void); @@ -457,6 +464,11 @@ extern "C" { int _bt_hal_register_profile(bt_hal_register_profile_info_t *info, gboolean use_default_rfcomm); void _bt_hal_unregister_profile(char *path); + int _bt_hal_connect_l2cap_le(char *address, + bt_hal_l2cap_le_profile_info_t *info, void *cb, gpointer func_data); + int _bt_hal_listen_l2cap_le(bt_hal_l2cap_le_profile_info_t *info); + void _bt_hal_remove_l2cap_le_socket(char *path); + int _bt_hal_get_psm_l2cap_le(bt_hal_l2cap_le_profile_info_t *info, uint32_t *psm); int _bt_hal_discover_services(char *address, char *uuid, void *cb, gpointer func_data); int _bt_hal_cancel_discovers(char *address); @@ -465,6 +477,10 @@ extern "C" { int _bt_hal_register_new_gdbus_object(const char *path, bt_hal_new_connection_cb cb); void _bt_hal_unregister_gdbus_object(int object_id); + int _bt_hal_register_new_l2cap_le_gdbus_object(const char *path, + bt_hal_new_connection_cb cb); + void _bt_hal_unregister_l2cap_le_gdbus_object(int object_id); + char *_bt_hal_get_control_device_path(bt_bdaddr_t *bd_addr); void _bt_hal_set_control_device_path(const char *path); void _bt_hal_remove_control_device_path(const char *path); diff --git a/bt-oal/bluez_hal/src/bt-hal-l2cap-le-dbus-handler.c b/bt-oal/bluez_hal/src/bt-hal-l2cap-le-dbus-handler.c new file mode 100644 index 0000000..d43b254 --- /dev/null +++ b/bt-oal/bluez_hal/src/bt-hal-l2cap-le-dbus-handler.c @@ -0,0 +1,918 @@ +/* + * Bluetooth-frwk + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "bt-hal-l2cap-le-dbus-handler.h" +#include "bt-hal-dbus-common-utils.h" + +#define BT_HAL_L2CAP_LE_ID_MAX 245 +#define BT_HAL_L2CAP_LE_MAX_BUFFER_SIZE 1024 +#define L2CAP_LE_DEFAULT_CHANNEL 0 +#define L2CAP_LE_PSM_MAX 0xFFFF +#define L2CAP_LE_UUID_SUBSTR "FFFFFFFF-FFFF-FFFF-FFFF-" + +typedef struct { + char remote_addr[BT_HAL_ADDRESS_STRING_SIZE]; + int hal_fd; + unsigned int hal_watch; + int stack_fd; + unsigned int bt_watch; +} l2cap_le_conn_info_t; + +typedef struct { + int psm; + char *device_path; + char *obj_path; + int object_id; + int id; + l2cap_le_conn_info_t *conn_info; +} l2cap_le_cb_data_t; + +typedef struct { + int psm; + char *obj_path; + int object_id; + int id; + int server_fd; + unsigned int server_watch; + GSList *conn_list; +} l2cap_le_server_data_t; + +static GSList *l2cap_le_clients; +static GSList *l2cap_le_servers; +static int latest_id = -1; +static gboolean id_used[BT_HAL_L2CAP_LE_ID_MAX]; + +static int __l2cap_le_assign_id(void) +{ + int index; + + DBG("latest_id: %d", latest_id); + + index = latest_id + 1; + if (index >= BT_HAL_L2CAP_LE_ID_MAX) + index = 0; + + DBG("index: %d", index); + + while (id_used[index] == TRUE) { + if (index == latest_id) { + ERR("All request ID is used"); + return -1; + } + + index++; + if (index >= BT_HAL_L2CAP_LE_ID_MAX) + index = 0; + } + + latest_id = index; + id_used[index] = TRUE; + INFO("Assigned Id: %d", latest_id); + + return latest_id; +} + +static void __l2cap_le_delete_id(int id) +{ + if (id >= BT_HAL_L2CAP_LE_ID_MAX || id < 0) { + ERR("Invalid id %d", id); + return; + } + + id_used[id] = FALSE; + latest_id = id - 1; + DBG("id: %d, latest_id: %d", id, latest_id); +} + +/*************************** L2CAP_LE Client Implementation ***************************/ +static l2cap_le_cb_data_t *__find_l2cap_le_client_info_from_path(const char *path) +{ + GSList *l; + + for (l = l2cap_le_clients; l != NULL; l = l->next) { + l2cap_le_cb_data_t *info = l->data; + + if (info != NULL) + if (g_strcmp0(info->obj_path, path) == 0) + return info; + } + + return NULL; +} + +static void __bt_free_conn(l2cap_le_conn_info_t *conn) +{ + if (conn == NULL) + return; + + if (0 < conn->hal_fd) + close(conn->hal_fd); + + if (conn->hal_watch > 0) { + g_source_remove(conn->hal_watch); + conn->hal_watch = 0; + } + + if (0 < conn->stack_fd) + close(conn->stack_fd); + + if (conn->bt_watch > 0) { + g_source_remove(conn->bt_watch); + conn->bt_watch = 0; + } + + g_free(conn); +} + +static void __bt_free_cb_data(l2cap_le_cb_data_t *cb_data) +{ + DBG("+"); + + if (cb_data->id >= 0) + __l2cap_le_delete_id(cb_data->id); + + if (cb_data->object_id > 0) + _bt_hal_unregister_gdbus_object(cb_data->object_id); + + if (cb_data->obj_path) { + INFO("remove l2cap_le socket"); + _bt_hal_remove_l2cap_le_socket(cb_data->obj_path); + } + + g_free(cb_data->obj_path); + g_free(cb_data->device_path); + g_free(cb_data); + + DBG("-"); +} + +static void __l2cap_le_cb_data_remove(l2cap_le_cb_data_t *info) +{ + if (!info) { + ERR("info is NULL"); + return; + } + + l2cap_le_clients = g_slist_remove(l2cap_le_clients, info); + __bt_free_conn(info->conn_info); + __bt_free_cb_data(info); +} + +static int write_all(int fd, unsigned char *buf, int len) +{ + int sent = 0; + DBG("len %d", len); + + while (len > 0) { + int written; + + written = write(fd, buf, len); + if (written < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + + if (!written) + return 0; + + len -= written; buf += written; sent += written; + } + + return sent; +} + +static gboolean app_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) +{ + gsize len; + int sent; + l2cap_le_cb_data_t *info = data; + l2cap_le_conn_info_t *conn_info; + unsigned char buff[BT_HAL_L2CAP_LE_MAX_BUFFER_SIZE]; + GError *err = NULL; + int fd; + char err_msg[256] = {0, }; + + DBG("+"); + fd = g_io_channel_unix_get_fd(io); + conn_info = info->conn_info; + + if (cond & G_IO_HUP) { + ERR("Socket %d hang up", fd); + goto fail; + } + + if (cond & (G_IO_ERR | G_IO_NVAL)) { + ERR("Socket %d error", fd); + goto fail; + } + + if (!conn_info) { + ERR("conn_info is NULL"); + return TRUE; + } + + /* Read data from application */ + if (g_io_channel_read_chars(io, (gchar *)buff, + BT_HAL_L2CAP_LE_MAX_BUFFER_SIZE, + &len, &err) == G_IO_STATUS_ERROR) { + if (err) + ERR("IO Channel read error: %s", err->message); + else + ERR("IO Channel read error client"); + goto fail; + } + + INFO("len: %zu", len); + if (0 == len) { + ERR("Other end of socket is closed"); + goto fail; + } + + /* Send data to remote device */ + sent = write_all(conn_info->stack_fd, buff, len); + if (sent < 0) { + strerror_r(errno, err_msg, sizeof(err_msg)); + ERR("write(): %s", err_msg); + goto fail; + } + + DBG("-"); + return TRUE; +fail: + __l2cap_le_cb_data_remove(info); + return FALSE; +} + +static gboolean stack_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) +{ + unsigned int len; + int sent; + l2cap_le_cb_data_t *info = data; + l2cap_le_conn_info_t *conn_info; + unsigned char buff[BT_HAL_L2CAP_LE_MAX_BUFFER_SIZE]; + GError *err = NULL; + int fd; + char err_msg[256] = {0, }; + + DBG("+"); + + fd = g_io_channel_unix_get_fd(io); + conn_info = info->conn_info; + + if (cond & G_IO_HUP) { + ERR("Socket %d hang up", fd); + goto fail; + } + + if (cond & (G_IO_ERR | G_IO_NVAL)) { + ERR("Socket %d error", fd); + goto fail; + } + + if (!conn_info) { + ERR("conn_info is NULL"); + return TRUE; + } + + /* Read data from remote device */ + if (g_io_channel_read_chars(io, (gchar *)buff, + BT_HAL_L2CAP_LE_MAX_BUFFER_SIZE, + (gsize *)&len, &err) == G_IO_STATUS_ERROR) { + if (err) + ERR("IO Channel read error: %s", err->message); + else + ERR("IO Channel read error client"); + goto fail; + } + + INFO("len: %d", len); + if (0 == len) { + ERR("Other end of socket is closed"); + goto fail; + } + + /* Send data to application */ + sent = write_all(conn_info->hal_fd, buff, len); + if (sent < 0) { + strerror_r(errno, err_msg, sizeof(err_msg)); + ERR("write(): %s", err_msg); + goto fail; + } + + DBG("-"); + return TRUE; +fail: + __l2cap_le_cb_data_remove(info); + return FALSE; +} + +static int __new_connection(const char *path, int fd, bt_bdaddr_t *addr) +{ + char address[BT_HAL_ADDRESS_STRING_SIZE]; + l2cap_le_cb_data_t *info; + l2cap_le_conn_info_t *conn_info; + struct hal_ev_sock_connect ev; + GIOCondition cond; + int len; + GIOChannel *io; + char err_msg[256] = {0, }; + + /* TODO: Temperary, later need to fill correct psm */ + int psm = 0; + + if (NULL == path || NULL == addr) { + ERR("NULL == path || NULL = addr"); + return -1; + } + + info = __find_l2cap_le_client_info_from_path(path); + if (info == NULL) { + ERR("info is null"); + return -1; + } + + conn_info = info->conn_info; + _bt_hal_convert_addr_type_to_string(address, addr->address); + if (conn_info == NULL) { + ERR("conn_info is NULL for dev:[%s]", address); + return -1; + } + + if (write(conn_info->hal_fd, &psm, sizeof(psm)) != sizeof(psm)) { + ERR("Error sending l2cap_le channel"); + goto fail; + } + + conn_info->stack_fd = fd; + DBG("Remote address: %s, l2cap_le stack fd: %d, hal_fd %d", + address, conn_info->stack_fd, conn_info->hal_fd); + + /* Send l2cap_le connected event */ + memset(&ev, 0, sizeof(ev)); + ev.size = sizeof(ev); + memcpy(ev.bdaddr, addr->address, 6); + ev.status = BT_STATUS_SUCCESS; + + // TODO: Need to fill corrrect psm later on + ev.channel = psm; + + len = write_all(conn_info->hal_fd, (unsigned char *)&ev, sizeof(ev)); + if (len < 0) { + strerror_r(errno, err_msg, sizeof(err_msg)); + ERR("%s", err_msg); + goto fail; + } + + if (len != sizeof(ev)) { + ERR("Error sending connect event"); + goto fail;; + } + + /* Handle events from App */ + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + io = g_io_channel_unix_new(conn_info->hal_fd); + conn_info->hal_watch = g_io_add_watch(io, cond, app_event_cb, info); + g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_unref(io); + + /* Handle l2cap_le events from bluez */ + cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + io = g_io_channel_unix_new(conn_info->stack_fd); + conn_info->bt_watch = g_io_add_watch(io, cond, stack_event_cb, info); + g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_unref(io); + + return 0; +fail: + __l2cap_le_cb_data_remove(info); + return -1; +} + +static void __bt_connect_response_cb(GDBusProxy *proxy, GAsyncResult *res, + gpointer user_data) +{ + GError *error = NULL; + l2cap_le_cb_data_t *cb_data; + GVariant *result; + + DBG("+"); + + cb_data = user_data; + if (cb_data == NULL) { + ERR("cb_data == NULL"); + return; + } + + result = g_dbus_proxy_call_finish(proxy, res, &error); + if (!result) { + ERR("Error : %s \n", error->message); + __l2cap_le_cb_data_remove(cb_data); + g_error_free(error); + } else { + g_variant_unref(result); + } + + if (proxy) + g_object_unref(proxy); + + DBG("-"); +} + +static l2cap_le_cb_data_t *__get_l2cap_le_cb_data(int psm) +{ + int id; + int object_id; + char *path; + l2cap_le_cb_data_t *cb_data; + + DBG("+"); + + id = __l2cap_le_assign_id(); + if (id < 0) { + ERR("__l2cap_le_assign_id failed"); + return NULL; + } + + path = g_strdup_printf("/org/socket/client/%d/%d", getpid(), id); + object_id = _bt_hal_register_new_l2cap_le_gdbus_object(path, __new_connection); + if (object_id < 0) { + ERR("_bt_hal_register_new_l2cap_le_gdbus_object failed"); + g_free(path); + __l2cap_le_delete_id(id); + return NULL; + } + + cb_data = g_malloc0(sizeof(l2cap_le_cb_data_t)); + cb_data->psm = psm; + cb_data->obj_path = path; + cb_data->object_id = object_id; + cb_data->id = id; + + DBG("-"); + return cb_data; +} + +static l2cap_le_conn_info_t *__l2cap_le_create_conn_info(char *addr, int *sock) +{ + int fds[2] = {-1, -1}; + l2cap_le_conn_info_t *conn; + char err_msg[256] = {0, }; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + strerror_r(errno, err_msg, sizeof(err_msg)); + ERR("socketpair(): %s", err_msg); + *sock = -1; + return NULL; + } + + conn = g_malloc0(sizeof(l2cap_le_conn_info_t)); + g_strlcpy(conn->remote_addr, addr, BT_HAL_ADDRESS_STRING_SIZE); + conn->hal_fd = fds[0]; + conn->stack_fd = -1; + *sock = fds[1]; + + INFO("hal_fd: %d, sock: %d", conn->hal_fd, *sock); + + return conn; +} + +int _bt_hal_dbus_handler_l2cap_le_connect(unsigned char *addr, int psm, int *sock) +{ + int ret; + l2cap_le_cb_data_t *cb_data; + l2cap_le_conn_info_t *conn; + char remote_addr[BT_HAL_ADDRESS_STRING_SIZE]; + bt_hal_l2cap_le_profile_info_t profile_info; + + if (!addr) { + ERR("remote_addr is NULL"); + return BT_STATUS_PARM_INVALID; + } + + if (!sock) { + ERR("sock is NULL"); + return BT_STATUS_PARM_INVALID; + } + + cb_data = __get_l2cap_le_cb_data(psm); + if (!cb_data) { + ERR("cb_Data is NULL"); + return BT_STATUS_FAIL; + } + + _bt_hal_convert_addr_type_to_string(remote_addr, addr); + INFO("Connecting to %s, psm %d", remote_addr, psm); + conn = __l2cap_le_create_conn_info(remote_addr, sock); + if (!conn) { + INFO("connection is NULL"); + __bt_free_cb_data(cb_data); + return BT_STATUS_FAIL; + } + + cb_data->conn_info = conn; + + memset(&profile_info, 0x00, sizeof(bt_hal_l2cap_le_profile_info_t)); + + profile_info.obj_path = cb_data->obj_path; + profile_info.psm = psm; + + INFO("l2cap_le connect psm: %d", profile_info.psm); + + ret = _bt_hal_connect_l2cap_le(remote_addr, &profile_info, + __bt_connect_response_cb, cb_data); + + if (ret != BT_STATUS_SUCCESS) { + ERR("Error returned while connecting l2cap_le"); + __bt_free_conn(conn); + __bt_free_cb_data(cb_data); + return BT_STATUS_FAIL; + } + + INFO("Adding callback information to l2cap_le_clients"); + l2cap_le_clients = g_slist_append(l2cap_le_clients, cb_data); + + return BT_STATUS_SUCCESS; +} + +/*************************** L2CAP_LE Server Implementation ***************************/ +static l2cap_le_server_data_t *__find_l2cap_le_server_info_from_path(const gchar *path) +{ + GSList *l; + + for (l = l2cap_le_servers; l != NULL; l = l->next) { + l2cap_le_server_data_t *info = l->data; + + if (g_strcmp0(info->obj_path, path) == 0) + return info; + } + + return NULL; +} + +static l2cap_le_server_data_t *__find_l2cap_le_server_info_from_psm(int psm) +{ + GSList *l; + + for (l = l2cap_le_servers; l != NULL; l = l->next) { + l2cap_le_server_data_t *info = l->data; + + if (info->psm == psm) + return info; + } + + return NULL; +} + +static int __send_sock_fd(int sock_fd, const void *buf, int size, int send_fd) +{ + ssize_t ret; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + char cmsg_buf[CMSG_SPACE(sizeof(int))]; + char err_msg[256] = {0, }; + + if (sock_fd == -1 || send_fd == -1) + return -1; + + memset(&msg, 0, sizeof(msg)); + memset(cmsg_buf, 0, sizeof(cmsg_buf)); + + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd)); + + memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd)); + + iov.iov_base = (unsigned char *) buf; + iov.iov_len = size; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ret = sendmsg(sock_fd, &msg, MSG_NOSIGNAL); + if (ret < 0) { + strerror_r(errno, err_msg, sizeof(err_msg)); + ERR("sendmsg(): sock_fd %d send_fd %d: %s", + sock_fd, send_fd, err_msg); + } + + return ret; +} + +static int __new_server_connection(const char *path, int fd, bt_bdaddr_t *addr) +{ + int ret; + l2cap_le_server_data_t *info; + struct hal_ev_sock_connect ev; + + INFO("%s %d", path, fd); + + if (0 > fd) { + ERR("Invalid socket fd received"); + return -1; + } + + info = __find_l2cap_le_server_info_from_path(path); + if (info == NULL) { + ERR("l2cap_le server info not found"); + return -1; + } + + /* Send l2cap_le client connected event */ + memset(&ev, 0, sizeof(ev)); + ev.size = sizeof(ev); + memcpy(ev.bdaddr, addr->address, 6); + ev.status = BT_STATUS_SUCCESS; + ret = __send_sock_fd(info->server_fd, (void *)&ev, sizeof(ev), fd); + if (ret < 0) { + ERR("Error sending connect event"); + close(fd); + return -1; + } + + /* Remove local reference to client socket fd */ + close(fd); + return 0; +} + +static void __free_l2cap_le_server_data(l2cap_le_server_data_t *data) +{ + DBG("+"); + + if (!data) { + ERR("server data is NULL"); + return; + } + + if (data->id >= 0) + __l2cap_le_delete_id(data->id); + + if (data->object_id > 0) + _bt_hal_unregister_l2cap_le_gdbus_object(data->object_id); + + if (data->obj_path) { + INFO("Remove socket"); + _bt_hal_remove_l2cap_le_socket(data->obj_path); + } + + if (0 < data->server_fd) + close(data->server_fd); + + if (data->server_watch > 0) { + g_source_remove(data->server_watch); + data->server_watch = 0; + } + + g_free(data->obj_path); + g_free(data); + + DBG("-"); +} + +static void __remove_l2cap_le_server(l2cap_le_server_data_t *data) +{ + DBG("+"); + + l2cap_le_servers = g_slist_remove(l2cap_le_servers, data); + __free_l2cap_le_server_data(data); + + DBG("-"); +} + +static int __listen_l2cap_le_server(l2cap_le_server_data_t *server_data) +{ + int ret; + uint32_t psm; + bt_hal_l2cap_le_profile_info_t profile_info; + + INFO("+"); + + memset(&profile_info, 0x00, sizeof(bt_hal_l2cap_le_profile_info_t)); + + profile_info.authentication = TRUE; + profile_info.authorization = TRUE; + profile_info.obj_path = server_data->obj_path; + profile_info.psm = server_data->psm; + + INFO("Listen psm: %d", profile_info.psm); + + ret = _bt_hal_listen_l2cap_le(&profile_info); + if (ret < 0) { + ERR("Error: Listen L2CAP_LE server failed"); + return BT_STATUS_FAIL; + } + + if (server_data->psm == L2CAP_LE_DEFAULT_CHANNEL) { + ret = _bt_hal_get_psm_l2cap_le(&profile_info, &psm); + if (ret < 0) { + ERR("Error: get_psm failed"); + return BT_STATUS_FAIL; + } + INFO("l2cap_le server, listening to psm %d", psm); + server_data->psm = (int)psm; + } + + INFO("-"); + return BT_STATUS_SUCCESS; +} + +static l2cap_le_server_data_t *__create_l2cap_le_server(int psm, int *sock) +{ + int id; + int ret; + int object_id; + char *path; + int fds[2] = {-1, -1}; + l2cap_le_server_data_t *data; + char err_msg[256] = {0, }; + + INFO("+"); + + data = __find_l2cap_le_server_info_from_psm(psm); + if (data) { + ERR("L2CAP_LE Server exists with psm: %d", psm); + return NULL; + } + + id = __l2cap_le_assign_id(); + if (id < 0) { + ERR("__l2cap_le_assign_id failed"); + return NULL; + } + + path = g_strdup_printf("/org/socket/server/%d/%d", getpid(), id); + object_id = _bt_hal_register_new_l2cap_le_gdbus_object(path, __new_server_connection); + if (object_id < 0) { + ERR("_bt_hal_register_new_l2cap_le_gdbus_object failed"); + g_free(path); + __l2cap_le_delete_id(id); + return NULL; + } + + data = g_malloc0(sizeof(l2cap_le_server_data_t)); + data->obj_path = path; + data->object_id = object_id; + data->id = id; + data->psm = psm; + + ret = __listen_l2cap_le_server(data); + if (ret != BT_STATUS_SUCCESS) { + ERR("Error returned while registering service"); + __free_l2cap_le_server_data(data); + return NULL; + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + strerror_r(errno, err_msg, sizeof(err_msg)); + ERR("socketpair(): %s", err_msg); + __free_l2cap_le_server_data(data); + *sock = -1; + return NULL; + } + + data->server_fd = fds[0]; + *sock = fds[1]; + + INFO("server_fd: %d, sock: %d", data->server_fd, *sock); + return data; +} + +static gboolean __server_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) +{ + gsize len; + l2cap_le_server_data_t *info = data; + unsigned char buff[BT_HAL_L2CAP_LE_MAX_BUFFER_SIZE]; + GError *err = NULL; + + DBG("+"); + + if (cond & G_IO_HUP) { + ERR("Socket %d hang up", info->server_fd); + goto fail; + } + + if (cond & (G_IO_ERR | G_IO_NVAL)) { + ERR("Socket %d error", info->server_fd); + goto fail; + } + + /* Read data from application */ + if (g_io_channel_read_chars(io, (gchar *)buff, + BT_HAL_L2CAP_LE_MAX_BUFFER_SIZE, + &len, &err) == G_IO_STATUS_ERROR) { + if (err) + ERR("IO Channel read error: %s", err->message); + else + ERR("IO Channel read error client"); + goto fail; + } + + INFO("len: %zu", len); + if (0 == len) { + ERR("Other end of socket is closed"); + goto fail; + } + + DBG("-"); + return TRUE; +fail: + __remove_l2cap_le_server(info); + return FALSE; +} + +int _bt_hal_dbus_handler_l2cap_le_listen(int psm, int *sock) +{ + int new_psm = 0; + l2cap_le_server_data_t *server_data; + GIOCondition cond; + GIOChannel *io; + + if (!sock) { + ERR("sock is NULL"); + return BT_STATUS_PARM_INVALID; + } + + if (psm < 0 || psm > L2CAP_LE_PSM_MAX) { + ERR("Invalid PSM %d", psm); + return BT_STATUS_PARM_INVALID; + } + + server_data = __create_l2cap_le_server(psm, sock); + if (!server_data) + return BT_STATUS_BUSY; + + new_psm = server_data->psm; + INFO("sending l2cap_le psm %d", new_psm); + + if (write(server_data->server_fd, &new_psm, sizeof(new_psm)) != sizeof(new_psm)) { + ERR("Error sending l2cap_le psm"); + __free_l2cap_le_server_data(server_data); + *sock = -1; + return BT_STATUS_FAIL; + } + + /* L2CAP_LE server: Handle events from Application */ + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + io = g_io_channel_unix_new(server_data->server_fd); + server_data->server_watch = g_io_add_watch(io, cond, __server_event_cb, server_data); + g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_unref(io); + + INFO("Adding server information to l2cap_le_servers list"); + l2cap_le_servers = g_slist_append(l2cap_le_servers, server_data); + + return BT_STATUS_SUCCESS; +} + +gboolean _is_l2cap_le_server_uuid(const char *uuid) +{ + int psm; + const char *value; + DBG("+"); + + if (!uuid || + strncmp(uuid, L2CAP_LE_UUID_SUBSTR, strlen(L2CAP_LE_UUID_SUBSTR))) + return FALSE; + + value = uuid + strlen(L2CAP_LE_UUID_SUBSTR); + psm = atoi(value); + + if (NULL == __find_l2cap_le_server_info_from_psm(psm)) + return FALSE; + + DBG("-"); + return TRUE; +} diff --git a/bt-oal/bluez_hal/src/bt-hal-l2cap-le-dbus-handler.h b/bt-oal/bluez_hal/src/bt-hal-l2cap-le-dbus-handler.h new file mode 100644 index 0000000..419cbe2 --- /dev/null +++ b/bt-oal/bluez_hal/src/bt-hal-l2cap-le-dbus-handler.h @@ -0,0 +1,45 @@ +/* Bluetooth-frwk + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef _BT_HAL_L2CAP_LE_SOCKET_DBUS_HANDLER_H_ +#define _BT_HAL_L2CAP_LE_SOCKET_DBUS_HANDLER_H_ + +#include +#include + +#include "bt-hal.h" +#include "bt-hal-log.h" +#include "bt-hal-msg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int _bt_hal_dbus_handler_l2cap_le_connect( + unsigned char *addr, int psm, int *sock); + +int _bt_hal_dbus_handler_l2cap_le_listen( + int psm, int *sock); + +gboolean _is_l2cap_le_server_uuid(const char *uuid); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _BT_HAL_L2CAP_LE_SOCKET_DBUS_HANDLER_H_ */ diff --git a/bt-oal/bluez_hal/src/bt-hal-socket.c b/bt-oal/bluez_hal/src/bt-hal-socket.c index ac59bfb..37dae56 100644 --- a/bt-oal/bluez_hal/src/bt-hal-socket.c +++ b/bt-oal/bluez_hal/src/bt-hal-socket.c @@ -30,6 +30,7 @@ #include "bt-hal-msg.h" #include "bt-hal-utils.h" #include "bt-hal-rfcomm-dbus-handler.h" +#include "bt-hal-l2cap-le-dbus-handler.h" static bt_status_t listen(btsock_type_t type, const char *service_name, const uint8_t *uuid, int channel, int *sock_fd, int flags) @@ -38,15 +39,22 @@ static bt_status_t listen(btsock_type_t type, const char *service_name, DBG("+"); - if (service_name == NULL || sock_fd == NULL) { - ERR("invalid parameters, service_name:%p, uuid:%p, channel:%d, sock_fd:%p", - service_name, uuid, channel, sock_fd); + if (sock_fd == NULL) { + ERR("invalid parameters, sock_fd:%p", sock_fd); return BT_STATUS_PARM_INVALID; } - if (!uuid) { - ERR("Currently we only support to listen using UUID"); - return BT_STATUS_UNSUPPORTED; + if (type == BTSOCK_RFCOMM) { + if (service_name == NULL) { + ERR("invalid parameters, service_name:%p, uuid:%p, channel:%d, sock_fd:%p", + service_name, uuid, channel, sock_fd); + return BT_STATUS_PARM_INVALID; + } + + if (!uuid) { + ERR("Currently we only support to listen using UUID"); + return BT_STATUS_UNSUPPORTED; + } } INFO("channel: %d, sock_fd: %d, type: %d", channel, *sock_fd, type); @@ -65,6 +73,11 @@ static bt_status_t listen(btsock_type_t type, const char *service_name, ERR("bt sco socket not supported"); status = BT_STATUS_UNSUPPORTED; goto failed; + case BTSOCK_L2CAP_LE: + /* Call l2cap_le dbus handler to listen l2cap_le server */ + INFO("listening l2cap_le socket"); + status = _bt_hal_dbus_handler_l2cap_le_listen(channel, sock_fd); + break; default: ERR("unknown bt socket type:%d", type); status = BT_STATUS_UNSUPPORTED; @@ -92,7 +105,7 @@ static bt_status_t connect(const bt_bdaddr_t *bd_addr, btsock_type_t type, return BT_STATUS_PARM_INVALID; } - if (!uuid) { + if ((type == BTSOCK_RFCOMM) && !uuid) { ERR("Currently we only support to connect using UUID"); return BT_STATUS_UNSUPPORTED; } @@ -114,6 +127,12 @@ static bt_status_t connect(const bt_bdaddr_t *bd_addr, btsock_type_t type, ERR("bt sco socket not supported"); status = BT_STATUS_UNSUPPORTED; goto failed; + case BTSOCK_L2CAP_LE: + /* Call l2cap_le dbus handler to connect l2cap_le client */ + INFO("connect l2cap_le socket"); + status = _bt_hal_dbus_handler_l2cap_le_connect( + (unsigned char *)bd_addr->address, channel, sock_fd); + break; default: ERR("unknown bt socket type:%d", type); status = BT_STATUS_UNSUPPORTED; diff --git a/bt-oal/hardware/bt_sock.h b/bt-oal/hardware/bt_sock.h index 45b2356..fd0dae4 100644 --- a/bt-oal/hardware/bt_sock.h +++ b/bt-oal/hardware/bt_sock.h @@ -25,7 +25,8 @@ __BEGIN_DECLS typedef enum { BTSOCK_RFCOMM = 1, BTSOCK_SCO = 2, - BTSOCK_L2CAP = 3 + BTSOCK_L2CAP = 3, + BTSOCK_L2CAP_LE = 4 } btsock_type_t; /** Represents the standard BT SOCKET interface. */ -- 2.7.4