/*
 *  TOPPERS/ASP Kernel
 *      Toyohashi Open Platform for Embedded Real-Time Systems/
 *      Advanced Standard Profile Kernel
 * 
 *  Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory
 *                              Toyohashi Univ. of Technology, JAPAN
 *  Copyright (C) 2005-2010 by Embedded and Real-Time Systems Laboratory
 *              Graduate School of Information Science, Nagoya Univ., JAPAN
 * 
 *  上記著作権者は，以下の(1)〜(4)の条件を満たす場合に限り，本ソフトウェ
 *  ア（本ソフトウェアを改変したものを含む．以下同じ）を使用・複製・改
 *  変・再配布（以下，利用と呼ぶ）することを無償で許諾する．
 *  (1) 本ソフトウェアをソースコードの形で利用する場合には，上記の著作
 *      権表示，この利用条件および下記の無保証規定が，そのままの形でソー
 *      スコード中に含まれていること．
 *  (2) 本ソフトウェアを，ライブラリ形式など，他のソフトウェア開発に使
 *      用できる形で再配布する場合には，再配布に伴うドキュメント（利用
 *      者マニュアルなど）に，上記の著作権表示，この利用条件および下記
 *      の無保証規定を掲載すること．
 *  (3) 本ソフトウェアを，機器に組み込むなど，他のソフトウェア開発に使
 *      用できない形で再配布する場合には，次のいずれかの条件を満たすこ
 *      と．
 *    (a) 再配布に伴うドキュメント（利用者マニュアルなど）に，上記の著
 *        作権表示，この利用条件および下記の無保証規定を掲載すること．
 *    (b) 再配布の形態を，別に定める方法によって，TOPPERSプロジェクトに
 *        報告すること．
 *  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
 *      害からも，上記著作権者およびTOPPERSプロジェクトを免責すること．
 *      また，本ソフトウェアのユーザまたはエンドユーザからのいかなる理
 *      由に基づく請求からも，上記著作権者およびTOPPERSプロジェクトを
 *      免責すること．
 * 
 *  本ソフトウェアは，無保証で提供されているものである．上記著作権者お
 *  よびTOPPERSプロジェクトは，本ソフトウェアに関して，特定の使用目的
 *  に対する適合性も含めて，いかなる保証も行わない．また，本ソフトウェ
 *  アの利用により直接的または間接的に生じたいかなる損害に関しても，そ
 *  の責任を負わない．
 * 
 *  @(#) $Id: mempvar.c 1966 2010-11-20 07:23:56Z ertl-hiro $
 */

/*
 *		可変長メモリプール機能
 */

#include "kernel_impl.h"
#include "check.h"
#include "task.h"
#include "wait.h"
#include "mempvar.h"

/*
 *  トレースログマクロのデフォルト定義
 */
#ifndef LOG_ACRE_MPL_ENTER
#define LOG_ACRE_MPL_ENTER(pk_cmpl)
#endif /* LOG_ACRE_MPL_ENTER */

#ifndef LOG_ACRE_MPL_LEAVE
#define LOG_ACRE_MPL_LEAVE(ercd)
#endif /* LOG_ACRE_MPL_LEAVE */

#ifndef LOG_DEL_MPL_ENTER
#define LOG_DEL_MPL_ENTER(mplid)
#endif /* LOG_DEL_MPL_ENTER */

#ifndef LOG_DEL_MPL_LEAVE
#define LOG_DEL_MPL_LEAVE(ercd)
#endif /* LOG_DEL_MPL_LEAVE */

#ifndef LOG_GET_MPL_ENTER
#define LOG_GET_MPL_ENTER(mplid, blksz, p_blk)
#endif /* LOG_GET_MPL_ENTER */

#ifndef LOG_GET_MPL_LEAVE
#define LOG_GET_MPL_LEAVE(ercd, blk)
#endif /* LOG_GET_MPL_LEAVE */

#ifndef LOG_PGET_MPL_ENTER
#define LOG_PGET_MPL_ENTER(mplid, blksz, p_blk)
#endif /* LOG_PGET_MPL_ENTER */

#ifndef LOG_PGET_MPL_LEAVE
#define LOG_PGET_MPL_LEAVE(ercd, blk)
#endif /* LOG_PGET_MPL_LEAVE */

#ifndef LOG_TGET_MPL_ENTER
#define LOG_TGET_MPL_ENTER(mplid, blksz, p_blk, tmout)
#endif /* LOG_TGET_MPL_ENTER */

#ifndef LOG_TGET_MPL_LEAVE
#define LOG_TGET_MPL_LEAVE(ercd, blk)
#endif /* LOG_TGET_MPL_LEAVE */

#ifndef LOG_REL_MPL_ENTER
#define LOG_REL_MPL_ENTER(mplid, blk)
#endif /* LOG_REL_MPL_ENTER */

#ifndef LOG_REL_MPL_LEAVE
#define LOG_REL_MPL_LEAVE(ercd)
#endif /* LOG_REL_MPL_LEAVE */

#ifndef LOG_INI_MPL_ENTER
#define LOG_INI_MPL_ENTER(mplid)
#endif /* LOG_INI_MPL_ENTER */

#ifndef LOG_INI_MPL_LEAVE
#define LOG_INI_MPL_LEAVE(ercd)
#endif /* LOG_INI_MPL_LEAVE */

#ifndef LOG_REF_MPL_ENTER
#define LOG_REF_MPL_ENTER(mplid, pk_rmpl)
#endif /* LOG_REF_MPL_ENTER */

#ifndef LOG_REF_MPL_LEAVE
#define LOG_REF_MPL_LEAVE(ercd, pk_rmpl)
#endif /* LOG_REF_MPL_LEAVE */

/*
 *  可変長メモリプールの数
 */
#define tnum_mpl	((uint_t)(tmax_mplid - TMIN_MPLID + 1))
#define tnum_smpl	((uint_t)(tmax_smplid - TMIN_MPLID + 1))

/*
 *  可変長メモリプールIDから可変長メモリプール管理ブロックを取り出すた
 *  めのマクロ
 */
#define INDEX_MPL(mplid)	((uint_t)((mplid) - TMIN_MPLID))
#define get_mplcb(mplid)	(&(mplcb_table[INDEX_MPL(mplid)]))

/*
 *  特殊なインデックス値の定義
 */
#define INDEX_NULL		(~0U)		/* 空きブロックリストの最後 */
#define INDEX_ALLOC		(~1U)		/* 割当て済みのブロック */

#ifdef TOPPERS_mplini

/* 
 *  使用していない可変長メモリプール管理ブロックのリスト
 */
QUEUE	free_mplcb;

/* 
 *  可変長メモリプール機能の初期化
 */
void
initialize_mempvar(void)
{
	uint_t	i, j;
	MPLCB	*p_mplcb;
	MPLINIB	*p_mplinib;
	bool_t	stat;

	for (p_mplcb = mplcb_table, i = 0; i < tnum_smpl; p_mplcb++, i++) {
		queue_initialize(&(p_mplcb->wait_queue));
		p_mplcb->p_mplinib = &(mplinib_table[i]);
		p_mplinib = p_mplcb->p_mplinib;
		stat = kmem_setup(p_mplinib->mpl, p_mplinib->mplsz, &(p_mplcb->free_list));
		assert(stat);
	}
	queue_initialize(&free_mplcb);
	for (j = 0; i < tnum_mpl; p_mplcb++, i++, j++) {
		p_mplinib = &(amplinib_table[j]);
		p_mplinib->mplatr = TA_NOEXS;
		p_mplcb->p_mplinib = ((const MPLINIB *) p_mplinib);
		stat = kmem_setup(p_mplinib->mpl, p_mplinib->mplsz, &(p_mplcb->free_list));
		assert(stat);
		queue_insert_prev(&free_mplcb, &(p_mplcb->wait_queue));
	}
}

#endif /* TOPPERS_mplini */

/*
 *  可変長メモリプールの生成
 */
#ifdef TOPPERS_acre_mpl

ER_ID
acre_mpl(const T_CMPL *pk_cmpl)
{
	MPLCB	*p_mplcb;
	MPLINIB	*p_mplinib;
	ATR		mplatr;
	void	*mpl;
	MPLMB	*p_mplmb;
	ER		ercd;
	bool_t	stat;

	LOG_ACRE_MPL_ENTER(pk_cmpl);
	CHECK_TSKCTX_UNL();
	CHECK_RSATR(pk_cmpl->mplatr, TA_TPRI|TA_MPRI);
	CHECK_PAR(pk_cmpl->mplsz != 0);
	CHECK_ALIGN_MPL(pk_cmpl->mpl);
	mplatr = pk_cmpl->mplatr;
	mpl = pk_cmpl->mpl;
	p_mplmb = pk_cmpl->mplmb;

	t_lock_cpu();
	if (queue_empty(&free_mplcb)) {
		ercd = E_NOID;
	}
	else {
		if (mpl == NULL) {
			mpl = kernel_malloc(pk_cmpl->mplsz);
			mplatr |= TA_MEMALLOC;
		}
		if (mpl == NULL) {
			ercd = E_NOMEM;
		}
		else {
			if (p_mplmb == NULL) {
				p_mplmb = kernel_malloc(sizeof(MPLMB));
				mplatr |= TA_MBALLOC;
			}
			if (p_mplmb == NULL) {
				if ((mplatr & TA_MEMALLOC) != 0U) {
					kernel_free(mpl);
				}
				ercd = E_NOMEM;
			}
			else {
				p_mplcb = ((MPLCB *) queue_delete_next(&free_mplcb));
				p_mplinib = (MPLINIB *)(p_mplcb->p_mplinib);
				p_mplinib->mplatr = mplatr;
				p_mplinib->mpl = mpl;
				p_mplinib->mplsz = pk_cmpl->mplsz;
				p_mplinib->p_mplmb = p_mplmb;
				stat = kmem_setup(mpl, p_mplinib->mplsz, &(p_mplcb->free_list));
				if (stat) {
					queue_initialize(&(p_mplcb->wait_queue));
					ercd = MPLID(p_mplcb);
				} else {
					ercd = E_SYS;
				}
			}
		}
	}
	t_unlock_cpu();

  error_exit:
	LOG_ACRE_MPL_LEAVE(ercd);
	return(ercd);
}

#endif /* TOPPERS_acre_mpl */

/*
 *  可変長メモリプールの削除
 */
#ifdef TOPPERS_del_mpl

ER
del_mpl(ID mplid)
{
	MPLCB	*p_mplcb;
	MPLINIB	*p_mplinib;
	bool_t	dspreq;
	ER		ercd;

	LOG_DEL_MPL_ENTER(mplid);
	CHECK_TSKCTX_UNL();
	CHECK_MPLID(mplid);
	p_mplcb = get_mplcb(mplid);

	t_lock_cpu();
	if (p_mplcb->p_mplinib->mplatr == TA_NOEXS) {
		ercd = E_NOEXS;
	}
	else if (MPLID(p_mplcb) > tmax_smplid) {
		dspreq = init_wait_queue(&(p_mplcb->wait_queue));
		p_mplinib = (MPLINIB *)(p_mplcb->p_mplinib);
		if ((p_mplinib->mplatr & TA_MEMALLOC) != 0U) {
			kernel_free(p_mplinib->mpl);
		}
		if ((p_mplinib->mplatr & TA_MBALLOC) != 0U) {
			kernel_free(p_mplinib->p_mplmb);
		}
		p_mplinib->mplatr = TA_NOEXS;
		queue_insert_prev(&free_mplcb, &(p_mplcb->wait_queue));
		if (dspreq) {
			dispatch();
		}
		ercd = E_OK;
	}
	else {
		ercd = E_OBJ;
	}
	t_unlock_cpu();

  error_exit:
	LOG_DEL_MPL_LEAVE(ercd);
	return(ercd);
}

#endif /* TOPPERS_del_mpl */

/*
 *  可変長メモリブロックの獲得
 */
#ifdef TOPPERS_get_mpl

ER
get_mpl(ID mplid, uint_t blksz, void **p_blk)
{
	MPLCB	*p_mplcb;
	WINFO_MPL winfo_mpl;
	ER		ercd;

	LOG_GET_MPL_ENTER(mplid, blksz, p_blk);
	CHECK_DISPATCH();
	CHECK_MPLID(mplid);
	p_mplcb = get_mplcb(mplid);

	t_lock_cpu();
	if (kmem_allocate(p_mplcb->free_list, blksz, p_blk)) {
		ercd = E_OK;
	}
	else {
		winfo_mpl.blksz = blksz;
		p_runtsk->tstat = (TS_WAITING | TS_WAIT_MPL);
		wobj_make_wait((WOBJCB *) p_mplcb, (WINFO_WOBJ *) &winfo_mpl);
		dispatch();
		ercd = winfo_mpl.winfo.wercd;
		if (ercd == E_OK) {
			*p_blk = winfo_mpl.blk;
		}
	}
	t_unlock_cpu();

  error_exit:
	LOG_GET_MPL_LEAVE(ercd, *p_blk);
	return(ercd);
}

#endif /* TOPPERS_get_mpl */

/*
 *  可変長メモリブロックの獲得（ポーリング）
 */
#ifdef TOPPERS_pget_mpl

ER
pget_mpl(ID mplid, uint_t blksz, void **p_blk)
{
	MPLCB	*p_mplcb;
	ER		ercd;

	LOG_PGET_MPL_ENTER(mplid, blksz, p_blk);
	CHECK_TSKCTX_UNL();
	CHECK_MPLID(mplid);
	p_mplcb = get_mplcb(mplid);

	t_lock_cpu();
	if (kmem_allocate(p_mplcb->free_list, blksz, p_blk)) {
		ercd = E_OK;
	}
	else {
		ercd = E_TMOUT;
	}
	t_unlock_cpu();

  error_exit:
	LOG_PGET_MPL_LEAVE(ercd, *p_blk);
	return(ercd);
}

#endif /* TOPPERS_pget_mpl */

/*
 *  可変長メモリブロックの獲得（タイムアウトあり）
 */
#ifdef TOPPERS_tget_mpl

ER
tget_mpl(ID mplid, uint_t blksz, void **p_blk, TMO tmout)
{
	MPLCB	*p_mplcb;
	WINFO_MPL winfo_mpl;
	TMEVTB	tmevtb;
	ER		ercd;

	LOG_TGET_MPL_ENTER(mplid, blksz, p_blk, tmout);
	CHECK_DISPATCH();
	CHECK_MPLID(mplid);
	CHECK_TMOUT(tmout);
	p_mplcb = get_mplcb(mplid);

	t_lock_cpu();
	if (kmem_allocate(p_mplcb->free_list, blksz, p_blk)) {
		ercd = E_OK;
	}
	else if (tmout == TMO_POL) {
		ercd = E_TMOUT;
	}
	else {
		winfo_mpl.blksz = blksz;
		p_runtsk->tstat = (TS_WAITING | TS_WAIT_MPL);
		wobj_make_wait_tmout((WOBJCB *) p_mplcb, (WINFO_WOBJ *) &winfo_mpl,
														&tmevtb, tmout);
		dispatch();
		ercd = winfo_mpl.winfo.wercd;
		if (ercd == E_OK) {
			*p_blk = winfo_mpl.blk;
		}
	}
	t_unlock_cpu();

  error_exit:
	LOG_TGET_MPL_LEAVE(ercd, *p_blk);
	return(ercd);
}

#endif /* TOPPERS_tget_mpl */

/*
 *  可変長メモリブロックの返却
 */
#ifdef TOPPERS_rel_mpl

ER
rel_mpl(ID mplid, void *blk)
{
	MPLCB	*p_mplcb;
	TCB		*p_tcb;
	ER	ercd = E_OK;
	bool_t	stat;

	LOG_REL_MPL_ENTER(mplid, blk);
	CHECK_TSKCTX_UNL();
	CHECK_MPLID(mplid);
	p_mplcb = get_mplcb(mplid);

	t_lock_cpu();
	if ((uint8_t *)(p_mplcb->free_list) + p_mplcb->p_mplinib->mplsz <= (uint8_t *)blk ||
		(uint8_t *)blk < (uint8_t *)(p_mplcb->free_list)) {
		ercd = E_PAR;
	}
	else {
		stat = kmem_release(p_mplcb->free_list, blk);
		if (!stat) {
			ercd = E_PAR;
		} else while (!queue_empty(&(p_mplcb->wait_queue)))  {
			void *new_blk;
			uint_t blksz;

			/*
			 *  待ち行列の最初にあるタスクが要求している
			 *  可変長メモリのサイズを求める。
			 */
			p_tcb = (TCB *)(p_mplcb->wait_queue.p_next);
			blksz = ((WINFO_MPL *)(p_tcb->p_winfo))->blksz;

			if (!kmem_allocate(p_mplcb->free_list, blksz, &new_blk)) {
				break;
			}

			/*
			 *  取得できたら、待ちを解除してdispatch
			 */
			queue_delete_next(&(p_mplcb->wait_queue));
		((WINFO_MPL *)(p_tcb->p_winfo))->blk = new_blk;
		if (wait_complete(p_tcb)) {
			dispatch();
		}

		/*
		 *  ロックしているタスクがどうなろうとも、
		 *  今回の呼出しには関係なく、E_OKである。
		 */
		ercd = E_OK;
	}
}
	t_unlock_cpu();

  error_exit:
	LOG_REL_MPL_LEAVE(ercd);
	return(ercd);
}

#endif /* TOPPERS_rel_mpl */

/*
 *  可変長メモリプールの状態参照
 */
#ifdef TOPPERS_ref_mpl

ER
ref_mpl(ID mplid, T_RMPL *pk_rmpl)
{
	MPLCB	*p_mplcb;
	ER		ercd;
	bool_t	stat;

	LOG_REF_MPL_ENTER(mplid, pk_rmpl);
	CHECK_TSKCTX_UNL();
	CHECK_MPLID(mplid);
	p_mplcb = get_mplcb(mplid);

	t_lock_cpu();
	if (!(queue_empty(&(p_mplcb->wait_queue)))) {
		TCB	*p_tcb;
		p_tcb = (TCB *)queue_peek_next(&(p_mplcb->wait_queue));
		pk_rmpl->wtskid = TSKID(p_tcb);
	}
	else {
		pk_rmpl->wtskid = TSK_NONE;
	}

	stat = kmem_status(p_mplcb->free_list, &(pk_rmpl->fmplsz), &(pk_rmpl->fblksz));
	if (!stat) {
		/*
		 *  free_list が壊れている
		 */
		ercd = E_SYS;
	} else {
		ercd = E_OK;
	}

	t_unlock_cpu();

  error_exit:
	LOG_REF_MPL_LEAVE(ercd, pk_rmpl);
	return(ercd);
}

#endif /* TOPPERS_ref_mpl */
