[Adapt: HAL] Implement rfcomm socket listen 36/87736/1
authorAtul Rai <a.rai@samsung.com>
Wed, 31 Aug 2016 04:40:59 +0000 (10:10 +0530)
committerAtul Rai <a.rai@samsung.com>
Wed, 31 Aug 2016 04:40:59 +0000 (10:10 +0530)
This patch adds dbus based implementation to create and listen
on rfcomm socket in HAL.

Change-Id: Ia8c4dad75ef22f4e14d3d0d7ba1c3696ccf08f64
Signed-off-by: Atul Rai <a.rai@samsung.com>
bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.c
bt-oal/bluez_hal/src/bt-hal-rfcomm-dbus-handler.h
bt-oal/bluez_hal/src/bt-hal-socket.c

index 8d94ec2..7cda23e 100644 (file)
@@ -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;
+}
index 4ba1ac1..7493421 100644 (file)
@@ -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 */
index b8b3d86..c50a481 100644 (file)
 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,