1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - MB - demos - multiboot-Model
3   File:     main.c
4 
5   Copyright 2007-2009 Nintendo.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2009-06-04#$
14   $Rev: 10698 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 
18 
19 #include	<nitro.h>
20 #include <nitro/wm.h>
21 #include <nitro/mb.h>
22 
23 #include "mbp.h"
24 #include "common.h"
25 #include "disp.h"
26 #include "gmain.h"
27 #include "wh.h"
28 
29 /*
30  * Sample application that reconnects after multiboot
31  *
32  * Because the MB library samples use the multiboot functionality, multiple development units with the same communications environment (wired or wireless) are required
33  *
34  * The mb_child.bin program in the directory $NitroSDK/bin/ARM9-TS/Release/ is a sample providing the same features as the multiboot child in the final commercial unit. Load this binary into other development units using the same method as for sample programs, and execute them together.
35  *
36  *
37  *
38  *
39  *
40  */
41 
42 /******************************************************************************/
43 
44 static void WaitPressButton(void);
45 static void GetChannelMain(void);
46 static BOOL ConnectMain(u16 tgid);
47 static void PrintChildState(void);
48 static BOOL JudgeConnectableChild(WMStartParentCallback *cb);
49 static void OnPreSendMBVolat(u32 ggid);
50 
51 
52 //============================================================================
53 //  Constant Definitions
54 //============================================================================
55 
56 /* The GGID used in this demo */
57 #define WH_GGID                 SDK_MAKEGGID_SYSTEM(0x21)
58 
59 
60 /* The program information that this demo downloads */
61 const MBGameRegistry mbGameList = {
62     "/child.srl",                      // Child binary code
63     (u16 *)L"DataShareDemo",           // Game name
64     (u16 *)L"DataSharing demo",        // Game content description
65     "/data/icon.char",                 // Icon character data
66     "/data/icon.plt",                  // Icon palette data
67     WH_GGID,                           // GGID
68     MBP_CHILD_MAX + 1,                 // Max. number of players, including parents
69 };
70 
71 
72 
73 //============================================================================
74 //   Variable Definitions
75 //============================================================================
76 
77 static s32 gFrame;                     // Frame counter
78 
79 //-----------------------
80 // For keeping communication channels
81 //-----------------------
82 static u16 sChannel = 0;
83 static const MBPChildInfo *sChildInfo[MBP_CHILD_MAX];
84 
85 
86 //============================================================================
87 //   Function Definitions
88 //============================================================================
89 
90 /*---------------------------------------------------------------------------*
91   Name:         NitroMain
92 
93   Description:  Main routine
94 
95   Arguments:    None.
96 
97   Returns:      None.
98  *---------------------------------------------------------------------------*/
NitroMain(void)99 void NitroMain(void)
100 {
101     u16     tgid = 0;
102 
103     // Initialize screen, OS
104     CommonInit();
105     // Initialize screen
106     DispInit();
107 
108     // Initialize the heap
109     InitAllocateSystem();
110 
111     // Set data in WH
112     WH_SetGgid(WH_GGID);
113 
114     // Enable interrupts
115     (void)OS_EnableIrq();
116     (void)OS_EnableInterrupts();
117 
118     {                                  /* FS initialization */
119         static u32 fs_tablework[0x100 / 4];
120         FS_Init(FS_DMA_NOT_USE);
121         (void)FS_LoadTable(fs_tablework, sizeof(fs_tablework));
122     }
123 
124     DispOn();
125 
126     /*
127      * If the parent has the same TGID, the child does not update the parent information during DS Download Play.
128      * Therefore, it is necessary to consider assigning a different TGID value to a parent each time it is started during DS Download Play.
129      *
130      * Normally, you can use the WM_GetNextTgid function to get a relatively unique value based on the RTC.
131      *
132      */
133     tgid = WM_GetNextTgid();
134 
135     while (TRUE)
136     {
137         // Wait for A button to be pressed
138         WaitPressButton();
139 
140         // Search for a channel with little traffic
141         GetChannelMain();
142 
143         /*
144          * Normally, a different tgid value is set every time the parent starts.
145          * Note that you will no longer be able to reconnect to a multiboot child when starting with a different tgid unless you rescan.
146          *
147          * You do not need to save the tgid if you reconnect after rescanning.
148          *
149          */
150         // Multiboot distribution
151         if (ConnectMain(++tgid))
152         {
153             // Multiboot child startup is successful
154             break;
155         }
156     }
157 
158     //--------------
159     // Following multiboot, the child is reset and communication is temporarily disconnected.
160     // The parent also needs to end the communication with MB_End again in the current implementation.
161     // Start the communication from the beginning while parent and children are completely disconnected.
162     //
163     // At this time, the AIDs of the children will be shuffled. If necessary, save the AID and MAC address combination before multiboot, and link it to a new AID when reconnecting.
164     //
165     //
166     //--------------
167 
168 
169     // Set buffer for data sharing communication
170     GInitDataShare();
171 
172 #if !defined(MBP_USING_MB_EX)
173     if(!WH_Initialize())
174     {
175         OS_Panic("WH_Initialize failed.");
176     }
177 #endif
178 
179     // Set the function to determine connected children
180     WH_SetJudgeAcceptFunc(JudgeConnectableChild);
181 
182     /* Main routine */
183     for (gFrame = 0; TRUE; gFrame++)
184     {
185         OS_WaitVBlankIntr();
186 
187         ReadKey();
188 
189         BgClear();
190 
191         switch (WH_GetSystemState())
192         {
193         case WH_SYSSTATE_IDLE:
194             /* ----------------
195              * To reconnect a child to the same parent without rescanning, the parent needs to have the same tgid and channel as the child
196              *
197              * In this demo, the parent and child use the same channel as, and a tgid one greater than, the multiboot value, allowing them to connect without rescanning
198              *
199              *
200              * You don't need to use the same tgid or channel if you rescan with a specified MAC address
201              *
202              * ---------------- */
203             (void)WH_ParentConnect(WH_CONNECTMODE_DS_PARENT, (u16)(tgid + 1), sChannel);
204             break;
205 
206         case WH_SYSSTATE_CONNECTED:
207         case WH_SYSSTATE_KEYSHARING:
208         case WH_SYSSTATE_DATASHARING:
209             {
210                 BgPutString(8, 1, 0x2, "Parent mode");
211                 GStepDataShare(gFrame);
212                 GMain();
213             }
214             break;
215         }
216     }
217 }
218 
219 
220 
221 /*---------------------------------------------------------------------------*
222   Name:         WaitPressButton
223 
224   Description:  Function that waits in loop until A button is pressed.
225 
226   Arguments:    None.
227 
228   Returns:      None.
229  *---------------------------------------------------------------------------*/
WaitPressButton(void)230 static void WaitPressButton(void)
231 {
232     while (TRUE)
233     {
234         OS_WaitVBlankIntr();
235         ReadKey();
236         BgClear();
237         BgSetMessage(PLTT_WHITE, " Push A Button to start   ");
238         if (IS_PAD_TRIGGER(PAD_BUTTON_A))
239         {
240             return;
241         }
242     }
243 }
244 
245 /*---------------------------------------------------------------------------*
246   Name:         GetChannelMain
247 
248   Description:  Thoroughly checks the radio wave usage to find the channel to use.
249 
250   Arguments:    None.
251 
252   Returns:      None.
253  *---------------------------------------------------------------------------*/
GetChannelMain(void)254 static void GetChannelMain(void)
255 {
256 
257     /*-----------------------------------------------*
258      * Chooses the least-used channel after thoroughly checking the channels' radio wave activity.
259      *
260      * The state must be IDLE to call WM_MeasureChannel. This cannot be run in a multiboot state because it does not stop in the IDLE state.
261      *
262      * First call WM_Initialize to check radio wave activity, then end processing with WM_End. Finally, run MB_Init again.
263      *
264      *-----------------------------------------------*/
265     if(!WH_Initialize())
266     {
267         OS_Panic("WH_Initialize failed.");
268     }
269 
270     while (TRUE)
271     {
272         ReadKey();
273         BgClear();
274         BgSetMessage(PLTT_YELLOW, " Search Channel ");
275 
276         switch (WH_GetSystemState())
277         {
278             //-----------------------------------------
279             // Initialization complete
280         case WH_SYSSTATE_IDLE:
281             (void)WH_StartMeasureChannel();
282             break;
283             //-----------------------------------------
284             // Complete channel search
285         case WH_SYSSTATE_MEASURECHANNEL:
286             {
287                 sChannel = WH_GetMeasureChannel();
288 #if !defined(MBP_USING_MB_EX)
289                 (void)WH_End();
290 #else
291                 /* Proceed to the multiboot process while maintaining the IDLE state */
292                 return;
293 #endif
294             }
295             break;
296             //-----------------------------------------
297             // End WM
298         case WH_SYSSTATE_STOP:
299             /* Proceed to the multiboot process once WM_End completes */
300             return;
301             //-----------------------------------------
302             // Busy
303         case WH_SYSSTATE_BUSY:
304             break;
305             //-----------------------------------------
306             // Error occurred
307         case WH_SYSSTATE_ERROR:
308             (void)WH_Reset();
309             break;
310             //-----------------------------------------
311         default:
312             OS_Panic("Illegal State\n");
313         }
314         OS_WaitVBlankIntr();           // Wait for completion of V-Blank interrupt
315     }
316 }
317 
318 
319 /*---------------------------------------------------------------------------*
320   Name:         ConnectMain
321 
322   Description:  Connect with multiboot.
323 
324   Arguments:    tgid: The tgid for booting as parent
325 
326   Returns:      If transfer to the child is successful, TRUE.
327                 If transfer fails or is canceled, FALSE.
328  *---------------------------------------------------------------------------*/
ConnectMain(u16 tgid)329 static BOOL ConnectMain(u16 tgid)
330 {
331     MBP_Init(mbGameList.ggid, tgid);
332 
333     while (TRUE)
334     {
335         ReadKey();
336 
337         BgClear();
338 
339         BgSetMessage(PLTT_YELLOW, " MB Parent ");
340 
341         switch (MBP_GetState())
342         {
343             //-----------------------------------------
344             // Idle state
345         case MBP_STATE_IDLE:
346             {
347                 MBP_Start(&mbGameList, sChannel);
348                 /* Transmission test of user-defined data */
349                 MB_SetSendVolatCallback(OnPreSendMBVolat, MB_SEND_VOLAT_CALLBACK_TIMMING_BEFORE);
350             }
351             break;
352 
353             //-----------------------------------------
354             // Accepting entry from the child
355         case MBP_STATE_ENTRY:
356             {
357                 BgSetMessage(PLTT_WHITE, " Now Accepting            ");
358 
359                 if (IS_PAD_TRIGGER(PAD_BUTTON_B))
360                 {
361                     // Cancel multiboot with B button
362                     MBP_Cancel();
363                     break;
364                 }
365 
366                 // If there is at least one child in entry, start is possible
367                 if (MBP_GetChildBmp(MBP_BMPTYPE_ENTRY) ||
368                     MBP_GetChildBmp(MBP_BMPTYPE_DOWNLOADING) ||
369                     MBP_GetChildBmp(MBP_BMPTYPE_BOOTABLE))
370                 {
371                     BgSetMessage(PLTT_WHITE, " Push START Button to start   ");
372 
373                     if (IS_PAD_TRIGGER(PAD_BUTTON_START))
374                     {
375                         // Start download
376                         MBP_StartDownloadAll();
377                     }
378                 }
379             }
380             break;
381 
382             //-----------------------------------------
383             // Program distribution process
384         case MBP_STATE_DATASENDING:
385             {
386 
387                 // If everyone has completed downloading, start is possible
388                 if (MBP_IsBootableAll())
389                 {
390                     // Start boot
391                     MBP_StartRebootAll();
392                 }
393             }
394             break;
395 
396             //-----------------------------------------
397             // Reboot process
398         case MBP_STATE_REBOOTING:
399             {
400                 BgSetMessage(PLTT_WHITE, " Rebooting now                ");
401             }
402             break;
403 
404             //-----------------------------------------
405             // Reconnect process
406         case MBP_STATE_COMPLETE:
407             {
408                 // Once all members have successfully connected, multiboot processing ends. Restarts wireless communication as a normal parent.
409                 //
410                 BgSetMessage(PLTT_WHITE, " Reconnecting now             ");
411 
412                 OS_WaitVBlankIntr();
413                 return TRUE;
414             }
415             break;
416 
417             //-----------------------------------------
418             // Error occurred
419         case MBP_STATE_ERROR:
420             {
421                 // Cancel communication
422                 MBP_Cancel();
423             }
424             break;
425 
426             //-----------------------------------------
427             // Communication cancellation processing
428         case MBP_STATE_CANCEL:
429             // None.
430             break;
431 
432             //-----------------------------------------
433             // Abnormal termination of communication
434         case MBP_STATE_STOP:
435 #ifdef MBP_USING_MB_EX
436             switch (WH_GetSystemState())
437             {
438             case WH_SYSSTATE_IDLE:
439                 (void)WH_End();
440                 break;
441             case WH_SYSSTATE_BUSY:
442                 break;
443             case WH_SYSSTATE_STOP:
444                 return FALSE;
445             default:
446                 OS_Panic("illegal state\n");
447             }
448 #else
449             return FALSE;
450 #endif
451         }
452 
453         // Display child state
454         PrintChildState();
455 
456         OS_WaitVBlankIntr();           // Wait for completion of V-Blank interrupt
457     }
458 }
459 
460 
461 /*---------------------------------------------------------------------------*
462   Name:         PrintChildState
463 
464   Description:  Displays child information on screen.
465 
466   Arguments:    None.
467 
468   Returns:      None.
469  *---------------------------------------------------------------------------*/
PrintChildState(void)470 static void PrintChildState(void)
471 {
472     static const char *STATE_NAME[] = {
473         "NONE       ",
474         "CONNECTING ",
475         "REQUEST    ",
476         "ENTRY      ",
477         "DOWNLOADING",
478         "BOOTABLE   ",
479         "BOOTING    ",
480     };
481     enum
482     {
483         STATE_DISP_X = 2,
484         INFO_DISP_X = 15,
485         BASE_DISP_Y = 2
486     };
487 
488     u16     i;
489 
490     /* Child list display */
491     for (i = 1; i <= MBP_CHILD_MAX; i++)
492     {
493         const MBPChildInfo *childInfo;
494         MBPChildState childState = MBP_GetChildState(i);
495         const u8 *macAddr;
496 
497         SDK_ASSERT(childState < MBP_CHILDSTATE_NUM);
498 
499         // State display
500         BgPutString(STATE_DISP_X, i + BASE_DISP_Y, PLTT_WHITE, STATE_NAME[childState]);
501 
502         // User information display
503         childInfo = MBP_GetChildInfo(i);
504         macAddr = MBP_GetChildMacAddress(i);
505 
506         if (macAddr != NULL)
507         {
508             BgPrintStr(INFO_DISP_X, i + BASE_DISP_Y, PLTT_WHITE,
509                        "%02x%02x%02x%02x%02x%02x",
510                        macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]);
511         }
512     }
513 }
514 
515 
516 /*---------------------------------------------------------------------------*
517   Name:         JudgeConnectableChild
518 
519   Description:  Determines if the child is connectable during reconnect.
520 
521   Arguments:    cb: Information for the child attempting to connect
522 
523   Returns:      If connection is accepted, TRUE.
524                 If not accepted, FALSE.
525  *---------------------------------------------------------------------------*/
JudgeConnectableChild(WMStartParentCallback * cb)526 static BOOL JudgeConnectableChild(WMStartParentCallback *cb)
527 {
528     u16     playerNo;
529 
530     /*  Search the MAC address for the AID when multibooting the child of cb->aid */
531     playerNo = MBP_GetPlayerNo(cb->macAddress);
532 
533     OS_TPrintf("MB child(%d) -> DS child(%d)\n", playerNo, cb->aid);
534 
535     if (playerNo == 0)
536     {
537         return FALSE;
538     }
539 
540     sChildInfo[playerNo - 1] = MBP_GetChildInfo(playerNo);
541     return TRUE;
542 }
543 
544 /*---------------------------------------------------------------------------*
545   Name:         OnPreSendMBVolat
546 
547   Description:  Callback function used for notification before a parent sends an MB beacon.
548 
549   Arguments:    ggid: GGID of game information to send
550 
551   Returns:      None.
552  *---------------------------------------------------------------------------*/
OnPreSendMBVolat(u32 ggid)553 static void OnPreSendMBVolat(u32 ggid)
554 {
555     /*
556      * When multiple games are registered and the configuration value is switched for each game, use the ggid argument to determine the game information, as follows.
557      *
558      */
559     if (ggid == mbGameList.ggid)
560     {
561         /*
562          * Up to 8 bytes of user-defined data can be sent.
563          * This example sends incremental 64-bit values.
564          * Note that the child may not always be able to immediately receive dynamically updated data.
565          *
566          */
567         static u64 count ATTRIBUTE_ALIGN(8);
568         SDK_COMPILER_ASSERT(sizeof(count) <= MB_USER_VOLAT_DATA_SIZE);
569         SDK_COMPILER_ASSERT(MB_USER_VOLAT_DATA_SIZE == 8);
570         ++count;
571         MB_SetUserVolatData(ggid, (u8 *)&count, sizeof(count));
572     }
573 }
574