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