/*
 *  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-2019 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$
 */

/*
 *		システム状態管理機能
 */

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

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

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

#ifndef LOG_GET_TID_ENTER
#define LOG_GET_TID_ENTER(p_tskid)
#endif /* LOG_GET_TID_ENTER */

#ifndef LOG_GET_TID_LEAVE
#define LOG_GET_TID_LEAVE(ercd, p_tskid)
#endif /* LOG_GET_TID_LEAVE */

#ifndef LOG_GET_LOD_ENTER
#define LOG_GET_LOD_ENTER(tskpri, p_load)
#endif /* LOG_GET_LOD_ENTER */

#ifndef LOG_GET_LOD_LEAVE
#define LOG_GET_LOD_LEAVE(ercd, p_load)
#endif /* LOG_GET_LOD_LEAVE */

#ifndef LOG_GET_NTH_ENTER
#define LOG_GET_NTH_ENTER(tskpri, nth, p_tskid)
#endif /* LOG_GET_NTH_ENTER */

#ifndef LOG_GET_NTH_LEAVE
#define LOG_GET_NTH_LEAVE(ercd, p_tskid)
#endif /* LOG_GET_NTH_LEAVE */

#ifndef LOG_LOC_CPU_ENTER
#define LOG_LOC_CPU_ENTER()
#endif /* LOG_LOC_CPU_ENTER */

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

#ifndef LOG_UNL_CPU_ENTER
#define LOG_UNL_CPU_ENTER()
#endif /* LOG_UNL_CPU_ENTER */

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

#ifndef LOG_DIS_DSP_ENTER
#define LOG_DIS_DSP_ENTER()
#endif /* LOG_DIS_DSP_ENTER */

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

#ifndef LOG_ENA_DSP_ENTER
#define LOG_ENA_DSP_ENTER()
#endif /* LOG_ENA_DSP_ENTER */

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

#ifndef LOG_SNS_CTX_ENTER
#define LOG_SNS_CTX_ENTER()
#endif /* LOG_SNS_CTX_ENTER */

#ifndef LOG_SNS_CTX_LEAVE
#define LOG_SNS_CTX_LEAVE(state)
#endif /* LOG_SNS_CTX_LEAVE */

#ifndef LOG_SNS_LOC_ENTER
#define LOG_SNS_LOC_ENTER()
#endif /* LOG_SNS_LOC_ENTER */

#ifndef LOG_SNS_LOC_LEAVE
#define LOG_SNS_LOC_LEAVE(state)
#endif /* LOG_SNS_LOC_LEAVE */

#ifndef LOG_SNS_DSP_ENTER
#define LOG_SNS_DSP_ENTER()
#endif /* LOG_SNS_DSP_ENTER */

#ifndef LOG_SNS_DSP_LEAVE
#define LOG_SNS_DSP_LEAVE(state)
#endif /* LOG_SNS_DSP_LEAVE */

#ifndef LOG_SNS_DPN_ENTER
#define LOG_SNS_DPN_ENTER()
#endif /* LOG_SNS_DPN_ENTER */

#ifndef LOG_SNS_DPN_LEAVE
#define LOG_SNS_DPN_LEAVE(state)
#endif /* LOG_SNS_DPN_LEAVE */

#ifndef LOG_SNS_KER_ENTER
#define LOG_SNS_KER_ENTER()
#endif /* LOG_SNS_KER_ENTER */

#ifndef LOG_SNS_KER_LEAVE
#define LOG_SNS_KER_LEAVE(state)
#endif /* LOG_SNS_KER_LEAVE */

/*
 *  タスクの優先順位の回転［NGKI3548］
 */
#ifdef TOPPERS_rot_rdq

ER
rot_rdq(PRI tskpri)
{
	uint_t	pri;
	ER		ercd;

	LOG_ROT_RDQ_ENTER(tskpri);
	CHECK_UNL();								/*［NGKI2684］*/
	if (tskpri == TPRI_SELF && !sense_context()) {
		pri = p_runtsk->bpriority;				/*［NGKI2689］*/
	}
	else {
		CHECK_PAR(VALID_TPRI(tskpri));			/*［NGKI2685］*/
		pri = INT_PRIORITY(tskpri);
	}

	lock_cpu();
	rotate_ready_queue(&(ready_queue[pri]));
	if (p_runtsk != p_schedtsk) {
		if (!sense_context()) {
			dispatch();
		}
		else {
			request_dispatch_retint();
		}
	}
	ercd = E_OK;
	unlock_cpu();

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

#endif /* TOPPERS_rot_rdq */

/*
 *  実行状態のタスクIDの参照［NGKI3550］
 */
#ifdef TOPPERS_get_tid

ER
get_tid(ID *p_tskid)
{
	bool_t lock;
	ER		ercd;

	LOG_GET_TID_ENTER(p_tskid);
	//CHECK_UNL();								/*［NGKI2707］*/
	lock = sense_lock();

	if (!lock)
		lock_cpu();
	*p_tskid = (p_runtsk == NULL) ? TSK_NONE : TSKID(p_runtsk);
	ercd = E_OK;								/*［NGKI2710］［NGKI2709］*/
	if (!lock)
		unlock_cpu();

  error_exit:
	LOG_GET_TID_LEAVE(ercd, p_tskid);
	return(ercd);
}

#endif /* TOPPERS_get_tid */

/*
 *  実行できるタスクの数の参照［NGKI3623］
 */
#ifdef TOPPERS_get_lod

ER
get_lod(PRI tskpri, uint_t *p_load)
{
	uint_t	pri, load;
	QUEUE	*p_queue, *p_entry;
	ER		ercd;

	LOG_GET_LOD_ENTER(p_tskid, p_load);
	CHECK_TSKCTX_UNL();							/*［NGKI3624］［NGKI3625］*/
	if (tskpri == TPRI_SELF) {
		pri = p_runtsk->bpriority;				/*［NGKI3631］*/
	}
	else {
		CHECK_PAR(VALID_TPRI(tskpri));			/*［NGKI3626］*/
		pri = INT_PRIORITY(tskpri);
	}
	p_queue = &(ready_queue[pri]);

	lock_cpu();
	load = 0U;
	for (p_entry = p_queue->p_next; p_entry != p_queue;
										p_entry = p_entry->p_next) {
		load += 1U;
	}
	*p_load = load;
	ercd = E_OK;
	unlock_cpu();

  error_exit:
	LOG_GET_LOD_LEAVE(ercd, p_load);
	return(ercd);
}

#endif /* TOPPERS_get_lod */

/*
 *  指定した優先順位のタスクIDの参照［NGKI3641］
 */
#ifdef TOPPERS_get_nth

ER
get_nth(PRI tskpri, uint_t nth, ID *p_tskid)
{
	uint_t	pri;
	QUEUE	*p_queue, *p_entry;
	ID		tskid;
	ER		ercd;

	LOG_GET_NTH_ENTER(p_tskid, nth, p_tskid);
	CHECK_TSKCTX_UNL();							/*［NGKI3642］［NGKI3643］*/
	if (tskpri == TPRI_SELF) {
		pri = p_runtsk->bpriority;				/*［NGKI3650］*/
	}
	else {
		CHECK_PAR(VALID_TPRI(tskpri));			/*［NGKI3644］*/
		pri = INT_PRIORITY(tskpri);
	}
	p_queue = &(ready_queue[pri]);

	lock_cpu();
	tskid = TSK_NONE;
	for (p_entry = p_queue->p_next; p_entry != p_queue;
										p_entry = p_entry->p_next) {
		if (nth == 0U) {
			tskid = TSKID((TCB *) p_entry);
			break;
		}
		nth -= 1U;
	}
	*p_tskid = tskid;
	ercd = E_OK;
	unlock_cpu();

  error_exit:
	LOG_GET_NTH_LEAVE(ercd, p_tskid);
	return(ercd);
}

#endif /* TOPPERS_get_nth */

/*
 *  CPUロック状態への遷移［NGKI3538］
 */
#ifdef TOPPERS_loc_cpu

ER
loc_cpu(void)
{
	ER		ercd;

	LOG_LOC_CPU_ENTER();

	if (!sense_lock()) {						/*［NGKI2731］*/
		lock_cpu();								/*［NGKI2730］*/
	}
	ercd = E_OK;

	LOG_LOC_CPU_LEAVE(ercd);
	return(ercd);
}

#endif /* TOPPERS_loc_cpu */

/*
 *  CPUロック状態の解除［NGKI3539］
 *
 *  CPUロック中は，ディスパッチが必要となるサービスコールを呼び出すこ
 *  とはできないため，CPUロック状態の解除時にディスパッチャを起動する
 *  必要はない．
 */
#ifdef TOPPERS_unl_cpu

ER
unl_cpu(void)
{
	ER		ercd;

	LOG_UNL_CPU_ENTER();

	if (sense_lock()) {							/*［NGKI2738］*/
		unlock_cpu();							/*［NGKI2737］*/
	}
	ercd = E_OK;

	LOG_UNL_CPU_LEAVE(ercd);
	return(ercd);
}

#endif /* TOPPERS_unl_cpu */

/*
 *  ディスパッチの禁止［NGKI2740］
 */
#ifdef TOPPERS_dis_dsp

ER
dis_dsp(void)
{
	ER		ercd;

	LOG_DIS_DSP_ENTER();
	CHECK_TSKCTX_UNL();							/*［NGKI2741］［NGKI2742］*/

	lock_cpu();
	enadsp = false;
	dspflg = false;
	ercd = E_OK;
	unlock_cpu();

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

#endif /* TOPPERS_dis_dsp */

/*
 *  ディスパッチの許可［NGKI2746］
 */
#ifdef TOPPERS_ena_dsp

ER
ena_dsp(void)
{
	ER		ercd;

	LOG_ENA_DSP_ENTER();
	CHECK_TSKCTX_UNL();							/*［NGKI2747］［NGKI2748］*/

	lock_cpu();
	enadsp = true;
	if (t_get_ipm() == TIPM_ENAALL) {
		set_dspflg();
		if (p_runtsk->raster && p_runtsk->enater) {
			task_terminate(p_runtsk);
			exit_and_dispatch();
			ercd = E_SYS;
		}
		else {
			if (p_runtsk != p_schedtsk) {
				dispatch();
			}
			ercd = E_OK;
		}
	}
	else {
		ercd = E_OK;
	}
	unlock_cpu();

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

#endif /* TOPPERS_ena_dsp */

/*
 *  コンテキストの参照［NGKI2752］
 */
#ifdef TOPPERS_sns_ctx

bool_t
sns_ctx(void)
{
	bool_t	state;

	LOG_SNS_CTX_ENTER();
	state = sense_context() ? true : false;
	LOG_SNS_CTX_LEAVE(state);
	return(state);
}

#endif /* TOPPERS_sns_ctx */

/*
 *  CPUロック状態の参照［NGKI2754］
 */
#ifdef TOPPERS_sns_loc

bool_t
sns_loc(void)
{
	bool_t	state;

	LOG_SNS_LOC_ENTER();
	state = sense_lock() ? true : false;
	LOG_SNS_LOC_LEAVE(state);
	return(state);
}

#endif /* TOPPERS_sns_loc */

/*
 *  ディスパッチ禁止状態の参照［NGKI2756］
 */
#ifdef TOPPERS_sns_dsp

bool_t
sns_dsp(void)
{
	bool_t	state;

	LOG_SNS_DSP_ENTER();
	state = !enadsp;
	LOG_SNS_DSP_LEAVE(state);
	return(state);
}

#endif /* TOPPERS_sns_dsp */

/*
 *  ディスパッチ保留状態の参照［NGKI2758］
 */
#ifdef TOPPERS_sns_dpn

bool_t
sns_dpn(void)
{
	bool_t	state;

	LOG_SNS_DPN_ENTER();
	state = (sense_context() || sense_lock() || !dspflg) ? true : false;
	LOG_SNS_DPN_LEAVE(state);
	return(state);
}

#endif /* TOPPERS_sns_dpn */

/*
 *  カーネル非動作状態の参照［NGKI2760］
 */
#ifdef TOPPERS_sns_ker

bool_t
sns_ker(void)
{
	bool_t	state;

	LOG_SNS_KER_ENTER();
	state = kerflg ? false : true;
	LOG_SNS_KER_LEAVE(state);
	return(state);
}

#endif /* TOPPERS_sns_ker */
