" </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)
}
}
+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) {
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;
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;
--- /dev/null
+/*
+ * 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;
+}