/* 
 * Copyright (C) 1995 Advanced RISC Machines Limited. All rights reserved.
 * 
 * This software may be freely used, copied, modified, and distributed
 * provided that the above copyright notice is preserved in all copies of the
 * software.
 */

/* -*-C-*-
 *
 * $Revision: 1.3 $
 *     $Date: 1999/11/01 13:31:39 $
 *
 *
 *   Project: ANGEL
 *
 *     Title: Parameter negotiation utility functions
 */

#include "params.h"

#include "angel_endian.h"
#include "logging.h"


/*
 * Function: Angel_FindParam
 *  Purpose: find the value of a given parameter from a config.
 *
 * see params.h for details
 */
bool Angel_FindParam( ADP_Parameter          type,
                      const ParameterConfig *config,
                      unsigned int          *value )
{
    unsigned int i;

    for ( i = 0; i < config->num_parameters; ++i )
       if ( config->param[i].type == type )
       {
           *value = config->param[i].value;
           return TRUE;
       }

    return FALSE;
}


#if !defined(TARGET) || !defined(MINIMAL_ANGEL) || MINIMAL_ANGEL == 0

ParameterList *Angel_FindParamList( const ParameterOptions *options,
                                    ADP_Parameter           type )
{
    unsigned int i;

    for ( i = 0; i < options->num_param_lists; ++i )
       if ( options->param_list[i].type == type )
          return &options->param_list[i];

    return NULL;
}


#if defined(TARGET) || defined(TEST_PARAMS)
/*
 * Function: Angel_MatchParams
 *  Purpose: find a configuration from the requested options which is
 *           the best match from the supported options.
 *
 * see params.h for details
 */
const ParameterConfig *Angel_MatchParams( const ParameterOptions *requested,
                                          const ParameterOptions *supported )
{
    static Parameter        chosen_params[AP_NUM_PARAMS];
    static ParameterConfig  chosen_config = {
        AP_NUM_PARAMS,
        chosen_params
    };
    unsigned int i;

    ASSERT( requested != NULL, "requested is NULL" );
    ASSERT( requested != NULL, "requested is NULL" );
    ASSERT( supported->num_param_lists <= AP_NUM_PARAMS, "supp_num too big" );

    if ( requested->num_param_lists > supported->num_param_lists )
    {
        WARN( "req_num exceeds supp_num" );
        return NULL;
    }

    for ( i = 0; i < requested->num_param_lists; ++i )
    {
        bool           match;
        unsigned int   j;

        const ParameterList *req_list = &requested->param_list[i];
        ADP_Parameter        req_type = req_list->type;
        const ParameterList *sup_list = Angel_FindParamList(
                                            supported, req_type );

        if ( sup_list == NULL )
        {
#ifdef ASSERTIONS_ENABLED
            __rt_warning( "option %x not supported\n", req_type );
#endif
            return NULL;
        }

        for ( j = 0, match = FALSE;
              (j < req_list->num_options) && !match;
              ++j
            )
        {
            unsigned int k;

            for ( k = 0;
                  (k < sup_list->num_options) && !match;
                  ++k
                )
            {
                if ( req_list->option[j] == sup_list->option[k] )
                {
#ifdef DEBUG
                    __rt_info( "chose value %d for option %x\n",
                               req_list->option[j], req_type );
#endif
                    match = TRUE;
                    chosen_config.param[i].type = req_type;
                    chosen_config.param[i].value = req_list->option[j];
                }
            }
        }

        if ( !match )
        {
#ifdef ASSERTIONS_ENABLED
            __rt_warning( "no match found for option %x\n", req_type );
#endif
            return NULL;
        }
    }

    chosen_config.num_parameters = i;
    INFO( "match succeeded" );
    return &chosen_config;
}
#endif /* defined(TARGET) || defined(TEST_PARAMS) */


#if !defined(TARGET) || defined(TEST_PARAMS)
/*
 * Function: Angel_StoreParam
 *  Purpose: store the value of a given parameter to a config.
 *
 * see params.h for details
 */
bool Angel_StoreParam( ParameterConfig *config,
                       ADP_Parameter    type,
                       unsigned int     value )
{
    unsigned int i;

    for ( i = 0; i < config->num_parameters; ++i )
       if ( config->param[i].type == type )
       {
           config->param[i].value = value;
           return TRUE;
       }

    return FALSE;
}
#endif /* !defined(TARGET) || defined(TEST_PARAMS) */


#if defined(TARGET) || defined(LINK_RECOVERY) || defined(TEST_PARAMS)
/*
 * Function: Angel_BuildParamConfigMessage
 *  Purpose: write a parameter config to a buffer in ADP format.
 *
 * see params.h for details
 */
unsigned int Angel_BuildParamConfigMessage( unsigned char         *buffer,
                                            const ParameterConfig *config )
{
    unsigned char *start = buffer;
    unsigned int i;

    PUT32LE( buffer, config->num_parameters );
    buffer += sizeof( word );

    for ( i = 0; i < config->num_parameters; ++i )
    {
        PUT32LE( buffer, config->param[i].type );
        buffer += sizeof( word );
        PUT32LE( buffer, config->param[i].value );
        buffer += sizeof( word );
    }

    return (buffer - start);
}
#endif /* defined(TARGET) || defined(LINK_RECOVERY) || defined(TEST_PARAMS) */


#if !defined(TARGET) || defined(TEST_PARAMS)
/*
 * Function: Angel_BuildParamOptionsMessage
 *  Purpose: write a parameter Options to a buffer in ADP format.
 *
 * see params.h for details
 */
unsigned int Angel_BuildParamOptionsMessage( unsigned char          *buffer,
                                             const ParameterOptions *options )
{
    unsigned char *start = buffer;
    unsigned int i, j;

    PUT32LE( buffer, options->num_param_lists );
    buffer += sizeof( word );

    for ( i = 0; i < options->num_param_lists; ++i )
    {
        PUT32LE( buffer, options->param_list[i].type );
        buffer += sizeof( word );
        PUT32LE( buffer, options->param_list[i].num_options );
        buffer += sizeof( word );
        for ( j = 0; j < options->param_list[i].num_options; ++j )
        {
            PUT32LE( buffer, options->param_list[i].option[j] );
            buffer += sizeof( word );
        }
    }

    return (buffer - start);
}
#endif /* !defined(TARGET) || defined(TEST_PARAMS) */


#if !defined(TARGET) || defined(LINK_RECOVERY) || defined(TEST_PARAMS)
/*
 * Function: Angel_ReadParamConfigMessage
 *  Purpose: read a parameter config from a buffer where it is in ADP format.
 *
 * see params.h for details
 */
bool Angel_ReadParamConfigMessage( const unsigned char *buffer,
                                   ParameterConfig     *config )
{
    unsigned int word;
    unsigned int i;

    word = GET32LE( buffer );
    buffer += sizeof( word );
    if ( word > config->num_parameters )
    {
        WARN( "not enough space" );
        return FALSE;
    }
    config->num_parameters = word;

    for ( i = 0; i < config->num_parameters; ++i )
    {
        config->param[i].type = (ADP_Parameter)GET32LE( buffer );
        buffer += sizeof( word );
        config->param[i].value = GET32LE( buffer );
        buffer += sizeof( word );
    }

    return TRUE;
}
#endif /* !defined(TARGET) || defined(LINK_RECOVERY) || defined(TEST_PARAMS) */


#if defined(TARGET) || defined(TEST_PARAMS)
/*
 * Function: Angel_ReadParamOptionsMessage
 *  Purpose: read a parameter options block from a buffer
 *             where it is in ADP format.
 *
 * see params.h for details
 */
bool Angel_ReadParamOptionsMessage( const unsigned char *buffer,
                                    ParameterOptions    *options )
{
    unsigned int word;
    unsigned int i, j;

    word = GET32LE( buffer );
    buffer += sizeof( word );
    if ( word > options->num_param_lists )
    {
        WARN( "not enough space" );
        return FALSE;
    }
    options->num_param_lists = word;

    for ( i = 0; i < options->num_param_lists; ++i )
    {
        ParameterList *list = &options->param_list[i];

        list->type = (ADP_Parameter)GET32LE( buffer );
        buffer += sizeof( word );
        word = GET32LE( buffer );
        buffer += sizeof( word );
        if ( word > list->num_options )
        {
            WARN( "not enough list space" );
            return FALSE;
        }
        list->num_options = word;

        for ( j = 0; j < list->num_options; ++j )
        {
            list->option[j] = GET32LE( buffer );
            buffer += sizeof( word );
        }
    }

    return TRUE;
}
#endif /* defined(TARGET) || defined(TEST_PARAMS) */

#endif /* !define(TARGET) || !defined(MINIMAL_ANGEL) || MINIMAL_ANGEL == 0 */

/* EOF params.c */
