/*---------------------------------------------------------------------------*
  Project:  NitroSDK - wireless_shared - demos - wfs
  File:     wfs_common.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_common.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.23  2004/10/25 04:20:09  yosizaki
  add WFS_InitParent and WFS_InitChild.
  add some getter functions.

  Revision 1.22  2004/10/18 00:17:29  yosizaki
  fix WFSi_OnChildSystemCallback().

  Revision 1.21  2004/10/15 09:02:09  yosizaki
  merge from branch.

  Revision 1.20  2004/10/13 02:17:39  yosizaki
  fix WFS_end in child-mode. (add async-cancelling)

  Revision 1.19  2004/09/29 07:31:45  yosizaki
  change WFS_Init.
  change assertion of getter functions.

  Revision 1.18  2004/09/29 07:11:38  yosizaki
  merge from branch.

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

  Revision 1.16  2004/09/15 01:11:06  yosizaki
  add WFS_SetPacketSize().

  Revision 1.15  2004/09/11 08:11:01  yosizaki
  fix around recv_idle.

  Revision 1.14  2004/09/11 04:13:10  yosizaki
  unify MP-size definition.

  Revision 1.13  2004/09/09 14:15:48  yosizaki
  fix WFS_GetCurrentDownloadProgress().

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

  Revision 1.11  2004/09/07 04:08:59  yosizaki
  change to delay ack-timing.

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

  Revision 1.9  2004/09/06 08:47:48  yosizaki
  fix archive. (around result of FS_ReadFile())

  Revision 1.8  2004/09/06 06:23:41  yosizaki
  add double-buffering system.

  Revision 1.7  2004/09/04 13:23:55  yosizaki
  change to load TABLE-memory with WFSi_Alloc.

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

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

  Revision 1.4  2004/09/03 12:24:05  yosizaki
  change names. (WBT_RegisterUserDefinedBlock, WBT_UnRegisterBlock)

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

  Revision 1.2  2004/09/03 09:58:38  yosizaki
  add wfs_thread.c

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

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

#include <nitro.h>

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


/*
 * Implementation notes:
 *  The parent does not instantly respond to individual requests.
 * It always sends timed to WBT_PutUserData() free status.
 *  Make sure that the child does not send the next request without waiting for a response.
 */

/******************************************************************************/
/* Constants */

/*
 * Default WBT block list user definition ID.
 * Basically, WFS uses PutUserData to control.
 * It does not communicate via this information region.
 */
static const u8	wfsi_def_user_data[WBT_USER_ID_LEN] = { 0, } ;


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

/* WFS work variables */
WFSWork	wfsi_work[1] ATTRIBUTE_ALIGN(32);
static BOOL	init_flag = FALSE;
BOOL	wfsi_debug_enable;


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

static void	WFSi_ReallocBitmap(WFSChildContext *p_child, int size);


/* Message *****************************************************************/

/* Send message callback function */
static void	WFSi_OnSendMessageDone(void *arg)
{
	WFSWork	* const pw = wfsi_work;

	(void)arg;
	if(pw->state != WFS_STATE_STOP) {
		if(pw->is_parent) {
			pw->context.parent->msg_busy = FALSE;
			WFSi_SendAck();
		}
	}
}

/*
 * Send message using WBT user data command.
 */
static inline void	WFSi_SendMessage(u8 type, u16 bitmap, u32 id, u32 flag/* = 0 */)
{
	WFSiMessage	packet;
	packet.type = type;
	packet.flag = flag;
	packet.arg = id;
	packet.pck_h = (u8)(wfsi_work->parent_packet_size >> 8);
	packet.pck_l = (u8)(wfsi_work->parent_packet_size >> 0);
	(void)WBT_PutUserData(bitmap, &packet, WBT_SIZE_USER_DATA, WFSi_OnSendMessageDone);
}

/* If there is an unsent Ack, send it. (parent) */
void	WFSi_SendAck(void)
{
	WFSWork	* const pw = wfsi_work;
	WFSParentContext	* const p_parent = pw->context.parent;

	/* Ignore if a parent and not in PutUserData send enabled state. */
	if(pw->is_parent) {
		if(!p_parent->msg_busy) {

			/* Reflect the current valid communication status to each bitmap.*/
			p_parent->ack_bitmap &= p_parent->all_bitmap;
			p_parent->sync_bitmap &= p_parent->all_bitmap;
			p_parent->busy_bitmap &= p_parent->all_bitmap;
			p_parent->deny_bitmap &= p_parent->all_bitmap;

			/* Packet size change waits until all current receive processes complete. */
			if(p_parent->is_changing && !p_parent->busy_count) {
				/* Packet size change */
				BOOL	ret;
				const int	size_p = p_parent->new_packet_size;
				const int	size_c = pw->child_packet_size;
				p_parent->is_changing = FALSE;
				pw->parent_packet_size = size_p;
				ret = WBT_SetPacketSize(size_p, size_c);
				WFS_DEBUG_OUTPUT("%s to WBT_SetPacketSize(%d, %d)\n",
					ret ? "succeeded" : "failed", size_p, size_c);
				/* At this point requests made with the old size will be collectively denied. */
				if(p_parent->deny_bitmap) {
					WFS_DEBUG_OUTPUT("send : deny openfile(%08X)\n", p_parent->deny_bitmap);
					WFSi_SendMessage(WFS_MSG_OPENFILE_ACK, (u16)p_parent->deny_bitmap, 0, FALSE);
					p_parent->msg_busy = TRUE;
					p_parent->deny_bitmap = 0;
				}
			}

			/* Requests accepted before the packet size change are supported as they are. */
			else
			{
				int	bitmap = p_parent->ack_bitmap;

				/* If the response is empty, it will be ignored this time. */
				if(bitmap) {

					WFSiMessage	*msg = NULL;
					int	i;
					const int	sync = p_parent->sync_bitmap;
					const BOOL	is_sync = (sync && ((bitmap & sync) == sync));

					/*           Respond collectively when the group of children specified for synchronization is ready. */
					if(is_sync)
						bitmap = sync;
					/* If not, normal response. */
					else
						bitmap &= ~sync;

					/* Search for relevant response capable children */
					for( i = 0 ; ; ++i ) {
						const int	bit = (1 << i);
						if(bit > bitmap)
							break;
						if((bit & bitmap) != 0) {
							/* Search for response capable children in order from the smallest aid. */
							if(!msg)
								msg = &p_parent->recv_msg[i];
							/* If the same type of response, send collectively. */
							else if((msg->type == p_parent->recv_msg[i].type) &&
								(msg->arg == p_parent->recv_msg[i].arg))
								;
							/* This time defer other children.*/
							else
								bitmap &= ~bit;
						}
					}

					/*
					 * NOTE:
					 *   If the application has this problem
					 *   "Although sync is specified, request content differs"
					 *   it is not possible to determine whether
					 *   simply one of the synch contents is out of synch, or
					 *   if they are out of sync, which should respond first to recover, or
					 *   they are not out of sync and the request content is fatally different.
					 *   Therefore, recover with
					 *   "Issuing a warning and automatically releasing sync specification."
					 */
					if(is_sync && (bitmap != sync)) {
						p_parent->sync_bitmap = 0;
						OS_TWarning(
							"[WFS] specified synchronous-access failed! "
							"(then synchronous-setting was reset)");
					}

					/* Send the response that was selected this time. */
					if(msg) {
						switch(msg->type) {
						case WFS_MSG_OPENFILE_REQ:
							msg->type = WFS_MSG_OPENFILE_ACK;
							break;
						case WFS_MSG_CLOSEFILE_REQ:
							msg->type = WFS_MSG_CLOSEFILE_ACK;
							p_parent->busy_bitmap &= ~bitmap;
							break;
						}
						WFSi_SendMessage((u8)msg->type, (u16)bitmap, msg->arg, TRUE);
						p_parent->msg_busy = TRUE;
						p_parent->ack_bitmap &= ~bitmap;
					}
				}
			}
		}
	}
}

/*
 * File preparation is OK. Respond to OPENFILE_REQ.
 * If it is a new file, register it.
 */
void	WFSi_SendOpenAck(WFSParentContext *p, WFSiFileList *trg, BOOL ret)
{
	OSIntrMode	bak_cpsr = OS_DisableInterrupts();

	if(wfsi_work->state != WFS_STATE_STOP) {

		/* Add those for which preparation completed this time to the response capable bitmap. */
		int	bitmap = trg->req_bitmap;
		trg->req_bitmap = 0;
		p->ack_bitmap |= bitmap;

		/* If a new file, move to registered list. */
		if(trg->ref <= 0) {
			/*
			 * If the access region is within the cache page range,
			 * specify the cache pointer as-is.
			 * If it is larger than that, use a callback from WBT
			 * to do paging and operate as with virtual memory.
			 */
			const u32	id = trg->own_id;
			const u32	size = FS_GetLength(&trg->file);
			const void	*src = (size <= WFS_FILE_CACHE_SIZE) ?
				trg->cache : NULL;
			(void)WBT_RegisterBlock(&trg->info, id,
				wfsi_def_user_data, src, size, 0);
			WFSi_FromBusyToAlive(p, trg);
		}

		{	/* Increment reference count */
			int	i;
			for( i = 0 ; ; ++i ) {
				const int	bit = (1 << i);
				if(bit > bitmap)
					break;
				if((bit & bitmap) != 0) {
					++trg->ref;
					/* Store block ID for reply */
					p->recv_msg[i].arg = trg->own_id;
				}
			}
		}

		(void)ret;
	}

	(void)OS_RestoreInterrupts(bak_cpsr);
}


/* List operation *****************************************************************/

/* Search currently registered files. */
WFSiFileList*	WFSi_FindAlive(WFSParentContext *p, u32 top, u32 len)
{
	WFSiFileList	*pb;
	for( pb = p->alive_list ; pb ; pb = pb->next ) {
		if((pb->stat == WFS_FILE_STAT_ALIVE) &&
			(FS_GetFileImageTop(&pb->file) == top) &&
			(FS_GetLength(&pb->file) == len))
			break;
	}
	return	pb;
}

/* Search files currently being prepared for registration. */
WFSiFileList*	WFSi_FindBusy(WFSParentContext *p_parent, u32 src, u32 len)
{
	WFSiFileList	*trg;
	/* Search files currently being prepared for registration. */
	for( trg = p_parent->busy_list ; trg ; trg = trg->next ) {
		if((trg->stat == WFS_FILE_STAT_OPENING) &&
			(trg->rom_src == src) && (trg->rom_len == len))
			break;
	}
	/* If new, add from free list to in preparation list. */
	if(!trg) {
		trg = WFSi_FromFreeToBusy(p_parent);
		trg->ref = 0;
		trg->rom_src = src;
		trg->rom_len = len;
		WFSi_NotifyBusy();
	}
	return	trg;
}

/* Search for file registered with specified ID. */
static WFSiFileList*	WFSi_FindAliveForID(WFSParentContext *p_parent, u32 id)
{
	WFSiFileList	*trg;
	for( trg = p_parent->alive_list ; trg ; trg = trg->next ) {
		if((trg->stat == WFS_FILE_STAT_ALIVE) &&
			(trg->info.data_info.id == id))
			break;
	}
	return	trg;
}

/* Move between lists */
static void	WFSi_MoveList(WFSiFileList **src, WFSiFileList **dst, WFSiFileList *trg)
{
	OSIntrMode	bak_cpsr = OS_DisableInterrupts();
	/* Look for self from move source list, insert in the front of the destination list. */
	WFSiFileList	*pb = *src;
	if(pb == trg) {
		*src = trg->next;
		trg->next = *dst;
		*dst = trg;
	} else {
		for(  ; pb ; pb = pb->next ) {
			if(pb->next == trg) {
				pb->next = trg->next;
				trg->next = *dst;
				*dst = trg;
			}
			break;
		}
	}
	(void)OS_RestoreInterrupts(bak_cpsr);
}

/*
 * Move file list status from free to in preparation.
 * Parent reference count ++
 */
WFSiFileList*	WFSi_FromFreeToBusy(WFSParentContext *p_parent)
{
	WFSiFileList	* const trg = p_parent->free_list;
	/* Free definitely exists. */
	SDK_ASSERT(trg);
	WFSi_MoveList(&(p_parent->free_list), &(p_parent->busy_list), trg);
	trg->stat = WFS_FILE_STAT_OPENING;
	++p_parent->busy_count;
	return	trg;
}

/*
 * Move file list status from in preparation to registered.
 */
void	WFSi_FromBusyToAlive(WFSParentContext *p, WFSiFileList *trg)
{
	WFSi_MoveList(&(p->busy_list), &(p->alive_list), trg);
	trg->stat = WFS_FILE_STAT_ALIVE;
}

/*
 * Move file list status from registered to releasing.
 */
void	WFSi_FromAliveToBusy(WFSParentContext *p, WFSiFileList *trg)
{
	WFSi_MoveList(&(p->alive_list), &(p->busy_list), trg);
	trg->stat = WFS_FILE_STAT_CLOSING;
}

/*
 * Move file list status from in preparation to free.
 * Parent reference count --
 */
void	WFSi_FromBusyToFree(WFSParentContext *p, WFSiFileList *trg)
{
	WFSi_MoveList(&(p->busy_list), &(p->free_list), trg);
	trg->stat = WFS_FILE_STAT_FREE;
	--p->busy_count;
}

/*
 * Read a specified file (child )
 */
void	WFSi_ReadRequest(FSFile *p_file)
{
	WFSChildContext	* const p_child = wfsi_work->context.child;
	/* OpenFile request without storing specified file. */
	WFSi_ReallocBitmap(p_child, p_file->arg.readfile.len);
	wfsi_work->target_file = p_file;
	WFSi_SendMessage(
		WFS_MSG_OPENFILE_REQ,
		WFS_BITMAP_TO_PARENT,
		p_child->base_offset + p_file->prop.file.pos,
		p_file->arg.readfile.len);
}


/* WM *************************************************************************/

static void	WFSi_SetMPData(void);
static void	WFSi_OnSetMPDataDone(void *arg);
static void	WFSi_PortCallback(void *arg);

/* Packet send processes common to parent and child */
static void	WFSi_SetMPData(void)
{
	WFSWork	* const pw = wfsi_work;

	u16	size;
	if(pw->is_parent)
		size = (u16)pw->parent_packet_size;
	else
		size = (u16)pw->child_packet_size;

	/*Confirm completion of send/receive of previous frame */
	if(pw->send_idle) {
		int	ret;
		void	*buf = pw->send_buf;
		if(pw->is_parent)
			size = (u16)WBT_MpParentSendHook(buf, size);
		else
			size = (u16)WBT_MpChildSendHook(buf, size);
		ret = WM_SetMPDataToPort(
			WFSi_OnSetMPDataDone, buf, size, 0xFFFF,
			(u16)pw->port, WM_PRIORITY_LOW);
		pw->is_running = (ret == WM_ERRCODE_OPERATING);
		pw->send_idle = !wfsi_work->is_running;
	}
}

/* Process when SetMPData completes. */
static void	WFSi_OnSetMPDataDone(void *arg)
{
	WFSWork	* const pw = wfsi_work;

	(void)arg;

	if(pw->state != WFS_STATE_STOP) {
		pw->send_idle = TRUE;
		WFSi_SendAck();
		WFSi_SetMPData();
	}
}

/* Port receive callback */
static void	WFSi_PortCallback(void *arg)
{
	WFSWork	* const pw = wfsi_work;
	WFSParentContext	* const p_parent = pw->context.parent;

	if(pw->state != WFS_STATE_STOP) {

		const WMPortRecvCallback	* const p_recv =
			(const WMPortRecvCallback*)arg;

		switch(p_recv->state) {

		case WM_STATECODE_CONNECTED:
			/* If is a parent and is not running, start connecting child to it. */
			if(pw->is_parent) {
				if(!pw->is_running)
					WFSi_SetMPData();
			}
			break;

		case WM_STATECODE_DISCONNECTED:
			if(pw->is_parent) {
				/* Remove detected child that is using WBT port from bitmap. */
				const int	aid = p_recv->aid;
				p_parent->all_bitmap &= ~(1 << aid);
			}
			break;

		case WM_STATECODE_PORT_RECV:
			{	/* Hook packet received from other party to WBT */
				const void	*src = p_recv->data;
				const int	len = p_recv->length;
				if(pw->is_parent) {
					/* Add detected child that is using WBT port to bitmap. */
					const int	aid = p_recv->aid;
					p_parent->all_bitmap |= (1 << aid);
					WBT_MpParentRecvHook(src, len, aid);
				}
				else {
					WBT_MpChildRecvHook(src, len);
				}
			}
			break;

		}

	}
}


/* WBT ************************************************************************/

static void	WFSi_OnParentSystemCallback(void *arg);
static void	WFSi_OnChildSystemCallback(void *arg);


/* Parent side callback function */
static void	WFSi_OnParentSystemCallback(void *arg)
{
	WBTCommand *uc = (WBTCommand*)arg;
	const int	peer_aid = WBT_AidbitmapToAid(uc->peer_bmp);

	switch(uc->command) {

	default:
		WFS_DEBUG_OUTPUT("invalid code %d\n", uc->command);
		break;

	case WBT_CMD_SYSTEM_CALLBACK:
		/* Indication */

		switch(uc->event) {
		case WBT_CMD_REQ_USER_DATA:
			/* WFS fixed message notice */
			{
				const WFSiMessage	* const msg =
					(const WFSiMessage*)uc->user_data.data;

				/* Store received message. */
				wfsi_work->context.parent->recv_msg[peer_aid] = *msg;

				switch(msg->type) {

				case WFS_MSG_OPENFILE_REQ:
					/* OPENFILE request*/
					WFS_DEBUG_OUTPUT("recv : WFS_MSG_OPENFILE_REQ(%08X[%8X]) from %02X\n",
						msg->arg, msg->flag, uc->peer_bmp);
					{
						const u32	src = msg->arg;
						const u32	len = msg->flag;
						const int	packet_size = ((msg->pck_h << 8) | msg->pck_l);

						WFSParentContext	* const p_parent = wfsi_work->context.parent;

						WFS_DEBUG_OUTPUT("       packet-size(%d)\n", packet_size);

						/*
						 * While changing packet size, if requests are made with the old size,
						 * wait for the current receive process to complete and then respond with a denial.
						 */
						if(p_parent->is_changing ||
							(packet_size != wfsi_work->parent_packet_size)) {
							p_parent->is_changing = TRUE;
							p_parent->deny_bitmap |= (1 << peer_aid);
							p_parent->busy_bitmap |= (1 << peer_aid);
						}
						/*
						 * If in normal status, search list for identical request content.
						 */
						else {
							WFSiFileList	*trg;

							/* If currently registered, reference it. */
							trg = WFSi_FindAlive(p_parent, src, len);
							if(trg) {
								p_parent->ack_bitmap |= (1 << peer_aid);
								p_parent->recv_msg[peer_aid].arg = trg->own_id;
								++trg->ref;
							}
							else {
								/*
								 * If in preparation, reference it.
								 * If not add new task.
								 */
								trg = WFSi_FindBusy(p_parent, src, len);
								trg->req_bitmap |= (1 << peer_aid);
							}
							p_parent->busy_bitmap |= (1 << peer_aid);
						}
					}
					break;

				case WFS_MSG_CLOSEFILE_REQ:
					/* CLOSEFILE request */
					WFS_DEBUG_OUTPUT("recv : WFS_MSG_CLOSEFILE_REQ(%08X) from %02X\n", msg->arg, uc->peer_bmp);
					{
						WFSParentContext	* const p_parent = wfsi_work->context.parent;

						{
							OSIntrMode	bak_cpsr = OS_DisableInterrupts();

							/* Only response messages are returned instantly. */
							p_parent->ack_bitmap |= (1 << peer_aid);

							{	/* Ignore if there are no registered files. */
								WFSiFileList	*trg = WFSi_FindAliveForID(p_parent, msg->arg);
								if(trg) {
									/* If last reference is lost, free process */
									/* Can this be pushed to WFSi_FindAliveForID()? */
									if(--trg->ref <= 0) {
										trg->req_bitmap = 0;
										(void)WBT_UnregisterBlock(msg->arg);
										WFSi_FromAliveToBusy(p_parent, trg);
										WFSi_NotifyBusy();
									}
								}
							}
							(void)OS_RestoreInterrupts(bak_cpsr);
						}
					}
					break;

				}
			}
			break;

		case WBT_CMD_REQ_SYNC:
			/*
			 * Synch notification from child
			 * No particular operation needs to be done.
			 */
			break;

		case WBT_CMD_PREPARE_SEND_DATA:
			/*
			 * Request to prepare virtual page.
			 * Provide data pointer if preparation can be done here.
			 * If not, prepare for the next request.
			 */
			{
				WFSParentContext	* const p_parent = wfsi_work->context.parent;
				WFSiFileList	*trg;

				WBTPrepareSendDataCallback	* const p_prep = &uc->prepare_send_data;

				const u32	id = p_prep->block_id;

				p_prep->data_ptr = NULL;

				/* Determine whether file is registered. */
				trg = WFSi_FindAliveForID(p_parent, id);
				if(trg) {

					const int	width = p_prep->own_packet_size;
					const int	bak_req_seq = trg->ack_seq;
					const int	new_req_seq = p_prep->block_seq_no;
					int	cur_page;

					/* If cache is being reloaded, check for completion. */
					if((trg->busy_page != WFS_FILE_CACHE_LINE) &&
						!FS_IsBusy(&trg->file))
						trg->busy_page = WFS_FILE_CACHE_LINE;

					/* Respond to this time's request number the next time. */
					trg->ack_seq = new_req_seq;

					/* Search for the page that cache hits the previous request number. */
					{
						const int	pos = width * bak_req_seq;
						for( cur_page = 0 ; cur_page < WFS_FILE_CACHE_LINE ; ++cur_page ) {
							if(cur_page != trg->busy_page) {
								const int	ofs = pos - trg->page[cur_page];
								if((ofs >= 0) && (ofs + width <= WFS_FILE_CACHE_SIZE)) {
									p_prep->block_seq_no = bak_req_seq;
									p_prep->data_ptr = trg->cache[cur_page] + ofs;
									WFS_DEBUG_OUTPUT("       response now!(%5d)\n", bak_req_seq);
									break;
								}
							}
						}
						if(cur_page >= WFS_FILE_CACHE_LINE)
							WFS_DEBUG_OUTPUT("           cache missed!(%5d)\n", bak_req_seq);
					}

					/* Determine whether it will cache miss this time's request number. */
					if(trg->busy_page == WFS_FILE_CACHE_LINE) {
						int	i;
						int	pos = width * new_req_seq;
						for( i = 0 ; i < WFS_FILE_CACHE_LINE ; ++i ) {
							const int	ofs = pos - trg->page[i];
							if((ofs >= 0) && (ofs + width <= WFS_FILE_CACHE_SIZE)) {
								break;
							}
						}
						/* In the case of cache miss, reload in round robin. */
						if(i >= WFS_FILE_CACHE_LINE) {
							int	new_page = trg->last_page;
							do {
								++new_page;
								if(new_page >= WFS_FILE_CACHE_LINE)
									new_page = 0;
							} while(new_page == cur_page);
							trg->last_page = new_page;
							trg->busy_page = new_page;
							pos &= ~(CARD_ROM_PAGE_SIZE - 1);
							trg->page[new_page] = pos;
							(void)FS_SeekFile(&trg->file, pos, FS_SEEK_SET);
							(void)FS_ReadFileAsync(&trg->file, trg->cache[new_page], WFS_FILE_CACHE_SIZE);
							WFS_DEBUG_OUTPUT("               ... and now loading!(%5d)\n", new_req_seq);
						}
					}
				}

			}
			break;

		case WBT_CMD_REQ_GET_BLOCK_DONE:
			break;

		default:
			WFS_DEBUG_OUTPUT("sys-callback code %d\n", uc->event);
			break;
		}
		break;
	}
}

/*
 * Reallocate bitmap that can receive the specified file size.
 * If size < 0, unconditionally reallocate with current settings.
 */
static void	WFSi_ReallocBitmap(WFSChildContext *p_child, int size)
{
	if(size < 0) {
		size = p_child->max_file_size;
		p_child->max_file_size = 0;
	}
	if(p_child->max_file_size < size) {
		p_child->max_file_size = size;
		WFS_DEBUG_OUTPUT("WFSi_ReallocBitmap...(%d:%d)->%d\n",
			wfsi_work->parent_packet_size, p_child->max_file_size,
			WBT_PACKET_BITMAP_SIZE(p_child->max_file_size, wfsi_work->parent_packet_size));
		WFSi_Free(p_child->recv_pkt_bmp_buf);
		p_child->recv_pkt_bmp_buf = (u32*)(WFSi_Alloc(sizeof(u32) *
			WBT_PACKET_BITMAP_SIZE(p_child->max_file_size, wfsi_work->parent_packet_size)));
		p_child->recv_buf_packet_bmp_table.packet_bitmap[0] = p_child->recv_pkt_bmp_buf;
	}
	SDK_ASSERT(p_child->recv_pkt_bmp_buf);
}


/* Child side callback function */
void	WFSi_OnChildSystemCallback(void *arg)
{
	WBTCommand	*uc = (WBTCommand*)arg;
	int	peer_aid = WBT_AidbitmapToAid(uc->peer_bmp);
	WFSChildContext	* const p_child = wfsi_work->context.child;

	switch( uc->command )
	{
	case WBT_CMD_REQ_SYNC:
		//* Complete WBT_RequestSync */
		if(!uc->target_bmp)
		{
			/*
			 * Acquire MP packet size set by parent.
			 * This size is used for sequence number management
			 * and by WM_SetMPDataToPort. Both are required.
			 */
			wfsi_work->parent_packet_size = uc->sync.peer_packet_size + WBT_PACKET_SIZE_MIN;
			wfsi_work->child_packet_size = uc->sync.my_packet_size + WBT_PACKET_SIZE_MIN;
			WFS_DEBUG_OUTPUT("Sync(%d, %d)\n",
				wfsi_work->parent_packet_size, wfsi_work->child_packet_size);
			/*
			 * The first time acquire total size of FAT+FNT+OVT information.
			 * The parent assures that this is always at the head of the block list (index=0).
			 *
			 */
			if(!wfsi_work->table) {
				(void)WBT_GetBlockInfo(WFS_BITMAP_TO_PARENT,
					0, &p_child->block_info_table,
					WFSi_OnChildSystemCallback);
			}
			/*
			 * In the case of doing sync again, it will be considered that OpenFile was denied.
			 * Therefore it will retry the same request.
			 */
			else if(wfsi_work->target_file) {
				WFSi_ReallocBitmap(p_child, -1);
				WFSi_ReadRequest(wfsi_work->target_file);
			}
		}
		break;

	case WBT_CMD_REQ_GET_BLOCKINFO:
		/* Complete WBT_GetBlockInfo */
		/*
		 * Load FAT+FNT+OVT information.
		 */
		wfsi_work->table_size = p_child->block_info_table.block_info[peer_aid]->block_size;
		wfsi_work->table = (u8*)WFSi_Alloc(wfsi_work->table_size);
		SDK_ASSERT(wfsi_work->table);
		WFSi_ReallocBitmap(p_child, wfsi_work->table_size);
		p_child->recv_buf_table.recv_buf[peer_aid] = wfsi_work->table;
		(void)WBT_GetBlock(WFS_BITMAP_TO_PARENT,
			WFS_FILE_TO_BLOCK(WFS_FILE_TABLEINFO),
			&p_child->recv_buf_table,
			wfsi_work->table_size,
			&p_child->recv_buf_packet_bmp_table,
			WFSi_OnChildSystemCallback);
		break;

	case WBT_CMD_REQ_GET_BLOCK:
		/*
		 * Complete WBT_GetBlock
		 * This becomes a receive complete notification for
		 * either FAT+FNT+OVT information, or a normal file.
		 */
		if(!uc->target_bmp)
		{
			/* Restructure archive if table load is complete. */
			if(wfsi_work->state != WFS_STATE_READY) {
				DC_FlushRange(wfsi_work->table, wfsi_work->table_size);
				p_child->base_offset = WFSi_ReplaceRomArchive(wfsi_work->table);
				/* Use callback to notify move to FS enabled status. */
				wfsi_work->state = WFS_STATE_READY;
				if(wfsi_work->state_func) {
					(*wfsi_work->state_func)(NULL);
				}
			}
			/* Otherwise complete ReadFile */
			else {
				/* CloseFile request */
				WFSi_SendMessage(
					WFS_MSG_CLOSEFILE_REQ,
					WFS_BITMAP_TO_PARENT,
					p_child->block_id,
					TRUE);
			}
		}
		break;

	default:
		WFS_DEBUG_OUTPUT("invalid code %d\n", uc->command);
		break;

	/* Indication */
	case WBT_CMD_SYSTEM_CALLBACK:

		switch(uc->event) {
		case WBT_CMD_REQ_USER_DATA:

			/* Notification */
			{
				const WFSiMessage	* const msg = (const WFSiMessage*)uc->user_data.data;

				switch(msg->type) {

				case WFS_MSG_OPENFILE_ACK:
					WFS_DEBUG_OUTPUT("recv : WFS_MSG_OPENFILE_ACK(%08X)[%c] from %02X\n",
						msg->arg, (msg->flag ? 'T' : 'F'), uc->peer_bmp);

					/* If it is denied, because the packet size has changed, will retry from Sync */
					if(!msg->flag) {
						/* The first contact with a parent is the Sync command. */
						WFS_DEBUG_OUTPUT("recv : openfile kicked(packet-size changed)\n");
						(void)WBT_RequestSync(WFS_BITMAP_TO_PARENT, WFSi_OnChildSystemCallback);
					}
					/* In line with a permission response, begin receiving the file. */
					else
					{
						/* Begin receiving with the acquired block ID */
						p_child->block_id = msg->arg;
						p_child->recv_buf_table.recv_buf[peer_aid] =
							wfsi_work->target_file->arg.readfile.dst;
						WFS_DEBUG_OUTPUT("send : GetBlock(%08X, %d)\n",
							p_child->block_id,
							wfsi_work->target_file->arg.readfile.len);
						(void)WBT_GetBlock(WFS_BITMAP_TO_PARENT,
							p_child->block_id,
							&p_child->recv_buf_table,
							wfsi_work->target_file->arg.readfile.len,
							&p_child->recv_buf_packet_bmp_table,
							WFSi_OnChildSystemCallback);
					}
					break;

				case WFS_MSG_CLOSEFILE_ACK:
					WFS_DEBUG_OUTPUT("recv : WFS_MSG_CLOSEFILE_ACK(%08X)[%c] from %02X\n",
						msg->arg, (msg->flag ? 'T' : 'F'), uc->peer_bmp);
					{	/* In line with the Closefile response, complete receiving. */
						FSFile	* const trg = wfsi_work->target_file;
						FSArchive	* const p_arc = FS_GetAttachedArchive(wfsi_work->target_file);
						wfsi_work->target_file = NULL;
						/* Advance the file pointer */
						trg->prop.file.pos += trg->arg.readfile.len;
						FS_NotifyArchiveAsyncEnd(p_arc, FS_RESULT_SUCCESS);
					}
					break;

				}
			}
			break;

		default:
			WFS_DEBUG_OUTPUT("sys-callback code %d\n", uc->event);
			break;
		}
		break;
	}
}


/* interface ******************************************************************/

/*---------------------------------------------------------------------------*
  Name:         WFSi_InitCommon

  Description:  initialize Wireless File System common parameters.

  Arguments:    port             available WM port for WFS
                callback         callback of various state-notifications.
                allocator        dynamic memory allocator for WFS
                allocator_arg    argument of 'allocator'

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void	WFSi_InitCommon(int port, WFSStateCallback callback,
	WFSAllocator allocator, void *allocator_arg)
{
	WFSWork	* const pw = wfsi_work;

	/*
	 * Parent and child both need an FS.
	 * If not yet initialized, do it here.
	 * (At the point of doing multiboot, one would think that the parent side would be initialized...)
	 */
	FS_Init(FS_DMA_NOT_USE);
	pw->port = (u16)port;
	pw->send_idle = FALSE;
	pw->is_running = FALSE;

	pw->state = WFS_STATE_IDLE;
	pw->state_func = callback;
	pw->alloc_func = allocator;
	pw->alloc_arg = allocator_arg;
	pw->target_file = NULL;
	pw->table_size = 0;
	pw->table = NULL;

	pw->parent_packet_size = 0;
	pw->child_packet_size = WBT_PACKET_SIZE_MIN;
	wfsi_debug_enable = FALSE;

	/*
	 * Set the callback that drives basic WBT operation.
	 * Fails if still in STOP state.
	 */
	if(WM_SetPortCallback((u16)port, WFSi_PortCallback, NULL)
		!= WM_ERRCODE_SUCCESS) {
		OS_Panic(
			"wireless file-system : "
			"WFS_Init failed! (not called WM_Initialize yet)");
	}
}

/*---------------------------------------------------------------------------*
  Name:         WFS_InitParent

  Description:  initialize Wireless File System parent parameters.

  Arguments:    port             available WM port for WFS
                callback         callback of various state-notifications.
                allocator        dynamic memory allocator for WFS
                allocator_arg    argument of 'allocator'
                parent_packet    available WM packet for WFS
                p_rom            target file which directs SRL-program
                                 to export FAT/FNT and OVT.
                                 if specified NULL, use own.
                use_parent_fs    if TRUE, ignore p_rom's FAT/FNT and use own.
                                 this setting enables both independent
                                 program to share FAT/FNT (except OVT).
                                 if p_rom is NULL, always as TRUE.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void	WFS_InitParent(int port, WFSStateCallback callback,
	WFSAllocator allocator, void *allocator_arg, int parent_packet,
	FSFile *p_rom, BOOL use_parent_fs);


/*---------------------------------------------------------------------------*
  Name:         WFS_InitChild

  Description:  initialize Wireless File System child parameters.

  Arguments:    port             available WM port for WFS
                callback         callback of various state-notifications.
                allocator        dynamic memory allocator for WFS
                allocator_arg    argument of 'allocator'

  Returns:      None.
 *---------------------------------------------------------------------------*/
void	WFS_InitChild(int port, WFSStateCallback callback,
	WFSAllocator allocator, void *allocator_arg);




/*---------------------------------------------------------------------------*
  Name:         WFS_InitParent

  Description:  initialize Wireless File System parent parameters.

  Arguments:    port             available WM port for WFS
                callback         callback of various state-notifications.
                allocator        dynamic memory allocator for WFS
                allocator_arg    argument of 'allocator'
                parent_packet    available WM packet for WFS
                p_rom            target file which directs SRL-program
                                 to export FAT/FNT and OVT.
                                 if specified NULL, use own.
                use_parent_fs    if TRUE, ignore p_rom's FAT/FNT and use own.
                                 this setting enables both independent
                                 program to share FAT/FNT (except OVT).
                                 if p_rom is NULL, always as TRUE.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void	WFS_InitParent(int port, WFSStateCallback callback,
	WFSAllocator allocator, void *allocator_arg, int parent_packet,
	FSFile *p_rom, BOOL use_parent_fs)
{
	OSIntrMode	bak_cpsr = OS_DisableInterrupts();
	if(!init_flag) {
		init_flag = TRUE;
		WFSi_InitCommon(port, callback, allocator, allocator_arg);
		(void)OS_RestoreInterrupts(bak_cpsr);

		{
			WFSWork	* const pw = wfsi_work;
			WFSParentContext	* const p = pw->context.parent;

			pw->is_parent = TRUE;

			DC_FlushRange(p, sizeof(*p));
			DC_WaitWriteBufferEmpty();

			{
				/* Add all block information to free list. */
				enum { BLOCK_TOTAL_COUNT = WFS_FILE_HANDLE_MAX } ;
				WFSiFileList	*pb = &p->list[0];
				p->free_list = pb;
				for( ; pb + 1 < &p->list[BLOCK_TOTAL_COUNT] ; pb = pb + 1 ) {
					pb->next = pb + 1;
					FS_InitFile(&pb->file);
					pb->ref = 0;
					pb->stat = WFS_FILE_STAT_FREE;
					pb->own_id = (u32)(WFS_FILE_TABLEINFO + 1 + pb - &p->list[0]);
				}
				pb->next = NULL;
			}

			p->sync_bitmap = 0;
			p->ack_bitmap = 0;
			p->msg_busy = FALSE;
			p->alive_list = NULL;
			p->busy_list = NULL;
			pw->parent_packet_size = parent_packet;

			p->all_bitmap = 1;
			p->busy_bitmap = 0;

			p->is_changing = FALSE;
			p->deny_bitmap = 0;
			p->busy_count = 0;
			p->new_packet_size = pw->parent_packet_size;

			DC_FlushRange(p, sizeof(*p));
			DC_WaitWriteBufferEmpty();

			/*
			 * Initialize WBT
			 *
			 * If you make the packet size used for WBT smaller than the actual maximum size,
			 * it is possible to add data sharing etc to one MP communication cycle.
			 * However, if you do this, the maximum full time transfer size will decrease.
			 * WFS has the lowest priority and runs in the background.
			 * Therefore allocating the maximum size possible will not cause any particular problems.
			 */
			WBT_InitParent(
				pw->parent_packet_size,
				pw->child_packet_size,
				WFSi_OnParentSystemCallback);

			/*
			 * Read FNT+FAT+OVT information, and register it as a file.
			 * The following selection forks are possible:
			 * (1) Normal independent child program: p_rom && !use_parent_fs
			 * (2) Shared file child program: p_rom && use_parent_fs
			 * (3) Parent clone boot: !p_rom   * Implementation under study
			 */
			WFSi_LoadTables(p_rom, use_parent_fs);
			{
				WFSiFileList	*pb = p->free_list;
				BOOL	ret = FALSE;
				p->free_list = pb->next;
				pb->next = p->alive_list;
				p->alive_list = pb;
				pb->ref = 1;
				MI_CpuClear8(pb, sizeof(*pb));
				ret = WBT_RegisterBlock(&pb->info,
					(u32)WFS_FILE_TO_BLOCK(WFS_FILE_TABLEINFO),
					wfsi_def_user_data, pw->table,
					wfsi_work->table_size, 0);
				SDK_ASSERT(ret);
			}

			/* Start a task thread that automatically processes a child's request. */
			WFSi_CreateTaskThread();
		}
	}
}

/*---------------------------------------------------------------------------*
  Name:         WFS_InitChild

  Description:  initialize Wireless File System child parameters.

  Arguments:    port             available WM port for WFS
                callback         callback of various state-notifications.
                allocator        dynamic memory allocator for WFS
                allocator_arg    argument of 'allocator'

  Returns:      None.
 *---------------------------------------------------------------------------*/
void	WFS_InitChild(int port, WFSStateCallback callback,
	WFSAllocator allocator, void *allocator_arg)
{
	OSIntrMode	bak_cpsr = OS_DisableInterrupts();
	if(!init_flag) {
		init_flag = TRUE;
		WFSi_InitCommon(port, callback, allocator, allocator_arg);
		(void)OS_RestoreInterrupts(bak_cpsr);

		{
			WFSWork	* const pw = wfsi_work;
			WFSChildContext	* const p = pw->context.child;

			pw->is_parent = FALSE;
			DC_FlushRange(p, sizeof(*p));
			DC_WaitWriteBufferEmpty();

			{	/* Initialize a receive array for GetBlockInfo, GetBlock. */
				int	i;
				for( i = 0 ; i < WBT_NUM_OF_AID ; ++i )
				{
					p->block_info_table.block_info[i] = &p->block_info[i];
					p->recv_buf_table.recv_buf[i] = NULL;
					p->recv_buf_packet_bmp_table.packet_bitmap[i] = NULL;
				}
			}

			/* Initialize information that can only be understood when table information is received from the parent. */
			p->recv_pkt_bmp_buf = NULL;
			p->max_file_size = 0;
			p->block_id = 0;

			DC_FlushRange(p, sizeof(*p));
			DC_WaitWriteBufferEmpty();

			/*
			 * Initialize WBT
			 *
			 * Actual process flow:
			 * (1) FS_ReadFile(Async) -> wfs_archive
			 * (2) wfs_archive -> WBT
			 * (3) WBT -> FS_NotifyArchiveAsyncEnd
			 * After this, file access is complete.
			 */
			WBT_InitChild(WFSi_OnChildSystemCallback);
		}
	}
}


/*---------------------------------------------------------------------------*
  Name:         WFS_Init

  Description:  initialize Wireless File System.

  Arguments:    is_parent        if parent, TRUE
                port             available WM port for WFS
                callback         callback of various state-notifications.
                allocator        dynamic memory allocator for WFS
                allocator_arg    argument of 'allocator'

  Returns:      None.
 *---------------------------------------------------------------------------*/
void	WFS_Init(BOOL is_parent, int port, int parent_packet,
	WFSStateCallback callback,
	WFSAllocator allocator, void *allocator_arg)
{
	if(is_parent)
		WFS_InitParent(port, callback, allocator, allocator_arg,
			parent_packet, NULL, TRUE);
	else
		WFS_InitChild(port, callback, allocator, allocator_arg);
}


void	WFS_Start(void)
{
	SDK_ASSERT(init_flag);

	wfsi_work->send_idle = TRUE;

	/* The child side acquires its own AID.*/
	if(!wfsi_work->is_parent) {
		WFSChildContext	* const p = wfsi_work->context.child;
		int	ret = WM_ReadStatus(p->status);
		SDK_ASSERT(ret == WM_ERRCODE_SUCCESS);
		WBT_SetOwnAid(p->status->aid);
		/* The first contact with a parent is the Sync command. */
		(void)WBT_RequestSync(WFS_BITMAP_TO_PARENT, WFSi_OnChildSystemCallback);
	}
	/* The parent side instantly moves to READY status. */
	else {
		wfsi_work->state = WFS_STATE_READY;
		if(wfsi_work->state_func) {
			(*wfsi_work->state_func)(NULL);
		}
	}

	/* After connecting set the first data. */
	WFSi_SetMPData();
}


void	WFS_End(void)
{
	WFSWork	* const pw = wfsi_work;

	OSIntrMode	bak_cpsr = OS_DisableInterrupts();
	if(init_flag) {

		/* Free the table data. */
		if(pw->table) {
			WFSi_Free(pw->table);
			pw->table = NULL;
			pw->table_size = 0;
		}

		pw->state = WFS_STATE_STOP;

		/* In the case of a child, if processing, stop and free the bitmap. */
		if(!wfsi_work->is_parent) {
			WFSChildContext	* const p_child = pw->context.child;
			if(pw->target_file) {
				FSFile	* const trg = pw->target_file;
				FSArchive	* const p_arc = FS_GetAttachedArchive(trg);
				pw->target_file = NULL;
				FS_NotifyArchiveAsyncEnd(p_arc, FS_RESULT_ERROR);
			}
			WFSi_Free(p_child->recv_pkt_bmp_buf);
		}
		/* In the case of a parent, terminate the file management thread. */
		else {
			WFSi_EndTaskThread();
		}

		/* End WBT */
		WBT_End();

		init_flag = FALSE;

	}
	(void)OS_RestoreInterrupts(bak_cpsr);
}


/*
 * Acquire current status.
 */
WFSState	WFS_GetStatus(void)
{
	return	wfsi_work->state;
}

/*---------------------------------------------------------------------------*
  Name:         WFS_GetCurrentBitmap

  Description:  Acquire a bitmap of the current child group that WBT recognizes.
                Only a parent can call this function.

  Arguments:    None.

  Returns:      Currently recognized child group.
 *---------------------------------------------------------------------------*/
int	WFS_GetCurrentBitmap(void)
{
	if(!init_flag || !wfsi_work->is_parent)
		return	0;
	return	wfsi_work->context.parent->all_bitmap;
}

/*---------------------------------------------------------------------------*
  Name:         WFS_GetSyncBitmap

  Description:  Acquire a bitmap of the group of children specified for access synchronization.
                Only a parent can call this function.

  Arguments:    None.

  Returns:      Group of children specified for access synchronization.
 *---------------------------------------------------------------------------*/
int	WFS_GetSyncBitmap(void)
{
	if(!init_flag || !wfsi_work->is_parent)
		return	0;
	return	wfsi_work->context.parent->sync_bitmap;
}

/*---------------------------------------------------------------------------*
  Name:         WFS_GetPacketSize

  Description:  Acquire the parent's MP communication packet size that is set in the WFS.

  Arguments:    None.

  Returns:      The parent's MP communication packet size that is set in the WFS.
 *---------------------------------------------------------------------------*/
int	WFS_GetPacketSize(void)
{
	if(!init_flag)
		return	0;
	return	wfsi_work->parent_packet_size;
}

/*---------------------------------------------------------------------------*
  Name:         WFS_GetBusyBitmap

  Description:  Acquires the bitmap of the group of children that are currently accessing a parent.
                Only a parent can call this function.

  Arguments:    None.

  Returns:      Group of children currently accessing a parent.
 *---------------------------------------------------------------------------*/
int	WFS_GetBusyBitmap(void)
{
	if(!init_flag || !wfsi_work->is_parent)
		return	0;
	return	wfsi_work->context.parent->busy_bitmap;
}

/*---------------------------------------------------------------------------*
  Name:         WFS_IsBusy

  Description:  Determines whether a child with a specified aid is currently accessing.
                Only a parent can call this function.

  Arguments:    aid              aid of the child for which determination is being made.

  Returns:      If the specified child is accessing, returns TRUE. Otherwise returns FALSE.
 *---------------------------------------------------------------------------*/
BOOL	WFS_IsBusy(int aid)
{
	return	((1 << aid) & WFS_GetBusyBitmap()) != 0;
}

/* Acquires download progress of file currently operated on by ReadFile. */
BOOL	WFS_GetCurrentDownloadProgress(int *current_count, int *total_count)
{
	if(wfsi_work->is_parent ||
		(wfsi_work->state != WFS_STATE_READY) ||
		!wfsi_work->target_file)
		return	FALSE;
	return
		WBT_GetCurrentDownloadProgress(
			wfsi_work->context.child->block_id,
			0, current_count, total_count);
}

/*
 * Sets the packet size sent from the parent. (parent only)
 * You can only call this functions before calling WFS_Start()
 * or if all children have properly finished connections.
 *
 * If called at any other time, we do not guarantee behavior.
 */
void	WFS_SetPacketSize(int size)
{
	SDK_ASSERT(init_flag);
	SDK_ASSERT(wfsi_work->is_parent);
	SDK_ASSERT(size >= WBT_PACKET_SIZE_MIN);

	{
		WFSParentContext	* const p = wfsi_work->context.parent;
		OSIntrMode	bak_cpsr = OS_DisableInterrupts();
		p->new_packet_size = size;
		p->is_changing = TRUE;
		/* If not prepared, instantly reset size. */
		if(wfsi_work->state != WFS_STATE_READY) {
			p->is_changing = FALSE;
			p->deny_bitmap = 0;
			wfsi_work->parent_packet_size = p->new_packet_size;
		}
		(void)OS_RestoreInterrupts(bak_cpsr);
	}
}

/*---------------------------------------------------------------------------*
  Name:         WFS_EnableSync

  Description:  On the parent side sets up the child group that is acquiring sync.
                This function syncs responses for the group of children which are clearly
                assured of access to the same file group, in the same order.
                Therefore it utilizes the special characteristics of the WBT library to realize efficient transfer.
                However, notice that in cases in which sync start timing is not logically stable,
                if responses to the child group get out of sync, there will be deadlock.
                Only a parent can call this function.

  Arguments:    sync_bitmap      aid bitmap for the group of children that are acquiring access sync.
                                 The lower order bit 1 that indicates a parent is ignored.
                                 If 0 is specified for this value, it will not sync.
                                 This is the default status.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void	WFS_EnableSync(int sync_bitmap)
{
	SDK_ASSERT(init_flag && wfsi_work->is_parent);

	{
		OSIntrMode	bak_cpsr = OS_DisableInterrupts();

		WFSParentContext	* const p_parent = wfsi_work->context.parent;

		p_parent->sync_bitmap = (sync_bitmap & ~1);

		(void)OS_RestoreInterrupts(bak_cpsr);
	}
}

/*
 * Enables / Disables WFS internal debug output.
 */
void	WFS_SetDebugMode(BOOL e)
{
//	SDK_ASSERT(init_flag);
	wfsi_debug_enable = e;
}
