/*---------------------------------------------------------------------------*
  Project:  NitroSDK - MB - libraries
  File:     mb_fake_child.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: mb_fake_child.c,v $
  Revision 1.6  2004/11/29 04:53:08  takano_makoto
  Fixed the problem where the same region was used by both sendBuffer of WM_StartMP, and SendBuffer of WM_SetMPData.

  Revision 1.5  2004/11/29 02:55:30  takano_makoto
  Fixed 32byte align bug

  Revision 1.4  2004/11/25 12:30:28  takano_makoto
  Changed print debug to OFF

  Revision 1.3  2004/11/24 13:00:35  takano_makoto
  Added error processing

  Revision 1.2  2004/11/23 23:51:20  takano_makoto
  Removed Warning during FINALROM build.

  Revision 1.1  2004/11/22 12:38:30  takano_makoto
  Initial update.

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

#include <nitro.h>
#include "mb_common.h"
#include "mb_block.h"
#include "mb_child.h"
#include "mb_private.h"
#include "tmp_lib_stub.h"
#include "mb_fake_child.h"
#include "mb_wm.h"

#define MB_FAKE_PRINT   (0)

#if (MB_FAKE_PRINT == 1)
#define MB_FAKE_OUTPUT    OS_TPrintf
#else
#define MB_FAKE_OUTPUT( ... ) ((void)0)
#endif

#define MY_ROUND(n, a)      (((u32) (n) + (a) - 1) & ~((a) - 1))

typedef struct
{
    u32 sendBuf[ MB_COMM_PARENT_RECV_MAX * 2 / sizeof(u32) ]        ATTRIBUTE_ALIGN(32); // send buffer 0x400 byte
    u32 recvBuf[ (MB_COMM_PARENT_SEND_MAX + 3) / sizeof(u32) ]      ATTRIBUTE_ALIGN(32); // receive buffer 0xF00 byte
    u32 sendDataBuf[ MB_COMM_PARENT_RECV_MAX * 2 / sizeof(u32) ]    ATTRIBUTE_ALIGN(32); // buffer for creating send data 0x400 byte
    WMBssDesc            bssDescBuf ATTRIBUTE_ALIGN(32); // 32 byte align vital to buffer for parent scan  0xC0 byte
    WMScanParam          scanParam  ATTRIBUTE_ALIGN(32); // scan parameter     vital 32 byte align  0x20 byte
    MBWMWork             wmWork     ATTRIBUTE_ALIGN(32); // work buffer for MP communication   vital 32 byte align
    MBUserInfo           userInfo;                       // user information
    u8                   _padding1[2];
    MBFakeScanCallbackFunc   scanCallback;               // callback for parent scan notification
    MBCommCStateCallbackFunc stateCallback;              // callback for state notification
    u32                  ggid;                           // GGID to search for
    MbBeaconRecvStatus   beaconRecvStatus;               // beacon receive status
    BOOL                 scanning;                       // scanning flag
    BOOL                 endScanBusy;                    // scan end processing flag
    BOOL                 locking;                        // locking flag
    BOOL                 endFlag;                        // MB end processing request
    u32                  targetGgid;                     // binary GGID to request
    u16                  targetFileNo;                   // binary FileNo to request
    u16                  c_comm_state;                   // child state
    u8                   _padding2[16];
} MBFakeWork;

//============================================================================
// Function Prototype Declarations
//============================================================================

static void MBFi_EndComplete( void );

static void MBFi_CommChangeChildState( u16 state, void *arg );
static void MBFi_SendCallback( u16 state, void *arg );
static void MBFi_ErrorCallback( u16 apiid, u16 errcode, BOOL isApiError );

/* for scan */
static void MBFi_StateInStartScan( void );
static void MBFi_StateOutStartScanParent( void* arg );
static void MBFi_CommBeaconRecvCallback(MbBeaconMsg msg, MBGameInfoRecvList *gInfop, int index);

static void MBFi_ScanCallback( u16 state, MBGameInfoRecvList *gInfop, int index );
static void MBFi_ScanErrorCallback( u16 apiid, u16 errcode );
static void MBFi_ScanLock( u8* macAddr );
static void MBFi_ScanUnlock( void );
static BOOL RotateChannel( void );

static void MBFi_StateInEndScan( void );
static void MBFi_StateOutEndScan( void* arg );

/* for entry processing */
static void MBFi_WMCallback( u16 type, void *arg );
static void MBFi_CommChildRecvData( WMPortRecvCallback *cb );
static void MBFi_CommChildSendData( void );

/* for debug output */
#if (MB_FAKE_PRINT == 1)
static void MBFi_PrintMBCallbackType( u16 type );
static void MBFi_PrintMBCommCallbacyType( u16 type );
#else
#define MBFi_PrintMBCallbackType(a)         ((void)0)
#define MBFi_PrintMBCommCallbackType(a)     ((void)0)
#endif

//============================================================================
// Local Variable Declarations
//============================================================================

static vu16         mbf_initialize;
static MBFakeWork   *mbf_work;



//============================================================================
// Function Definitions
//============================================================================


/*---------------------------------------------------------------------------*
  Name:         MB_FakeInit

  Description:  Fake multiboot child initialization function.

  Arguments:    buf     The pointer to the work region necessary to fake fake child.
                        You can get the necessary size with MB_FakeGetWorkSize().
                user    The pointer child user information.
  
  Returns:      None.
 *---------------------------------------------------------------------------*/
void MB_FakeInit( void* buf, const MBUserInfo *user )
{
    SDK_NULL_ASSERT( buf );
    SDK_ASSERT( ((u32)buf & 0x1F) == 0 ); // check 32 byte align
    
    if ( mbf_initialize )
    {
        OS_TWarning("MB_FakeInit multiply called\n");
        return;
    }
    mbf_initialize = 1;
    
    MB_FAKE_OUTPUT("MB_Fake Initialized\n");
    
    // buffer clear
    MI_CpuClear8( buf, sizeof( MBFakeWork ) );
    
    mbf_work = (MBFakeWork*)buf;
    mbf_work->c_comm_state  = MB_COMM_CSTATE_NONE;
    
    MI_CpuCopy8( user, &mbf_work->userInfo, sizeof( MBUserInfo ) );
    // set work buffer for beacon reception
    MBi_SetBeaconRecvStatusBuffer( &mbf_work->beaconRecvStatus );
    // initialize game information reception status
    MB_InitRecvGameInfoStatus();
    // set scan lock function
    MBi_SetScanLockFunc( MBFi_ScanLock, MBFi_ScanUnlock );
}


/*---------------------------------------------------------------------------*
  Name:         MB_FakeEnd

  Description:  Fake multiboot child end function.

  Arguments:    None.
  
  Returns:      None.
 *---------------------------------------------------------------------------*/
void MB_FakeEnd( void )
{
    if ( mbf_work->endFlag )
    {
        // when already in end processing, does nothing
        return;
    }
    
    mbf_work->endFlag = TRUE;
    
    switch ( mbf_work->c_comm_state )
    {
    case MB_COMM_CSTATE_NONE :
        // if scanning, end scan
        if ( mbf_work->scanning )
        {
            mbf_work->scanning = FALSE;
        }
        // before scan start, or after EndScan finish, does nothing and finishes
        else if ( ! mbf_work->endScanBusy )
        {
            MBFi_EndComplete();
        }
        else
        {
            // if scan end processing, waits to end
        }
        break;
        
    case MB_COMM_CSTATE_INIT_COMPLETE:
    case MB_COMM_CSTATE_CONNECT:
    case MB_COMM_CSTATE_CONNECT_FAILED:
    case MB_COMM_CSTATE_DISCONNECTED_BY_PARENT:
    case MB_COMM_CSTATE_REQ_REFUSED:
    case MB_COMM_CSTATE_ERROR:
    case MB_COMM_CSTATE_MEMBER_FULL:
        // reset and end
        MBi_WMReset();
        break;
    
    case MB_COMM_CSTATE_REQ_ENABLE:
    case MB_COMM_CSTATE_DLINFO_ACCEPTED:
    case MB_COMM_CSTATE_RECV_PROCEED:
    case MB_COMM_CSTATE_RECV_COMPLETE:
        // end safely in order from MP
        MBi_WMDisconnect();
        break;
        
    case MB_COMM_CSTATE_BOOTREQ_ACCEPTED:
        // wait for completion of disconnect
        break;
    
    case MB_COMM_CSTATE_BOOT_READY:
    case MB_COMM_CSTATE_CANCELLED:
        // already fallen into IDLE state so ends
        MBFi_EndComplete();
        break;
        
    default:
        OS_TPanic("MB fake child is in Illegal State\n");
    }
}


/*---------------------------------------------------------------------------*
  Name:         MBFi_EndComplete

  Description:  fake child library end processing

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_EndComplete( void )
{
    mbf_initialize = 0;
    mbf_work->endFlag = 0;
    // clear port callback
    MBi_WMClearCallback();
    
    // end notification to user
    MBFi_CommChangeChildState( MB_COMM_CSTATE_FAKE_END, NULL );
}


/*---------------------------------------------------------------------------*
  Name:         MB_FakeGetWorkSize

  Description:  Gets work size needed for fake child library.

  Arguments:    None.

  Returns:      size of necessary work buffer
 *---------------------------------------------------------------------------*/
u32 MB_FakeGetWorkSize( void )
{
    return sizeof( MBFakeWork );
}



/*---------------------------------------------------------------------------*
  Name:         MB_FakeSetCStateCallback

  Description:  Sets the callback function that sends notification of fake child state transition.

  Arguments:    callback    callback function to set

  Returns:      None.
 *---------------------------------------------------------------------------*/
void MB_FakeSetCStateCallback( MBCommCStateCallbackFunc callback )
{
    OSIntrMode enabled = OS_DisableInterrupts();
    
    mbf_work->stateCallback = callback;
    
    (void)OS_RestoreInterrupts( enabled );
}


/*---------------------------------------------------------------------------*
  Name:         MBFi_CommChangeChildState

  Description:  Function that changes child state.
                Returns callback to user program at the same time as state change.

  Arguments:    state   state to set
                arg     argument to callback

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_CommChangeChildState( u16 state, void* arg )
{
#pragma unused( arg )
    OSIntrMode enabled;
    
    static const char* const CSTATE_NAME_STRING[] =
    {
        "MB_COMM_CSTATE_NONE", 
        "MB_COMM_CSTATE_INIT_COMPLETE",
        "MB_COMM_CSTATE_CONNECT", 
        "MB_COMM_CSTATE_CONNECT_FAILED", 
        "MB_COMM_CSTATE_DISCONNECTED_BY_PARENT", 
        "MB_COMM_CSTATE_REQ_ENABLE", 
        "MB_COMM_CSTATE_REQ_REFUSED", 
        "MB_COMM_CSTATE_DLINFO_ACCEPTED", 
        "MB_COMM_CSTATE_RECV_PROCEED", 
        "MB_COMM_CSTATE_RECV_COMPLETE", 
        "MB_COMM_CSTATE_BOOTREQ_ACCEPTED", 
        "MB_COMM_CSTATE_BOOT_READY", 
        "MB_COMM_CSTATE_CANCELLED",
        "MB_COMM_CSTATE_AUTHENTICATION_FAILED",
        "MB_COMM_CSTATE_MEMBER_FULL",
        "MB_COMM_CSTATE_GAMEINFO_VALIDATED",
        "MB_COMM_CSTATE_GAMEINFO_INVALIDATED",
        "MB_COMM_CSTATE_GAMEINFO_LOST",
        "MB_COMM_CSTATE_GAMEINFO_LIST_FULL",
        "MB_COMM_CSTATE_ERROR",
        "MB_COMM_CSTATE_FAKE_END",
    };
    
    MB_FAKE_OUTPUT("state %s -> %s\n", CSTATE_NAME_STRING[ mbf_work->c_comm_state ], 
                                   CSTATE_NAME_STRING[ state ] );
    
    enabled = OS_DisableInterrupts();
    mbf_work->c_comm_state = state;
    (void)OS_RestoreInterrupts( enabled );
    MBFi_SendCallback( state, arg );
}


/*---------------------------------------------------------------------------*
  Name:         MBFi_SendCallback

  Description:  Sends callback.

  Arguments:    state   fake child state
                arg     callback argument

  Returns:      None.
 *---------------------------------------------------------------------------*/
static inline void MBFi_SendCallback( u16 state, void *arg )
{
    if ( mbf_work->stateCallback == NULL )
    {
        return;
    }
    mbf_work->stateCallback( state, arg );
}

/*---------------------------------------------------------------------------*
  Name:         MBFi_ErrorCallback

  Description:  Sends notification of error occurrence with a callback.

  Arguments:    apiid   Sends notification of the API that caused the error with a callback.
                errcode  Sends notification, by callback, of the code that caused the error.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static inline void MBFi_ErrorCallback( u16 apiid, u16 errcode, BOOL isApiError )
{
    MBErrorStatus cb;
    u16 error_type;
    
    if ( mbf_work->stateCallback == NULL )
    {
        return;
    }
    
    if ( isApiError )
    {
        // error when calling API function
        switch ( errcode )
        {
        case WM_ERRCODE_INVALID_PARAM:
        case WM_ERRCODE_FAILED:
        case WM_ERRCODE_WM_DISABLE:
        case WM_ERRCODE_NO_DATASET:
        case WM_ERRCODE_FIFO_ERROR:
        case WM_ERRCODE_TIMEOUT:
            error_type = MB_ERRCODE_FATAL;
            break;
        case WM_ERRCODE_OPERATING:
        case WM_ERRCODE_ILLEGAL_STATE:
        case WM_ERRCODE_NO_CHILD:
        case WM_ERRCODE_OVER_MAX_ENTRY:
        case WM_ERRCODE_NO_ENTRY:
        case WM_ERRCODE_INVALID_POLLBITMAP:
        case WM_ERRCODE_NO_DATA:
        case WM_ERRCODE_SEND_QUEUE_FULL:
        case WM_ERRCODE_SEND_FAILED:
        default:
            error_type = MB_ERRCODE_WM_FAILURE;
            break;
        }
    }
    else
    {
        // error during callback
        switch ( apiid )
        {
        case WM_APIID_INITIALIZE:
        case WM_APIID_SET_LIFETIME:
        case WM_APIID_SET_P_PARAM:
        case WM_APIID_SET_BEACON_IND:
        case WM_APIID_START_PARENT:
        case WM_APIID_START_MP:
        case WM_APIID_SET_MP_DATA:
        case WM_APIID_START_DCF:
        case WM_APIID_SET_DCF_DATA:
        case WM_APIID_DISCONNECT:
        case WM_APIID_START_KS:
            /* the above errors are necessary to WM initialization */
            error_type = MB_ERRCODE_FATAL;
            break;
        case WM_APIID_RESET:
        case WM_APIID_END:
        default:
            /* Other errors are returned as callback errors. */
            error_type = MB_ERRCODE_WM_FAILURE;
            break;
        }
    }
    
    cb.errcode = error_type;
    mbf_work->stateCallback( MB_COMM_CSTATE_ERROR, &cb );
}


//============================================================================
// Scan Functions
//============================================================================

/*---------------------------------------------------------------------------*
  Name:         MB_FakeStartScanParent

  Description:  Starts multiboot parent scan.

  Arguments:    callback    function that notifies of parent scan callback
                ggid        MB parent GGID to search for. Not discovered if GGID is different.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void MB_FakeStartScanParent( MBFakeScanCallbackFunc callback, u32 ggid )
{
    MB_FAKE_OUTPUT("%s\n", __func__ );
    
    mbf_work->scanCallback = callback;
    mbf_work->ggid         = ggid;
    mbf_work->scanParam.channel      = 0;
    mbf_work->scanParam.scanBuf      = &mbf_work->bssDescBuf;
    mbf_work->scanning     = TRUE;
    mbf_work->locking      = FALSE;
    mbf_work->beaconRecvStatus.nowScanTargetFlag = FALSE;
    MBFi_StateInStartScan();
}

/*---------------------------------------------------------------------------*
  Name:         MBFi_StateInStartScan

  Description:  Issues scan command.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_StateInStartScan( void )
{
    WMErrCode result;
    
    // issue WM_StartScan()
    if ( mbf_work->locking  ) 
    {
        mbf_work->scanParam.maxChannelTime = MB_SCAN_TIME_LOCKING;
    }
    else
    {
        static const u8 ANY_PARENT[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
        WM_CopyBssid( ANY_PARENT, mbf_work->scanParam.bssid );
        
        mbf_work->scanParam.maxChannelTime = MB_SCAN_TIME_NORMAL;
        // if not locking, sets channel
        if ( ! RotateChannel() )
        {
            // state where wireless cannot be used
            MBFi_ScanErrorCallback( WM_APIID_MEASURE_CHANNEL, 0 );
            return;
        }
    }
    
    if ( mbf_work->scanParam.channel == 0 )
    {
        mbf_work->scanParam.channel = 1;
    }
    result = WM_StartScan( MBFi_StateOutStartScanParent, &mbf_work->scanParam );
    if ( result != WM_ERRCODE_OPERATING )
    {
        MBFi_ScanErrorCallback( WM_APIID_START_SCAN, result );
    }
}

/*---------------------------------------------------------------------------*
  Name:         MBFi_StateOutStartScanParent

  Description:  WM_StartScan callback function

  Arguments:    arg     WM_StartScan callback argument

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_StateOutStartScanParent( void* arg )
{
    WMStartScanCallback *cb = (WMStartScanCallback*)arg;
    
    if ( WM_ERRCODE_SUCCESS != cb->errcode )
    {
        MBFi_ScanErrorCallback( WM_APIID_START_SCAN, cb->errcode );
        return;
    }
    
    switch ( cb->state )
    {
    case WM_STATECODE_SCAN_START:
        break;
    
    case WM_STATECODE_PARENT_FOUND:
        // stores parent information obtained with Scan
        {
            // analyzes if GGID is same
            if ( cb->gameInfo.ggid == mbf_work->ggid )
            {
                DC_InvalidateRange( &mbf_work->bssDescBuf, WM_BSS_DESC_SIZE );
                (void)MB_RecvGameInfoBeacon( MBFi_CommBeaconRecvCallback, cb->linkLevel, &mbf_work->bssDescBuf );
            }
        }
        // don't break;
        
    case WM_STATECODE_PARENT_NOT_FOUND:
        /* lifetime count of parent game information */
        MB_CountGameInfoLifetime( MBFi_CommBeaconRecvCallback, FALSE );
        
        if ( mbf_work->scanning )
        {
            MBFi_StateInStartScan();
        }
        else
        {
            MBFi_StateInEndScan();
        }
        break;

    default:
        MBFi_ScanErrorCallback( WM_APIID_START_SCAN, WM_ERRCODE_FAILED );
        break;
    }
}


/*---------------------------------------------------------------------------*
  Name:         MBFi_ScanCallback

  Description:  Sends, by callback, notification of parent scan information for user program.

  Arguments:    state   state to notify
                gInfop  pointer to discovered parent information
                index   control number of internally controlled parent

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_ScanCallback( u16 state, MBGameInfoRecvList *gInfop, int index )
{
    MBFakeScanCallback cb;
    
    if ( mbf_work->scanCallback == NULL )
    {
        return;
    }
    
    cb.index    = (u16)index;
    if ( gInfop != NULL )
    {
        cb.gameInfo = &gInfop->gameInfo;
        cb.bssDesc  = &gInfop->bssDesc;
    }
    else
    {
        cb.gameInfo = NULL;
        cb.bssDesc  = NULL;
    }
    mbf_work->scanCallback( state, &cb );
}

/*---------------------------------------------------------------------------*
  Name:         MBFi_ScanErrorCallback

  Description:  Notifies of error occurrence during scan with a callback.

  Arguments:    apiid    APIID that caused the error
                errcode  code that caused the error

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_ScanErrorCallback( u16 apiid, u16 errcode )
{
    MBFakeScanErrorCallback cb;
    
    if ( mbf_work->scanCallback == NULL )
    {
        return;
    }
    
    cb.apiid  = apiid;
    cb.errcode = errcode;
    mbf_work->scanCallback( MB_FAKESCAN_API_ERROR, &cb );
}

/*---------------------------------------------------------------------------*
  Name:         MBFi_ScanLock

  Description:  locks parent to scan

  Arguments:    macAddr  : MAC address of parent to lock

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_ScanLock( u8* macAddr )
{
    MB_FAKE_OUTPUT("Scan Locked\n");
    mbf_work->locking = TRUE;
    WM_CopyBssid( macAddr, mbf_work->scanParam.bssid );
}

/*---------------------------------------------------------------------------*
  Name:         MBFi_ScanUnlock

  Description:  unlocks parent to scan

  Arguments:    None.
  
  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_ScanUnlock( void )
{
    MB_FAKE_OUTPUT("Scan Unlocked\n");
    mbf_work->locking = FALSE;
}

/*---------------------------------------------------------------------------*
  Name:         MBi_CommBeaconRecvCallback

  Description:  child beacon receive callback

  Arguments:    msg    : beacon receive message
                gInfop : parent game information
                index  : beacon index

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_CommBeaconRecvCallback(MbBeaconMsg msg, MBGameInfoRecvList *gInfop, int index)
{
    switch( msg ) 
    {
    case MB_BC_MSG_GINFO_VALIDATED:
        MBFi_ScanCallback( MB_FAKESCAN_PARENT_FOUND, (void*)gInfop, index );
        MB_FAKE_OUTPUT("Parent Info validated\n");
        break;
    case MB_BC_MSG_GINFO_INVALIDATED:
        MB_FAKE_OUTPUT("Parent Info invalidate\n");
        break;
    case MB_BC_MSG_GINFO_LOST:
        MBFi_ScanCallback( MB_FAKESCAN_PARENT_LOST, (void*)gInfop, index );
        MB_FAKE_OUTPUT("Parent Info Lost\n");
        break;
    case MB_BC_MSG_GINFO_LIST_FULL:
        MB_FAKE_OUTPUT("Parent List Full\n");
        break;
    }
}

/*---------------------------------------------------------------------------*
  Name:         RotateChannel

  Description:  channel rotation

  Arguments:    None.

  Returns:      Returns FALSE if no available channel found.
 *---------------------------------------------------------------------------*/
static BOOL RotateChannel( void )
{
    u16 allowedChannel = WM_GetAllowedChannel();
    if ( allowedChannel == 0x8000 || allowedChannel == 0 )
    {
        return FALSE;
    }
    
    // rotate channel
    mbf_work->scanParam.channel++;
    while ( TRUE )
    {
        mbf_work->scanParam.channel++;
        if ( mbf_work->scanParam.channel > 16 )
        {
            mbf_work->scanParam.channel = 1;
        }
        if ( allowedChannel & ( 0x0001 << (mbf_work->scanParam.channel -1) ) )
        {
            break;
        }
    }
    return TRUE;
}


/*---------------------------------------------------------------------------*
  Name:         MB_FakeEndScan

  Description:  end parent scan

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void MB_FakeEndScan( void )
{
    mbf_work->scanning    = FALSE;
    mbf_work->endScanBusy = TRUE;
}


/*---------------------------------------------------------------------------*
  Name:         MBFi_StateInEndScan

  Description:  issue parent scan end command

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_StateInEndScan( void )
{
    WMErrCode result;
    
    result = WM_EndScan( MBFi_StateOutEndScan );
    if ( result != WM_ERRCODE_OPERATING )
    {
        MBFi_ScanErrorCallback( WM_APIID_END_SCAN, result );
    }
}

/*---------------------------------------------------------------------------*
  Name:         MBFi_StateOutEndScan

  Description:  parent scan end callack

  Arguments:    arg     WM_EndScan callback argument

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_StateOutEndScan( void* arg )
{
    WMCallback *cb = (WMCallback*)arg;
    
    if ( cb->errcode != WM_ERRCODE_SUCCESS )
    {
        MBFi_ScanErrorCallback( WM_APIID_END_SCAN, cb->errcode );
        return;
    }
    mbf_work->endScanBusy = FALSE;
    
    if ( mbf_work->endFlag )
    {
        MBFi_EndComplete();
        return;
    }
    
    MBFi_ScanCallback( MB_FAKESCAN_END_SCAN, NULL, 0 );
    
}



//============================================================================
// Processing of entry to parent
//============================================================================

/*---------------------------------------------------------------------------*
  Name:         MB_FakeEntryToParent

  Description:  starts entry to parent

  Arguments:    index   Provides, as argument, internal control number of parent to enter.
                        Please provide index to get with MB_FakeStartScanParent callback. 

  Returns:      TRUE if argument index is valid.
                FALSE if it is not valid.
 *---------------------------------------------------------------------------*/
BOOL MB_FakeEntryToParent( u16 index )
{
    MBGameInfoRecvList *info;
    
    info = MB_GetGameInfoRecvList( index );
    
    if ( info == NULL )
    {
        return FALSE;
    }
    mbf_work->targetGgid   = info->gameInfo.ggid;
    mbf_work->targetFileNo = info->gameInfo.fileNo;
    
    MBi_WMSetBuffer( &mbf_work->wmWork );
    MBi_WMSetCallback( MBFi_WMCallback );
    MBFi_CommChangeChildState( MB_COMM_CSTATE_INIT_COMPLETE, NULL );
    MI_CpuCopy8( &info->bssDesc, &mbf_work->bssDescBuf, sizeof( WMBssDesc ) );
    MBi_WMStartConnect( &mbf_work->bssDescBuf );
    
    return TRUE;
}


/*---------------------------------------------------------------------------*
  Name:         MBFi_WMCallback

  Description:  MP communication callback function

  Arguments:    type    callback type
                arg     callback argument

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_WMCallback( u16 type, void *arg )
{
#pragma unused( arg )
    
    switch ( type )
    {
    //--------------------------------------------
    // notification of failure to connect to parent
    case MB_CALLBACK_CONNECT_FAILED:
        MBFi_CommChangeChildState( MB_COMM_CSTATE_CONNECT_FAILED, NULL );
        break;
    //--------------------------------------------
    // notification of completion of connection to parent
    case MB_CALLBACK_CONNECTED_TO_PARENT:
        MB_FAKE_OUTPUT("connect to parent\n");
        MBFi_CommChangeChildState( MB_COMM_CSTATE_CONNECT, arg );
        MBi_ChildStartMP( (u16*)mbf_work->sendBuf, (u16*)mbf_work->recvBuf );
        break;
    //--------------------------------------------
    // notification of disconnect from parent
    case MB_CALLBACK_DISCONNECTED_FROM_PARENT:
        MBFi_CommChangeChildState( MB_COMM_CSTATE_DISCONNECTED_BY_PARENT, arg );
        break;
    //--------------------------------------------
    // notification of start of MP communication
    case MB_CALLBACK_MP_STARTED:
        break;
    //-----------------------------
    // notification of sendability of data
    case MB_CALLBACK_MP_SEND_ENABLE:
        {
            // send data set processing
            MBFi_CommChildSendData();
        }
        break;
    //-----------------------------
    // notification of receivability of data
    case MB_CALLBACK_MP_CHILD_RECV:
        {
            MBFi_CommChildRecvData( (WMPortRecvCallback*)arg );
        }
        break;
    //-----------------------------
    // notification of completion of disconnect from child
    case MB_CALLBACK_DISCONNECT_COMPLETE:
        if ( mbf_work->c_comm_state == MB_COMM_CSTATE_BOOTREQ_ACCEPTED )
        {
            // transitions to boot complete state if after reception of boot request
            MBFi_CommChangeChildState( MB_COMM_CSTATE_BOOT_READY, NULL );
        }
        else
        {
            // transitions to cancel state if before reception of boot request
            MBFi_CommChangeChildState( MB_COMM_CSTATE_CANCELLED, NULL );
        }
        if ( mbf_work->endFlag )
        {
            MBFi_EndComplete();
        }
        break;
    //------------------------------
    // MP communication notification
    case MB_CALLBACK_MP_CHILD_SENT:
    case MB_CALLBACK_MP_CHILD_SENT_TIMEOUT:
    case MB_CALLBACK_MP_CHILD_SENT_ERR:
        // merely MP level communication state notification, so no particular processing is necessary
        break;
    //------------------------------
    // error handling
    case MB_CALLBACK_API_ERROR:
        {
            MBErrorCallback *cb = (MBErrorCallback*)arg;
            
            MBFi_ErrorCallback( cb->apiid, cb->errcode, TRUE );
        }
        break;
    case MB_CALLBACK_ERROR:
        {
            MBErrorCallback *cb = (MBErrorCallback*)arg;
            
            MBFi_ErrorCallback( cb->apiid, cb->errcode, FALSE );
        }
        break;
    default:
        break;
    }
}


/*---------------------------------------------------------------------------*
  Name:         MBFi_CommChildRecvData

  Description:  data receive processing from parent

  Arguments:    cb  callback argument of port callback

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_CommChildRecvData( WMPortRecvCallback *cb )
{
    MBCommParentBlockHeader hd;
    
    // header analysis
    (void)MBi_SetRecvBufferFromParent( &hd, (u8*)cb->data );
    
//    MBFi_PrintMBCommCallbacyType( hd.type );
    
    switch ( hd.type )    // state transition depending on receive Block type
    {
    case MB_COMM_TYPE_PARENT_SENDSTART:
        // send start message from parent
        if ( mbf_work->c_comm_state == MB_COMM_CSTATE_CONNECT) 
        {
            MB_FAKE_OUTPUT("Allowd to request file from parent!\n");
            MBFi_CommChangeChildState( MB_COMM_CSTATE_REQ_ENABLE, NULL );
        }
        break;
        
    case MB_COMM_TYPE_PARENT_KICKREQ:   // Kick message from parent
        if ( mbf_work->c_comm_state == MB_COMM_CSTATE_REQ_ENABLE ) 
        {
            MBFi_CommChangeChildState( MB_COMM_CSTATE_REQ_REFUSED, NULL );
        }
        break;
        
    case MB_COMM_TYPE_PARENT_MEMBER_FULL:   // member exceeded message from parent
        if ( mbf_work->c_comm_state == MB_COMM_CSTATE_REQ_ENABLE ) 
        {
            MBFi_CommChangeChildState( MB_COMM_CSTATE_MEMBER_FULL, NULL );
        }
        break;

    case MB_COMM_TYPE_PARENT_DL_FILEINFO:
        // MbDownloadFileInfoHeader reception
        if ( mbf_work->c_comm_state == MB_COMM_CSTATE_REQ_ENABLE ) 
        {
            /* Pass received MbDownloadFileInfo as argument. */
            MBFi_CommChangeChildState( MB_COMM_CSTATE_DLINFO_ACCEPTED, NULL );
        }
        
        break;

    case MB_COMM_TYPE_PARENT_DATA:
        /* block data reception */
        if ( mbf_work->c_comm_state == MB_COMM_CSTATE_DLINFO_ACCEPTED )
        {
            MBFi_CommChangeChildState( MB_COMM_CSTATE_RECV_PROCEED, NULL );
        }

        if ( mbf_work->c_comm_state == MB_COMM_CSTATE_RECV_PROCEED ) 
        {
            MBFi_CommChangeChildState( MB_COMM_CSTATE_RECV_COMPLETE, NULL ); // receive complete
        }
        break;
        
    case MB_COMM_TYPE_PARENT_BOOTREQ:
        if ( mbf_work->c_comm_state == MB_COMM_CSTATE_RECV_COMPLETE )
        {
            MBFi_CommChangeChildState( MB_COMM_CSTATE_BOOTREQ_ACCEPTED, NULL );
        } 
        else if ( mbf_work->c_comm_state == MB_COMM_CSTATE_BOOTREQ_ACCEPTED )
        {
            MBi_WMDisconnect();    // end notification
        }
        break;
    default:
        break;
    }
        
    return;
}



/*---------------------------------------------------------------------------*
  Name:         MBi_CommChildSendData

  Description:  send child data

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/

static void MBFi_CommChildSendData( void )
{
    MBCommChildBlockHeader hd;
    WMErrCode errcode = WM_ERRCODE_SUCCESS;
    u16 pollbmp = 0xffff;
    
    switch ( mbf_work->c_comm_state )
    {
    case MB_COMM_CSTATE_REQ_ENABLE:
        {
            MBCommRequestData req_data;
            u8* databuf;
            
            req_data.ggid    = mbf_work->targetGgid;
            req_data.version = MB_IPL_VERSION;
            req_data.fileid  = (u8)mbf_work->targetFileNo;
            MI_CpuCopy8( &mbf_work->userInfo, &req_data.userinfo, sizeof(MBUserInfo) );
            
            hd.type = MB_COMM_TYPE_CHILD_FILEREQ;
            hd.req_data.piece = MBi_SendRequestDataPiece( hd.req_data.data, &req_data );
            databuf = MBi_MakeChildSendBuffer( &hd, (u8*)mbf_work->sendDataBuf );
            if ( databuf == NULL )
            {
                return;
            }
            errcode = MBi_MPSendToParent( MB_COMM_CHILD_HEADER_SIZE, pollbmp, mbf_work->sendDataBuf );
        }
        break;
        
    case MB_COMM_CSTATE_DLINFO_ACCEPTED:
        // DownloadInfo reception message
        hd.type = MB_COMM_TYPE_CHILD_ACCEPT_FILEINFO;
        (void)MBi_MakeChildSendBuffer( &hd, (u8*)mbf_work->sendDataBuf );
        errcode = MBi_MPSendToParent( MB_COMM_CHILD_HEADER_SIZE, pollbmp, mbf_work->sendDataBuf );
        break;
        
    case MB_COMM_CSTATE_RECV_PROCEED:
        break;
        
    case MB_COMM_CSTATE_RECV_COMPLETE:
        // block transfer stop message (continues to send until BOOTREQ comes from parent)
        hd.type = MB_COMM_TYPE_CHILD_STOPREQ;
        (void)MBi_MakeChildSendBuffer( &hd, (u8*)mbf_work->sendDataBuf );
        errcode = MBi_MPSendToParent( MB_COMM_CHILD_HEADER_SIZE, pollbmp, mbf_work->sendDataBuf );
        break;
        
    case MB_COMM_CSTATE_BOOTREQ_ACCEPTED:
        hd.type = MB_COMM_TYPE_CHILD_BOOTREQ_ACCEPTED;
        (void)MBi_MakeChildSendBuffer( &hd, (u8*)mbf_work->sendDataBuf );
        errcode = MBi_MPSendToParent( MB_COMM_CHILD_HEADER_SIZE, pollbmp, mbf_work->sendDataBuf );
        break;
    default:
        // send DUMMY MP to establish MP communication
        hd.type = MB_COMM_TYPE_DUMMY;
        (void)MBi_MakeChildSendBuffer( &hd, (u8*)mbf_work->sendDataBuf );
        errcode = MBi_MPSendToParent( MB_COMM_CHILD_HEADER_SIZE, pollbmp, mbf_work->sendDataBuf );
        break;
        
    }
}


/*---------------------------------------------------------------------------*
  Name:         MB_FakeGetParentGameInfo

  Description:  Gets parent game information from parent list.

  Arguments:    index     parent index
                pGameInfo  pointer to region that gets parent information

  Returns:      TRUE if valid parent index
                FALSE if invalid parent index
 *---------------------------------------------------------------------------*/
BOOL MB_FakeGetParentGameInfo( u16 index, MBGameInfo* pGameInfo )
{
    MBGameInfoRecvList *parentInfo;
    OSIntrMode enabled;
    
    if ( index >= MB_GAME_INFO_RECV_LIST_NUM )
    {
        return FALSE;
    }
    
    enabled = OS_DisableInterrupts();
    
    parentInfo = MB_GetGameInfoRecvList( index );
    if ( parentInfo == NULL )
    {
        (void)OS_RestoreInterrupts( enabled );
        return FALSE;
    }
    MI_CpuCopy8( &parentInfo->gameInfo, pGameInfo, sizeof( MBGameInfo ) );
    (void) OS_RestoreInterrupts( enabled );
    return TRUE;
}


/*---------------------------------------------------------------------------*
  Name:         MB_FakeGetParentBssDesc

  Description:  Gets parent BssDesc from parent list.

  Arguments:    index    parent index
                pBssDesc     pointer to region that gets parent information

  Returns:      TRUE if valid parent index
                FALSE if invalid parent index
 *---------------------------------------------------------------------------*/
BOOL MB_FakeGetParentBssDesc( u16 index, WMBssDesc* pBssDesc )
{
    MBGameInfoRecvList *parentInfo;
    OSIntrMode enabled;
    
    if ( index >= MB_GAME_INFO_RECV_LIST_NUM )
    {
        return FALSE;
    }
    
    enabled = OS_DisableInterrupts();
    
    parentInfo = MB_GetGameInfoRecvList( index );
    if ( parentInfo == NULL )
    {
        (void)OS_RestoreInterrupts( enabled );
        return FALSE;
    }
    MI_CpuCopy8( &parentInfo->bssDesc, pBssDesc, sizeof( WMBssDesc ) );
    (void) OS_RestoreInterrupts( enabled );
    return TRUE;
}


/*---------------------------------------------------------------------------*
  Name:         MB_FakeReadParentBssDesc

  Description:  Gets information for WM_StartConnect function based on parent BssDesc of parent list.

  Arguments:    index           parent index
                pBssDesc        pointer to region that gets parent information
                parent_max_size    maximum send size of MP parent
                child_max_size      maximum send size of MP child
                ks_flag           key sharing setting flag
                cs_flag         continuous transfer setting flag

  Returns:      TRUE if valid parent index
                FALSE if invalid parent index
 *---------------------------------------------------------------------------*/
BOOL MB_FakeReadParentBssDesc( u16 index, WMBssDesc* pBssDesc, u16 parent_max_size, 
                               u16 child_max_size, BOOL ks_flag, BOOL cs_flag )
{
    BOOL result;
    
    SDK_NULL_ASSERT( pBssDesc );
    
    result = MB_FakeGetParentBssDesc( index, pBssDesc );
    
    if ( ! result )
    {
        return FALSE;
    }
    
    pBssDesc->gameInfoLength              = 0x10;
    pBssDesc->gameInfo.userGameInfoLength = 0;
    pBssDesc->gameInfo.parentMaxSize      = parent_max_size;
    pBssDesc->gameInfo.childMaxSize       = child_max_size;
    pBssDesc->gameInfo.gameNameCount_attribute = (u8)( (ks_flag ? WM_ATTR_FLAG_KS : 0) |
                                                       (cs_flag ? WM_ATTR_FLAG_CS : 0) |
                                                       WM_ATTR_FLAG_ENTRY );
    return TRUE;
}



#if (MB_FAKE_PRINT == 1)

/*---------------------------------------------------------------------------*
  Name:         MBFi_PrintMBCallbackType

  Description:  Debug outputs callback type from MB_WM.

  Arguments:    type  callback type

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_PrintMBCallbackType( u16 type )
{
#pragma unused( type )
    static const char* const CALLBACK_NAME[] =
    {
        "MB_CALLBACK_CHILD_CONNECTED",
        "MB_CALLBACK_CHILD_DISCONNECTED",
        "MB_CALLBACK_MP_PARENT_SENT",
        "MB_CALLBACK_MP_PARENT_RECV",
        "MB_CALLBACK_PARENT_FOUND",
        "MB_CALLBACK_PARENT_NOT_FOUND",
        "MB_CALLBACK_CONNECTED_TO_PARENT",
        "MB_CALLBACK_DISCONNECTED",
        "MB_CALLBACK_MP_CHILD_SENT",
        "MB_CALLBACK_MP_CHILD_RECV",
        "MB_CALLBACK_DISCONNECTED_FROM_PARENT",
        "MB_CALLBACK_CONNECT_FAILED",
        "MB_CALLBACK_DCF_CHILD_SENT",
        "MB_CALLBACK_DCF_CHILD_SENT_ERR",
        "MB_CALLBACK_DCF_CHILD_RECV",
        "MB_CALLBACK_DISCONNECT_COMPLETE",
        "MB_CALLBACK_DISCONNECT_FAILED",
        "MB_CALLBACK_END_COMPLETE",
        "MB_CALLBACK_MP_CHILD_SENT_ERR",
        "MB_CALLBACK_MP_PARENT_SENT_ERR",
        "MB_CALLBACK_MP_STARTED",
        "MB_CALLBACK_INIT_COMPLETE",
        "MB_CALLBACK_END_MP_COMPLETE",
        "MB_CALLBACK_SET_GAMEINFO_COMPLETE",
        "MB_CALLBACK_SET_GAMEINFO_FAILED",
        "MB_CALLBACK_MP_SEND_ENABLE",
        "MB_CALLBACK_PARENT_STARTED",
        "MB_CALLBACK_BEACON_LOST",
        "MB_CALLBACK_BEACON_SENT",
        "MB_CALLBACK_BEACON_RECV",
        "MB_CALLBACK_MP_SEND_DISABLE",
        "MB_CALLBACK_DISASSOCIATE",
        "MB_CALLBACK_REASSOCIATE",
        "MB_CALLBACK_AUTHENTICATE",
        "MB_CALLBACK_SET_LIFETIME",
        "MB_CALLBACK_DCF_STARTED",
        "MB_CALLBACK_DCF_SENT",
        "MB_CALLBACK_DCF_SENT_ERR",
        "MB_CALLBACK_DCF_RECV",
        "MB_CALLBACK_DCF_END",
        "MB_CALLBACK_MPACK_IND",
        "MB_CALLBACK_MP_CHILD_SENT_TIMEOUT",
        "MB_CALLBACK_SEND_QUEUE_FULL_ERR",
        "MB_CALLBACK_API_ERROR",
        "MB_CALLBACK_ERROR",
    };
    
    MB_FAKE_OUTPUT("RecvCallback %s\n", CALLBACK_NAME[ type ]);
}

/*---------------------------------------------------------------------------*
  Name:         MBFi_PrintMBCommCallbacyType

  Description:  Debug outputs packet message type from parent.

  Arguments:    type          message type

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MBFi_PrintMBCommCallbacyType( u16 type )
{
#pragma unused( type )
    static const char* const MB_TYPE_STRING[] =
    {
        "MB_COMM_TYPE_DUMMY",                   //      0
        "MB_COMM_TYPE_PARENT_SENDSTART",        //      1
        "MB_COMM_TYPE_PARENT_KICKREQ",          //      2
        "MB_COMM_TYPE_PARENT_DL_FILEINFO",      //      3
        "MB_COMM_TYPE_PARENT_DATA",             //      4
        "MB_COMM_TYPE_PARENT_BOOTREQ",          //      5
        "MB_COMM_TYPE_PARENT_MEMBER_FULL",      //      6
        "MB_COMM_TYPE_CHILD_FILEREQ",           //      7
        "MB_COMM_TYPE_CHILD_ACCEPT_FILEINFO",   //      8
        "MB_COMM_TYPE_CHILD_CONTINUE",          //      9
        "MB_COMM_TYPE_CHILD_STOPREQ",           //      10
        "MB_COMM_TYPE_CHILD_BOOTREQ_ACCEPTED",  //      11
    };
    
    MB_FAKE_OUTPUT("RECV %s\n", MB_TYPE_STRING[ type ]);
}

#endif

