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