/*---------------------------------------------------------------------------* Project: TwlSDK - MB - libraries File: mb_wm.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-18#$ $Rev: 8573 $ $Author: okubata_ryoma $ *---------------------------------------------------------------------------*/ #include #include "mb_common.h" #include "mb_wm.h" #include "mb_child.h" #include "mb_wm_base.h" #include "mb_block.h" //=========================================================================== // Prototype Declarations //=========================================================================== static BOOL IsSendEnabled(void); static void MBi_WMStateOutStartConnect(void *arg); static void ChildStateOutStartMP(void *arg); static void ChildPortCallback(void *arg); static void StateOutMPSendToParent(void *arg); static void MBi_WMStateOutStartConnect(void *arg); static void MBi_WMStateOutEndMP(void *arg); static void MBi_WMStateOutDisconnect(void *arg); static void MBi_WMStateInDisconnect(void); static void MBi_WMStateOutReset(void *arg); static void MBi_WMSendCallback(u16 type, void *arg); static void MBi_WMErrorCallback(u16 apiid, u16 error_code); static void MBi_WMApiErrorCallback(u16 apiid, u16 error_code); //=========================================================================== // Variable Declarations //=========================================================================== static MBWMWork *wmWork = NULL; //=========================================================================== // Function Definitions //=========================================================================== /*---------------------------------------------------------------------------* Name: MBi_WMSetBuffer Description: Sets the buffer that MB_WM uses for MP communication. You must provide only the size of MBWMWork. Arguments: buf: Pointer to the region used as a work buffer. Returns: None. *---------------------------------------------------------------------------*/ void MBi_WMSetBuffer(void *buf) { SDK_NULL_ASSERT(buf); SDK_ASSERT(((u32)buf & 0x1f) == 0); // Whether 32-byte aligned or not. wmWork = (MBWMWork *) buf; wmWork->start_mp_busy = 0; // Prevent multiple parent StartMP calls. wmWork->mpStarted = 0; wmWork->child_bitmap = 0; wmWork->mpBusy = 0; wmWork->endReq = 0; wmWork->sendBuf = NULL; wmWork->recvBuf = NULL; wmWork->mpCallback = NULL; } /*---------------------------------------------------------------------------* Name: MBi_WMSetCallback Description: Sets a callback. Arguments: callback: Callback function. Returns: None. *---------------------------------------------------------------------------*/ void MBi_WMSetCallback(MBWMCallbackFunc callback) { OSIntrMode enabled = OS_DisableInterrupts(); wmWork->mpCallback = callback; (void)OS_RestoreInterrupts(enabled); } /*---------------------------------------------------------------------------* Name: MBi_WMStartConnect Description: Begins a connection to a parent device. Arguments: bssDesc: BssDesc of the parent device to connect. Returns: None. *---------------------------------------------------------------------------*/ void MBi_WMStartConnect(WMBssDesc *bssDesc) { WMErrCode result; wmWork->mpStarted = 0; wmWork->endReq = 0; wmWork->sendBufSize = (u16)WM_SIZE_MP_CHILD_SEND_BUFFER(bssDesc->gameInfo.childMaxSize, FALSE); wmWork->recvBufSize = (u16)WM_SIZE_MP_CHILD_RECEIVE_BUFFER(bssDesc->gameInfo.parentMaxSize, FALSE); wmWork->pSendLen = bssDesc->gameInfo.parentMaxSize; wmWork->pRecvLen = bssDesc->gameInfo.childMaxSize; wmWork->blockSizeMax = (u16)MB_COMM_CALC_BLOCK_SIZE(wmWork->pSendLen); MBi_SetChildMPMaxSize(wmWork->pRecvLen); result = WM_StartConnect(MBi_WMStateOutStartConnect, bssDesc, NULL); if (result != WM_ERRCODE_OPERATING) { MBi_WMSendCallback(MB_CALLBACK_CONNECT_FAILED, NULL); } } /*---------------------------------------------------------------------------* Name: MBi_WMStateOutStartConnect Description: Connection callback to the parent. Arguments: arg: WM_StartConnect callback argument. Returns: None. *---------------------------------------------------------------------------*/ static void MBi_WMStateOutStartConnect(void *arg) { WMStartConnectCallback *cb = (WMStartConnectCallback *)arg; if (cb->errcode != WM_ERRCODE_SUCCESS) { MBi_WMSendCallback(MB_CALLBACK_CONNECT_FAILED, arg); return; } switch (cb->state) { case WM_STATECODE_BEACON_LOST: break; case WM_STATECODE_CONNECT_START: break; case WM_STATECODE_DISCONNECTED: MBi_WMSendCallback(MB_CALLBACK_DISCONNECTED_FROM_PARENT, NULL); break; case WM_STATECODE_DISCONNECTED_FROM_MYSELF: // Do nothing if it disconnects itself. break; case WM_STATECODE_CONNECTED: // When authentication is complete. MBi_WMSendCallback(MB_CALLBACK_CONNECTED_TO_PARENT, arg); break; } } /*---------------------------------------------------------------------------* Name: MBi_ChildStartMP Description: Start MP communication Arguments: sendBuf: Pointer to the region to set as the send buffer. recvBuf: Pointer to the region to set as the receive buffer. Returns: Error code, normally WM_ERRCODE_OPERATING. *---------------------------------------------------------------------------*/ void MBi_ChildStartMP(u16 *sendBuf, u16 *recvBuf) { WMErrCode result; wmWork->sendBuf = (u32 *)sendBuf; wmWork->recvBuf = (u32 *)recvBuf; result = WM_SetPortCallback(WM_PORT_BT, ChildPortCallback, NULL); if (result != WM_ERRCODE_SUCCESS) { MBi_WMApiErrorCallback(WM_APIID_START_MP, result); return; } result = WM_StartMPEx(ChildStateOutStartMP, recvBuf, wmWork->recvBufSize, sendBuf, wmWork->sendBufSize, 1, // mpFreq 0, // defaultRetryCount FALSE, // minPollBmpMode FALSE, // singlePacketMode TRUE, // fixFrameMode TRUE); // ignoreFatalError if (result != WM_ERRCODE_OPERATING) { MBi_WMApiErrorCallback(WM_APIID_START_MP, result); } } /*---------------------------------------------------------------------------* Name: ChildStateOutStartMP Description: Child WM_StartMPEx callback function. Arguments: arg: WM_StartMP callback argument. Returns: Error code, normally WM_ERRCODE_OPERATING. *---------------------------------------------------------------------------*/ static void ChildStateOutStartMP(void *arg) { WMStartMPCallback *cb = (WMStartMPCallback *)arg; if (cb->errcode != WM_ERRCODE_SUCCESS) { // End when the error notification does not require error handling. if (cb->errcode == WM_ERRCODE_SEND_FAILED) { return; } else if (cb->errcode == WM_ERRCODE_TIMEOUT) { return; } else if (cb->errcode == WM_ERRCODE_INVALID_POLLBITMAP) { return; } MBi_WMErrorCallback(cb->apiid, cb->errcode); return; } switch (cb->state) { case WM_STATECODE_MP_START: wmWork->mpStarted = 1; // Set the flag that indicates MP has started. wmWork->mpBusy = 0; wmWork->child_bitmap = 0; MBi_WMSendCallback(MB_CALLBACK_MP_STARTED, NULL); { // MP send-enabled callback. MBi_WMSendCallback(MB_CALLBACK_MP_SEND_ENABLE, NULL); } break; case WM_STATECODE_MP_IND: // None. break; case WM_STATECODE_MPACK_IND: // None. break; case WM_STATECODE_MPEND_IND: // Only occurs on a parent device. default: MBi_WMErrorCallback(cb->apiid, WM_ERRCODE_FAILED); break; } } /*---------------------------------------------------------------------------* Name: MBi_WMDisconnect Description: Child MP disconnect processing. Disconnects from the parent after WM_EndMP completes. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void MBi_WMDisconnect(void) { WMErrCode result; wmWork->endReq = 1; result = WM_EndMP(MBi_WMStateOutEndMP); if (result != WM_ERRCODE_OPERATING) { wmWork->endReq = 0; MBi_WMApiErrorCallback(WM_APIID_END_MP, result); } } /*---------------------------------------------------------------------------* Name: MBi_WMStateOutEndMP Description: The callback function for WM_EndMP. Arguments: arg: The callback argument for WM_EndMP. Returns: None. *---------------------------------------------------------------------------*/ static void MBi_WMStateOutEndMP(void *arg) { WMCallback *cb = (WMCallback *)arg; if (cb->errcode != WM_ERRCODE_SUCCESS) { wmWork->endReq = 0; MBi_WMErrorCallback(cb->apiid, cb->errcode); return; } wmWork->mpStarted = 0; MBi_WMStateInDisconnect(); } /*---------------------------------------------------------------------------* Name: MBi_WMStateInDisconnect Description: Disconnects a child from the parent and transitions to the IDLE state. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void MBi_WMStateInDisconnect(void) { WMErrCode result; result = WM_Disconnect(MBi_WMStateOutDisconnect, 0); if (result != WM_ERRCODE_OPERATING) { wmWork->endReq = 0; MBi_WMApiErrorCallback(WM_APIID_DISCONNECT, result); } } /*---------------------------------------------------------------------------* Name: MBi_WMStateInDisconnect Description: WM_Disconnect callback argument. Arguments: arg: WM_Disconnect callback argument. Returns: None. *---------------------------------------------------------------------------*/ static void MBi_WMStateOutDisconnect(void *arg) { WMCallback *cb = (WMCallback *)arg; wmWork->endReq = 0; if (cb->errcode != WM_ERRCODE_SUCCESS) { MBi_WMErrorCallback(cb->apiid, cb->errcode); return; } MBi_WMSendCallback(MB_CALLBACK_DISCONNECT_COMPLETE, NULL); } /*---------------------------------------------------------------------------* Name: MBi_WMReset Description: Resets the child, and causes a transition to the IDLE state. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void MBi_WMReset(void) { WMErrCode result; result = WM_Reset(MBi_WMStateOutReset); if (result != WM_ERRCODE_OPERATING) { MBi_WMApiErrorCallback(WM_APIID_RESET, result); } } /*---------------------------------------------------------------------------* Name: MBi_WMStateOutReset Description: Resets the child, and causes a transition to the IDLE state. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void MBi_WMStateOutReset(void *arg) { WMCallback *cb = (WMCallback *)arg; if (cb->errcode != WM_ERRCODE_SUCCESS) { MBi_WMErrorCallback(cb->apiid, cb->errcode); return; } // Reset sets to the idling (standby) state without starting the next state. MBi_WMSendCallback(MB_CALLBACK_DISCONNECT_COMPLETE, NULL); } /* * Check MP transmit permission Added the mpBusy flag, which is set when SetMP is executed, to the determination elements, so that MP will not be set again after SetMP and before the callback returns. */ /*---------------------------------------------------------------------------* Name: IsSendEnabled Description: This function determines whether or not it is OK to set new MP data at the present time. Added the mpBusy flag, which is set when SetMP is executed, to the determination elements, so that MP will not be set again after SetMP and before the callback returns. Arguments: None. Returns: TRUE if it is OK to set new data. Otherwise returns FALSE. *---------------------------------------------------------------------------*/ static BOOL IsSendEnabled(void) { return (wmWork->mpStarted == 1) && (wmWork->mpBusy == 0) && (wmWork->endReq == 0); } /*---------------------------------------------------------------------------* Name: ChildPortCallback Description: Child MP port callback function. Arguments: arg: Port callback argument for MP communication. Returns: None. *---------------------------------------------------------------------------*/ static void ChildPortCallback(void *arg) { WMPortRecvCallback *cb = (WMPortRecvCallback *)arg; if (cb->errcode != WM_ERRCODE_SUCCESS) { return; } switch (cb->state) { case WM_STATECODE_PORT_RECV: // Notify the program that data was received. MBi_WMSendCallback(MB_CALLBACK_MP_CHILD_RECV, cb); break; case WM_STATECODE_CONNECTED: // Connection notification break; case WM_STATECODE_PORT_INIT: case WM_STATECODE_DISCONNECTED: case WM_STATECODE_DISCONNECTED_FROM_MYSELF: break; } } /*---------------------------------------------------------------------------* Name: MBi_MPSendToParent Description: Sends buffer contents to the parent. Arguments: body_len: Data size. pollbmp: Poll bitmap of the other party (irrelevant for a child). sendbuf: Pointer to send data. Returns: If send processing started successfully, returns WM_ERRCODE_OPERATING. If it failed, some other code is returned. *---------------------------------------------------------------------------*/ WMErrCode MBi_MPSendToParent(u32 body_len, u16 pollbmp, u32 *sendbuf) { WMErrCode result; // 32 byte align check SDK_ASSERT(((u32)sendbuf & 0x1F) == 0); DC_FlushRange(sendbuf, sizeof(body_len)); DC_WaitWriteBufferEmpty(); if (!IsSendEnabled()) { return WM_ERRCODE_FAILED; } result = WM_SetMPDataToPort(StateOutMPSendToParent, (u16 *)sendbuf, (u16)body_len, pollbmp, WM_PORT_BT, WM_PRIORITY_LOW); if (result != WM_ERRCODE_OPERATING) { return result; } wmWork->mpBusy = 1; return WM_ERRCODE_OPERATING; } /*---------------------------------------------------------------------------* Name: StateOutMPSendToParent Description: Notification callback for a completed MP transmission. Arguments: arg: WM_SetMPDataToPort callback argument. Returns: None. *---------------------------------------------------------------------------*/ static void StateOutMPSendToParent(void *arg) { WMCallback *cb = (WMCallback *)arg; wmWork->mpBusy = 0; if (cb->errcode == WM_ERRCODE_SUCCESS) { MBi_WMSendCallback(MB_CALLBACK_MP_CHILD_SENT, arg); } else if (cb->errcode == WM_ERRCODE_TIMEOUT) { MBi_WMSendCallback(MB_CALLBACK_MP_CHILD_SENT_TIMEOUT, arg); } else { MBi_WMSendCallback(MB_CALLBACK_MP_CHILD_SENT_ERR, arg); } // Allow the next transmission. MBi_WMSendCallback(MB_CALLBACK_MP_SEND_ENABLE, NULL); } /*---------------------------------------------------------------------------* Name: MBi_WMSendCallback Description: Performs callback notification in the WM layer. Arguments: type The callback type arg : Callback argument Returns: None. *---------------------------------------------------------------------------*/ static inline void MBi_WMSendCallback(u16 type, void *arg) { if (wmWork->mpCallback == NULL) { return; } wmWork->mpCallback(type, arg); } /*---------------------------------------------------------------------------* Name: MBi_WMErrorCallback Description: Performs error notification in the WM layer. Arguments: apiid: WM_APIID that was the cause. error_code Error code Returns: None. *---------------------------------------------------------------------------*/ static inline void MBi_WMErrorCallback(u16 apiid, u16 error_code) { MBErrorCallback arg; if (wmWork->mpCallback == NULL) { return; } arg.apiid = apiid; arg.errcode = error_code; wmWork->mpCallback(MB_CALLBACK_ERROR, &arg); } /*---------------------------------------------------------------------------* Name: MBi_WMApiErrorCallback Description: Performs a notification when an error occurs with the return value of a WM API call. Arguments: apiid: WM_APIID that was the cause. error_code Error code Returns: None. *---------------------------------------------------------------------------*/ static inline void MBi_WMApiErrorCallback(u16 apiid, u16 error_code) { MBErrorCallback arg; if (wmWork->mpCallback == NULL) { return; } arg.apiid = apiid; arg.errcode = error_code; wmWork->mpCallback(MB_CALLBACK_API_ERROR, &arg); } /*---------------------------------------------------------------------------* Name: MBi_WMApiErrorCallback Description: Performs a notification when an error occurs with the return value of a WM API call. Arguments: apiid: WM_APIID that was the cause. error_code Error code Returns: None. *---------------------------------------------------------------------------*/ void MBi_WMClearCallback(void) { (void)WM_SetPortCallback(WM_PORT_BT, NULL, NULL); }