1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - WXC - libraries -
3   File:     wxc_protocol_impl_common.c.c
4 
5   Copyright 2006-2009 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   $Date:: 2008-12-16#$
14   $Rev: 9661 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 
18 #include <nitro.h>
19 
20 #include <nitro/wxc/common.h>
21 #include <nitro/wxc/protocol.h>
22 
23 
24 
25 /*****************************************************************************/
26 /* Declaration */
27 
28 /* Protocol beacon format structure of shared chance encounter communications */
29 typedef struct WXCCommonProtocolBeaconFormat
30 {
31     u32     send_length;    /* Send data size (fixed for each game) */
32 }
33 WXCCommonProtocolBeaconFormat;
34 
35 
36 /* SSID format structure used when connecting with the common chance encounter communication protocol */
37 typedef struct WXCCommonProtocolSsidFormat
38 {
39     char    header[4];      /* Version information "WXC1" */
40     u32     ggid;           /* GGID to declare from child device */
41 }
42 WXCCommonProtocolSsidFormat;
43 
44 
45 /* Normal command packet format */
46 typedef struct PacketCommandFormat
47 {
48     u16     segment:1;  /* always 0 */
49     u16     ack:1;
50     u16     turn:1;
51     u16     phase:5;
52     u16     checksum:8;
53     u16     recv_arg;
54     u16     send_arg;
55 }
56 PacketCommandFormat;
57 
58 /* Segment format for data block conversion */
59 typedef struct PacketSegmentFormat
60 {
61     u16     segment:1;  /* always 1 */
62     u16     recv_arg:15;
63     u16     turn:1;
64     u16     send_arg:15;
65     u8      buffer[0];
66 }
67 PacketSegmentFormat;
68 
69 /* Information structure for shared chance encounter communication protocol */
70 typedef struct WXCCommonProtocolImplContext
71 {
72     /* Beacon send/receive configuration */
73     WXCCommonProtocolBeaconFormat beacon_format;
74     /* MP network communications configuration */
75     u32     parent_send;
76     u32     child_send;
77     BOOL    executing;
78     /* Protocol control */
79     int     req_phase;
80     u32     req_turn;
81     int     ack_phase;
82     /* Data conversion process */
83     u32     own_packet;
84     u32     peer_packet;
85     u32     req_index;
86     u32     ack_index;
87     u32     recv_count;
88     u32     recv_total;
89     u32     recv_bitset[WXC_MAX_DATA_SIZE / (8 * 4) + 1];
90 
91 //#define DEBUG_CALCSPEED
92 
93 #if defined(DEBUG_CALCSPEED)
94     /* Debug member for speed measurement */
95     OSTick  tick_count;
96 #endif
97 }
98 WXCCommonProtocolImplContext;
99 
100 static void WXCi_BeaconSendHook(WXCProtocolContext * protocol, WMParentParam *p_param);
101 static BOOL WXCi_BeaconRecvHook(WXCProtocolContext * protocol, const WMBssDesc *p_desc);
102 static void WXCi_PreConnectHook(WXCProtocolContext * protocol, const WMBssDesc *p_desc, u8 *ssid);
103 static void WXCi_PacketSendHook(WXCProtocolContext * protocol, WXCPacketInfo * packet);
104 static BOOL WXCi_PacketRecvHook(WXCProtocolContext * protocol, const WXCPacketInfo * packet);
105 static void WXCi_InitSequence(WXCProtocolContext * protocol, u16 send_max, u16 recv_max);
106 static BOOL WXCi_AddData(WXCProtocolContext * protocol, const void *send_buf, u32 send_size,
107                          void *recv_buf, u32 recv_max);
108 static BOOL WXCi_IsExecuting(WXCProtocolContext * protocol);
109 
110 
111 /*****************************************************************************/
112 /* Constants */
113 
114 /* State transition for data conversion protocol */
115 enum
116 {
117     PHASE_IDLE,
118     PHASE_QUIT,
119     PHASE_INIT,
120     PHASE_DATA,
121     PHASE_DONE
122 };
123 
124 
125 /*****************************************************************************/
126 /* Variables */
127 
128 /* Work buffer for WXC protocol */
129 static WXCCommonProtocolImplContext work_common ATTRIBUTE_ALIGN(32);
130 
131 /* WXC protocol interface */
132 static WXCProtocolImpl impl_common =
133 {
134     "COMMON",
135     WXCi_BeaconSendHook,
136     WXCi_BeaconRecvHook,
137     WXCi_PreConnectHook,
138     WXCi_PacketSendHook,
139     WXCi_PacketRecvHook,
140     WXCi_InitSequence,
141     WXCi_AddData,
142     WXCi_IsExecuting,
143     &work_common,
144 };
145 
146 
147 /*****************************************************************************/
148 /* Functions */
149 
150 /*---------------------------------------------------------------------------*
151   Name:         WXCi_BeaconSendHook
152 
153   Description:  Hook called at beacon update.
154 
155   Arguments:    protocol: WXCProtocolContext structure
156                 p_param: WMParentParam structure used for next beacon
157                          Change inside the function as necessary.
158 
159   Returns:      None.
160  *---------------------------------------------------------------------------*/
WXCi_BeaconSendHook(WXCProtocolContext * protocol,WMParentParam * p_param)161 void WXCi_BeaconSendHook(WXCProtocolContext * protocol, WMParentParam *p_param)
162 {
163     /*
164      * This hook is called only by normal game titles or relay points exclusively used in parent device mode
165      *
166      */
167     WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work;
168     WXCProtocolRegistry * const reg = WXC_GetCurrentBlock(protocol);
169 
170     work->beacon_format.send_length = reg->send.length;
171     p_param->userGameInfo = (u16*)&work->beacon_format;
172     p_param->userGameInfoLength = sizeof(work->beacon_format);
173 }
174 
175 /*---------------------------------------------------------------------------*
176   Name:         WXCi_BeaconRecvHook
177 
178   Description:  Hook called for individual scanned beacons.
179 
180   Arguments:    protocol: WXCProtocolContext structure
181                 p_desc: Scanned WMBssDesc structure
182 
183   Returns:      If it is seen as connection target, return TRUE. Otherwise, return FALSE.
184  *---------------------------------------------------------------------------*/
WXCi_BeaconRecvHook(WXCProtocolContext * protocol,const WMBssDesc * p_desc)185 BOOL WXCi_BeaconRecvHook(WXCProtocolContext * protocol, const WMBssDesc *p_desc)
186 {
187 #pragma unused(protocol)
188 #pragma unused(p_desc)
189     /*
190      * This hook is called only when a game title is in child device mode.
191      * A user callback is notified on relay points that use WXC_GGID_COMMON_ANY.
192      */
193     return TRUE;
194 }
195 
196 /*---------------------------------------------------------------------------*
197   Name:         WXCi_PreConnectHook
198 
199   Description:  Hook called prior to connection to network communication target.
200 
201   Arguments:    protocol: WXCProtocolContext structure
202                 p_desc: Connection target WMBssDesc structure
203                 ssid: Buffer that stores the SSID for configuration
204 
205   Returns:      None.
206  *---------------------------------------------------------------------------*/
WXCi_PreConnectHook(WXCProtocolContext * protocol,const WMBssDesc * p_desc,u8 ssid[WM_SIZE_CHILD_SSID])207 void WXCi_PreConnectHook(WXCProtocolContext * protocol, const WMBssDesc *p_desc,
208                          u8 ssid[WM_SIZE_CHILD_SSID])
209 {
210 #pragma unused(p_desc)
211     /*
212      * This hook is called only when a game title is in child device mode.
213      * A user callback is notified on relay points that use WXC_GGID_COMMON_ANY.
214      */
215     WXCCommonProtocolSsidFormat *p = (WXCCommonProtocolSsidFormat*)ssid;
216     WXCProtocolRegistry * const reg = WXC_GetCurrentBlock(protocol);
217     p->header[0] = 'W';
218     p->header[1] = 'X';
219     p->header[2] = 'C';
220     p->header[3] = '1';
221     p->ggid = reg->ggid;
222 }
223 
224 /*---------------------------------------------------------------------------*
225   Name:         WXCi_InitSequence
226 
227   Description:  Reinitializes the WXC library protocol.
228 
229   Arguments:    protocol: WXCProtocolContext structure
230                 send_max: Maximum send packet size
231                 recv_max: Maximum receive packet size
232 
233   Returns:      None.
234  *---------------------------------------------------------------------------*/
WXCi_InitSequence(WXCProtocolContext * protocol,u16 send_max,u16 recv_max)235 void WXCi_InitSequence(WXCProtocolContext * protocol, u16 send_max, u16 recv_max)
236 {
237     WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work;
238 
239     protocol->send.buffer = NULL;
240     protocol->send.length = 0;
241     protocol->send.buffer_max = 0;
242     protocol->send.checksum = 0;
243     protocol->recv.buffer = NULL;
244     protocol->recv.length = 0;
245     protocol->recv.buffer_max = 0;
246     protocol->recv.checksum = 0;
247 
248     work->parent_send = send_max;
249     work->child_send = recv_max;
250 
251     work->req_phase = PHASE_QUIT;
252     work->req_turn = 0;
253     work->ack_phase = PHASE_IDLE;
254 
255     work->executing = TRUE;
256 }
257 
258 /*---------------------------------------------------------------------------*
259   Name:         WXCi_AddData
260 
261   Description:  Configures the block data exchange.
262 
263   Arguments:    protocol: WXCProtocolContext structure
264                 send_buf: Send buffer
265                 send_size: Send buffer size
266                 recv_buf: Receive buffer
267                 recv_max: Receive buffer size
268 
269   Returns:      If registration is possible, return TRUE after configuration.
270  *---------------------------------------------------------------------------*/
WXCi_AddData(WXCProtocolContext * protocol,const void * send_buf,u32 send_size,void * recv_buf,u32 recv_max)271 BOOL WXCi_AddData(WXCProtocolContext * protocol, const void *send_buf, u32 send_size,
272                   void *recv_buf, u32 recv_max)
273 {
274     BOOL    retval = FALSE;
275 
276     WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work;
277 
278     if (work->req_phase == PHASE_QUIT)
279     {
280         retval = TRUE;
281         protocol->send.buffer = (void*)send_buf;
282         protocol->send.buffer_max = (u16)send_size;
283         protocol->send.length = (u16)send_size;
284         protocol->send.checksum = MATH_CalcChecksum8(send_buf, send_size);
285         protocol->recv.buffer = recv_buf;
286         protocol->recv.buffer_max = (u16)recv_max;
287         /* The receive size and checksum are finalized after data conversion */
288         work->req_phase = PHASE_INIT;
289         work->ack_phase = PHASE_IDLE;
290 #if defined(DEBUG_CALCSPEED)
291         if (OS_IsTickAvailable())
292         {
293             work->tick_count = OS_GetTick();
294         }
295 #endif
296 
297     }
298     return retval;
299 }
300 
301 /*---------------------------------------------------------------------------*
302   Name:         WXCi_IsExecuting
303 
304   Description:  Checks whether data is currently being exchanged.
305 
306   Arguments:    None.
307 
308   Returns:      Return TRUE if data is currently being exchanged.
309  *---------------------------------------------------------------------------*/
WXCi_IsExecuting(WXCProtocolContext * protocol)310 BOOL WXCi_IsExecuting(WXCProtocolContext * protocol)
311 {
312     WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work;
313     return work->executing;
314 }
315 
316 /*---------------------------------------------------------------------------*
317   Name:         WXCi_PacketSendHook
318 
319   Description:  Hook called at MP packet transmission.
320 
321   Arguments:    protocol: WXCProtocolContext structure
322                 packet: WXCPacketInfo pointer configuring transmission packet information
323 
324   Returns:      None.
325  *---------------------------------------------------------------------------*/
WXCi_PacketSendHook(WXCProtocolContext * protocol,WXCPacketInfo * packet)326 void WXCi_PacketSendHook(WXCProtocolContext * protocol, WXCPacketInfo * packet)
327 {
328     WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work;
329 
330     if ((work->req_phase == PHASE_DATA) || (work->req_phase == PHASE_DONE))
331     {
332         PacketSegmentFormat *format = (PacketSegmentFormat*)packet->buffer;
333         format->segment = TRUE;
334         format->turn = work->req_turn;
335         format->recv_arg = work->req_index;
336         format->send_arg = work->ack_index;
337         if (work->ack_index != 0x7FFF)
338         {
339             u32     segment = work->own_packet;
340             u32     offset = segment * work->ack_index;
341             u32     rest = protocol->send.length - offset;
342             MI_CpuCopy8((u8*)protocol->send.buffer + offset, format->buffer, MATH_MIN(segment, rest));
343         }
344     WXC_PACKET_LOG("--- [send] %d:F:%04X:%04X\n", format->turn, format->recv_arg, format->send_arg);
345     }
346     else
347     {
348         PacketCommandFormat *format = (PacketCommandFormat*)packet->buffer;
349         format->segment = FALSE;
350         format->ack = (u8)(work->ack_phase == work->req_phase);
351         format->turn = work->req_turn;
352         format->phase = (u8)work->req_phase;
353         format->checksum = (u8)protocol->send.checksum;
354         format->recv_arg = (u16)protocol->recv.buffer_max;
355         format->send_arg = (u16)protocol->send.length;
356     WXC_PACKET_LOG("--- [send] %d:%d:%04X:%04X\n", format->turn, format->phase, format->recv_arg, format->send_arg);
357     }
358 }
359 
360 /*---------------------------------------------------------------------------*
361   Name:         WXCi_PacketRecvHook
362 
363   Description:  Hook called at MP packet reception.
364 
365   Arguments:    protocol: WXCProtocolContext structure
366                 packet: WXCPacketInfo pointer configuring reception packet information
367 
368   Returns:      If a single data exchange is completed, return TRUE.
369  *---------------------------------------------------------------------------*/
WXCi_PacketRecvHook(WXCProtocolContext * protocol,const WXCPacketInfo * packet)370 BOOL WXCi_PacketRecvHook(WXCProtocolContext * protocol, const WXCPacketInfo * packet)
371 {
372     BOOL    retval = FALSE;
373 
374     WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work;
375 
376     PacketCommandFormat format;
377     BOOL    ack = FALSE;
378     BOOL    turn = !work->req_turn;
379     u32     recv_arg;
380     u32     send_arg;
381     u16     checksum = 0;
382 
383     /* First, analyze the partner's command */
384     if (packet->length < sizeof(format))
385     {
386         OS_TWarning("not enough length.\n");
387         return FALSE;
388     }
389     MI_CpuCopy8(packet->buffer, &format, sizeof(format));
390     /* Data segment packet */
391     if (format.segment)
392     {
393         PacketSegmentFormat segment;
394         MI_CpuCopy8(packet->buffer, &segment, sizeof(segment));
395         turn = segment.turn;
396         recv_arg = segment.recv_arg;
397         send_arg = segment.send_arg;
398         work->ack_phase = ((recv_arg == 0x7FFF) && (send_arg == 0x7FFF)) ?
399             PHASE_DONE : PHASE_DATA;
400         WXC_PACKET_LOG("---                            [recv] %d:F:%04X:%04X\n", segment.turn, segment.recv_arg, segment.send_arg);
401     }
402     else
403     {
404         work->ack_phase = format.phase;
405         ack = format.ack;
406         turn = format.turn;
407         recv_arg = format.recv_arg;
408         send_arg = format.send_arg;
409         checksum = format.checksum;
410         WXC_PACKET_LOG("---                            [recv] %d:%d:%04X:%04X\n", format.turn, format.phase, format.recv_arg, format.send_arg);
411     }
412 
413     /* If the turn is different, when the local device is DONE, complete data conversion */
414     if (turn != work->req_turn)
415     {
416         if (work->req_phase == PHASE_DONE)
417         {
418             retval = TRUE;
419         }
420     }
421     /* If the turn is the same, continue the data conversion process */
422     else
423     {
424         switch (work->ack_phase)
425         {
426         case PHASE_INIT:
427             if (work->req_phase != PHASE_INIT)
428             {
429                 break;
430             }
431             /* Save the send/receive size */
432             protocol->send.buffer_max = recv_arg;
433             protocol->recv.checksum = checksum;
434             protocol->recv.length = send_arg;
435             /* Proceed to QUIT if the configuration exceeds either buffer size */
436             if ((protocol->send.length > protocol->send.buffer_max) ||
437                 (protocol->recv.length > protocol->recv.buffer_max))
438             {
439                 OS_TWarning("not enough buffer length for data exchange.\n");
440                 work->req_phase = PHASE_QUIT;
441             }
442             else if (!ack)
443             {
444                 break;
445             }
446             /* Proceed to DATA if there is a response */
447         case PHASE_DATA:
448             /* Preparation for start of data block conversion */
449             if (work->req_phase == PHASE_INIT)
450             {
451                 work->req_phase = PHASE_DATA;
452                 if (packet->length == work->child_send)
453                 {
454                     work->own_packet = work->parent_send;
455                     work->peer_packet = work->child_send;
456                 }
457                 else
458                 {
459                     work->own_packet = work->child_send;
460                     work->peer_packet = work->parent_send;
461                 }
462                 work->own_packet -= sizeof(PacketSegmentFormat);
463                 work->peer_packet -= sizeof(PacketSegmentFormat);
464                 work->req_index = 0;
465                 work->ack_index = 0;
466                 work->recv_count = 0;
467                 work->recv_total = (u32)((protocol->recv.length  + work->peer_packet
468                                          - 1) / work->peer_packet);
469                 MI_CpuFill32(work->recv_bitset, 0x00, sizeof(work->recv_bitset));
470             }
471             if (work->ack_phase != PHASE_DATA)
472             {
473                 break;
474             }
475             /* Data block conversion process */
476             if (work->req_phase == PHASE_DATA)
477             {
478                 /* Save this request number for the next send */
479                 work->ack_index = recv_arg;
480                 /* Merge the receive buffers */
481                 if (send_arg != 0x7FFF)
482                 {
483                     u32     pos = (send_arg >> 5UL);
484                     u32     bit = (send_arg & 31UL);
485                     if ((work->recv_bitset[pos] & (1 << bit)) == 0)
486                     {
487                         const u8 *argument = &packet->buffer[sizeof(PacketSegmentFormat)];
488                         u32     segment = work->peer_packet;
489                         u32     offset = segment * send_arg;
490                         u32     rest = protocol->recv.length - offset;
491                         MI_CpuCopy8(argument, (u8*)protocol->recv.buffer + offset,
492                                     MATH_MIN(segment, rest));
493                         work->recv_bitset[pos] |= (1 << bit);
494                         ++work->recv_count;
495                     }
496                 }
497                 /* If transmission is not complete, calculate the next request index */
498                 if (work->recv_count < work->recv_total)
499 				{
500                     u32     index = work->req_index;
501                     u32     pos = (index >> 5UL);
502                     u32     bit = (index & 31UL);
503                     for (;;)
504                     {
505                         if (++bit >= 32)
506                         {
507                             ++pos, bit = 0;
508                             for (; work->recv_bitset[pos] == (u32)~0; ++pos)
509                             {
510                             }
511                             bit = MATH_CTZ((u32)~work->recv_bitset[pos]);
512                         }
513                         if ((pos << 5UL) + bit >= work->recv_total)
514                         {
515                             pos = 0, bit = 0;
516                         }
517                         if ((work->recv_bitset[pos] & (1 << bit)) == 0)
518                         {
519                             break;
520                         }
521                     }
522                     work->req_index = (pos << 5UL) + bit;
523                 }
524                 /* If the transmission is complete, close the request index */
525                 else
526                 {
527                     work->req_index = 0x7FFF;
528                     /* Proceed to DONE if both transmissions are complete */
529                     if (work->ack_index == 0x7FFF)
530                     {
531                         work->req_phase = PHASE_DONE;
532                     }
533                 }
534             }
535             break;
536         case PHASE_DONE:
537             if (work->req_phase == PHASE_DATA)
538             {
539                 work->ack_index = recv_arg;
540                 /* Proceed to DONE if both transmissions are complete */
541                 if ((work->ack_index == 0x7FFF) && (work->req_index == 0x7FFF))
542                 {
543                     work->req_phase = PHASE_DONE;
544                 }
545             }
546             if (work->req_phase == PHASE_DONE)
547             {
548                 retval = TRUE;
549             }
550             break;
551         case PHASE_QUIT:
552             /* Proceed to QUIT here as well */
553             work->req_phase = PHASE_QUIT;
554             /* Communications can be ended if there is a response */
555             if (ack)
556             {
557                 work->executing = FALSE;
558             }
559             break;
560         case PHASE_IDLE:
561         default:
562             /* Ignore */
563             break;
564         }
565     }
566     if (retval)
567     {
568 #if defined(SDK_DEBUG)
569         /* Confirm checksum */
570         u8      checksum = MATH_CalcChecksum8(protocol->recv.buffer, protocol->recv.length);
571         if (protocol->recv.checksum != checksum)
572         {
573             OS_TWarning("checksum error!\n");
574         }
575 #endif
576 #if defined(DEBUG_CALCSPEED)
577         /* Speed measurement (target is the larger of the send or receive data) */
578         if (OS_IsTickAvailable())
579         {
580             OSTick diff = OS_GetTick() - work->tick_count;
581             u64    milli = OS_TicksToMilliSeconds(diff);
582             u64    length = MATH_MAX(protocol->send.length, protocol->recv.length);
583             WXC_PACKET_LOG("data-exchange : %d[B/s]\n", (length * 1000) / milli);
584         }
585 #endif
586         work->req_turn = !work->req_turn;
587         work->req_phase = PHASE_QUIT;
588     }
589 
590     return retval;
591 }
592 
593 /*---------------------------------------------------------------------------*
594   Name:         WXCi_GetProtocolImplCommon
595 
596   Description:  Gets the interface for the common chance encounter communication protocol.
597 
598   Arguments:    None.
599 
600   Returns:      None.
601  *---------------------------------------------------------------------------*/
WXCi_GetProtocolImplCommon(void)602 WXCProtocolImpl* WXCi_GetProtocolImplCommon(void)
603 {
604     return &impl_common;
605 }
606