/*---------------------------------------------------------------------------*
  Project:  NitroSDK - wireless_shared - demos - wfs
  File:     wfs_thread.c

  Copyright 2003,2004 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.

  $Log: wfs_thread.c,v $
  Revision 1.1  2004/11/16 01:12:55  yosizaki
  moved from /demos/wbt/wbt-fs.


 -[old log at /demos/wbt/wbt-fs/wfs]-

  Revision 1.9  2004/09/29 07:11:23  yosizaki
  merge from branch.

  Revision 1.8  2004/09/15 09:10:25  yosizaki
  add Seek&Read.

  Revision 1.7  2004/09/09 05:03:18  yosizaki
  fix file page-cache.

  Revision 1.6  2004/09/07 03:05:51  yosizaki
  source code Maintenance.

  Revision 1.5  2004/09/06 06:23:37  yosizaki
  add double-buffering system.

  Revision 1.4  2004/09/04 08:29:53  yosizaki
  change around PREPARE-response.

  Revision 1.3  2004/09/04 01:45:21  yosizaki
  delete obsolete codes and waste parameters.

  Revision 1.2  2004/09/03 11:35:48  yosizaki
  support page-cache (test version)

  Revision 1.1  2004/09/03 09:58:10  yosizaki
  (none)

  $NoKeywords: $
 *---------------------------------------------------------------------------*/


#include <nitro.h>
#include <nitro/fs.h>
#include <nitro/card.h>

#include "wfs.h"
#include "wfs_private.h"


/********************************************************************/
/* Variables */

typedef	struct
{
	u8	stack[0x400];
	OSThread	th[1];
	OSThreadQueue	th_q[4 / sizeof(OSThreadQueue)];
} WFSiTask;

static WFSiTask	wfsi_task[1] ATTRIBUTE_ALIGN(4);


/********************************************************************/
/* Functions */

/* Notify task to file management thread */
void	WFSi_NotifyBusy(void)
{
	OS_WakeupThreadDirect(wfsi_task->th);
}

/* File management thread */
static void	WFSi_TaskThread(void *arg)
{
	WFSiTask	* const p_task = (WFSiTask*)arg;
	WFSParentContext	* const p_parent = wfsi_work->context.parent;

	int	i;
	FSFileID	file_id;
	file_id.arc = FS_FindArchive("rom", 3);

	WFS_DEBUG_OUTPUT("task : started\n");
	for( ; ; ) {
		WFSiFileList	*pb = NULL;

		{	/* Wait for a task that has busy status. */
			OSIntrMode	bak_cpsr = OS_DisableInterrupts();
			while((pb = p_parent->busy_list) == NULL) {
				/* If already finished, end the thread. */
				if(wfsi_work->state == WFS_STATE_STOP) {
					(void)OS_RestoreInterrupts(bak_cpsr);
					return;
				}
				OS_SleepThread(NULL);
			}
			(void)OS_RestoreInterrupts(bak_cpsr);
		}

		/* In the case of OPENFILE new */
		if(pb->stat == WFS_FILE_STAT_OPENING) {
			WFS_DEBUG_OUTPUT("task : openfile(%08X)\n", pb->info.data_info.id);
			{	/* Open file and read the first page to cache */
				(void)FS_OpenFileDirect(&pb->file, file_id.arc,
					pb->rom_src, pb->rom_src + pb->rom_len, pb->own_id);
				for( i = 0 ; i < WFS_FILE_CACHE_LINE ; ++i ) {
					pb->page[i] = WFS_FILE_CACHE_SIZE * i;
					(void)FS_ReadFile(&pb->file, pb->cache[i], WFS_FILE_CACHE_SIZE);
				}
				pb->ack_seq = 0;
				pb->last_page = WFS_FILE_CACHE_LINE - 1;
				pb->busy_page = WFS_FILE_CACHE_LINE;
			}
			{	/* Notify WBT and move to registered list. */
				OSIntrMode	bak_cpsr = OS_DisableInterrupts();
				WFSi_SendOpenAck(p_parent, pb, TRUE);
				(void)OS_RestoreInterrupts(bak_cpsr);
			}
		}
		/* In the case of CLOSEFILE delete */
		else {
			WFS_DEBUG_OUTPUT("task : closefile(%08X)\n", pb->info.data_info.id);
			{	/* Simply close the file. */
				(void)FS_CloseFile(&pb->file);
			}
			{	/* Move to free list. */
				OSIntrMode	bak_cpsr = OS_DisableInterrupts();
				WFSi_FromBusyToFree(p_parent, pb);
				(void)OS_RestoreInterrupts(bak_cpsr);
			}
		}
	}
}

/* Start a file management thread */
void	WFSi_CreateTaskThread(void)
{
	WFSiTask	* const p = wfsi_task;

#if	!defined(SDK_NO_THREAD)
	/* Start a task thread */
	OS_InitThreadQueue(p->th_q);
	OS_CreateThread(p->th, WFSi_TaskThread, p,
		p->stack + sizeof(p->stack), sizeof(p->stack), 15);
	OS_WakeupThreadDirect(p->th);
#endif
}

/* End the management thread */
void	WFSi_EndTaskThread(void)
{
	WFSiTask	* const p = wfsi_task;

	/* Wait for thread to end. */
	while(!OS_IsThreadTerminated(p->th)) {
		OS_WakeupThreadDirect(p->th);
		OS_JoinThread(p->th);
	}
}
