blob: 3c663696ff4d7197931fa688be5e38389d9ef9cc [file] [log] [blame]
/* 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_os_utils.h"
#include "glink.h"
#include "glink_internal.h"
#include "glink_core_if.h"
#include "smem_list.h"
#include "glink_channel_migration.h"
#define FEATURE_CH_MIGRATION_FREE
void glinki_scan_notif_list_and_notify
(
glink_transport_if_type *if_ptr,
glink_link_state_type state
);
/*===========================================================================
GLOBAL DATA DECLARATIONS
===========================================================================*/
/*===========================================================================
LOCAL FUNCTION DEFINITIONS
===========================================================================*/
/*===========================================================================
FUNCTION glink_process_negotiation_complete
===========================================================================*/
/**
This is called when negotiation is complete.
It will set the version and call link up callback to notify
@param[in] xport_ctx transport context
@param[in] if_ptr Pointer to interface instance; must be unique
for each edge
@param[in] version negotiated version
@param[in] features negotiated with local side
@return None
@sideeffects None.
@dependencies None.
*/
/*=========================================================================*/
static void glink_process_negotiation_complete
(
glink_core_xport_ctx_type *xport_ctx,
glink_transport_if_type *if_ptr,
uint32 version,
uint32 features
)
{
/* Version/Feautre can be negotiated both in ver_req and ver_ack
* Only go through process once in case they are negotiated
* in ver_req before receiving ver_ack */
if( xport_ctx->status == GLINK_XPORT_LINK_UP )
{
return;
}
/* setup core based on transport capabilities*/
xport_ctx->xport_capabilities = if_ptr->set_version( if_ptr,
version,
features );
glink_core_setup(if_ptr);
/* transport is ready to open channels */
if_ptr->glink_core_priv->status = GLINK_XPORT_LINK_UP;
/* Scan the notification list to check is we have to let any registered
* clients know that link came online */
glinki_scan_notif_list_and_notify(if_ptr, GLINK_LINK_STATE_UP);
}
static void glink_dummy_ch_migration_notification_cb
(
glink_handle_type handle,
const void *priv,
glink_channel_event_type event
)
{
}
/*===========================================================================
EXTERNAL FUNCTION DEFINITIONS
===========================================================================*/
/*===========================================================================
FUNCTION glink_link_up
DESCRIPTION Indicates that transport is now ready to start negotiation
using the v0 configuration
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
RETURN VALUE None.
SIDE EFFECTS None
===========================================================================*/
void glink_link_up
(
glink_transport_if_type *if_ptr
)
{
const glink_core_version_type *version_array;
glink_core_xport_ctx_type *xport_ctx;
ASSERT(if_ptr != NULL);
xport_ctx = if_ptr->glink_core_priv;
version_array = xport_ctx->version_array;
/* Update the transport state */
if_ptr->glink_core_priv->status = GLINK_XPORT_NEGOTIATING;
/* Start the negtiation */
if_ptr->tx_cmd_version(if_ptr, version_array->version,
version_array->features);
GLINK_LOG_EVENT(GLINK_EVENT_LINK_UP, NULL, xport_ctx->xport,
xport_ctx->remote_ss, GLINK_STATUS_SUCCESS);
}
/*===========================================================================
FUNCTION glink_rx_cmd_version
DESCRIPTION Receive transport version for remote-initiated version
negotiation
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
version Remote Version
features Remote Features
RETURN VALUE None.
SIDE EFFECTS None
===========================================================================*/
void glink_rx_cmd_version
(
glink_transport_if_type *if_ptr,
uint32 version,
uint32 features
)
{
const glink_core_version_type *ver;
uint32 negotiated_features;
glink_core_xport_ctx_type *xport_ctx;
ASSERT(if_ptr != NULL);
xport_ctx = if_ptr->glink_core_priv;
/* The version to use must be a subset of supported version and features
* on this host and remote host */
ver = &xport_ctx->version_array[xport_ctx->version_indx];
ASSERT(ver);
if (version == ver->version)
{
/* Call the transport's negotiation function */
negotiated_features = ver->negotiate_features(if_ptr, ver, features);
/* If negotiated features match the provided features, version nogetiation
* is complete */
if(negotiated_features == features)
{
/* send version ack before allowing to open channels */
if_ptr->tx_cmd_version_ack(if_ptr, version, features);
glink_process_negotiation_complete( xport_ctx, if_ptr,
version, features );
return;
}
else
{
if_ptr->tx_cmd_version_ack(if_ptr, version, negotiated_features);
}
}
else
{
/* Next time use older version */
ver = &xport_ctx->version_array[xport_ctx->version_indx];
/* Versions don't match, return ACK with the feature set that we support */
if_ptr->tx_cmd_version_ack(if_ptr, ver->version, ver->features);
}
}
/*===========================================================================
FUNCTION glink_rx_cmd_version_ack
DESCRIPTION Receive ACK to previous glink_transport_if::tx_cmd_version
command
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
version Remote Version
features Remote Features
RETURN VALUE None.
SIDE EFFECTS None
===========================================================================*/
void glink_rx_cmd_version_ack
(
glink_transport_if_type *if_ptr,
uint32 version,
uint32 features
)
{
const glink_core_version_type* ver;
uint32 negotiated_features;
glink_core_xport_ctx_type *xport_ctx;
ASSERT(if_ptr != NULL);
xport_ctx = if_ptr->glink_core_priv;
/* Check to see if version returned by remote end is supported by
* this host. Remote side would have acked only when the version/features
* sent by this host did not match the remote */
ver = &xport_ctx->version_array[xport_ctx->version_indx];
ASSERT(ver);
if (ver->version == version)
{
/* Call the transport's negotiation function */
negotiated_features = ver->negotiate_features(if_ptr, ver, features);
if(negotiated_features != features)
{
/* Continue negotiating */
if_ptr->tx_cmd_version(if_ptr, version, negotiated_features);
}
else
{
glink_process_negotiation_complete( xport_ctx, if_ptr,
version, features );
}
}
else
{
while (ver->version > version)
{
/* Next time use older version */
ASSERT(xport_ctx->version_indx > 0);
xport_ctx->version_indx--;
ver = &xport_ctx->version_array[xport_ctx->version_indx];
}
/* Versions don't match, return ACK with the feature set that we support */
if_ptr->tx_cmd_version(if_ptr, ver->version, ver->features);
}
}
/*===========================================================================
FUNCTION glink_rx_cmd_ch_remote_open
DESCRIPTION Receive remote channel open request; Calls
glink_transport_if:: tx_cmd_ch_remote_open_ack as a result
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
rcid Remote Channel ID
*name String name for logical channel
prio xport priority requested by remote side
RETURN VALUE None.
SIDE EFFECTS None
===========================================================================*/
void glink_rx_cmd_ch_remote_open
(
glink_transport_if_type *if_ptr,
uint32 rcid,
const char *name,
glink_xport_priority priority
)
{
glink_channel_ctx_type *remote_ch_ctx;
glink_channel_ctx_type *allocated_ch_ctx;
glink_core_xport_ctx_type *xport_ctx;
glink_err_type status;
glink_xport_priority negotiated_prio;
boolean migration_state = TRUE;
ASSERT(if_ptr != NULL);
ASSERT(name != NULL);
#ifndef FEATURE_CH_MIGRATION_FREE
glink_transport_if_type *present_if_ptr = NULL;
boolean channel_exist = FALSE;
glink_channel_ctx_type *present_ch_ctx = NULL;
/* search if channel with given name exists locally */
channel_exist = glinki_local_channel_exists(if_ptr, &present_if_ptr, name, &present_ch_ctx, TRUE);
/* start channel migration negotiation only if channel exists on local side
* for negotiation AND channel is willing to migrate */
if(channel_exist &&
(present_ch_ctx->ch_open_options & GLINK_OPT_INITIAL_XPORT))
{
/* channel exists on one of the xports for given remote ss */
current_prio = present_ch_ctx->req_if_ptr->glink_priority;
negotiated_prio = glinki_negotiate_ch_migration(priority, current_prio);
/* if current channel is open in same xport as negotiated xport
* local side wont migrate. Set migration flag to FALSE */
if(negotiated_prio == present_if_ptr->glink_priority)
{
migration_state = FALSE;
}
}
else if(channel_exist)
{
/* channel exists but channel does not want to be moved to another xport.
channel is set in stone */
negotiated_prio = present_if_ptr->glink_priority;
migration_state = FALSE;
}
else
{
/* channel does not exist on local side as yet
* return negotiated prio as current xport prio on which
* remote open request is received */
negotiated_prio = if_ptr->glink_priority;
migration_state = FALSE;
}
#else
negotiated_prio = if_ptr->glink_priority;
migration_state = FALSE;
#endif
xport_ctx = if_ptr->glink_core_priv;
/* Allocate and initialize channel info structure */
remote_ch_ctx = glink_os_calloc(sizeof(glink_channel_ctx_type));
if(remote_ch_ctx == NULL) {
GLINK_LOG_EVENT(GLINK_EVENT_RM_CH_OPEN, name, xport_ctx->xport,
xport_ctx->remote_ss, GLINK_STATUS_OUT_OF_RESOURCES);
ASSERT(0);
}
/* Fill in the channel info structure */
glink_os_string_copy(remote_ch_ctx->name, name, sizeof(remote_ch_ctx->name));
remote_ch_ctx->rcid = rcid;
status = glinki_add_ch_to_xport(if_ptr, NULL, remote_ch_ctx,
&allocated_ch_ctx, FALSE,
migration_state, negotiated_prio);
ASSERT(status == GLINK_STATUS_SUCCESS);
GLINK_LOG_EVENT(GLINK_EVENT_RM_CH_OPEN, name, xport_ctx->xport,
xport_ctx->remote_ss, GLINK_STATUS_SUCCESS);
#ifndef FEATURE_CH_MIGRATION_FREE
/* We are done with channel migration negotiation at this stage
* Tag all channels with given name on xports other than negotiated
* xport for deletion */
glinki_tag_ch_for_deletion(if_ptr, name, negotiated_prio);
if(migration_state == TRUE)
{
glink_channel_ctx_type *new_ch_ctx;
/* create a new channel context as current channel will be migrated */
new_ch_ctx = (glink_channel_ctx_type *)
glink_os_calloc(sizeof(glink_channel_ctx_type));
/* Fill in the channel info structure */
new_ch_ctx->req_if_ptr =
glinki_get_xport_from_prio(negotiated_prio,
if_ptr->glink_core_priv->remote_ss);
glink_os_string_copy(new_ch_ctx->name, present_ch_ctx->name,
sizeof(present_ch_ctx->name));
new_ch_ctx->priv = present_ch_ctx->priv;
new_ch_ctx->notify_rx = present_ch_ctx->notify_rx;
new_ch_ctx->notify_rxv = present_ch_ctx->notify_rxv;
new_ch_ctx->notify_tx_done = present_ch_ctx->notify_tx_done;
new_ch_ctx->notify_state = present_ch_ctx->notify_state;
new_ch_ctx->notify_rx_intent_req = present_ch_ctx->notify_rx_intent_req;
new_ch_ctx->notify_rx_intent = present_ch_ctx->notify_rx_intent;
new_ch_ctx->notify_rx_sigs = present_ch_ctx->notify_rx_sigs;
new_ch_ctx->ch_open_options = present_ch_ctx->ch_open_options;
// glink_os_cs_init(&new_ch_ctx->intent_q_cs);
/* close current channel */
present_ch_ctx->notify_state = glink_dummy_ch_migration_notification_cb;
if_ptr->tx_cmd_ch_close( if_ptr, present_ch_ctx->lcid );
/* add new channel context on negotiated xport */
glinki_add_ch_to_xport(new_ch_ctx->req_if_ptr,
new_ch_ctx->req_if_ptr,
new_ch_ctx,
&allocated_ch_ctx,
TRUE,
TRUE,
new_ch_ctx->req_if_ptr->glink_priority);
}
#endif
}
/*===========================================================================
FUNCTION glink_rx_cmd_ch_open_ack
DESCRIPTION This function is invoked by the transport in response to
glink_transport_if:: tx_cmd_ch_open
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
lcid Local Channel ID
prio Negotiated xport priority from remote side
RETURN VALUE None.
SIDE EFFECTS None
===========================================================================*/
void glink_rx_cmd_ch_open_ack
(
glink_transport_if_type *if_ptr,
uint32 lcid,
glink_xport_priority migrated_ch_prio
)
{
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;
/* Move to closed state. Implies we clean up the channel from the
* open list */
/* Find channel in the open_list */
open_ch_ctx = smem_list_first(&if_ptr->glink_core_priv->open_list);
while(open_ch_ctx != NULL)
{
if(open_ch_ctx->lcid == lcid) {
#ifndef FEATURE_CH_MIGRATION_FREE
glink_transport_if_type *present_if_ptr = NULL;
glink_channel_ctx_type *present_ch_ctx = NULL;
boolean channel_exist;
/* find the channel in all xport list */
channel_exist = glinki_local_channel_exists(if_ptr,
&present_if_ptr,
open_ch_ctx->name,
&present_ch_ctx,
FALSE);
if(if_ptr->glink_priority == migrated_ch_prio && !channel_exist)
{
/* only local side has opened the channel. Remote side has not come up yet
* which implies negotiation did not take place on remote side */
/* DO NOTHING */
}
else if(if_ptr->glink_priority == migrated_ch_prio)
{
/* remote channel exists. channel migration negotiation happened
* on remote side and negotitated xport is same as current xport */
if(present_ch_ctx->ref_count == 1)
{
/* remote channel is present on different xport than negotiated one.
* remote side will migrate its channel to negotiated xport */
/* DO NOTHING */
}
else if(present_ch_ctx->ref_count == 2)
{
/* remote channel is open on same xport as current xport.
* change channel state to GLINK_CH_STATE_OPEN and notify client */
open_ch_ctx->state = GLINK_CH_STATE_OPEN;
open_ch_ctx->notify_state(open_ch_ctx, open_ch_ctx->priv,
GLINK_CONNECTED);
}
else
{
/* something went wrong in updating ref_count of channel */
ASSERT(0);
}
}
else
{
/* migrated xport priority <> current xport priority */
/* check if remote channel is opened on negotiated xport already */
if(migrated_ch_prio == present_if_ptr->glink_priority &&
(open_ch_ctx->ch_open_options & GLINK_OPT_INITIAL_XPORT))
{
/* remote channel is already on negotiated xport. remote channel
* will not migrate. Local side should migrate */
glink_channel_ctx_type *new_ch_ctx =
(glink_channel_ctx_type *)glink_os_calloc(
sizeof(glink_channel_ctx_type));
/* Fill in the channel info structure */
glink_os_string_copy(new_ch_ctx->name,
open_ch_ctx->name,
sizeof(open_ch_ctx->name));
new_ch_ctx->priv = open_ch_ctx->priv;
new_ch_ctx->notify_rx = open_ch_ctx->notify_rx;
new_ch_ctx->notify_rxv = open_ch_ctx->notify_rxv;
new_ch_ctx->notify_tx_done = open_ch_ctx->notify_tx_done;
new_ch_ctx->notify_state = open_ch_ctx->notify_state;
new_ch_ctx->notify_rx_intent_req =
open_ch_ctx->notify_rx_intent_req;
new_ch_ctx->notify_rx_intent = open_ch_ctx->notify_rx_intent;
new_ch_ctx->notify_rx_sigs = open_ch_ctx->notify_rx_sigs;
new_ch_ctx->ch_open_options = open_ch_ctx->ch_open_options;
present_ch_ctx->notify_state = glink_dummy_ch_migration_notification_cb;
if_ptr->tx_cmd_ch_close( if_ptr, present_ch_ctx->lcid );
/* migrate to negotiated xport */
glinki_add_ch_to_xport(present_if_ptr,
present_if_ptr,
new_ch_ctx,
NULL,
TRUE,
TRUE,
present_if_ptr->glink_priority);
}
else
{
/* Either our transport is "set in stone" OR */
/* remote side will migrate to negotiated xport and will call
* remote open on this side which will cause channel migration
* negotiation and this side will ultimately migrate */
/* DO NOTHING */
}
}
#else
if(open_ch_ctx->ref_count == 2)
{
/* remote channel is open on same xport as current xport.
* change channel state to GLINK_CH_STATE_OPEN and notify client */
open_ch_ctx->state = GLINK_CH_STATE_OPEN;
open_ch_ctx->notify_state(open_ch_ctx, open_ch_ctx->priv,
GLINK_CONNECTED);
}
#endif
GLINK_LOG_EVENT(GLINK_EVENT_CH_OPEN_ACK, open_ch_ctx->name,
xport_ctx->xport, xport_ctx->remote_ss, lcid);
return;
}
open_ch_ctx = smem_list_next(open_ch_ctx);
}
/* We are here in case we could not find the channel in the open list. */
ASSERT(0);
}
/*===========================================================================
FUNCTION glink_rx_cmd_ch_close_ack
DESCRIPTION This function is invoked by the transport in response to
glink_transport_if_type:: tx_cmd_ch_close
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
lcid Local Channel ID
RETURN VALUE None.
SIDE EFFECTS None
===========================================================================*/
void glink_rx_cmd_ch_close_ack
(
glink_transport_if_type *if_ptr, /* Pointer to the interface instance */
uint32 lcid /* Local channel ID */
)
{
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;
/* Move to closed state. Implies we clean up the channel from the
* open list */
/* 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)
{
if(open_ch_ctx->lcid == lcid) {
/* Found channel */
open_ch_ctx->ref_count--;
GLINK_LOG_EVENT(GLINK_EVENT_CH_CLOSE_ACK, open_ch_ctx->name,
xport_ctx->xport, xport_ctx->remote_ss, lcid);
/* Transition state */
if(open_ch_ctx->state == GLINK_CH_STATE_OPEN) {
/* this side initiated close procedure */
open_ch_ctx->state = GLINK_CH_STATE_REMOTE_OPEN_LOCAL_CLOSE;
} else if(open_ch_ctx->state == GLINK_CH_STATE_LOCAL_OPEN ||
open_ch_ctx->state == GLINK_CH_STATE_LOCAL_OPEN_REMOTE_CLOSE ||
open_ch_ctx->state == GLINK_CH_STATE_REMOTE_OPEN) {
/* Other side never opened the port, or closed from its end */
/* Clear everything */
if(open_ch_ctx->ref_count == 0)
{
xport_ctx->channel_cleanup(open_ch_ctx);
/* re-use channel id if it can be done */
if(lcid == (xport_ctx->free_lcid-1)) {
/* If channel being closed is the last opened channel
re-use the lcid of this channel for any new channels */
xport_ctx->free_lcid--;
}
smem_list_delete(&if_ptr->glink_core_priv->open_list, open_ch_ctx);
/* Notify the client */
open_ch_ctx->notify_state( open_ch_ctx, open_ch_ctx->priv,
GLINK_LOCAL_DISCONNECTED);
glink_os_free(open_ch_ctx);
}
} else {
/* Unsupported state */
ASSERT(0);
}
glink_os_cs_release(&xport_ctx->channel_q_cs);
return;
}
open_ch_ctx = smem_list_next(open_ch_ctx);
}/* end while */
/* We are here in case we could not find the channel in the open list. */
ASSERT(0);
glink_os_cs_release(&xport_ctx->channel_q_cs);
}
/*===========================================================================
FUNCTION glink_rx_cmd_ch_remote_close
DESCRIPTION Remote channel close request; will result in sending
glink_transport_if_type:: tx_cmd_ch_remote_close_ack
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
rcid Remote Channel ID
RETURN VALUE None.
SIDE EFFECTS None
===========================================================================*/
void glink_rx_cmd_ch_remote_close
(
glink_transport_if_type *if_ptr, /* Pointer to the interface instance */
uint32 rcid /* Remote channel ID */
)
{
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)
{
if(open_ch_ctx->rcid == rcid) {
GLINK_LOG_EVENT(GLINK_EVENT_CH_REMOTE_CLOSE, open_ch_ctx->name,
xport_ctx->xport, xport_ctx->remote_ss, rcid);
/* Found channel, transition it to appropriate state based
* on current state */
open_ch_ctx->ref_count--;
if(open_ch_ctx->state == GLINK_CH_STATE_OPEN) {
open_ch_ctx->state = GLINK_CH_STATE_LOCAL_OPEN_REMOTE_CLOSE;
/* Inform the client */
open_ch_ctx->notify_state( open_ch_ctx, open_ch_ctx->priv,
GLINK_REMOTE_DISCONNECTED);
/* Send the remote close ACK back to the other side */
if_ptr->tx_cmd_ch_remote_close_ack(if_ptr, open_ch_ctx->rcid);
/* Free channel resources */
xport_ctx->channel_cleanup(open_ch_ctx);
GLINK_LOG_EVENT(GLINK_EVENT_CH_REMOTE_CLOSE, open_ch_ctx->name,
xport_ctx->xport, xport_ctx->remote_ss, rcid);
glink_os_cs_release(&xport_ctx->channel_q_cs);
return;
} else if (open_ch_ctx->state == GLINK_CH_STATE_REMOTE_OPEN ||
open_ch_ctx->state == GLINK_CH_STATE_REMOTE_OPEN_LOCAL_CLOSE) {
/* Local side never opened the channel OR it opened it but closed it */
/* Free channel resources */
xport_ctx->channel_cleanup(open_ch_ctx);
/* Send the remote close ACK back to the other side */
if_ptr->tx_cmd_ch_remote_close_ack(if_ptr, open_ch_ctx->rcid);
if(open_ch_ctx->ref_count == 0)
{
/* re-use channel id if it can be done */
if (open_ch_ctx->lcid == (xport_ctx->free_lcid - 1)) {
/* If channel being closed is the last opened channel
re-use the lcid of this channel for any new channels */
xport_ctx->free_lcid--;
}
smem_list_delete(&if_ptr->glink_core_priv->open_list, open_ch_ctx);
glink_os_free(open_ch_ctx);
}
glink_os_cs_release(&xport_ctx->channel_q_cs);
return;
} else {
/* SHould not get this callback for a channel in any other state */
//OS_LOG_ERROR(3, "GLINK_RX_CMD_CH_REMOTE_CLOSE: received close cmd for invalid state"
// "[channel ctx: 0x%x][current channel state: %d]",
// open_ch_ctx, open_ch_ctx->state );
glink_os_cs_release(&xport_ctx->channel_q_cs);
if_ptr->tx_cmd_ch_remote_close_ack(if_ptr, open_ch_ctx->rcid);
return;
}
}
open_ch_ctx = smem_list_next(open_ch_ctx);
}/* end while */
/* We are here in case we could not find the channel in the open list
* or OPEN state. */
ASSERT(0);
glink_os_cs_release(&xport_ctx->channel_q_cs);
}
/*===========================================================================
FUNCTION glink_ch_state_local_trans
DESCRIPTION Process local state transition
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
rcid Remote Channel ID
RETURN VALUE None.
SIDE EFFECTS None
===========================================================================*/
void glink_ch_state_local_trans
(
glink_transport_if_type *if_ptr, /* Pointer to the interface instance */
uint32 lcid, /* Local channel ID */
glink_ch_state_type new_state /* New local channel state */
)
{
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 */
open_ch_ctx = smem_list_first(&if_ptr->glink_core_priv->open_list);
while(open_ch_ctx != NULL)
{
if(open_ch_ctx->lcid == lcid) {
/* Found channel, transition it to appropriate state */
open_ch_ctx->state = new_state;
}
open_ch_ctx = smem_list_next(open_ch_ctx);
GLINK_LOG_EVENT(GLINK_EVENT_CH_STATE_TRANS, open_ch_ctx->name,
xport_ctx->xport, xport_ctx->remote_ss, new_state);
return;
}/* end while */
/* We are here in case we could not find the channel in the open list
* or OPEN state. */
ASSERT(0);
}
/*===========================================================================
FUNCTION glink_rx_put_pkt_ctx
DESCRIPTION Transport invokes this call to receive a packet fragment (must
have previously received an rx_cmd_rx_data packet)
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
rcid Remote Channel ID
*intent_ptr Pointer to the intent fragment
complete True if pkt is complete
RETURN VALUE None
SIDE EFFECTS None
===========================================================================*/
void glink_rx_put_pkt_ctx
(
glink_transport_if_type *if_ptr, /* Pointer to the interface instance */
uint32 rcid, /* Remote channel ID */
glink_rx_intent_type *intent_ptr, /* Fragment ptr */
boolean complete /* True if pkt is complete */
)
{
glink_channel_ctx_type *open_ch_ctx;
glink_core_xport_ctx_type *xport_ctx;
ASSERT(if_ptr != NULL && intent_ptr != NULL);
xport_ctx = if_ptr->glink_core_priv;
/* Find channel in the open_list */
open_ch_ctx = smem_list_first(&if_ptr->glink_core_priv->open_list);
while(open_ch_ctx != NULL)
{
if(open_ch_ctx->rcid == rcid) {
/* Found channel */
GLINK_LOG_EVENT(GLINK_EVENT_CH_PUT_PKT_CTX, open_ch_ctx->name,
xport_ctx->xport, xport_ctx->remote_ss, intent_ptr->iid);
xport_ctx->channel_receive_pkt(open_ch_ctx, intent_ptr);
return;
}
open_ch_ctx = smem_list_next(open_ch_ctx);
}/* end while */
/* We end up here if we don't find the channel */
ASSERT(0);
}
/*===========================================================================
FUNCTION glink_rx_cmd_remote_sigs
DESCRIPTION Transport invokes this call to inform GLink of remote side
changing its control signals
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
rcid Remote Channel ID
remote_sigs Remote signal state.
RETURN VALUE None.
SIDE EFFECTS None
===========================================================================*/
void glink_rx_cmd_remote_sigs
(
glink_transport_if_type *if_ptr, /* Pointer to the interface instance */
uint32 rcid, /* Remote channel ID */
uint32 remote_sigs /* Remote control signals */
)
{
glink_channel_ctx_type *open_ch_ctx;
uint32 prev_sigs;
ASSERT(if_ptr != NULL);
/* Find channel in the open_list */
open_ch_ctx = smem_list_first(&if_ptr->glink_core_priv->open_list);
while(open_ch_ctx != NULL)
{
if(open_ch_ctx->rcid == rcid) {
/* Found channel, let client know of new remote signal state */
prev_sigs = open_ch_ctx->remote_sigs;
open_ch_ctx->remote_sigs = remote_sigs;
open_ch_ctx->notify_rx_sigs(open_ch_ctx, open_ch_ctx->priv,
prev_sigs, remote_sigs);
return;
}
open_ch_ctx = smem_list_next(open_ch_ctx);
}
/* We end up here if we don't find the channel */
ASSERT(0);
}
/*===========================================================================
FUNCTION glink_tx_resume
DESCRIPTION If transport was full and could not continue a transmit
operation, then it will call this function to notify the core
that it is ready to resume transmission.
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
RETURN VALUE None.
SIDE EFFECTS None
===========================================================================*/
void glink_tx_resume
(
glink_transport_if_type *if_ptr /* Pointer to the interface instance */
)
{
/* Not sure what to do here */
}
/*===========================================================================
FUNCTION glink_set_core_version
DESCRIPTION Sets the core version used by the transport; called after
completing negotiation.
ARGUMENTS *if_ptr Pointer to interface instance; must be unique
for each edge
version Negotiated transport version
RETURN VALUE None.
SIDE EFFECTS None
===========================================================================*/
void glink_set_core_version
(
glink_transport_if_type *if_ptr, /* Pointer to the interface instance */
uint32 version /* Version */
)
{
}