platform: msm_shared: Add GLink support

GLink is a protocol for IPC communication and is needed
for LK to talk to RPM processor

Change-Id: I2f4a44ac0d9950d5a5046c6b4aa341821a8dc663
diff --git a/platform/msm_shared/glink/glink_api.c b/platform/msm_shared/glink/glink_api.c
new file mode 100644
index 0000000..24f777d
--- /dev/null
+++ b/platform/msm_shared/glink/glink_api.c
@@ -0,0 +1,1532 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of The Linux Foundation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+/*===========================================================================
+                        INCLUDE FILES
+===========================================================================*/
+#include <glink.h>
+#include <glink_rpm.h>
+#include <glink_os_utils.h>
+#include <glink_internal.h>
+#include <glink_vector.h>
+#include <glink_channel_migration.h>
+#include <smem_list.h>
+#include <smem_type.h>
+
+#define GLINK_NOT_INITIALIZED 0
+#define GLINK_INITIALIZED     1
+
+/*===========================================================================
+                              GLOBAL DATA DECLARATIONS
+===========================================================================*/
+int glink_core_status = GLINK_NOT_INITIALIZED;
+
+os_cs_type *glink_transport_q_cs;
+os_cs_type *glink_mem_log_cs;
+
+glink_mem_log_entry_type glink_mem_log_arr[GLINK_MEM_LOG_SIZE];
+uint32 glink_mem_log_idx = 0;
+
+/* Keep a list of registered transport for each edge allowed for this host */
+smem_list_type glink_registered_transports[GLINK_NUM_HOSTS];
+
+smem_list_type glink_link_notif_list;
+
+/* List of supported hosts */
+const char* glink_hosts_supported[]   = { "apss",
+                                          "mpss",
+                                          "lpass",
+                                          "dsps",
+                                          "wcnss",
+                                          "tz",
+                                          "rpm",
+                                        };
+
+/* Forward function declarations */
+void glinki_free_intents(glink_channel_ctx_type *open_ch_ctx);
+
+/*===========================================================================
+                    LOCAL FUNCTION DEFINITIONS
+===========================================================================*/
+
+glink_err_type glinki_add_ch_to_xport
+(
+  glink_transport_if_type  *if_ptr,
+  glink_transport_if_type  *req_if_ptr,
+  glink_channel_ctx_type   *ch_ctx,
+  glink_channel_ctx_type   **allocated_ch_ctx,
+  unsigned int              local_open,
+  boolean                      migration_state,
+  glink_xport_priority      migrated_ch_prio
+)
+{
+  glink_err_type             status = 0;
+  glink_channel_ctx_type     *open_ch_ctx;
+  glink_core_xport_ctx_type  *xport_ctx = if_ptr->glink_core_priv;
+
+  /* See if channel already exists in open_list */
+  glink_os_cs_acquire(&xport_ctx->channel_q_cs);
+  open_ch_ctx = smem_list_first(&if_ptr->glink_core_priv->open_list);
+  while(open_ch_ctx != NULL)
+  {
+    if (strcmp(open_ch_ctx->name, ch_ctx->name) == 0)
+    {
+      /* We've found a channel name is already in the list of open channel */
+      /* increase reference open count for channel */
+      *allocated_ch_ctx = open_ch_ctx;
+      open_ch_ctx->ref_count++;
+
+      /* Case A: Channel was opened before on the same host */
+      if((open_ch_ctx->state == GLINK_CH_STATE_REMOTE_OPEN) && local_open) {
+
+        open_ch_ctx->req_if_ptr = req_if_ptr;
+
+        /* Copy local open ctx params */
+        open_ch_ctx->notify_rx = ch_ctx->notify_rx;
+        open_ch_ctx->notify_rxv = ch_ctx->notify_rxv;
+        open_ch_ctx->notify_tx_done = ch_ctx->notify_tx_done;
+        open_ch_ctx->notify_state = ch_ctx->notify_state;
+        open_ch_ctx->notify_rx_intent_req = ch_ctx->notify_rx_intent_req;
+        open_ch_ctx->notify_rx_intent = ch_ctx->notify_rx_intent;
+        open_ch_ctx->notify_rx_sigs = ch_ctx->notify_rx_sigs;
+        open_ch_ctx->priv = ch_ctx->priv;
+        open_ch_ctx->ch_open_options = ch_ctx->ch_open_options;
+
+        /* release lock before context switch otherwise it is causing
+         * deadlock */
+        glink_os_cs_release(&xport_ctx->channel_q_cs);
+
+        /* Send open cmd to transport */
+        status = if_ptr->tx_cmd_ch_open(if_ptr,
+                      open_ch_ctx->lcid, open_ch_ctx->name,
+                      open_ch_ctx->req_if_ptr->glink_priority);
+      }
+      else if ((open_ch_ctx->state == GLINK_CH_STATE_LOCAL_OPEN) &&
+               (!local_open))
+      {
+        /* Case B: Channel was opened on this end and we got a remote open */
+        open_ch_ctx->rcid = ch_ctx->rcid;
+
+        status = xport_ctx->channel_init(open_ch_ctx);
+
+        /* release lock before context switch otherwise it is causing deadlock */
+        glink_os_cs_release(&xport_ctx->channel_q_cs);
+
+        if (status == GLINK_STATUS_SUCCESS)
+        {
+          /* Send ACK to transport */
+          if_ptr->tx_cmd_ch_remote_open_ack(if_ptr, open_ch_ctx->rcid, migrated_ch_prio);
+        }
+      } else if ((open_ch_ctx->state == GLINK_CH_STATE_REMOTE_OPEN_LOCAL_CLOSE)
+                  && (local_open)) {
+        /* Allocate new channel context */
+        break; /* code would break out of loop and create new ch ctx */
+      } else {
+        /* Can't handle this state */
+        ASSERT(0);
+      }
+
+      break;
+    } /* end if match found */
+    open_ch_ctx = smem_list_next(open_ch_ctx);
+  }/* end while */
+
+  if (open_ch_ctx != NULL)
+  {
+    glink_os_free(ch_ctx);
+
+    /* connect channel here if state is local open and remote open request
+     * comes up and channel migration is done; channel will be connected in
+     * remote_open_ack if channel state is remote open and local open
+     * request comes up */
+    if(open_ch_ctx->state == GLINK_CH_STATE_LOCAL_OPEN &&
+       migration_state == FALSE && status == GLINK_STATUS_SUCCESS)
+    {
+      /* Set the channel state to OPEN */
+      open_ch_ctx->state = GLINK_CH_STATE_OPEN;
+      /* Inform the client */
+      open_ch_ctx->notify_state(open_ch_ctx, open_ch_ctx->priv,
+                                GLINK_CONNECTED);
+    }
+  }
+  else
+  {
+    /* Channel not in the list - it was not previously opened */
+    ch_ctx->if_ptr = if_ptr;
+    *allocated_ch_ctx = ch_ctx;
+
+    /* Set channel state */
+    if (local_open) {
+      /* This is a local open */
+      ch_ctx->state      = GLINK_CH_STATE_LOCAL_OPEN;
+      ch_ctx->req_if_ptr = req_if_ptr;
+    }
+    else {
+      ch_ctx->state = GLINK_CH_STATE_REMOTE_OPEN;
+    }
+
+    glink_os_cs_init(&ch_ctx->tx_cs);
+
+    /* Append the channel to the transport interface's open_list */
+    ch_ctx->ref_count++;
+    ch_ctx->lcid = xport_ctx->free_lcid;
+    xport_ctx->free_lcid++;
+    smem_list_append(&if_ptr->glink_core_priv->open_list, ch_ctx);
+
+    /* release lock before context switch otherwise it is causing deadlock */
+    glink_os_cs_release(&xport_ctx->channel_q_cs);
+
+    /* Send the OPEN command to transport */
+    if (local_open)
+    {
+      status = if_ptr->tx_cmd_ch_open(if_ptr, ch_ctx->lcid,
+                                      ch_ctx->name,
+                                      ch_ctx->req_if_ptr->glink_priority);
+    }
+    else
+    {
+      /* initialize channel resources */
+      status = xport_ctx->channel_init(ch_ctx);
+
+      /* ACK the transport for remote open */
+      if (status == GLINK_STATUS_SUCCESS)
+      {
+        if_ptr->tx_cmd_ch_remote_open_ack(if_ptr, ch_ctx->rcid,
+                                          migrated_ch_prio);
+      }
+    }
+
+    if (status != GLINK_STATUS_SUCCESS)
+    {
+      /* Remove the channel from the transport interface's open_list */
+      xport_ctx->free_lcid--;
+
+      glink_os_cs_acquire(&xport_ctx->channel_q_cs);
+      smem_list_delete(&if_ptr->glink_core_priv->open_list, ch_ctx);
+      glink_os_cs_release(&xport_ctx->channel_q_cs);
+
+      /* free the ch_ctx structure and return */
+      xport_ctx->channel_cleanup(ch_ctx);
+      glink_os_free(ch_ctx);
+    }
+  }
+
+  return status;
+}
+
+/** Default implementation of optional callbacks */
+
+static void glink_default_notify_rx_sigs
+(
+  glink_handle_type  handle,
+  const void         *priv,
+  uint32             prev,
+  uint32             curr
+)
+{
+  return;
+}
+
+static void glinki_call_link_notifier
+(
+  glink_link_notif_data_type *link_notif_data,
+  glink_core_xport_ctx_type  *xport_ctx,
+  glink_link_state_type      state
+)
+{
+  glink_link_info_type link_info;
+
+  ASSERT(xport_ctx);
+  ASSERT(link_notif_data);
+
+  link_info.xport = xport_ctx->xport;
+  link_info.remote_ss = xport_ctx->remote_ss;
+  link_info.link_state = state;
+  link_notif_data->link_notifier(&link_info, link_notif_data->priv);
+}
+
+static uint32 glinki_find_remote_host
+(
+  const char *remote_ss
+)
+{
+  uint32 remote_host;
+  ASSERT(remote_ss);
+
+  for(remote_host = 0;
+      remote_host < sizeof(glink_hosts_supported)/sizeof(char *);
+      remote_host++) {
+    if( 0 == strcmp(glink_hosts_supported[remote_host], remote_ss) ) {
+      /* Match found, break out of loop */
+      break;
+    }
+  }
+  return remote_host;
+}
+
+static void glinki_check_xport_and_notify
+(
+  glink_link_notif_data_type *link_notif_data,
+  glink_core_xport_ctx_type  *xport_ctx,
+  glink_link_state_type      state
+)
+{
+  glink_link_info_type link_info;
+
+  ASSERT(xport_ctx);
+  ASSERT(link_notif_data);
+
+  link_info.xport = xport_ctx->xport;
+  link_info.remote_ss = xport_ctx->remote_ss;
+  link_info.link_state = state;
+
+  if(link_notif_data->xport == NULL ||
+     0 == strcmp(xport_ctx->xport, link_notif_data->xport)) {
+    /* xport not specified, or it is specified and matches the current xport */
+    /* Invoke registered callback */
+    link_notif_data->link_notifier(&link_info, link_notif_data->priv);
+  }
+}
+
+
+static void glinki_scan_xports_and_notify
+(
+  glink_link_notif_data_type *link_notif_data
+)
+{
+  unsigned int remote_host;
+  glink_transport_if_type *if_ptr;
+  glink_core_xport_ctx_type  *xport_ctx;
+
+  ASSERT(link_notif_data);
+
+  /* Find matching subsystem */
+  if(link_notif_data->remote_ss) {
+    remote_host = glinki_find_remote_host(link_notif_data->remote_ss);
+
+    /* Find the xport and give link UP notification */
+    if_ptr = smem_list_first(&glink_registered_transports[remote_host]);
+    if(if_ptr == NULL) {
+      /* No registered xports at this time, return without doing anything */
+      return;
+    }
+
+    if(link_notif_data->xport) {
+      do {
+        xport_ctx = if_ptr->glink_core_priv;
+        if( 0 == strcmp(xport_ctx->xport, link_notif_data->xport) ) {
+          /* Match found, break out of loop */
+          break;
+        }
+      }while ((if_ptr = smem_list_next(if_ptr)) != NULL);
+
+      if((if_ptr != NULL) && (xport_ctx->status == GLINK_XPORT_LINK_UP)) {
+        /* Invoke registered callback */
+        glinki_call_link_notifier(link_notif_data, xport_ctx,
+                                  GLINK_LINK_STATE_UP);
+      }
+    } else {
+      /* No xport has been specified, invoke notifier for all registered
+       * xports */
+      do {
+        xport_ctx = if_ptr->glink_core_priv;
+        if(xport_ctx->status == GLINK_XPORT_LINK_UP) {
+          /* Invoke registered callback */
+          glinki_call_link_notifier(link_notif_data, xport_ctx,
+                                    GLINK_LINK_STATE_UP);
+        }
+      }while ((if_ptr = smem_list_next(if_ptr)) != NULL);
+    }
+  } else {
+    /* No remote ss is specified, invoke notifier for all remote_ss */
+    for(remote_host = 0;
+        remote_host < sizeof(glink_hosts_supported)/sizeof(char *);
+        remote_host++) {
+      /* Find the xport and give link UP notification */
+      if_ptr = smem_list_first(&glink_registered_transports[remote_host]);
+      if(if_ptr == NULL) {
+        /* No registered xports at this time, continue with next remote_ss */
+        continue;
+      }
+
+      if(link_notif_data->xport) {
+        do {
+          xport_ctx = if_ptr->glink_core_priv;
+          if( 0 == strcmp(xport_ctx->xport, link_notif_data->xport) ) {
+            /* Match found, break out of loop */
+            break;
+          }
+        }while ((if_ptr = smem_list_next(if_ptr)) != NULL);
+
+        if((if_ptr != NULL) && (xport_ctx->status == GLINK_XPORT_LINK_UP)) {
+          /* Invoke registered callback */
+          glinki_call_link_notifier(link_notif_data, xport_ctx,
+                                    GLINK_LINK_STATE_UP);
+        }
+      } else {
+        /* No xport has been specified, invoke notifier for all registered
+         * xports */
+        do {
+          xport_ctx = if_ptr->glink_core_priv;
+          if(xport_ctx->status == GLINK_XPORT_LINK_UP) {
+            /* Invoke registered callback */
+            glinki_call_link_notifier(link_notif_data, xport_ctx,
+                                      GLINK_LINK_STATE_UP);
+          }
+        }while ((if_ptr = smem_list_next(if_ptr)) != NULL);
+      }
+    } /* end for remote_host */
+  }/* end if else (link_notif_data->remote_ss) */
+} /* glinki_scan_xports_and_notify */
+
+void glinki_scan_notif_list_and_notify
+(
+  glink_transport_if_type *if_ptr,
+  glink_link_state_type state
+)
+{
+  glink_link_notif_data_type *link_notif_data;
+  glink_core_xport_ctx_type  *xport_ctx = if_ptr->glink_core_priv;
+
+  link_notif_data = smem_list_first(&glink_link_notif_list);
+
+  if(link_notif_data == NULL) {
+    /* list empty */
+    return;
+  }
+
+  do {
+      if(link_notif_data->remote_ss &&
+         0 == strcmp(xport_ctx->remote_ss, link_notif_data->remote_ss)) {
+        /* remote_ss specified and matches */
+        glinki_check_xport_and_notify(link_notif_data, xport_ctx, state);
+      } else if(link_notif_data->remote_ss == NULL) {
+        /* remote_ss not specified, invoke link notif for any remote_ss */
+        if(link_notif_data->xport) {
+          glinki_check_xport_and_notify(link_notif_data, xport_ctx, state);
+        } /* if else link_notif_data->xport */
+      } /* if else link_notif_data->remote_ss */
+  } while ( (link_notif_data = smem_list_next(link_notif_data)) != NULL);
+
+} /* glinki_scan_notif_list_and_notify */
+
+void glinki_scan_channels_and_notify_discon
+(
+  glink_transport_if_type *if_ptr
+)
+{
+  glink_channel_ctx_type *open_ch_ctx;
+  glink_core_xport_ctx_type  *xport_ctx;
+
+  ASSERT(if_ptr != NULL);
+
+  xport_ctx = if_ptr->glink_core_priv;
+
+  /* Find channel in the open_list */
+  glink_os_cs_acquire(&xport_ctx->channel_q_cs);
+  open_ch_ctx = smem_list_first(&if_ptr->glink_core_priv->open_list);
+  while(open_ch_ctx != NULL)
+  {
+    /* Found channel, transition it to appropriate state based
+     * on current state */
+    if(open_ch_ctx->state == GLINK_CH_STATE_OPEN) {
+      open_ch_ctx->state = GLINK_CH_STATE_LOCAL_OPEN;
+
+      /* Inform the client */
+      open_ch_ctx->notify_state(open_ch_ctx, open_ch_ctx->priv,
+          GLINK_REMOTE_DISCONNECTED);
+    } else if (open_ch_ctx->state == GLINK_CH_STATE_REMOTE_OPEN) {
+      /* Local side never opened the channel */
+      /* Free channel resources */
+      xport_ctx->channel_cleanup(open_ch_ctx);
+
+      smem_list_delete(&if_ptr->glink_core_priv->open_list, open_ch_ctx);
+
+      glink_os_free(open_ch_ctx);
+    }
+    open_ch_ctx = smem_list_next(open_ch_ctx);
+  }/* end while */
+  glink_os_cs_release(&xport_ctx->channel_q_cs);
+}
+
+void glink_ssr(const char* remote_ss)
+{
+  unsigned int remote_host;
+  glink_transport_if_type *if_ptr;
+
+  remote_host = glinki_find_remote_host(remote_ss);
+
+  /* Scan through the registered interfaces with the crashing ss
+     and let the clients know about the crash via LINK_DOWN
+     notification followed by REMOTE_DISCONNECT */
+  if_ptr = smem_list_first(&glink_registered_transports[remote_host]);
+
+  while(if_ptr != NULL) {
+    /* Invoke LINK_DOWN notification for any registered notifiers */
+    glinki_scan_notif_list_and_notify(if_ptr, GLINK_LINK_STATE_DOWN);
+
+    /* Invoke REMOTE_DISCONNECT for all channels associated with if_ptr */
+    glinki_scan_channels_and_notify_discon(if_ptr);
+
+    /* Let the xport know about ssr */
+    if_ptr->ssr(if_ptr);
+
+    if_ptr = smem_list_next(if_ptr);
+  }
+}
+
+
+/*===========================================================================
+                    EXTERNAL FUNCTION DEFINITIONS
+===========================================================================*/
+/*===========================================================================
+FUNCTION      glink_init
+
+DESCRIPTION   Initializes the GLink core library.
+
+ARGUMENTS     None
+
+RETURN VALUE  None
+
+SIDE EFFECTS  None
+===========================================================================*/
+void glink_init(void)
+{
+  uint32 i;
+
+  glink_mem_log_cs = glink_os_cs_create();
+
+  glink_core_status = GLINK_INITIALIZED;
+
+  /* Create/Initalize crtitical sections */
+  glink_transport_q_cs = glink_os_cs_create();
+  if(glink_transport_q_cs == NULL) {
+    return;
+  }
+
+  glink_os_cs_acquire(glink_transport_q_cs);
+  for(i= 0; i < sizeof(glink_registered_transports)/sizeof(smem_list_type);
+      i++)
+  {
+    smem_list_init(&glink_registered_transports[i]);
+  }
+  glink_os_cs_release(glink_transport_q_cs);
+}
+
+/*===========================================================================
+FUNCTION      glink_core_register_transport
+
+DESCRIPTION   Transport calls this API to register its interface with GLINK
+              Core
+
+ARGUMENTS   *if_ptr   Pointer to interface instance; must be unique
+                      for each edge
+
+            *cfg      Pointer to transport configuration structure.
+
+RETURN VALUE  Standard GLINK error codes.
+
+SIDE EFFECTS  None
+===========================================================================*/
+glink_err_type glink_core_register_transport
+(
+  glink_transport_if_type       *if_ptr,
+  glink_core_transport_cfg_type *cfg
+)
+{
+  unsigned int remote_host = 0;
+  glink_core_xport_ctx_type *xport_ctx;
+  /* Param validation */
+  if(if_ptr == NULL || cfg == NULL)
+  {
+    GLINK_LOG_EVENT(GLINK_EVENT_REGISTER_XPORT, NULL, "", "",
+        GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  if(cfg->name == NULL       ||
+     cfg->remote_ss == NULL  ||
+     cfg->version == NULL    ||
+     cfg->version_count == 0 ||
+     cfg->max_cid == 0)
+  {
+    GLINK_LOG_EVENT(GLINK_EVENT_REGISTER_XPORT, NULL, "", "",
+        GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+
+  if(if_ptr->tx_cmd_version == NULL             ||
+     if_ptr->tx_cmd_version_ack == NULL         ||
+     if_ptr->set_version == NULL                ||
+     if_ptr->tx_cmd_ch_open == NULL             ||
+     if_ptr->tx_cmd_ch_close == NULL            ||
+     if_ptr->tx_cmd_ch_remote_open_ack == NULL  ||
+     if_ptr->tx_cmd_ch_remote_close_ack == NULL ||
+     if_ptr->tx_cmd_set_sigs  == NULL           ||
+     if_ptr->ssr  == NULL)
+  {
+    GLINK_LOG_EVENT(GLINK_EVENT_REGISTER_XPORT, NULL, cfg->name,
+        cfg->remote_ss, GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;;
+  }
+
+
+  /* Allocate/fill out the GLink Core interface structure */
+  {
+    glink_core_if_type *core_if = glink_os_calloc(sizeof(glink_core_if_type));
+    if(core_if == NULL) {
+      GLINK_LOG_EVENT(GLINK_EVENT_REGISTER_XPORT, NULL, cfg->name,
+          cfg->remote_ss, GLINK_STATUS_OUT_OF_RESOURCES);
+      return GLINK_STATUS_OUT_OF_RESOURCES;
+    }
+    core_if->link_up = glink_link_up;
+    core_if->rx_cmd_version = glink_rx_cmd_version;
+    core_if->rx_cmd_version_ack = glink_rx_cmd_version_ack;
+    core_if->rx_cmd_ch_remote_open = glink_rx_cmd_ch_remote_open;
+    core_if->rx_cmd_ch_open_ack = glink_rx_cmd_ch_open_ack;
+    core_if->rx_cmd_ch_close_ack = glink_rx_cmd_ch_close_ack;
+    core_if->rx_cmd_ch_remote_close = glink_rx_cmd_ch_remote_close;
+    core_if->ch_state_local_trans = glink_ch_state_local_trans;
+    core_if->rx_put_pkt_ctx = glink_rx_put_pkt_ctx;
+    core_if->rx_cmd_remote_sigs = glink_rx_cmd_remote_sigs;
+    core_if->tx_resume = glink_tx_resume;
+    core_if->set_core_version = glink_set_core_version;
+
+    /* Set the glink_core_if_ptr to point to the allocated structure */
+    if_ptr->glink_core_if_ptr = core_if;
+  }
+
+  /* Allocate/fill out the GLink private context data */
+  {
+     xport_ctx = glink_os_calloc(sizeof(glink_core_xport_ctx_type));
+    if(xport_ctx == NULL) {
+      /* Free previously allocated memory */
+      glink_os_free(if_ptr->glink_core_if_ptr);
+
+      GLINK_LOG_EVENT(GLINK_EVENT_REGISTER_XPORT, NULL, cfg->name,
+          cfg->remote_ss, GLINK_STATUS_OUT_OF_RESOURCES);
+
+      return GLINK_STATUS_OUT_OF_RESOURCES;
+    }
+
+    glink_os_string_copy(xport_ctx->xport, cfg->name,
+        sizeof(xport_ctx->xport));
+    glink_os_string_copy(xport_ctx->remote_ss, cfg->remote_ss,
+        sizeof(xport_ctx->xport));
+    xport_ctx->free_lcid = 1; /* lcid 0 is reserved for invalid channel */
+    xport_ctx->version_array = cfg->version;
+    xport_ctx->version_indx = cfg->version_count - 1;
+
+    glink_os_cs_init(&xport_ctx->channel_q_cs);
+    glink_os_cs_init(&xport_ctx->liid_cs);
+
+    glink_os_cs_acquire(&xport_ctx->channel_q_cs);
+    smem_list_init(&xport_ctx->open_list);
+    glink_os_cs_release(&xport_ctx->channel_q_cs);
+
+    /* Set the glink_core_if_ptr to point to the allocated structure */
+    if_ptr->glink_core_priv = xport_ctx;
+    xport_ctx->status = GLINK_XPORT_REGISTERED;
+  }
+
+  /* Push the transport interface into appropriate queue */
+  remote_host = glinki_find_remote_host(cfg->remote_ss);
+
+  if(remote_host == GLINK_NUM_HOSTS ) {
+    /* Unknown transport name trying to register with GLink */
+    GLINK_LOG_EVENT(GLINK_EVENT_REGISTER_XPORT, NULL, xport_ctx->xport,
+           xport_ctx->remote_ss, GLINK_STATUS_INVALID_PARAM);
+
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+  glink_os_cs_acquire(glink_transport_q_cs);
+  smem_list_append(&glink_registered_transports[remote_host], if_ptr);
+  glink_os_cs_release(glink_transport_q_cs);
+
+  GLINK_LOG_EVENT(GLINK_EVENT_REGISTER_XPORT, NULL, xport_ctx->xport,
+      xport_ctx->remote_ss, GLINK_STATUS_SUCCESS);
+
+  return GLINK_STATUS_SUCCESS;
+}
+
+/**
+ * Regsiters a client specified callback to be invoked when the specified
+ * transport (link) is up/down.
+ *
+ * @param[in]    link_id  Pointer to the configuration structure for the
+ *                        xport(link) to be monitored. See glink.h
+ * @param[in]    priv     Callback data returned to client when callback
+ *                        is invoked.
+ *
+ * @return       Standard GLink error codes
+ *
+ * @sideeffects  Puts the callback in a queue which gets scanned when a
+ *               transport(link) comes up OR an SSR happnes.
+ */
+glink_err_type glink_register_link_state_cb
+(
+  glink_link_id_type *link_id,
+  void*              priv
+)
+{
+  glink_link_notif_data_type* link_notif_data;
+  unsigned int remote_host;
+
+  /* Input validation */
+  ASSERT(link_id != NULL);
+
+  /* Make sure client provided us with the correct version of the input
+   * structure */
+  if(link_id->version != GLINK_LINK_ID_VER || link_id->link_notifier == NULL) {
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  /* Save the callback on the notification list */
+  if((link_notif_data = glink_os_malloc(sizeof(glink_link_notif_data_type)))
+       == NULL) {
+    return GLINK_STATUS_OUT_OF_RESOURCES;
+  }
+
+  /* Check for remote_ss validity */
+  if(link_id->remote_ss != NULL) {
+    remote_host = glinki_find_remote_host(link_id->remote_ss);
+
+    if(remote_host == sizeof(glink_hosts_supported)/sizeof(char *)) {
+      glink_os_free(link_notif_data);
+      return GLINK_STATUS_INVALID_PARAM;
+    }
+  }
+
+  link_notif_data->xport = link_id->xport;
+  link_notif_data->remote_ss = link_id->remote_ss;
+  link_notif_data->link_notifier = link_id->link_notifier;
+  link_notif_data->priv = priv; /* private client data */
+
+  /* Append the request to the list for link UP/DOWN notifications */
+  smem_list_append(&glink_link_notif_list, link_notif_data);
+
+  /* Scan the list of available transport to see if this link is already up */
+  glinki_scan_xports_and_notify(link_notif_data);
+
+  return GLINK_STATUS_SUCCESS;
+}
+
+/**
+ * Degsiter the link UP/DOWN notification callback associated with the
+ * provided handle.
+ *
+ * @param[in]    handle  Callback handler returned by
+ *                       glink_register_link_state_cb
+ *
+ * @return       Standard GLink error codes
+ *
+ * @sideeffects  Removes the callback in a queue which gets scanned when a
+ *               transport(link) comes up OR an SSR happnes.
+ */
+glink_err_type glink_deregister_link_state_cb
+(
+  glink_link_handle_type handle
+)
+{
+  ASSERT(handle);
+
+  smem_list_delete(&glink_link_notif_list,
+                   (glink_link_notif_data_type*)handle);
+
+  glink_os_free(handle);
+
+  return GLINK_STATUS_SUCCESS;
+}
+
+/**
+ * This function gives best available transport for give edge
+ *
+ * @param[in]    remote_host  Index into glink_registered_transports array of
+ *                            registered transports list per edge
+ *
+ * @return       pointer to glink_transport_if_type
+ *
+ * @sideeffects  NONE
+ */
+glink_transport_if_type* glink_get_best_xport
+(
+  unsigned int remote_host
+)
+{
+  glink_transport_if_type   *if_ptr    = NULL, *best_if_ptr = NULL;
+  glink_xport_priority       priority  = GLINK_MIN_PRIORITY;
+  glink_core_xport_ctx_type *xport_ctx = NULL;
+
+  best_if_ptr = if_ptr = smem_list_first(
+                          &glink_registered_transports[remote_host]);
+
+  while(if_ptr != NULL)
+  {
+    /* check if priority of current transport is higher than
+     * current highest priority (0 = highest priority)
+     */
+    xport_ctx = if_ptr->glink_core_priv;
+    if( xport_ctx->status == GLINK_XPORT_LINK_UP &&
+        if_ptr->glink_priority < priority )
+    {
+      best_if_ptr = if_ptr;
+      priority    = if_ptr->glink_priority;
+    }
+
+    if_ptr    = smem_list_next(if_ptr);
+  } /* end while() */
+
+  return best_if_ptr;
+}
+
+/**
+ * Opens a logical GLink based on the specified config params
+ *
+ * @param[in]    cfg_ptr  Pointer to the configuration structure for the
+ *                        GLink. See glink.h
+ * @param[out]   handle   GLink handle associated with the logical channel
+ *
+ * @return       Standard GLink error codes
+ *
+ * @sideeffects  Allocates channel resources and informs remote host about
+ *               channel open.
+ */
+glink_err_type glink_open
+(
+  glink_open_config_type *cfg_ptr,
+  glink_handle_type      *handle
+)
+{
+  glink_transport_if_type *if_ptr, *req_if_ptr;
+  glink_channel_ctx_type  *ch_ctx;
+  unsigned int            remote_host;
+
+  /* Param validation */
+  if(cfg_ptr == NULL)
+  {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_OPEN, NULL, "", "",
+        GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  if(cfg_ptr->remote_ss == NULL             ||
+     cfg_ptr->name == NULL                  ||
+     cfg_ptr->notify_state == NULL)
+  {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_OPEN, NULL, "", "",
+        GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  /* Evaluate the equivalent edge name->enum for future use */
+  remote_host = glinki_find_remote_host(cfg_ptr->remote_ss);
+
+  if(remote_host == GLINK_NUM_HOSTS ) {
+    /* Unknown transport name trying to register with GLink */
+    GLINK_LOG_EVENT(GLINK_EVENT_REGISTER_XPORT, cfg_ptr->name, "",
+        cfg_ptr->remote_ss, GLINK_STATUS_INVALID_PARAM);
+
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  /* Allocate and initialize channel info structure */
+  ch_ctx = glink_os_calloc(sizeof(glink_channel_ctx_type));
+  if(ch_ctx == NULL) {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_OPEN, cfg_ptr->name, "",
+        "", GLINK_STATUS_OUT_OF_RESOURCES);
+    return GLINK_STATUS_OUT_OF_RESOURCES;
+  }
+
+  /* Fill in the channel info structure */
+  glink_os_string_copy(ch_ctx->name, cfg_ptr->name, sizeof(ch_ctx->name));
+  ch_ctx->priv = cfg_ptr->priv;
+  ch_ctx->notify_rx = cfg_ptr->notify_rx;
+  ch_ctx->notify_rxv = cfg_ptr->notify_rxv;
+  ch_ctx->notify_tx_done = cfg_ptr->notify_tx_done;
+  ch_ctx->notify_state = cfg_ptr->notify_state;
+  ch_ctx->notify_rx_intent_req = cfg_ptr->notify_rx_intent_req;
+  ch_ctx->notify_rx_intent = cfg_ptr->notify_rx_intent;
+  ch_ctx->notify_rx_sigs = cfg_ptr->notify_rx_sigs;
+  ch_ctx->ch_open_options = cfg_ptr->options;
+  ch_ctx->notify_rx_abort = cfg_ptr->notify_rx_abort;
+  ch_ctx->notify_tx_abort = cfg_ptr->notify_tx_abort;
+
+  if (ch_ctx->notify_rx_sigs == NULL) {
+    /* set default callback */
+    ch_ctx->notify_rx_sigs = glink_default_notify_rx_sigs;
+  }
+
+  glink_os_cs_acquire(glink_transport_q_cs);
+
+  /* Check to see if requested transport is available */
+  for (if_ptr = smem_list_first(&glink_registered_transports[remote_host]);
+       if_ptr != NULL;
+       if_ptr = smem_list_next(if_ptr))
+  {
+    glink_core_xport_ctx_type *xport_ctx = if_ptr->glink_core_priv;
+    glink_channel_ctx_type    *allocated_ch_ctx;
+
+    if (xport_ctx->status == GLINK_XPORT_LINK_UP &&
+        (cfg_ptr->transport == NULL ||
+         0 == strcmp(cfg_ptr->transport, xport_ctx->xport)) &&
+        xport_ctx->verify_open_cfg(ch_ctx))
+    {
+      glink_err_type status;
+
+      if(cfg_ptr->transport == NULL)
+      {
+        /* get best available transport */
+        if_ptr = req_if_ptr = glink_get_best_xport(remote_host);
+      }
+      else
+      {
+        if(cfg_ptr->options & GLINK_OPT_INITIAL_XPORT)
+        {
+          /* xport suggested by client is optional.
+           * get best available xport */
+          req_if_ptr = glink_get_best_xport(remote_host);
+        }
+        else
+        {
+          req_if_ptr = if_ptr;
+        }
+      }
+      /* Xport match found */
+      status = glinki_add_ch_to_xport( if_ptr,
+                                       req_if_ptr,
+                                       ch_ctx,
+                                       &allocated_ch_ctx,
+                                       TRUE,
+                                       TRUE,
+                                       if_ptr->glink_priority );
+
+      if (status == GLINK_STATUS_SUCCESS) {
+        /* Set the handle and return */
+        *handle = allocated_ch_ctx;
+      }
+      else {
+        *handle = NULL;
+      }
+
+      glink_os_cs_release(glink_transport_q_cs);
+
+      GLINK_LOG_EVENT(GLINK_EVENT_CH_OPEN, ch_ctx->name, xport_ctx->xport,
+        xport_ctx->remote_ss, status);
+      return status;
+    }
+  } /* end for() */
+
+  glink_os_cs_release(glink_transport_q_cs);
+
+  /* Code gets here if we are not able to find reqeusted transport */
+  GLINK_LOG_EVENT(GLINK_EVENT_CH_OPEN, cfg_ptr->name, cfg_ptr->transport,
+      cfg_ptr->remote_ss, GLINK_STATUS_NO_TRANSPORT);
+  glink_os_free(ch_ctx);
+  return GLINK_STATUS_NO_TRANSPORT;
+}
+
+/**
+ * Closes the GLink logical channel specified by the handle.
+ *
+ * @param[in]    handle   GLink handle associated with the logical channel
+ *
+ * @return       Standard GLink error codes
+ *
+ * @sideeffects  Closes local end of the channel and informs remote host
+ */
+glink_err_type glink_close
+(
+  glink_handle_type handle
+)
+{
+  glink_err_type status;
+  glink_core_xport_ctx_type *xport_ctx = handle->if_ptr->glink_core_priv;
+  glink_ch_state_type ch_state;
+
+  if(handle == NULL) {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_CLOSE, NULL, "",
+        "", GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  ch_state = handle->state;
+
+  /* Check to see if channel is in open/opening state */
+  if (ch_state != GLINK_CH_STATE_OPEN &&
+      ch_state != GLINK_CH_STATE_LOCAL_OPEN &&
+      ch_state != GLINK_CH_STATE_LOCAL_OPEN_REMOTE_CLOSE &&
+      ch_state != GLINK_CH_STATE_REMOTE_OPEN)
+  {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_CLOSE, handle->name, xport_ctx->xport,
+      xport_ctx->remote_ss, ch_state);
+    return GLINK_STATUS_FAILURE;
+  }
+
+  /* Transition to closing */
+  //handle->state = GLINK_CH_STATE_CLOSING;
+
+  /* Send CLOSE cmd to the transport interface */
+  status = handle->if_ptr->tx_cmd_ch_close(handle->if_ptr, handle->lcid);
+  GLINK_LOG_EVENT(GLINK_EVENT_CH_CLOSE, handle->name, xport_ctx->xport,
+      xport_ctx->remote_ss, status);
+  return status;
+
+}
+
+/**
+ * Transmit the provided buffer over GLink.
+ *
+ * @param[in]    handle    GLink handle associated with the logical channel
+ *
+ * @param[in]   *pkt_priv  Per packet private data
+ *
+ * @param[in]   *data      Pointer to the data buffer to be transmitted
+ *
+ * @param[in]   size       Size of buffer
+ *
+ * @param[in]   req_intent Whether to block and request for remote rx intent in
+ *                         case it is not available for this pkt tx
+ *
+ * @return       Standard GLink error codes
+ *
+ * @sideeffects  Causes remote host to wake-up and process rx pkt
+ */
+glink_err_type glink_tx
+(
+  glink_handle_type handle,
+  const void        *pkt_priv,
+  const void        *data,
+  size_t            size,
+  uint32            options
+)
+{
+  return glink_txv(handle, pkt_priv, (void*)data, size,
+                   &glink_dummy_tx_vprovider, NULL, options);
+
+}
+
+/**
+ * Transmit the provided vector buffer over GLink.
+ *
+ * @param[in]    handle    GLink handle associated with the logical channel
+ *
+ * @param[in]   *pkt_priv  Per packet private data
+ *
+ * @param[in]   *iovec     Pointer to the vector buffer to be transmitted
+ *
+ * @param[in]   size       Size of buffer
+ *
+ * @param[in]   vprovider  Buffer provider for virtual space
+ *
+ * @param[in]   pprovider  Buffer provider for physical space
+ *
+ * @param[in]   req_intent Whether to block and request for remote rx intent in
+ *                         case it is not available for this pkt tx
+ *
+ * @return       Standard GLink error codes
+ *
+ * @sideeffects  Causes remote host to wake-up and process rx pkt
+ */
+glink_err_type glink_txv
+(
+  glink_handle_type        handle,
+  const void               *pkt_priv,
+  void                     *iovec,
+  size_t                   size,
+  glink_buffer_provider_fn vprovider,
+  glink_buffer_provider_fn pprovider,
+  uint32                   options
+)
+{
+  glink_err_type         status;
+  glink_core_tx_pkt_type pctx;
+  boolean                req_intent = options & GLINK_TX_REQ_INTENT;
+  glink_core_xport_ctx_type *xport_ctx = handle->if_ptr->glink_core_priv;
+
+  /* Input validation */
+  if(handle == NULL || iovec == NULL || size == 0 ||
+     (vprovider == NULL && pprovider == NULL)) {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_TX, NULL, "",
+        "", GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  /* Make sure channel is in OPEN state */
+  if(handle->state != GLINK_CH_STATE_OPEN )
+  {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_CLOSE, handle->name, xport_ctx->xport,
+      xport_ctx->remote_ss, GLINK_STATUS_FAILURE);
+    return GLINK_STATUS_FAILURE;
+  }
+
+  /* Protect the entire tx operation under a lock as a client may call
+     tx in different thread context */
+  glink_os_cs_acquire(&handle->tx_cs);
+
+  pctx.pkt_priv = pkt_priv;
+  pctx.size = size;
+  pctx.size_remaining = size;
+  pctx.vprovider = vprovider;
+  pctx.pprovider = pprovider;
+
+  if (vprovider == &glink_dummy_tx_vprovider)
+  {
+    pctx.data = (void*)iovec;
+    pctx.iovec = &pctx;
+  }
+  else
+  {
+    pctx.data = (void*)iovec;
+    pctx.iovec = iovec;
+  }
+
+  status = xport_ctx->use_rm_intent(handle, &pctx, req_intent);
+
+  /* Call transport API to transmit data */
+  while (pctx.size_remaining != 0 && status == GLINK_STATUS_SUCCESS)
+  {
+    status = handle->if_ptr->tx(handle->if_ptr, handle->lcid, &pctx);
+  }
+
+  GLINK_LOG_EVENT(GLINK_EVENT_CH_TX, handle->name, xport_ctx->xport,
+      xport_ctx->remote_ss, status);
+
+  glink_os_cs_release(&handle->tx_cs);
+  return status;
+}
+
+/**
+ * Queue one or more Rx intent for the logical GPIC Link channel.
+ *
+ * @param[in]    handle   GLink handle associated with the logical channel
+ *
+ * @param[in]   *pkt_priv Per packet private data
+ *
+ * @param[in]   size      Size of buffer
+ *
+ * @return       Standard GLink error codes
+ *
+ * @sideeffects  GLink XAL allocates rx buffers for receiving packets
+ */
+glink_err_type glink_queue_rx_intent
+(
+  glink_handle_type handle,
+  const void        *pkt_priv,
+  size_t            size
+)
+{
+  glink_err_type         status;
+  glink_rx_intent_type   *lc_intent;
+  glink_core_xport_ctx_type *xport_ctx = handle->if_ptr->glink_core_priv;
+  size_t tmp;
+
+  /* Input validation */
+  if(handle == NULL || size == 0) {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_Q_RX_INTENT, NULL, "",
+        "", GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  /* short circuit for intentless mode */
+  if(xport_ctx->xport_capabilities & GLINK_CAPABILITY_INTENTLESS) {
+    return GLINK_STATUS_FAILURE;
+  }
+
+  /* Make sure channel is in OPEN state */
+  if(handle->state != GLINK_CH_STATE_OPEN)
+  {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_Q_RX_INTENT, handle->name, xport_ctx->xport,
+      xport_ctx->remote_ss, GLINK_STATUS_FAILURE);
+    return GLINK_STATUS_FAILURE;
+  }
+
+  /* Allocate an intent structure */
+  lc_intent = glink_os_calloc(sizeof(glink_rx_intent_type));
+  if(lc_intent == NULL) {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_Q_RX_INTENT, handle->name, xport_ctx->xport,
+        xport_ctx->remote_ss, GLINK_STATUS_OUT_OF_RESOURCES);
+    return GLINK_STATUS_OUT_OF_RESOURCES;
+  }
+
+  glink_os_cs_acquire(&handle->if_ptr->glink_core_priv->liid_cs);
+
+  /* Call transport API to allocate rx intent buffer */
+  status = handle->if_ptr->allocate_rx_intent(handle->if_ptr, size, lc_intent);
+  if(status != GLINK_STATUS_SUCCESS) {
+    glink_os_free(lc_intent);
+    glink_os_cs_release(&handle->if_ptr->glink_core_priv->liid_cs);
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_Q_RX_INTENT, handle->name, xport_ctx->xport,
+        xport_ctx->remote_ss, status);
+    return status;
+  }
+
+  if (handle->notify_rxv == NULL &&
+     (lc_intent->vprovider(lc_intent->iovec, 0, &tmp) == NULL || tmp < size)) {
+    /* Allocate bounce buffer for non-vectored Rx */
+    lc_intent->data = glink_os_malloc(size);
+
+    if(lc_intent->data == NULL) {
+      handle->if_ptr->deallocate_rx_intent(handle->if_ptr, lc_intent);
+      glink_os_free(lc_intent);
+      glink_os_cs_release(&handle->if_ptr->glink_core_priv->liid_cs);
+      GLINK_LOG_EVENT(GLINK_EVENT_CH_Q_RX_INTENT, handle->name,
+        xport_ctx->xport, xport_ctx->remote_ss, GLINK_STATUS_OUT_OF_RESOURCES);
+      return GLINK_STATUS_OUT_OF_RESOURCES;
+    }
+  }
+
+  /* push the intent on local queue. Do this before calling tx cmd
+     as transport may try to read data into the newly queued rx_buffer */
+  lc_intent->iid = handle->if_ptr->glink_core_priv->liid;
+  lc_intent->size = size;
+  lc_intent->pkt_priv = pkt_priv;
+  glink_os_cs_acquire(&handle->pintents->intent_q_cs);
+  smem_list_append(&handle->pintents->local_intent_q, lc_intent);
+  glink_os_cs_release(&handle->pintents->intent_q_cs);
+
+  /* Call transport API to queue rx intent */
+  /* Increment the local intent ID counter associated with this channel */
+  handle->if_ptr->glink_core_priv->liid++;
+
+  status = handle->if_ptr->tx_cmd_local_rx_intent(handle->if_ptr,
+              handle->lcid, size, lc_intent->iid);
+  if(status != GLINK_STATUS_SUCCESS) {
+    /* Failure */
+    glink_os_cs_acquire(&handle->pintents->intent_q_cs);
+    smem_list_delete(&handle->pintents->local_intent_q, lc_intent);
+    glink_os_cs_release(&handle->pintents->intent_q_cs);
+
+    handle->if_ptr->deallocate_rx_intent(handle->if_ptr, lc_intent);
+    glink_os_free(lc_intent->data);
+    glink_os_free(lc_intent);
+  }
+  glink_os_cs_release(&handle->if_ptr->glink_core_priv->liid_cs);
+
+  GLINK_LOG_EVENT(GLINK_EVENT_CH_Q_RX_INTENT, handle->name, xport_ctx->xport,
+      xport_ctx->remote_ss, status);
+  return status;
+}
+
+/**
+ * Client uses this to signal to GLink layer that it is done with the received
+ * data buffer. This API should be called to free up the receive buffer, which,
+ * in zero-copy mode is actually remote-side's transmit buffer.
+ *
+ * @param[in]    handle   GLink handle associated with the logical channel
+ *
+ * @param[in]   *ptr      Pointer to the received buffer
+ *
+ * @param[in]   reuse    Reuse intent
+ *
+ * @return       Standard GLink error codes
+ *
+ * @sideeffects  GLink XAL frees the Rx buffer
+ */
+glink_err_type glink_rx_done
+(
+  glink_handle_type handle,
+  const void        *ptr,
+  boolean           reuse
+)
+{
+  glink_rx_intent_type      *lc_intent;
+  glink_core_xport_ctx_type *xport_ctx = handle->if_ptr->glink_core_priv;
+
+  /* Input validation */
+  if(handle == NULL || ptr == NULL) {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_RX_DONE, NULL, "",
+        "", GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  /* short circuit for intentless mode */
+  if(xport_ctx->xport_capabilities & GLINK_CAPABILITY_INTENTLESS) {
+    return GLINK_STATUS_SUCCESS;
+  }
+
+  /* Make sure channel is in OPEN state */
+  if(handle->state != GLINK_CH_STATE_OPEN)
+  {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_RX_DONE, handle->name, xport_ctx->xport,
+      xport_ctx->remote_ss, GLINK_STATUS_FAILURE);
+    return GLINK_STATUS_FAILURE;
+  }
+
+  /* Free the intent */
+  lc_intent = smem_list_first(&handle->pintents->local_intent_client_q);
+  while(lc_intent != NULL) {
+    size_t tmp;
+
+    if(lc_intent->iovec == ptr || (handle->notify_rxv == NULL &&
+       (lc_intent->data == ptr ||
+       ptr == lc_intent->vprovider(lc_intent->iovec, 0, &tmp)))) {
+
+      uint32 iid;
+
+      /* Found intent, delete it */
+      glink_os_cs_acquire(&handle->pintents->intent_q_cs);
+      smem_list_delete(&handle->pintents->local_intent_client_q, lc_intent);
+      glink_os_cs_release(&handle->pintents->intent_q_cs);
+
+      iid = lc_intent->iid;
+
+      if (reuse)
+      {
+        lc_intent->used = 0;
+
+        glink_os_cs_acquire(&handle->pintents->intent_q_cs);
+        smem_list_append(&handle->pintents->local_intent_q, lc_intent);
+        glink_os_cs_release(&handle->pintents->intent_q_cs);
+      }
+      else
+      {
+        /* Free the intent */
+        handle->if_ptr->deallocate_rx_intent(handle->if_ptr, lc_intent);
+        if(lc_intent->data) {
+          /* Free the bounce buffer if we had allocated one */
+          glink_os_free(lc_intent->data);
+        }
+        glink_os_free(lc_intent);
+      }
+
+      /* Note that the actual buffer, lc_intent->data, was allocated by the
+      * transport and should be freed by the xport. We should not touch it */
+      /* Let the xport know we are done with the buffer */
+      handle->if_ptr->tx_cmd_local_rx_done(handle->if_ptr, handle->lcid,
+                                           iid, reuse);
+
+      GLINK_LOG_EVENT(GLINK_EVENT_CH_RX_DONE, handle->name, xport_ctx->xport,
+          xport_ctx->remote_ss, GLINK_STATUS_SUCCESS);
+      return GLINK_STATUS_SUCCESS;
+    }
+    lc_intent = smem_list_next(lc_intent);
+  }
+
+  GLINK_LOG_EVENT(GLINK_EVENT_CH_RX_DONE, handle->name, xport_ctx->xport,
+      xport_ctx->remote_ss, GLINK_STATUS_INVALID_PARAM);
+  return GLINK_STATUS_INVALID_PARAM;
+}
+
+/**
+ * Set the 32 bit control signal field. Depending on the transport, it may
+ * take appropriate actions on the set bit-mask, or transmit the entire
+ * 32-bit value to the remote host.
+ *
+ * @param[in]   handle     GLink handle associated with the logical channel
+ *
+ * @param[in]   sig_value  32 bit signal word
+ *
+ * @return       Standard GLink error codes
+ *
+ * @sideeffects  None
+ */
+glink_err_type glink_sigs_set
+(
+  glink_handle_type handle,
+  uint32            sig_value
+)
+{
+  glink_core_xport_ctx_type *xport_ctx = handle->if_ptr->glink_core_priv;
+  glink_err_type status;
+
+  /* Input validation */
+  if(handle == NULL) {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_SIG_SET, NULL, "",
+        "", GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  /* Make sure channel is in OPEN state */
+  if(handle->state != GLINK_CH_STATE_OPEN)
+  {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_SIG_SET, handle->name, xport_ctx->xport,
+      xport_ctx->remote_ss, GLINK_STATUS_FAILURE);
+    return GLINK_STATUS_FAILURE;
+  }
+
+  status = handle->if_ptr->tx_cmd_set_sigs(handle->if_ptr, handle->lcid,
+                                           sig_value);
+  if(GLINK_STATUS_SUCCESS == status) {
+    /* Update local copy of local control signal state */
+    handle->local_sigs = sig_value;
+  }
+
+  return status;
+}
+
+/**
+ * Get the local 32 bit control signal bit-field.
+ *
+ * @param[in]   handle      GLink handle associated with the logical channel
+ *
+ * @param[out]  *sig_value  Pointer to a 32 bit signal word to get sig value
+ *
+ * @return      Standard GLink error codes
+ *
+ * @sideeffects  None
+ */
+glink_err_type glink_sigs_local_get
+(
+  glink_handle_type handle,
+  uint32            *sig_value
+)
+{
+  /* Input validation */
+  if(handle == NULL || sig_value == NULL) {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_SIG_L_GET, NULL, "",
+        "", GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  *sig_value = handle->local_sigs;
+
+  return GLINK_STATUS_SUCCESS;
+}
+
+/**
+ * Get the remote 32 bit control signal bit-field.
+ *
+ * @param[in]   handle      GLink handle associated with the logical channel
+ *
+ * @param[out]  *sig_value  Pointer to a 32 bit signal word to get sig value
+ *
+ * @return      Standard GLink error codes
+ *
+ * @sideeffects  None
+ */
+glink_err_type glink_sigs_remote_get
+(
+  glink_handle_type handle,
+  uint32            *sig_value
+)
+{
+  /* Input validation */
+  if(handle == NULL || sig_value == NULL) {
+    GLINK_LOG_EVENT(GLINK_EVENT_CH_SIG_R_GET, NULL, "",
+        "", GLINK_STATUS_INVALID_PARAM);
+    return GLINK_STATUS_INVALID_PARAM;
+  }
+
+  *sig_value = handle->remote_sigs;
+
+  return GLINK_STATUS_SUCCESS;
+}
+
+/*================= RESTRICTED API ==========================*/
+
+/**
+ * This API allows the RPM client to poll the transport for any new data
+ * that might have come in. It would *usually* be used when incoming interrupts
+ * are disabled.
+ *
+ * @param[in]   handle      GLink handle associated with the logical channel
+ *
+ * @return      Standard GLink error codes
+ *
+ * @sideeffects  None
+ */
+glink_err_type glink_rpm_rx_poll
+(
+  glink_handle_type handle
+)
+{
+  ASSERT(handle);
+
+  if(handle->if_ptr->poll) {
+    return handle->if_ptr->poll(handle->if_ptr);
+  }
+  return GLINK_STATUS_FAILURE;
+}
+
+/**
+ * This API allows the RPM client to mask/unmask rx interrupts
+ *
+ * @param[in]   handle      GLink handle associated with the logical channel
+ *
+ * @param[in]   mask        Whether to mask or un-mask the incoming interrupt
+ *
+ * @param[in]   platform_struct Any platform specific into that transport may
+                                require
+ *
+ * @return      Standard GLink error codes
+ *
+ * @sideeffects  None
+ */
+glink_err_type glink_rpm_mask_rx_interrupt
+(
+  glink_handle_type handle,
+  boolean           mask,
+  void              *platform_struct
+)
+{
+  ASSERT(handle);
+
+  if(handle->if_ptr->mask_rx_irq) {
+    return handle->if_ptr->mask_rx_irq(handle->if_ptr, mask);
+  }
+  return GLINK_STATUS_FAILURE;
+}
+
+/**
+ * This API waits until link is down.
+ *
+ * @param[in]   handle      GLink handle associated with the logical channel
+ *
+ * @return      Standard GLink error codes
+ *
+ * @sideeffects  None
+ */
+glink_err_type glink_wait_link_down
+(
+  glink_handle_type handle
+)
+{
+  ASSERT(handle && handle->if_ptr && handle->if_ptr->wait_link_down);
+
+  return handle->if_ptr->wait_link_down(handle->if_ptr) ?
+         GLINK_STATUS_SUCCESS : GLINK_STATUS_FAILURE;
+}
+
+/* ============ Internal Logging API ================ */
+void glink_mem_log
+(
+  const char *func,
+  uint32 line,
+  glink_log_event_type type,
+  const char *msg,
+  const char *xport,
+  const char *remote_ss,
+  uint32 param
+)
+{
+#ifdef DEBUG_GLINK
+  dprintf(INFO, "%s:%u, event:%d, msg:%s, xport:%s, remote_ss:%s, param:%u\n", func, line, type, msg, xport, remote_ss, param);
+#endif
+}