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];
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;
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;
+}