/*
 * @history		2019_12_27  [SDK3.0] タスクをTA_FPU属性付で起動していない不具合を修正 (uC3版) (#2698)
 */
/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017 AXELL CORPORATION, all rights reserved.
*/

/*****************************************************************************/
/*     include file                                                          */
/*****************************************************************************/
#include "kernel.h"
#include "sample_common.h"
#include "uhs_configtop.h"
#include "storage/usb_msc_type.h"
#include "storage/usb_msc_api.h"
#include <stdint.h>
#include "vram/vrammgr.h"
#include "usb_sample.h"
#include "sample_util.h"
#include "sample_app_msc.h"


/*****************************************************************************/
/*     typedef                                                               */
/*****************************************************************************/
/* タスク情報 */
typedef struct MscTaskInfo {
	ID					tskid;								/* タスクID							*/
	ID					mbxid;								/* メールボックスID					*/
	VB					drive;								/* ドライブレター					*/
	char				*pFileName;							/* ファイル名						*/
	char				*pReadBuff;							/* 読み出し用バッファアドレス		*/
	char				*pWriteBuff;						/* 書き込み用バッファアドレス		*/
	void				*pContext;							/* 接続されていなければNULL			*/
} MSC_TASK_INFO;

/* 接続／切断メッセージ */
typedef struct MscConnMsgPkt {
	uhs_ubit32_t		msg;								/* メッセージ内容					*/
	void				*pContext;							/* 通知元デバイスのコンテキスト		*/
} MSC_CONN_MSG_PKT;

/* アプリケーションタスクメッセージ */
typedef struct MscAppMsgPkt {
	uhs_ubit32_t		msg;								/* メッセージ内容					*/
	void				*pContext;							/* 通知元デバイスのコンテキスト		*/
} MSC_APP_MSG_PKT;

/*****************************************************************************/
/*     define                                                                */
/*****************************************************************************/
/* バッファサイズ */
#define BUFF_SIZE					(128)

/* 作成するディレクトリ名 */
#define DIR_NAME					"DIR"

/*****************************************************************************/
/*     global variable                                                       */
/*****************************************************************************/
/* バッファ */
static char g_ReadBuff[USBH_MAX_CLS_MSC_NUM][BUFF_SIZE];
static char g_WriteBuff[USBH_MAX_CLS_MSC_NUM][BUFF_SIZE];

/* タスク情報テーブル */
static MSC_TASK_INFO g_MscTaskInfoTbl[USBH_MAX_CLS_MSC_NUM];

/* 接続切断タスク用メールボックスID */
ER_ID				g_mbxid_msc_conn;

/* 接続切断タスク用タスクID */
ER_ID				g_tskid_msc_conn;

/*****************************************************************************/
/*     extern function                                                       */
/*****************************************************************************/
extern	int32_t AG903_VRAMMgrCreateMpl(AG903_VRAMMgrMplPrm* mplprm);
extern	int32_t AG903_VRAMMgrDeleteMpl(int32_t mplid);
extern	void* AG903_VRAMMgrMalloc(int32_t mplid, uint32_t blksz);
extern	int32_t AG903_VRAMMgrFree(int32_t mplid, void* blk);

/*****************************************************************************/
/*     function prototype                                                    */
/*****************************************************************************/
static int msc_registTaskInfo(void* pContext);
static int msc_unregistTaskInfo(void* pContext);
static void msc_conn_task(void* context);

/******************************************************************************
   Function：  msc_appinit
   Argument：  なし
   Return  ：  なし
   Summary ：  MSC初期化を行います
   Caution ：  なし
******************************************************************************/
void msc_appinit(void)
{
	T_CMBX					cmbx;							/* メールボックス生成パラメータ		*/
	T_CTSK					ctsk;							/* タスク生成パラメータ				*/
	uhs_ubit8_t				majorVersion, minorVersion;		/* バージョン						*/
	char fileName[]			= "FILE_01.txt";				/* ファイル名						*/
	char writeBuff[] =
		{ "FILE_01.txt 0001\r\nThe quick brown fox jumps over the lazy dog. 1234567890" };
	uhs_status_t			retval;
//	ER						ercd;
	int						i;


	/*----------------*/
	/* バージョン取得 */
	/*----------------*/
	retval = Usbh_Stor_GetVersion(&majorVersion, &minorVersion);
	if(retval == USBSTOR_SUCCESS) {
		SAMPLE_PRINT("MSC Driver version : ");
		SAMPLE_PRINT_BYTE_HEX(majorVersion);
		SAMPLE_PRINT(".");
		SAMPLE_PRINT_BYTE_HEX(minorVersion);
		SAMPLE_PRINT("\r\n");
	}

	/*--------------------------*/
	/* タスク情報テーブル初期化 */
	/*--------------------------*/
	for(i = 0; i < USBH_MAX_CLS_MSC_NUM; i++) {
		g_MscTaskInfoTbl[i].tskid		= 0;
		g_MscTaskInfoTbl[i].mbxid		= 0;
		g_MscTaskInfoTbl[i].drive		= ('C' + i);
		fileName[6] = ('1' + i);
		g_MscTaskInfoTbl[i].pFileName	= AG903_VRAMMgrMalloc(g_mplid_app, sizeof(fileName));
		sample_memcpy(g_MscTaskInfoTbl[i].pFileName, fileName, sizeof(fileName));
		g_MscTaskInfoTbl[i].pReadBuff	= g_ReadBuff[i];
		g_MscTaskInfoTbl[i].pWriteBuff	= g_WriteBuff[i];
		g_MscTaskInfoTbl[i].pContext	= NULL;
	}
	
	/*--------------*/
	/* バッファ設定 */
	/*--------------*/
	/* 書き込みデータ設定 */
	for(i = 0; i < USBH_MAX_CLS_MSC_NUM; i++) {
		sample_memset(g_MscTaskInfoTbl[i].pWriteBuff, 0, BUFF_SIZE);
		writeBuff[6]= ('1' + i);
		sample_memcpy(g_MscTaskInfoTbl[i].pWriteBuff, writeBuff, sizeof(writeBuff));
	}
	
	/*--------------------*/
	/* メールボックス生成 */
	/*--------------------*/
	/* パラメータ設定 */
    cmbx.mbxatr		= TA_TFIFO | TA_MFIFO;
    cmbx.maxmpri	= 1;
    cmbx.mprihd		= NULL;

	/* 接続切断タスク用メールボックス生成 */
	g_mbxid_msc_conn = acre_mbx(&cmbx);
	if(g_mbxid_msc_conn < 0) {
		/* エラー */
		return;
	}

	/*----------------------*/
	/* 接続／切断タスク生成 */
	/*----------------------*/
	/* パラメータ設定 */
	ctsk.tskatr		= TA_HLNG | TA_ACT | TA_FPU;
	ctsk.exinf		= (void *)NULL;							/* TASK起動時のパラメータ(無し)			*/
	ctsk.task		= (FP)msc_conn_task;					/* TASKの起動アドレス					*/
	ctsk.itskpri	= (PRI)TSK_PRI_SAMPLE;					/* TASKの優先度							*/
	ctsk.stksz		= (INT)TSK_STACK_SIZE_SAMPLE;			/* TASKのスタックSIZE					*/
	ctsk.stk		= NULL;									/* TASKのスタックアドレス=NULL(自動)	*/
	
	/* タスク生成 */
	g_tskid_msc_conn = acre_tsk(&ctsk);
	if(g_tskid_msc_conn < 0) {
		/* エラー */
		return;
	}
}

/******************************************************************************
   Function：  msc_append
   Argument：  なし
   Return  ：  なし
   Summary ：  MSC終了処理を行います
   Caution ：  なし
******************************************************************************/
void msc_append(void)
{
	int						i;

	del_mbx(g_mbxid_msc_conn);
	
	for(i = 0; i < USBH_MAX_CLS_MSC_NUM; i++) {
		del_mbx(g_MscTaskInfoTbl[i].mbxid);
	}

	ter_tsk(g_tskid_msc_conn);
	del_tsk(g_tskid_msc_conn);
	
	for(i = 0; i < USBH_MAX_CLS_MSC_NUM; i++) {
		ter_tsk(g_MscTaskInfoTbl[i].tskid);
		del_tsk(g_MscTaskInfoTbl[i].tskid);
	}

	return;
}

/******************************************************************************
   Function：  msc_getContext
   Argument：  devnum   ： デバイス番号 [0〜USBH_MAX_CLS_MSC_NUM]
               pContext	： 取得context
   Return  ：  なし
   Summary ：  contextを取得します
   Caution ：  なし
******************************************************************************/
void msc_getContext(uint8_t devnum, int32_t* pContext)
{

	if( (USBH_MAX_CLS_MSC_NUM <= devnum) ||
		(NULL == g_MscTaskInfoTbl[devnum].pContext) ) {
		(*pContext) = -1;
		return;
	}

	(*pContext) = (int32_t)g_MscTaskInfoTbl[devnum].pContext;

	return;
}

/******************************************************************************
   Function：  msc_callback
   Argument：  callback_id : コールバック種別
               pContext    : コールバックcontext
   Return  ：  なし
   Summary ：  MSCクラスドライバからの接続／切断コールバック処理
   Caution ：  なし
******************************************************************************/
void msc_callback(int callback_id, void* pContext)
{
	MSC_CONN_MSG_PKT*		pk_msg;							/* メッセージパケット				*/
	ER						ercd;
	
	
	/* メッセージバッファ取得 */
	pk_msg = AG903_VRAMMgrMalloc(g_mplid_app, sizeof(MSC_CONN_MSG_PKT));
	/* メッセージ内容 */
	pk_msg->msg = (uhs_ubit32_t)callback_id;
	/* コンテキスト */
	pk_msg->pContext = pContext;
	
	switch(callback_id){
		/*--------*/
		/* 接続   */
		/*--------*/
		case CBID_STOR_DEVATTACH:

//			SAMPLE_PRINT("MSC device attached\r\n");

			/* コンテキストを登録 */
			msc_registTaskInfo(pContext);
			
			/* メッセージ送信 */
			ercd = snd_mbx(g_mbxid_msc_conn, (T_MSG *)pk_msg);
			if(ercd != E_OK) {
				/* バッファ解放 */
				AG903_VRAMMgrFree(g_mplid_app, pk_msg);
				return;
			}
			break;
		
		/*--------*/
		/* 切断   */
		/*--------*/
		case CBID_STOR_DEVDETACH:

//			SAMPLE_PRINT("MSC device detached\r\n");

			/* コンテキストを削除 */
			msc_unregistTaskInfo(pContext);

			/* メッセージ送信 */
			ercd = snd_mbx(g_mbxid_msc_conn, (T_MSG *)pk_msg);
			if(ercd != E_OK) {
				/* バッファ解放 */
				AG903_VRAMMgrFree(g_mplid_app, pk_msg);
				return;
			}
			break;
		
		/*----------------------------------*/
		/* 一時的に使用出来ないデバイス接続 */
		/*----------------------------------*/
		case CBID_STOR_DEVATTACH_NOT_READY:
		
//			SAMPLE_PRINT("MSC device attached not ready\r\n");
			/* バッファ解放 */
			AG903_VRAMMgrFree(g_mplid_app, pk_msg);
			break;
			
		/*--------------------------*/
		/* 使用出来ないデバイス接続 */
		/*--------------------------*/
		case CBID_STOR_DEVATTACH_NOT_USE:
		
//			SAMPLE_PRINT("MSC device attached not use\r\n");
			/* バッファ解放 */
			AG903_VRAMMgrFree(g_mplid_app, pk_msg);
			break;

		/*--------*/
		/* その他 */
		/*--------*/
		default:

//			SAMPLE_PRINT("MSC callback other\r\n");
			/* バッファ解放 */
			AG903_VRAMMgrFree(g_mplid_app, pk_msg);
			break;
	}
}

/******************************************************************************
   Function：  msc_registTaskInfo
   Argument：  pContext     : コールバックcontext
   Return  ：  SAMPLE_TRUE  : 成功
               SAMPLE_FALSE : 失敗
   Summary ：  タスク情報にコンテキストを登録します
   Caution ：  なし
******************************************************************************/
static int msc_registTaskInfo(void* pContext)
{
	int						i;


	/* テーブルをサーチ */
	for(i = 0; i < USBH_MAX_CLS_MSC_NUM; i++) {
		/* 未登録 */
		if(g_MscTaskInfoTbl[i].pContext == NULL) {
			/* コンテキストを登録 */
			g_MscTaskInfoTbl[i].pContext = pContext;
			return SAMPLE_TRUE;
		}
	}
	/* 空きなし */
	return SAMPLE_FALSE;
}

/******************************************************************************
   Function：  msc_unregistTaskInfo
   Argument：  pContext     : コールバックcontext
   Return  ：  SAMPLE_TRUE  : 成功
               SAMPLE_FALSE : 失敗
   Summary ：  タスク情報からコンテキストを削除します
   Caution ：  なし
******************************************************************************/
static int msc_unregistTaskInfo(void* pContext)
{
	int						i;


	/* テーブルをサーチ */
	for(i = 0; i < USBH_MAX_CLS_MSC_NUM; i++) {
		/* コンテキストが一致 */
		if(g_MscTaskInfoTbl[i].pContext == pContext) {
			/* コンテキストを削除 */
			g_MscTaskInfoTbl[i].pContext = NULL;
			return SAMPLE_TRUE;
		}
	}
	/* コンテキストなし */
	return SAMPLE_FALSE;
}

/******************************************************************************
   Function：  msc_conn_task
   Argument：  context     : NULL
   Return  ：  なし
   Summary ：  MSC接続／切断検知タスク
   Caution ：  MSCデバイス共通
******************************************************************************/
static void msc_conn_task(void* context)
{
	MSC_CONN_MSG_PKT*		pk_msg;							/* メッセージパケット				*/
	uhs_ubit32_t			msg;							/* メッセージ内容					*/
	void*					pContext;						/* コンテキスト						*/

	((void)context);

	while(1) {
		/* 接続／切断メッセージ待ち */
		rcv_mbx(g_mbxid_msc_conn, (T_MSG **)&pk_msg);
			
		/* 接続／されたデバイスのコンテキスト取得 */
		pContext = pk_msg->pContext;
		/* 取得失敗 */
		if(pContext == NULL) {
			/* バッファ解放 */
			AG903_VRAMMgrFree(g_mplid_app, pk_msg);
			continue;
		}

		SAMPLE_PRINT("  context : 0x");
		SAMPLE_PRINT_LONG_HEX((unsigned long)pContext);
		SAMPLE_PRINT("\r\n");

		/* メッセージ内容保存 */
		msg = pk_msg->msg;
		
		/*------*/
		/* 接続 */
		/*------*/
		if(msg == CBID_STOR_DEVATTACH) {
			/* 処理なし */
		}
		
		/*------*/
		/* 切断 */
		/*------*/
		else {
			/* バッファ解放 */
			AG903_VRAMMgrFree(g_mplid_app, pk_msg);
		}
	}
}

