[Adapt: OAL] Added socket_connect API in OAL 67/80067/1
authorAtul Rai <a.rai@samsung.com>
Tue, 12 Jul 2016 10:38:35 +0000 (19:38 +0900)
committerAtul Rai <a.rai@samsung.com>
Thu, 14 Jul 2016 09:55:38 +0000 (18:55 +0900)
This patch adds implementation for socket_connect API in OAL.

Change-Id: Ic099930e9c60620aa599d8e0e48546abb567e8db
Signed-off-by: Atul Rai <a.rai@samsung.com>
bt-oal/include/oal-event.h
bt-oal/include/oal-socket.h
bt-oal/oal-socket.c

index fdc4d7d..7a6de30 100755 (executable)
@@ -67,6 +67,8 @@ extern "C" {
        EVENT(OAL_EVENT_OAL_INITIALISED_FAILED)                 /* OAL Initialisation event */  \
        EVENT(OAL_EVENT_HID_CONNECTED)                                          /* event_hid_conn_t */\
        EVENT(OAL_EVENT_HID_DISCONNECTED)                                       /* event_hid_conn_t */\
+       EVENT(OAL_EVENT_SOCKET_OUTGOING_CONNECTED)                 /* RFCOMM */  \
+       EVENT(OAL_EVENT_SOCKET_DISCONNECTED)            /* RFCOMM */  \
        EVENT(OAL_EVENT_END)                                /* End of event*/\
 
 
@@ -143,6 +145,14 @@ typedef struct {
        oal_status_t status;
 } event_hid_conn_t;
 
+/********* Datastructures for Socket event ******************/
+/* SOCKET:: socket outgoing client connection event data */
+typedef struct {
+       int fd;                 /**< FD of Outgoing client */
+       int sock_type;          /**< Type of socket */
+       bt_address_t address;   /**< Address of remote server */
+} event_socket_client_conn_t;
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index 8b7c0f7..448ff6b 100755 (executable)
 extern "C" {
 #endif
 
+typedef enum {
+       OAL_SOCK_RFCOMM,
+       OAL_SOCK_SCO,
+       OAL_SOCK_L2CAP
+} oal_sock_type_t;
+
 /**
  * @fn oal_status_t socket_enable(void);
  * @brief Enables the rfcomm profile
@@ -55,6 +61,31 @@ oal_status_t socket_enable(void);
  * */
 oal_status_t socket_disable(void);
 
+/**
+ * @fn int  socket_connect(oal_sock_type_t sock_type, bt_address_t* bd, oal_uuid_t* p_uuid, int channel);
+ * @brief Creates outgoing client
+ *
+ * This API connects to the remote RFCOMM server. It takes either server UUID or Channel
+ * along with the bluetooth MAC address of Server.
+ * It returns the Outgoing Client FD. Two events are associated with this API:
+ *     1. OAL_EVENT_OUTGOING_CONNECTED: When Outgoing Client connects
+ *     succefully to remote Server.
+ *     2. OAL_EVENT_SOCKET_DISCONNECTED: When Outgoing Client fails to
+ *     connect/Outgoing client hung up.
+ * The FD received can be used to send data using interface socket_write. Data
+ * coming from remote interface comes through the callback registered at time of
+ * socket_init
+ * This function is a asynchronous call.
+ *
+ * @param[in]   sock_type  Bluetooth socket type to be connected
+ * @param[in]   p_uuid     UUID of the remote RFCOMM SERVER
+ * @param[in]   channel    channel
+ * @param[in]   bd         Bluetooth Mac address of RFCOMM server
+ * @return      Outgoing client FD --- Success
+ * @see         socket_disconnect
+ */
+int socket_connect(oal_sock_type_t sock_type, oal_uuid_t* p_uuid, int channel, bt_address_t* bd);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index 50f4250..59eb4bd 100755 (executable)
                } \
        } while (0)
 
+/* Definitions */
+#define MAX_RETRY 5
+#define SOCK_SHORT_LEN 2
+#define SOCK_INT_LEN 4
+#define SOCK_CONNECT_INFO_LEN 16
+#define SOCK_BD_ADDR_LEN 6
+
+typedef struct {
+       int fd;
+       int sock_type;
+       bt_address_t address;
+       guint control_id;
+       GIOChannel *control_io;
+} oal_client_info_t;
+
 /*
  * Global variables
  */
 static const btsock_interface_t* socket_api = NULL;
 
+static int getInt(char *buf, int len)
+{
+       int val = 0;
+
+       if(len != SOCK_INT_LEN)
+               return -1;
+       val = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+       return val;
+}
+
+static int getShort(char *buf, int len)
+{
+       int val = 0;
+
+       if(len != SOCK_SHORT_LEN)
+               return -1;
+       val = buf[0] | (buf[1] << 8);
+       return val;
+}
+
+static int getBdaddr(char *buf, int len, bt_bdaddr_t *bd)
+{
+       int val = 0;
+
+       if(len != SOCK_BD_ADDR_LEN)
+               return -1;
+       bd->address[0] = buf[0];
+       bd->address[1] = buf[1];
+       bd->address[2] = buf[2];
+       bd->address[3] = buf[3];
+       bd->address[4] = buf[4];
+       bd->address[5] = buf[5];
+       BT_DBG("Address: %.2X:%.2X:%.2X:%.2X:%.2X:%.2X",
+                       bd->address[0], bd->address[1], bd->address[2],
+                       bd->address[3], bd->address[4], bd->address[5]);
+       return val;
+}
+static void remove_io_channel(guint gid, GIOChannel *gch)
+{
+       if(gch != NULL)
+               g_io_channel_shutdown(gch, TRUE, NULL);
+       if(gid > 0)
+               g_source_remove(gid);
+}
+
+static int socket_process_cmsg(struct msghdr *pMsg, int * data_fd)
+{
+       struct cmsghdr *cmsgptr = NULL;
+
+       for (cmsgptr = CMSG_FIRSTHDR(pMsg);
+                       cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) {
+
+               if (cmsgptr->cmsg_level != SOL_SOCKET) {
+                       continue;
+               }
+
+               if (cmsgptr->cmsg_type == SCM_RIGHTS) {
+                       int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
+                       int count = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));
+
+                       if (count < 0) {
+                               BT_ERR("ERROR Invalid count of descriptors");
+                               continue;
+                       }
+
+                       BT_DBG("Server, socket fd for connection: %d", pDescriptors[0]);
+                       *data_fd = pDescriptors[0];
+               }
+       }
+
+       return 0;
+}
+
+static int socket_read(int fd, char *buf, size_t len, int *data_fd)
+{
+       int ret;
+       struct msghdr msg;
+       struct iovec iv;
+       struct cmsghdr cmsgbuf[2*sizeof(struct cmsghdr) + 0x100];
+       int retryCount = 0;
+       int flags = 0;
+
+       fd_set  toselect_fd;
+       struct timeval wait;
+
+       BT_INFO("socket_read, fd = %d", fd);
+
+       retv_if(fd < 0, -1);
+
+       memset(&msg, 0, sizeof(msg));
+       memset(&iv, 0, sizeof(iv));
+
+       iv.iov_base = buf;
+       iv.iov_len = len;
+
+       msg.msg_iov = &iv;
+       msg.msg_iovlen = 1;
+       msg.msg_control = cmsgbuf;
+       msg.msg_controllen = sizeof(cmsgbuf);
+
+       flags = fcntl(fd, F_GETFL, 0);
+       if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+               BT_ERR("Not able to change socket nonblocking");
+               return -2;
+       }
+
+       FD_ZERO(&toselect_fd);
+       FD_SET(fd, &toselect_fd);
+       wait.tv_sec = 1;
+       wait.tv_usec = 0;
+
+       ret = select(fd + 1, &toselect_fd, NULL, NULL, &wait);
+       if (ret < 0) {
+               fcntl(fd, F_SETFL, flags );
+               BT_ERR("Time out on selecy = %d", ret);
+               return -1;
+       }
+
+repeat:
+       retryCount ++;
+       ret = recvmsg(fd, &msg, 0); //MSG_NOSIGNAL);
+       BT_DBG("socket_read, recvmsg ret = %d", ret);
+       if(ret < 0 && errno == EINTR) {
+               if (retryCount < MAX_RETRY) {
+                       goto repeat;
+               } else {
+                       fcntl(fd, F_SETFL, flags );
+                       return -2;
+               }
+       }
+
+       if (ret < 0 && errno == EPIPE) {
+               // Treat this as an end of stream
+               fcntl(fd, F_SETFL, flags );
+               BT_ERR("EOS errno: %d", errno);
+               return 0;
+       }
+
+       if (ret < 0) {
+               fcntl(fd, F_SETFL, flags );
+               BT_ERR("Ret errno: %d", errno);
+               return -1;
+       }
+
+       /* FD_ISSET need not be checked */
+       fcntl(fd, F_SETFL, flags );
+       if ((msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) {
+               // To us, any of the above flags are a fatal error
+               BT_ERR("MSG Flags errno: %d", errno);
+               return -2;
+       }
+
+       if (ret >= 0 && data_fd) {
+               BT_INFO("Connection received");
+               socket_process_cmsg(&msg, data_fd);
+       }
+
+       return ret;
+}
+
+static int sock_wait_for_channel(int sock_fd)
+{
+       int readlen = -1;
+       char buf[SOCK_INT_LEN];
+
+       readlen = socket_read(sock_fd, buf, SOCK_INT_LEN, NULL);
+       return getInt(buf, readlen);
+}
+
+static int sock_wait_for_connect_signal(int sock_fd,
+               int *data_fd, bt_bdaddr_t *bd_addr)
+{
+       int readlen = -1;
+       char buf[SOCK_CONNECT_INFO_LEN];
+       int size = -1, channel = -1, status = -1;
+       int len = 0;
+
+       readlen = socket_read(sock_fd, buf, SOCK_CONNECT_INFO_LEN, data_fd);
+       BT_DBG("Socket Read len: %d", readlen);
+       if(readlen == 0) {
+               BT_WARN("Listen stopped");
+               return -1; /* This essentially means that the listen is stopped */
+       }
+
+       if(readlen != SOCK_CONNECT_INFO_LEN) {
+               BT_ERR("Read length is not same as socket info length");
+               return -2;
+       }
+
+       size = getShort(&buf[len], SOCK_SHORT_LEN);
+       len += SOCK_SHORT_LEN;
+       if(size != SOCK_CONNECT_INFO_LEN)
+               return -3;
+
+       getBdaddr(&buf[len], SOCK_BD_ADDR_LEN, bd_addr);len += SOCK_BD_ADDR_LEN;
+       channel = getInt(&buf[len], SOCK_INT_LEN); len += SOCK_INT_LEN;
+       status = getInt(&buf[len], SOCK_INT_LEN);
+
+       BT_INFO("BTSOCK CONNECTION ESTABLISHED REMOTE Channel:%d, Status:%d",
+                       channel, status);
+       return 0;
+}
+
+static int sock_wait_for_connection_setup(oal_client_info_t *p_oal_client_info)
+{
+       int channel = -1;
+       int ret = -1;
+
+       /* First, wait for channel number */
+       channel = sock_wait_for_channel(p_oal_client_info->fd);
+       if(channel < 0) {
+               BT_ERR("ERROR, incorrect channel= %d", channel);
+               return OAL_STATUS_INTERNAL_ERROR;
+       }
+
+       BT_INFO("client channel= %d", channel);
+
+       /* Now, wait for connection signal */
+       ret = sock_wait_for_connect_signal(p_oal_client_info->fd,
+                       NULL, (bt_bdaddr_t *)&p_oal_client_info->address);
+       if (0 > ret) {
+               BT_ERR("ERROR, sock_wait_for_connect_signal failed");
+               return OAL_STATUS_INTERNAL_ERROR;
+       }
+
+       return OAL_STATUS_SUCCESS;
+}
+
+/*
+ * This function will be called only once as connection setup is done here
+ * and then data will be received directly in application context using fd
+ * passed in socket connection event.
+ */
+static gboolean sockclient_thread(GIOChannel *gio, GIOCondition cond, gpointer data)
+{
+       event_socket_client_conn_t *client_info_ev;
+       oal_client_info_t *p_oal_client_info = (oal_client_info_t *)data;
+
+       retv_if(p_oal_client_info == NULL, FALSE);
+       retv_if(p_oal_client_info->fd < 0, FALSE);
+
+       BT_DBG("Client fd= %d", p_oal_client_info->fd);
+
+       /* This memory will be freed by event dispatcher */
+       client_info_ev = g_new0(event_socket_client_conn_t, 1);
+       client_info_ev->fd = p_oal_client_info->fd;
+       client_info_ev->sock_type = p_oal_client_info->sock_type;
+       memcpy(&client_info_ev->address, &p_oal_client_info->address, sizeof(bt_address_t));
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+               BT_INFO("Client disconnected-0x%X (fd = %d)",
+                               cond, p_oal_client_info->fd);
+               goto failed;
+       }
+
+       if (OAL_STATUS_SUCCESS == sock_wait_for_connection_setup(p_oal_client_info)) {
+               BT_INFO("connection setup success");
+               send_event_bda_trace(OAL_EVENT_SOCKET_OUTGOING_CONNECTED, client_info_ev,
+                               sizeof(event_socket_client_conn_t), &p_oal_client_info->address);
+               goto done;
+       }
+
+       BT_ERR("ERROR, incorrect connection setup");
+
+failed:
+       remove_io_channel(p_oal_client_info->control_id, p_oal_client_info->control_io);
+       send_event_bda_trace(OAL_EVENT_SOCKET_DISCONNECTED, client_info_ev,
+                       sizeof(event_socket_client_conn_t), &p_oal_client_info->address);
+done:
+       g_free(p_oal_client_info);
+       return FALSE;
+}
+
+int socket_connect(oal_sock_type_t sock_type, oal_uuid_t *p_uuid, int channel, bt_address_t* bd)
+{
+       oal_client_info_t *p_oal_client_info = NULL;
+       int sock_fd = -1;
+       bdstr_t bdstr;
+
+       API_TRACE("Socket connect: %s", bdt_bd2str(bd, &bdstr));
+
+       CHECK_OAL_SOCKET_ENABLED();
+
+       p_oal_client_info = g_new0(oal_client_info_t, 1);
+
+       switch (sock_type) {
+       case OAL_SOCK_RFCOMM:
+               if(channel < 0 )
+                       socket_api->connect((const bt_bdaddr_t *)bd,
+                               BTSOCK_RFCOMM, p_uuid->uuid, 0, &sock_fd, 0);
+               else
+                       socket_api->connect((const bt_bdaddr_t *)bd,
+                               BTSOCK_RFCOMM, NULL, channel, &sock_fd, 0);
+               break;
+       default:
+               BT_ERR("Socket type: %d not supported");
+       }
+
+       if(sock_fd < 0) {
+               BT_ERR("Bluetooth socket connect failed");
+               return sock_fd;
+       }
+
+       BT_INFO("Bluetooth client socket created, sock_fd=%d", sock_fd);
+
+       p_oal_client_info->fd = sock_fd;
+       p_oal_client_info->sock_type = sock_type;
+       memcpy(&p_oal_client_info->address, bd, sizeof(bt_address_t));
+       p_oal_client_info->control_io = g_io_channel_unix_new(p_oal_client_info->fd);
+       p_oal_client_info->control_id = g_io_add_watch(p_oal_client_info->control_io,
+                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                       sockclient_thread, p_oal_client_info);
+       g_io_channel_set_close_on_unref(p_oal_client_info->control_io, FALSE);
+       g_io_channel_unref(p_oal_client_info->control_io);
+
+       BT_DBG("Client watch added");
+       return sock_fd;
+}
+
 oal_status_t socket_enable()
 {
        const bt_interface_t *blued_api;