/*
 *  TOPPERS/FI4 Kernel
 *      Toyohashi Open Platform for Embedded Real-Time Systems/
 *      Fullset uItron4 Kernel
 * 
 *  Copyright (C) 2003-2004 by Monami software Limited Partnership, JAPAN
 * 
 *  上記著作権者は，以下の (1)〜(4) の条件か，Free Software Foundation 
 *  によって公表されている GNU General Public License の Version 2 に記
 *  述されている条件を満たす場合に限り，本ソフトウェア（本ソフトウェア
 *  を改変したものを含む．以下同じ）を使用・複製・改変・再配布（以下，
 *  利用と呼ぶ）することを無償で許諾する．
 *  (1) 本ソフトウェアをソースコードの形で利用する場合には，上記の著作
 *      権表示，この利用条件および下記の無保証規定が，そのままの形でソー
 *      スコード中に含まれていること．
 *  (2) 本ソフトウェアを，ライブラリ形式など，他のソフトウェア開発に使
 *      用できる形で再配布する場合には，再配布に伴うドキュメント（利用
 *      者マニュアルなど）に，上記の著作権表示，この利用条件および下記
 *      の無保証規定を掲載すること．
 *  (3) 本ソフトウェアを，機器に組み込むなど，他のソフトウェア開発に使
 *      用できない形で再配布する場合には，次のいずれかの条件を満たすこ
 *      と．
 *    (a) 再配布に伴うドキュメント（利用者マニュアルなど）に，上記の著
 *        作権表示，この利用条件および下記の無保証規定を掲載すること．
 *    (b) 再配布の形態を，別に定める方法によって，TOPPERSプロジェクトに
 *        報告すること．
 *  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
 *      害からも，上記著作権者およびTOPPERSプロジェクトを免責すること．
 * 
 *  本ソフトウェアは，無保証で提供されているものである．上記著作権者お
 *  よびTOPPERSプロジェクトは，本ソフトウェアに関して，その適用可能性も
 *  含めて，いかなる保証も行わない．また，本ソフトウェアの利用により直
 *  接的または間接的に生じたいかなる損害に関しても，その責任を負わない．
 * 
 *  @(#) $Id: /local/fi4-1.4/toppers/RELEASE_20061105_1/kernel/kmem.c 2750 2006-03-07T16:15:24.532712Z monaka  $
 */

/*
 *  カーネルメモリアロケータ
 */
#include <limits.h>
#include <stddef.h>
#include "kernel_impl.h"
#include "kmem.h"

typedef union {
	long l;
#ifdef _int64_
	_int64_ i64;
#endif
#if (__STDC_VERSION__ >= 199901L)
	long long ll;
#endif
	//double d;
	//long double ld;
} __dummy_for_align;

static const int maxalign = alignof(__dummy_for_align);

/*
 *
 */
#ifdef TOPPERS_kmemsetup

bool_t 
kmem_setup(void *buf, uint_t bufsz, KMEMB **p_kmemb)
{
	KMEMB *top;
	KMEMB *kmemb;

	if (bufsz < sizeof(KMEMB) * 2 ||
	    (uint_t)buf % alignof(__dummy_for_align) != 0) {
		return false;
	}

	top = (KMEMB *)buf;
	top->magic = 0;
	top->used = true;
	top->size = 0;
	queue_initialize(&(top->queue));

	kmemb = top + 1;
	kmemb->magic = KMEM_MAGIC;
	kmemb->used = false;
	kmemb->size = bufsz - sizeof(KMEMB);
	queue_insert_prev((QUEUE *)top, (QUEUE *)kmemb);

	*p_kmemb = top;

	return true;
}

#endif /* TOPPERS_kmemsetup */

/*
 *
 */
#ifdef TOPPERS_kmemallocp

bool_t kmem_allocatable(KMEMB *kmemb, uint_t size)
{
	KMEMB *ptr;

	/* alignmentをとっておく。 */
	size = ((size + sizeof(KMEMB)) + sizeof(__dummy_for_align) - 1) & - sizeof(__dummy_for_align);

	for (ptr = kmemb; ptr != kmemb; ptr = (KMEMB *)(ptr->queue.p_next)) {
		if (ptr->magic != KMEM_MAGIC) {
			return false;
		}

		if (ptr->used == false && ptr->size >= size) {
			/* 見つかった */
			return true;
		}
	}

	return false;
}

#endif /* TOPPERS_kmemallocp */

/*
 *
 */
#ifdef TOPPERS_kmemallocate

bool_t kmem_allocate(KMEMB *kmemb, uint_t size, void **p_blk)
{
	KMEMB *ptr;

	/* alignmentをとっておく。 */
	size = ((size + sizeof(KMEMB)) + sizeof(__dummy_for_align) - 1) & - sizeof(__dummy_for_align);

	/* 空領域の探索 */
	for (ptr = (KMEMB *)(kmemb->queue.p_next); ptr != kmemb; ptr = (KMEMB *)(ptr->queue.p_next)) {
		if (ptr->magic != KMEM_MAGIC) {
			return false;
		}

		if (ptr->used == false && ptr->size >= size) {
			/* 見つかった */

			if (ptr->size - size > KMEM_DELTA) {
				/* 空領域に十分な余裕があるので、分割する。*/
				KMEMB *s;
				s = (KMEMB *)((uint8_t *)ptr + size);
				s->size = ptr->size - size;
				s->magic = ptr->magic = KMEM_MAGIC;
				s->used = false;

				ptr->size = size;
				ptr->used = true;

				queue_insert_prev((QUEUE *)ptr, (QUEUE *)s);

			} else {
				/* 空領域に十分な余裕が無いので、全てを占有する。*/
				ptr->used = true;
			}

			/* ユーザがつかえるのは、ヘッダの直後から。*/
			*p_blk = (void *)(ptr + 1);
			return true;
		}
	}

	return false;
}

#endif /* TOPPERS_kmemallocate */

/*
 *
 */
#ifdef TOPPERS_kmemrelease

bool_t kmem_release(KMEMB *kmemb, void *buf)
{
	KMEMB *hdr;
	KMEMB *check_kmemb;

	if ((KMEMB *)buf < kmemb) {
		/*
		 *  この条件はあり得ない。
		 */
		return false;
	}
	hdr = (KMEMB *)buf - 1;

	if (hdr->magic != KMEM_MAGIC) {
		/*
		 * マジックナンバが異れば、
		 * 不正なアドレスを渡して来たとみなす。
		 */
		return false;
	}

	hdr->used = false;
/* Toppers porting F.Okazaki Delete start　magicを消すと二度と使用できないよ */
/*	hdr->magic = 0;*/
/* Toppers porting F.Okazaki Delete end */

/* Toppers porting F.Okazaki Delete,Append start　下のと逆です*/
/*	check_kmemb = (KMEMB *)(hdr->queue.p_next);*/
	check_kmemb = (KMEMB *)(hdr->queue.p_prev);
/* Toppers porting F.Okazaki Delete,Append end */
	if (check_kmemb->used == false) {
		hdr->size += check_kmemb->size;
		check_kmemb->magic = 0;
		check_kmemb->queue.p_next->p_prev = check_kmemb->queue.p_prev;
		check_kmemb->queue.p_prev->p_next = check_kmemb->queue.p_next;
	}

	/*
	 *  直前も空領域なら、合併する。
	 */
/* Toppers porting F.Okazaki Delete,Append start　上のと逆です*/
/*	check_kmemb = (KMEMB *)(hdr->queue.p_prev);*/
	check_kmemb = (KMEMB *)(hdr->queue.p_next);
/* Toppers porting F.Okazaki Delete,Append end */
	if (check_kmemb->used == false) {
		check_kmemb->size += hdr->size;
		hdr->magic = 0;
		hdr->queue.p_next->p_prev = hdr->queue.p_prev;
		hdr->queue.p_prev->p_next = hdr->queue.p_next;
	}

	return true;
}

#endif /* TOPPERS_kmemrelease */

/*
 *
 */
#ifdef TOPPERS_kmemstat

bool_t
kmem_status(KMEMB *kmemb, uint_t *remain, uint_t *maxblk)
{
	KMEMB *blk;
	QUEUE *entry = NULL;
	uint_t maxblksize = 0;

	if (kmemb->magic != 0) {
		/*
		 * マジックナンバが異れば、
		 * 不正なアドレスを渡して来たとみなす。
		 */
		return false;
	}

	*remain = 0;
	while((entry = queue_enumerate((QUEUE *)kmemb, entry)) != NULL) {
		blk = (KMEMB *)entry;

		if (blk->used == false) {
			*remain += blk->size;
			if (maxblksize < blk->size) {
				maxblksize = blk->size;
			}
		}
	}

	/*
	 * ref_mpl の仕様に合わせた。
	 * 「すぐに獲得できる最大のメモリブロックサイズがuint_t型で表わせる
	 * 最大値よりも大きい場合には、uint_t型で表わせる最大値を…。」
	 */
	if (maxblksize >= UINT_MAX) {
		*maxblk = UINT_MAX;
	} else {
		*maxblk = (uint_t)maxblksize;
	}
	
	return true;
}
#endif /* TOPPERS_kmemstat */
