1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - MB - demos - multiboot
3 File: parent.c
4
5 Copyright 2006-2008 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-03-02#$
14 $Rev: 10122 $
15 $Author: kitase_hirotake $
16 *---------------------------------------------------------------------------*/
17 #include <nitro.h>
18 #include <nitro/wm.h>
19 #include <nitro/mb.h>
20
21 #include "common.h"
22 #include "dispfunc.h"
23
24
25 /*
26 * Basic processing on the parent.
27 *
28 * Because the MB library samples use the multiboot functionality, multiple development units with the same communications environment (wired or wireless) are required
29 *
30 * The mb_child_NITRO.srl and mb_child_TWL.srl sample programs in the $TwlSDK/bin/ARM9-TS/Rom/ directory provides the same features as a multiboot child for final retail units. Load this binary on other hardware as with any sample program, and run them together.
31 *
32 *
33 *
34 *
35 */
36
37
38 /******************************************************************************/
39 /* Variables */
40
41 /*
42 * Memory for loading the download program's segment information.
43 */
44
45 static u8 *p_segbuf[GAME_PROG_MAX];
46 static u8 cursor = 0;
47 static MBUserInfo myUser;
48 static u16 parent_channel = PARENT_CHANNEL;
49 static u16 parent_send_size = 256;
50 static u8 entry_max = 15;
51
52 /*
53 * Switch for automatically controlling responses from the parent to a child in this sample.
54 * Children wait for a response from the parent before sending requests. By timing this response to be the same for all children or individually different, you can configure the behavior of multi-boot applications.
55 * For example, you could respond individually if you are only distributing a demo, and respond all at once when the entry number is reached during a multiplayer match.
56 *
57 *
58 *
59 *
60 *
61 */
62
63 static u8 auto_accept = 0; /* Automate the connection permission */
64 static u8 auto_send = 0; /* Automate the download permission */
65 static u8 auto_boot = 0; /* Automate the boot permission */
66
67 /******************************************************************************/
68 /* Functions */
69
70 /* Initialize the file buffer pointer */
InitFileBuffer(void)71 void InitFileBuffer(void)
72 {
73 int i;
74
75 for (i = 0; i < GAME_PROG_MAX; i++)
76 {
77 p_segbuf[i] = NULL;
78 }
79
80 }
81
82 /* Deallocate the allocated file buffers */
FreeFileBuffer(void)83 static void FreeFileBuffer(void)
84 {
85 int i;
86
87 for (i = 0; i < GAME_PROG_MAX; i++)
88 {
89 if (p_segbuf[i])
90 {
91 OS_Free(p_segbuf[i]);
92 p_segbuf[i] = NULL;
93 }
94
95 }
96
97 }
98
99 /* Generate MBUserInfo */
MakeUserInfo(MBUserInfo * user,u8 favoriteColor,u8 playerNo,u16 * name,u16 name_length)100 static void MakeUserInfo(MBUserInfo *user, u8 favoriteColor, u8 playerNo, u16 *name,
101 u16 name_length)
102 {
103 SDK_ASSERT(user != NULL);
104 user->favoriteColor = favoriteColor;
105 user->nameLength = (u8)(name_length);
106 MI_CpuCopy8((char *)name, (char *)user->name, (u32)(name_length * 2));
107 user->playerNo = playerNo;
108 }
109
110 /* Cyclically change channels */
changeChannel(u16 * p_channel)111 static BOOL changeChannel(u16 *p_channel)
112 {
113 u16 channel_bmp, channel, i;
114
115 /* Get channel bitmap. */
116 channel_bmp = WM_GetAllowedChannel();
117
118 /* If there are no usable channels, call OS_Panic */
119 if (channel_bmp == 0)
120 {
121 OS_Panic("No Available Parent channel\n");
122 return FALSE;
123 }
124
125 /* If usable channels exist. */
126 for (i = 0, channel = *p_channel;
127 i < 16; i++, channel = (u16)((channel == 16) ? 1 : channel + 1))
128 {
129 if (channel_bmp & (1 << (channel - 1)))
130 {
131 /* If the detected channel is the same as before, search for another channel.
132
133
134 */
135 if (*p_channel != channel)
136 {
137 *p_channel = channel;
138 break;
139 }
140 }
141
142 }
143
144 return TRUE;
145
146 }
147
148 /* Parent device file download state notification callback */
ParentStateCallback(u16 aid,u32 status,void * arg)149 static void ParentStateCallback(u16 aid, u32 status, void *arg)
150 {
151
152 switch (status)
153 {
154 case MB_COMM_PSTATE_INIT_COMPLETE:
155 /* Initialization process complete */
156 {
157 WMStartParentCallback *p = (WMStartParentCallback *)arg;
158 BgSetMessage(WHITE, "MB lib Init complete");
159 }
160 break;
161
162 case MB_COMM_PSTATE_CONNECTED:
163 /* Notification the moment the child device becomes connected */
164 {
165 WMStartParentCallback *p = (WMStartParentCallback *)arg;
166
167 BgSetMessage(YELLOW, "Connected [%2d]", aid);
168 BgSetMessage(YELLOW, "MACAddress %04x %04x %04x",
169 p->macAddress[0], p->macAddress[1], p->macAddress[2]);
170 }
171 break;
172
173 case MB_COMM_PSTATE_DISCONNECTED:
174 /* Notification when the child device has terminated connection */
175 {
176 WMStartParentCallback *p = (WMStartParentCallback *)arg;
177
178 BgSetMessage(RED, "Disconnected [%2d]", aid);
179 /* ReasonCode display at the time of disconnection */
180 // BgSetMessage( WHITE, "reason code : %2d", p->reason);
181 }
182 break;
183
184 case MB_COMM_PSTATE_KICKED:
185 /* Notification when a child is kicked */
186 BgSetMessage(RED, "Entry Refused [%2d]", aid);
187 break;
188
189 case MB_COMM_PSTATE_REQ_ACCEPTED:
190 /* Notification when received the download request from a child */
191 BgSetMessage(YELLOW, "Download Request [%2d]", aid);
192 break;
193
194 case MB_COMM_PSTATE_SEND_PROCEED:
195 /* Notification when starting the binary transmission to a child */
196 BgSetMessage(CYAN, "Start Sending [%2d]", aid);
197 break;
198
199 case MB_COMM_PSTATE_SEND_COMPLETE:
200 /* Notification when the binary transmission to the child completed */
201 BgSetMessage(CYAN, "Send Completed [%2d]", aid);
202 if (auto_boot == 1)
203 {
204 /* Automatic boot request */
205 (void)MB_CommBootRequest(aid);
206 BgSetMessage(WHITE, "-->Boot Request [%2d]", aid);
207 }
208 break;
209
210 case MB_COMM_PSTATE_BOOT_REQUEST:
211 /* Notification when the boot request is sent to the child device */
212 BgSetMessage(CYAN, "Send boot request [%2d]", aid);
213 break;
214
215 case MB_COMM_PSTATE_BOOT_STARTABLE:
216 /* Notification when a series of processes has ended, and an application that supports multiboot can be started up */
217 BgSetMessage(YELLOW, "Boot ready [%2d]", aid);
218 break;
219
220 case MB_COMM_PSTATE_REQUESTED:
221 /* Notification of the moment the entry request came from a child */
222 BgSetMessage(YELLOW, "Entry Requested [%2d]", aid);
223
224 if (auto_accept == 1)
225 {
226 /* Auto-accept entry */
227 (void)MB_CommResponseRequest(aid, MB_COMM_RESPONSE_REQUEST_ACCEPT);
228 BgSetMessage(WHITE, "-->Entry Accept [%2d]", aid);
229 }
230 break;
231
232 case MB_COMM_PSTATE_MEMBER_FULL:
233 /* Notification when a child device is attempting to connect to a game with the full number of players */
234 BgSetMessage(RED, "Entry Member full [%2d]", aid);
235 break;
236
237 case MB_COMM_PSTATE_END:
238 /* Notification when the parent device ends */
239 BgSetMessage(WHITE, "MB lib End");
240 prog_state = STATE_MENU;
241 break;
242
243 case MB_COMM_PSTATE_WAIT_TO_SEND:
244 /* Notification when the send wait state to the child device has started */
245 BgSetMessage(CYAN, "Waiting to send [%2d]", aid);
246 if (auto_send == 1)
247 {
248 /* Start auto-send */
249 (void)MB_CommStartSending(aid);
250 BgSetMessage(WHITE, "-->Start Sending [%2d]", aid);
251 }
252 break;
253
254 }
255 }
256
257
258
259 /* Parent device initialization */
ParentInit(void)260 void ParentInit(void)
261 {
262 int i;
263
264 OSOwnerInfo MyInfo;
265
266 OS_GetOwnerInfo(&MyInfo);
267 MakeUserInfo(&myUser, MyInfo.favoriteColor, 0, (u16 *)MyInfo.nickName, MyInfo.nickNameLength);
268
269 BgClear();
270 BgSetMessage(WHITE, "Initializing Parent.");
271
272 /*
273 * Begin MB parent device control
274 * Enters the wait state when everything is ready
275 */
276
277 /* During re-initialization, deallocate the heap that was used previously */
278 FreeFileBuffer();
279
280 /* Multiboot library initialization */
281 (void)MB_Init(cwork, &myUser, MY_GGID, MB_TGID_AUTO, MB_DMA_NO);
282
283 /* Specify the data size to send / receive */
284 (void)MB_SetParentCommParam(parent_send_size, entry_max);
285 (void)MB_CommSetParentStateCallback(ParentStateCallback);
286 // MB_StartParent calls WM_Initialize internally
287 if(MB_StartParent(parent_channel) != MB_SUCCESS)
288 {
289 OS_Panic("MB_StartParent() failed.");
290 }
291
292 BgSetMessage(WHITE, "** Parameters **");
293 BgSetMessage(WHITE, "channel : %2d", parent_channel);
294 BgSetMessage(WHITE, "send size : %3d", parent_send_size);
295 BgSetMessage(WHITE, "max children : %2d", entry_max);
296 BgSetMessage(WHITE, "GGID:%08x TGID:%04x", MY_GGID, MB_GetTgid());
297
298 prog_state = STATE_MB_PARENT;
299 cursor = 0;
300
301 /*
302 * Next, register information for the download program file.
303 *
304 * Of the entire program file, only the ARM9-static and ARM7-static segments are required when multi-booting.
305 *
306 * (Everything else will be overlays, data files, and so on)
307 *
308 * To read only this information from the program, use the MB_ReadSegment function first.
309 *
310 * This sample dynamically allocates memory using the size obtained by the MB_GetSegmentLength function.
311 *
312 *
313 * The program file must be read using FS_ReadFile.
314 * Usually, programs are maintained as files in CARD-ROM and pose no problems. If you anticipate a special multi-boot system, however, use FSArchive to build an original archive to handle it.
315 *
316 *
317 */
318 for (i = 0; i < GAME_PROG_MAX; ++i)
319 {
320 FSFile file[1];
321 FS_InitFile(file);
322
323 /*
324 * Open the child program file.
325 * This file is used by MB_ReadSegment below.
326 */
327 if (!FS_OpenFileEx(file, mbGameList[i].romFilePathp, FS_FILEMODE_R))
328 {
329 OS_TPrintf("ParentInit : file cannot open (mbGameList[i].romFilePathp=\"%s)\"\n",
330 mbGameList[i].romFilePathp ? mbGameList[i].romFilePathp : "(NULL)");
331 }
332 else
333 {
334 /*
335 * Get the size of the segment information.
336 * If this is not a valid download program, 0 will be returned for this size.
337 *
338 */
339 u32 length = MB_GetSegmentLength(file);
340 if (length == 0)
341 {
342 OS_TPrintf
343 ("ParentInit : specified file may be invalid format. (mbGameList[i].romFilePathp=\"%s)\"\n",
344 mbGameList[i].romFilePathp ? mbGameList[i].romFilePathp : "(NULL)");
345 }
346 else
347 {
348 /*
349 * Allocate memory for the segment information.
350 * This memory may be prepared statically as long as it is large enough.
351 *
352 */
353 p_segbuf[i] = (u8 *)OS_Alloc(length);
354 if (!p_segbuf[i])
355 {
356 OS_TPrintf("ParentInit : memory allocation failed. (%d BYTE)\n", length);
357 }
358 else
359 {
360 /*
361 * Extract segment information from the file.
362 * The extracted segment information must stay resident in main memory while the download program is being distributed.
363 *
364 *
365 * If extraction fails here even though the size was obtained successfully, it may be because the file handle was changed in some way
366 *
367 *
368 * (The file was closed, a seek to some location was run, and so on)
369 */
370 if (!MB_ReadSegment(file, p_segbuf[i], length))
371 {
372 OS_TPrintf("ParentInit : failed to extract segment\n");
373 }
374 else
375 {
376 /*
377 * Register the download program in the MBGameRegistry with the extracted segment information.
378 *
379 *
380 */
381
382 if (MB_RegisterFile(&mbGameList[i], p_segbuf[i]))
383 {
384 BgSetMessage(LIGHT_GREEN, "Registered\"%s\"",
385 mbGameList[i].romFilePathp);
386 }
387 else
388 {
389 OS_TPrintf("ParentInit : failed to register file No %d\n", i);
390 }
391 }
392 }
393 }
394 }
395 if (FS_IsFile(file))
396 (void)FS_CloseFile(file);
397 }
398
399 }
400
401
402 /* Parent device main process in each single frame */
ParentMain(void)403 void ParentMain(void)
404 {
405 char userName[MB_USER_NAME_LENGTH * 2 + 1];
406 const int children_num = MB_CommGetChildrenNumber();
407 const u16 keyData = ReadKeySetTrigger(PAD_Read());
408 enum
409 { DISP_OX = 2, DISP_OY = 3 };
410 u16 i;
411 OSIntrMode enabled;
412 enum
413 { PSTATE_NUM = 16 };
414 const char *pstate_string[] = {
415 "NONE ", // STATE_NONE
416 "INIT OK ", // STATE_INIT_COMPLETE
417 "CONNECTED ", // STATE_CONNECTED
418 "DISCONNECTED", // STATE_DISCONNECTED
419 "KICKED ", // STATE_KICKED
420 "ENTRY OK ", // STATE_REQ_ACCEPTED
421 "SENDING ", // STATE_SEND_PROCEED
422 "SEND END ", // STATE_SEND_COMPLETE
423 "BOOT REQ ", // STATE_BOOT_REQUEST
424 "BOOT READY ", // STATE_BOOT_STARTABLE
425 "ENTRY REQ ", // STATE_REQUESTED
426 "MEMBER FULL ", // STATE_MEMBER_FULL
427 "END ", // STATE_END
428 "ERROR ", // STATE_ERROR
429 "WAIT TO SEND", // STATE_WAIT_TO_SEND
430 };
431
432 BgPrintf(DISP_OX, DISP_OY - 2, WHITE, "CH:%2d Max Children:%2d", parent_channel, entry_max);
433 BgPutString(DISP_OX, DISP_OY - 1, WHITE, "AID USERNAME STATE ");
434 BgPutString(DISP_OX, DISP_OY + MB_MAX_CHILD + 1, WHITE, "A:operate B:refuse SEL:restart");
435 BgPutString(DISP_OX, DISP_OY + MB_MAX_CHILD + 2, WHITE, "START:operate all children");
436
437 enabled = OS_DisableInterrupts(); /* Interrupts disabled */
438
439 /* Child list display */
440 for (i = 0; i < entry_max; ++i)
441 {
442 const u16 aid = (u16)(i + 1);
443 const MBUserInfo *p_child = MB_CommGetChildUser(aid);
444 int p_state;
445
446 BgPutString(DISP_OX, (s16)(DISP_OY + i), WHITE, " ");
447
448 BgPrintf(DISP_OX + 0, (s16)(DISP_OY + i), WHITE, "%2d ", aid);
449
450 if (p_child && p_child->nameLength)
451 {
452 u8 palette = p_child->favoriteColor;
453 MI_CpuCopy8(p_child->name, userName, (u32)(p_child->nameLength * 2));
454 BgPutStringN((s16)(DISP_OX + 4), (s16)(DISP_OY + i), palette, userName, 8);
455 }
456
457 p_state = MB_CommGetParentState(aid);
458
459 if (p_state < PSTATE_NUM)
460 {
461 BgPrintf((s16)(DISP_OX + 13), (s16)(DISP_OY + i), WHITE,
462 "%s ", (char *)pstate_string[p_state]);
463 }
464
465 BgPutChar((s16)(DISP_OX - 2), (s16)(DISP_OY + i), RED, (char)((i == cursor) ? '*' : ' '));
466 }
467
468 (void)OS_RestoreInterrupts(enabled); /* Cancel interrupt disabling */
469
470 /* All child devices are targets */
471 if (keyData & PAD_BUTTON_START)
472 {
473 BOOL result = FALSE;
474
475 if (!auto_accept)
476 {
477 for (i = 1; i <= entry_max; i++)
478 {
479 /* Entry permission */
480 if (MB_COMM_PSTATE_REQUESTED == MB_CommGetParentState(i))
481 {
482 result = MB_CommResponseRequest(i, MB_COMM_RESPONSE_REQUEST_ACCEPT);
483 }
484 }
485 if (TRUE == result)
486 return;
487 }
488
489 if (!auto_send)
490 {
491 result = MB_CommStartSendingAll();
492 if (TRUE == result)
493 return;
494 }
495
496 if (!auto_boot)
497 {
498 result = MB_CommBootRequestAll();
499 if (TRUE == result)
500 return;
501 }
502 }
503
504 /* Individually send to designated child devices */
505 if (keyData & PAD_BUTTON_A)
506 {
507 const u16 aid = (u16)(cursor + 1);
508
509 BgSetMessage(WHITE, "AID%2d State:%s", aid, pstate_string[MB_CommGetParentState(aid)]);
510
511 /* Entry permission */
512 if (MB_COMM_PSTATE_REQUESTED == MB_CommGetParentState(aid))
513 {
514 if (!auto_accept)
515 {
516 (void)MB_CommResponseRequest(aid, MB_COMM_RESPONSE_REQUEST_ACCEPT);
517 BgSetMessage(WHITE, "-->Entry Accept [%2d]", aid);
518 }
519 }
520 /* Start send */
521 else if (MB_COMM_PSTATE_WAIT_TO_SEND == MB_CommGetParentState(aid))
522 {
523 if (!auto_send)
524 {
525 (void)MB_CommStartSending(aid);
526 BgSetMessage(WHITE, "-->Start Sending [%2d]", aid);
527 }
528 }
529 else
530 /* Boot request */
531 if (TRUE == MB_CommIsBootable(aid))
532 {
533 if (!auto_boot)
534 {
535 (void)MB_CommBootRequest(aid);
536 BgSetMessage(WHITE, "-->Boot Request [%2d]", aid);
537 }
538 }
539 }
540 else
541 /* Deny entry to the designated child device */
542 if (keyData & PAD_BUTTON_B)
543 {
544 const u16 aid = (u16)(cursor + 1);
545
546 BgSetMessage(WHITE, "AID%2d State:%s", aid, pstate_string[MB_CommGetParentState(aid)]);
547 if (MB_COMM_PSTATE_REQUESTED == MB_CommGetParentState(aid))
548 {
549 if (!auto_accept)
550 {
551 (void)MB_CommResponseRequest(aid, MB_COMM_RESPONSE_REQUEST_KICK);
552 BgSetMessage(CYAN, "Entry Refuse [%2d]", aid);
553 }
554 }
555 }
556
557 /* When SELECT is pressed, end and re-initialize */
558 if (keyData & PAD_BUTTON_SELECT)
559 {
560 /* Change channel */
561 (void)changeChannel(&parent_channel);
562 MB_End();
563 }
564
565 /* Change cursor number */
566 if (keyData & PAD_KEY_DOWN)
567 {
568 (void)RotateU8(&cursor, 0, (u8)(entry_max - 1), 1);
569 }
570 else if (keyData & PAD_KEY_UP)
571 {
572 (void)RotateU8(&cursor, 0, (u8)(entry_max - 1), -1);
573 }
574
575 }
576
577 /* Set parent device MP send size */
SetParentSendSize(u16 p_size)578 void SetParentSendSize(u16 p_size)
579 {
580 parent_send_size = p_size;
581 }
582
583 /* Get parent device MP send size */
GetParentSendSize(void)584 u16 GetParentSendSize(void)
585 {
586 return parent_send_size;
587 }
588
589 /* Set maximum number of child device connections */
SetMaxEntry(u8 num)590 void SetMaxEntry(u8 num)
591 {
592 entry_max = num;
593 }
594
595 /* Get the maximum number of child device connections */
GetMaxEntry(void)596 u8 GetMaxEntry(void)
597 {
598 return entry_max;
599 }
600
601 /* Configure parent device auto operations */
SetAutoOperation(u8 fAccept,u8 fSend,u8 fBoot)602 void SetAutoOperation(u8 fAccept, u8 fSend, u8 fBoot)
603 {
604 auto_accept = fAccept;
605 auto_send = fSend;
606 auto_boot = fBoot;
607 }
608