From 553a4e0189b673b8aa1bee195c570c3afd8a3b7f Mon Sep 17 00:00:00 2001 From: Atul Rai Date: Wed, 13 Jul 2016 19:23:03 +0900 Subject: [PATCH] [Adapt: HAL] Implement rfcomm socket connect This patch adds dbus based implementation to handle rfcomm outgoing socket connection request in HAL. Change-Id: I1bd0ef13bb25e1b4ca4ac95ca3f4e137fda27f62 Signed-off-by: Atul Rai --- bt-oal/bluez_hal/CMakeLists.txt | 1 + bt-oal/bluez_hal/inc/bt-hal-msg.h | 7 + bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.c | 675 ++++++++++++++++++++++ bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.h | 42 ++ bt-oal/bluez_hal/src/bt-hal-socket.c | 9 +- 5 files changed, 731 insertions(+), 3 deletions(-) create mode 100644 bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.c create mode 100644 bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.h diff --git a/bt-oal/bluez_hal/CMakeLists.txt b/bt-oal/bluez_hal/CMakeLists.txt index d37b579..4d96cf2 100644 --- a/bt-oal/bluez_hal/CMakeLists.txt +++ b/bt-oal/bluez_hal/CMakeLists.txt @@ -13,6 +13,7 @@ SET(SRCS ./src/bt-hal-hidhost.c ./src/bt-hal-hid-dbus-handler.c ./src/bt-hal-socket.c +./src/bt-hal-rfcomm-dbus-handler.c ) SET(PREFIX ${CMAKE_INSTALL_PREFIX}) diff --git a/bt-oal/bluez_hal/inc/bt-hal-msg.h b/bt-oal/bluez_hal/inc/bt-hal-msg.h index 974960c..36b8f50 100644 --- a/bt-oal/bluez_hal/inc/bt-hal-msg.h +++ b/bt-oal/bluez_hal/inc/bt-hal-msg.h @@ -233,5 +233,12 @@ struct hal_ev_hidhost_handshake { uint8_t status; } __attribute__((packed)); +/* Bluetooth Socket HAL events */ +struct hal_ev_sock_connect { + short size; + uint8_t bdaddr[6]; + int channel; + int status; +} __attribute__((packed)); #endif //_BT_HAL_MSG_H_ diff --git a/bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.c b/bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.c new file mode 100644 index 0000000..d6eea7c --- /dev/null +++ b/bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.c @@ -0,0 +1,675 @@ +/* + * Bluetooth-frwk + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Atul Kumar Rai + * + * 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 +#include +#include + +#include "bt-hal-rfcomm-dbus-handler.h" +#include "bt-hal-dbus-common-utils.h" + +#define BT_HAL_RFCOMM_ID_MAX 245 +#define BT_HAL_RFCOMM_MAX_BUFFER_SIZE 1024 + +typedef struct { + char uuid[BT_HAL_UUID_STRING_LEN]; + char *device_path; + char *obj_path; + int object_id; + int id; + GSList *rfcomm_conns; +} rfcomm_cb_data_t; + +typedef struct { + char remote_addr[BT_HAL_ADDRESS_STRING_SIZE]; + int hal_fd; + unsigned int hal_watch; + int stack_fd; + unsigned int bt_watch; +} rfcomm_conn_info_t; + +static GSList *rfcomm_clients; +static int latest_id = -1; +static gboolean id_used[BT_HAL_RFCOMM_ID_MAX]; + +int __rfcomm_assign_id(void) +{ + int index; + + DBG("latest_id: %d", latest_id); + + index = latest_id + 1; + if (index >= BT_HAL_RFCOMM_ID_MAX) + index = 0; + + DBG("index: %d", index); + + while (id_used[index] == TRUE) { + if (index == latest_id) { + /* No available ID */ + ERR("All request ID is used"); + return -1; + } + + index++; + if (index >= BT_HAL_RFCOMM_ID_MAX) + index = 0; + } + + latest_id = index; + id_used[index] = TRUE; + DBG("Assigned Id: %d", latest_id); + + return latest_id; +} + +void __rfcomm_delete_id(int id) +{ + if (id >= BT_HAL_RFCOMM_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); +} + +static rfcomm_cb_data_t *__find_rfcomm_info_from_uuid(const char *uuid) +{ + GSList *l; + + for (l = rfcomm_clients; l != NULL; l = l->next) { + rfcomm_cb_data_t *info = l->data; + + if (g_strcmp0(info->uuid, uuid) == 0) + return info; + } + + return NULL; +} + +static rfcomm_cb_data_t *__find_rfcomm_info_from_path(const char *path) +{ + GSList *l; + + for (l = rfcomm_clients; l != NULL; l = l->next) { + rfcomm_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_cb_data(rfcomm_cb_data_t *cb_data) +{ + DBG("+"); + + if (cb_data->id >= 0) + __rfcomm_delete_id(cb_data->id); + + if (cb_data->object_id > 0) + _bt_unregister_gdbus_object(cb_data->object_id); + + if (cb_data->obj_path) { + INFO("Unregister profile"); + _bt_unregister_profile(cb_data->obj_path); + } + + g_free(cb_data->obj_path); + + g_free(cb_data->device_path); + g_free(cb_data); + + DBG("-"); +} + +static void __rfcomm_cb_data_remove(rfcomm_cb_data_t *info) +{ + if (!info) { + ERR("info == NULL"); + return; + } + + if (info->rfcomm_conns == NULL) { + INFO("No more device connected remove info"); + rfcomm_clients = g_slist_remove(rfcomm_clients, info); + __bt_free_cb_data(info); + } +} + +static rfcomm_conn_info_t *__find_conn_info_with_stack_fd(rfcomm_cb_data_t *info, int fd) +{ + GSList *l; + + for (l = info->rfcomm_conns; l != NULL; l = l->next) { + rfcomm_conn_info_t *conn_info = l->data; + + if (conn_info && (conn_info->stack_fd == fd)) + return conn_info; + } + + return NULL; +} + +static rfcomm_conn_info_t *__find_conn_info_with_hal_fd(rfcomm_cb_data_t *info, int fd) +{ + GSList *l; + for (l = info->rfcomm_conns; l != NULL; l = l->next) { + rfcomm_conn_info_t *conn_info = l->data; + + if (conn_info && (conn_info->hal_fd == fd)) + return conn_info; + } + + return NULL; +} + +static gint compare(gpointer *a, gpointer *b) +{ + rfcomm_conn_info_t *node = (rfcomm_conn_info_t *)a; + char *address = (char *)b; + return g_strcmp0(node->remote_addr, address); +} + +static rfcomm_conn_info_t *__get_conn_info_from_address(rfcomm_cb_data_t *info, + char *dev_address) +{ + GSList *l = NULL; + rfcomm_conn_info_t *conn_info = NULL; + l = g_slist_find_custom(info->rfcomm_conns, dev_address, + (GCompareFunc)compare); + if (l) + conn_info = l->data; + return conn_info; +} + +static void __bt_free_conn(rfcomm_conn_info_t *conn) +{ + DBG("+"); + + 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); + DBG("-"); +} + +static void __rfcomm_remove_conn_info_t(rfcomm_cb_data_t *info, char *address) +{ + rfcomm_conn_info_t *conn_info; + + conn_info = __get_conn_info_from_address(info, address); + if (conn_info) { + info->rfcomm_conns = g_slist_remove(info->rfcomm_conns, conn_info); + __bt_free_conn(conn_info); + } +} + +static int write_all(int fd, unsigned char *buf, int len) +{ + int sent = 0; + + 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; + unsigned int sent; + rfcomm_cb_data_t *info = data; + rfcomm_conn_info_t *conn_info; + unsigned char buff[BT_HAL_RFCOMM_MAX_BUFFER_SIZE]; + GError *err = NULL; + int fd; + + DBG("+"); + fd = g_io_channel_unix_get_fd(io); + conn_info = __find_conn_info_with_hal_fd(info, fd); + + 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_RFCOMM_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; + } + + DBG("len: %d", 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) { + ERR("write(): %s", strerror(errno)); + goto fail; + } + + DBG("-"); + return TRUE; +fail: + __rfcomm_remove_conn_info_t(info, conn_info->remote_addr); + __rfcomm_cb_data_remove(info); + return FALSE; +} + +static gboolean stack_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) +{ + unsigned int len; + unsigned int sent; + rfcomm_cb_data_t *info = data; + rfcomm_conn_info_t *conn_info; + unsigned char buff[BT_HAL_RFCOMM_MAX_BUFFER_SIZE]; + GError *err = NULL; + int fd; + + DBG("+"); + + fd = g_io_channel_unix_get_fd(io); + conn_info = __find_conn_info_with_stack_fd(info, fd); + + 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_RFCOMM_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; + } + + DBG("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) { + ERR("write(): %s", strerror(errno)); + goto fail; + } + + DBG("-"); + return TRUE; +fail: + __rfcomm_remove_conn_info_t(info, conn_info->remote_addr); + __rfcomm_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]; + rfcomm_cb_data_t *info; + rfcomm_conn_info_t *conn_info; + struct hal_ev_sock_connect ev; + GIOCondition cond; + int len; + GIOChannel *io; + + /* TODO: Temperary, later need to fill correct channel form correct place */ + int chan = 0; + + if (NULL == path || NULL == addr) { + ERR("NULL == path || NULL = addr"); + return -1; + } + + _bt_convert_addr_type_to_string(address, addr->address); + info = __find_rfcomm_info_from_path(path); + if (info == NULL) + return -1; + + conn_info = __get_conn_info_from_address(info, address); + if (conn_info == NULL) { + ERR("Device Address %s not found in connection list", address); + return -1; + } + + if (write(conn_info->hal_fd, &chan, sizeof(chan)) != sizeof(chan)) { + ERR("Error sending RFCOMM channel"); + goto fail; + } + + conn_info->stack_fd = fd; + DBG("Remote address: %s, RFCOMM fd: %d", address, conn_info->stack_fd); + + /* Send rfcomm connected event */ + memset(&ev, 0, sizeof(ev)); + ev.size = sizeof(ev); + memcpy(ev.bdaddr, addr->address, 6); + ev.status = BT_STATUS_SUCCESS; + len = write_all(conn_info->hal_fd, (unsigned char *)&ev, sizeof(ev)); + if (len < 0) { + ERR("%s", strerror(errno)); + 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_unref(io); + + /* Handle rfcomm 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_unref(io); + + return 0; +fail: + __rfcomm_remove_conn_info_t(info, address); + __rfcomm_cb_data_remove(info); + return -1; +} + +static void __bt_connect_response_cb(GDBusProxy *proxy, + GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + rfcomm_cb_data_t *cb_data; + char dev_address[BT_HAL_ADDRESS_STRING_SIZE]; + const char *path; + + DBG("+"); + + cb_data = user_data; + if (cb_data == NULL) { + ERR("cb_data == NULL"); + return; + } + + if (!g_dbus_proxy_call_finish(proxy, res, &error)) { + ERR("Error : %s \n", error->message); + path = g_dbus_proxy_get_object_path(proxy); + _bt_convert_device_path_to_address(path, dev_address); + __rfcomm_remove_conn_info_t(cb_data, dev_address); + __rfcomm_cb_data_remove(cb_data); + g_error_free(error); + } + + if (proxy) + g_object_unref(proxy); + + DBG("-"); +} + +static void __bt_discover_service_response_cb(GDBusProxy *proxy, + GAsyncResult *res, gpointer user_data) +{ + rfcomm_cb_data_t *cb_data; + int ret = 0; + GError *err = NULL; + bt_hal_register_profile_info_t info = {0}; + char dev_address[BT_HAL_ADDRESS_STRING_SIZE]; + const char *path; + + DBG("+"); + + cb_data = user_data; + if (!cb_data) { + ERR("cb_data == NULL"); + return; + } + + path = g_dbus_proxy_get_object_path(proxy); + _bt_convert_device_path_to_address(path, dev_address); + DBG("Device Adress [%s]", dev_address); + + g_dbus_proxy_call_finish(proxy, res, &err); + if (proxy) + g_object_unref(proxy); + if (err != NULL) { + ERR("Error occured in Proxy call [%s]\n", err->message); + __rfcomm_remove_conn_info_t(cb_data, dev_address); + __rfcomm_cb_data_remove(cb_data); + goto done; + } else { + INFO("Services are Updated checking required uuid is there"); + /* Check here for uuid present */ + ret = _bt_discover_service_uuids(dev_address, cb_data->uuid); + if (ret == BT_STATUS_SUCCESS) { + info.uuid = (char *)cb_data->uuid; + info.obj_path = cb_data->obj_path; + info.role = "client"; + + ret = _bt_register_profile(&info, FALSE); + if (ret < 0) + DBG("Error: register profile"); + ret = _bt_connect_profile(dev_address, cb_data->uuid, + __bt_connect_response_cb, cb_data); + if (ret != BT_STATUS_SUCCESS) { + ERR("ConnectProfile failed"); + __rfcomm_remove_conn_info_t(cb_data, dev_address); + __rfcomm_cb_data_remove(cb_data); + goto done; + } + } else { + ERR("remote uuid not found"); + __rfcomm_remove_conn_info_t(cb_data, dev_address); + __rfcomm_cb_data_remove(cb_data); + } + } +done: + if (err) + g_clear_error(&err); +} + +static rfcomm_cb_data_t *__get_rfcomm_cb_data(char *remote_uuid) +{ + int id; + int object_id; + char *path; + rfcomm_cb_data_t *cb_data; + + DBG("+"); + + cb_data = __find_rfcomm_info_from_uuid(remote_uuid); + if (!cb_data) { + id = __rfcomm_assign_id(); + if (id < 0) { + ERR("__rfcomm_assign_id failed"); + return NULL; + } + + path = g_strdup_printf("/org/socket/client/%d/%d", getpid(), id); + object_id = _bt_register_new_gdbus_object(path, __new_connection); + if (object_id < 0) { + ERR("_bt_register_new_gdbus_object failed"); + __rfcomm_delete_id(id); + return NULL; + } + + cb_data = g_malloc0(sizeof(rfcomm_cb_data_t)); + g_strlcpy(cb_data->uuid, remote_uuid, BT_HAL_UUID_STRING_LEN); + cb_data->obj_path = path; + cb_data->object_id = object_id; + cb_data->id = id; + } + + DBG("-"); + return cb_data; +} + +static rfcomm_conn_info_t *__rfcomm_create_conn_info(char *addr, int *sock) +{ + int fds[2] = {-1, -1}; + rfcomm_conn_info_t *conn; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + ERR("socketpair(): %s", strerror(errno)); + *sock = -1; + return NULL; + } + + conn = g_malloc0(sizeof(rfcomm_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]; + + DBG("hal_fd: %d, sock: %d", conn->hal_fd, *sock); + + return conn; +} + +int _bt_hal_dbus_handler_rfcomm_connect(unsigned char *addr, unsigned char *uuid, int *sock) +{ + int ret; + rfcomm_cb_data_t *cb_data; + rfcomm_conn_info_t *conn; + char remote_addr[BT_HAL_ADDRESS_STRING_SIZE]; + char remote_uuid[BT_HAL_UUID_SIZE]; + + if (!addr) { + ERR("remote_addr is NULL"); + return BT_STATUS_PARM_INVALID; + } + + if (!uuid) { + ERR("remote_uuid is NULL"); + return BT_STATUS_PARM_INVALID; + } + + if (!sock) { + ERR("sock is NULL"); + return BT_STATUS_PARM_INVALID; + } + + _bt_convert_uuid_type_to_string(remote_uuid, uuid); + cb_data = __get_rfcomm_cb_data(remote_uuid); + if (!cb_data) + return BT_STATUS_FAIL; + + _bt_convert_addr_type_to_string(remote_addr, addr); + DBG("Connecting to %s, uuid %s", remote_addr, remote_uuid); + conn = __rfcomm_create_conn_info(remote_addr, sock); + if (!conn) + return BT_STATUS_FAIL; + + cb_data->rfcomm_conns = g_slist_append(cb_data->rfcomm_conns, conn); + ret = _bt_discover_services(remote_addr, (char *)remote_uuid, + __bt_discover_service_response_cb, cb_data); + if (ret != BT_STATUS_SUCCESS) { + ERR("Error returned while service discovery"); + __rfcomm_remove_conn_info_t(cb_data, conn->remote_addr); + __rfcomm_cb_data_remove(cb_data); + return BT_STATUS_FAIL; + } + + if (g_slist_find(rfcomm_clients, cb_data) == NULL) { + INFO("Adding callback information to rfcomm_clients"); + rfcomm_clients = g_slist_append(rfcomm_clients, cb_data); + } else + INFO("Callback information is already added"); + + return BT_STATUS_SUCCESS; +} diff --git a/bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.h b/bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.h new file mode 100644 index 0000000..4ba1ac1 --- /dev/null +++ b/bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.h @@ -0,0 +1,42 @@ +/* Bluetooth-frwk + * + * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: Atul Kumar Rai + * + * 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_SOCKET_DBUS_HANDLER_H_ +#define _BT_HAL_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_rfcomm_connect( + unsigned char *addr, unsigned char *uuid, int *sock); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _BT_HAL_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 21f5586..b8b3d86 100644 --- a/bt-oal/bluez_hal/src/bt-hal-socket.c +++ b/bt-oal/bluez_hal/src/bt-hal-socket.c @@ -29,6 +29,7 @@ #include "bt-hal-log.h" #include "bt-hal-msg.h" #include "bt-hal-utils.h" +#include "bt-hal-rfcomm-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) @@ -59,9 +60,11 @@ static bt_status_t connect(const bt_bdaddr_t *bd_addr, btsock_type_t type, switch (type) { case BTSOCK_RFCOMM: - ERR("bt rfcomm socket type not supported"); - status = BT_STATUS_UNSUPPORTED; - goto failed; + /* Call rfcomm dbus handler to connect rfcomm client */ + status = _bt_hal_dbus_handler_rfcomm_connect( + (unsigned char *)bd_addr->address, + (unsigned char *)uuid, sock_fd); + break; case BTSOCK_L2CAP: ERR("bt l2cap socket type not supported"); status = BT_STATUS_UNSUPPORTED; -- 2.7.4