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