/*
 * @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 "uhs_configtop.h"
#include "cdc/usb_cdc_commif_api.h"
#include "cdc/usb_cdc_dataif_api.h"
#include "cdc/usb_cdc_api.h"
#include <stdint.h>
#include "vram/vrammgr.h"
#include "usb_sample.h"
#include "sample_util.h"
#include "sample_app_cdc.h"

#define TEST_LOOPBAKC_DEVICE	(1)		/* Loopbackデバイスを用いたテスト時は「1」 */
#if TEST_LOOPBAKC_DEVICE
	static uint8_t			testdata[64] = {0};
#endif
/*****************************************************************************/
/*     typedef                                                               */
/*****************************************************************************/
/* タスク情報 */
typedef struct CdcTaskInfo {
	ID					tskid;								/* タスクID							*/
	ID					mbxid;								/* メールボックスID					*/
	uhs_ubit8_t*		pNotifyBuff;						/* Notify用バッファアドレス			*/
	uhs_ubit8_t*		pBulkInBuff;						/* バルクINバッファアドレス			*/
	void*				pContext;							/* 接続されていなければNULL			*/
} CDC_TASK_INFO;

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

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

/*****************************************************************************/
/*     define                                                                */
/*****************************************************************************/
/* バッファサイズ */
#define NOTIFY_BUFF_SIZE			(64)

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

/* ControlLineState */
#define LINE_STATE_DTR				(0x0001)
#define LINE_STATE_RTS				(0x0002)

/* NetworkConnection */
#define NETWORK_CONNECTION_DISCON	(0x0000)
#define NETWORK_CONNECTION_CONN		(0x0001)

//	BULK ERR通知MSGID
#define	CDC_DETECT_BULKIN_ERR		(100)

/*****************************************************************************/
/*     global variable                                                       */
/*****************************************************************************/
/* Notify用バッファ */
static uhs_ubit8_t	g_notifyBuff[USBH_MAX_CLS_CDC_NUM][NOTIFY_BUFF_SIZE];


/* タスク情報テーブル */
static CDC_TASK_INFO g_CdcTaskInfoTbl[USBH_MAX_CLS_CDC_NUM];

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

/* 接続切断タスク用タスクID */
ER_ID				g_tskid_cdc_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 void cdc_notifyCallback(PCDC_NOTIFY_INFO pNotifyInfo, uhs_status_t RetVal);
static int cdc_registTaskInfo(void* pContext);
static int cdc_unregistTaskInfo(void* pContext);
static ID cdc_getMbxId(void* pContext);
static uhs_ubit8_t* cdc_getNotifyBuff(void* pContext);
static void cdc_conn_task(void* context);
static void cdc_app_task(void* context);
static uhs_status_t cdc_attachDeviceInit(void* pContext);

/******************************************************************************
   Function：  cdc_appinit
   Argument：  なし
   Return  ：  なし
   Summary ：  CDC初期化を行います
   Caution ：  なし
******************************************************************************/
void cdc_appinit(void)
{
	T_CMBX					cmbx;							/* メールボックス生成パラメータ		*/
	T_CTSK					ctsk;							/* タスク生成パラメータ				*/
	uhs_ubit8_t				majorVersion, minorVersion;		/* バージョン						*/
	uhs_status_t			retval;
	int						i;
	
	
	/*----------------*/
	/* バージョン取得 */
	/*----------------*/
	retval = Usbh_Cdc_GetVersion(&majorVersion, &minorVersion);
	if(retval == USBCDC_API_SUCCESS) {
		SAMPLE_PRINT("CDC 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_CDC_NUM; i++) {
		g_CdcTaskInfoTbl[i].tskid		= 0;
		g_CdcTaskInfoTbl[i].mbxid		= 0;
		g_CdcTaskInfoTbl[i].pNotifyBuff	= g_notifyBuff[i];
		g_CdcTaskInfoTbl[i].pBulkInBuff	= AG903_VRAMMgrMalloc(g_mplid_app, BUFF_SIZE);
		g_CdcTaskInfoTbl[i].pContext	= NULL;
	}
	
	/*--------------------*/
	/* メールボックス生成 */
	/*--------------------*/
	/* パラメータ設定 */
    cmbx.mbxatr		= TA_TFIFO | TA_MFIFO;
    cmbx.maxmpri	= 1;
    cmbx.mprihd		= NULL;

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

	/* アプリケーションタスク用メールボックス生成×２ */
	for(i = 0; i < USBH_MAX_CLS_CDC_NUM; i++) {
		g_CdcTaskInfoTbl[i].mbxid = acre_mbx(&cmbx);
		if(g_CdcTaskInfoTbl[i].mbxid < 0) {
			/* エラー */
			return;
		}
	}

	/*----------------------*/
	/* 接続／切断タスク生成 */
	/*----------------------*/
	/* パラメータ設定 */
	ctsk.tskatr		= TA_HLNG | TA_ACT | TA_FPU;
	ctsk.exinf		= (void *)NULL;							/* TASK起動時のパラメータ(無し)			*/
	ctsk.task		= (FP)cdc_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_cdc_conn = acre_tsk(&ctsk);
	if(g_tskid_cdc_conn < 0) {
		/* エラー */
		return;
	}

	/*----------------------------*/
	/* アプリケーションタスク生成 */
	/*----------------------------*/
	/* パラメータ設定 */
	ctsk.task		= (FP)cdc_app_task;						/* TASKの起動アドレス					*/

	/* タスク生成 */
	for(i = 0; i < USBH_MAX_CLS_CDC_NUM; i++) {
		ctsk.exinf = (void *)&g_CdcTaskInfoTbl[i];			/* TASK起動時のパラメータ(テーブル)		*/
		g_CdcTaskInfoTbl[i].tskid = acre_tsk(&ctsk);
		if(g_CdcTaskInfoTbl[i].tskid < 0) {
			/* エラー */
			return;
		}
	}
}

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

	del_mbx(g_mbxid_cdc_conn);
	
	for(i = 0; i < USBH_MAX_CLS_CDC_NUM; i++) {
		del_mbx(g_CdcTaskInfoTbl[i].mbxid);
	}

	ter_tsk(g_tskid_cdc_conn);
	del_tsk(g_tskid_cdc_conn);
	
	for(i = 0; i < USBH_MAX_CLS_CDC_NUM; i++) {
		ter_tsk(g_CdcTaskInfoTbl[i].tskid);
		del_tsk(g_CdcTaskInfoTbl[i].tskid);
	}

	return;
}

/******************************************************************************
   Function：  cdc_appstop
   Argument：  なし
   Return  ：  AG903_ENONE	 正常終了
           ：  -AG903_EFAULT   異常終了
   Summary ：  CDCの転送停止を行います
   Caution ：  なし
******************************************************************************/
int32_t cdc_appstop(void)
{
	int32_t			retval=AG903_ENONE;
	uint32_t		loop;
	uhs_status_t	ret;
	
	for( loop=0 ; loop<USBH_MAX_CLS_CDC_NUM; loop++ ){
		if( NULL != g_CdcTaskInfoTbl[loop].pContext ) {
			ret = Usbh_Cdc_StopIntIN(g_CdcTaskInfoTbl[loop].pContext);
			if( (USBCDC_API_SUCCESS  != ret) &&
				(USBCDC_API_NOT_OPEN != ret) &&
				(USBCDC_API_ALREADY_STOP_INTIN != ret) ) {
				retval=-AG903_EFAULT;
			}
			ret = Usbh_Cdc_StopBulkIN(g_CdcTaskInfoTbl[loop].pContext);
			if( (USBCDC_API_SUCCESS  != ret) &&
				(USBCDC_API_NOT_OPEN != ret) &&
				(USBCDC_API_ALREADY_STOP_BULKIN != ret) ) {
				retval=-AG903_EFAULT;
			}
		}
	}
	
	return retval;
}

/******************************************************************************
   Function：  cdc_apprestart
   Argument：  なし
   Return  ：  AG903_ENONE	 正常終了
           ：  -AG903_EFAULT   異常終了
   Summary ：  CDCの転送再開を行います
   Caution ：  なし
******************************************************************************/
int32_t cdc_apprestart(void)
{
	int32_t			retval=AG903_ENONE;
	uint32_t		loop;
	uhs_status_t	ret;
	
	for( loop=0 ; loop<USBH_MAX_CLS_CDC_NUM; loop++ ){
		if( NULL != g_CdcTaskInfoTbl[loop].pContext ) {
			ret = Usbh_Cdc_RestartIntIN(g_CdcTaskInfoTbl[loop].pContext);
			if( (USBCDC_API_SUCCESS  != ret) &&
				(USBCDC_API_NOT_OPEN != ret) &&
				(USBCDC_API_ALREADY_START_INTIN != ret) ) {
				retval=-AG903_EFAULT;
			}
			ret = Usbh_Cdc_RestartBulkIN(g_CdcTaskInfoTbl[loop].pContext);
			if( (USBCDC_API_SUCCESS  != ret) &&
				(USBCDC_API_NOT_OPEN != ret) &&
				(USBCDC_API_ALREADY_START_BULKIN != ret) ) {
				retval=-AG903_EFAULT;
			}
		}
	}
	
	return retval;
}

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

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

			/* コンテキストを登録 */
			cdc_registTaskInfo(pContext);

			/* コールバック登録 */
			retval = Usbh_Cdc_RegistCallBack(pContext, cdc_notifyCallback, (uhs_ubit8_t)(16));
			if(retval != USBCDC_API_SUCCESS){
				/* バッファ解放 */
				AG903_VRAMMgrFree(g_mplid_app, pk_msg);
				return;
			}

			/* メッセージ送信 */
			ercd = snd_mbx(g_mbxid_cdc_conn, (T_MSG *)pk_msg);
			if(ercd != E_OK) {
				/* バッファ解放 */
				AG903_VRAMMgrFree(g_mplid_app, pk_msg);
				return;
			}
			break;
		
		/*--------*/
		/* 切断   */
		/*--------*/
		case CBID_CDC_DEVDETACH:

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

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

			/* メッセージ送信 */
			ercd = snd_mbx(g_mbxid_cdc_conn, (T_MSG *)pk_msg);
			if(ercd != E_OK) {
				/* バッファ解放 */
				AG903_VRAMMgrFree(g_mplid_app, pk_msg);
				return;
			}
			break;
		
		/*--------*/
		/* その他 */
		/*--------*/
		default:

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

/******************************************************************************
   Function：  cdc_notifyCallback
   Argument：  pNotifyInfo : 通知情報
               RetVal      : 処理結果
   Return  ：  なし
   Summary ：  CDCクラスドライバからのInterrupt転送Notification受信コールバック
               処理
   Caution ：  なし
******************************************************************************/
static void cdc_notifyCallback(PCDC_NOTIFY_INFO pNotifyInfo, uhs_status_t RetVal)
{
	CDC_APP_MSG_PKT*		pk_msg;							/* メッセージパケット				*/
	uhs_ubit8_t*			pBuff;							/* Notify用バッファアドレス			*/
	ER						ercd;
	uhs_ubit32_t			i;
	

	/* 処理結果確認 */
	if(RetVal != USBCDC_API_SUCCESS) {

		if(pNotifyInfo->CallbackNotifyType == CDC_NOTIFY_TYPE_BULK_IN) {
			//	接続／切断タスクに投げる
			pk_msg = AG903_VRAMMgrMalloc(g_mplid_app, sizeof(CDC_APP_MSG_PKT));  

			/* メッセージ種別 */
			pk_msg->msg = CDC_DETECT_BULKIN_ERR;

			/* メッセージ内容 */
			sample_memcpy((void *)&pk_msg->notify, (void *)pNotifyInfo, sizeof(CDC_NOTIFY_INFO));

			/* コンテキスト */
			pk_msg->pContext = pNotifyInfo->pContext;

			/* メッセージ送信 */
			ercd = snd_mbx( g_mbxid_cdc_conn , (T_MSG *)pk_msg);
			if(ercd != E_OK) {
				SAMPLE_PRINT("  snd_mbx err : ");
				SAMPLE_PRINT_NUM(ercd);
				SAMPLE_PRINT("\r\n");

				/* バッファ解放 */
				AG903_VRAMMgrFree(g_mplid_app, pk_msg);
			}
		}

		return;
	}

	/* メッセージバッファ取得 */
	pk_msg = AG903_VRAMMgrMalloc(g_mplid_app, sizeof(CDC_APP_MSG_PKT));
	/* メッセージ内容 */
	sample_memcpy((void *)&pk_msg->notify, (void *)pNotifyInfo, sizeof(CDC_NOTIFY_INFO));
	/* コンテキスト */
	pk_msg->pContext = pNotifyInfo->pContext;

	/* Interrupt データあり */
	if(pNotifyInfo->CallbackNotifyType == CDC_NOTIFY_TYPE_INTERRUPT_IN) {
		
		/* Interruptデータなし */
		if(pNotifyInfo->u32Length <= 0) {
			/* バッファ解放 */
			AG903_VRAMMgrFree(g_mplid_app, pk_msg);
			return;
		}
		
		/* バッファ取得 */
		pBuff = cdc_getNotifyBuff(pNotifyInfo->pContext);
		if(pBuff == NULL) {
			/* バッファ解放 */
			AG903_VRAMMgrFree(g_mplid_app, pk_msg);
			return;
		}

		sample_memset(pBuff, 0, NOTIFY_BUFF_SIZE);

		/* データ引き取り */
		for(i = 0; i < pNotifyInfo->u32Length; i++) {
			pBuff[i] = pNotifyInfo->pu8Buff[i];
		};
		/* バッファ置き換え */
		pk_msg->notify.pu8Buff = pBuff;
	}

	/* メッセージ送信 */
	ercd = snd_mbx(cdc_getMbxId(pNotifyInfo->pContext), (T_MSG *)pk_msg);
	if(ercd != E_OK) {
		/* バッファ解放 */
		AG903_VRAMMgrFree(g_mplid_app, pk_msg);
	}
}

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


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

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


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

/******************************************************************************
   Function：  cdc_getMbxId
   Argument：  pContext    : コールバックcontext
   Return  ：  メールボックスID
   Summary ：  登録してあるコンテキストに紐付けられたメールボックスIDを取得します
   Caution ：  なし
******************************************************************************/
static ID cdc_getMbxId(void* pContext)
{
	int						i;


	/* テーブルをサーチ */
	for(i = 0; i < USBH_MAX_CLS_CDC_NUM; i++) {
		/* コンテキストが一致 */
		if(g_CdcTaskInfoTbl[i].pContext == pContext) {
			/* メールボックスIDを返す */
			return g_CdcTaskInfoTbl[i].mbxid;
		}
	}
	
	/* エラー */
	return 0;
}

/******************************************************************************
   Function：  cdc_getNotifyBuff
   Argument：  pContext    : コールバックcontext
   Return  ：  Notify用バッファ
   Summary ：  登録してあるコンテキストに紐付けられたNotify用バッファを取得します
   Caution ：  なし
******************************************************************************/
static uhs_ubit8_t *cdc_getNotifyBuff(void* pContext)
{
	int						i;


	/* テーブルをサーチ */
	for(i = 0; i < USBH_MAX_CLS_CDC_NUM; i++) {
		/* コンテキストが一致 */
		if(g_CdcTaskInfoTbl[i].pContext == pContext) {
			/* Notify用バッファを返す */
			return g_CdcTaskInfoTbl[i].pNotifyBuff;
		}
	}
	
	/* エラー */
	return NULL;
}

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

	((void)context);

	while(1) {
		/* 接続／切断メッセージ待ち */
		rcv_mbx(g_mbxid_cdc_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;

		/* バッファ解放 */
		AG903_VRAMMgrFree(g_mplid_app, pk_msg);
		
		/*------*/
		/* 接続 */
		/*------*/
		if(msg == CBID_CDC_DEVATTACH) {

			/* クラスリクエスト発行 */
			cdc_attachDeviceInit(pContext);
			
			/* CDC データクラス転送許可 */
			retval = Usbh_Cdc_Open(pContext);
			if(retval != USBCDC_API_SUCCESS){
				continue;
			}
#if TEST_LOOPBAKC_DEVICE
			writeData.pu8Buff =  testdata;
			writeData.u32RequestLength = sizeof(testdata);
			retval = Usbh_Cdc_WriteData(pContext, &writeData);
			if(retval != USBCDC_API_SUCCESS){
				SAMPLE_PRINT("!!!!!!! CDC : Detect Write Error !!!!!\x0d\x0a");
			}
#endif
		}
		
		/*------*/
		/* 切断 */
		/*------*/
		else if(msg == CBID_CDC_DEVDETACH) {
			/* 処理なし */
		}

		/*---------*/
		/* ReStart */
		/*---------*/
		else if( msg == CDC_DETECT_BULKIN_ERR ){

			dly_tsk( 10 );

			//	Restart BULK_IN
			SAMPLE_PRINT("!!!!!!! CDC : Detect Status Error !!!!!\x0d\x0a");
			Usbh_Cdc_RestartBulkIN( pContext );
			SAMPLE_PRINT("!!!!!!! CDC : RESTART BULK_IN !!!!!\x0d\x0a ");
		}
	}
}

/******************************************************************************
   Function：  cdc_app_task
   Argument：  context     : タスク起動パラメータ(タスク情報)
   Return  ：  なし
   Summary ：  CDCアプリケーションタスク
   Caution ：  デバイス単位で使用
******************************************************************************/
static void cdc_app_task(void* context)
{
	CDC_TASK_INFO*			pCdcTaskInfo;					/* タスク情報						*/
	CDC_APP_MSG_PKT*		pk_msg;							/* メッセージパケット				*/
	void*					pContext;						/* コンテキスト						*/
	CDC_BUFF_INFO			readData;						/* 受信データ						*/
	CDC_BUFF_INFO			writeData;						/* 送信データ						*/
	uhs_ubit32_t			remainDataSize;					/* 受信データの残サイズ				*/
	uhs_ubit32_t			remainBuffSize;					/* 受信バッファの残サイズ			*/
	uhs_status_t			retval;
	uhs_ubit32_t			i, j;


	/* タスク情報取得 */
	pCdcTaskInfo = (CDC_TASK_INFO *)context;

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

		/* コールバック通知種別取得 */
		switch(pk_msg->notify.CallbackNotifyType) {
			
			/*----------*/
			/* バルクIN */
			/*----------*/
			case (CDC_NOTIFY_TYPE_BULK_IN):
				
				/* NULL レングス受信時は、メッセージ受信待ちへ */
				if(pk_msg->notify.u32Length == 0) {
					/* バッファ解放 */
					AG903_VRAMMgrFree(g_mplid_app, pk_msg);
					continue;
				}

				/* 受信バッファ設定 */
				readData.pu8Buff = pCdcTaskInfo->pBulkInBuff;
				/* 受信データ長 */
				remainDataSize = pk_msg->notify.u32Length;
				/* 受信データがバッファサイズ以下 */
				if(remainDataSize <= BUFF_SIZE) {
					readData.u32RequestLength = remainDataSize;
				}
				/* 受信データがバッファサイズより大きい */
				else {
					readData.u32RequestLength = BUFF_SIZE;
				}
				
				/*----------------------------------*/
				/* 全受信データをリードし、折り返し */
				/*----------------------------------*/
				while(1) {
					/* データリード */
					retval = Usbh_Cdc_ReadData(pContext, &readData, &remainBuffSize);
					if(retval != USBCDC_API_SUCCESS){
						break;
					}
				
#if TEST_LOOPBAKC_DEVICE
					SAMPLE_PRINT(" data : ");
					SAMPLE_PRINT_NUM((*readData.pu8Buff));
					SAMPLE_PRINT("\r\n");
					dly_tsk( 20 );
					if(0xFF == (*readData.pu8Buff)) {
						Usbh_Cdc_StopBulkIN(pContext);
						Usbh_Cdc_Close(pContext);
						break;
					}
					if((0x0A==(*readData.pu8Buff)) || (0x0D==(*readData.pu8Buff))) {
						sample_memset(readData.pu8Buff, (*readData.pu8Buff)+1, readData.u32ActualLength);
					}
#endif
					/* 送信バッファ設定 */
					writeData.pu8Buff = pCdcTaskInfo->pBulkInBuff;
					writeData.u32RequestLength = readData.u32ActualLength;

					/* データライト */
					retval = Usbh_Cdc_WriteData(pContext, &writeData);
					if(retval != USBCDC_API_SUCCESS){
						break;
					}
					
					/* 受信済みデータ更新 */
					remainDataSize -= readData.u32ActualLength;
					
					/* 残データあり */
					if(remainDataSize > 0) {
						/* 残データがバッファサイズ以下 */
						if(remainDataSize <= BUFF_SIZE) {
							readData.u32RequestLength = remainDataSize;
						}
						/* 残データがバッファサイズより大きい */
						else {
							readData.u32RequestLength = BUFF_SIZE;
						}
					}
					/* 残データなし */
					else {
						/* 折り返し終了 */
						break;
					}
				}
				break;
			
			/*----------------*/
			/* インタラプトIN */
			/*----------------*/
			case (CDC_NOTIFY_TYPE_INTERRUPT_IN):
				
				/* 通知種別取得 */
				switch(pk_msg->notify.ClassNotifyCode) {
					/* NetworkConnection */
					case CDC_NOTIFY_CODE_NETWORK_CONNECTION :
						SAMPLE_PRINT("  NetworkConnection : ");
						/* Disconnect */
						if(pk_msg->notify.u16wValue == NETWORK_CONNECTION_DISCON) {
							SAMPLE_PRINT("Disconnect\r\n");
						}
						else {
							SAMPLE_PRINT("Connect\r\n");
						}
						break;
					
					/* ResponseAvailable */
					case CDC_NOTIFY_CODE_RESPONSE_AVAILABLE :
						SAMPLE_PRINT("  ResponseAvailable\r\n");
						break;
					
					/* SerialState */
					case CDC_NOTIFY_CODE_SERIAL_STATE :
						SAMPLE_PRINT("  SerialState :");
						SAMPLE_PRINT("\r\n");
						break;

					/* その他 */
					default:
						SAMPLE_PRINT("  Notify Code :");
						SAMPLE_PRINT("\r\n");
						break;
				}
				
				/* 通知データ表示 */
				SAMPLE_PRINT("  --- recv length [");
				SAMPLE_PRINT_NUM(pk_msg->notify.u32Length);
				SAMPLE_PRINT("] byte");
				SAMPLE_PRINT("\r\n");
				for(i = 0, j = 0; i < pk_msg->notify.u32Length; i++, j++) {
					if(j == 16) {
						j = 0;
						SAMPLE_PRINT("\r\n");
					}
					if(j == 0) {
						SAMPLE_PRINT("      ");
					}
					else if(j == 8) {
						SAMPLE_PRINT(" ");
					}
					SAMPLE_PRINT_BYTE_HEX(pk_msg->notify.pu8Buff[i]);
					SAMPLE_PRINT(" ");
				}
				SAMPLE_PRINT("\r\n");

				break;
			
			/* その他 */
			default:
				/* Do Nothing */
				break;
		}
		/* バッファ解放 */
		AG903_VRAMMgrFree(g_mplid_app, pk_msg);
	}
}

/******************************************************************************
   Function：  cdc_attachDeviceInit
   Argument：  pContext    : 接続時のcontext
   Return  ：  なし
   Summary ：  CDCデバイス接続時のクラスリクエスト発行
   Caution ：  なし
******************************************************************************/
static uhs_status_t cdc_attachDeviceInit(void* pContext)
{
	CDC_LINE_CODING			tmpLineCoding;					/* シリアル通信設定情報				*/
	CDC_LINE_CODING			lineCoding;						/* シリアル通信設定情報				*/
	CDC_CONTROL_LINE_STATE	controlLineState;				/* RS232 制御信号線設定情報			*/
	uhs_status_t			retval;


	/* GetLineCoding */
	retval = Usbh_Cdc_GetLineCoding(pContext, &tmpLineCoding);
	if(retval != USBCDC_API_SUCCESS){
		return retval;
	}

	/* SetControlLineState */
	controlLineState.u16Data = 0;
	retval = Usbh_Cdc_SetControlLineState(pContext, &controlLineState);
	if(retval != USBCDC_API_SUCCESS){
		return retval;
	}

	/* GetLineCoding */
	retval = Usbh_Cdc_GetLineCoding(pContext, &tmpLineCoding);
	if(retval != USBCDC_API_SUCCESS){
		return retval;
	}
	/* GetLineCoding */
	retval = Usbh_Cdc_GetLineCoding(pContext, &tmpLineCoding);
	if(retval != USBCDC_API_SUCCESS){
		return retval;
	}
    
	/* SetLineCoding */
	lineCoding.u32BaudRate	= 0x0001C200;				/* 115200bps	*/
	lineCoding.StopBit		= LINE_CODING_STOPBIT_1;	/* 1 Stop Bit	*/
	lineCoding.Parity 		= LINE_CODING_PARITY_NONE;	/* パリティなし	*/
	lineCoding.DataBit		= LINE_CODING_DATABIT_7;	/* 7 Data Bit	*/
	retval = Usbh_Cdc_SetLineCoding(pContext, &lineCoding);
	if(retval != USBCDC_API_SUCCESS){
		return retval;
	}

	/* GetLineCoding */
	retval = Usbh_Cdc_GetLineCoding(pContext, &tmpLineCoding);
	if(retval != USBCDC_API_SUCCESS){
		return retval;
	}

	/* SetControlLineState */
	controlLineState.u16Data = (LINE_STATE_DTR | LINE_STATE_RTS);
	retval = Usbh_Cdc_SetControlLineState(pContext, &controlLineState);
	if(retval != UDI_OK){
		return retval;
	}

	/* SetLineCoding */
	lineCoding.u32BaudRate	= 0x0001C200;				/* 115200bps	*/
	lineCoding.StopBit		= LINE_CODING_STOPBIT_1;	/* 1 Stop Bit	*/
	lineCoding.Parity 		= LINE_CODING_PARITY_NONE;	/* パリティなし	*/
	lineCoding.DataBit		= LINE_CODING_DATABIT_8;	/* 8 Data Bit	*/
	retval = Usbh_Cdc_SetLineCoding(pContext, &lineCoding);
	if(retval != USBCDC_API_SUCCESS){
		return retval;
	}

	/* GetLineCoding */
	retval = Usbh_Cdc_GetLineCoding(pContext, &tmpLineCoding);
	if(retval != USBCDC_API_SUCCESS){
		return retval;
	}

	return retval;
}
