1 /*---------------------------------------------------------------------------*
2   Project:  NitroSDK - WFS - libraries
3   File:     wfs_thread.c
4 
5   Copyright 2007-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  *---------------------------------------------------------------------------*/
14 
15 
16 #include <nitro.h>
17 
18 
19 /*---------------------------------------------------------------------------*/
20 /* Constants */
21 
22 /*
23  * Various ROM cache setting values.
24  * [Page size]
25  * - An integer multiple of the ROM page size (512 bytes) is desirable.
26  * - A value that is too small will increase the ROM access overhead.
27  * - A value that is too large will cause server ROM accesses (FS/SND/...) to become blocked at irregular intervals.
28  * [Page count]
29  * - If large, this will contribute to stabilizing transfer efficiency (the responsiveness of resend processing in restricted communication environments).
30  * - A value larger than the total number of clients is desirable if access locality is high.
31  */
32 #define	WFS_FILE_CACHE_SIZE	    1024UL
33 #define WFS_CACHE_PAGE_TOTAL    8
34 
35 
36 /*---------------------------------------------------------------------------*/
37 /* Declarations */
38 
39 typedef struct WFSServerThread
40 {
41     WFSEventCallback    hook;
42     MIDevice            device[1];
43     FSFile              file[1];
44     MICache             cache[1];
45     u8                  cache_buf[MI_CACHE_BUFFER_WORKSIZE(
46                                   WFS_FILE_CACHE_SIZE, WFS_CACHE_PAGE_TOTAL)];
47     OSMessageQueue      msg_queue[1];
48     OSMessage           msg_array[1];
49     OSThread            thread[1];
50     u8                  thread_stack[0x400];
51 }
52 WFSServerThread;
53 
54 
55 /*---------------------------------------------------------------------------*/
56 /* Functions */
57 
58 /*---------------------------------------------------------------------------*
59   Name:         WFSi_ReadRomCallback
60 
61   Description:  ROM device read callback
62 
63   Arguments:    userdata         WFSServerThread structure
64                 buffer           The transfer target memory.
65                 offset           The transfer source offset.
66                 length           The transfer size.
67 
68   Returns:      Returns a value of 0 or higher if successful; a negative number otherwise.
69  *---------------------------------------------------------------------------*/
WFSi_ReadRomCallback(void * userdata,void * buffer,u32 offset,u32 length)70 static int WFSi_ReadRomCallback(void *userdata, void *buffer, u32 offset, u32 length)
71 {
72     WFSServerThread * const thread = (WFSServerThread*)userdata;
73 
74     /* NITRO-SDK specific: Read from memory because the first 32 KB cannot be accessed. */
75     if(offset < sizeof(CARDRomRegion))
76     {
77         const u8 *header = (const u8 *)CARD_GetOwnRomHeader();
78         if (length > sizeof(CARDRomHeader) - offset)
79         {
80             length = sizeof(CARDRomHeader) - offset;
81         }
82         MI_CpuCopy8(header + offset, buffer, length);
83     }
84 #if defined(SDK_TWL)
85     // Unique to TWL-SDK:  Do not send data from the TWL-exclusive region to the network carelessly.
86     else if (OS_IsRunOnTwl() && ((((const u8 *)HW_TWL_ROM_HEADER_BUF)[0x1C] & 0x01) != 0) &&
87              ((offset >= CARD_GetOwnRomHeader()->rom_size) ||
88              (offset + length > CARD_GetOwnRomHeader()->rom_size)))
89     {
90         MI_CpuFill8(buffer, 0x00, length);
91     }
92 #endif
93     else
94     {
95         if (offset < 0x8000)
96         {
97             u32     memlen = length;
98             if (memlen > 0x8000 - offset)
99             {
100                 memlen = 0x8000 - offset;
101             }
102             MI_CpuFill8(buffer, 0x00, length);
103             offset += memlen;
104             length -= memlen;
105         }
106         if (length > 0)
107         {
108             (void)FS_SeekFile(thread->file, (int)offset, FS_SEEK_SET);
109             (void)FS_ReadFile(thread->file, buffer, (int)length);
110         }
111     }
112     return (int)length;
113 }
114 
115 /*---------------------------------------------------------------------------*
116   Name:         WFSi_WriteRomCallback
117 
118   Description:  ROM device write callback (dummy)
119 
120   Arguments:    userdata         WFSServerThread structure
121                 buffer           The transfer target memory.
122                 offset           The transfer source offset.
123                 length           The transfer size.
124 
125   Returns:      Returns a value of 0 or higher if successful; a negative number otherwise.
126  *---------------------------------------------------------------------------*/
WFSi_WriteRomCallback(void * userdata,const void * buffer,u32 offset,u32 length)127 static int WFSi_WriteRomCallback(void *userdata, const void *buffer, u32 offset, u32 length)
128 {
129     (void)userdata;
130     (void)buffer;
131     (void)offset;
132     (void)length;
133     return -1;
134 }
135 
136 /*---------------------------------------------------------------------------*
137   Name:         WFSi_ServerThreadProc
138 
139   Description:  Thread procedure for the WFS server.
140 
141   Arguments:    arg             WFSServerThread structure.
142 
143   Returns:      None.
144  *---------------------------------------------------------------------------*/
WFSi_ServerThreadProc(void * arg)145 static void WFSi_ServerThreadProc(void *arg)
146 {
147     WFSServerThread *const thread = (WFSServerThread*)arg;
148     for (;;)
149     {
150         BOOL    busy = FALSE;
151         (void)OS_ReceiveMessage(thread->msg_queue, (OSMessage*)&busy, OS_MESSAGE_BLOCK);
152         if (!busy)
153         {
154             break;
155         }
156         MI_LoadCache(thread->cache, thread->device);
157     }
158 }
159 
160 /*---------------------------------------------------------------------------*
161   Name:         WFSi_ThreadHook
162 
163   Description:  A hook function set in the WFS server segment callback.
164 
165   Arguments:    work            WFSServerThread structure
166                 argument        WFSSegmentBuffer structure
167                                 Completion notification if NULL.
168 
169   Returns:      None.
170  *---------------------------------------------------------------------------*/
WFSi_ThreadHook(void * work,void * argument)171 static void WFSi_ThreadHook(void *work, void *argument)
172 {
173     WFSServerThread * const thread = (WFSServerThread *)work;
174     WFSSegmentBuffer * const segment = (WFSSegmentBuffer*)argument;
175     /* Completion notification if NULL */
176     if (!segment)
177     {
178         (void)OS_SendMessage(thread->msg_queue, (OSMessage)FALSE, OS_MESSAGE_BLOCK);
179         OS_JoinThread(thread->thread);
180         (void)FS_CloseFile(thread->file);
181     }
182     /* Segment request (or notification of preparation) if not NULL */
183     else if (!MI_ReadCache(thread->cache, segment->buffer, segment->offset, segment->length))
184     {
185         /* Instruct thread to read in case of a cache miss */
186         segment->buffer = NULL; /* == "could not prepare immediately" */
187         // NOBLOCK, because it is acceptable for notifications not to line up in the queue if the same ones have accumulated.
188         (void)OS_SendMessage(thread->msg_queue, (OSMessage)TRUE, OS_MESSAGE_NOBLOCK);
189     }
190 }
191 
192 /*---------------------------------------------------------------------------*
193   Name:         WFS_ExecuteRomServerThread
194 
195   Description:  Registers the specified ROM file to the WFS library so that it will be distributed, and internally automatically starts up the ROM access thread.
196 
197                 These threads are automatically destroyed in the WFS_EndServer function.
198 
199   Arguments:    context:          The WFSServerContext structure.
200                 file:             The SRL file that contains the file system to be distributed.
201                                  Specify NULL for clone boot.
202                 sharedFS:         TRUE if the file system is to be shared by the parent and child.
203                                  In such a case, a combined file system is distributed. For this combined file system, only the overlay held by 'file' is extracted and appended to the file system of the parent device itself.
204 
205 
206                                  If 'file' is set to NULL, it specifies clone boot, so this argument is ignored. (It is interpreted as always TRUE.)
207 
208 
209 
210   Returns:      TRUE if ROM file registration and thread creation succeed.
211  *---------------------------------------------------------------------------*/
WFS_ExecuteRomServerThread(WFSServerContext * context,FSFile * file,BOOL sharedFS)212 BOOL WFS_ExecuteRomServerThread(WFSServerContext *context, FSFile *file, BOOL sharedFS)
213 {
214     BOOL    retval = FALSE;
215 
216     WFSServerThread *thread = MI_CallAlloc(context->allocator, sizeof(WFSServerThread), 32);
217     if (!thread)
218     {
219         OS_TWarning("failed to allocate memory enough to create internal thread.");
220     }
221     else
222     {
223         /* Determine whether this is single ROM mode, clone boot mode, or shared FS mode */
224         u32     pos = file ? (FS_GetFileImageTop(file) + FS_GetFilePosition(file)) : 0;
225         u32     fatbase = (u32)((file && !sharedFS) ? pos : 0);
226         u32     overlay = (u32)(file ? pos : 0);
227         /* Initialize the ROM access device and cache */
228         FS_InitFile(thread->file);
229         if (!FS_CreateFileFromRom(thread->file, 0, 0x7FFFFFFF))
230         {
231             OS_TPanic("failed to create ROM-file!");
232         }
233         MI_InitDevice(thread->device, thread,
234                       WFSi_ReadRomCallback, WFSi_WriteRomCallback);
235         MI_InitCache(thread->cache, WFS_FILE_CACHE_SIZE,
236                      thread->cache_buf, sizeof(thread->cache_buf));
237         /* Register file table */
238         if (WFS_RegisterServerTable(context, thread->device, fatbase, overlay))
239         {
240             /* Specify a hook and start a thread if the process was successful */
241             context->thread_work = thread;
242             context->thread_hook = WFSi_ThreadHook;
243             OS_InitMessageQueue(thread->msg_queue, thread->msg_array, 1);
244             OS_CreateThread(thread->thread, WFSi_ServerThreadProc, thread,
245                             thread->thread_stack + sizeof(thread->thread_stack),
246                             sizeof(thread->thread_stack), 15);
247             OS_WakeupThreadDirect(thread->thread);
248             retval = TRUE;
249         }
250         else
251         {
252             MI_CallFree(context->allocator, thread);
253         }
254     }
255     return retval;
256 }
257 
258 
259 /*---------------------------------------------------------------------------*
260   $Log: wfs_thread.c,v $
261   Revision 1.1  2007/06/14 13:14:46 PM  yosizaki
262   Initial upload.
263 
264   $NoKeywords: $
265  *---------------------------------------------------------------------------*/
266