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