/*---------------------------------------------------------------------------* Project: NitroSDK - WFS - libraries File: wfs_server.c Copyright 2007-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. *---------------------------------------------------------------------------*/ #include /*---------------------------------------------------------------------------*/ /* Functions */ /*---------------------------------------------------------------------------* Name: WFSi_NotifySegmentEvent Description: Notifies the caller of a WFS_EVENT_SERVER_SEGMENT_REQUEST event. Arguments: context: The WFSServerContext structure. argument The event argument. Returns: None. *---------------------------------------------------------------------------*/ static void WFSi_NotifySegmentEvent(WFSServerContext *context, void *argument) { if (context->thread_work) { (*context->thread_hook)(context->thread_work, argument); } else if (context->callback) { (*context->callback)(context, WFS_EVENT_SERVER_SEGMENT_REQUEST, argument); } } /*---------------------------------------------------------------------------* Name: WFSi_WBTCallback Description: Server-side WBT event notification. Arguments: userdata The WFSServerContext structure. uc The WBT event argument. Returns: None. *---------------------------------------------------------------------------*/ static void WFSi_WBTCallback(void *userdata, WBTCommand *uc) { WFSServerContext *work = (WFSServerContext *)userdata; const int aid = (int)MATH_CTZ(uc->peer_bmp); switch (uc->command) { default: WFS_DEBUG_OUTPUT(("invalid WBT callback (command = %d)", uc->command)); break; case WBT_CMD_REQ_USER_DATA: /* Confirm whether the message has been sent to everyone */ if (!uc->target_bmp) { work->msg_busy = FALSE; } break; case WBT_CMD_SYSTEM_CALLBACK: /* Indication */ switch (uc->event) { default: WFS_DEBUG_OUTPUT(("unknown WBT system callback (event = %d)", uc->event)); break; case WBT_CMD_REQ_SYNC: case WBT_CMD_REQ_GET_BLOCK_DONE: break; case WBT_CMD_REQ_USER_DATA: /* Message receipt notification */ { /* Save the received message */ const WFSMessageFormat *const msg = (const WFSMessageFormat *)uc->user_data.data; work->recv_msg[aid] = *msg; switch (msg->type) { case WFS_MSG_LOCK_REQ: /* Read lock request */ { const u32 offset = MI_LEToH32(msg->arg1); const u32 length = MI_LEToH32_BITFIELD(24, msg->arg2); const int packet = (int)((msg->packet_hi << 8) | msg->packet_lo); WFS_DEBUG_OUTPUT(("WBT-MSG(LOCK):recv [offset=0x%08X, length=0x%08X] (AID=%d, packet=%d)", offset, length, aid, packet)); /* Deny response when the packet size is changing */ if (work->is_changing || (packet != WBT_GetParentPacketLength(work->wbt) + WBT_PACKET_SIZE_MIN)) { work->is_changing = TRUE; work->deny_bitmap |= (1 << aid); } /* If there is a match with an existing lock region, reference it */ else { WFSLockInfo *file = NULL; int index; for (index = 0; (1 << index) <= work->use_bitmap; ++index) { if(((1 << index) & work->use_bitmap) != 0) { if ((work->list[index].offset == offset) && (work->list[index].length == length)) { file = &work->list[index]; ++file->ref; break; } } } /* If a new lock region, allocate from the available list */ if (!file) { index = (int)MATH_CTZ((u32)~work->use_bitmap); if (index < WFS_LOCK_HANDLE_MAX) { PLATFORM_ENTER_CRITICALSECTION(); work->use_bitmap |= (1 << index); file = &work->list[index]; file->ref = 1; file->offset = offset; file->length = length; /* Transfer to the registered list as it is a new file */ (void)WBT_RegisterBlockInfo(work->wbt, &file->info, (u32)(WFS_LOCKED_BLOCK_INDEX + index), NULL, NULL, (int)file->length); file->ack_seq = 0; PLATFORM_LEAVE_CRITICALSECTION(); /* Request to read just the header in advance */ { WFSSegmentBuffer segment[1]; segment->offset = file->offset; segment->length = (u32)WBT_GetParentPacketLength(work->wbt); segment->buffer = NULL; WFSi_NotifySegmentEvent(work, segment); } } else { OS_TPanic("internal-error (no available lock handles)"); } } work->ack_bitmap |= (1 << aid); work->recv_msg[aid].arg1 = MI_HToLE32((u32)(WFS_LOCKED_BLOCK_INDEX + index)); } work->busy_bitmap |= (1 << aid); } break; case WFS_MSG_UNLOCK_REQ: /* CLOSEFILE request */ { PLATFORM_ENTER_CRITICALSECTION(); u32 id = MI_LEToH32(msg->arg1); u32 index = id - WFS_LOCKED_BLOCK_INDEX; if (index < WFS_LOCK_HANDLE_MAX) { WFSLockInfo *file = &work->list[index]; /* Deallocate process when all references are gone */ if (--file->ref <= 0) { (void)WBT_UnregisterBlockInfo(work->wbt, id); work->use_bitmap &= ~(1 << index); } } work->ack_bitmap |= (1 << aid); PLATFORM_LEAVE_CRITICALSECTION(); WFS_DEBUG_OUTPUT(("WBT-MSG(UNLOCK):recv [id=0x%08X] (AID=%d)", id, aid)); } break; } } break; case WBT_CMD_PREPARE_SEND_DATA: /* GETBLOCK buffer preparation request */ { WBTPrepareSendDataCallback *const p_prep = &uc->prepare_send_data; u32 id = p_prep->block_id; p_prep->data_ptr = NULL; /* Response when a valid file */ id -= WFS_LOCKED_BLOCK_INDEX; if (id < WFS_LOCK_HANDLE_MAX) { WFSLockInfo *file = &work->list[id]; /* Current response to the previous request, next response for current request */ WFSSegmentBuffer segment[1]; const u32 length = p_prep->own_packet_size; const u32 current = file->ack_seq; const u32 next = (u32)p_prep->block_seq_no; file->ack_seq = next; /* Preparation for next time */ segment->offset = file->offset + length * next; segment->length = MATH_MIN(length, file->length - length * next); segment->buffer = NULL; WFSi_NotifySegmentEvent(work, segment); /* Current request */ segment->offset = file->offset + length * current; segment->length = MATH_MIN(length, file->length - length * current); segment->buffer = work->cache_hit_buf; WFSi_NotifySegmentEvent(work, segment); if (segment->buffer != NULL) { p_prep->block_seq_no = (s32)current; p_prep->data_ptr = segment->buffer; } } } break; } break; } } /*---------------------------------------------------------------------------* Name: WFS_CallServerConnectHook Description: Server-side connection notification. Arguments: context: The WFSServerContext structure. peer: Information for the connected client. Returns: None. *---------------------------------------------------------------------------*/ void WFS_CallServerConnectHook(WFSServerContext *context, const WFSPeerInfo *peer) { /* * 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 * */ (void)context; (void)peer; } /*---------------------------------------------------------------------------* Name: WFS_CallServerDisconnectHook Description: Disconnect notification on the server side. Arguments: context: The WFSServerContext structure. peer: Information for the disconnected client. Returns: None. *---------------------------------------------------------------------------*/ void WFS_CallServerDisconnectHook(WFSServerContext *context, const WFSPeerInfo *peer) { const int bit = (1 << peer->aid); context->all_bitmap &= ~bit; (void)WBT_CancelCommand(context->wbt, bit); } /*---------------------------------------------------------------------------* Name: WFS_CallServerPacketSendHook Description: Notification of timing when it is possible to send packets on the server side. Arguments: context: The WFSServerContext structure. packet: Send packet settings. Returns: None. *---------------------------------------------------------------------------*/ void WFS_CallServerPacketSendHook(WFSServerContext *context, WFSPacketBuffer *packet) { /* Restart the non-response process when it is possible to send a message */ if (!context->msg_busy) { /* Show the current valid communications state in each type of bitmap */ context->ack_bitmap &= context->all_bitmap; context->sync_bitmap &= context->all_bitmap; context->busy_bitmap &= context->all_bitmap; context->deny_bitmap &= context->all_bitmap; /* Wait to change packet size until all processes being received are complete */ if (context->is_changing && !context->use_bitmap) { /* Change packet size */ context->is_changing = FALSE; (void)WBT_SetPacketLength(context->wbt, context->new_packet, WBT_PACKET_SIZE_MIN); /* Overall denial response for requests with the old size */ if (context->deny_bitmap) { WFS_DEBUG_OUTPUT(("WBT-MSG(LOCK):deny [packet-length renewal] (BITMAP=%d)", context->deny_bitmap)); (void)WFS_SendMessageLOCK_ACK(context->wbt, WFSi_WBTCallback, context->deny_bitmap, 0); context->msg_busy = TRUE; context->deny_bitmap = 0; } } /* Corresponds as-is to the requests received before the packet size change */ else if (context->ack_bitmap) { int bitmap = context->ack_bitmap; WFSMessageFormat *msg = NULL; int i; const int sync = context->sync_bitmap; const BOOL is_sync = (sync && ((bitmap & sync) == sync)); /* Respond all at once if there is a uniform child device group that received the synchronous designation */ if (is_sync) { bitmap = sync; } /* Otherwise, respond as normal */ else { bitmap &= ~sync; } /* Search for relevant child devices that can respond */ for (i = 0;; ++i) { const int bit = (1 << i); if (bit > bitmap) { break; } if ((bit & bitmap) != 0) { /* Search for child devices that can respond in order from the lowest aid */ if (!msg) { msg = &context->recv_msg[i]; } /* Send all at once if same type of response */ else if ((msg->type == context->recv_msg[i].type) && (msg->arg1 == context->recv_msg[i].arg1)) { } /* If not, then hold this time for the child device */ else { bitmap &= ~bit; } } } /* * NOTE: * 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. * * * * * * * */ if (is_sync && (bitmap != sync)) { context->sync_bitmap = 0; OS_TWarning("[WFS] specified synchronous-access failed! " "(then synchronous-setting was reset)"); } /* Send the response selected this time */ if (msg) { switch (msg->type) { case WFS_MSG_LOCK_REQ: (void)WFS_SendMessageLOCK_ACK(context->wbt, WFSi_WBTCallback, bitmap, MI_LEToH32(msg->arg1)); break; case WFS_MSG_UNLOCK_REQ: (void)WFS_SendMessageUNLOCK_ACK(context->wbt, WFSi_WBTCallback, bitmap, MI_LEToH32(msg->arg1)); context->busy_bitmap &= ~bitmap; break; } context->msg_busy = TRUE; context->ack_bitmap &= ~bitmap; } } } /* Call WBT after updating all the latest statuses */ packet->length = WBT_CallPacketSendHook(context->wbt, packet->buffer, packet->length, TRUE); } /*---------------------------------------------------------------------------* Name: WFS_CallServerPacketRecvHook Description: Server-side packet receipt notification. Arguments: context: The WFSServerContext structure. packet: Sender packet information. Returns: None. *---------------------------------------------------------------------------*/ void WFS_CallServerPacketRecvHook(WFSServerContext *context, const WFSPacketBuffer *packet) { int aid = (int)MATH_CTZ((u32)packet->bitmap); const void *buffer = packet->buffer; int length = packet->length; /* * Detect a connection using actual packet receipt from a client to prevent impact on unrelated clients from unneeded port communication from the parent. * */ context->all_bitmap |= (1 << aid); WBT_CallPacketRecvHook(context->wbt, aid, buffer, length); } /*---------------------------------------------------------------------------* Name: WFS_InitServer Description: Initializes the WFS server context. Arguments: context: The WFSServerContext structure. userdata: Any user-defined value associated with the context. callback: The system event notification callback. Specify NULL if not needed. allocator: The allocator used internally. packet: The parent's initial packet size. Returns: None. *---------------------------------------------------------------------------*/ void WFS_InitServer(WFSServerContext *context, void *userdata, WFSEventCallback callback, MIAllocator *allocator, int packet) { /* Initialize the basic settings */ MI_CpuClear8(context, sizeof(*context)); context->userdata = userdata; context->callback = callback; context->allocator = allocator; /* Initialize the internal status */ context->new_packet = packet; context->table->buffer = NULL; context->table->length = 0; context->sync_bitmap = 0; context->ack_bitmap = 0; context->msg_busy = FALSE; context->all_bitmap = 1; context->busy_bitmap = 0; context->is_changing = FALSE; context->deny_bitmap = 0; context->use_bitmap = 0; context->thread_work = NULL; context->thread_hook = NULL; /* Initialize WBT */ WBT_InitContext(context->wbt, context, WFSi_WBTCallback); WBT_AddCommandPool(context->wbt, context->wbt_list, sizeof(context->wbt_list) / sizeof(*context->wbt_list)); WBT_StartParent(context->wbt, packet, WBT_PACKET_SIZE_MIN); } /*---------------------------------------------------------------------------* Name: WFS_EndServer Description: Deallocates the WFS server context. Arguments: context: The WFSServerContext structure. Returns: None. *---------------------------------------------------------------------------*/ void WFS_EndServer(WFSServerContext *context) { if (context->thread_work) { (*context->thread_hook)(context->thread_work, NULL); MI_CallFree(context->allocator, context->thread_work); context->thread_work = NULL; } WBT_ResetContext(context->wbt, NULL); if (context->table->buffer) { MI_CallFree(context->allocator, context->table->buffer); context->table->buffer = NULL; context->table->length = 0; } } /*---------------------------------------------------------------------------* Name: WFS_RegisterServerTable Description: Loads the ROM file table from the device and registers it to the server. Arguments: context: The WFSServerContext structure. device: The device storing the NTR binary. fatbase: The offset within the device wherein the NTR binary is located. overlay: The device-internal offset where the NTR binary containing the overlay to be merged is located. (If not merging multiple ROM file tables, this value is identical to "fatbase") Returns: TRUE when the ROM file table is correctly loaded and registered. *---------------------------------------------------------------------------*/ BOOL WFS_RegisterServerTable(WFSServerContext *context, MIDevice *device, u32 fatbase, u32 overlay) { BOOL retval = FALSE; /* Multiple file tables cannot be registered. */ if (context->table->buffer) { OS_TWarning("table is already registered.\n"); } /* Load the ROM file table from the device */ else if (WFS_LoadTable(context->table, context->allocator, device, fatbase, overlay)) { /* Register the ROM file table */ (void)WBT_RegisterBlockInfo(context->wbt, context->table_info, WFS_TABLE_BLOCK_INDEX, NULL, context->table->buffer, (int)context->table->length); retval = TRUE; } return retval; } /*---------------------------------------------------------------------------* Name: WFS_GetServerPacketLength Description: Obtain the size of packets sent by the server. Arguments: context: The WFSServerContext structure. Returns: The currently specified packet size. *---------------------------------------------------------------------------*/ int WFS_GetServerPacketLength(const WFSServerContext *context) { return context->new_packet; } /*---------------------------------------------------------------------------* Name: WFS_SetServerPacketLength Description: Sets the size of the server send packet. Arguments: context: The WFSServerContext structure. length: The packet size to be set. Returns: None. *---------------------------------------------------------------------------*/ void WFS_SetServerPacketLength(WFSServerContext *context, int length) { SDK_ASSERT(length >= WBT_PACKET_SIZE_MIN); { PLATFORM_ENTER_CRITICALSECTION(); context->new_packet = length; context->is_changing = TRUE; PLATFORM_LEAVE_CRITICALSECTION(); } } /*---------------------------------------------------------------------------* Name: WFS_SetServerSync Description: Sets the clients that synchronously access the server. 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. 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. Arguments: context: The WFSServerContext structure. bitmap: The AID bitmap for the client to be synchronized. Synchronization will not occur when 0 (the default value) is specified. The last bit is always ignored. Returns: None. *---------------------------------------------------------------------------*/ void WFS_SetServerSync(WFSServerContext *context, int bitmap) { PLATFORM_ENTER_CRITICALSECTION(); context->sync_bitmap = (bitmap & ~1); PLATFORM_LEAVE_CRITICALSECTION(); } /*---------------------------------------------------------------------------* $Log: wfs_server.c,v $ Revision 1.6 2007/10/04 05:36:31 yosizaki Fix related to WFS_EndServer. Revision 1.5 2007/06/14 13:15:27 yosizaki Added hook to a thread-proc. Revision 1.4 2007/06/11 10:23:32 yosizaki Minor fixes. Revision 1.3 2007/06/11 06:40:00 yosizaki Added WFS_GetServerPacketLength(). Revision 1.2 2007/04/17 00:01:06 yosizaki Renamed some structures. Revision 1.1 2007/04/13 04:12:37 yosizaki Initial upload. $NoKeywords: $ *---------------------------------------------------------------------------*/