/*---------------------------------------------------------------------------*
  Project:  NitroSDK - WM - libraries
  File:     wm_sync.c

  Copyright 2003,2004 Nintendo.  All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law.  They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Log: wm_sync.c,v $
  Revision 1.7  2004/10/22 04:37:28  terui
  Changed functions that return int type as WMErrCode so that they return WMErrCode enumerator type.

  Revision 1.6  2004/10/18 04:17:46  terui
  Added way to baratsukaseru beacon interval and scan interval.

  Revision 1.5  2004/10/15 14:12:07  terui
  Changed fixed value returned when WM_GetAllowedChannel function is not initialized.

  Revision 1.4  2004/10/01 05:38:10  seiki_masashi
  Made changes to return the minimum radio wave strength when there is no child

  Revision 1.3  2004/10/01 04:04:14  terui
  Made correction due to the addition of a feature that precalculates the wireless communication permitted channel when it is started

  Revision 1.2  2004/09/16 02:57:32  seiki_masashi
  Fixed bug in WM_GetMPReceiveBufferSize

  Revision 1.1  2004/09/10 11:03:30  terui
  Divided wm.c, so made new upload.

  $NoKeywords: $
 *---------------------------------------------------------------------------*/

#include    <nitro/wm.h>
#include    "wm_arm9_private.h"


/*---------------------------------------------------------------------------*
  Name:         WM_SetIndCallback

  Description:  Sets the function called for status notification from WM7.

  Arguments:    callback    -   Specifies the callback function called during status notification
  						from wireless firmware.

  Returns:      WMErrCode   -   Returns process results.
 *---------------------------------------------------------------------------*/
WMErrCode
WM_SetIndCallback( WMCallbackFunc callback )
{
    WMErrCode   result;
    OSIntrMode  e;

    // Prohibit interrupts
    e = OS_DisableInterrupts();

    // Confirm that initialized
    result = WMi_CheckInitialized();
    if( result != WM_ERRCODE_SUCCESS )
    {
        // End prohibition of interrupts
        (void)OS_RestoreInterrupts( e );
        return result;
    }

    // Set the callback for Indication
    WMi_GetSystemWork()->indCallback = callback;
    // End prohibition of interrupts
    (void)OS_RestoreInterrupts( e );

    return WM_ERRCODE_SUCCESS;
}

/*---------------------------------------------------------------------------*
  Name:         WM_SetPortCallback

  Description:  Sets the function called for communication frame reception notification from WM7.

  Arguments:    port        -   Port number.
                callback    -   Callback function called during receive notification.
                arg         -   Argument passed to callback function as WMPortRecvCallback.arg.

  Returns:      WMErrCode   -   Returns process results.
 *---------------------------------------------------------------------------*/
WMErrCode
WM_SetPortCallback( u16 port, WMCallbackFunc callback, void* arg )
{
    WMErrCode   result;
    OSIntrMode  e;

    // Prohibit interrupts
    e = OS_DisableInterrupts();
    // Confirm that initialized
    result = WMi_CheckInitialized();
    if( result != WM_ERRCODE_SUCCESS )
    {
        // End prohibition of interrupts
        (void)OS_RestoreInterrupts( e );
        return result;
    }

    // Set the callback for data reception
    {
        WMArm9Buf*  p = WMi_GetSystemWork();
    
        p->portCallbackTable[ port ] = callback;
        p->portCallbackArgument[ port ] = arg;
    }
    // End prohibition of interrupts
    (void)OS_RestoreInterrupts( e );

    return WM_ERRCODE_SUCCESS;
}

/*---------------------------------------------------------------------------*
  Name:         WM_ReadStatus

  Description:  Gets the structure showing the wireless library status.

  Arguments:    statusBuf   -   pointer to the variable that gets status

  Returns:      WMErrCode   -   Returns process results.
 *---------------------------------------------------------------------------*/
WMErrCode
WM_ReadStatus( WMStatus* statusBuf )
{
    WMErrCode   result;
    WMArm9Buf*  p = WMi_GetSystemWork();

    // Confirm that initialized
    result = WMi_CheckInitialized();
    WM_CHECK_RESULT( result );

    // Check parameter
    if( statusBuf == NULL )
    {
        WM_WARNING( "Parameter \"statusBuf\" must not be NULL.\n" );
        return WM_ERRCODE_INVALID_PARAM;
    }

    // Copy the WMStatus structure to the CPU
    DC_InvalidateRange( p->status , sizeof( WMStatus ) );
    MI_CpuCopyFast( p->status , statusBuf , sizeof( WMStatus ) );

    return WM_ERRCODE_SUCCESS;
}

/*---------------------------------------------------------------------------*
  Name:         WM_GetMPSendBufferSize

  Description:  Calculate the size of the Send buffer for MP communications.
                At this time, StartParent and StartConnect must be finished.

  Arguments:    None.

  Returns:      int     -   send buffer size that must pass to WM_StartMP.
 *---------------------------------------------------------------------------*/
int WM_GetMPSendBufferSize( void )
{
    WMErrCode   result;
    int         maxSendSize;
    WMArm9Buf*  p = WMi_GetSystemWork();

    // Check status
    result = WMi_CheckStateEx( 2, WM_STATE_PARENT, WM_STATE_CHILD );
    if( result != WM_ERRCODE_SUCCESS )
    {
        return 0;
    }

    // Confirm MP status
    DC_InvalidateRange( &( p->status->mp_flag ) , 4 );    // Invalidates ARM7 status region's cache
    if( p->status->mp_flag == TRUE )
    {
        WM_WARNING( "Already started MP protocol. So can't execute request.\n" );
        return 0;
    }

    // Reference the MWStatus structure for information needed for the calculation
    DC_InvalidateRange( &( p->status->maxSendSize ) , 4 );
    maxSendSize = p->status->maxSendSize;

    return ( ( maxSendSize + 31 ) & ~0x1f );
}

/*---------------------------------------------------------------------------*
  Name:         WM_GetMPReceiveBufferSize

  Description:  Calculate the size of the Receive buffer for MP communications.
                At this time, StartParent and StartConnect must be finished.

  Arguments:    None.

  Returns:      int     -   size of receive buffer that must be passed to WM_StartMP
 *---------------------------------------------------------------------------*/
int WM_GetMPReceiveBufferSize( void )
{
    WMErrCode   result;
    BOOL        isParent;
    int         maxReceiveSize;
    int         maxEntry;
    WMArm9Buf*  p = WMi_GetSystemWork();
    
    // Check status
    result = WMi_CheckStateEx( 2, WM_STATE_PARENT, WM_STATE_CHILD );
    if( result != WM_ERRCODE_SUCCESS )
    {
        return 0;
    }

    // Confirm MP status
    DC_InvalidateRange( &( p->status->mp_flag ) , 4 );
    if( p->status->mp_flag == TRUE )
    {
        WM_WARNING( "Already started MP protocol. So can't execute request.\n" );
        return 0;
    }

    // Reference the MWStatus structure for information needed for the calculation
    DC_InvalidateRange( &( p->status->aid ) , 2 );
    isParent = ( p->status->aid == 0 ) ? TRUE : FALSE;
    DC_InvalidateRange( &( p->status->maxReceiveSize ) , 2 );
    maxReceiveSize = p->status->maxReceiveSize;
    if( isParent == TRUE )
    {
        DC_InvalidateRange( &( p->status->pparam.maxEntry ) , 2 );
        maxEntry = p->status->pparam.maxEntry;
        return ((sizeof( WMmpRecvHeader ) - sizeof( WMmpRecvData ) +
                ( ( sizeof( WMmpRecvData ) + maxReceiveSize - 2 + 2/*MACBUG*/ ) * maxEntry )
                 + 31) & ~0x1f) * 2;
    }
    else
    {
        return ((sizeof( WMMpRecvBuf ) + maxReceiveSize - 4 + 31) & ~0x1f) * 2;
    }
}

/*---------------------------------------------------------------------------*
  Name:         WM_ReadMPData

  Description:  Extracts data from the specified child from all the 
                received data.

  Arguments:    header      -   pointer that shows all receive data
                aid         -   AID of child that extracts data

  Returns:      WMMpRecvData* - Returns pointer to received data from corresponding child.
                                Returns NULL when extraction fails.
 *---------------------------------------------------------------------------*/
WMMpRecvData*
WM_ReadMPData( const WMMpRecvHeader* header , u16 aid )
{
    int             i;
    WMErrCode       result;
    WMMpRecvData*   recvdata_p[ WM_NUM_MAX_CHILD ];    // Array of pointers to data starting position for each child (up to 15 devices).
    WMArm9Buf*      p   = WMi_GetSystemWork();

    // Confirm that initialized
    result = WMi_CheckInitialized();
    if( result != WM_ERRCODE_SUCCESS )
    {
        return NULL;
    }

    // Check parameter
    if( ( aid < 1 ) || ( aid > WM_NUM_MAX_CHILD ) )
    {
        WM_WARNING( "Parameter \"aid\" must be between 1 and %d.\n" , WM_NUM_MAX_CHILD );
        return NULL;
    }

    // Does child with that AID exist?
    DC_InvalidateRange( &( p->status->child_bitmap ) , 2 );
    if( 0 == ( p->status->child_bitmap & ( 0x0001 << aid ) ) )
    {
        return NULL;
    }

    // Is there any received data?
    if( header->count == 0 )
    {
        return NULL;
    }

    // Calculate starting position for child data
    recvdata_p[ 0 ] = (WMMpRecvData*)( header->data );

    i = 0;
    do
    {
        // Returns pointer to data of child with specified AID, if it exists
        if( recvdata_p[ i ]->aid == aid )
            return recvdata_p[ i ];

        ++i;
        recvdata_p[ i ] = (WMMpRecvData*)( (u32)( recvdata_p[ i - 1 ] ) + header->length );
    }while( i < header->count );

    return NULL;
}

/*---------------------------------------------------------------------------*
  Name:         WM_GetAllowedChannel

  Description:  Gets the channel that is allowed to be used for communications.

  Arguments:    None.

  Returns:      u16 -   Returns bit field of channels permitted for use. The lowest
  				bit indicates channel 1; the highest bit indicates channel 16.
  				Channels with bit 1 are permitted for use. Channels with bit 0 are
  				prohibited for use. Normally, within channels 1 to 13, several values
  				where bit is 1 are returned. When 0x0000 is returned, there is no channel
  				permitted for use, so the wireless function cannot use it.
  				Also, when function fails because it is not initialized, 0x8000 is returned.
 *---------------------------------------------------------------------------*/
u16
WM_GetAllowedChannel( void )
{
#ifdef WM_PRECALC_ALLOWEDCHANNEL
    WMErrCode   result;

    // confirm initialization
    result = WMi_CheckInitialized();
    if( result != WM_ERRCODE_SUCCESS )
    {
        return WM_GET_ALLOWED_CHANNEL_BEFORE_INIT;
    }

    return *( (u16*)(
        (u32)( OS_GetSystemWork()->nvramUserInfo ) +
        ( ( sizeof( NVRAMConfig ) + 3 ) & ~0x00000003 )
        + 6
    ) );
#else
    WMErrCode   result;
    WMArm9Buf*  p   = WMi_GetSystemWork();

    // confirm that wireless hardware has started
    result = WMi_CheckIdle();
    if( result != WM_ERRCODE_SUCCESS )
    {
        return WM_GET_ALLOWED_CHANNEL_BEFORE_INIT;
    }

    DC_InvalidateRange( &( p->status->allowedChannel ) , 2 );
    return p->status->allowedChannel;
#endif
}

/*---------------------------------------------------------------------------*
  Name:         WM_GetLinkLevel

  Description:  Gets the link strength when communicating. This is an
                asynchronous function.

  Arguments:    None.

  Returns:      WMLinkLevel -   Returns link strength on a four-step scale. 
                                .
 *---------------------------------------------------------------------------*/
WMLinkLevel
WM_GetLinkLevel( void )
{
    WMErrCode   result;
    WMArm9Buf*  p   = WMi_GetSystemWork();

    // Confirm that initialized
    result = WMi_CheckInitialized();
    if( result != WM_ERRCODE_SUCCESS )
    {
        return WM_LINK_LEVEL_0; // Lowest radio-wave strength
    }

    DC_InvalidateRange( &( p->status->state ) , 2 );
    switch( p->status->state )
    {
    case WM_STATE_MP_PARENT:
        // If parent
        DC_InvalidateRange( &( p->status->child_bitmap ) , 2 );
        if ( p->status->child_bitmap == 0 )
        {
            // when there is not child
            return WM_LINK_LEVEL_0; // Lowest radio wave strength
        }
    case WM_STATE_MP_CHILD:
    case WM_STATE_DCF_CHILD:
        // If child
        DC_InvalidateRange( &( p->status->linkLevel ) , 2 );
        return (WMLinkLevel)( p->status->linkLevel );
    }

    // when unconnected
    return WM_LINK_LEVEL_0; // lowest electric strength
}

/*---------------------------------------------------------------------------*
  Name:         WM_GetDispersionBeaconPeriod

  Description:  Gets beacon interval value to set when acting as parent.

  Arguments:    None.

  Returns:      u16 -   beacon interval value (ms) to set
 *---------------------------------------------------------------------------*/
u16
WM_GetDispersionBeaconPeriod( void )
{
    u8      mac[ 6 ];
    u16     ret;
    s32     i;

    OS_GetMacAddress( mac );
    for( i = 0 , ret = 0 ; i < 6 ; i ++ )
    {
        ret += mac[ i ];
    }
    ret += OS_GetVBlankCount();
    ret *= 7;
    return (u16)( WM_DEFAULT_BEACON_PERIOD + ( ret % 20 ) );
}

/*---------------------------------------------------------------------------*
  Name:         WM_GetDispersionScanPeriod

  Description:  Gets search limit time to set when searching for parent as child.

  Arguments:    None.

  Returns:      u16 -   search limit time (ms) to set
 *---------------------------------------------------------------------------*/
u16
WM_GetDispersionScanPeriod( void )
{
    u8      mac[ 6 ];
    u16     ret;
    s32     i;

    OS_GetMacAddress( mac );
    for( i = 0 , ret = 0 ; i < 6 ; i ++ )
    {
        ret += mac[ i ];
    }
    ret += OS_GetVBlankCount();
    ret *= 13;
    return (u16)( WM_DEFAULT_SCAN_PERIOD + ( ret % 10 ) );
}

/*---------------------------------------------------------------------------*
    End of file
 *---------------------------------------------------------------------------*/

