/*---------------------------------------------------------------------------* Project: NitroSDK - WFS - libraries File: wfs_thread.c Copyright 2007-2008 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 /*---------------------------------------------------------------------------*/ /* Constants */ /* * Various ROM cache setting values. * [Page size] * - An integer multiple of the ROM page size (512 bytes) is desirable. * - A value that is too small will increase the ROM access overhead. * - A value that is too large will cause server ROM accesses (FS/SND/...) to become blocked at irregular intervals. * [Page count] * - If large, this will contribute to stabilizing transfer efficiency (the responsiveness of resend processing in restricted communication environments). * - A value larger than the total number of clients is desirable if access locality is high. */ #define WFS_FILE_CACHE_SIZE 1024UL #define WFS_CACHE_PAGE_TOTAL 8 /*---------------------------------------------------------------------------*/ /* Declarations */ typedef struct WFSServerThread { WFSEventCallback hook; MIDevice device[1]; FSFile file[1]; MICache cache[1]; u8 cache_buf[MI_CACHE_BUFFER_WORKSIZE( WFS_FILE_CACHE_SIZE, WFS_CACHE_PAGE_TOTAL)]; OSMessageQueue msg_queue[1]; OSMessage msg_array[1]; OSThread thread[1]; u8 thread_stack[0x400]; } WFSServerThread; /*---------------------------------------------------------------------------*/ /* Functions */ /*---------------------------------------------------------------------------* Name: WFSi_ReadRomCallback Description: ROM device read callback Arguments: userdata WFSServerThread structure buffer The transfer target memory. offset The transfer source offset. length The transfer size. Returns: Returns a value of 0 or higher if successful; a negative number otherwise. *---------------------------------------------------------------------------*/ static int WFSi_ReadRomCallback(void *userdata, void *buffer, u32 offset, u32 length) { WFSServerThread * const thread = (WFSServerThread*)userdata; /* NITRO-SDK specific: Read from memory because the first 32 KB cannot be accessed. */ if(offset < sizeof(CARDRomRegion)) { const u8 *header = (const u8 *)CARD_GetOwnRomHeader(); if (length > sizeof(CARDRomHeader) - offset) { length = sizeof(CARDRomHeader) - offset; } MI_CpuCopy8(header + offset, buffer, length); } #if defined(SDK_TWL) // Unique to TWL-SDK: Do not send data from the TWL-exclusive region to the network carelessly. else if (OS_IsRunOnTwl() && ((((const u8 *)HW_TWL_ROM_HEADER_BUF)[0x1C] & 0x01) != 0) && ((offset >= CARD_GetOwnRomHeader()->rom_size) || (offset + length > CARD_GetOwnRomHeader()->rom_size))) { MI_CpuFill8(buffer, 0x00, length); } #endif else { if (offset < 0x8000) { u32 memlen = length; if (memlen > 0x8000 - offset) { memlen = 0x8000 - offset; } MI_CpuFill8(buffer, 0x00, length); offset += memlen; length -= memlen; } if (length > 0) { (void)FS_SeekFile(thread->file, (int)offset, FS_SEEK_SET); (void)FS_ReadFile(thread->file, buffer, (int)length); } } return (int)length; } /*---------------------------------------------------------------------------* Name: WFSi_WriteRomCallback Description: ROM device write callback (dummy) Arguments: userdata WFSServerThread structure buffer The transfer target memory. offset The transfer source offset. length The transfer size. Returns: Returns a value of 0 or higher if successful; a negative number otherwise. *---------------------------------------------------------------------------*/ static int WFSi_WriteRomCallback(void *userdata, const void *buffer, u32 offset, u32 length) { (void)userdata; (void)buffer; (void)offset; (void)length; return -1; } /*---------------------------------------------------------------------------* Name: WFSi_ServerThreadProc Description: Thread procedure for the WFS server. Arguments: arg WFSServerThread structure. Returns: None. *---------------------------------------------------------------------------*/ static void WFSi_ServerThreadProc(void *arg) { WFSServerThread *const thread = (WFSServerThread*)arg; for (;;) { BOOL busy = FALSE; (void)OS_ReceiveMessage(thread->msg_queue, (OSMessage*)&busy, OS_MESSAGE_BLOCK); if (!busy) { break; } MI_LoadCache(thread->cache, thread->device); } } /*---------------------------------------------------------------------------* Name: WFSi_ThreadHook Description: A hook function set in the WFS server segment callback. Arguments: work WFSServerThread structure argument WFSSegmentBuffer structure Completion notification if NULL. Returns: None. *---------------------------------------------------------------------------*/ static void WFSi_ThreadHook(void *work, void *argument) { WFSServerThread * const thread = (WFSServerThread *)work; WFSSegmentBuffer * const segment = (WFSSegmentBuffer*)argument; /* Completion notification if NULL */ if (!segment) { (void)OS_SendMessage(thread->msg_queue, (OSMessage)FALSE, OS_MESSAGE_BLOCK); OS_JoinThread(thread->thread); (void)FS_CloseFile(thread->file); } /* Segment request (or notification of preparation) if not NULL */ else if (!MI_ReadCache(thread->cache, segment->buffer, segment->offset, segment->length)) { /* Instruct thread to read in case of a cache miss */ segment->buffer = NULL; /* == "could not prepare immediately" */ // NOBLOCK, because it is acceptable for notifications not to line up in the queue if the same ones have accumulated. (void)OS_SendMessage(thread->msg_queue, (OSMessage)TRUE, OS_MESSAGE_NOBLOCK); } } /*---------------------------------------------------------------------------* Name: WFS_ExecuteRomServerThread Description: Registers the specified ROM file to the WFS library so that it will be distributed, and internally automatically starts up the ROM access thread. These threads are automatically destroyed in the WFS_EndServer function. Arguments: context: The WFSServerContext structure. file: The SRL file that contains the file system to be distributed. Specify NULL for clone boot. sharedFS: TRUE if the file system is to be shared by the parent and child. In such a case, a combined file system is distributed. For this combined file system, only the overlay held by 'file' is extracted and appended to the file system of the parent device itself. If 'file' is set to NULL, it specifies clone boot, so this argument is ignored. (It is interpreted as always TRUE.) Returns: TRUE if ROM file registration and thread creation succeed. *---------------------------------------------------------------------------*/ BOOL WFS_ExecuteRomServerThread(WFSServerContext *context, FSFile *file, BOOL sharedFS) { BOOL retval = FALSE; WFSServerThread *thread = MI_CallAlloc(context->allocator, sizeof(WFSServerThread), 32); if (!thread) { OS_TWarning("failed to allocate memory enough to create internal thread."); } else { /* Determine whether this is single ROM mode, clone boot mode, or shared FS mode */ u32 pos = file ? (FS_GetFileImageTop(file) + FS_GetFilePosition(file)) : 0; u32 fatbase = (u32)((file && !sharedFS) ? pos : 0); u32 overlay = (u32)(file ? pos : 0); /* Initialize the ROM access device and cache */ FS_InitFile(thread->file); if (!FS_CreateFileFromRom(thread->file, 0, 0x7FFFFFFF)) { OS_TPanic("failed to create ROM-file!"); } MI_InitDevice(thread->device, thread, WFSi_ReadRomCallback, WFSi_WriteRomCallback); MI_InitCache(thread->cache, WFS_FILE_CACHE_SIZE, thread->cache_buf, sizeof(thread->cache_buf)); /* Register file table */ if (WFS_RegisterServerTable(context, thread->device, fatbase, overlay)) { /* Specify a hook and start a thread if the process was successful */ context->thread_work = thread; context->thread_hook = WFSi_ThreadHook; OS_InitMessageQueue(thread->msg_queue, thread->msg_array, 1); OS_CreateThread(thread->thread, WFSi_ServerThreadProc, thread, thread->thread_stack + sizeof(thread->thread_stack), sizeof(thread->thread_stack), 15); OS_WakeupThreadDirect(thread->thread); retval = TRUE; } else { MI_CallFree(context->allocator, thread); } } return retval; } /*---------------------------------------------------------------------------* $Log: wfs_thread.c,v $ Revision 1.1 2007/06/14 13:14:46 PM yosizaki Initial upload. $NoKeywords: $ *---------------------------------------------------------------------------*/