From 83c37861040eebb832440afd3294af056318369f Mon Sep 17 00:00:00 2001 From: Atul Rai Date: Wed, 31 Aug 2016 10:10:59 +0530 Subject: [PATCH] [Adapt: HAL] Implement rfcomm socket listen This patch adds dbus based implementation to create and listen on rfcomm socket in HAL. Change-Id: Ia8c4dad75ef22f4e14d3d0d7ba1c3696ccf08f64 Signed-off-by: Atul Rai --- bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.c | 338 ++++++++++++++++++++++ bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.h | 3 + bt-oal/bluez_hal/src/bt-hal-socket.c | 45 ++- 3 files changed, 384 insertions(+), 2 deletions(-) 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 index 8d94ec2..7cda23e 100644 --- a/bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.c +++ b/bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.c @@ -56,7 +56,19 @@ typedef struct { rfcomm_conn_info_t *conn_info; } rfcomm_cb_data_t; +typedef struct { + char uuid[BT_HAL_UUID_STRING_LEN]; + char *svc_name; + char *obj_path; + int object_id; + int id; + int server_fd; + unsigned int server_watch; + GSList *conn_list; +} rfcomm_server_data_t; + static GSList *rfcomm_clients; +static GSList *rfcomm_servers; static int latest_id = -1; static gboolean id_used[BT_HAL_RFCOMM_ID_MAX]; @@ -103,6 +115,7 @@ void __rfcomm_delete_id(int id) DBG("id: %d, latest_id: %d", id, latest_id); } +/*************************** RFCOMM Client Implementation ***************************/ static rfcomm_cb_data_t *__find_rfcomm_info_from_path(const char *path) { GSList *l; @@ -587,3 +600,328 @@ int _bt_hal_dbus_handler_rfcomm_connect(unsigned char *addr, unsigned char *uuid return BT_STATUS_SUCCESS; } + +/*************************** RFCOMM Server Implementation ***************************/ +static rfcomm_server_data_t *__find_rfcomm_server_info_with_path(const gchar *path) +{ + GSList *l; + + for (l = rfcomm_servers; l != NULL; l = l->next) { + rfcomm_server_data_t *info = l->data; + + if (g_strcmp0(info->obj_path, path) == 0) + return info; + } + + return NULL; +} + +static rfcomm_server_data_t *__find_rfcomm_server_info_from_uuid(const char *uuid) +{ + GSList *l; + + for (l = rfcomm_servers; l != NULL; l = l->next) { + rfcomm_server_data_t *info = l->data; + + if (g_strcmp0(info->uuid, uuid) == 0) + 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))]; + + 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) + ERR("sendmsg(): sock_fd %d send_fd %d: %s", + sock_fd, send_fd, strerror(errno)); + + return ret; +} + +int __new_server_connection(const char *path, int fd, bt_bdaddr_t *addr) +{ + int ret; + rfcomm_server_data_t *info; + struct hal_ev_sock_connect ev; + + DBG("%s %d", path, fd); + + if (0 > fd) { + ERR("Invalid socket fd received"); + return -1; + } + + info = __find_rfcomm_server_info_with_path(path); + if (info == NULL) { + ERR("rfcomm server info not found"); + return -1; + } + + /* Send rfcomm 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; + } + + return 0; +} + +static void __free_rfcomm_server_data(rfcomm_server_data_t *data) +{ + DBG("+"); + + if (!data) { + ERR("server data is NULL"); + return; + } + + if (data->id >= 0) + __rfcomm_delete_id(data->id); + + if (data->object_id > 0) + _bt_unregister_gdbus_object(data->object_id); + + if (data->obj_path) { + INFO("Unregister profile"); + _bt_unregister_profile(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->svc_name); + g_free(data); + + DBG("-"); +} + +static void __remove_rfcomm_server(rfcomm_server_data_t *data) +{ + + DBG("+"); + + rfcomm_servers = g_slist_remove(rfcomm_servers, data); + __free_rfcomm_server_data(data); + + DBG("-"); + +} + +static int __register_rfcomm_server(rfcomm_server_data_t *server_data) +{ + int ret; + bt_hal_register_profile_info_t profile_info; + + memset(&profile_info, 0x00, sizeof(bt_hal_register_profile_info_t)); + + profile_info.authentication = TRUE; + profile_info.authorization = TRUE; + profile_info.obj_path = server_data->obj_path; + profile_info.role = "server"; + profile_info.service = server_data->uuid; + profile_info.uuid = server_data->uuid; + + INFO("uuid %s, svc: %s, role: %s", + profile_info.uuid, profile_info.service, profile_info.role); + ret = _bt_register_profile(&profile_info, TRUE); + if (ret < 0) { + ERR("Error: register profile"); + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} + +static rfcomm_server_data_t *__create_rfcomm_server(char *uuid, const char *svc_name, int *sock) +{ + int id; + int ret; + int object_id; + char *path; + int fds[2] = {-1, -1}; + rfcomm_server_data_t *data; + + DBG("+"); + + data = __find_rfcomm_server_info_from_uuid(uuid); + if (data) { + ERR("RFCOMM Server exists with UUID: %s, sv_name: %s", uuid, data->svc_name); + return NULL; + } + + id = __rfcomm_assign_id(); + if (id < 0) { + ERR("__rfcomm_assign_id failed"); + return NULL; + } + + path = g_strdup_printf("/org/socket/server/%d/%d", getpid(), id); + object_id = _bt_register_new_gdbus_object(path, __new_server_connection); + if (object_id < 0) { + ERR("_bt_register_new_gdbus_object failed"); + __rfcomm_delete_id(id); + return NULL; + } + + data = g_malloc0(sizeof(rfcomm_server_data_t)); + g_strlcpy(data->uuid, uuid, BT_HAL_UUID_STRING_LEN); + data->svc_name = g_strdup(svc_name); + data->obj_path = path; + data->object_id = object_id; + data->id = id; + + ret = __register_rfcomm_server(data); + if (ret != BT_STATUS_SUCCESS) { + ERR("Error returned while registering service"); + __free_rfcomm_server_data(data); + return NULL; + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + ERR("socketpair(): %s", strerror(errno)); + __free_rfcomm_server_data(data); + *sock = -1; + return NULL; + } + + data->server_fd = fds[0]; + *sock = fds[1]; + + DBG("server_fd: %d, sock: %d", data->server_fd, *sock); + return data; +} + +static gboolean __server_event_cb(GIOChannel *io, GIOCondition cond, gpointer data) +{ + gsize len; + rfcomm_server_data_t *info = data; + unsigned char buff[BT_HAL_RFCOMM_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_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; + } + + DBG("-"); + return TRUE; +fail: + __remove_rfcomm_server(info); + return FALSE; +} + +int _bt_hal_dbus_handler_rfcomm_listen(const char *svc_name, unsigned char *uuid, int *sock) +{ + int chan = 0; + rfcomm_server_data_t *server_data; + char server_uuid[BT_HAL_UUID_STRING_LEN]; + GIOCondition cond; + GIOChannel *io; + + if (!svc_name) { + ERR("svc_name is NULL"); + return BT_STATUS_PARM_INVALID; + } + + if (!uuid) { + ERR("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(server_uuid, uuid); + server_data = __create_rfcomm_server(server_uuid, svc_name, sock); + if (!server_data) + return BT_STATUS_BUSY; + + /* TODO: Temperary, later need to fill correct channel form correct place */ + if (write(server_data->server_fd, &chan, sizeof(chan)) != sizeof(chan)) { + ERR("Error sending RFCOMM channel"); + __free_rfcomm_server_data(server_data); + *sock = -1; + return BT_STATUS_FAIL; + } + + /* Rfcomm 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 rfcomm_servers list"); + rfcomm_servers = g_slist_append(rfcomm_servers, server_data); + + DBG("-"); + 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 index 4ba1ac1..7493421 100644 --- a/bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.h +++ b/bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.h @@ -36,6 +36,9 @@ extern "C" { int _bt_hal_dbus_handler_rfcomm_connect( unsigned char *addr, unsigned char *uuid, int *sock); +int _bt_hal_dbus_handler_rfcomm_listen( + const char *svc_name, unsigned char *uuid, int *sock); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bt-oal/bluez_hal/src/bt-hal-socket.c b/bt-oal/bluez_hal/src/bt-hal-socket.c index b8b3d86..c50a481 100644 --- a/bt-oal/bluez_hal/src/bt-hal-socket.c +++ b/bt-oal/bluez_hal/src/bt-hal-socket.c @@ -34,8 +34,49 @@ static bt_status_t listen(btsock_type_t type, const char *service_name, const uint8_t *uuid, int channel, int *sock_fd, int flags) { - DBG(""); - return BT_STATUS_UNSUPPORTED; + bt_status_t status; + + 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); + 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); + + switch (type) { + case BTSOCK_RFCOMM: + /* Call rfcomm dbus handler to connect rfcomm client */ + status = _bt_hal_dbus_handler_rfcomm_listen( + service_name, (unsigned char *)uuid, sock_fd); + break; + case BTSOCK_L2CAP: + ERR("bt l2cap socket type not supported"); + status = BT_STATUS_UNSUPPORTED; + goto failed; + case BTSOCK_SCO: + ERR("bt sco socket not supported"); + status = BT_STATUS_UNSUPPORTED; + goto failed; + default: + ERR("unknown bt socket type:%d", type); + status = BT_STATUS_UNSUPPORTED; + goto failed; + } + + DBG("-"); + return status; + +failed: + *sock_fd = -1; + return status; } static bt_status_t connect(const bt_bdaddr_t *bd_addr, btsock_type_t type, -- 2.7.4