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