/*---------------------------------------------------------------------------* Project: TwlSDK - WXC - libraries - File: wxc_protocol_impl_common.c.c Copyright 2006-2009 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: 2008-12-16#$ $Rev: 9661 $ $Author: okubata_ryoma $ *---------------------------------------------------------------------------*/ #include #include #include /*****************************************************************************/ /* Declaration */ /* Protocol beacon format structure of shared chance encounter communications */ typedef struct WXCCommonProtocolBeaconFormat { u32 send_length; /* Send data size (fixed for each game) */ } WXCCommonProtocolBeaconFormat; /* SSID format structure used when connecting with the common chance encounter communication protocol */ typedef struct WXCCommonProtocolSsidFormat { char header[4]; /* Version information "WXC1" */ u32 ggid; /* GGID to declare from child device */ } WXCCommonProtocolSsidFormat; /* Normal command packet format */ typedef struct PacketCommandFormat { u16 segment:1; /* always 0 */ u16 ack:1; u16 turn:1; u16 phase:5; u16 checksum:8; u16 recv_arg; u16 send_arg; } PacketCommandFormat; /* Segment format for data block conversion */ typedef struct PacketSegmentFormat { u16 segment:1; /* always 1 */ u16 recv_arg:15; u16 turn:1; u16 send_arg:15; u8 buffer[0]; } PacketSegmentFormat; /* Information structure for shared chance encounter communication protocol */ typedef struct WXCCommonProtocolImplContext { /* Beacon send/receive configuration */ WXCCommonProtocolBeaconFormat beacon_format; /* MP network communications configuration */ u32 parent_send; u32 child_send; BOOL executing; /* Protocol control */ int req_phase; u32 req_turn; int ack_phase; /* Data conversion process */ u32 own_packet; u32 peer_packet; u32 req_index; u32 ack_index; u32 recv_count; u32 recv_total; u32 recv_bitset[WXC_MAX_DATA_SIZE / (8 * 4) + 1]; //#define DEBUG_CALCSPEED #if defined(DEBUG_CALCSPEED) /* Debug member for speed measurement */ OSTick tick_count; #endif } WXCCommonProtocolImplContext; static void WXCi_BeaconSendHook(WXCProtocolContext * protocol, WMParentParam *p_param); static BOOL WXCi_BeaconRecvHook(WXCProtocolContext * protocol, const WMBssDesc *p_desc); static void WXCi_PreConnectHook(WXCProtocolContext * protocol, const WMBssDesc *p_desc, u8 *ssid); static void WXCi_PacketSendHook(WXCProtocolContext * protocol, WXCPacketInfo * packet); static BOOL WXCi_PacketRecvHook(WXCProtocolContext * protocol, const WXCPacketInfo * packet); static void WXCi_InitSequence(WXCProtocolContext * protocol, u16 send_max, u16 recv_max); static BOOL WXCi_AddData(WXCProtocolContext * protocol, const void *send_buf, u32 send_size, void *recv_buf, u32 recv_max); static BOOL WXCi_IsExecuting(WXCProtocolContext * protocol); /*****************************************************************************/ /* Constants */ /* State transition for data conversion protocol */ enum { PHASE_IDLE, PHASE_QUIT, PHASE_INIT, PHASE_DATA, PHASE_DONE }; /*****************************************************************************/ /* Variables */ /* Work buffer for WXC protocol */ static WXCCommonProtocolImplContext work_common ATTRIBUTE_ALIGN(32); /* WXC protocol interface */ static WXCProtocolImpl impl_common = { "COMMON", WXCi_BeaconSendHook, WXCi_BeaconRecvHook, WXCi_PreConnectHook, WXCi_PacketSendHook, WXCi_PacketRecvHook, WXCi_InitSequence, WXCi_AddData, WXCi_IsExecuting, &work_common, }; /*****************************************************************************/ /* Functions */ /*---------------------------------------------------------------------------* Name: WXCi_BeaconSendHook Description: Hook called at beacon update. Arguments: protocol: WXCProtocolContext structure p_param: WMParentParam structure used for next beacon Change inside the function as necessary. Returns: None. *---------------------------------------------------------------------------*/ void WXCi_BeaconSendHook(WXCProtocolContext * protocol, WMParentParam *p_param) { /* * This hook is called only by normal game titles or relay points exclusively used in parent device mode * */ WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work; WXCProtocolRegistry * const reg = WXC_GetCurrentBlock(protocol); work->beacon_format.send_length = reg->send.length; p_param->userGameInfo = (u16*)&work->beacon_format; p_param->userGameInfoLength = sizeof(work->beacon_format); } /*---------------------------------------------------------------------------* Name: WXCi_BeaconRecvHook Description: Hook called for individual scanned beacons. Arguments: protocol: WXCProtocolContext structure p_desc: Scanned WMBssDesc structure Returns: If it is seen as connection target, return TRUE. Otherwise, return FALSE. *---------------------------------------------------------------------------*/ BOOL WXCi_BeaconRecvHook(WXCProtocolContext * protocol, const WMBssDesc *p_desc) { #pragma unused(protocol) #pragma unused(p_desc) /* * This hook is called only when a game title is in child device mode. * A user callback is notified on relay points that use WXC_GGID_COMMON_ANY. */ return TRUE; } /*---------------------------------------------------------------------------* Name: WXCi_PreConnectHook Description: Hook called prior to connection to network communication target. Arguments: protocol: WXCProtocolContext structure p_desc: Connection target WMBssDesc structure ssid: Buffer that stores the SSID for configuration Returns: None. *---------------------------------------------------------------------------*/ void WXCi_PreConnectHook(WXCProtocolContext * protocol, const WMBssDesc *p_desc, u8 ssid[WM_SIZE_CHILD_SSID]) { #pragma unused(p_desc) /* * This hook is called only when a game title is in child device mode. * A user callback is notified on relay points that use WXC_GGID_COMMON_ANY. */ WXCCommonProtocolSsidFormat *p = (WXCCommonProtocolSsidFormat*)ssid; WXCProtocolRegistry * const reg = WXC_GetCurrentBlock(protocol); p->header[0] = 'W'; p->header[1] = 'X'; p->header[2] = 'C'; p->header[3] = '1'; p->ggid = reg->ggid; } /*---------------------------------------------------------------------------* Name: WXCi_InitSequence Description: Reinitializes the WXC library protocol. Arguments: protocol: WXCProtocolContext structure send_max: Maximum send packet size recv_max: Maximum receive packet size Returns: None. *---------------------------------------------------------------------------*/ void WXCi_InitSequence(WXCProtocolContext * protocol, u16 send_max, u16 recv_max) { WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work; protocol->send.buffer = NULL; protocol->send.length = 0; protocol->send.buffer_max = 0; protocol->send.checksum = 0; protocol->recv.buffer = NULL; protocol->recv.length = 0; protocol->recv.buffer_max = 0; protocol->recv.checksum = 0; work->parent_send = send_max; work->child_send = recv_max; work->req_phase = PHASE_QUIT; work->req_turn = 0; work->ack_phase = PHASE_IDLE; work->executing = TRUE; } /*---------------------------------------------------------------------------* Name: WXCi_AddData Description: Configures the block data exchange. Arguments: protocol: WXCProtocolContext structure send_buf: Send buffer send_size: Send buffer size recv_buf: Receive buffer recv_max: Receive buffer size Returns: If registration is possible, return TRUE after configuration. *---------------------------------------------------------------------------*/ BOOL WXCi_AddData(WXCProtocolContext * protocol, const void *send_buf, u32 send_size, void *recv_buf, u32 recv_max) { BOOL retval = FALSE; WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work; if (work->req_phase == PHASE_QUIT) { retval = TRUE; protocol->send.buffer = (void*)send_buf; protocol->send.buffer_max = (u16)send_size; protocol->send.length = (u16)send_size; protocol->send.checksum = MATH_CalcChecksum8(send_buf, send_size); protocol->recv.buffer = recv_buf; protocol->recv.buffer_max = (u16)recv_max; /* The receive size and checksum are finalized after data conversion */ work->req_phase = PHASE_INIT; work->ack_phase = PHASE_IDLE; #if defined(DEBUG_CALCSPEED) if (OS_IsTickAvailable()) { work->tick_count = OS_GetTick(); } #endif } return retval; } /*---------------------------------------------------------------------------* Name: WXCi_IsExecuting Description: Checks whether data is currently being exchanged. Arguments: None. Returns: Return TRUE if data is currently being exchanged. *---------------------------------------------------------------------------*/ BOOL WXCi_IsExecuting(WXCProtocolContext * protocol) { WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work; return work->executing; } /*---------------------------------------------------------------------------* Name: WXCi_PacketSendHook Description: Hook called at MP packet transmission. Arguments: protocol: WXCProtocolContext structure packet: WXCPacketInfo pointer configuring transmission packet information Returns: None. *---------------------------------------------------------------------------*/ void WXCi_PacketSendHook(WXCProtocolContext * protocol, WXCPacketInfo * packet) { WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work; if ((work->req_phase == PHASE_DATA) || (work->req_phase == PHASE_DONE)) { PacketSegmentFormat *format = (PacketSegmentFormat*)packet->buffer; format->segment = TRUE; format->turn = work->req_turn; format->recv_arg = work->req_index; format->send_arg = work->ack_index; if (work->ack_index != 0x7FFF) { u32 segment = work->own_packet; u32 offset = segment * work->ack_index; u32 rest = protocol->send.length - offset; MI_CpuCopy8((u8*)protocol->send.buffer + offset, format->buffer, MATH_MIN(segment, rest)); } WXC_PACKET_LOG("--- [send] %d:F:%04X:%04X\n", format->turn, format->recv_arg, format->send_arg); } else { PacketCommandFormat *format = (PacketCommandFormat*)packet->buffer; format->segment = FALSE; format->ack = (u8)(work->ack_phase == work->req_phase); format->turn = work->req_turn; format->phase = (u8)work->req_phase; format->checksum = (u8)protocol->send.checksum; format->recv_arg = (u16)protocol->recv.buffer_max; format->send_arg = (u16)protocol->send.length; WXC_PACKET_LOG("--- [send] %d:%d:%04X:%04X\n", format->turn, format->phase, format->recv_arg, format->send_arg); } } /*---------------------------------------------------------------------------* Name: WXCi_PacketRecvHook Description: Hook called at MP packet reception. Arguments: protocol: WXCProtocolContext structure packet: WXCPacketInfo pointer configuring reception packet information Returns: If a single data exchange is completed, return TRUE. *---------------------------------------------------------------------------*/ BOOL WXCi_PacketRecvHook(WXCProtocolContext * protocol, const WXCPacketInfo * packet) { BOOL retval = FALSE; WXCCommonProtocolImplContext *work = (WXCCommonProtocolImplContext*)WXC_GetCurrentBlockImpl(protocol)->impl_work; PacketCommandFormat format; BOOL ack = FALSE; BOOL turn = !work->req_turn; u32 recv_arg; u32 send_arg; u16 checksum = 0; /* First, analyze the partner's command */ if (packet->length < sizeof(format)) { OS_TWarning("not enough length.\n"); return FALSE; } MI_CpuCopy8(packet->buffer, &format, sizeof(format)); /* Data segment packet */ if (format.segment) { PacketSegmentFormat segment; MI_CpuCopy8(packet->buffer, &segment, sizeof(segment)); turn = segment.turn; recv_arg = segment.recv_arg; send_arg = segment.send_arg; work->ack_phase = ((recv_arg == 0x7FFF) && (send_arg == 0x7FFF)) ? PHASE_DONE : PHASE_DATA; WXC_PACKET_LOG("--- [recv] %d:F:%04X:%04X\n", segment.turn, segment.recv_arg, segment.send_arg); } else { work->ack_phase = format.phase; ack = format.ack; turn = format.turn; recv_arg = format.recv_arg; send_arg = format.send_arg; checksum = format.checksum; WXC_PACKET_LOG("--- [recv] %d:%d:%04X:%04X\n", format.turn, format.phase, format.recv_arg, format.send_arg); } /* If the turn is different, when the local device is DONE, complete data conversion */ if (turn != work->req_turn) { if (work->req_phase == PHASE_DONE) { retval = TRUE; } } /* If the turn is the same, continue the data conversion process */ else { switch (work->ack_phase) { case PHASE_INIT: if (work->req_phase != PHASE_INIT) { break; } /* Save the send/receive size */ protocol->send.buffer_max = recv_arg; protocol->recv.checksum = checksum; protocol->recv.length = send_arg; /* Proceed to QUIT if the configuration exceeds either buffer size */ if ((protocol->send.length > protocol->send.buffer_max) || (protocol->recv.length > protocol->recv.buffer_max)) { OS_TWarning("not enough buffer length for data exchange.\n"); work->req_phase = PHASE_QUIT; } else if (!ack) { break; } /* Proceed to DATA if there is a response */ case PHASE_DATA: /* Preparation for start of data block conversion */ if (work->req_phase == PHASE_INIT) { work->req_phase = PHASE_DATA; if (packet->length == work->child_send) { work->own_packet = work->parent_send; work->peer_packet = work->child_send; } else { work->own_packet = work->child_send; work->peer_packet = work->parent_send; } work->own_packet -= sizeof(PacketSegmentFormat); work->peer_packet -= sizeof(PacketSegmentFormat); work->req_index = 0; work->ack_index = 0; work->recv_count = 0; work->recv_total = (u32)((protocol->recv.length + work->peer_packet - 1) / work->peer_packet); MI_CpuFill32(work->recv_bitset, 0x00, sizeof(work->recv_bitset)); } if (work->ack_phase != PHASE_DATA) { break; } /* Data block conversion process */ if (work->req_phase == PHASE_DATA) { /* Save this request number for the next send */ work->ack_index = recv_arg; /* Merge the receive buffers */ if (send_arg != 0x7FFF) { u32 pos = (send_arg >> 5UL); u32 bit = (send_arg & 31UL); if ((work->recv_bitset[pos] & (1 << bit)) == 0) { const u8 *argument = &packet->buffer[sizeof(PacketSegmentFormat)]; u32 segment = work->peer_packet; u32 offset = segment * send_arg; u32 rest = protocol->recv.length - offset; MI_CpuCopy8(argument, (u8*)protocol->recv.buffer + offset, MATH_MIN(segment, rest)); work->recv_bitset[pos] |= (1 << bit); ++work->recv_count; } } /* If transmission is not complete, calculate the next request index */ if (work->recv_count < work->recv_total) { u32 index = work->req_index; u32 pos = (index >> 5UL); u32 bit = (index & 31UL); for (;;) { if (++bit >= 32) { ++pos, bit = 0; for (; work->recv_bitset[pos] == (u32)~0; ++pos) { } bit = MATH_CTZ((u32)~work->recv_bitset[pos]); } if ((pos << 5UL) + bit >= work->recv_total) { pos = 0, bit = 0; } if ((work->recv_bitset[pos] & (1 << bit)) == 0) { break; } } work->req_index = (pos << 5UL) + bit; } /* If the transmission is complete, close the request index */ else { work->req_index = 0x7FFF; /* Proceed to DONE if both transmissions are complete */ if (work->ack_index == 0x7FFF) { work->req_phase = PHASE_DONE; } } } break; case PHASE_DONE: if (work->req_phase == PHASE_DATA) { work->ack_index = recv_arg; /* Proceed to DONE if both transmissions are complete */ if ((work->ack_index == 0x7FFF) && (work->req_index == 0x7FFF)) { work->req_phase = PHASE_DONE; } } if (work->req_phase == PHASE_DONE) { retval = TRUE; } break; case PHASE_QUIT: /* Proceed to QUIT here as well */ work->req_phase = PHASE_QUIT; /* Communications can be ended if there is a response */ if (ack) { work->executing = FALSE; } break; case PHASE_IDLE: default: /* Ignore */ break; } } if (retval) { #if defined(SDK_DEBUG) /* Confirm checksum */ u8 checksum = MATH_CalcChecksum8(protocol->recv.buffer, protocol->recv.length); if (protocol->recv.checksum != checksum) { OS_TWarning("checksum error!\n"); } #endif #if defined(DEBUG_CALCSPEED) /* Speed measurement (target is the larger of the send or receive data) */ if (OS_IsTickAvailable()) { OSTick diff = OS_GetTick() - work->tick_count; u64 milli = OS_TicksToMilliSeconds(diff); u64 length = MATH_MAX(protocol->send.length, protocol->recv.length); WXC_PACKET_LOG("data-exchange : %d[B/s]\n", (length * 1000) / milli); } #endif work->req_turn = !work->req_turn; work->req_phase = PHASE_QUIT; } return retval; } /*---------------------------------------------------------------------------* Name: WXCi_GetProtocolImplCommon Description: Gets the interface for the common chance encounter communication protocol. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ WXCProtocolImpl* WXCi_GetProtocolImplCommon(void) { return &impl_common; }