/** 
 * @brief           	uC3 OS Wrapper
 * @author		AXELL
 * @description	uC3 OS Wrapper Layer.
 * @note            	none
 * @history     2017_02_22  初版
 * @history     2017_10_26  Ver2.0
 * @history     2019_12_27  [SDK3.0] AG903_OSWprGetMplAlignでアライン調節前アドレスの保存方法を変更 (#2430)
 * @history     2019_12_27  [SDK3.0] AG903_OSWprInitMemoryでメモリ管理領域変更前にキャッシュ同期するように修正 (#2740)
 * @history     2019_12_27  [SDK3.0] AG903_OSWprGetMplの実装をAG903_OSWprGetMplAlignを呼び出すように変更 (#2741)
 * @history     2019_12_27  [SDK3.0] AG903_OSWprGetMplAlignでアライン引数のチェックを追加 (#2742)
 * @history     2019_12_27  [SDK3.0] AG903_OSWprCreMpfとAG903_OSWprCreMplにメモリ属性引数のチェックを追加 (#2743)
*/
/* DOM-IGNORE-BEGIN */
/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2019 AXELL CORPORATION, all rights reserved.
 */
/* DOM-IGNORE-END */

#include "AG903_errno.h"
#include "osw/oswrapper.h"
#include "uc3wrapper.h"

typedef struct _AG903_OSWMemMap {
	uintptr_t	ttbr0;		/* TTBR0変換テーブルアドレス */
	uint32_t	ttbr0sz;	/* TTBR0変換テーブルサイズ   */
	uintptr_t	ttbr1;		/* TTBR1変換テーブルアドレス */
	uint32_t	ttbr1sz;	/* TTBR1変換テーブルサイズ   */
	uintptr_t	l2tbl;		/* L2変換テーブルアドレス    */
	uint32_t	l2tblsz;	/* L2変換テーブルサイズ      */
	uintptr_t	ramtop;		/* 管理対象領域先頭アドレス  */
	uintptr_t	ramend;		/* 管理対象領域サイズ        */
} AG903_OSWMemMap;

typedef struct _AG903_OSWBlkInf {
	VP		topaddr;		/* メモリブロック先頭アドレス */
}AG903_OSWBlkInf;
/* AG903_OSWBlkInfを記憶するアドレスとアプリへ返すアライン調節済アドレスの両方が
 * get_mplが返すアドレスの境界と一致するようにするための調整
 */
#define BOUNDARY		8	/* get_mplが返すアドレスの境界 */
#define OSWBLKINF_SIZE	(((sizeof(AG903_OSWBlkInf) + BOUNDARY-1)/BOUNDARY)*BOUNDARY)

static volatile ER OswLastErr=0;
static AG903_OSWMemMap  OswMemMap = {0};	/* メモリマップ */
static uint16_t			OswIntDisableCnt[AG903_OSW_IRQ_NUM_MAX]={0};	/* 割込み禁止ネストカウント */
static uint8_t			OswIntPriorityTbl[AG903_OSW_IRQ_NUM_MAX]={0};	/* 割込み優先度テーブル */


static int32_t OSW_CreMpl(int32_t mplid, AG903_OSWCMpl* pk_cmpl, int32_t* getid);
static int32_t OSW_CreMpf(int32_t mpfid, AG903_OSWCMpf* pk_cmpf, int32_t* getid);
static void OSW_SetMemoryType(uintptr_t addr, uint32_t size, uint8_t type);
static void OSW_CleanDataCache(uintptr_t addr, uint32_t size);
static void OSW_InvalidDataCache(uintptr_t addr, uint32_t size);
static void OSW_SetLastErcd(ER ercd);
static int32_t OSW_ChgErcd(ER ercd);

extern void OSW_GetTtbInfo(uint32_t* ttbcr, uint32_t* ttbr0, uint32_t* ttbr1);


/**
 * @brief           VRAMマネージャ管理下のVRAM領域のRAM初期化
 * @param           ramtop [in] VRAM領域の開始アドレス
 * @param           ramsize [in] VRAM領域サイズ
 * @return          初期化結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @description     指定されたVRAM領域のアドレス変換テーブルをページサイズ(4KB)単位で
 *                  変更できるように実装して下さい。既にそうなっている場合は何もする必要はありません。
 * @note            この関数はMMUとアドレス変換テーブルが初期化された後一度だけ呼び出されます。
 * @note            ramsizeとramtopは1MB単位で指定して下さい。
 * @note            VRAM領域の一部は管理のために使用されます。
*/
int32_t AG903_OSWprInitMemory(uintptr_t ramtop, uint32_t ramsize)
{

	uint32_t*	wp;
	uint32_t	ttbcr, ttbr0, ttbr1;
	uint32_t	loop, cnt;
	uint32_t	l1offset;
	uint32_t	setval;
	uint8_t		ttbcrn;
	
	if( (0>=ramtop) || (0>=ramsize) ||
		(0xFFFFF&ramtop) || (0xFFFFF&ramsize) ) { 		/* 1MB単位以外はNG */
		return -AG903_EINVAL; 
	}
	OSW_GetTtbInfo(&ttbcr, &ttbr0, &ttbr1);
	ttbcrn   = (uint8_t)(ttbcr&0x03);
	OswMemMap.ttbr0   = ttbr0&(0xFFFFFF80<<(7-ttbcrn));	/* TTB0 [31:x] (x=14-TTBCR.N) */
	OswMemMap.ttbr0sz = (0x4000>>ttbcrn);				/* TTB0 Size (16KB Max) */
	OswMemMap.ttbr1   = ttbr1&0xFFFFFC00;				/* TTB1 [31:14] */
	OswMemMap.ttbr1sz = 0x4000;							/* TTB0 Size (16KB Fixed) */
	OswMemMap.l2tblsz = 0x1000*((ramsize>>22)+1);		/* L2 Table Size (4KB align) */
	OswMemMap.l2tbl   = (uintptr_t)(ramtop+ramsize-OswMemMap.l2tblsz);
	OswMemMap.ramtop  = ramtop;
	OswMemMap.ramend  = OswMemMap.l2tbl-1;
	
	/* 2ndレベルディスクリプタをVRAM領域内に生成 */
	setval = (ramtop | AG903_OSW_MEMTYPE_NORMAL_CACHE_ON);
	cnt    = (ramsize>>12);								/* 4KB(Small page) 単位 */
	wp     = (uint32_t*)OswMemMap.l2tbl;
	for(loop=0; loop<cnt; loop++) {
		(*wp)   = setval;
		setval += 0x1000;
		wp++;
	}
	
	/* 1stレベルディスクリプタ書き換え前にキャッシュの整合をとる */
	OSW_CleanDataCache(OswMemMap.ttbr0, OswMemMap.ttbr0sz);
	OSW_InvalidDataCache(OswMemMap.ttbr0, OswMemMap.ttbr0sz);
	OSW_CleanDataCache(OswMemMap.ttbr1, OswMemMap.ttbr1sz);
	OSW_InvalidDataCache(OswMemMap.ttbr1, OswMemMap.ttbr1sz);
	OSW_CleanDataCache(OswMemMap.l2tbl, OswMemMap.l2tblsz);
	OSW_InvalidDataCache(OswMemMap.l2tbl, OswMemMap.l2tblsz);

	/* 1stレベルディスクリプタが生成した2ndレベルディスクリプタを指すように変更 */
	setval   = (OswMemMap.l2tbl | 0x01);				/* Section */
	cnt      = (ramsize>>20);							/* 1MB単位 */
	l1offset = (ramtop>>18);							/* 1Entry = 4[byte] */
	if(OswMemMap.ttbr0sz > l1offset) {
		wp = (uint32_t*)(OswMemMap.ttbr0+l1offset);
	}
	else {
		wp = (uint32_t*)(OswMemMap.ttbr1+(l1offset-OswMemMap.ttbr0sz));
	}
	for(loop=0; loop<cnt; loop++) {
		(*wp)   = setval;
		setval += 0x400;
		if(++wp == (uint32_t*)(OswMemMap.ttbr0+OswMemMap.ttbr0sz)) {
			wp  = (uint32_t*)OswMemMap.ttbr1;
		}
	}
	/* アドレス変換テーブル更新により現在のTLBを一旦無効化 */
	_kernel_invalid_tlb();
	
	return AG903_ENONE;
}

/**
 * @brief           可変長メモリプールの生成
 * @param           mplid [in] メモリプールID番号
 * @param           pk_cmpl [in] 生成情報
 * @return          可変長メモリプールの生成結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @retval          -AG903_EFAULT  処理失敗(メモリ不足)
 * @retval          -AG903_EEXIST  既に生成済み
 * @retval          -AG903_ESYS    システムエラー(想定外エラー)
 * @description     可変長メモリプールの生成をします。
 *                  （μITRON4.0実装時、cre_mplを使用します。）
 * @note            メモリプールのアドレスを静的に指定する場合、そのアドレスは
 *                  ページサイズ(AG903_OSW_CACHE_PAGESIZE)にアラインされていなければなりません。
 *                  また他のメモリ領域と重複した場合の動作は不定です。
 * @note            メモリプールのアドレスをNULLとした場合は動的に割り当てられます。
 *                  このときアライン指定及びキャッシュ指定はできません。
 *
*/
int32_t AG903_OSWprCreMpl(int32_t mplid, AG903_OSWCMpl* pk_cmpl)
{
	int32_t retval;
	int32_t getid;
	
	retval = OSW_CreMpl(mplid, pk_cmpl, &getid);
	
	return retval;
}

/**
 * @brief           可変長メモリプールの生成（ID自動割付け）
 * @param           pk_cmpl [in] 生成情報
 * @return          可変長メモリプールの生成結果
 * @retval          メモリプールID番号  正常終了時
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @retval          -AG903_EFAULT  処理失敗(メモリ不足)
 * @retval          -AG903_ENOID   空ID無し
 * @retval          -AG903_ESYS    システムエラー(想定外エラー)
 * @description     可変長メモリプールの生成（ID自動割付け）をします。
 *                  （μITRON4.0実装時、acre_mplを使用します。）
 * @note            メモリプールのアドレスを静的に指定する場合、そのアドレスは
 *                  ページサイズ(AG903_OSW_CACHE_PAGESIZE)にアラインされていなければなりません。
 *                  また他のメモリ領域と重複した場合の動作は不定です。
 * @note 　　　　   メモリプールのアドレスをNULLとした場合は動的に割り当てられます。
*/
int32_t AG903_OSWprCreMplAuto(AG903_OSWCMpl* pk_cmpl)
{
	int32_t retval;
	int32_t getid = 0;
	
	retval = OSW_CreMpl(0, pk_cmpl, &getid);
	if( (AG903_ENONE==retval)&&(0<getid) ) {
		retval = getid;
	}
	
	return retval;
}

/**
 * @brief           可変長メモリプールの削除
 * @param           mplid [in] メモリプールID番号
 * @return          可変長メモリプールの削除結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @description     可変長メモリプールの削除をします
 *                  （uITRON4.0実装時、del_mplを使用します。）
 * @note            削除済みID番号に対する再削除は禁止します。
*/
int32_t AG903_OSWprDelMpl(int32_t mplid)
{
	ER ercd;
	int32_t retval;

	ercd = del_mpl((ID)mplid);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);

	return retval;
}

/**
 * @brief           可変長メモリブロックの獲得
 * @param           mplid [in] メモリプールID番号
 * @param           blksz [in] 獲得するブロックサイズ[byte]
 * @param           p_blk [out] 獲得したメモリブロックの先頭番地
 * @return          可変長メモリブロックの獲得結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @retval          -AG903_EFAULT  処理失敗(メモリ不足)
 * @retval          -AG903_ECANCELED  待ち強制解除等
 * @description     可変長メモリブロックの獲得をします。
 *                  （uITRON4.0実装時、pget_mplを使用します。）<p>
 * @note            エラーとなった場合p_blkの値は不定です。エラーはエラーコードで確認してください。
 * @note            可変長メモリブロックの確保では実際に内部的に確保されるメモリサイズは
 *                  管理等の領域を確保するために要求したサイズよりも少し大きくなります。
 * @note            獲得されるメモリブロックのアドレスは少なくとも4バイトアラインを保障して下さい。
*/
int32_t AG903_OSWprGetMpl(int32_t mplid, uint32_t blksz, void** p_blk)
{
	return AG903_OSWprGetMplAlign(mplid, BOUNDARY, blksz, p_blk);
}

/**
 * @brief           可変長メモリブロックの獲得（アライン指定）
 * @param           mplid [in] メモリプールID番号
 * @param           align [in] アラインサイズ[byte] (2の累乗で4以上)
 * @param           blksz [in] 獲得するブロックサイズ[byte]
 * @param           p_blk [out] 獲得したメモリブロックの先頭番地
 * @return          可変長メモリブロックの獲得結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @retval          -AG903_EFAULT  処理失敗(メモリ不足)
 * @retval          -AG903_ECANCELED  待ち強制解除等
 * @description     指定したアラインで可変長メモリブロックを獲得します。
 * @note            エラーとなった場合p_blkの値は不定です。エラーはエラーコードで確認してください。
 * @note            可変長メモリブロックの確保では実際に内部的に確保されるメモリサイズは
 *                  管理等の領域を確保するために要求したサイズよりも少し大きくなります。
 * @note            ポーティング例を以下に示します。<p>
 *   * ベースとしてuITRON4.0のget_mplを使用します。<p>
 *   * 実際に確保するメモリサイズはアプリが要求したサイズと次の領域の合計になります。<p>
 *   *   1 アライン調節のためのメモリ領域<p>
 *   *   2 アライン調節済メモリのアドレスから元々get_mplが返したアドレスを知るための
 *         AG903_OSWBlkInfo構造体を埋めておくための領域<p>
 *   * アプリへ返すアライン調節済アドレスは、get_mplで確保したアドレスに上記1と2のサイズを加え
 *     そこから要求されたアラインになるように少し戻ったアドレスになります。<p>
 *   * AG903_OSWBlkInfoはさらにそのアドレスから構造体のサイズだけ戻ったアドレスに記憶されます。<p>
 */
int32_t AG903_OSWprGetMplAlign(int32_t mplid, uint32_t align, uint32_t blksz, void** p_blk)
{
	ER ercd;
	VP getaddr;
	AG903_OSWBlkInf* blkinf;	/* アライン調節前アドレスの保存先 */
	int32_t		retval;
	uint32_t	extsize;		/* 調節前アドレスとアライン調節のためのサイズ */
	void		*blk;			/* アライン調節後アドレス */
	int			bit_count;
	int	 	 	ii;

	if( (NULL == p_blk) ||
		(0 == blksz) ) {
		return -AG903_EINVAL;
	}

	if (align < 4) {
		return -AG903_EINVAL;
	}
	/* alignが2の累乗であることを確認(alignのビット数で表現可能な範囲で) */
	bit_count = 0;
	for (ii = 0; ii < (int)sizeof(align)*8; ii++) {
		if (align>>ii & 0x1) {
			bit_count += 1;
		}
	}
	if (bit_count != 1) {
		return -AG903_EINVAL;
	}

	extsize = OSWBLKINF_SIZE + align-1;

	ercd = pget_mpl((ID)mplid, (UINT)(blksz+extsize), (VP*)&getaddr);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);
	
	if(AG903_ENONE == retval) {
		blk = (void *)(((uintptr_t)getaddr + extsize) & ~(align-1));
		*p_blk = blk;

		blkinf = (AG903_OSWBlkInf*)((uintptr_t)blk - OSWBLKINF_SIZE);
		blkinf->topaddr = getaddr;
	} else {
		*p_blk = NULL;
	}
	
	return retval;
}

/**
 * @brief           可変長メモリブロックの返却
 * @param           mplid [in] ID番号
 * @param           blk [in] 返却するメモリブロックの先頭番地
 * @return          可変長メモリブロックの返却結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @description     可変長メモリブロックの返却をします。
 *                  （uITRON4.0実装時、rel_mplを使用します。）
 * @note            返却済みID番号に対する再返却は禁止します。
*/
int32_t AG903_OSWprRelMpl(int32_t mplid, void* blk)
{
	ER ercd;
	AG903_OSWBlkInf* blkinf;
	int32_t retval;

	if(NULL == blk) {
		return -AG903_EINVAL;
	}
	
	blkinf = (AG903_OSWBlkInf*)((int8_t*)blk - OSWBLKINF_SIZE);
	blk = blkinf->topaddr;
	
	ercd = rel_mpl((ID)mplid, (VP)blk);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);

	return retval;
}

/**
 * @brief           可変長メモリブロックの状態参照
 * @param           mplid [in] ID番号
 * @param           pk_rmpl [out] 状態情報返却アドレス
 * @return          可変長メモリブロックの状態参照結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @description     可変長メモリブロックの状態参照をします。
 *                  （uITRON4.0実装時、ref_mplを使用します。）
 * @note            実際に確保可能なメモリサイズは取得するメモリのアライン調節や管理領域のため
 *                  この関数が返したサイズよりも少なくなります。<p>
 *                  少なくなるサイズの目安は AG903_OSWprGetMplAlign を参照して下さい。
*/
int32_t AG903_OSWprRefMpl(int32_t mplid, AG903_OSWRMpl* pk_rmpl)
{
	ER ercd;
	int32_t retval;
	T_RMPL rmpl;

	if(NULL == pk_rmpl) {
		return -AG903_EINVAL;
	}
	
	ercd = ref_mpl((ID)mplid, &rmpl);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);
	
	if(AG903_ENONE == retval) {
		pk_rmpl->wtskid = rmpl.wtskid;
		pk_rmpl->fmplsz = rmpl.fmplsz;
		pk_rmpl->fblksz = (rmpl.fblksz > OSWBLKINF_SIZE) ? (rmpl.fblksz - OSWBLKINF_SIZE) : 0;
	}

	return retval;
}

/**
 * @brief           固定長メモリプールの生成
 * @param           mpfid [in] メモリプールID番号
 * @param           pk_cmpf [in] 生成情報
 * @return          固定長メモリプールの生成結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @retval          -AG903_EFAULT  処理失敗(メモリ不足)
 * @retval          -AG903_EEXIST  既に生成済み
 * @retval          -AG903_ESYS    システムエラー(想定外エラー)
 * @description     固定長メモリプールの生成をします。
 *                  （uITRON4.0実装時、cre_mpfを使用します。）
 * @note            メモリプールのアドレスを静的に指定する場合、そのアドレスは
 *                  ページサイズ(AG903_OSW_CACHE_PAGESIZE)にアラインされていなければなりません。
 *                  また他のメモリ領域と重複した場合の動作は不定です。
 * @note            メモリプールのアドレスをNULLとした場合は動的に割り当てられます。
 *                  このときアライン指定及びキャッシュ指定はできません。
*/
int32_t AG903_OSWprCreMpf(int32_t mpfid, AG903_OSWCMpf* pk_cmpf)
{
	int32_t retval;
	int32_t getid;
	
	retval = OSW_CreMpf(mpfid, pk_cmpf, &getid);
	
	return retval;
}

/**
 * @brief           固定長メモリプールの生成（ID自動割付け）
 * @param           pk_cmpf [in] 生成情報
 * @return          固定長メモリプールの生成結果
 * @retval          メモリプールID番号
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @retval          -AG903_EFAULT  処理失敗(メモリ不足)
 * @retval          -AG903_ENOID  空ID無し
 * @retval          -AG903_ESYS    システムエラー(想定外エラー)
 * @description     固定長メモリプールの生成（ID自動割付け）を行います。
 *                  （uITRON4.0実装時、acre_mpfを使用します。）
*/
int32_t AG903_OSWprCreMpfAuto(AG903_OSWCMpf* pk_cmpf)
{
	int32_t retval;
	int32_t getid = 0;
	
	retval = OSW_CreMpf(0, pk_cmpf, &getid);
	if( (AG903_ENONE==retval)&&(0<getid) ) {
		retval = getid;
	}
	
	return retval;
}

/**
 * @brief           固定長メモリプールの削除
 * @param           mpfid [in] メモリプールID番号
 * @return          固定長メモリプールの削除結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @description     固定長メモリプールの削除をします。
 *                  （uITRON4.0実装時、del_mpfを使用します。）
*/
int32_t AG903_OSWprDelMpf(int32_t mpfid)
{
	ER ercd;
	int32_t retval;

	ercd = del_mpf((ID)mpfid);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);

	return retval;
}

/**
 * @brief           固定長メモリブロックの獲得（ポーリング）
 * @param           mpfid [in] メモリプールID番号
 * @param           p_blk [out] 獲得したメモリブロックの先頭番地
 * @return          固定長メモリブロックの獲得結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @retval          -AG903_EFAULT  処理失敗(メモリ不足)
 * @retval          -AG903_ECANCELED  待ち強制解除等
 * @description     固定長メモリブロックの獲得（ポーリング）をします。
 *                  （uITRON4.0実装時、pget_mpfを使用します。）
 * @note            エラーとなった場合p_blkの値は不定です。エラーはエラーコードで確認してください。
*/
int32_t AG903_OSWprGetMpf(int32_t mpfid, void** p_blk)
{
	ER ercd;
	int32_t retval;

	if(NULL == p_blk) {
		return -AG903_EINVAL;
	}

	ercd = pget_mpf((ID)mpfid, (VP*)p_blk);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);

	return retval;
}

/**
 * @brief           固定長メモリブロックの返却
 * @param           mpfid [in] メモリプールID番号
 * @param           blk [in] 返却するメモリブロックの先頭番地
 * @return          固定長メモリブロックの返却結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @description     固定長メモリブロックの返却をします。
 *                  （uITRON4.0実装時、rel_mpfを使用します。）
*/
int32_t AG903_OSWprRelMpf(int32_t mpfid, void* blk)
{
	ER ercd;
	int32_t retval;

	if(NULL == blk) {
		return -AG903_EINVAL;
	}

	ercd = rel_mpf((ID)mpfid, (VP)blk);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);

	return retval;
}

/**
 * @brief           固定長メモリブロックの状態参照
 * @param           mpfid [in] ID番号
 * @param           pk_rmpf [out] 状態を返すパケットへのポインタ
 * @return          固定長メモリブロックの状態参照結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @description     固定長メモリブロックの状態を参照します。
 *                  （uITRON4.0実装時、ref_mpfを使用します。）
*/
int32_t AG903_OSWprRefMpf(int32_t mpfid, AG903_OSWRMpf* pk_rmpf)
{
	ER ercd;
	int32_t retval;
	T_RMPF	rmpf;

	if(NULL == pk_rmpf) {
		return -AG903_EINVAL;
	}
	
	ercd = ref_mpf((ID)mpfid, &rmpf);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);
	
	if(AG903_ENONE == retval) {
		pk_rmpf->wtskid  = rmpf.wtskid;
		pk_rmpf->fblkcnt = rmpf.fblkcnt;
	}

	return retval;
}

/**
 * @brief           割込み初期化
 * @param           none
 * @return          割込み初期化の結果
 * @retval          AG903_ENONE    正常終了
 * @description     内部変数の初期化を行います。
 * @note            INTMgrの使用を開始する前に必ず実行してください。
 * @note            割り込み優先度変換テーブルの定義などを行います。
 * @note            OSWの関数では割込み優先度は0:High〜31:Lowであると規定しています。
 *                  優先度の初期値は標準ではAG903_OSW_DEFAULT_INTRPRI(20)です。
*/
int32_t AG903_OSWprIniInt(void)
{
	uint32_t	loop;

	for(loop=0; loop<AG903_OSW_IRQ_NUM_MAX; loop++) {
		OswIntPriorityTbl[loop] = AG903_OSW_DEFAULT_INTRPRI;	/* 優先度初期化 */
	}

	return AG903_ENONE;
}

/**
 * @brief           割込みサービスルーチンの生成（ID自動割付け）
 * @param           pk_cisr [in] 生成情報
 * @return          割込みサービスルーチンの生成結果
 * @retval          サービスルーチンID番号
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @retval          -AG903_EFAULT  処理失敗(メモリ不足)
 * @retval          -AG903_ENOID   空ID無し
 * @retval          -AG903_ESYS    システムエラー(想定外エラー)
 * @description     割込みサービスルーチンの生成（ID自動割付け）をします。
 *                  （uITRON4.0実装時、acre_isrを使用します。）
 * @note            pk_cisr->intnoで指定する割り込み番号は0-63の範囲のIRQ番号です。<p>
                    ポーティングの際にはこのIRQ番号をOSの対応する割り込み番号に変換してサービスコールを呼び出してください。
*/
int32_t AG903_OSWprCreIsrAuto(AG903_OSWCIsr* pk_cisr)
{
	ID isrid;
	int32_t retval;
	T_CISR	cisr;

	if(NULL == pk_cisr) {
		return -AG903_EINVAL;
	}
	if( (AG903_OSW_IRQ_NUM_MAX <= pk_cisr->intno) ||
		(AG903_OSW_HLNG    != pk_cisr->atr)   ||
		(NULL == pk_cisr->func) ) {
		return -AG903_EINVAL;
	}

	cisr.isratr = (ATR)(TA_HLNG|TA_FPU);
	cisr.exinf	= (VP_INT)pk_cisr->exinf;
	cisr.intno	= (INTNO)(AG903_OSW_IRQ_TOPID + pk_cisr->intno);	/* uC3はGICID I/F */
	cisr.isr	= (FP)pk_cisr->func;
	cisr.imask  = (IMASK)(OswIntPriorityTbl[pk_cisr->intno]<<3); 	/* OSWでの割り込み優先度(0-31)をOS仕様(0-255)に変換 */
	isrid = acre_isr(&cisr);
	OSW_SetLastErcd(isrid);
	if(0 > isrid) {
		retval = OSW_ChgErcd(isrid);
	}
	else if(0 == isrid) {
		retval = -AG903_ESYS;
	}
	else {
		retval = isrid;
	}

	return retval;
}

/**
 * @brief           割込みサービスルーチンの削除
 * @param           isrid [in] サービスルーチンID番号
 * @return          割込みサービスルーチンの削除結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @description     割込みサービスルーチンの削除をします。
 *                  （uITRON4.0実装時、del_isrを使用します。）
*/
int32_t AG903_OSWprDelIsr(int32_t isrid)
{
	ER ercd;
	int32_t retval;

	ercd = del_isr((ID)isrid);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);

	return retval;
}

/**
 * @brief           割込みサービスルーチンの状態参照
 * @param           isrid [in] サービスルーチンID番号
 * @param           pk_risr [out] 状態情報返却アドレス
 * @return          割込みサービスルーチンの状態参照結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @retval          -AG903_EPERM   不正使用(割り込みからのCALLなど)
 * @description     割込みサービスルーチンの状態を参照します。
 *                  （uITRON4.0実装時、ref_isrを使用します。）
*/
int32_t AG903_OSWprRefIsr(int32_t isrid, AG903_OSWRIsr* pk_risr)
{
	ER ercd;
	int32_t retval;
	T_RISR	risr;
	
	if(NULL == pk_risr) {
		return -AG903_EINVAL;
	}
	
	ercd = ref_isr((ID)isrid, &risr);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);
	
	if(AG903_ENONE == retval) {
		pk_risr->intno  = (risr.intno - AG903_OSW_IRQ_TOPID);	/* uC3はGICID I/F */
		pk_risr->isr    = (VP)risr.isr;
	}

	return retval;
}

/**
 * @brief           割込みの禁止
 * @param           intno [in] 割込み番号
 * @return          割込みの禁止結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @description     割込みの禁止をします。
 *                  （uITRON4.0実装時、dis_intを使用します。）<p>
 *                  dis_int コール後、内部カウンタをインクリメントします。（割込み禁止のネスト対応）
*/
int32_t AG903_OSWprDisInt(uint32_t intno)
{
	ER ercd;
	int32_t retval;
	
	if(AG903_OSW_IRQ_NUM_MAX <= intno) {
		return -AG903_EINVAL;
	}
	
	ercd = dis_int((INTNO)(AG903_OSW_IRQ_TOPID+intno));	/* uC3はGICID I/F */
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);

	OswIntDisableCnt[intno]++;
	
	return retval;
}

/**
 * @brief           割込みの許可
 * @param           intno [in] 割込み番号
 * @return          割込みの許可結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @description     割込みの許可をします。
 *                  （uITRON4.0実装時、ena_intを使用します。）<p>
 *                  内部カウンタをデクリメントします。（カウンタは AG903_OSWprDisInt でインクリメントされます）
 *                  デクリメント前またはデクリメント後のカウンタ値が0だった場合に ena_int をコールします。
*/
int32_t AG903_OSWprEnaInt(uint32_t intno)
{
	ER ercd;
	int32_t retval = AG903_ENONE;
	
	if(AG903_OSW_IRQ_NUM_MAX <= intno) {
		return -AG903_EINVAL;
	}

	if(0 < OswIntDisableCnt[intno]) {
		OswIntDisableCnt[intno]--;
	}
	if(0 >= OswIntDisableCnt[intno]) {
		ercd = ena_int((INTNO)(AG903_OSW_IRQ_TOPID+intno));	/* uC3はGICID I/F */
		OSW_SetLastErcd(ercd);
		retval = OSW_ChgErcd(ercd);
	}
	
	return retval;
}

/**
 * @brief           割込みマスクの変更
 * @param           imask [in] マスク値 (1以上：mask, 0：unmask)
 * @return          割込みマスクの変更結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @description     割込みマスクの変更をします。
 *                  （uITRON4.0実装時、chg_imsを使用します。）
*/
int32_t AG903_OSWprChgIms(uint32_t imask)
{
	ER ercd;
	int32_t retval;

	ercd = chg_ims((IMASK)imask);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);
	
	return retval;
}

/**
 * @brief           割込みマスクの参照
 * @param           imask [out] マスク値 (1以上：mask, 0：unmask)
 * @return          割込みマスクの参照結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @description     割込みマスクの参照をします。
 *                  （uITRON4.0実装時、get_imsを使用します。）
*/
int32_t AG903_OSWprGetIms(uint32_t* imask)
{
	ER ercd;
	int32_t retval;
	
	if(NULL == imask) {
		return -AG903_EINVAL;
	}

	*imask = 0;
	ercd = get_ims((IMASK*)imask);
	OSW_SetLastErcd(ercd);
	retval = OSW_ChgErcd(ercd);
	
	return retval;
}

/**
 * @brief           割込み優先度の設定
 * @param           intno [in] 割込み番号
 * @param           intpri [in] プライオリティ値(0:High?31:Low)
 * @return          割込み優先度の設定結果
 * @retval          AG903_ENONE    正常終了
 * @retval          -AG903_EINVAL  引数異常
 * @description     割込み優先度の設定をします。
 *                  内部のテーブル値を書き換えます。
 * @note            サービスルーチン登録前に実行して下さい。
 *                  サービスルーチン登録後、割込み許可時に優先度が反映されます。
*/
int32_t AG903_OSWprSetIpri (uint32_t intno, uint8_t intpri)
{
	if( (AG903_OSW_IRQ_NUM_MAX <= intno) ||
		(AG903_OSW_IRQ_PRI_MAX <= intpri) ) {
		return -AG903_EINVAL;
	}
	OswIntPriorityTbl[intno] = intpri;
	
	return AG903_ENONE;
}

/*
 * @brief	可変長メモリプール生成
 * @param	mplid [in] メモリプールID番号
 * @param	pk_cmpl [in] 生成情報
 * @param	getid [out] メモリプールID番号
 * @return	エラーコード
 * @note    メモリプールのアドレスを静的に指定する場合、そのアドレスは
 *          ページサイズ(AG903_OSW_CACHE_PAGESIZE)にアラインされていなければなりません。
 *          また他のメモリ領域と重複した場合の動作は不定です。
 * @note    メモリプールのアドレスをNULLとした場合は必要なメモリ領域が自動的に確保されます。
*/
static int32_t OSW_CreMpl(int32_t mplid, AG903_OSWCMpl* pk_cmpl, int32_t* getid)
{
	ER 		ercd;
	int32_t retval = AG903_ENONE;
	int32_t result;
	uint32_t size;
	T_CMPL	cmpl;
	
	if(NULL == pk_cmpl) {
		return -AG903_EINVAL;
	}
	if(AG903_OSW_ATRNUM <= pk_cmpl->mplatr) {
		return -AG903_EINVAL;
	}
	switch (pk_cmpl->memtype) {
	case AG903_OSW_NORMAL_CACHE_OFF:
	case AG903_OSW_NORMAL_CACHE_ON:
	case AG903_OSW_STRONGLY_ORDERED:
		break;
	default:
		return -AG903_EINVAL;
	}
	if(NULL != pk_cmpl->mpl) {
		if((uint32_t)pk_cmpl->mpl % AG903_OSW_CACHE_PAGESIZE) {
			return -AG903_EINVAL;
		}
		size = ((pk_cmpl->mplsz+AG903_OSW_CACHE_PAGESIZE-1) / AG903_OSW_CACHE_PAGESIZE);
		size *= AG903_OSW_CACHE_PAGESIZE;	/* キャッシュのページサイズ単位で確保 */
		if( (OswMemMap.ramtop > (uint32_t)pk_cmpl->mpl) ||
			(OswMemMap.ramend < ((uint32_t)pk_cmpl->mpl+size)) ) {
			return -AG903_EINVAL;
		}
	}
	else {
		/* NULL設定時はuC3のMPLMEMから確保される */
		/* この場合アライン指定及びキャッシュ指定は不可 */
		/* キャッシュはMPLMEM領域の設定に依存 */
		size = pk_cmpl->mplsz;
	}
	
	if(AG903_OSW_ATRPRI == pk_cmpl->mplatr) {
		cmpl.mplatr = (ATR)TA_TPRI;
	}
	else {
		cmpl.mplatr = (ATR)TA_TFIFO;
	}
	cmpl.mplsz  = (SIZE)size;
	cmpl.mpl    = (VP)pk_cmpl->mpl;
	cmpl.name   = NULL;
	if(0==mplid) {
		result = acre_mpl(&cmpl);
		OSW_SetLastErcd(result);
		if(0 > result) {
			retval = OSW_ChgErcd(result);
		}
		else if(0 == result) {
			retval = -AG903_ESYS;
		}
		else {
			(*getid) = result;
		}
	}
	else {
		ercd = cre_mpl((ID)mplid, &cmpl);
		OSW_SetLastErcd(ercd);
		if(E_OK == ercd) {
			(*getid) = mplid;
		}
		else {
			retval = OSW_ChgErcd(ercd);
		}
	}
	
	if(AG903_ENONE == retval) {
		if(NULL == pk_cmpl->mpl) {
			/* キャッシュ設定無効 */
		}
		else {
			OSW_SetMemoryType((uintptr_t)pk_cmpl->mpl, size, pk_cmpl->memtype);
		}
	}

	return retval;
}

/*
 * @brief	固定長メモリプールの生成
 * @param	mpfid [in] メモリプールID番号
 * @param	pk_cmpf [in] 生成情報
 * @param	getid [out] メモリプールID番号
 * @return	エラーコード
*/
static int32_t OSW_CreMpf(int32_t mpfid, AG903_OSWCMpf* pk_cmpf, int32_t* getid)
{
	ER 		ercd;
	int32_t retval = AG903_ENONE;
	int32_t result;
	uint32_t size = 0;
	T_CMPF	cmpf;
	
	if(NULL == pk_cmpf) {
		return -AG903_EINVAL;
	}
	if(AG903_OSW_ATRNUM <= pk_cmpf->mpfatr) {
		return -AG903_EINVAL;
	}
	switch (pk_cmpf->memtype) {
	case AG903_OSW_NORMAL_CACHE_OFF:
	case AG903_OSW_NORMAL_CACHE_ON:
	case AG903_OSW_STRONGLY_ORDERED:
		break;
	default:
		return -AG903_EINVAL;
	}
	if(NULL != pk_cmpf->mpf) {
		if((uint32_t)pk_cmpf->mpf % AG903_OSW_CACHE_PAGESIZE) {
			return -AG903_EINVAL;
		}
		size = (pk_cmpf->blksz * pk_cmpf->blkcnt);
		if(size % AG903_OSW_CACHE_PAGESIZE) {
			return -AG903_EINVAL;
		}
		if( (OswMemMap.ramtop > (uint32_t)pk_cmpf->mpf) ||
			(OswMemMap.ramend < ((uint32_t)pk_cmpf->mpf+size)) ) {
			return -AG903_EINVAL;
		}
	}
	else {
		/* NULL設定時はuC3のMPLMEMから確保される */
		/* この場合アライン指定及びキャッシュ指定は不可 */
		/* キャッシュはMPLMEM領域の設定に依存 */
	}
	
	if(AG903_OSW_ATRPRI == pk_cmpf->mpfatr) {
		cmpf.mpfatr = (ATR)TA_TPRI;
	}
	else {
		cmpf.mpfatr = (ATR)TA_TFIFO;
	}
	cmpf.blkcnt	= (UINT)pk_cmpf->blkcnt;
	cmpf.blksz	= (UINT)pk_cmpf->blksz;
	cmpf.mpf	= (VP)pk_cmpf->mpf;
	cmpf.name	= NULL;
	if(0==mpfid) {
		result = acre_mpf(&cmpf);
		OSW_SetLastErcd(result);
		if(0 > result) {
			retval = OSW_ChgErcd(result);
		}
		else if(0 == result) {
			retval = -AG903_ESYS;
		}
		else {
			(*getid) = result;
		}
	}
	else {
		ercd = cre_mpf((ID)mpfid, &cmpf);
		OSW_SetLastErcd(ercd);
		if(E_OK == ercd) {
			(*getid) = mpfid;
		}
		else {
			retval = OSW_ChgErcd(ercd);
		}
	}
	
	if(AG903_ENONE == retval) {
		if(NULL == pk_cmpf->mpf) {
			/* キャッシュ設定無効 */
		}
		else {
			OSW_SetMemoryType((uintptr_t)pk_cmpf->mpf, size, pk_cmpf->memtype);
		}
	}
	
	return retval;
}

/*
 * @brief	メモリタイプ設定.
 * @param	addr [in] 対象アドレス.
 * @param	size [in] サイズ.
 * @param	type [in] メモリタイプ.
 * @note    typeの指定はenum _AG903_OswMemType を使用して下さい。
 *          0または定義にない値を指定した場合はAG903_OSW_NORMAL_CACHE_OFF(=0)として扱われます。
*/
static void OSW_SetMemoryType(uintptr_t addr, uint32_t size, uint8_t type)
{
	uint32_t*	wp;
	uint32_t	loop, cnt;
	uint32_t	setval;
	uint32_t	offset;
	
	OSW_CleanDataCache(addr, size);		/* Clean Cache */
	OSW_InvalidDataCache(addr, size);	/* Invalid Cache */

	 switch(type) {
		case AG903_OSW_NORMAL_CACHE_ON:
			setval = (addr | AG903_OSW_MEMTYPE_NORMAL_CACHE_ON);
			break;
		case AG903_OSW_NORMAL_CACHE_OFF:
			setval = (addr | AG903_OSW_MEMTYPE_NORMAL_CACHE_OFF);
			break;
		case AG903_OSW_STRONGLY_ORDERED:
			setval = (addr | AG903_OSW_MEMTYPE_STRONGLY_ORDERED);
			break;
		default:
			/* 未定義の場合はNORMAL_CACHE_OFF */
			setval = (addr | AG903_OSW_MEMTYPE_NORMAL_CACHE_OFF);
			break;
	}
	offset = (addr-OswMemMap.ramtop)>>10;			/* 4KB(Small page)で1Entry */
	wp     = (uint32_t*)(OswMemMap.l2tbl+offset);
	cnt    = (size>>12); 							/* 4KB(Small page) 単位 */
	for(loop=0; loop<cnt; loop++) {					/* L2テーブル書換え */
		(*wp)   = setval;
		setval += 0x1000;
		wp++;
	}
	OSW_CleanDataCache(OswMemMap.l2tbl+offset, (cnt<<2));	/* Clean Cache */
	OSW_InvalidDataCache(OswMemMap.l2tbl+offset, (cnt<<2));	/* Invalid Cache */
	_kernel_invalid_tlb();									/* TLB invalid */

	return;
}

/*
 * @brief	データキャッシュクリーニング.
 * @param	addr [in] 対象アドレス.
 * @param	size [in] サイズ.
*/
static void OSW_CleanDataCache(uintptr_t addr, uint32_t size)
{
	_kernel_clean_data_cache((void*)addr, size);
	_kernel_clean_l2_cache((void*)addr, size);

	return;
}

/*
 * @brief	データキャッシュ無効化.
 * @param	addr [in] 対象アドレス.
 * @param	size [in] サイズ.
*/
static void OSW_InvalidDataCache(uintptr_t addr, uint32_t size)
{
	_kernel_invalid_data_cache((void*)addr, size);
	_kernel_invalid_l2_cache((void*)addr, size);

	return;
}

/*
 * @brief	ラストエラーの保存.
 * @param	ercd [in] uC3エラーコード.
*/
static void OSW_SetLastErcd(ER ercd)
{
	OswLastErr = ercd;
	return;
}

/*
 * @brief	エラーコードの変換.
 * @param	ercd [in] uC3エラーコード.
 * @return	変換後のエラーコード.
*/
static int32_t OSW_ChgErcd(ER ercd)
{
	int32_t retval;

	switch(ercd) {
		case E_OK:
			retval = AG903_ENONE;
			break;
		case E_SYS:
			retval = -AG903_ESYS;
			break;
		case E_NOSPT:
			retval = -AG903_ENOTSUP;
			break;
		case E_RSFN:
		case E_RSATR:
		case E_PAR:
		case E_ID:
		case E_NOEXS:
			retval = -AG903_EINVAL;
			break;
		case E_CTX:
		case E_MACV:
		case E_OACV:
		case E_ILUSE:
			retval = -AG903_EPERM;
			break;
		case E_NOMEM:
		case E_TMOUT:
			retval = -AG903_EFAULT;
			break;
		case E_NOID:
			retval = -AG903_ENOID;
			break;
		case E_OBJ:
			retval = -AG903_EEXIST;
			break;
		case E_QOVR:
			retval = -AG903_ENOBUFS;
			break;
		case E_RLWAI:
		case E_DLT:
		case E_CLS:
			retval = -AG903_ECANCELED;
			break;
		case E_BOVR:
			retval = -AG903_ENOBUFS;
			break;
		default:
			retval = -AG903_ESYS;
			break;
	}
	
	return retval;
}
