/*---------------------------------------------------------------------------* Project: Revolution MP simple async demo File: mpsimple.c Copyright 2006 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: mpsimpleAsync.c,v $ Revision 1.16 2007/10/26 07:29:51 seiki_masashi Cleaned up the code. Revision 1.15 2007/10/26 07:05:24 seiki_masashi Revised the link level display. Revision 1.13 2007/10/26 05:48:03 seiki_masashi Changed the prefix character for variables from 'g' to 's'. Revision 1.8 2007/10/04 09:41:19 seiki_masashi Support for MP_BEACON_PERIOD_AUTO. Revision 1.7 2007/10/03 04:58:41 seiki_masashi Corrections following specification changes to the MPSendAsync function. Revision 1.6 2007/08/24 04:44:59 seiki_masashi Changed the working memory size for the MP library from 32k to 64k. Revision 1.5 2007/08/01 09:36:29 kitase_hirotake Changed TAB to SPACE. Revision 1.4 2006/11/26 14:22:12 seiki_masashi Reduced the MP heap size to the current minimum value, 32kB. Removed the debug output at data receive/transfer. Revision 1.3 2006/11/13 06:54:10 kitase_hirotake Made changes so that buffer passed to MP is taken from MEM2. Made changes so that REXDEMO will be used. Revision 1.2 2006/09/08 11:54:02 yosizaki Revised the include file. Revision 1.1 2006/08/23 04:33:20 kitase_hirotake Added mpsimpleAsync. Revision 1.1 2006/08/22 01:44:42 kitase_hirotake Initial upload $NoKeywords: $ *---------------------------------------------------------------------------*/ // // This sample performs MP communications with the demos/wm/simple-1 demo in the NITRO-SDK. // The MPSendAsync asynchronous transmission function is used. // // This demo only performs a minimal amount of processing to determine if communications are being performed. // Refer to the mpdsmodel demo, an MPDS library demo, when creating an actual application. // // #include #include #include #include #include "rexdemo/demokpad.h" #include "rexdemo/graphic.h" #define USERHEAP_SIZE ( 65536 ) /*===========================================================================*/ static MEMHeapHandle sUserHeap; static u32 sRecvBuf[MP_AID_MAX]; static BOOL sRecvFlag[MP_AID_MAX]; static OSMessageQueue sMessageQueue; static OSMessage sMessageArray[16]; // This demo only uses one level. /*===========================================================================*/ static const GXColor white = { 0xFF, 0xFF, 0xFF, }; static const GXColor yellow = { 0xFF, 0xFF, 0x00, }; static const GXColor gray = { 0x80, 0x80, 0x80, }; static const GXColor black = { 0x00, 0x00, 0x00, }; static const GXColor red = { 0xFF, 0x00, 0x18 }; static const GXColor green = { 0x00, 0xFF, 0x00 }; /*===========================================================================*/ void* myAlloc( u32 size ); void myFree( void* ptr ); static void PortCallbackFunction( s32 type, MPPortCallbackInfo* info ); static void SendAsyncCallbackFunction( s32 result, struct MPCallbackInfo *info ); /*===========================================================================*/ // The GGID is a unique ID for MP communications assigned to each application. // You must receive a GGID assignment from Nintendo for an actual application. // Here, a GGID is used that is identical to that of the demos/wm/simple-1 demo. #define MY_GGID 0x003fff10 // the port number used by MP communications to send and receive data #define MY_PORT 4 // MP communication settings static MPConfig config = { myAlloc, myFree, 8, // threadPriority; set to a priority that is higher than the main thread by 4 or more MP_MODE_PARENT, // mode MY_GGID, // ggid MP_TGID_AUTO, // tgid 1, // channel // Because the simple-1 demo of NITRO-SDK is fixed to ch1, parent device is also fixed to ch1 // // Normally applications should use automatic settings by specifying MP_CHANNEL_AUTO. MP_LIFETIME_DEFAULT,// lifeTime; usually equal to MP_LIFETIME_DEFAULT MP_BEACON_PERIOD_AUTO, // beaconPeriod; normally MP_BEACON_PERIOD_AUTO 15, // maxNodes 4, // parentMaxSize 4, // childMaxSize TRUE, // entryFlag FALSE, // multiBootFlag; usually FALSE 1, // frequency 0, // userGameInfoLength { 0, }, // userGameInfo NULL, // indicationCallbackFunction /// ... // port configuration (can be set up using MPSetPortConfig) }; void main(void) { s32 result; /* initialize OS and memory heap */ OSReport("test program started.\n"); REXDEMOKPadInit(); REXDEMOInitScreen( FALSE ); REXDEMOSetGroundColor( black ); REXDEMOSetFontSize( 10, 20 ); REXDEMOBeginRender(); REXDEMOWaitRetrace(); /* initialize heap for MP library */ { void* heapAddress; heapAddress = OSGetMEM2ArenaLo(); OSSetMEM2ArenaLo( (void*)OSRoundUp32B( (u32)heapAddress + USERHEAP_SIZE ) ); sUserHeap = MEMCreateExpHeapEx( heapAddress, USERHEAP_SIZE, MEM_HEAP_OPT_THREAD_SAFE ); if( sUserHeap == NULL ) { OSHalt( "Could not create heap.\n" ); } } // // initializes the message queue // OSInitMessageQueue( &sMessageQueue, sMessageArray, sizeof sMessageArray / sizeof sMessageArray[0]); while(TRUE) { MPSetPortConfig( &config, MY_PORT, PortCallbackFunction, MP_PRIORITY_NORMAL ); OSReport("MPStartup()\n"); result = MPStartup( &config ); if( result != MP_RESULT_OK ) { OSReport( "MPStartup returns %08x\n", result ); OSHalt( "** panic **\n" ); } { static u32 frameData[512/sizeof(u32)] ATTRIBUTE_ALIGN(32); u32 count = 0; u32 restBits = 0; frameData[0] = MPHToMP32(count); for( count = 0; TRUE; count++ ) { OSMessage msg; REXDEMOKPadRead(); if( REXDEMOGetAnyMixedPadTrigger() & (KPAD_BUTTON_A | (PAD_BUTTON_A << 16)) ) { break; } frameData[0] = MPHToMP32(count); result = MPSendAsync ( frameData, sizeof(u32), 0xfffe, MY_PORT, &restBits, SendAsyncCallbackFunction, NULL ); if( result == MP_RESULT_OK ) { // Succeeded in sending an asynchronous transmission request. // The success or failure of communications can be determined by referencing restBits after a notification has come in SendAsyncCallbackFunction. // // Using MessageQueue, wait for a notification to come to the callback. (void)OSReceiveMessage(&sMessageQueue, &msg, OS_MESSAGE_BLOCK); // There is now a valid result stored in restBits. if( restBits == 0 ) { // transmissions to all destinations succeeded } else { // transmissions to some child devices failed // A list of the child devices for whom transmission failed is stored in restBits. } } else { // failed to send an asynchronous transmission request // when the number of simultaneous MP function callers exceeded the upper limit of 32 OSReport("MPSendAsync failed: %08x\n", result); } REXDEMOBeginRender(); REXDEMOSetTextColor(green); REXDEMOPrintf(40, 8, 0, "Parent mode"); REXDEMOSetTextColor(white); REXDEMOPrintf(4, 400, 0, "push button to restart."); REXDEMOSetTextColor(green); { s32 linkLevel = MPGetLinkLevel(); REXDEMOPrintf(480, 400, 0, "Link Level: %d", (linkLevel >= 0) ? linkLevel : 0); } REXDEMOSetTextColor(yellow); REXDEMOPrintf(4, 30, 0, "Send: %08x", count); REXDEMOPrintf(4, 52, 0, "Receive:"); { int i; for (i = 1; i < MP_AID_MAX; i++) { if (sRecvFlag[i]) { REXDEMOSetTextColor(yellow); REXDEMOPrintf(40, (s16)(60 + i * 20), 0, "Child%02d: %08x", i, sRecvBuf[i]); } else { REXDEMOSetTextColor(red); REXDEMOPrintf(40, (s16)(60 + i * 20), 0, "No child"); } } } REXDEMOSetTextColor(white); REXDEMOWaitRetrace(); } } OSReport("MPCleanup()\n"); (void)MPCleanup(); } } /*===========================================================================*/ void SendAsyncCallbackFunction( s32 result, struct MPCallbackInfo *info ) { #pragma unused(result) #pragma unused(info) // This function is called from a thread for MP library callbacks. // This is not an interrupt callback, so functions for synchronizing threads can be called. However, these functions will affect MP communications if they block for too long. // // Keep blocking used for exclusive access to a minimum. // Depending on the situation, consider methods of transferring data that do not block, such as those that use OSMessageQueue. // // Notify the main thread that an asynchronous transmission request has completed. // In this demo, MPSendAsync is not invoked multiple times simultaneously. Consequently, blocking due to a full MessageQueue will not occur. // (void)OSSendMessage(&sMessageQueue, "Hello!", OS_MESSAGE_BLOCK); } void PortCallbackFunction( s32 type, MPPortCallbackInfo* info ) { // This function is called from a thread for MP library callbacks. // This is not an interrupt callback, so functions for synchronizing threads can be called. However, these functions will affect MP communications if they block for too long. // // Keep blocking used for exclusive access to a minimum. // Depending on the situation, consider methods of transferring data that do not block, such as those that use OSMessageQueue. // switch( type ) { case MP_PORT_CB_TYPE_STARTUP: OSReport( "PortCallback: startup notification\n" ); break; case MP_PORT_CB_TYPE_CLEANUP: OSReport( "PortCallback: cleanup notification\n" ); break; case MP_PORT_CB_TYPE_CONNECTED: // A connection notification was received from a child device. sRecvFlag[info->dataReceived.fromAid] = TRUE; OSReport( "PortCallback: connected(%d) " MP_MACADDRESS_PRINT_FORMAT "\n", info->connected.fromAid, MP_MACADDRESS_PRINT_LIST(info->connected.macAddress) ); break; case MP_PORT_CB_TYPE_DISCONNECTED: // A disconnection notification was received from a child device. sRecvFlag[info->dataReceived.fromAid] = FALSE; OSReport( "PortCallback: disconnected(%d) " MP_MACADDRESS_PRINT_FORMAT "\n", info->disconnected.fromAid, MP_MACADDRESS_PRINT_LIST(info->disconnected.macAddress) ); break; case MP_PORT_CB_TYPE_DATA_RECEIVED: // Data was received from a child device. // The contents of info->dataReceived.data are guaranteed only within this callback and must therefore be quickly copied to an application buffer. // sRecvBuf[info->dataReceived.fromAid] = MPMPToH32(*(u32*)info->dataReceived.data); // OSReport( "PortCallback: received data from aid %d: %08x\n", // info->dataReceived.fromAid, // MPMPToH32(*(u32*)info->dataReceived.data) ); break; default: OSReport( "PortCallback: Unknown Type: %d\n", type ); break; } } /*===========================================================================*/ void* myAlloc( u32 size ) { return MEMAllocFromExpHeapEx( sUserHeap, size, 32 ); } void myFree( void* ptr ) { MEMFreeToExpHeap( sUserHeap, ptr ); }