1 /*---------------------------------------------------------------------------*
2   Project:  NET demo
3   File:     sostartup.c
4 
5   Copyright 2007 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   $Log: sostartup.c,v $
14   Revision 1.6  2008/01/23 04:02:06  seiki_masashi
15   Slightly revised the displayed error code.
16 
17   Revision 1.5  2008/01/22 09:59:40  seiki_masashi
18   Added a call to the NCDHasEnabledNetworkConfig function.
19 
20   Revision 1.4  2007/10/26 08:06:20  seiki_masashi
21   Added support for REXDEMOGetAnyMixedPadTrigger()
22 
23   Revision 1.3  2007/09/07 02:25:12  seiki_masashi
24   Separated the SafeSOFinish function.
25 
26   Revision 1.2  2007/09/06 13:29:46  seiki_masashi
27   Cleaned up comments.
28 
29   Revision 1.1  2007/09/06 12:58:02  seiki_masashi
30   Added a demo that runs SOStartup and SOCleanup in a different thread.
31 
32   $NoKeywords: $
33  *---------------------------------------------------------------------------*/
34 
35 #include <revolution.h>
36 #include <revolution/mem.h>
37 #include <revolution/net.h>
38 #include <revolution/so.h>
39 
40 #include <string.h>     /* for memset() */
41 
42 #include "rexdemo/netconfig.h"
43 #include "rexdemo/graphic.h"
44 #include "rexdemo/demokpad.h"
45 
46 /*---------------------------------------------------------------------------*
47     Constant Definitions
48  *---------------------------------------------------------------------------*/
49 
50 /* heap size for the SO library */
51 #define SOCKET_HEAP_SIZE                (1024*128)
52 /* priority of the network thread */
53 /* the priority must be higher than that of the main thread */
54 #define NETWORK_THREAD_PRIORITY         14
55 /* stack size of the network thread */
56 #define NETWORK_THREAD_STACK_SIZE       4096
57 /* number of steps in the network thread's command queue */
58 /* This implementation uses mutual exclusion for normal Startup and Cleanup commands. Since Cleanup at completion is the only request made in parallel, the number of levels in the queue is set to 2. */
59 /* */
60 /* */
61 #define NETWORK_COMMAND_QUEUE_SIZE      2
62 /* number of steps in the event notification queue from the network thread */
63 #define NETWORK_EVENT_QUEUE_SIZE        2
64 /* upper limit on network termination retries */
65 /* a sufficiently large value is necessary because it is used to fix a blatantly abnormal state */
66 #define NETWORK_TERMINATION_RETRY_LIMIT   120
67 /* wait time (ms) when re-attempting network termination */
68 #define NETWORK_TERMINATION_RETRY_WAIT    500
69 
70 /* Network thread commands */
71 enum {
72     NETWORK_COMMAND_KILL_THREAD = 0,
73     NETWORK_COMMAND_STARTUP,
74     NETWORK_COMMAND_CLEANUP
75 };
76 
77 /* Events for which the network thread will send notifications */
78 enum {
79     NETWORK_EVENT_NONE = 0,
80     NETWORK_EVENT_STARTUP,
81     NETWORK_EVENT_CLEANUP
82     /* negative values directly indicate NET_ECODE_* */
83 };
84 
85 /* Main thread states */
86 enum {
87     STATE_OFFLINE = 0,
88     STATE_SOSTARTUP_IN_PROGRESS,
89     STATE_ONLINE,
90     STATE_SOCLEANUP_IN_PROGRESS,
91     STATE_TERMINATING,
92     STATE_TERMINATED,
93     STATE_ERROR
94 };
95 
96 /*---------------------------------------------------------------------------*
97     internal variable definitions
98  *---------------------------------------------------------------------------*/
99 /* heap handle for the SO library */
100 static MEMHeapHandle heapHandleSocket = NULL;
101 
102 /* Network thread */
103 static OSThread NetworkThread;
104 static u8       NetworkThreadStack[NETWORK_THREAD_STACK_SIZE] ATTRIBUTE_ALIGN(32);
105 
106 /* message queue for communicating with the network thread */
107 static OSMessageQueue NetworkCommandQueue;
108 static OSMessage      NetworkCommandQueueMessages[NETWORK_COMMAND_QUEUE_SIZE];
109 static OSMessageQueue NetworkEventQueue;
110 static OSMessage      NetworkEventQueueMessages[NETWORK_EVENT_QUEUE_SIZE];
111 
112 /*---------------------------------------------------------------------------*
113     Function Prototype
114  *---------------------------------------------------------------------------*/
115 static void InitNetwork( void );
116 static void TerminateNetwork( void );
117 static void SafeSOFinish( void );
118 
119 static s32 ReceiveNetworkEvent( void );
120 
121 static void* AllocForSocket( u32 name, s32 size );
122 static void FreeForSocket( u32 name, void* ptr, s32 size );
123 static BOOL CreateHeapForSocket( u32 size );
124 static void DestroyHeapForSocket( void );
125 
126 static const char* StateToString( u32 state );
127 static void SendCommand( u32 command );
128 
129 static void* NetworkHandler( void* param );
130 static void SendNetworkEvent( s32 event );
131 
132 /*===========================================================================*/
133 
134 #define REPORT_STATE( state ) REXDEMOReportEx( REXDEMO_COLOR_PURPLE, "state -> %s\n", StateToString(state) )
main(void)135 int main(void)
136 {
137     s32 result = 0;
138     u32 state;
139 
140     /* initialization for graphics (et al.) */
141     REXDEMOKPadInit();
142     REXDEMOInitScreen( FALSE );
143 
144     REXDEMOUsage( "Push (A) to startup/cleanup socket.\n"
145                   "Push (B) to terminate and restart.\n\n" );
146 
147 restart:
148     REXDEMOReport( "Initialize network features...\n" );
149 
150     InitNetwork();
151     state = STATE_OFFLINE;
152     REPORT_STATE( state );
153 
154     /* Main loop */
155     /* The main loop operates at 60 frames (for NTSC) due to the relocation of blocking functions, such as SOStartup and SOCleanup, to a separate thread.
156         */
157     while ( TRUE )
158     {
159         s32 event;
160 
161         REXDEMOKPadRead();
162 
163         /*************** Input-driven state transitions  ****************/
164 
165         /* (A) button */
166         /* Calls the SOStartup function if there is no network connection; calls the SOCleanup function if there is a network connection. */
167         /*    */
168         /* Do nothing during a state transition. */
169         if ( REXDEMOGetAnyMixedPadTrigger() & (KPAD_BUTTON_A | (PAD_BUTTON_A << 16)) )
170         {
171             switch ( state )
172             {
173             case STATE_OFFLINE:
174             case STATE_ERROR:
175                 /* Begin network connection processing in a separate thread */
176                 state = STATE_SOSTARTUP_IN_PROGRESS;
177                 REPORT_STATE( state );
178 
179                 /* send a start request */
180                 SendCommand( NETWORK_COMMAND_STARTUP );
181 
182                 break;
183 
184             case STATE_ONLINE:
185                 /* Begin network termination processing in a separate thread */
186                 state = STATE_SOCLEANUP_IN_PROGRESS;
187                 REPORT_STATE( state );
188 
189                 /* send a termination request */
190                 SendCommand( NETWORK_COMMAND_CLEANUP );
191 
192                 break;
193 
194             case STATE_SOSTARTUP_IN_PROGRESS:
195             case STATE_SOCLEANUP_IN_PROGRESS:
196                 /* in the middle of a state transition, so do nothing */
197                 break;
198 
199             case STATE_TERMINATING:
200             case STATE_TERMINATED:
201                 /* in the middle of termination processing, so do nothing */
202                 break;
203 
204             default:
205                 REXDEMOError( "Unknown state: %d\n", state );
206             }
207         }
208 
209         /* (B) button */
210         /* Exits the main loop immediately if there is no network connection; calls the SOCleanup function if there is a network connection. */
211         /*   */
212         /* Set up disconnection processing, even during network connection processing */
213         if ( REXDEMOGetAnyMixedPadTrigger() & (KPAD_BUTTON_B | (PAD_BUTTON_B << 16)) )
214         {
215             switch ( state )
216             {
217             case STATE_OFFLINE:
218             case STATE_SOCLEANUP_IN_PROGRESS:
219             case STATE_ERROR:
220                 /* exit the main loop */
221                 state = STATE_TERMINATED;
222                 REPORT_STATE( state );
223                 break;
224 
225             case STATE_SOSTARTUP_IN_PROGRESS:
226             case STATE_ONLINE:
227                 /* Begin network termination processing in a separate thread */
228                 state = STATE_TERMINATING;
229                 REPORT_STATE( state );
230 
231                 /* send a termination request */
232                 SendCommand( NETWORK_COMMAND_CLEANUP );
233 
234                 break;
235 
236             case STATE_TERMINATING:
237             case STATE_TERMINATED:
238                 /* in the middle of termination processing, so do nothing */
239                 break;
240 
241             default:
242                 REXDEMOError( "Unknown state: %d\n", state );
243             }
244         }
245 
246         /* Home button */
247         if ( REXDEMOGetAnyMixedPadTrigger() & (KPAD_BUTTON_HOME | (PAD_BUTTON_START << 16)) )
248         {
249             /* Return to the menu */
250             SafeSOFinish(); /* termination processing for the SO library */
251             OSReturnToMenu();
252         }
253 
254 
255         /*************** Event-driven state transitions ****************/
256 
257         event = ReceiveNetworkEvent();
258         switch ( event )
259         {
260         case NETWORK_EVENT_NONE:
261             break;
262 
263         case NETWORK_EVENT_STARTUP:
264             REXDEMOReport( "Received STARTUP event.\n" );
265             if ( state == STATE_SOSTARTUP_IN_PROGRESS )
266             {
267                 state = STATE_ONLINE;
268                 REPORT_STATE( state );
269             }
270             break;
271 
272         case NETWORK_EVENT_CLEANUP:
273             REXDEMOReport( "Received CLEANUP event.\n" );
274             if ( state == STATE_SOCLEANUP_IN_PROGRESS )
275             {
276                 state = STATE_OFFLINE;
277                 REPORT_STATE( state );
278             }
279             else if ( state == STATE_TERMINATING )
280             {
281                 state = STATE_TERMINATED;
282                 REPORT_STATE( state );
283             }
284             break;
285 
286         default:
287             if ( event < 0 )
288             {
289                 /* negative values directly indicate NET_ECODE_* */
290 
291                 state = STATE_ERROR;
292                 REPORT_STATE( state );
293 
294                 /**********************************/
295                 /** Error code display to the user **/
296                 /**********************************/
297                 REXDEMOError("Network Error Code is %d\n", -event);
298             }
299             else
300             {
301                 /* Unknown event */
302                 REXDEMOError("Unknown event: %d\n", event);
303             }
304         }
305 
306 
307         /*************** Screen display ****************/
308 
309         REXDEMOBeginRender();
310 
311         REXDEMOSetTextColor( REXDEMO_COLOR_RED );
312         REXDEMOPrintf( 464, 40, 0, "<<State>>" );
313         REXDEMOSetTextColor( REXDEMO_COLOR_ORANGE );
314         REXDEMOPrintf( 400, 60, 0, "%s", StateToString( state ) );
315 
316         REXDEMOSetTextColor( REXDEMO_COLOR_GRAY ); /* counter color */
317         REXDEMOWaitRetrace();
318 
319         if ( state == STATE_TERMINATED )
320         {
321             /* Exit the main loop when Cleanup has finished */
322             break;
323         }
324     }
325 
326     TerminateNetwork();
327 
328     REXDEMOReport( "All network features are terminated.\n" );
329 
330     REXDEMOWaitRetrace();
331 
332     goto restart;
333     return 0;
334 }
335 
336 /* Utility functions for displaying messages */
StateToString(u32 state)337 const char* StateToString( u32 state )
338 {
339     const char* strings[] = { "OFFLINE", "SOSTARTUP_IN_PROGRESS", "ONLINE", "SOCLEANUP_IN_PROGRESS", "TERMINATING", "TERMINATED", "ERROR" };
340     if ( state < sizeof(strings)/sizeof(strings[0]) )
341     {
342         return strings[state];
343     }
344     else
345     {
346         return "N/A";
347     }
348 }
349 
350 /*===========================================================================*/
351 
352 /* Network initialization processing */
InitNetwork(void)353 void InitNetwork( void )
354 {
355     s32 result;
356 
357     /* Create the heap to be given to the socket library */
358     if ( !CreateHeapForSocket( SOCKET_HEAP_SIZE ) )
359     {
360         REXDEMOError("CreateHeapForSocket() failed.\n");
361         OSHalt("failed");
362     }
363 
364     /* Initialize the socket library by specifying the SO allocator */
365     {
366         /* the SOInit argument can be an auto variable */
367         SOLibraryConfig libConfig = { AllocForSocket, FreeForSocket };
368         result = SOInit( &libConfig );
369         if ( result < SO_SUCCESS )
370         {
371             REXDEMOError("SOInit() failed.(%d)\n", result);
372             OSHalt("failed");
373         }
374     }
375 
376     /* Create a message queue for issuing commands to the network thread */
377     OSInitMessageQueue( &NetworkCommandQueue, NetworkCommandQueueMessages, NETWORK_COMMAND_QUEUE_SIZE );
378 
379     /* Create a message queue for receiving events from the network thread */
380     OSInitMessageQueue( &NetworkEventQueue, NetworkEventQueueMessages, NETWORK_EVENT_QUEUE_SIZE );
381 
382     /* Create a network thread */
383     (void)OSCreateThread(
384         &NetworkThread,
385         NetworkHandler,
386         0,
387         NetworkThreadStack + sizeof(NetworkThreadStack),
388         sizeof(NetworkThreadStack),
389         NETWORK_THREAD_PRIORITY,
390         0);
391 
392     /* start the network thread */
393     (void)OSResumeThread(&NetworkThread);
394 }
395 
396 /* termination processing for network functions */
TerminateNetwork(void)397 void TerminateNetwork( void )
398 {
399     /* send a termination request to the network thread */
400     SendCommand( NETWORK_COMMAND_KILL_THREAD );
401     (void) OSJoinThread( &NetworkThread, NULL );
402 
403     /* Deallocate the socket library resource */
404     SafeSOFinish();
405 
406     /* deallocate the heap given to the socket library */
407     DestroyHeapForSocket();
408 }
409 
410 /* the SO library will safely terminate in any state */
SafeSOFinish(void)411 void SafeSOFinish( void )
412 {
413     s32 iRetry;
414     s32 result;
415 
416     for ( iRetry = NETWORK_TERMINATION_RETRY_LIMIT; iRetry > 0; iRetry-- )
417     {
418         result = SOFinish();
419         if ( result == SO_EBUSY || result == SO_EAGAIN || result == SO_EINPROGRESS )
420         {
421             s32 r;
422             REXDEMOError("SOFinish() failed.(%d)\n", result);
423 
424             /* This is a state in which SOFinish cannot be called. Try SOCleanup. */
425             REXDEMOReport( "Try SOCleanup()...\n" );
426             r = SOCleanup();
427             if ( r < SO_SUCCESS )
428             {
429                 REXDEMOError("SOCleanup() failed.(%d)\n", r);
430             }
431             /* wait for other processes to release their network-related resources */
432             OSSleepMilliseconds( NETWORK_TERMINATION_RETRY_WAIT );
433         }
434         else
435         {
436             break;
437         }
438     }
439     if ( result == SO_EALREADY )
440     {
441         /* either SOFinish has already been called, or SOInit was not called in the first place */
442         REXDEMOError("SOFinish() already done. (%d)\n", result);
443     }
444     else if ( result < SO_SUCCESS )
445     {
446         /* an unexpected error in SOFinish that should not occur */
447         /* error processing is required */
448         REXDEMOError("SOFinish() failed.(%d)\n", result);
449         OSHalt("failed");
450     }
451     if ( iRetry <= 0 )
452     {
453         /* The retry limit was reached */
454         /* Error processing is required because network termination could not be carried out properly. */
455         REXDEMOError("Too many network termination retries; give up to call SOFinish().\n");
456         OSHalt("failed");
457     }
458 }
459 
460 /* function for sending commands to the network thread (blocking)*/
SendCommand(u32 command)461 void SendCommand( u32 command )
462 {
463     (void)OSSendMessage( &NetworkCommandQueue, (OSMessage)command, OS_MESSAGE_BLOCK );
464 }
465 
466 /*===========================================================================*/
467 
468 /* thread for SOStartup and SOCleanup processing */
NetworkHandler(void * param)469 void* NetworkHandler( void* param )
470 {
471 #pragma unused( param )
472     BOOL fExit = FALSE;
473 
474     while ( fExit == FALSE )
475     {
476         OSMessage msg;
477         u32 command;
478         s32 result;
479 
480         (void)OSReceiveMessage( &NetworkCommandQueue, &msg, OS_MESSAGE_BLOCK );
481         command = (u32)msg;
482 
483         switch ( command )
484         {
485         case NETWORK_COMMAND_KILL_THREAD:
486             /* make the thread terminate */
487             fExit = TRUE;
488             break;
489 
490         case NETWORK_COMMAND_STARTUP:
491             /* Start the network feature of the socket library. */
492 
493             /* Determine if valid network settings are saved before calling SOStartup. */
494 
495             /* It is acceptable to skip this procedure and call the SOStartup function. */
496             /* This has been provided primarily to quickly check for the existence of network settings before entering the network processing sequence. */
497             /*  */
498 
499             /* The NCDHasEnabledNetworkConfig function may block for more than a few seconds. */
500             if ( !NCDHasEnabledNetworkConfig() )
501             {
502                 REXDEMOError("NCDHasEnabledNetworkConfig() returns FALSE.\n");
503                 /* Display a 50200 error */
504                 SendNetworkEvent(NET_ECODE_NO_ENABLED_CONFIG);
505                 break;
506             }
507 
508             /* The SOCleanup function may block for more than a few seconds. */
509             result = SOStartup();
510 
511             if ( result >= SO_SUCCESS )
512             {
513                 /* SOStartup succeeded */
514                 SendNetworkEvent( NETWORK_EVENT_STARTUP );
515             }
516             else if ( result == SO_EBUSY )
517             {
518                 /* Processing is not currently possible, for reasons such as: SOCleanup is running. */
519                 REXDEMOError("Network stack is busy. (%d)\n", result);
520             }
521             else if ( result == SO_EALREADY )
522             {
523                 /* SOStartup has already run */
524                 REXDEMOError("SOStartup() already done. (%d)\n", result);
525             }
526             else
527             {
528                 s32 errcode;
529 
530                 /* other error states */
531                 REXDEMOError("SOStartup() failed.(%d)\n", result);
532 
533                 /* For display purposes, get the error code and notify the main thread. */
534                 errcode = NETGetStartupErrorCode(result);
535                 SendNetworkEvent( errcode );
536             }
537             break;
538 
539         case NETWORK_COMMAND_CLEANUP:
540             /* Stop the network function of the socket library */
541 
542             /* The SOCleanup function may block for more than a few seconds. */
543             result = SOCleanup();
544 
545             if ( result >= SO_SUCCESS )
546             {
547                 /* SOCleanup success */
548                 SendNetworkEvent( NETWORK_EVENT_CLEANUP );
549             }
550             else if ( result == SO_EBUSY )
551             {
552                 /* Processing is not currently possible, for reasons such as: SOStartup is running. */
553                 REXDEMOError("Network stack is busy. (%d)\n", result);
554             }
555             else if ( result == SO_EALREADY )
556             {
557                 /* SOCleanup has already run */
558                 REXDEMOError("SOCleanup() already done. (%d)\n", result);
559             }
560             else
561             {
562                 REXDEMOError("SOCleanup() failed.(%d)\n", result);
563                 /* Proceed as is because even if there is a failure, nothing can be done */
564                 SendNetworkEvent( NETWORK_EVENT_CLEANUP );
565             }
566             break;
567 
568         default:
569             /* unknown command */
570             REXDEMOError("Unknown command: %d\n", command );
571         }
572     }
573 
574     return NULL;
575 }
576 
577 /* Function for sending network events (blocking)*/
SendNetworkEvent(s32 event)578 void SendNetworkEvent( s32 event )
579 {
580     (void)OSSendMessage( &NetworkEventQueue, (OSMessage)event, OS_MESSAGE_BLOCK );
581 }
582 
583 /*===========================================================================*/
584 
585 /* Function for receiving network events (non-blocking)*/
ReceiveNetworkEvent(void)586 s32 ReceiveNetworkEvent( void )
587 {
588     OSMessage msg;
589     if ( OSReceiveMessage( &NetworkEventQueue, &msg, OS_MESSAGE_NOBLOCK ) )
590     {
591         return (s32)msg;
592     }
593     else
594     {
595         /* no events have occurred */
596         return NETWORK_EVENT_NONE;
597     }
598 }
599 
600 /*===========================================================================*/
601 
602 /* Memory allocation function for the SO library */
AllocForSocket(u32 name,s32 size)603 void* AllocForSocket( u32 name, s32 size )
604 {
605 #pragma unused( name )
606     void* ptr;
607 
608     if (0 < size)
609     {
610         /* This operation is thread-safe because the MEM_HEAP_OPT_THREAD_SAFE option has been specified in the MEMCreateExpHeapEx function. */
611         /*  */
612         ptr = MEMAllocFromExpHeapEx( heapHandleSocket, (u32) size, 32 );
613         return ptr;
614     }
615     else
616     {
617         return NULL;
618     }
619 }
620 
621 /* memory deallocation function for the SO library */
FreeForSocket(u32 name,void * ptr,s32 size)622 void FreeForSocket( u32 name, void* ptr, s32 size )
623 {
624 #pragma unused( name )
625 
626     if (ptr && 0 < size)
627     {
628         /* This operation is thread-safe because the MEM_HEAP_OPT_THREAD_SAFE option has been specified in the MEMCreateExpHeapEx function. */
629         /*  */
630         MEMFreeToExpHeap( heapHandleSocket, ptr );
631     }
632 }
633 
634 /* create the heap region for the SO library */
CreateHeapForSocket(u32 size)635 BOOL CreateHeapForSocket( u32 size )
636 {
637     void*            arenaLo;
638     void*            arenaHi;
639 
640     /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
641     /* !!! The heap for the SO library MUST be allocated from MEM2!!! */
642     /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
643     arenaLo = OSGetMEM2ArenaLo();
644     arenaHi = OSGetMEM2ArenaHi();
645 
646     if ((u32) arenaHi - (u32) arenaLo < size)
647     {
648         return FALSE;
649     }
650     heapHandleSocket = MEMCreateExpHeapEx( arenaLo, size, MEM_HEAP_OPT_THREAD_SAFE );
651     if (heapHandleSocket == MEM_HEAP_INVALID_HANDLE)
652     {
653         OSReport("MEMCreateExpHeapEx failed.\n");
654         return FALSE;
655     }
656 
657     OSSetMEM2ArenaLo((u8*)arenaLo + size);
658     return TRUE;
659 }
660 
661 /* destroy the heap region for the SO library */
DestroyHeapForSocket(void)662 void DestroyHeapForSocket( void )
663 {
664     s32   heapSize;
665     void* heapPtr;
666 
667     if ( heapHandleSocket == NULL )
668         return;
669 
670     heapSize = MEMGetHeapTotalSize(heapHandleSocket);
671     heapPtr  = MEMDestroyExpHeap(heapHandleSocket);
672 
673     if ( heapPtr != NULL )
674     {
675         if ( (u32)OSGetMEM2ArenaLo() == (u32)heapPtr + heapSize )
676         {
677             OSSetMEM2ArenaLo(heapPtr);
678         }
679     }
680     heapHandleSocket = NULL;
681 
682     return;
683 }
684 
685 
686 /*---------------------------------------------------------------------------*
687   End of file
688  *---------------------------------------------------------------------------*/
689