/*---------------------------------------------------------------------------* Project: TwlSDK - MB - libraries File: mb_fake_child.c Copyright 2007-2008 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. $Date:: 2008-09-17#$ $Rev: 8556 $ $Author: okubata_ryoma $ *---------------------------------------------------------------------------*/ #include #include "mb_common.h" #include "mb_block.h" #include "mb_child.h" #include "mb_private.h" #include "mb_wm_base.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[WM_SIZE_CHILD_SEND_BUFFER(MB_COMM_PARENT_RECV_MAX, FALSE) / sizeof(u32)] ATTRIBUTE_ALIGN(32); // Send buffer 0x400 byte u32 recvBuf[WM_SIZE_CHILD_RECEIVE_BUFFER(MB_COMM_PARENT_SEND_MAX, FALSE) / sizeof(u32)] ATTRIBUTE_ALIGN(32); // Receive buffer 0xF00 byte u32 sendDataBuf[WM_SIZE_CHILD_SEND_BUFFER(MB_COMM_PARENT_RECV_MAX, FALSE) / sizeof(u32)] ATTRIBUTE_ALIGN(32); // Buffer for creating send data 0x400 byte WMBssDesc bssDescBuf ATTRIBUTE_ALIGN(32); // Buffer for scanning parents. Needs to be 32-byte aligned 0xC0 byte WMScanParam scanParam ATTRIBUTE_ALIGN(32); // Scan parameter. Needs to be 32-byte aligned 0x20 byte MBWMWork wmWork ATTRIBUTE_ALIGN(32); // Work buffer for MP communication. Needs to be 32-byte aligned MBUserInfo userInfo; // User information u8 _padding1[2]; MBFakeScanCallbackFunc scanCallback; // Callback for parent scan notification MBCommCStateCallbackFunc stateCallback; // Callback for status notification u32 ggid; // GGID to search MbBeaconRecvStatus beaconRecvStatus; // Beacon receive status BOOL scanning; // Scanning flag BOOL endScanBusy; // Flag for processing scan termination BOOL locking; // Locking flag BOOL endFlag; // MB termination process request u32 targetGgid; // GGID of binary to request u16 targetFileNo; // FileNo of binary to request u16 c_comm_state; // Child status WMCallbackFunc verboseScanCallback; // Callback that alerts the user of the the raw WMScan notification MBFakeCompareGGIDCallbackFunc compareGGIDCallback; // User-defined GGID comparison callback u8 _padding2[8]; } MBFakeWork; SDK_COMPILER_ASSERT(sizeof(MBFakeWork) <= MB_FAKE_WORK_SIZE); //============================================================================ // 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 scanning */ 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 processes */ 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: Initialization function of the fake multiboot child Arguments: buf Pointer to the work area necessary for fake child. The necessary size can be obtained with MB_FakeGetWorkSize(). user Pointer to the user information of a child Returns: None. *---------------------------------------------------------------------------*/ void MB_FakeInit(void *buf, const MBUserInfo *user) { SDK_NULL_ASSERT(buf); SDK_ASSERT(((u32)buf & 0x1F) == 0); // Checks for the 32-byte alignment 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; mbf_work->verboseScanCallback = NULL; mbf_work->compareGGIDCallback = NULL; MI_CpuCopy8(user, &mbf_work->userInfo, sizeof(MBUserInfo)); // Sets the work buffer for receiving beacon MBi_SetBeaconRecvStatusBuffer(&mbf_work->beaconRecvStatus); // Initializes the game information receive status MB_InitRecvGameInfoStatus(); // Sets the function for scan lock MBi_SetScanLockFunc(MBFi_ScanLock, MBFi_ScanUnlock); } /*---------------------------------------------------------------------------* Name: MB_FakeEnd Description: Termination function for the fake multiboot child. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void MB_FakeEnd(void) { if (mbf_work->endFlag) { // Does not do anything if the termination process has been started return; } mbf_work->endFlag = TRUE; switch (mbf_work->c_comm_state) { case MB_COMM_CSTATE_NONE: // End the scan if scan is being performed if (mbf_work->scanning) { mbf_work->scanning = FALSE; } // End without doing anything if before starting scanning or after completing EndScan else if (!mbf_work->endScanBusy) { MBFi_EndComplete(); } else { // Wait for completion if scan termination process is being performed } 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 to 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: // Safely end from MP in order MBi_WMDisconnect(); break; case MB_COMM_CSTATE_BOOTREQ_ACCEPTED: // Wait for completion of disconnection break; case MB_COMM_CSTATE_BOOT_READY: case MB_COMM_CSTATE_CANCELLED: // End because it is already in IDLE state MBFi_EndComplete(); break; default: OS_TPanic("MB fake child is in Illegal State\n"); } } /*---------------------------------------------------------------------------* Name: MBFi_EndComplete Description: Termination process of the fake child library Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void MBFi_EndComplete(void) { mbf_initialize = 0; mbf_work->endFlag = 0; // Clears port callback MBi_WMClearCallback(); // Notify the user of termination MBFi_CommChangeChildState(MB_COMM_CSTATE_FAKE_END, NULL); } /*---------------------------------------------------------------------------* Name: MB_FakeGetWorkSize Description: Gets the work size necessary for the fake child library. Arguments: None. Returns: Size of the necessary work buffer *---------------------------------------------------------------------------*/ u32 MB_FakeGetWorkSize(void) { return sizeof(MBFakeWork); } /*---------------------------------------------------------------------------* Name: MB_FakeSetCStateCallback Description: Sets the callback function to notify the state change of the fake child. 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 the child state. Returns the callback to the user program at the same time as the status change. Arguments: state Status to set arg Argument for the 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 Status of the fake child. 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: Notifies the error generation with callback. Arguments: apiid Notifies the API that caused the error with callback. errcode Notifies the error cause code with callback. 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 at the time of 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 at the time of 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 important 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; } } MB_FAKE_OUTPUT("MBFi_ErrorCallback apiid = 0x%x, errcode = 0x%x\n", apiid, errcode); cb.errcode = error_type; mbf_work->stateCallback(MB_COMM_CSTATE_ERROR, &cb); } //============================================================================ // Function for scan //============================================================================ void MB_FakeSetVerboseScanCallback(WMCallbackFunc callback) { OSIntrMode enabled = OS_DisableInterrupts(); mbf_work->verboseScanCallback = callback; (void)OS_RestoreInterrupts(enabled); } /*---------------------------------------------------------------------------* Name: MB_FakeSetCompareGGIDCallback Description: Sets the MP parent's GGID comparison callback. Arguments: callback The GGID comparison callback function Returns: None. *---------------------------------------------------------------------------*/ void MB_FakeSetCompareGGIDCallback(MBFakeCompareGGIDCallbackFunc callback) { OSIntrMode enabled = OS_DisableInterrupts(); mbf_work->compareGGIDCallback = callback; (void)OS_RestoreInterrupts(enabled); } /*---------------------------------------------------------------------------* Name: MB_FakeStartScanParent Description: Starts scanning multiboot parent Arguments: callback Function that notifies callback of parent scan ggid GGID of the MB parent to search. If GGID is different, it would not be found. 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_FOR_FAKE_CHILD; // Sets a channel if it is not locked if (!RotateChannel()) { // The state which wireless is not usable 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: Callback function of WM_StartScan Arguments: arg Callback argument of WM_StartScan 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: // Store the parent information gotten with scan { /* * Make a target for beacon parsing if the specified GGID matches or the user-defined comparison is returned as TRUE. * * */ BOOL matched = (mbf_work->compareGGIDCallback == NULL) ? (cb->gameInfo.ggid == mbf_work->ggid) : (*mbf_work->compareGGIDCallback)(cb, mbf_work->ggid); if (mbf_work->verboseScanCallback) { mbf_work->verboseScanCallback(arg); } if (matched) { DC_InvalidateRange(&mbf_work->bssDescBuf, WM_BSS_DESC_SIZE); (void)MB_RecvGameInfoBeacon(MBFi_CommBeaconRecvCallback, cb->linkLevel, &mbf_work->bssDescBuf); MB_CountGameInfoLifetime(MBFi_CommBeaconRecvCallback, TRUE); } } // 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: Uses a callback to notify the user program about the parent Arguments: state Status to notify gInfop Pointer to the parent information that was found index Control number of the parent managed internally 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 the error generation at the time of scanning with callback. Arguments: apiid APIID that caused the error errcode Error cause code 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 the parent to scan Arguments: macAddr : MAC address of the 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: Releases the lock of the 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; case MB_BC_MSG_GINFO_BEACON: /* Acknowledge each time existing parent beacon is detected */ MBFi_ScanCallback(MB_FAKESCAN_PARENT_BEACON, (void *)gInfop, index); break; } } /*---------------------------------------------------------------------------* Name: RotateChannel Description: Rotates channels Arguments: None. Returns: If an available channel was not found, this returns FALSE. *---------------------------------------------------------------------------*/ static BOOL RotateChannel(void) { u16 allowedChannel = WM_GetAllowedChannel(); if (allowedChannel == 0x8000 || allowedChannel == 0) { return FALSE; } // Rotate channels 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 scanning for parent Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void MB_FakeEndScan(void) { mbf_work->scanning = FALSE; mbf_work->endScanBusy = TRUE; } /*---------------------------------------------------------------------------* Name: MBFi_StateInEndScan Description: Issues the parent scan termination 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 scanning termination callback Arguments: arg Callback argument of WM_EndScan 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); } //============================================================================ // Process for entering to parent //============================================================================ /*---------------------------------------------------------------------------* Name: MB_FakeEntryToParent Description: Starts the entry to the parent Arguments: index Gives internal control number of the parent for the entry as an argument. For this argument, you should pass the "index" obtained with the MB_FakeStartScanParent callback. Returns: TRUE if the index of the argument 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: Callback function of MP communication Arguments: type The callback type arg : Callback argument Returns: None. *---------------------------------------------------------------------------*/ static void MBFi_WMCallback(u16 type, void *arg) { #pragma unused( arg ) switch (type) { //-------------------------------------------- // Notification of connection failure to parent case MB_CALLBACK_CONNECT_FAILED: MBFi_CommChangeChildState(MB_COMM_CSTATE_CONNECT_FAILED, NULL); break; //-------------------------------------------- // Notification of connection to parent complete 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 disconnection from parent case MB_CALLBACK_DISCONNECTED_FROM_PARENT: MBFi_CommChangeChildState(MB_COMM_CSTATE_DISCONNECTED_BY_PARENT, arg); break; //-------------------------------------------- // Notify that the MP communication started case MB_CALLBACK_MP_STARTED: break; //----------------------------- // Notify that data send is possible case MB_CALLBACK_MP_SEND_ENABLE: { // Send data setting process MBFi_CommChildSendData(); } break; //----------------------------- // Notify that data receive is possible case MB_CALLBACK_MP_CHILD_RECV: { MBFi_CommChildRecvData((WMPortRecvCallback *)arg); } break; //----------------------------- // Notify that the disconnection from the child is completed. case MB_CALLBACK_DISCONNECT_COMPLETE: if (mbf_work->c_comm_state == MB_COMM_CSTATE_BOOTREQ_ACCEPTED) { // If it is after the boot request is received, it changes into the boot complete status MBFi_CommChangeChildState(MB_COMM_CSTATE_BOOT_READY, NULL); } else { // If it is before the boot request is received, it changes into the cancel status 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: // Process is not necessary because this is only notifying the communication condition of MP level. break; //------------------------------ // Handles error case MB_CALLBACK_API_ERROR: { MBErrorCallback *cb = (MBErrorCallback *) arg; MBFi_ErrorCallback(cb->apiid, cb->errcode, TRUE); if (mbf_work->endFlag) { MBi_WMReset(); } } break; case MB_CALLBACK_ERROR: { MBErrorCallback *cb = (MBErrorCallback *) arg; MBFi_ErrorCallback(cb->apiid, cb->errcode, FALSE); if (mbf_work->endFlag) { MBi_WMReset(); } } break; default: break; } } /*---------------------------------------------------------------------------* Name: MBFi_CommChildRecvData Description: Process for receiving data from a parent Arguments: cb Callback argument of the port callback Returns: None. *---------------------------------------------------------------------------*/ static void MBFi_CommChildRecvData(WMPortRecvCallback *cb) { MBCommParentBlockHeader hd; // Header parsing (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: // Message from parent regarding excessive number of members 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: // Receive MbDownloadFileInfoHeader if (mbf_work->c_comm_state == MB_COMM_CSTATE_REQ_ENABLE) { /* Pass the received MbDownloadFileInfo as an argument. */ MBFi_CommChangeChildState(MB_COMM_CSTATE_DLINFO_ACCEPTED, NULL); } break; case MB_COMM_TYPE_PARENT_DATA: /* Receive block data */ 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 communication } break; default: break; } return; } /*---------------------------------------------------------------------------* Name: MBi_CommChildSendData Description: Sends 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 (send continues 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 the game information of the parent from the parent list Arguments: index Parent index pGameInfo Pointer to the region that gets the parent information Returns: TRUE if the index of a valid parent. FALSE if the index of an invalid parent. *---------------------------------------------------------------------------*/ 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 the BssDesc of the parent from the parent list. Arguments: index Parent index pBssDesc Pointer to the region that obtains the parent information Returns: TRUE if the index of a valid parent. FALSE if the index of an invalid parent. *---------------------------------------------------------------------------*/ 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 the information for WM_StartConnect function based on the parent BssDesc of the parent list. Arguments: index Parent index pBssDesc Pointer to the region that obtains the parent information parent_max_size Maximum send size of the MP parent child_max_size Maximum send size of the MP child ks_flag Flag for setting key sharing cs_flag Flag for setting continuous transfer Returns: TRUE if the index of a valid parent. FALSE if the index of an invalid parent. *---------------------------------------------------------------------------*/ 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.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 the callback type from MB_WM Arguments: type The 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 the message type of the packet from a 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