1 /*---------------------------------------------------------------------------*
2   Project:  NitroSDK - WFS - libraries
3   File:     wfs_client.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/client.h>
17 
18 
19 /*---------------------------------------------------------------------------*/
20 /* Functions */
21 
22 /*---------------------------------------------------------------------------*
23   Name:         WFSi_NotifyEvent
24 
25   Description:  Notifies for events within the WFS server.
26 
27   Arguments:    context: The WFSClientContext structure.
28                 event: The event type for the notification.
29                 argument: The event argument.
30 
31   Returns:      None.
32  *---------------------------------------------------------------------------*/
WFSi_NotifyEvent(WFSClientContext * context,WFSEventType event,void * argument)33 inline static void WFSi_NotifyEvent(WFSClientContext *context,
34                                     WFSEventType event, void *argument)
35 {
36     if (context->callback)
37     {
38         context->callback(context, event, argument);
39     }
40 }
41 
42 /*---------------------------------------------------------------------------*
43   Name:         WFSi_ReallocBitmap
44 
45   Description:  Reallocates a bitmap capable of receiving the specified size.
46 
47   Arguments:    context: The WFSClientContext structure.
48                 length: The anticipated file size to be received.
49                                  Specifying a negative value will reallocate with the current value.
50 
51   Returns:      None.
52  *---------------------------------------------------------------------------*/
WFSi_ReallocBitmap(WFSClientContext * context,int length)53 static void WFSi_ReallocBitmap(WFSClientContext *context, int length)
54 {
55     if (length < 0)
56     {
57         length = (int)context->max_file_size;
58         context->max_file_size = 0;
59     }
60     if (context->max_file_size < length)
61     {
62         const int packet = WBT_GetParentPacketLength(context->wbt);
63         const u32 newsize = WBT_PACKET_BITMAP_SIZE((u32)length, packet);
64         context->max_file_size = (u32)length;
65         MI_CallFree(context->allocator, context->recv_pkt_bmp_buf);
66         context->recv_pkt_bmp_buf = (u32 *)MI_CallAlloc(context->allocator,
67                                     sizeof(u32) * newsize, sizeof(u32));
68         if (context->recv_pkt_bmp_buf == NULL)
69         {
70             OS_TPanic("cannot allocate bitmap buffer %d BYTEs!", sizeof(u32) * newsize);
71         }
72         context->recv_buf_packet_bmp_table.packet_bitmap[0] = context->recv_pkt_bmp_buf;
73     }
74     SDK_ASSERT(context->recv_pkt_bmp_buf);
75 }
76 
77 /*---------------------------------------------------------------------------*
78   Name:         WFSi_ReceiveTableSequence
79 
80   Description:  The sequence that receives the ROM file table.
81                 Can only be run once immediately after connecting to the server.
82 
83   Arguments:    userdata: The WFSClientContext structure.
84                 callback: The WBT completion callback argument.
85                                  Specify NULL for the call when at the start of the sequence.
86 
87   Returns:      None.
88  *---------------------------------------------------------------------------*/
WFSi_ReceiveTableSequence(void * userdata,WBTCommand * callback)89 static void WFSi_ReceiveTableSequence(void *userdata, WBTCommand *callback)
90 {
91     WFSClientContext * const    context = (WFSClientContext *)userdata;
92     WBTContext * const          wbt = context->wbt;
93 
94     if ((callback == NULL) || (callback->result == WBT_RESULT_SUCCESS))
95     {
96         BOOL    post = FALSE;
97         const int   bitmap = 0x0001;
98         /* Sequence start => issue WBT-SYNC() and synchronize the packet size */
99         if (callback == NULL)
100         {
101             WFS_DEBUG_OUTPUT(("WBT-SYNC():post"));
102             post = WBT_PostCommandSYNC(wbt, bitmap, WFSi_ReceiveTableSequence);
103         }
104         /* WBT-SYNC completion => issue WBT-INFO(0) and get the size of the ROM file table */
105         else if (callback->event == WBT_CMD_RES_SYNC)
106         {
107             WFS_DEBUG_OUTPUT(("WBT-SYNC():done [server = %d, client = %d]",
108                              callback->sync.peer_packet_size + WBT_PACKET_SIZE_MIN,
109                              callback->sync.my_packet_size + WBT_PACKET_SIZE_MIN));
110             WFS_DEBUG_OUTPUT(("WBT-INFO(0):post"));
111             post = WBT_PostCommandINFO(wbt, bitmap, WFSi_ReceiveTableSequence,
112                                        0, &context->block_info_table);
113         }
114         /* WBT-INFO(0) completion => issue WBT-GET(0x20000) and get the size of the ROM file table */
115         else if (callback->event == WBT_CMD_RES_GET_BLOCKINFO)
116         {
117             const int length = context->block_info_table.block_info[0]->block_size;
118             WFS_DEBUG_OUTPUT(("WBT-INFO(0):done [table-length = %d]", length));
119             context->table->length = (u32)length;
120             context->table->buffer = (u8 *)MI_CallAlloc(context->allocator, (u32)length, 1);
121             if (context->table->buffer == NULL)
122             {
123                 OS_TPanic("cannot allocate FAT buffer %d BYTEs!", length);
124             }
125             WFSi_ReallocBitmap(context, length);
126             context->recv_buf_table.recv_buf[0] = context->table->buffer;
127             WFS_DEBUG_OUTPUT(("WBT-GET(0x%08X):post", WFS_TABLE_BLOCK_INDEX));
128             post = WBT_PostCommandGET(wbt, bitmap, WFSi_ReceiveTableSequence,
129                                       WFS_TABLE_BLOCK_INDEX, context->table->length,
130                                       &context->recv_buf_table,
131                                       &context->recv_buf_packet_bmp_table);
132         }
133         /* WBT-GET(0x20000) completion => mount preparations complete event notification */
134         else if (callback->event == WBT_CMD_RES_GET_BLOCK)
135         {
136             WFS_DEBUG_OUTPUT(("WBT-GET(0x%08X):done [ready for mount]", WFS_TABLE_BLOCK_INDEX));
137             WFS_ParseTable(context->table);
138             context->fat_ready = TRUE;
139             WFSi_NotifyEvent(context, WFS_EVENT_CLIENT_READY, NULL);
140             post = TRUE;    /* As a matter of convenience */
141         }
142         /* WBT command issue failure (insufficient command queue resulting from an internal WFS problem) */
143         if (!post)
144         {
145             WFS_DEBUG_OUTPUT(("internal-error (failed to post WBT command)"));
146         }
147     }
148     /* Some kind of internal error */
149      else
150     {
151         /* Cancelled during the WFS start up (no need to do anything special here) */
152         if (callback->event == WBT_CMD_CANCEL)
153         {
154         }
155         /* An unexpected WBT error (a state management problem occurred as a result of an internal WFS problem) */
156         else
157         {
158             WFS_DEBUG_OUTPUT(("internal-error (unexpected WBT result)"));
159             WFS_DEBUG_OUTPUT(("  command = %d", callback->command));
160             WFS_DEBUG_OUTPUT(("  event   = %d", callback->event));
161             WFS_DEBUG_OUTPUT(("  result  = %d", callback->result));
162         }
163     }
164 }
165 
166 /*---------------------------------------------------------------------------*
167   Name:         WFSi_ReadRomSequence
168 
169   Description:  The sequence that receives the server-side ROM image.
170                 Run every time the FS_ReadFile function is called from the client.
171 
172   Arguments:    userdata: The WFSClientContext structure.
173                 callback: The WBT completion callback argument.
174                                  Specify NULL for the call when at the start of the sequence.
175 
176   Returns:      None.
177  *---------------------------------------------------------------------------*/
WFSi_ReadRomSequence(void * userdata,WBTCommand * callback)178 static void WFSi_ReadRomSequence(void *userdata, WBTCommand *callback)
179 {
180     WFSClientContext * const    context = (WFSClientContext *)userdata;
181     WBTContext * const          wbt = context->wbt;
182 
183     if ((callback == NULL) || (callback->result == WBT_RESULT_SUCCESS))
184     {
185         BOOL    post = FALSE;
186         const int   bitmap = 0x0001;
187         /* Sequence start => issue WBT-MSG(LOCK) and lock the transfer range */
188         if (callback == NULL)
189         {
190             WFS_DEBUG_OUTPUT(("WBT-MSG(LOCK):post"));
191             post = WFS_SendMessageLOCK_REQ(wbt, WFSi_ReadRomSequence, bitmap,
192                                            context->request_region.offset + context->table->origin,
193                                            context->request_region.length);
194         }
195         /* WBT-MSG() issued (wait for the server response; no need to do anything here) */
196         else if (callback->event == WBT_CMD_RES_USER_DATA)
197         {
198             /* waiting for response from server... */
199             post = TRUE;    /* As a matter of convenience */
200         }
201         else
202         {
203             const WFSMessageFormat *const msg = (const WFSMessageFormat *)callback->user_data.data;
204             /* WBT-MSG(LOCK) response received */
205             if ((callback->event == WBT_CMD_REQ_USER_DATA) &&
206                 (msg->type == WFS_MSG_LOCK_ACK))
207             {
208                 BOOL    accepted = (BOOL)MI_LEToH32(msg->arg2);
209                 /* Denial => reissue WBT-SYNC() and synchronize the packet size */
210                 if (!accepted)
211                 {
212                     WFS_DEBUG_OUTPUT(("WBT-MSG(LOCK):failed [packet-length renewal]"));
213                     WFS_DEBUG_OUTPUT(("WBT-SYNC():post"));
214                     post = WBT_PostCommandSYNC(wbt, bitmap, WFSi_ReadRomSequence);
215                 }
216                 /* Permission => reissue WBT-GET(id) and receive blocks */
217                 else
218                 {
219                     u32     id = MI_LEToH32(msg->arg1);
220                     WFS_DEBUG_OUTPUT(("WBT-MSG(LOCK):done [lock-id = 0x%08X]", id));
221                     context->block_id = id;
222                     context->recv_buf_table.recv_buf[0] = context->request_buffer;
223                     WFS_DEBUG_OUTPUT(("WBT-GET(0x%08X):post", id));
224                     post = WBT_PostCommandGET(wbt, bitmap, WFSi_ReadRomSequence,
225                                               context->block_id, context->request_region.length,
226                                               &context->recv_buf_table,
227                                               &context->recv_buf_packet_bmp_table);
228                 }
229             }
230             /* WBT-SYNC() completion => retry the sequence (will only be a single level recursive call) */
231             else if (callback->event == WBT_CMD_RES_SYNC)
232             {
233                 WFS_DEBUG_OUTPUT(("WBT-SYNC():done [server = %d, client = %d]",
234                                  callback->sync.peer_packet_size + WBT_PACKET_SIZE_MIN,
235                                  callback->sync.my_packet_size + WBT_PACKET_SIZE_MIN));
236                 WFSi_ReallocBitmap(context, -1);
237                 WFSi_ReadRomSequence(context, NULL);
238                 post = TRUE;    /* As a matter of convenience */
239             }
240             /* WBT-GET(id) completion => issue WBT-MSG(UNLOCK, id) and deallocate the transfer range */
241             else if (callback->event == WBT_CMD_RES_GET_BLOCK)
242             {
243                 u32     id = context->block_id;
244                 WFS_DEBUG_OUTPUT(("WBT-GET(0x%08X):done", id));
245                 WFS_DEBUG_OUTPUT(("WBT-MSG(UNLOCK,0x%08X):post", id));
246                 post = WFS_SendMessageUNLOCK_REQ(wbt, WFSi_ReadRomSequence, bitmap, id);
247             }
248             /* WBT-MSG(UNLOCK, id) response received => read complete event notification */
249             else if ((callback->event == WBT_CMD_REQ_USER_DATA) &&
250                 (msg->type == WFS_MSG_UNLOCK_ACK))
251             {
252                 WFS_DEBUG_OUTPUT(("WBT-MSG(UNLOCK,0x%08X):done [read-operation completed]", context->block_id));
253                 context->request_buffer = NULL;
254                 {
255                     WFSRequestClientReadDoneCallback callback = context->request_callback;
256                     void   *argument = context->request_argument;
257                     context->request_callback = NULL;
258                     context->request_argument = NULL;
259                     if (callback)
260                     {
261                         (*callback)(context, TRUE, argument);
262                     }
263                 }
264                 post = TRUE;    /* As a matter of convenience */
265             }
266         }
267         /* WBT command issue failure (insufficient command queue resulting from an internal WFS problem) */
268         if (!post)
269         {
270             WFS_DEBUG_OUTPUT(("internal-error (failed to post WBT command)"));
271         }
272     }
273     /* Some kind of internal error */
274      else
275     {
276         /* Cancelled during the WFS read process  (no need to do anything special here) */
277         if (callback->event == WBT_CMD_CANCEL)
278         {
279         }
280         /* An unexpected WBT error (a state management problem occurred as a result of an internal WFS problem) */
281         else
282         {
283             WFS_DEBUG_OUTPUT(("internal-error (unexpected WBT result)"));
284             WFS_DEBUG_OUTPUT(("  command = %d", callback->command));
285             WFS_DEBUG_OUTPUT(("  event   = %d", callback->event));
286             WFS_DEBUG_OUTPUT(("  result  = %d", callback->result));
287         }
288     }
289 }
290 
291 /*---------------------------------------------------------------------------*
292   Name:         WFSi_WBTSystemCallback
293 
294   Description:  The client-side WBT system callback.
295 
296   Arguments:    userdata: The WFSServerContext structure.
297                 callback: The WBT event argument.
298 
299   Returns:      None.
300  *---------------------------------------------------------------------------*/
WFSi_WBTSystemCallback(void * userdata,WBTCommand * callback)301 static void WFSi_WBTSystemCallback(void *userdata, WBTCommand *callback)
302 {
303     WFSClientContext * const context = (WFSClientContext *)userdata;
304     /* The response from the server is redirected to WFSi_ReadRomSequence */
305     if ((callback->event == WBT_CMD_REQ_USER_DATA) &&
306         (context->request_buffer))
307     {
308         WFSi_ReadRomSequence(context, callback);
309     }
310 }
311 
312 /*---------------------------------------------------------------------------*
313   Name:         WFS_CallClientConnectHook
314 
315   Description:  Connection notification on the client side.
316 
317   Arguments:    context: The WFSClientContext structure.
318                 peer: Information for the connected communication peer.
319 
320   Returns:      None.
321  *---------------------------------------------------------------------------*/
WFS_CallClientConnectHook(WFSClientContext * context,const WFSPeerInfo * peer)322 void WFS_CallClientConnectHook(WFSClientContext *context, const WFSPeerInfo *peer)
323 {
324     (void)context;
325     (void)peer;
326 }
327 
328 /*---------------------------------------------------------------------------*
329   Name:         WFS_CallClientDisconnectHook
330 
331   Description:  Disconnect notification on the client side.
332 
333   Arguments:    context: The WFSClientContext structure.
334                 peer: Information for the disconnected communication peer.
335 
336   Returns:      None.
337  *---------------------------------------------------------------------------*/
WFS_CallClientDisconnectHook(WFSClientContext * context,const WFSPeerInfo * peer)338 void WFS_CallClientDisconnectHook(WFSClientContext *context, const WFSPeerInfo *peer)
339 {
340     (void)context;
341     (void)peer;
342 }
343 
344 /*---------------------------------------------------------------------------*
345   Name:         WFS_CallClientPacketSendHook
346 
347   Description:  Notification of timing when it is possible to send packets on the client side.
348 
349   Arguments:    context: The WFSClientContext structure.
350                 packet: Send packet settings
351 
352   Returns:      The actual packet size.
353  *---------------------------------------------------------------------------*/
WFS_CallClientPacketSendHook(WFSClientContext * context,WFSPacketBuffer * packet)354 void WFS_CallClientPacketSendHook(WFSClientContext *context, WFSPacketBuffer *packet)
355 {
356     packet->length = WBT_CallPacketSendHook(context->wbt, packet->buffer, packet->length, FALSE);
357 }
358 
359 /*---------------------------------------------------------------------------*
360   Name:         WFS_CallClientPacketRecvHook
361 
362   Description:  Notification of timing when it is possible to receive packets on the client side.
363 
364   Arguments:    context: The WFSClientContext structure
365                 packet: Sender packet information
366 
367   Returns:      The actual packet size.
368  *---------------------------------------------------------------------------*/
WFS_CallClientPacketRecvHook(WFSClientContext * context,const WFSPacketBuffer * packet)369 void WFS_CallClientPacketRecvHook(WFSClientContext *context, const WFSPacketBuffer *packet)
370 {
371     int aid = (int)MATH_CTZ((u32)packet->bitmap);
372     WBT_CallPacketRecvHook(context->wbt, aid, packet->buffer, packet->length);
373 }
374 
375 /*---------------------------------------------------------------------------*
376   Name:         WFS_InitClient
377 
378   Description:  Initializes the WFS client context.
379 
380   Arguments:    context: The WFSClientContext structure
381                 userdata: Any user-defined value associated with the context
382                 callback: The system event notification callback
383                                  Specify NULL if not needed.
384                 allocator: The allocator used internally
385 
386   Returns:      None.
387  *---------------------------------------------------------------------------*/
WFS_InitClient(WFSClientContext * context,void * userdata,WFSEventCallback callback,MIAllocator * allocator)388 void WFS_InitClient(WFSClientContext *context,
389                     void *userdata, WFSEventCallback callback,
390                     MIAllocator *allocator)
391 {
392     int     i;
393     context->userdata = userdata;
394     context->callback = callback;
395     context->allocator = allocator;
396     context->fat_ready = FALSE;
397     /* Initialize WBT variables */
398     for (i = 0; i < WBT_NUM_OF_AID; ++i)
399     {
400         context->block_info_table.block_info[i] = &context->block_info[i];
401         context->recv_buf_table.recv_buf[i] = NULL;
402         context->recv_buf_packet_bmp_table.packet_bitmap[i] = NULL;
403     }
404     context->recv_pkt_bmp_buf = NULL;
405     context->max_file_size = 0;
406     context->block_id = 0;
407     context->request_buffer = NULL;
408     context->table->length = 0;
409     context->table->buffer = NULL;
410     context->unmount_callback = NULL;
411     /* Initialize WBT */
412     WBT_InitContext(context->wbt, context, WFSi_WBTSystemCallback);
413     WBT_AddCommandPool(context->wbt, context->wbt_list,
414                        sizeof(context->wbt_list) / sizeof(*context->wbt_list));
415 }
416 
417 /*---------------------------------------------------------------------------*
418   Name:         WFS_EndClient
419 
420   Description:  Deallocates the WFS client context.
421 
422   Arguments:    context: The WFSClientContext structure
423 
424   Returns:      None.
425  *---------------------------------------------------------------------------*/
WFS_EndClient(WFSClientContext * context)426 void WFS_EndClient(WFSClientContext *context)
427 {
428     MI_CallFree(context->allocator, context->recv_pkt_bmp_buf);
429     WBT_ResetContext(context->wbt, NULL);
430     if (context->table->buffer)
431     {
432         MI_CallFree(context->allocator, context->table->buffer);
433         context->table->buffer = NULL;
434     }
435     if (context->request_callback)
436     {
437         (*context->request_callback)(context->request_argument, FALSE, context->request_argument);
438     }
439     if (context->unmount_callback)
440     {
441         (*context->unmount_callback)(context);
442     }
443 }
444 
445 /*---------------------------------------------------------------------------*
446   Name:         WFS_StartClient
447 
448   Description:  Starts the WFS client context communication.
449 
450   Arguments:    context: The WFSClientContext structure
451                 peer: The local host's connection information.
452 
453   Returns:      None.
454  *---------------------------------------------------------------------------*/
WFS_StartClient(WFSClientContext * context,const WFSPeerInfo * peer)455 void WFS_StartClient(WFSClientContext *context, const WFSPeerInfo *peer)
456 {
457     WBT_StartChild(context->wbt, peer->aid);
458     WFSi_ReceiveTableSequence(context, NULL);
459 }
460 
461 /*---------------------------------------------------------------------------*
462   Name:         WFS_RequestClientRead
463 
464   Description:  Begins a ROM image read request from the server.
465                 When complete, a WFS_EVENT_CLIENT_READ notification occurs.
466 
467   Arguments:    context: The WFSClientContext structure
468                 buffer: Memory where the load data is stored
469                 offset: Starting position for the device load
470                 length: The load size.
471                 callback: Load completion callback
472                                  NULL if not necessary.
473                 arg: Argument passed to the load completion callback
474   Returns:      None.
475  *---------------------------------------------------------------------------*/
WFS_RequestClientRead(WFSClientContext * context,void * buffer,u32 offset,u32 length,WFSRequestClientReadDoneCallback callback,void * arg)476 void WFS_RequestClientRead(WFSClientContext *context, void *buffer, u32 offset,
477                            u32 length, WFSRequestClientReadDoneCallback callback,
478                            void *arg)
479 {
480     if (context->fat_ready)
481     {
482         context->request_buffer = buffer;
483         context->request_region.offset = offset;
484         context->request_region.length = length;
485         context->request_callback = callback;
486         context->request_argument = arg;
487         WFSi_ReallocBitmap(context, (int)length);
488         WFSi_ReadRomSequence(context, NULL);
489     }
490 }
491 
492 /*---------------------------------------------------------------------------*
493   Name:         WFS_GetClientReadProgress
494 
495   Description:  Gets the progress of the ROM image read request.
496 
497   Arguments:    context: The WFSClientContext structure
498                 current: Variable that gets the number of received packets
499                 total: Variable that gets the total expected number of packets
500 
501   Returns:      None.
502  *---------------------------------------------------------------------------*/
WFS_GetClientReadProgress(WFSClientContext * context,int * current,int * total)503 void WFS_GetClientReadProgress(WFSClientContext *context,int *current, int *total)
504 {
505     WBT_GetDownloadProgress(context->wbt, context->block_id, 0, current, total);
506 }
507 
508 
509 /*---------------------------------------------------------------------------*
510   $Log: wfs_client.c,v $
511   Revision 1.2  2007/06/11 06:39:24  yosizaki
512   Changed WFS_RequestClientRead().
513 
514   Revision 1.1  2007/04/13 04:12:37  yosizaki
515   Initial upload.
516 
517   $NoKeywords: $
518  *---------------------------------------------------------------------------*/
519