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