1 /*---------------------------------------------------------------------------*
2 Project: NitroSDK - WFS - libraries
3 File: wfs_server.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/wfs/server.h>
17
18
19 /*---------------------------------------------------------------------------*/
20 /* Functions */
21
22 /*---------------------------------------------------------------------------*
23 Name: WFSi_NotifySegmentEvent
24
25 Description: Notifies the caller of a WFS_EVENT_SERVER_SEGMENT_REQUEST event.
26
27 Arguments: context: The WFSServerContext structure.
28 argument The event argument.
29
30 Returns: None.
31 *---------------------------------------------------------------------------*/
WFSi_NotifySegmentEvent(WFSServerContext * context,void * argument)32 static void WFSi_NotifySegmentEvent(WFSServerContext *context, void *argument)
33 {
34 if (context->thread_work)
35 {
36 (*context->thread_hook)(context->thread_work, argument);
37 }
38 else if (context->callback)
39 {
40 (*context->callback)(context, WFS_EVENT_SERVER_SEGMENT_REQUEST, argument);
41 }
42 }
43
44 /*---------------------------------------------------------------------------*
45 Name: WFSi_WBTCallback
46
47 Description: Server-side WBT event notification.
48
49 Arguments: userdata The WFSServerContext structure.
50 uc The WBT event argument.
51
52 Returns: None.
53 *---------------------------------------------------------------------------*/
WFSi_WBTCallback(void * userdata,WBTCommand * uc)54 static void WFSi_WBTCallback(void *userdata, WBTCommand *uc)
55 {
56 WFSServerContext *work = (WFSServerContext *)userdata;
57 const int aid = (int)MATH_CTZ(uc->peer_bmp);
58
59 switch (uc->command)
60 {
61
62 default:
63 WFS_DEBUG_OUTPUT(("invalid WBT callback (command = %d)", uc->command));
64 break;
65
66 case WBT_CMD_REQ_USER_DATA:
67 /* Confirm whether the message has been sent to everyone */
68 if (!uc->target_bmp)
69 {
70 work->msg_busy = FALSE;
71 }
72 break;
73
74 case WBT_CMD_SYSTEM_CALLBACK:
75 /* Indication */
76
77 switch (uc->event)
78 {
79
80 default:
81 WFS_DEBUG_OUTPUT(("unknown WBT system callback (event = %d)", uc->event));
82 break;
83
84 case WBT_CMD_REQ_SYNC:
85 case WBT_CMD_REQ_GET_BLOCK_DONE:
86 break;
87
88 case WBT_CMD_REQ_USER_DATA:
89 /* Message receipt notification */
90 {
91 /* Save the received message */
92 const WFSMessageFormat *const msg = (const WFSMessageFormat *)uc->user_data.data;
93 work->recv_msg[aid] = *msg;
94
95 switch (msg->type)
96 {
97
98 case WFS_MSG_LOCK_REQ:
99 /* Read lock request */
100 {
101 const u32 offset = MI_LEToH32(msg->arg1);
102 const u32 length = MI_LEToH32_BITFIELD(24, msg->arg2);
103 const int packet = (int)((msg->packet_hi << 8) | msg->packet_lo);
104
105 WFS_DEBUG_OUTPUT(("WBT-MSG(LOCK):recv [offset=0x%08X, length=0x%08X] (AID=%d, packet=%d)",
106 offset, length, aid, packet));
107 /* Deny response when the packet size is changing */
108 if (work->is_changing ||
109 (packet != WBT_GetParentPacketLength(work->wbt) + WBT_PACKET_SIZE_MIN))
110 {
111 work->is_changing = TRUE;
112 work->deny_bitmap |= (1 << aid);
113 }
114 /* If there is a match with an existing lock region, reference it */
115 else
116 {
117 WFSLockInfo *file = NULL;
118 int index;
119 for (index = 0; (1 << index) <= work->use_bitmap; ++index)
120 {
121 if(((1 << index) & work->use_bitmap) != 0)
122 {
123 if ((work->list[index].offset == offset) &&
124 (work->list[index].length == length))
125 {
126 file = &work->list[index];
127 ++file->ref;
128 break;
129 }
130
131 }
132
133 }
134 /* If a new lock region, allocate from the available list */
135 if (!file)
136 {
137 index = (int)MATH_CTZ((u32)~work->use_bitmap);
138 if (index < WFS_LOCK_HANDLE_MAX)
139 {
140 PLATFORM_ENTER_CRITICALSECTION();
141 work->use_bitmap |= (1 << index);
142 file = &work->list[index];
143 file->ref = 1;
144 file->offset = offset;
145 file->length = length;
146 /* Transfer to the registered list as it is a new file */
147 WBT_RegisterBlockInfo(work->wbt, &file->info,
148 (u32)(WFS_LOCKED_BLOCK_INDEX + index),
149 NULL, NULL, (int)file->length);
150 file->ack_seq = 0;
151 PLATFORM_LEAVE_CRITICALSECTION();
152 /* Request to read just the header in advance */
153 {
154 WFSSegmentBuffer segment[1];
155 segment->offset = file->offset;
156 segment->length = (u32)WBT_GetParentPacketLength(work->wbt);
157 segment->buffer = NULL;
158 WFSi_NotifySegmentEvent(work, segment);
159 }
160 }
161 else
162 {
163 OS_TPanic("internal-error (no available lock handles)");
164 }
165 }
166 work->ack_bitmap |= (1 << aid);
167 work->recv_msg[aid].arg1 = MI_HToLE32((u32)(WFS_LOCKED_BLOCK_INDEX + index));
168 }
169 work->busy_bitmap |= (1 << aid);
170 }
171 break;
172
173 case WFS_MSG_UNLOCK_REQ:
174 /* CLOSEFILE request */
175 {
176 PLATFORM_ENTER_CRITICALSECTION();
177 u32 id = MI_LEToH32(msg->arg1);
178 u32 index = id - WFS_LOCKED_BLOCK_INDEX;
179 if (index < WFS_LOCK_HANDLE_MAX)
180 {
181 WFSLockInfo *file = &work->list[index];
182 /* Deallocate process when all references are gone */
183 if (--file->ref <= 0)
184 {
185 (void)WBT_UnregisterBlockInfo(work->wbt, id);
186 work->use_bitmap &= ~(1 << index);
187 }
188 }
189 work->ack_bitmap |= (1 << aid);
190 PLATFORM_LEAVE_CRITICALSECTION();
191 WFS_DEBUG_OUTPUT(("WBT-MSG(UNLOCK):recv [id=0x%08X] (AID=%d)", id, aid));
192 }
193 break;
194
195 }
196 }
197 break;
198
199 case WBT_CMD_PREPARE_SEND_DATA:
200 /* GETBLOCK buffer preparation request */
201 {
202 WBTPrepareSendDataCallback *const p_prep = &uc->prepare_send_data;
203 u32 id = p_prep->block_id;
204 p_prep->data_ptr = NULL;
205 /* Response when a valid file */
206 id -= WFS_LOCKED_BLOCK_INDEX;
207 if (id < WFS_LOCK_HANDLE_MAX)
208 {
209 WFSLockInfo *file = &work->list[id];
210 /* Current response to the previous request, next response for current request */
211 WFSSegmentBuffer segment[1];
212 const u32 length = p_prep->own_packet_size;
213 const u32 current = file->ack_seq;
214 const u32 next = (u32)p_prep->block_seq_no;
215 file->ack_seq = next;
216 /* Preparation for next time */
217 segment->offset = file->offset + length * next;
218 segment->length = MATH_MIN(length, file->length - length * next);
219 segment->buffer = NULL;
220 WFSi_NotifySegmentEvent(work, segment);
221 /* Current request */
222 segment->offset = file->offset + length * current;
223 segment->length = MATH_MIN(length, file->length - length * current);
224 segment->buffer = work->cache_hit_buf;
225 WFSi_NotifySegmentEvent(work, segment);
226 if (segment->buffer != NULL)
227 {
228 p_prep->block_seq_no = (s32)current;
229 p_prep->data_ptr = segment->buffer;
230 }
231 }
232 }
233 break;
234
235 }
236 break;
237 }
238 }
239
240 /*---------------------------------------------------------------------------*
241 Name: WFS_CallServerConnectHook
242
243 Description: Server-side connection notification.
244
245 Arguments: context: The WFSServerContext structure.
246 peer: Information for the connected client.
247
248 Returns: None.
249 *---------------------------------------------------------------------------*/
WFS_CallServerConnectHook(WFSServerContext * context,const WFSPeerInfo * peer)250 void WFS_CallServerConnectHook(WFSServerContext *context, const WFSPeerInfo *peer)
251 {
252 /*
253 * Because it is not necessarily true that all clients are using WFS, we ignore connection notifications here and instead treat PacketRecv as a connection notification
254 *
255 */
256 (void)context;
257 (void)peer;
258 }
259
260 /*---------------------------------------------------------------------------*
261 Name: WFS_CallServerDisconnectHook
262
263 Description: Disconnect notification on the server side.
264
265 Arguments: context: The WFSServerContext structure.
266 peer: Information for the disconnected client.
267
268 Returns: None.
269 *---------------------------------------------------------------------------*/
WFS_CallServerDisconnectHook(WFSServerContext * context,const WFSPeerInfo * peer)270 void WFS_CallServerDisconnectHook(WFSServerContext *context, const WFSPeerInfo *peer)
271 {
272 const int bit = (1 << peer->aid);
273 context->all_bitmap &= ~bit;
274 (void)WBT_CancelCommand(context->wbt, bit);
275 }
276
277 /*---------------------------------------------------------------------------*
278 Name: WFS_CallServerPacketSendHook
279
280 Description: Notification of timing when it is possible to send packets on the server side.
281
282 Arguments: context: The WFSServerContext structure.
283 packet: Send packet settings.
284
285 Returns: None.
286 *---------------------------------------------------------------------------*/
WFS_CallServerPacketSendHook(WFSServerContext * context,WFSPacketBuffer * packet)287 void WFS_CallServerPacketSendHook(WFSServerContext *context, WFSPacketBuffer *packet)
288 {
289 /* Restart the non-response process when it is possible to send a message */
290 if (!context->msg_busy)
291 {
292 /* Show the current valid communications state in each type of bitmap */
293 context->ack_bitmap &= context->all_bitmap;
294 context->sync_bitmap &= context->all_bitmap;
295 context->busy_bitmap &= context->all_bitmap;
296 context->deny_bitmap &= context->all_bitmap;
297 /* Wait to change packet size until all processes being received are complete */
298 if (context->is_changing && !context->use_bitmap)
299 {
300 /* Change packet size */
301 context->is_changing = FALSE;
302 (void)WBT_SetPacketLength(context->wbt, context->new_packet, WBT_PACKET_SIZE_MIN);
303 /* Overall denial response for requests with the old size */
304 if (context->deny_bitmap)
305 {
306 WFS_DEBUG_OUTPUT(("WBT-MSG(LOCK):deny [packet-length renewal] (BITMAP=%d)", context->deny_bitmap));
307 (void)WFS_SendMessageLOCK_ACK(context->wbt, WFSi_WBTCallback, context->deny_bitmap, 0);
308 context->msg_busy = TRUE;
309 context->deny_bitmap = 0;
310 }
311 }
312 /* Corresponds as-is to the requests received before the packet size change */
313 else if (context->ack_bitmap)
314 {
315 int bitmap = context->ack_bitmap;
316 WFSMessageFormat *msg = NULL;
317 int i;
318 const int sync = context->sync_bitmap;
319 const BOOL is_sync = (sync && ((bitmap & sync) == sync));
320 /* Respond all at once if there is a uniform child device group that received the synchronous designation */
321 if (is_sync)
322 {
323 bitmap = sync;
324 }
325 /* Otherwise, respond as normal */
326 else
327 {
328 bitmap &= ~sync;
329 }
330 /* Search for relevant child devices that can respond */
331 for (i = 0;; ++i)
332 {
333 const int bit = (1 << i);
334 if (bit > bitmap)
335 {
336 break;
337 }
338 if ((bit & bitmap) != 0)
339 {
340 /* Search for child devices that can respond in order from the lowest aid */
341 if (!msg)
342 {
343 msg = &context->recv_msg[i];
344 }
345 /* Send all at once if same type of response */
346 else if ((msg->type == context->recv_msg[i].type) &&
347 (msg->arg1 == context->recv_msg[i].arg1))
348 {
349 }
350 /* If not, then hold this time for the child device */
351 else
352 {
353 bitmap &= ~bit;
354 }
355 }
356 }
357 /*
358 * NOTE:
359 * When an application-side bug occurs such that there are differing request contents even though synchronization has been specified, it is not possible to determine (1) whether some synchronization contents are simply out of synch, (2) and if so, which request should be responded to first in order to recover, or (3) whether there was in fact a fatal difference in the request contents to begin with. Since this cannot be judged, recovery is performed by automatically revoking the synchronization specification while continually issuing a warning.
360 *
361 *
362 *
363 *
364 *
365 *
366 *
367 */
368 if (is_sync && (bitmap != sync))
369 {
370 context->sync_bitmap = 0;
371 OS_TWarning("[WFS] specified synchronous-access failed! "
372 "(then synchronous-setting was reset)");
373 }
374 /* Send the response selected this time */
375 if (msg)
376 {
377 switch (msg->type)
378 {
379 case WFS_MSG_LOCK_REQ:
380 (void)WFS_SendMessageLOCK_ACK(context->wbt, WFSi_WBTCallback, bitmap,
381 MI_LEToH32(msg->arg1));
382 break;
383 case WFS_MSG_UNLOCK_REQ:
384 (void)WFS_SendMessageUNLOCK_ACK(context->wbt, WFSi_WBTCallback, bitmap,
385 MI_LEToH32(msg->arg1));
386 context->busy_bitmap &= ~bitmap;
387 break;
388 }
389 context->msg_busy = TRUE;
390 context->ack_bitmap &= ~bitmap;
391 }
392 }
393 }
394
395 /* Call WBT after updating all the latest statuses */
396 packet->length = WBT_CallPacketSendHook(context->wbt, packet->buffer, packet->length, TRUE);
397 }
398
399 /*---------------------------------------------------------------------------*
400 Name: WFS_CallServerPacketRecvHook
401
402 Description: Server-side packet receipt notification.
403
404 Arguments: context: The WFSServerContext structure.
405 packet: Sender packet information.
406
407 Returns: None.
408 *---------------------------------------------------------------------------*/
WFS_CallServerPacketRecvHook(WFSServerContext * context,const WFSPacketBuffer * packet)409 void WFS_CallServerPacketRecvHook(WFSServerContext *context, const WFSPacketBuffer *packet)
410 {
411 int aid = (int)MATH_CTZ((u32)packet->bitmap);
412 const void *buffer = packet->buffer;
413 int length = packet->length;
414 /*
415 * Detect a connection using actual packet receipt from a client to prevent impact on unrelated clients from unneeded port communication from the parent.
416 *
417 */
418 context->all_bitmap |= (1 << aid);
419 WBT_CallPacketRecvHook(context->wbt, aid, buffer, length);
420 }
421
422 /*---------------------------------------------------------------------------*
423 Name: WFS_InitServer
424
425 Description: Initializes the WFS server context.
426
427 Arguments: context: The WFSServerContext structure.
428 userdata: Any user-defined value associated with the context.
429 callback: The system event notification callback.
430 Specify NULL if not needed.
431 allocator: The allocator used internally.
432 packet: The parent's initial packet size.
433
434 Returns: None.
435 *---------------------------------------------------------------------------*/
WFS_InitServer(WFSServerContext * context,void * userdata,WFSEventCallback callback,MIAllocator * allocator,int packet)436 void WFS_InitServer(WFSServerContext *context,
437 void *userdata, WFSEventCallback callback,
438 MIAllocator *allocator, int packet)
439 {
440 /* Initialize the basic settings */
441 MI_CpuClear8(context, sizeof(*context));
442 context->userdata = userdata;
443 context->callback = callback;
444 context->allocator = allocator;
445
446 /* Initialize the internal status */
447 context->new_packet = packet;
448 context->table->buffer = NULL;
449 context->table->length = 0;
450 context->sync_bitmap = 0;
451 context->ack_bitmap = 0;
452 context->msg_busy = FALSE;
453 context->all_bitmap = 1;
454 context->busy_bitmap = 0;
455 context->is_changing = FALSE;
456 context->deny_bitmap = 0;
457 context->use_bitmap = 0;
458 context->thread_work = NULL;
459 context->thread_hook = NULL;
460
461 /* Initialize WBT */
462 WBT_InitContext(context->wbt, context, WFSi_WBTCallback);
463 WBT_AddCommandPool(context->wbt, context->wbt_list,
464 sizeof(context->wbt_list) / sizeof(*context->wbt_list));
465 WBT_StartParent(context->wbt, packet, WBT_PACKET_SIZE_MIN);
466 }
467
468 /*---------------------------------------------------------------------------*
469 Name: WFS_EndServer
470
471 Description: Deallocates the WFS server context.
472
473 Arguments: context: The WFSServerContext structure.
474
475 Returns: None.
476 *---------------------------------------------------------------------------*/
WFS_EndServer(WFSServerContext * context)477 void WFS_EndServer(WFSServerContext *context)
478 {
479 if (context->thread_work)
480 {
481 (*context->thread_hook)(context->thread_work, NULL);
482 MI_CallFree(context->allocator, context->thread_work);
483 context->thread_work = NULL;
484 }
485 WBT_ResetContext(context->wbt, NULL);
486 if (context->table->buffer)
487 {
488 MI_CallFree(context->allocator, context->table->buffer);
489 context->table->buffer = NULL;
490 context->table->length = 0;
491 }
492 }
493
494 /*---------------------------------------------------------------------------*
495 Name: WFS_RegisterServerTable
496
497 Description: Loads the ROM file table from the device and registers it to the server.
498
499 Arguments: context: The WFSServerContext structure.
500 device: The device storing the NTR binary.
501 fatbase: The offset within the device wherein the NTR binary is located.
502 overlay: The device-internal offset where the NTR binary containing the overlay to be merged is located.
503
504 (If not merging multiple ROM file tables, this value is identical to "fatbase")
505
506
507 Returns: TRUE when the ROM file table is correctly loaded and registered.
508 *---------------------------------------------------------------------------*/
WFS_RegisterServerTable(WFSServerContext * context,MIDevice * device,u32 fatbase,u32 overlay)509 BOOL WFS_RegisterServerTable(WFSServerContext *context,
510 MIDevice *device, u32 fatbase, u32 overlay)
511 {
512 BOOL retval = FALSE;
513 /* Multiple file tables cannot be registered. */
514 if (context->table->buffer)
515 {
516 OS_TWarning("table is already registered.\n");
517 }
518 /* Load the ROM file table from the device */
519 else if (WFS_LoadTable(context->table, context->allocator, device, fatbase, overlay))
520 {
521 /* Register the ROM file table */
522 WBT_RegisterBlockInfo(context->wbt, context->table_info,
523 WFS_TABLE_BLOCK_INDEX, NULL,
524 context->table->buffer,
525 (int)context->table->length);
526 retval = TRUE;
527 }
528 return retval;
529 }
530
531 /*---------------------------------------------------------------------------*
532 Name: WFS_GetServerPacketLength
533
534 Description: Obtain the size of packets sent by the server.
535
536 Arguments: context: The WFSServerContext structure.
537
538 Returns: The currently specified packet size.
539 *---------------------------------------------------------------------------*/
WFS_GetServerPacketLength(const WFSServerContext * context)540 int WFS_GetServerPacketLength(const WFSServerContext *context)
541 {
542 return context->new_packet;
543 }
544
545 /*---------------------------------------------------------------------------*
546 Name: WFS_SetServerPacketLength
547
548 Description: Sets the size of the server send packet.
549
550 Arguments: context: The WFSServerContext structure.
551 length: The packet size to be set.
552
553 Returns: None.
554 *---------------------------------------------------------------------------*/
WFS_SetServerPacketLength(WFSServerContext * context,int length)555 void WFS_SetServerPacketLength(WFSServerContext *context, int length)
556 {
557 SDK_ASSERT(length >= WBT_PACKET_SIZE_MIN);
558 {
559 PLATFORM_ENTER_CRITICALSECTION();
560 context->new_packet = length;
561 context->is_changing = TRUE;
562 PLATFORM_LEAVE_CRITICALSECTION();
563 }
564 }
565
566 /*---------------------------------------------------------------------------*
567 Name: WFS_SetServerSync
568
569 Description: Sets the clients that synchronously access the server.
570 This function achieves efficient transmission rates that utilities unique characteristics of the WBT library; this is done by synchronizing responses to clients that are all clearly guaranteed to access the same files in precisely the same order.
571
572
573 However, be cautious of the fact that if the synchronization start timing is not logically safe, responses will become out-of-sync, causing deadlocks to occur.
574
575
576 Arguments: context: The WFSServerContext structure.
577 bitmap: The AID bitmap for the client to be synchronized.
578 Synchronization will not occur when 0 (the default value) is specified.
579 The last bit is always ignored.
580
581 Returns: None.
582 *---------------------------------------------------------------------------*/
WFS_SetServerSync(WFSServerContext * context,int bitmap)583 void WFS_SetServerSync(WFSServerContext *context, int bitmap)
584 {
585 PLATFORM_ENTER_CRITICALSECTION();
586 context->sync_bitmap = (bitmap & ~1);
587 PLATFORM_LEAVE_CRITICALSECTION();
588 }
589
590
591 /*---------------------------------------------------------------------------*
592 $Log: wfs_server.c,v $
593 Revision 1.6 2007/10/04 05:36:31 yosizaki
594 Fix related to WFS_EndServer.
595
596 Revision 1.5 2007/06/14 13:15:27 yosizaki
597 Added hook to a thread-proc.
598
599 Revision 1.4 2007/06/11 10:23:32 yosizaki
600 Minor fixes.
601
602 Revision 1.3 2007/06/11 06:40:00 yosizaki
603 Added WFS_GetServerPacketLength().
604
605 Revision 1.2 2007/04/17 00:01:06 yosizaki
606 Renamed some structures.
607
608 Revision 1.1 2007/04/13 04:12:37 yosizaki
609 Initial upload.
610
611 $NoKeywords: $
612 *---------------------------------------------------------------------------*/
613