LE CoC: Add HAL implementation for L2CAP_LE type socket 00/299400/1
authorAyush Garg <ayush.garg@samsung.com>
Mon, 21 Feb 2022 07:43:45 +0000 (13:13 +0530)
committerAnuj Jain <anuj01.jain@samsung.com>
Wed, 27 Sep 2023 09:04:36 +0000 (14:34 +0530)
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

To avoid build conflict please merge this patch after merging bluez
patch: (Change-Id: I7f4f6ba5fe49b6d3fa05bfbc3a1b2bfdd269c6bb)

Change-Id: Ifd4297f77a327ded5b7f51a3bc56c72f1416c9f1
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
Signed-off-by: Anuj Jain <anuj01.jain@samsung.com>
bt-oal/bluez_hal/CMakeLists.txt
bt-oal/bluez_hal/src/bt-hal-agent.c
bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.c
bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.h
bt-oal/bluez_hal/src/bt-hal-l2cap-le-dbus-handler.c [new file with mode: 0644]
bt-oal/bluez_hal/src/bt-hal-l2cap-le-dbus-handler.h [new file with mode: 0644]
bt-oal/bluez_hal/src/bt-hal-socket.c
bt-oal/hardware/bt_sock.h

index e388be9..56d1833 100644 (file)
@@ -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})
index fcbe25a..230a3ec 100644 (file)
@@ -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"
 
@@ -850,6 +851,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-<psm in 12 digits>".
+                * 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,
index adc2151..66340c6 100644 (file)
@@ -85,6 +85,17 @@ static const gchar rfcomm_agent_xml[] =
 "  </interface>"
 "</node>";
 
+static GDBusNodeInfo *new_l2cap_le_conn_node;
+static const gchar l2cap_le_agent_xml[] =
+"<node name='/'>"
+" <interface name='org.bluez.l2cap_le'>"
+"     <method name='NewConnection'>"
+"          <arg type='o' name='object' direction='in'/>"
+"          <arg type='h' name='fd' direction='in'/>"
+"     </method>"
+"  </interface>"
+"</node>";
+
 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;
index 283d317..c858338 100644 (file)
@@ -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 (file)
index 0000000..d43b254
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <dlog.h>
+
+#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 (file)
index 0000000..419cbe2
--- /dev/null
@@ -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 <glib.h>
+#include <sys/types.h>
+
+#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_ */
index ac59bfb..37dae56 100644 (file)
@@ -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;
index 45b2356..fd0dae4 100644 (file)
@@ -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. */