/*---------------------------------------------------------------------------*
  Project:  NitroSDK - CARD - libraries
  File:     card_backup.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: card_backup.c,v $
  Revision 1.12  2004/11/12 05:46:01  yosizaki
  add CARDi_CheckValidity().

  Revision 1.11  2004/11/02 10:04:12  yosizaki
  fix comment.

  Revision 1.10  2004/09/10 00:00:16  yosizaki
  add assertion checking.

  Revision 1.9  2004/09/02 11:54:42  yosizaki
  fix CARD include directives.

  Revision 1.8  2004/08/25 23:57:49  yosizaki
  fix CARD_IdentifyBackup. (set callback NULL)

  Revision 1.7  2004/08/23 10:38:10  yosizaki
  add write-command.
  add retry count on writing timeout.

  Revision 1.6  2004/08/18 13:42:05  yosizaki
  add FLASH 2Mbit.

  Revision 1.5  2004/08/18 12:11:37  yosizaki
  small fix (cardi_device->type)

  Revision 1.4  2004/08/18 08:19:02  yosizaki
  change CARDBackupType format.

  Revision 1.3  2004/07/28 13:18:06  yosizaki
  asynchronous mode support.

  Revision 1.2  2004/07/23 08:28:46  yosizaki
  change backup-routine to ARM7 only.

  Revision 1.1  2004/07/19 13:14:50  yosizaki
  (none)

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


#include <nitro.h>


#include "../include/card_spi.h"
#include "../include/card_common.h"


/********************************************************************/
/* function */

#if	defined(SDK_ARM9)

/* Task completion process */
static void	CARDi_RequestTaskEnd(CARDiCommon *p)
{
	const MIDmaCallback	func = p->callback;
	void	* const arg = p->callback_arg;

	CARDi_EndTask(p);
	if(func)	(*func)(arg);
}

/* The body of the synchronization process ***************************************************/

/* Designate the device type */
static void	CARDi_IdentifyBackupSyncCore(CARDiCommon *p)
{
	/* Send the device type */
	p->cmd->type = CARD_GetCurrentBackupType();
	(void)CARDi_Request(p, CARD_REQ_IDENTIFY, 1);

	CARDi_RequestTaskEnd(p);
}

/* Read */
static void	CARDi_ReadBackupSyncCore(CARDiCommon *p)
{
	do {
		/* PXI read request in temporary buffer units */
		const u32	size = sizeof(p->backup_cache_page_buf);
		const u32	len = (size < p->len) ? size : p->len;
		DC_InvalidateRange(p->backup_cache_page_buf,
			sizeof(p->backup_cache_page_buf));
		p->cmd->src = (u32)p->src;
		p->cmd->dst = (u32)p->backup_cache_page_buf;
		p->cmd->len = len;
		if(!CARDi_Request(p, CARD_REQ_READ_BACKUP, 1))
			break;
		/* Copy from the cache */
		MI_CpuCopy8(p->backup_cache_page_buf, (void*)p->dst, len);
		p->src += len;
		p->dst += len;
		p->len -= len;
	} while(p->len > 0);

	CARDi_RequestTaskEnd(p);
}

/* Program */
static void	CARDi_ProgramBackupSyncCore(CARDiCommon *p)
{
	do {
		/* Copy to the cache */
		const u32	size = sizeof(p->backup_cache_page_buf);
		const u32	len = (size < p->len) ? size : p->len;
		MI_CpuCopy8((const void*)p->src, p->backup_cache_page_buf, len);
		DC_FlushRange(p->backup_cache_page_buf, len);
		DC_WaitWriteBufferEmpty();
		/* PXI Write request in page units */
		p->cmd->src = (u32)p->backup_cache_page_buf;
		p->cmd->dst = (u32)p->dst;
		p->cmd->len = len;
		if(!CARDi_Request(p, CARD_REQ_PROGRAM_BACKUP, CARD_RETRY_COUNT_MAX))
			break;
		p->src += len;
		p->dst += len;
		p->len -= len;
	} while(p->len > 0);

	CARDi_RequestTaskEnd(p);
}

/* Write */
static void	CARDi_WriteBackupSyncCore(CARDiCommon *p)
{
	do {
		/* Copy to the cache */
		const u32	size = sizeof(p->backup_cache_page_buf);
		const u32	len = (size < p->len) ? size : p->len;
		MI_CpuCopy8((const void*)p->src, p->backup_cache_page_buf, len);
		DC_FlushRange(p->backup_cache_page_buf, len);
		DC_WaitWriteBufferEmpty();
		/* PXI Write request in page units */
		p->cmd->src = (u32)p->backup_cache_page_buf;
		p->cmd->dst = (u32)p->dst;
		p->cmd->len = len;
		if(!CARDi_Request(p, CARD_REQ_WRITE_BACKUP, CARD_RETRY_COUNT_MAX))
			break;
		p->src += len;
		p->dst += len;
		p->len -= len;
	} while(p->len > 0);

	CARDi_RequestTaskEnd(p);
}

/* Verify */
static void	CARDi_VerifyBackupSyncCore(CARDiCommon *p)
{
	do {
		/* Copy to the cache */
		const u32	size = sizeof(p->backup_cache_page_buf);
		const u32	len = (size < p->len) ? size : p->len;
		MI_CpuCopy8((const void*)p->src, p->backup_cache_page_buf, len);
		DC_FlushRange(p->backup_cache_page_buf, len);
		DC_WaitWriteBufferEmpty();
		/* PXI Write request in page units */
		p->cmd->src = (u32)p->backup_cache_page_buf;
		p->cmd->dst = (u32)p->dst;
		p->cmd->len = len;
		if(!CARDi_Request(p, CARD_REQ_VERIFY_BACKUP, 1))
			break;
		p->src += len;
		p->dst += len;
		p->len -= len;
	} while(p->len > 0);

	CARDi_RequestTaskEnd(p);
}

/* Program + Verify */
static void	CARDi_ProgramAndVerifyBackupSyncCore(CARDiCommon *p)
{
	do {
		/* Copy to the cache */
		const u32	size = sizeof(p->backup_cache_page_buf);
		const u32	len = (size < p->len) ? size : p->len;
		MI_CpuCopy8((const void*)p->src, p->backup_cache_page_buf, len);
		DC_FlushRange(p->backup_cache_page_buf, len);
		DC_WaitWriteBufferEmpty();
		/* PXI Write request in page units */
		p->cmd->src = (u32)p->backup_cache_page_buf;
		p->cmd->dst = (u32)p->dst;
		p->cmd->len = len;
		if(!CARDi_Request(p, CARD_REQ_PROGRAM_BACKUP, CARD_RETRY_COUNT_MAX))
			break;
		/* Further verification request with the same configuration */
		if(!CARDi_Request(p, CARD_REQ_VERIFY_BACKUP, 1))
			break;
		p->src += len;
		p->dst += len;
		p->len -= len;
	} while(p->len > 0);

	CARDi_RequestTaskEnd(p);
}

/* Write + Verify */
static void	CARDi_WriteAndVerifyBackupSyncCore(CARDiCommon *p)
{
	do {
		/* Copy to the cache */
		const u32	size = sizeof(p->backup_cache_page_buf);
		const u32	len = (size < p->len) ? size : p->len;
		MI_CpuCopy8((const void*)p->src, p->backup_cache_page_buf, len);
		DC_FlushRange(p->backup_cache_page_buf, len);
		DC_WaitWriteBufferEmpty();
		/* PXI Write request in page units */
		p->cmd->src = (u32)p->backup_cache_page_buf;
		p->cmd->dst = (u32)p->dst;
		p->cmd->len = len;
		if(!CARDi_Request(p, CARD_REQ_WRITE_BACKUP, CARD_RETRY_COUNT_MAX))
			break;
		/* Further verification request with the same configuration */
		if(!CARDi_Request(p, CARD_REQ_VERIFY_BACKUP, 1))
			break;
		p->src += len;
		p->dst += len;
		p->len -= len;
	} while(p->len > 0);

	CARDi_RequestTaskEnd(p);
}

/* Transmission Source / Transmission Destination / Shared processes of the commands that use the transmission Size */
static inline BOOL	CARDi_StreamCommand(u32 src, u32 dst, u32 len,
	MIDmaCallback callback, void *arg, BOOL is_async,
	void (*task)(CARDiCommon*))
{
	CARDiCommon	* const p = &cardi_common;

	CARDi_WaitTask(p);
	p->src = src;
	p->dst = dst;
	p->len = len;
	p->callback = callback;
	p->callback_arg = arg;
	if(is_async) {
		CARDi_SetTask(task);
		return	TRUE;
	} else {
		cardi_common.cur_th = OS_GetCurrentThread();
		(*task)(p);
		return	(p->cmd->result == CARD_RESULT_SUCCESS);
	}
}


/* Read */
BOOL	CARDi_ReadBackup(
	u32 src, void *dst, u32 len,
	MIDmaCallback callback, void *arg, BOOL is_async)
{
	SDK_ASSERT(CARD_IsAvailable());
	SDK_ASSERT(CARDi_GetTargetMode() == CARD_TARGET_BACKUP);

	return	CARDi_StreamCommand(src, (u32)dst, len, callback, arg,
		is_async, CARDi_ReadBackupSyncCore);
}

/* Program */
BOOL	CARDi_ProgramBackup(
	u32 dst, const void *src, u32 len,
	MIDmaCallback callback, void *arg, BOOL is_async)
{
	SDK_ASSERT(CARD_IsAvailable());
	SDK_ASSERT(CARDi_GetTargetMode() == CARD_TARGET_BACKUP);

	return	CARDi_StreamCommand((u32)src, dst, len, callback, arg,
		is_async, CARDi_ProgramBackupSyncCore);
}

/* Write */
BOOL	CARDi_WriteBackup(
	u32 dst, const void *src, u32 len,
	MIDmaCallback callback, void *arg, BOOL is_async)
{
	SDK_ASSERT(CARD_IsAvailable());
	SDK_ASSERT(CARDi_GetTargetMode() == CARD_TARGET_BACKUP);

	return	CARDi_StreamCommand((u32)src, dst, len, callback, arg,
		is_async, CARDi_WriteBackupSyncCore);
}

/* Verify */
BOOL	CARDi_VerifyBackup(
	u32 dst, const void *src, u32 len,
	MIDmaCallback callback, void *arg, BOOL is_async)
{
	SDK_ASSERT(CARD_IsAvailable());
	SDK_ASSERT(CARDi_GetTargetMode() == CARD_TARGET_BACKUP);

	return	CARDi_StreamCommand((u32)src, dst, len, callback, arg,
		is_async, CARDi_VerifyBackupSyncCore);
}

/* Program + Verify */
BOOL	CARDi_ProgramAndVerifyBackup(
	u32 dst, const void *src, u32 len,
	MIDmaCallback callback, void *arg, BOOL is_async)
{
	SDK_ASSERT(CARD_IsAvailable());
	SDK_ASSERT(CARDi_GetTargetMode() == CARD_TARGET_BACKUP);

	return	CARDi_StreamCommand((u32)src, dst, len, callback, arg,
		is_async, CARDi_ProgramAndVerifyBackupSyncCore);
}

/* Write + Verify */
BOOL	CARDi_WriteAndVerifyBackup(
	u32 dst, const void *src, u32 len,
	MIDmaCallback callback, void *arg, BOOL is_async)
{
	SDK_ASSERT(CARD_IsAvailable());
	SDK_ASSERT(CARDi_GetTargetMode() == CARD_TARGET_BACKUP);

	return	CARDi_StreamCommand((u32)src, dst, len, callback, arg,
		is_async, CARDi_WriteAndVerifyBackupSyncCore);
}

#if	0

/* Erase Sector */
static void	CARDi_EraseBackupSectorSyncCore(CARDiCommon *p)
{
	/* Always do a single Erase request in sector units */
	p->cmd->dst = (u32)p->dst;
	(void)CARDi_Request(p, CARD_REQ_ERASE_SECTOR_BACKUP, 2);

	CARDi_RequestTaskEnd(p);
}

/* Erase */
BOOL	CARDi_EraseBackupSector(
	u32 dst,
	MIDmaCallback callback, void *arg, BOOL is_async)
{
	SDK_ASSERT(CARD_IsAvailable());
	SDK_ASSERT(CARDi_GetTargetMode() == CARD_TARGET_BACKUP);

	return	CARDi_StreamCommand(0, dst, cardi_device->sect_size,
		callback, arg, is_async, CARDi_EraseBackupSectorSyncCore);
}
#endif


/********************************************************************/
/* Interface */

/* Get the currently configured device type */
CARDBackupType	CARD_GetCurrentBackupType(void)
{
	SDK_ASSERT(CARD_IsAvailable());

	return	cardi_device->type;
}

/* get full size of device */
u32	CARD_GetBackupTotalSize(void)
{
	SDK_ASSERT(CARD_IsAvailable());

	return	cardi_device->total_size;
}

/* Get the device write sector size (in bytes) */
u32	CARD_GetBackupSectorSize(void)
{
	SDK_ASSERT(CARD_IsAvailable());

	return	cardi_device->sect_size;
}

/* Get the write page size of the backup device */
u32	CARD_GetBackupPageSize(void)
{
	SDK_ASSERT(CARD_IsAvailable());

	return	cardi_device->page_size;
}

/* Specification of the CARD Backup Device (Exclusively for ARM9) */
void	CARD_IdentifyBackup(CARDBackupType type)
{
	CARDiCommon	* const p = &cardi_common;
	SDK_ASSERT(CARD_IsAvailable());
	SDK_ASSERTMSG(CARDi_FindDeviceInfo(type) != NULL,
		"card module : specified backup-type is invalid.");

	/* Controlling here is sufficient for assessing validity for backup.*/
	CARD_VALIDITY_ASSERTION();

	/*
	 * Wait for a task and send the device type to ARM7.
	 * This does not directly operate the device, so it is not necessary to lock beforehand.
	 */
	CARDi_WaitTask(p);
	CARDi_IdentifyBackupCore(type);

	p->callback = NULL;
	p->callback_arg = NULL;
	cardi_common.cur_th = OS_GetCurrentThread();
	CARDi_IdentifyBackupSyncCore(p);
}

BOOL	CARD_WaitBackupAsync(void)
{
	return	CARDi_WaitAsync();
}

BOOL	CARD_TryWaitBackupAsync(void)
{
	return	CARDi_TryWaitAsync();
}


#endif	/* SDK_ARM9 */
