/*
 * @history     2025_03_06  [SDK3.7] SSPサンプルにSSPライブラリのDMA送受信関数使用例を追加 (#5782)
 * @history     2025_03_06  [SDK3.7] DMA要求が非同期のときの対策がされていない不具合を修正 (#5786)
 */
/*
 * This program was created by Axell Corporation.
 * Copyright (C) 2017-2025 Axell Corporation, all rights reserved.
 */
#include "kernel.h" /* uC3-Fs */
#include "ffsys.h"
#include "sample_common.h"
#include "com.h"
#include "ssp_sample.h"
#include "ssp/sspmgr.h"
#include "register/AG903_sspreg.h"
#include "dmac/dmacmgr.h"
#include "i2c/i2cmgr.h"


/* プロトタイプ */
static void Ssp_InitHw(uint8_t ch, uint8_t type);
static int32_t Ssp_SelectDevice(int8_t* dev);
static void Ssp_GetRecTime(uint16_t* rectime);
static int32_t Ssp_SetDmac(AG903_DMACMgrHandle* handle, uint8_t mode, uint8_t sspch, uint8_t sspwidth,
														uint32_t addr1, uint32_t addr2, uint32_t size);
static int32_t Ssp_GetMem(uint8_t** buf1, uint8_t** buf2, uint32_t size);
static int32_t Ssp_RelMem(uint8_t* buf1, uint8_t* buf2);
static int32_t Ssp_MountDevice(int8_t dev);
static FILE* Ssp_OpenFile(int8_t dev, uint8_t type);
static int32_t Ssp_WriteFile(FILE* fp, uint32_t size, uint8_t* buf);
static uint32_t Ssp_ReadFile(FILE* fp, uint32_t size, uint8_t* buf);
static int32_t Ssp_CloseFile(int8_t devnum, FILE* fp);
static int32_t Ssp_CreateHeaer(FILE* fp, uint8_t type);
static int32_t Ssp_CheckHeader(FILE* fp, uint32_t* size, uint16_t* bitnum);
static void Ssp_SetCodec(uint8_t type);
static int32_t Ssp_WaitSend(uint32_t timout);
static int32_t Ssp_WaitReceive(uint32_t timout);
static int32_t Ssp_WaitDmaTerm(uint32_t timout);
static int32_t Ssp_FlushTxFifo(AG903_SSPMgrHandle* handle, uint32_t timout);
static void Ssp_Callback(AG903_SSPMgrHandle* handle, uint32_t event);
static void Ssp_CallbackDmaTerm(AG903_DMACMgrHandle* handle, AG903_DMACMgrDesc** desc, uint8_t* end);
static void Ssp_CallbackDmaAbort(AG903_DMACMgrHandle* handle);
static void Ssp_CallbackDmaErr(AG903_DMACMgrHandle* handle);
static void Ssp_CallbackSigSemTx(AG903_SSPMgrHandle* handle, uint32_t event);
static void Ssp_CallbackSigSemRx(AG903_SSPMgrHandle* handle, uint32_t event);
static void Ssp_WaiSemTx(void);
static void Ssp_WaiSemRx(void);

/* Param */
#define SSP_SMP_TRNS_WORD		(44100*2)				/* データ転送数[word] (ステレオで1秒分) */
#define SSP_SMP_DMACH			(3)						/* DMAC CH */
#define SSP_SMP_DMACSEL			(4)						/* DMAC Handshake Select */
#define SSP_SMP_DMASEL_BASE		(0x18)					/* dma_req選択値(SSP0) */
#define SSP_SMP_REG_INTERVAL	(0x00100000) 			/* SSP Register Interval */
#define SSP_SMP_REC_OFFSET		(0xF000)				/* Sampling Start Offset [実機調整] */
#define SSP_SMP_UNIT_TIME		(1)						/* 現在は常に1 */
#define	SSP_SMP_REC_FILEPATH	"A:\\ssp_sample.wav"	/* 録音ファイルパス */
#define SSP_SMP_PLAY_FILEPATH	"A:\\ssp_sample.wav"	/* 再生ファイルパス */
#define SSP_SMP_HEADER_SIZE		(0x2C)					/* ファイルヘッダサイズ */
#define SSP_SMP_ALIGN_SIZE		(32)					/* バッファアラインサイズ */

enum {	/* ファイル種別 */
	SSP_SMP_FILE_REC=0,
	SSP_SMP_FILE_PLAY,
};

enum {	/* ヘッダ生成種別 */
	SSP_SMP_HEADER_INIT=0,
	SSP_SMP_HEADER_WRITE,
};

enum {	/* Codec設定種別 */
	SSP_SMP_CODEC_STOP=0,
	SSP_SMP_CODEC_START_REC,
	SSP_SMP_CODEC_START_PLAY,
};

enum {	/* DMA Direction */
	SSP_SMP_DMA_SEND=0,
	SSP_SMP_DMA_RECEIVE,
};

/* Macro */
#define	EPRINT(FMT, ...)		sys_print(0, FMT,##__VA_ARGS__)
#define	PRINT(FMT, ...)			sys_print(0, FMT"\r\n",##__VA_ARGS__)
#define SSP_ERROR(str)			sys_print(1, " ERROR:%s[%d] *%s*\r\n",__FUNCTION__, __LINE__, str)

/* データ */
static volatile _Bool	SspTxEnd=false;		/* 送信完了フラグ */
static volatile _Bool	SspRxEnd=false;		/* 受信完了フラグ */
static volatile _Bool	SspDmaTerm=false;	/* DMA完了フラグ */
static volatile uint32_t	SspTxSem=0;
static volatile uint32_t	SspRxSem=0;

static const uint8_t SspDevType[3] = {FSIF_DEV_SD, FSIF_DEV_CF, FSIF_DEV_USB0};	/* デバイス種別 */

/**
 * @brief		オーディオ録音処理 [PIO]
 * @param[in]	ch		SSPチャネル
 * @param[in]	format	通信フォーマット
 * @return		なし
 * @note		LINE-IN1から入力したデータをSDに保存します
 */
void Ssp_AudioRecPio(uint8_t ch, uint8_t format)
{
	AG903_SSPMgrHandle*		handle;
	AG903_SSPMgrI2sParam	i2s;
	FILE*		fp;
	uint8_t* 	buf[2];
	int32_t		result;
	uint32_t	loop;
	uint32_t	buf_size;
	uint16_t	rectime;
	int8_t		dev;

	((void)format);

	Ssp_InitHw(ch, AG903_I2S_MASTER_READ); 					/* ハードウェア設定 */

	while(1) {	/* Mount〜File Open */
		result = Ssp_SelectDevice(&dev);
		if(AG903_ENONE != result) {
			PRINT(" Invalid");
			continue;
		}
		result = Ssp_MountDevice(dev);
		if(AG903_ENONE != result) {
			PRINT(" Device Error");
			continue;
		}
		fp = Ssp_OpenFile(dev, SSP_SMP_FILE_REC);
		if (NULL == fp) {
			PRINT(" File Error");
			continue;
		}
		break;
	}
	result = Ssp_CreateHeaer(fp, SSP_SMP_HEADER_INIT);		/* ヘッダ生成 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Create Header");
	}

	result = AG903_SSPMgrGetHandle(ch, &handle);			/* SSPハンドル取得 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP GetHandle");
	}
	result = AG903_SSPMgrReset(handle);						/* SSPリセット */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Reset");
	}
	i2s.wordlen	  = 32;	/* 32bit */
	i2s.mono	  = false;
	i2s.polarity  = AG903_SSP_POL_NEGATIVE;
	i2s.firstbit  = AG903_SSP_FIRSTBIT_MSB;
	i2s.padlen	  = 0;
	i2s.justified = AG903_SSP_LEFT_JUSTIFIED;
	i2s.clkdiv    = 4;	/* 11.2896/4 = 2.8224[MHz] */
	i2s.slave     = false;
	result = AG903_SSPMgrSetI2sMode(handle, &i2s);	/* I2S指定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetMode");
	}
	result = AG903_SSPMgrSetCallback(handle, (AG903_SSPMgrClbk)Ssp_Callback);
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetCallback");
	}

	buf_size = ((i2s.wordlen/8)*SSP_SMP_TRNS_WORD);
	result = Ssp_GetMem(&buf[0], &buf[1], buf_size);		/* バッファ確保 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SetMem");
	}

	Ssp_GetRecTime(&rectime);			/* 録音時間取得 */

	Ssp_SetCodec(SSP_SMP_CODEC_START_REC);	/* Codec設定 */

	result = AG903_SSPMgrSetReceiveBuf(handle, buf[0], SSP_SMP_TRNS_WORD);		/* 受信バッファ設定 buf0 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetSendBuf");
	}
	if((SSP_SMP_UNIT_TIME*2) <= rectime) {
		result = AG903_SSPMgrSetReceiveBuf(handle, buf[1], SSP_SMP_TRNS_WORD);	/* 受信バッファ設定 buf1 */
		if(AG903_ENONE != result) {
			SSP_ERROR("SSP SetSendBuf");
		}
	}
	result = AG903_SSPMgrEnableTransfer(handle);			/* I2S Enable */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Enable");
	}

	for(loop=0; loop<rectime; loop+=SSP_SMP_UNIT_TIME) {	/* 録音 */
		result = Ssp_WaitReceive(SSP_SMP_UNIT_TIME*2*1000);
		if(AG903_ENONE != result) {
			SSP_ERROR("SSP Receive");
		}
		if(0==loop) {
			sys_memset(buf[0], 0, SSP_SMP_REC_OFFSET);
			result = Ssp_WriteFile(fp, buf_size, buf[0]);
		}
		else {
			result = Ssp_WriteFile(fp, buf_size, buf[loop%2]);
		}
		if(AG903_ENONE != result) {
			SSP_ERROR("File Write Error");
			break;
		}
		if((loop+(SSP_SMP_UNIT_TIME*2)) < rectime) {
			result = AG903_SSPMgrSetReceiveBuf(handle, buf[loop%2], SSP_SMP_TRNS_WORD);
			if(AG903_ENONE != result) {
				SSP_ERROR("SSP SetSendBuf");
			}
		}
	}

	result = AG903_SSPMgrDisableTransfer(handle);			/* I2S 停止 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Disable");
	}
	result = AG903_SSPMgrClearRxFifo(handle);				/* FIFOクリア */
	if(AG903_ENONE != result) {
		SSP_ERROR("Clear FIFO");
	}
	result = AG903_SSPMgrReleaseHandle(handle);				/* ハンドル解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Release Handle");
	}

	Ssp_SetCodec(SSP_SMP_CODEC_STOP);						/* Codec停止 */

	result = Ssp_CreateHeaer(fp, SSP_SMP_HEADER_WRITE);		/* ヘッダ生成 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Create Header");
	}
	result = Ssp_CloseFile(dev, fp);						/* ファイルClose */
	if(AG903_ENONE != result) {
		SSP_ERROR("File Close Error");
	}

	result = Ssp_RelMem(buf[0], buf[1]);					/* バッファ解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("RelMem");
	}

	return;
}

/**
 * @brief		オーディオ再生処理 [PIO]
 * @param[in]	ch		SSPチャネル
 * @param[in]	format	通信フォーマット
 * @return		なし
 * @note		SDからファイルを読込みLINE-OUT1へ出力します
 */
void Ssp_AudioPlayPio(uint8_t ch, uint8_t format)
{
	AG903_SSPMgrHandle*		handle;
	AG903_SSPMgrI2sParam	i2s;
	FILE*		fp;
	uint8_t* 	buf[2];
	int32_t		result;
	uint32_t	loop;
	uint32_t	size, r_size, rd_size, dat_size, buf_size;
	uint16_t	bitnum;
	int8_t		dev;

	((void)format);

	Ssp_InitHw(ch, AG903_I2S_MASTER_WRITE); 				/* ハードウェア設定 */

	while(1) {	/* Mount〜File Open */
		result = Ssp_SelectDevice(&dev);
		if(AG903_ENONE != result) {
			PRINT(" Invalid");
			continue;
		}
		result = Ssp_MountDevice(dev);
		if(AG903_ENONE != result) {
			PRINT(" Device Error");
			continue;
		}
		fp = Ssp_OpenFile(dev, SSP_SMP_FILE_PLAY);
		if (NULL == fp) {
			PRINT(" File Error");
			continue;
		}
		break;
	}
	result = Ssp_CheckHeader(fp, &dat_size, &bitnum);		/* ヘッダ確認 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Get Data Info");
	}

	result = AG903_SSPMgrGetHandle(ch, &handle);			/* SSPハンドル取得 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP GetHandle");
	}
	result = AG903_SSPMgrReset(handle);						/* SSPリセット */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Reset");
	}
	i2s.wordlen	  = bitnum;
	i2s.mono	  = false;
	i2s.polarity  = AG903_SSP_POL_NEGATIVE;
	i2s.firstbit  = AG903_SSP_FIRSTBIT_MSB;
	if(16 == bitnum) {
		i2s.padlen = 16;
	}
	else {
		i2s.padlen = 0;
	}
	i2s.justified = AG903_SSP_LEFT_JUSTIFIED;
	i2s.clkdiv    = 4;	/* 11.2896/4 = 2.8224[MHz] */
	i2s.slave     = false;
	result = AG903_SSPMgrSetI2sMode(handle, &i2s);	/* I2S指定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetMode");
	}
	result = AG903_SSPMgrSetCallback(handle, (AG903_SSPMgrClbk)Ssp_Callback);
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetCallback");
	}

	buf_size = ((i2s.wordlen/8)*SSP_SMP_TRNS_WORD);
	result = Ssp_GetMem(&buf[0], &buf[1], buf_size);		/* バッファ確保 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SetMem");
	}

	Ssp_SetCodec(SSP_SMP_CODEC_START_PLAY);					/* Codec設定 */

	if(buf_size >= dat_size) {
		size = dat_size;
	}
	else {
		size = buf_size;
	}
	rd_size = Ssp_ReadFile(fp, size, buf[0]);				/* データ読込み buf0 */
	if(rd_size != size) {
		SSP_ERROR("Read File");
	}
	result = AG903_SSPMgrSetSendBuf(handle, buf[0], (rd_size/(i2s.wordlen/8)));	/* 送信バッファ設定 buf0 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetSendBuf");
	}
	result = AG903_SSPMgrEnableTransfer(handle);			/* I2S Enable */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Enable");
	}

	for(loop=1; ;loop++) {	/* 再生 */
		if(dat_size<=rd_size) {
			break;	/* データ読込み完了 */
		}
		if(buf_size >= (dat_size-rd_size)) {
			size = (dat_size-rd_size);
		}
		else {
			size = buf_size;
		}
		r_size = Ssp_ReadFile(fp, size, buf[loop%2]);		/* データ読込み[裏buf] */
		if(r_size != size) {
			SSP_ERROR("Read File");
		}
		result = AG903_SSPMgrSetSendBuf(handle, buf[loop%2], (r_size/(i2s.wordlen/8)));	/* 送信バッファ設定[裏buf] */
		if(AG903_ENONE != result) {
			SSP_ERROR("SSP SetSendBuf");
		}
		rd_size += r_size;
		result = Ssp_WaitSend(20000);						/* 送信待ち[表buf] */
		if(AG903_ENONE != result) {
			SSP_ERROR("WaitReceive");
		}
	}
	result = Ssp_WaitSend(20000);							/* 送信待ち[Last data] */
	if(AG903_ENONE != result) {
		SSP_ERROR("WaitReceive");
	}

	Ssp_SetCodec(SSP_SMP_CODEC_STOP);						/* Codec停止 */

	result = AG903_SSPMgrDisableTransfer(handle);			/* I2S 停止 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Disable");
	}
	result = AG903_SSPMgrClearRxFifo(handle);				/* FIFOクリア */
	if(AG903_ENONE != result) {
		SSP_ERROR("Clear FIFO");
	}
	result = AG903_SSPMgrReleaseHandle(handle);				/* ハンドル解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Release Handle");
	}

	result = Ssp_CloseFile(dev, fp);						/* ファイルClose */
	if(AG903_ENONE != result) {
		SSP_ERROR("File Close Error");
	}

	result = Ssp_RelMem(buf[0], buf[1]);					/* バッファ解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("RelMem");
	}

	return;
}

/**
 * @brief		オーディオ録音処理 [ユーザーDMA制御]
 * @param[in]	ch		SSPチャネル
 * @param[in]	format	通信フォーマット
 * @return		なし
 * @note		LINE-IN1から入力したデータをSDに保存します
 */
void Ssp_AudioRecDmaExternal(uint8_t ch, uint8_t format)
{
	AG903_SSPMgrHandle*		i2shdl;
	AG903_DMACMgrHandle*  	dmahdl;
	AG903_SSPMgrI2sParam	i2s;
	FILE*		fp;
	uint8_t* 	buf[2];
	uint32_t	loop;
	uint32_t	buf_size;
	int32_t		result;
	uint16_t	rectime;
	int8_t		dev;

	((void)format);

	Ssp_InitHw(ch, AG903_I2S_MASTER_READ); 					/* ハードウェア設定 */

	while(1) {	/* Mount〜File Open */
		result = Ssp_SelectDevice(&dev);
		if(AG903_ENONE != result) {
			PRINT(" Invalid");
			continue;
		}
		result = Ssp_MountDevice(dev);
		if(AG903_ENONE != result) {
			PRINT(" Device Error");
			continue;
		}
		fp = Ssp_OpenFile(dev, SSP_SMP_FILE_REC);
		if (NULL == fp) {
			PRINT(" File Error");
			continue;
		}
		break;
	}
	result = Ssp_CreateHeaer(fp, SSP_SMP_HEADER_INIT);		/* ヘッダ生成 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Create Header");
	}

	Ssp_GetRecTime(&rectime);

	result = AG903_SSPMgrGetHandle(ch, &i2shdl);			/* SSPハンドル取得 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP GetHandle");
	}
	result = AG903_SSPMgrReset(i2shdl);						/* SSPリセット */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Reset");
	}
	result = AG903_SSPMgrEnableDmaMode(i2shdl, (1<<1));		/* DMA有効設定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("EnableDmaMode");
	}
	i2s.wordlen	  = 32;	/* 32bit */
	i2s.mono	  = false;
	i2s.polarity  = AG903_SSP_POL_NEGATIVE;
	i2s.firstbit  = AG903_SSP_FIRSTBIT_MSB;
	i2s.padlen	  = 0;
	i2s.justified = AG903_SSP_LEFT_JUSTIFIED;
	i2s.clkdiv    = 4;	/* 11.2896/4 = 2.8224[MHz] */
	i2s.slave     = false;
	result = AG903_SSPMgrSetI2sMode(i2shdl, &i2s);	/* I2S指定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetMode");
	}

	buf_size = ((i2s.wordlen/8)*SSP_SMP_TRNS_WORD);
	result = Ssp_GetMem(&buf[0], &buf[1], buf_size);		/* バッファ確保 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SetMem");
	}

	result = AG903_DMACMgrGetHandle(SSP_SMP_DMACH, &dmahdl);/* DMACハンドル取得 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Dmac GetHandle");
	}
	result = AG903_DMACMgrSetIntCallback(dmahdl, Ssp_CallbackDmaTerm,
											Ssp_CallbackDmaAbort, Ssp_CallbackDmaErr);
	if(AG903_ENONE != result) {
		SSP_ERROR("Dmac SetCallback");
	}
	result = Ssp_SetDmac(dmahdl, SSP_SMP_DMA_RECEIVE, ch, i2s.wordlen,	/* DMAC設定 */
								(uint32_t)buf[0], (uint32_t)buf[1], buf_size);
	if(AG903_ENONE != result) {
		SSP_ERROR("Set Dmac");
	}

	Ssp_SetCodec(SSP_SMP_CODEC_START_REC);					/* Codec設定 */

	result = AG903_DMACMgrEnable(dmahdl);					/* DMA Enable */
	if(AG903_ENONE != result) {
		SSP_ERROR(" DMAC Enable");
	}
	result = AG903_SSPMgrEnableTransfer(i2shdl);			/* I2S Enable */
	if(AG903_ENONE != result) {
		SSP_ERROR("Master Enable");
	}

	for(loop=0; loop<rectime; loop+=SSP_SMP_UNIT_TIME) {	/* 録音 */
		result = Ssp_WaitDmaTerm(SSP_SMP_UNIT_TIME*2*1000);
		if(AG903_ENONE != result) {
			SSP_ERROR("SSP Receive");
		}
		if(0==loop) {
			sys_memset(buf[0], 0, SSP_SMP_REC_OFFSET);
			result = Ssp_WriteFile(fp, buf_size, buf[0]);
		}
		else {
			result = Ssp_WriteFile(fp, buf_size, buf[loop%2]);
		}
		if(AG903_ENONE != result) {
			SSP_ERROR("File Write Error");
			break;
		}
	}

	result = AG903_SSPMgrDisableTransfer(i2shdl);			/* I2S 停止 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Disable");
	}
	result = AG903_SSPMgrClearRxFifo(i2shdl);				/* FIFOクリア */
	if(AG903_ENONE != result) {
		SSP_ERROR("Clear FIFO");
	}
	result = AG903_SSPMgrReleaseHandle(i2shdl);				/* ハンドル解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Release Handle");
	}

	result = AG903_DMACMgrAbort(dmahdl);					/* DMA停止 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Dmac Abort");
	}
	result = AG903_DMACMgrReleaseHandle(dmahdl);
	if(AG903_ENONE != result) {
		SSP_ERROR("Dmac ReleaseHandle");
	}

	Ssp_SetCodec(SSP_SMP_CODEC_STOP);						/* Codec停止 */

	result = Ssp_CreateHeaer(fp, SSP_SMP_HEADER_WRITE);		/* ヘッダ生成 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Create Header");
	}
	result = Ssp_CloseFile(dev, fp);						/* ファイルClose */
	if(AG903_ENONE != result) {
		SSP_ERROR("File Close Error");
	}

	result = Ssp_RelMem(buf[0], buf[1]);					/* バッファ解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("RelMem");
	}

	return;
}

/**
 * @brief		オーディオ再生処理 [ユーザーDMA制御]
 * @param[in]	ch		SSPチャネル
 * @param[in]	format	通信フォーマット
 * @return		なし
 * @note		SDからファイルを読込みLINE-OUT1へ出力します
 */
void Ssp_AudioPlayDmaExternal(uint8_t ch, uint8_t format)
{
	AG903_SSPMgrHandle*		i2shdl;
	AG903_DMACMgrHandle*  	dmahdl;
	AG903_SSPMgrI2sParam	i2s;
	FILE*		fp;
	uint8_t* 	buf[2];
	uint32_t	loop;
	uint32_t	size, r_size, rd_size, dat_size, buf_size;
	int32_t		result;
	uint16_t	bitnum;
	int8_t		dev;

	((void)format);

	Ssp_InitHw(ch, AG903_I2S_MASTER_WRITE); 				/* ハードウェア設定 */

	while(1) {	/* Mount〜File Open */
		result = Ssp_SelectDevice(&dev);
		if(AG903_ENONE != result) {
			PRINT(" Invalid");
			continue;
		}
		result = Ssp_MountDevice(dev);
		if(AG903_ENONE != result) {
			PRINT(" Device Error");
			continue;
		}
		fp = Ssp_OpenFile(dev, SSP_SMP_FILE_PLAY);
		if (NULL == fp) {
			PRINT(" File Error");
			continue;
		}
		break;
	}
	result = Ssp_CheckHeader(fp, &dat_size, &bitnum);		/* ヘッダ確認 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Get Data Info");
	}

	result = AG903_SSPMgrGetHandle(ch, &i2shdl);			/* SSPハンドル取得 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP GetHandle");
	}
	result = AG903_SSPMgrReset(i2shdl);						/* SSPリセット */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Reset");
	}
	result = AG903_SSPMgrEnableDmaMode(i2shdl, (1<<0));		/* DMA有効設定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("EnableDmaMode");
	}
	i2s.wordlen	  = bitnum;
	i2s.mono	  = false;
	i2s.polarity  = AG903_SSP_POL_NEGATIVE;
	i2s.firstbit  = AG903_SSP_FIRSTBIT_MSB;
	if(16 == bitnum) {
		i2s.padlen = 16;
	}
	else {
		i2s.padlen = 0;
	}
	i2s.justified = AG903_SSP_LEFT_JUSTIFIED;
	i2s.clkdiv    = 4;	/* 11.2896/4 = 2.8224[MHz] */
	i2s.slave     = false;
	result = AG903_SSPMgrSetI2sMode(i2shdl, &i2s);	/* I2S指定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetMode");
	}

	buf_size = ((i2s.wordlen/8)*SSP_SMP_TRNS_WORD);
	result = Ssp_GetMem(&buf[0], &buf[1], buf_size);		/* バッファ確保 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SetMem");
	}

	result = AG903_DMACMgrGetHandle(SSP_SMP_DMACH, &dmahdl);/* DMACハンドル取得 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Dmac GetHandle");
	}
	result = AG903_DMACMgrSetIntCallback(dmahdl, Ssp_CallbackDmaTerm,
											Ssp_CallbackDmaAbort, Ssp_CallbackDmaErr);
	if(AG903_ENONE != result) {
		SSP_ERROR("Dmac SetCallback");
	}
	result = Ssp_SetDmac(dmahdl, SSP_SMP_DMA_SEND, ch, i2s.wordlen,	/* DMAC設定 */
								(uint32_t)buf[0], (uint32_t)buf[1], buf_size);
	if(AG903_ENONE != result) {
		SSP_ERROR("Set Dmac");
	}

	Ssp_SetCodec(SSP_SMP_CODEC_START_PLAY);					/* Codec設定 */

	if(buf_size >= dat_size) {
		size = dat_size;
	}
	else {
		size = buf_size;
	}
	rd_size = Ssp_ReadFile(fp, size, buf[0]);				/* データ読込み buf0 */
	if(rd_size != size) {
		SSP_ERROR("Read File");
	}

	result = AG903_DMACMgrEnable(dmahdl);					/* DMA Enable */
	if(AG903_ENONE != result) {
		SSP_ERROR(" DMAC Enable");
	}
	result = AG903_SSPMgrEnableTransfer(i2shdl);			/* I2S Enable */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Enable");
	}

	for(loop=1; ;loop++) {	/* 再生 */
		if(dat_size<=rd_size) {
			break;	/* データ読込み完了 */
		}
		if(buf_size >= (dat_size-rd_size)) {
			size = (dat_size-rd_size);
		}
		else {
			size = buf_size;
		}
		r_size = Ssp_ReadFile(fp, size, buf[loop%2]);		/* データ読込み[裏buf] */
		if(r_size != size) {
			SSP_ERROR("Read File");
		}
		rd_size += r_size;
		if( (dat_size<=rd_size) && (buf_size>r_size) ) {
			sys_memset((buf[loop%2]+r_size), 0, (buf_size-r_size));	/* Last 0埋め */
		}
		result = Ssp_WaitDmaTerm(20000);					/* 送信待ち[表buf] */
		if(AG903_ENONE != result) {
			SSP_ERROR("WaitReceive");
		}
	}
	result = Ssp_WaitDmaTerm(20000);						/* 送信待ち[Last data] */
	if(AG903_ENONE != result) {
		SSP_ERROR("WaitReceive");
	}

	Ssp_SetCodec(SSP_SMP_CODEC_STOP);						/* Codec停止 */

	result = AG903_DMACMgrAbort(dmahdl);					/* DMA停止 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Dmac Abort");
	}
	result = AG903_DMACMgrReleaseHandle(dmahdl);
	if(AG903_ENONE != result) {
		SSP_ERROR("Dmac ReleaseHandle");
	}

	result = AG903_SSPMgrDisableTransfer(i2shdl);			/* I2S 停止 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Disable");
	}
	result = AG903_SSPMgrClearRxFifo(i2shdl);				/* FIFOクリア */
	if(AG903_ENONE != result) {
		SSP_ERROR("Clear FIFO");
	}
	result = AG903_SSPMgrReleaseHandle(i2shdl);				/* ハンドル解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Release Handle");
	}

	result = Ssp_CloseFile(dev, fp);						/* ファイルClose */
	if(AG903_ENONE != result) {
		SSP_ERROR("File Close Error");
	}

	result = Ssp_RelMem(buf[0], buf[1]);					/* バッファ解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("RelMem");
	}

	return;
}

/**
 * @brief		オーディオ録音処理 [ライブラリ制御DMA]
 * @param[in]	ch		SSPチャネル
 * @param[in]	format	通信フォーマット
 * @return		なし
 * @note		LINE-IN1から入力したデータをSDに保存します
 */
void Ssp_AudioRecDmaInternal(uint8_t ch, uint8_t format)
{
	AG903_SSPMgrHandle*		handle;
	AG903_SSPMgrI2sParam	i2s;
	FILE*		fp;
	uint8_t* 	buf[2];
	uint32_t	loop;
	uint32_t	buf_size;
	int32_t		result;
	uint16_t	rectime;
	int8_t		dev;
	uint8_t		beat;

	((void)format);

	Ssp_InitHw(ch, AG903_I2S_MASTER_READ); 					/* ハードウェア設定 */

	while(1) {	/* Mount〜File Open */
		result = Ssp_SelectDevice(&dev);
		if(AG903_ENONE != result) {
			PRINT(" Invalid");
			continue;
		}
		result = Ssp_MountDevice(dev);
		if(AG903_ENONE != result) {
			PRINT(" Device Error");
			continue;
		}
		fp = Ssp_OpenFile(dev, SSP_SMP_FILE_REC);
		if (NULL == fp) {
			PRINT(" File Error");
			continue;
		}
		break;
	}
	result = Ssp_CreateHeaer(fp, SSP_SMP_HEADER_INIT);		/* ヘッダ生成 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Create Header");
	}

	Ssp_GetRecTime(&rectime);

	result = AG903_SSPMgrGetHandle(ch, &handle);			/* SSPハンドル取得 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP GetHandle");
	}
	result = AG903_SSPMgrReset(handle);						/* SSPリセット */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Reset");
	}
	beat = 2; /* ステレオのためL/Rで2ワード単位の転送が保障できる */
	result = AG903_SSPMgrSetRxFifoThreshold(handle, beat);	/* 受信FIFOしきい値設定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SetRxFifoThreshold");
	}
	result = AG903_SSPMgrEnableDmaMode(handle, (1<<1));		/* DMA有効設定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("EnableDmaMode");
	}
	result = AG903_SSPMgrAttachRxDma(handle, SSP_SMP_DMACH, SSP_SMP_DMACSEL);	/* DMA取り付け */
	if(AG903_ENONE != result) {
		SSP_ERROR("AttachDma");
	}
	result = AG903_SSPMgrSetRxDmaBeat(handle, beat);
	if(AG903_ENONE != result) {
		SSP_ERROR("SetDmaBeat");
	}
	i2s.wordlen	  = 32;	/* 32bit */
	i2s.mono	  = (2<=beat) ? false : true;
	i2s.polarity  = AG903_SSP_POL_NEGATIVE;
	i2s.firstbit  = AG903_SSP_FIRSTBIT_MSB;
	i2s.padlen	  = 0;
	i2s.justified = AG903_SSP_LEFT_JUSTIFIED;
	i2s.clkdiv    = 4;	/* 11.2896/4 = 2.8224[MHz] */
	i2s.slave     = false;
	result = AG903_SSPMgrSetI2sMode(handle, &i2s);	/* I2S指定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetMode");
	}
	SspRxSem = 0;
	result = AG903_SSPMgrSetCallback(handle, (AG903_SSPMgrClbk)Ssp_CallbackSigSemRx);
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetCallback");
	}

	buf_size = ((i2s.wordlen/8)*SSP_SMP_TRNS_WORD);
	result = Ssp_GetMem(&buf[0], &buf[1], buf_size);		/* バッファ確保 */
	if(AG903_ENONE != result) {
		SSP_ERROR("GetMem");
	}

	Ssp_SetCodec(SSP_SMP_CODEC_START_REC);					/* Codec設定 */

	result = AG903_SSPMgrReceiveDma(handle, buf[0], buf_size/(i2s.wordlen/8));	/* 受信開始[buf0] */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Receive");
	}
	result = AG903_SSPMgrEnableTransfer(handle);			/* I2S Enable */
	if(AG903_ENONE != result) {
		SSP_ERROR("Master Enable");
	}

	for(loop=0; loop<rectime; loop+=1) {	/* 録音 */
		Ssp_WaiSemRx();										/* 受信待ち[表buf] */
		if(loop+1<rectime) {
			result = AG903_SSPMgrReceiveDma(handle, buf[(loop+1)%2], buf_size/(i2s.wordlen/8));	/* 受信開始[裏buf] */
			if(AG903_ENONE != result) {
				SSP_ERROR("SSP Receive");
			}
		}
		if(0==loop) {
			sys_memset(buf[0], 0, SSP_SMP_REC_OFFSET);
			result = Ssp_WriteFile(fp, buf_size, buf[0]);
		}
		else {
			result = Ssp_WriteFile(fp, buf_size, buf[loop%2]);	/* データ書き出し[表buf] */
		}
		if(AG903_ENONE != result) {
			SSP_ERROR("File Write Error");
			break;
		}
	}
	result = AG903_SSPMgrDisableTransfer(handle);			/* I2S 停止 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Disable");
	}

	Ssp_SetCodec(SSP_SMP_CODEC_STOP);						/* Codec停止 */

	result = AG903_SSPMgrClearRxFifo(handle);				/* FIFOクリア */
	if(AG903_ENONE != result) {
		SSP_ERROR("Clear FIFO");
	}
	result = AG903_SSPMgrDetachRxDma(handle);				/* DMA取り外し */
	if(AG903_ENONE != result) {
		SSP_ERROR("DetachDma");
	}
	result = AG903_SSPMgrReleaseHandle(handle);				/* ハンドル解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Release Handle");
	}

	result = Ssp_CreateHeaer(fp, SSP_SMP_HEADER_WRITE);		/* ヘッダ生成 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Create Header");
	}
	result = Ssp_CloseFile(dev, fp);						/* ファイルClose */
	if(AG903_ENONE != result) {
		SSP_ERROR("File Close Error");
	}

	result = Ssp_RelMem(buf[0], buf[1]);					/* バッファ解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("RelMem");
	}

	return;
}

/**
 * @brief		オーディオ再生処理 [ライブラリ制御DMA]
 * @param[in]	ch		SSPチャネル
 * @param[in]	format	通信フォーマット
 * @return		なし
 * @note		SDからファイルを読込みLINE-OUT1へ出力します
 */
void Ssp_AudioPlayDmaInternal(uint8_t ch, uint8_t format)
{
	AG903_SSPMgrHandle*		handle;
	AG903_SSPMgrI2sParam	i2s;
	FILE*		fp;
	uint8_t* 	buf[2];
	int32_t		result;
	uint32_t	loop;
	uint32_t	size, r_size, rd_size, dat_size, buf_size;
	uint16_t	bitnum;
	int8_t		dev;
	uint8_t		beat;

	((void)format);

	Ssp_InitHw(ch, AG903_I2S_MASTER_WRITE); 				/* ハードウェア設定 */

	while(1) {	/* Mount〜File Open */
		result = Ssp_SelectDevice(&dev);
		if(AG903_ENONE != result) {
			PRINT(" Invalid");
			continue;
		}
		result = Ssp_MountDevice(dev);
		if(AG903_ENONE != result) {
			PRINT(" Device Error");
			continue;
		}

		fp = Ssp_OpenFile(dev, SSP_SMP_FILE_PLAY);
		if (NULL == fp) {
			PRINT(" File Error");
			continue;
		}
		break;
	}
	result = Ssp_CheckHeader(fp, &dat_size, &bitnum);		/* ヘッダ確認 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Get Data Info");
	}

	result = AG903_SSPMgrGetHandle(ch, &handle);			/* SSPハンドル取得 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP GetHandle");
	}
	result = AG903_SSPMgrReset(handle);						/* SSPリセット */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Reset");
	}
	beat = 2; /* ステレオのためL/Rで2ワード単位の転送が保障できる */
	result = AG903_SSPMgrSetTxFifoThreshold(handle, 16-beat);	/* 送信FIFOしきい値設定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SetTxFifoThreshold");
	}
	result = AG903_SSPMgrEnableDmaMode(handle, (1<<0));		/* DMA有効設定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("EnableDmaMode");
	}
	result = AG903_SSPMgrAttachTxDma(handle, SSP_SMP_DMACH, SSP_SMP_DMACSEL);	/* DMA取り付け */
	if(AG903_ENONE != result) {
		SSP_ERROR("AttachDma");
	}
	result = AG903_SSPMgrSetTxDmaBeat(handle, beat);
	if(AG903_ENONE != result) {
		SSP_ERROR("SetDmaBeat");
	}
	
	i2s.wordlen	  = bitnum;
	i2s.mono	  = (2<=beat) ? false : true;
	i2s.polarity  = AG903_SSP_POL_NEGATIVE;
	i2s.firstbit  = AG903_SSP_FIRSTBIT_MSB;
	if(16 == bitnum) {
		i2s.padlen = 16;
	}
	else {
		i2s.padlen = 0;
	}
	i2s.justified = AG903_SSP_LEFT_JUSTIFIED;
	i2s.clkdiv    = 4;	/* 11.2896/4 = 2.8224[MHz] */
	i2s.slave     = false;
	result = AG903_SSPMgrSetI2sMode(handle, &i2s);	/* I2S指定 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetMode");
	}
	SspTxSem = 0;
	result = AG903_SSPMgrSetCallback(handle, (AG903_SSPMgrClbk)Ssp_CallbackSigSemTx);
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP SetCallback");
	}
	
	buf_size = ((i2s.wordlen/8)*SSP_SMP_TRNS_WORD);
	result = Ssp_GetMem(&buf[0], &buf[1], buf_size);		/* バッファ確保 */
	if(AG903_ENONE != result) {
		SSP_ERROR("GetMem");
	}

	Ssp_SetCodec(SSP_SMP_CODEC_START_PLAY);					/* Codec設定 */

	if(buf_size >= dat_size) {
		size = dat_size;
	}
	else {
		size = buf_size;
	}
	rd_size = Ssp_ReadFile(fp, size, buf[0]);				/* データ読込み buf0 */
	if(rd_size != size) {
		SSP_ERROR("Read File");
	}

	result = AG903_SSPMgrSendDma(handle, buf[0], (rd_size/(i2s.wordlen/8))); /* 送信開始 buf0 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Send");
	}
	result = AG903_SSPMgrEnableTransfer(handle);			/* I2S Enable */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Enable");
	}

	for(loop=1; ;loop++) {	/* 再生 */
		//PRINT("[%d]%d/%d",loop,rd_size,dat_size);
		if(dat_size<=rd_size) {
			break;	/* データ読込み完了 */
		}
		if(buf_size >= (dat_size-rd_size)) {
			size = (dat_size-rd_size);
		}
		else {
			size = buf_size;
		}
		r_size = Ssp_ReadFile(fp, size, buf[loop%2]);		/* データ読込み[裏buf] */
		if(r_size != size) {
			SSP_ERROR("Read File");
		}
		Ssp_WaiSemTx();										/* 送信待ち[表buf] */
		result = AG903_SSPMgrSendDma(handle, buf[loop%2], (r_size/(i2s.wordlen/8)));	/* 送信開始[裏buf] */
		if(AG903_ENONE != result) {
			SSP_ERROR("SSP Send");
		}
		rd_size += r_size;
	}
	Ssp_WaiSemTx();											/* 送信待ち[Last data] */
	result = Ssp_FlushTxFifo(handle, 1000);				/* 送信FIFOからの出力待ち */
	if(AG903_ENONE != result) {
		SSP_ERROR("FlushTxFifo");
	}
	result = AG903_SSPMgrDisableTransfer(handle);			/* I2S 停止 */
	if(AG903_ENONE != result) {
		SSP_ERROR("SSP Disable");
	}

	Ssp_SetCodec(SSP_SMP_CODEC_STOP);						/* Codec停止 */

	result = AG903_SSPMgrClearTxFifo(handle);				/* FIFOクリア */
	if(AG903_ENONE != result) {
		SSP_ERROR("Clear FIFO");
	}
	result = AG903_SSPMgrDetachTxDma(handle);				/* DMA取り外し */
	if(AG903_ENONE != result) {
		SSP_ERROR("DetachDma");
	}
	result = AG903_SSPMgrReleaseHandle(handle);				/* ハンドル解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("Release Handle");
	}

	result = Ssp_CloseFile(dev, fp);						/* ファイルClose */
	if(AG903_ENONE != result) {
		SSP_ERROR("File Close Error");
	}

	result = Ssp_RelMem(buf[0], buf[1]);					/* バッファ解放 */
	if(AG903_ENONE != result) {
		SSP_ERROR("RelMem");
	}

	return;
}

/**
 * @brief		HW設定
 * @param[in]	ch			SSPチャネル
 * @param[in]	type		設定種別
 * @return		なし
 * @note		使用するHW環境(基板,FPGA ver.,,,)によって異なる可能性があります
 */
static void Ssp_InitHw(uint8_t ch, uint8_t type)
{
	/* 端子設定はhw_init()でSSP2,SSP3に対して実施済み */

	if(2 == ch) {
		Board_SelectEthernetType(AG903_ETHER_NONE);	/* SSP-CH2を使用する場合はEtherを未接続にする */
	}
	Board_ActivateAudioCodec();	/* Activate Codec */

	Board_SetSspCommuType(ch, type);
}

/**
 * @brief		デバイス選択
 * @param[out]	dev		デバイス番号
 * @return		AG903_ENONE	正常終了
 * @return		-AG903_EINVAL	入力値異常
 * @note		なし
 */
static int32_t Ssp_SelectDevice(int8_t* dev)
{
	int32_t retval = AG903_ENONE;
	char  chara[4];
	int8_t	num;

	PRINT(" input Device Number");
#ifdef AG903_SAMPLE_USE_FS_ONLY_SD
	PRINT(" 1:SD");
	EPRINT(" ");
	COM_GetNum_Wait(chara, 3);  /* 3 = 1(Device No.)+CRLF */
	num = ASCtoBIN(chara[0]);
	if( (1>num)||(1<num) ) {
		retval = -AG903_EINVAL;
	}
#else
	PRINT(" 1:SD  2:CF  3:USB");
	EPRINT(" ");
	COM_GetNum_Wait(chara, 3);  /* 3 = 1(Device No.)+CRLF */
	num = ASCtoBIN(chara[0]);
	if( (1>num)||(3<num) ) {
		retval = -AG903_EINVAL;
	}
#endif
	(*dev) = num;

	return retval;
}

/**
 * @brief		録音時間取得
 * @param[out]	rectime		録音時間
 * @return		なし
 * @note		なし
 */
static void Ssp_GetRecTime(uint16_t* rectime)
{
	uint16_t	time;
	char		chara[6];
	
	PRINT(" input Rec Time [sec]  ex.030 = 30[sec]");
	EPRINT(" ");
	COM_GetNum_Wait(chara, 5);  /* 5 = 3(time[sec])+CRLF */
	time  = (ASCtoBIN(chara[0])*100);
	time += (ASCtoBIN(chara[1])*10);
	time += (ASCtoBIN(chara[2]));
	(*rectime) = time;

	return;
}

/**
 * @brief		DMAC設定
 * @param[in]	handle		DMAハンドル
 * @param[in]	mode		転送モード
 * @param[in]	sspch		SSPチャネル
 * @param[in]	sspwidth	SSPデータ幅
 * @param[in]	addr		転送メモリアドレス1
 * @param[in]	addr		転送メモリアドレス2
 * @param[in]	size		転送サイズ
 * @return		なし
 * @note		なし
 */
static int32_t Ssp_SetDmac(AG903_DMACMgrHandle* handle, uint8_t mode, uint8_t sspch, uint8_t sspwidth,
														uint32_t addr1, uint32_t addr2, uint32_t size)
{
	AG903_DMACMgrConfig   config;
	AG903_DMACMgrDesc     desc[2];
	int32_t 	result;

	if(SSP_SMP_DMA_SEND == mode) {
		/* DMAC設定 [Memory→SSP] */
		Sys_SetDmaInterface(SSP_SMP_DMACSEL, (SSP_SMP_DMASEL_BASE+(0x02*sspch)+1)); /* dma_req 選択(送信buf) */
		AG903_DMACMgrSetSyncPeripheral(SSP_SMP_DMACH, 1);
		config.val = 0;
		config.st.DstHEn = 1;
		config.st.DstRS  = SSP_SMP_DMACSEL;

		desc[0].Ctrl.val = 0;
		desc[0].Ctrl.st.SrcWidth = 2; /* 32bit */
		if(16==sspwidth) {
			desc[0].Ctrl.st.DstWidth = 1; /* 16bit */
		}
		else {
			desc[0].Ctrl.st.DstWidth = 2; /* 32bit */
		}
		desc[0].Ctrl.st.DstCtrl = 2;  /* Fixed */
		desc[0].SrcAddr  = (uint32_t)addr1;
		desc[0].DstAddr  = (uint32_t)(&AG903_SSPn(sspch)->TxRxDR);
		desc[0].Trns.val = (size/4);
		desc[0].Stride.val = 0;
		desc[0].next = (void*)&desc[1]; /* desc[1]へ */

		desc[1].Ctrl.val = 0;
		desc[1].Ctrl.st.SrcWidth = 2; /* 32bit */
		if(16==sspwidth) {
			desc[1].Ctrl.st.DstWidth = 1; /* 16bit */
		}
		else {
			desc[1].Ctrl.st.DstWidth = 2; /* 32bit */
		}
		desc[1].Ctrl.st.DstCtrl = 2;  /* Fixed */
		desc[1].SrcAddr  = (uint32_t)addr2;
		desc[1].DstAddr  = (uint32_t)(&AG903_SSPn(sspch)->TxRxDR);
		desc[1].Trns.val = (size/4);
		desc[1].Stride.val = 0;
		desc[1].next = (void*)&desc[0]; /* desc[0]へ */
	}
	else {
		/* DMAC設定 [SSP→Memory] */
		Sys_SetDmaInterface(SSP_SMP_DMACSEL, (SSP_SMP_DMASEL_BASE+(0x02*sspch))); /* dma_req 選択(受信buf) */
		AG903_DMACMgrSetSyncPeripheral(SSP_SMP_DMACH, 1);
		config.val = 0;
		config.st.SrcHEn = 1;
		config.st.SrcRS  = SSP_SMP_DMACSEL;

		desc[0].Ctrl.val = 0;
		desc[0].Ctrl.st.SrcWidth = 2; /* 32bit */
		desc[0].Ctrl.st.DstWidth = 2; /* 32bit */
		desc[0].Ctrl.st.SrcCtrl = 2;  /* Fixed */
		desc[0].SrcAddr  = (uint32_t)(&AG903_SSPn(sspch)->TxRxDR);
		desc[0].DstAddr  = (uint32_t)addr1;
		desc[0].Trns.val = (size/4);
		desc[0].Stride.val = 0;
		desc[0].next = (void*)&desc[1]; /* desc[1]へ */

		desc[1].Ctrl.val = 0;
		desc[1].Ctrl.st.SrcWidth = 2; /* 32bit */
		desc[1].Ctrl.st.DstWidth = 2; /* 32bit */
		desc[1].Ctrl.st.SrcCtrl = 2;  /* Fixed */
		desc[1].SrcAddr  = (uint32_t)(&AG903_SSPn(sspch)->TxRxDR);
		desc[1].DstAddr  = (uint32_t)addr2;
		desc[1].Trns.val = (size/4);
		desc[1].Stride.val = 0;
		desc[1].next = (void*)&desc[0]; /* desc[0]へ */
	}
	result = AG903_DMACMgrSetDescList(handle, &config, desc, 2); 					/* ディスクリプタ設定 */

	return result;
}

/**
 * @brief		転送用メモリ確保
 * @param[out]	buf1	バッファ1
 * @param[out]	buf2	バッファ2
 * @param[in]	size	バッファサイズ
 * @return		なし
 * @note		確保後に内容を初期化します
 */
static int32_t Ssp_GetMem(uint8_t** buf1, uint8_t** buf2, uint32_t size)
{
	/* sys_meminitはSsp_init_moduleで実行 */
	(*buf1) = (uint8_t*)sys_memalign(size, SSP_SMP_ALIGN_SIZE);	/* バッファ確保 */
	(*buf2) = (uint8_t*)sys_memalign(size, SSP_SMP_ALIGN_SIZE);	/* バッファ確保 */
	if((NULL==(*buf1)) || (NULL==(*buf2))) {
		return -AG903_EFAULT;
	}

	return AG903_ENONE;
}

/**
 * @brief		転送用メモリ解放
 * @param[in]	送信バッファ
 * @param[in]	受信バッファ
 * @return		なし
 * @note		なし
 */
static int32_t Ssp_RelMem(uint8_t* buf1, uint8_t* buf2)
{
	sys_free(buf1);
	sys_free(buf2);
	/* sys_memfinalはSsp_term_moduleで実行 */

	return AG903_ENONE;
}

/**
 * @brief		デバイスマウント
 * @param[in]	dev		デバイス番号
 * @return	AG903_ENONE	 	正常終了.
 * @return	-AG903_EINVAL  	引数異常.
 * @return	-AG903_EFAULT		異常終了.
 * @note		なし
 */
static int32_t Ssp_MountDevice(int8_t dev)
{
	ER		ret;

	if( (0>=dev)||((int8_t)(sizeof(SspDevType))<dev) ) {
		return -AG903_EINVAL;
	}

	ret = mountp('A', 0, (ID)Fsif_GetDevid(SspDevType[dev-1]));
	if (E_OK != ret) {
		return -AG903_EFAULT;
	}

	return AG903_ENONE;
}

/**
 * @brief		ファイルオープン
 * @param[in]	dev		デバイス番号
 * @param[in]	type	ファイル種別
 * @return		ファイルポインタ (NULL = 異常終了)
 * @note		なし
 */
static FILE* Ssp_OpenFile(int8_t dev, uint8_t type)
{
	FILE*	fp;

	((void)dev);

	if(SSP_SMP_FILE_REC == type) {	/* Rec File Open */
		fp = fopen((const char*)SSP_SMP_REC_FILEPATH, (const char*)"wb");	/* Write Open */
	}
	else if(SSP_SMP_FILE_PLAY == type) { /* Play File Open */
		fp = fopen((const char*)SSP_SMP_PLAY_FILEPATH, (const char*)"rb");	/* Read Open */
	}
	else {
		fp = NULL;
	}

	return fp;
}

/**
 * @brief		ファイル書込み
 * @param[in]	fp		ファイルポインタ
 * @param[in]	size    サイズ
 * @param[in]	buf		バッファアドレス
 * @return		なし
 * @note		なし
 */
static int32_t Ssp_WriteFile(FILE* fp, uint32_t size, uint8_t* buf)
{
	size_t	w_size;

	w_size = fwrite((const void*)buf, (size_t)1, (size_t)size, fp);
	if(w_size != (size_t)size) {
		return -AG903_EFAULT;
	}

	return AG903_ENONE;
}

/**
 * @brief		ファイル読込み
 * @param[in]	fp		ファイルポインタ
 * @param[in]	size    サイズ
 * @param[in]	buf		バッファアドレス
 * @return		なし
 * @note		なし
 */
static uint32_t Ssp_ReadFile(FILE* fp, uint32_t size, uint8_t* buf)
{
	size_t	r_size;

	r_size = fread((void*)buf, (size_t)1, (size_t)size, fp);

	return (uint32_t)r_size;
}

/**
 * @brief		ファイルクローズ
 * @param[in]	dev		デバイス番号
 * @param[in]	fp		ファイルポインタ
 * @return		なし
 * @note		なし
 */
static int32_t Ssp_CloseFile(int8_t dev, FILE* fp)
{
	int32_t	ret;

	if( (0>=dev)||((int8_t)(sizeof(SspDevType))<dev) ) {
		return -AG903_EFAULT;
	}

	ret = fclose(fp);
	if(0 != ret) {
		return -AG903_EFAULT;
	}

	umountp(0, (ID)Fsif_GetDevid(SspDevType[dev-1]));

	return AG903_ENONE;
}

/**
 * @brief		ヘッダ生成（RIFF/WAVE）
 * @param[in]	fp		ファイルポインタ
 * @param[in]	type	生成種別
 * @return		なし
 * @note		なし
 */
static int32_t Ssp_CreateHeaer(FILE* fp, uint8_t type)
{
	int32_t		retval = -AG903_EFAULT;
	int32_t		result;
	int32_t		size;
	int32_t		w_size;
	uint32_t	pnt=0;
	uint8_t		buf[SSP_SMP_HEADER_SIZE] = {0};

	if(SSP_SMP_HEADER_INIT == type) {
		w_size = fwrite((const void*)buf, (size_t)1, (size_t)SSP_SMP_HEADER_SIZE, fp);	/* Header Write [0埋め] */
		if(w_size == SSP_SMP_HEADER_SIZE) {
			retval = AG903_ENONE;
		}
	}
	else if(SSP_SMP_HEADER_WRITE == type) {
		do {
			size = ftell(fp); /* サイズ取得 */
			if(0 > size) {
				break;
			}
			result = fseek(fp, 0, SEEK_SET); /* 先頭へseek */
			if(0 > result) {
				break;
			}
			buf[pnt++]='R'; buf[pnt++]='I'; buf[pnt++]='F'; buf[pnt++]='F';	/* RIFFヘッダ */
			size -= 8; /* 8 = RIFF  +size field */
			buf[pnt++]=(uint8_t)size;
			buf[pnt++]=(uint8_t)(size>>8);
			buf[pnt++]=(uint8_t)(size>>16);
			buf[pnt++]=(uint8_t)(size>>24);
			buf[pnt++]='W'; buf[pnt++]='A'; buf[pnt++]='V'; buf[pnt++]='E'; /* WAVEヘッダ */
			buf[pnt++]='f'; buf[pnt++]='m'; buf[pnt++]='t'; buf[pnt++]=' '; /* fmt field */
			buf[pnt++]=0x10; buf[pnt++]=0x00; buf[pnt++]=0x00; buf[pnt++]=0x00; /* field size */
			buf[pnt++]=0x01; buf[pnt++]=0x00; /* format ID = PCM */
			buf[pnt++]=0x02; buf[pnt++]=0x00; /* stereo */
			buf[pnt++]=0x44; buf[pnt++]=0xAC; buf[pnt++]=0x00; buf[pnt++]=0x00; /* sampling = 44.1kH */
			buf[pnt++]=0x20; buf[pnt++]=0x62; buf[pnt++]=0x05; buf[pnt++]=0x00; /* Byte/sec */
			buf[pnt++]=0x08; buf[pnt++]=0x00; /* block size */
			buf[pnt++]=0x20; buf[pnt++]=0x00; /* bit/sample */
			buf[pnt++]='d'; buf[pnt++]='a'; buf[pnt++]='t'; buf[pnt++]='a'; /* data field */
			size -= (SSP_SMP_HEADER_SIZE-8);
			buf[pnt++]=(uint8_t)size;
			buf[pnt++]=(uint8_t)(size>>8);
			buf[pnt++]=(uint8_t)(size>>16);
			buf[pnt++]=(uint8_t)(size>>24);
			w_size = fwrite((const void*)buf, (size_t)1, (size_t)SSP_SMP_HEADER_SIZE, fp);	/* Header Write */
			if(SSP_SMP_HEADER_SIZE == w_size) {
				retval = AG903_ENONE;
			}
		}while(0);
	}
	else {
		/* 処理なし */
	}

	return retval;
}

/**
 * @brief		ヘッダ確認（RIFF/WAVE）
 * @param[in]	fp		ファイルポインタ
 * @param[out]	size	データサイズ
 * @param[out]	bitnum	1サンプルあたりのbit数
 * @return		なし
 * @note		対応フォーマットは以下に限る
 * @note		フォーマットID：0x0001（リニアPCM）
 * @note		チャネル数：0x0002（stereo）
 * @note		サンプリングレート：0x0000AC44（44.1kHz）
 * @note		1サンプルあたりのbit数：16 or 32
 */
static int32_t Ssp_CheckHeader(FILE* fp, uint32_t* size, uint16_t* bitnum)
{
	int32_t	 retval=AG903_ENONE;
	uint32_t loop;
	uint32_t blksz;
	uint32_t buf[4];
	size_t	 r_size;

	r_size = fread((void*)buf, (size_t)1, (size_t)12, fp);
	if( (12 != r_size) 		 	||
		(0x46464952 != buf[0])	||	/* RIFF */
		(0x45564157 != buf[2]) ) { 	/* WAVE */
		return -AG903_EFAULT;
	}

	for(loop=0; loop<1000; loop++) {
		r_size = fread((void*)buf, (size_t)1, (size_t)8, fp);
		if(8 != r_size) {
			retval = -AG903_EFAULT;
			break;
		}

		if(0x20746D66==buf[0]) {	/* fmt  */
			blksz = buf[1];
			r_size = fread((void*)buf, (size_t)1, (size_t)16, fp);
			if( (16 != r_size) 		 	||
				(0x00020001 != buf[0])	||	/* PCMかつStereo以外は非対応 */
				(0x0000AC44 != buf[1]) ) { 	/* rate 44.1kHz以外は非対応 */
				retval = -AG903_EFAULT;
				break;
			}
			(*bitnum) = (uint16_t)(buf[3]>>16);
			if( (16!=(*bitnum)) && (32!=(*bitnum)) ) { /* 16bit,32bit以外は非対応 */
				retval = -AG903_EFAULT;
				break;
			}
			if(16 < blksz) {
				fseek(fp, (blksz-16), SEEK_CUR);
			}
		}
		else if(0x61746164==buf[0]) {	/* data */
			(*size) = buf[1];
			break;
		}
		else {	/* その他 */
			blksz = buf[1];
			fseek(fp, blksz, SEEK_CUR);
		}
	}
	if(1000<=loop) {
		retval = -AG903_EFAULT;	/* dataが見つからなかった */
	}

	return retval;
}

/**
 * @brief		コーデック設定
 * @param[in]	type	設定種別
 * @return		なし
 * @note		なし
 */
static void Ssp_SetCodec(uint8_t type)
{
	switch(type) {
		case SSP_SMP_CODEC_STOP:
			Board_WriteAudioCodec(0x02, (0x07<<5)|(0x0F<<1)|(0x00<<0));				/* ALL PDN */
			break;

		case SSP_SMP_CODEC_START_REC:
			Board_WriteAudioCodec(0x02, (0x06<<5)|(0x0F<<1)|(0x00<<0));				/* ADC1 ON */
			Board_WriteAudioCodec(0x03, (0x03<<6)|(0x03<<4)|(0x00<<1)|(0x00<<0));	/* Slava Mode, MCLK [11.2896MHz] */
			Board_WriteAudioCodec(0x04, (0x00<<7)|(0x00<<6)|(0x01<<3)|(0x01<<0));	/* I2S Format */
			Board_WriteAudioCodec(0x05, (0x00<<6)|(0x00<<5)|(0x07<<2)|(0x00<<0));	/* Single-Ended */
			Board_WriteAudioCodec(0x06, (0x00<<7)|(0x00<<5)|(0x01<<4)|(0x00<<3)|(0x00<<2)|(0x00<<0));
			Board_WriteAudioCodec(0x07, (0x03<<6)|(0x03<<4)|(0x03<<2)|(0x03<<0));	/* Mute Enable */
			Board_WriteAudioCodec(0x11, (0x00));	/* Volume Control */
			Board_WriteAudioCodec(0x12, (0x00));	/* Volume Control */
			sys_dlytsk(400);
			break;

		case SSP_SMP_CODEC_START_PLAY:
			Board_WriteAudioCodec(0x02, (0x07<<5)|(0x0E<<1)|(0x00<<0));				/* DAC1 ON */
			Board_WriteAudioCodec(0x03, (0x03<<6)|(0x03<<4)|(0x00<<1)|(0x00<<0));	/* Slava Mode, MCLK [11.2896MHz] */
			Board_WriteAudioCodec(0x04, (0x00<<7)|(0x00<<6)|(0x01<<3)|(0x01<<0));	/* I2S Format */
			Board_WriteAudioCodec(0x06, (0x00<<7)|(0x00<<5)|(0x00<<4)|(0x00<<3)|(0x00<<2)|(0x00<<0));
			Board_WriteAudioCodec(0x07, (0x00<<6)|(0x00<<4)|(0x00<<2)|(0x00<<0));	/* Mute Disable */
			Board_WriteAudioCodec(0x08, (0x00));	/* Volume Control */
			Board_WriteAudioCodec(0x09, (0x00));	/* Volume Control */
			Board_WriteAudioCodec(0x1B, (0x01<<1)|(0x00<<0));						/* Mute Disable */
			sys_dlytsk(400);
			break;

		default:
			/* 処理なし */
			break;
	}

	return;
}

/**
 * @brief		送信完了待ち
 * @param[in]	timout	タイムアウト[ms]
 * @retval		AG903_ENONE	正常終了
 * @retval		-AG903_ETIMEDOUT タイムアウト
 * @note		なし
 */
static int32_t Ssp_WaitSend(uint32_t timout)
{
	int32_t		retval = AG903_ENONE;
	uint32_t	cnt;
	uint32_t	loop;

	cnt = timout/10;
	if(timout%10) {
		cnt++;
	}
	for(loop=0; loop<cnt; loop++) {
		if(true == SspTxEnd) {
			SspTxEnd = false;
			break;
		}
		sys_dlytsk(10);
	}
	if(cnt<=loop) {
		retval = -AG903_ETIMEDOUT;
	}

	return retval;
}

/**
 * @brief		受信完了待ち
 * @param[in]	timout	タイムアウト[ms]
 * @retval		AG903_ENONE	正常終了
 * @retval		-AG903_ETIMEDOUT タイムアウト
 * @note		なし
 */
static int32_t Ssp_WaitReceive(uint32_t timout)
{
	int32_t		retval = AG903_ENONE;
	uint32_t	cnt;
	uint32_t	loop;

	cnt = timout/10;
	if(timout%10) {
		cnt++;
	}
	for(loop=0; loop<cnt; loop++) {
		if(true == SspRxEnd) {
			SspRxEnd = false;
			break;
		}
		sys_dlytsk(10);
	}
	if(cnt<=loop) {
		retval = -AG903_ETIMEDOUT;
	}

	return retval;
}

/**
 * @brief		送信FIFOからの出力待ち
 * @param[in]	handle	SSPハンドル
 * @param[in]	timout	タイムアウト[ms]
 * @retval		AG903_ENONE	正常終了
 * @retval		-AG903_ETIMEDOUT タイムアウト
 * @retval		-AG903_EINVAL 引数異常
 * @note		なし
 */
static int32_t Ssp_FlushTxFifo(AG903_SSPMgrHandle* handle, uint32_t timout)
{
	int32_t		retval = AG903_ENONE;
	uint32_t	cnt;
	uint32_t	loop;
	AG903_SSPMgrStatus	status;

	cnt = timout/10;
	if(timout%10) {
		cnt++;
	}
	for(loop=0; loop<cnt; loop++) {
		retval = AG903_SSPMgrGetStatus(handle, &status);
		if(AG903_ENONE != retval) {
			SSP_ERROR("GetStatus");
			break;
		}
		if(0 == status.txfifo_num) {
			break;
		}
		sys_dlytsk(10);
	}
	if(cnt<=loop) {
		retval = -AG903_ETIMEDOUT;
	}

	return retval;
}

/**
 * @brief		DMA転送完了待ち
 * @param[in]	timout	タイムアウト[ms]
 * @retval		AG903_ENONE	正常終了
 * @retval		-AG903_ETIMEDOUT タイムアウト
 * @note		なし
 */
static int32_t Ssp_WaitDmaTerm(uint32_t timout)
{
	int32_t		retval = AG903_ENONE;
	uint32_t	cnt;
	uint32_t	loop;

	cnt = timout/10;
	if(timout%10) {
		cnt++;
	}
	for(loop=0; loop<cnt; loop++) {
		if(true == SspDmaTerm) {
			SspDmaTerm = false;
			break;
		}
		sys_dlytsk(10);
	}
	if(cnt<=loop) {
		retval = -AG903_ETIMEDOUT;
	}

	return retval;
}

/**
 * @brief	コールバック (SSP[RxCh]).
 * @param[in]	handle  SSPハンドル.
 * @param[in]	event	コールバックイベント.
 * @return	なし.
 * @note	なし.
 */
static void Ssp_Callback(AG903_SSPMgrHandle* handle, uint32_t event)
{
	((void)handle);

	if(AG903_SSP_EVENT_SNDEND & event) {
		SspTxEnd = true;
	}

	if(AG903_SSP_EVENT_RCVEND & event) {
		SspRxEnd = true;
	}

	if((AG903_SSP_EVENT_OVERRUN|AG903_SSP_EVENT_UNDERRUN) & event) {
		/* 処理なし */
	}

	return;
}

/**
 * @brief		DMAC 転送終了コールバック
 * @param[in]	handle	ハンドル
 * @param[out]	desc	ディスクリプタ格納先
 * @param[out]	end		循環終了フラグ
 * @return		なし
 * @note		なし
 */
static void Ssp_CallbackDmaTerm(AG903_DMACMgrHandle* handle, AG903_DMACMgrDesc** desc, uint8_t* end)
{
	((void)handle);
	((void)end);

	SspDmaTerm = true;
	(*desc) = NULL;

	return;
}

/**
 * @brief		DMAC アボートコールバック
 * @param[in]	handle	ハンドル
 * @return		なし
 * @note		なし
 */
static void Ssp_CallbackDmaAbort(AG903_DMACMgrHandle* handle)
{
	((void)handle);
	return;
}

/**
 * @brief		DMAC エラーコールバック
 * @param[in]	handle	ハンドル
 * @return		なし
 * @note		なし
 */
static void Ssp_CallbackDmaErr(AG903_DMACMgrHandle* handle)
{
	((void)handle);
	return;
}

static void Ssp_CallbackSigSemTx(AG903_SSPMgrHandle* handle, uint32_t event)
{
	((void)handle);

	if(AG903_SSP_EVENT_SNDEND & event) {
		if(1 > SspTxSem) {
			SspTxSem += 1;
		}
	}

	return;
}

static void Ssp_WaiSemTx(void)
{
	while (0 == SspTxSem);
	SspTxSem -= 1;

	return;
}

static void Ssp_CallbackSigSemRx(AG903_SSPMgrHandle* handle, uint32_t event)
{
	((void)handle);

	if(AG903_SSP_EVENT_RCVEND & event) {
		if(1 > SspRxSem) {
			SspRxSem += 1;
		}
	}

	return;
}

static void Ssp_WaiSemRx(void)
{
	while (0 == SspRxSem);
	SspRxSem -= 1;

	return;
}
