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