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