/*****************************************************************************/
/* Copyright (c) 2008 NXP B.V.                                               */
/*                                                                           */
/* This program is free software; you can redistribute it and/or modify      */
/* it under the terms of the GNU General Public License as published by      */
/* the Free Software Foundation, using version 2 of the License.             */
/*                                                                           */
/* This program is distributed in the hope that it will be useful,           */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
/* GNU General Public License for more details.                              */
/*                                                                           */
/* You should have received a copy of the GNU General Public License         */
/* along with this program; if not, write to the Free Software               */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307       */
/* USA.                                                                      */
/*                                                                           */
/*****************************************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <asm/delay.h>
#include <asm/page.h>

#include "tmml.h"
#include "tmosal.h"

MODULE_AUTHOR("Nikita V. Youshchenko <yoush.cs.msu.su>, NXP B.V.");
MODULE_DESCRIPTION("OSAL test module");
MODULE_LICENSE("GPL v2");

static void empty(void)
{
}

static const char *err2str(tmErrorCode_t e)
{
	switch (e) {
#include "osal_test_retstrings.inc"
		default: return "something strange";
	}
}

#define CHECK_EXPECTED(res, exp, str) do { \
	if (res == exp) \
		printk("%s: got expected %s\n", str, err2str(res)); \
	else \
		printk("%s: expected %s, got %s\n", \
				str, err2str(exp), err2str(res)); \
} while (0)
#define CHECK_OK(res, str) CHECK_EXPECTED(res, TM_OK, str)

#define CHECK_SIZE(size, exp, str) do { \
	if (size == exp) \
		printk("%s: expected size\n", str); \
	else \
		printk("%s: unexpected size %d instead of %d\n", str, (int)size, (int)exp); \
} while (0)

#define CHECK_SIZE_AND_DATA(size, exp, data, expdata, str) do { \
	CHECK_SIZE(size, exp, str); \
	if (size == exp && memcmp(data, expdata, exp) != 0) \
		printk("%s: unexpected data\n", str); \
} while (0)

static void busy_wait_verbose(int msecs, const char *str)
{
	while (msecs) {
		udelay(1000);
		if (msecs % 100 == 0)
			printk("%s: %d msecs left\n", str, msecs);
		msecs--;
	}
}

/* 1. Simple task creation and sleeping. */

static void test_1_task(void *unused)
{
	int i;
	
	printk("sleep_test_task: running\n");
	for (i = 1; i <= 5; i++) {
		tmosalTaskSleep(1000);
		printk("test_1_task: wakeup %d\n", i);
	}

	tmosalTaskExit();
}

static void test_1(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalTaskCreate(test_1_task, 0, 50, 4096, &handle, "T1",
			tmosalTaskStarted);
	CHECK_OK(res, "test_1: tmosalTaskCreate()");

	tmosalTaskSleep(7000);
}


/* 2. Getting current task handle */

static tmosalTaskHandle_t test_2_handle;

static void test_2_task(void *unused)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalTaskIdent(&handle);
	CHECK_OK(res, "test_2: tmosalTaskIdent()");

	printk("test_2_task: handles %s\n", (handle == test_2_handle) ? "are same" : "differ");

	tmosalTaskExit();
}

static void test_2(void)
{
	tmErrorCode_t res;

	res = tmosalTaskCreate(test_2_task, 0, 50, 4096, &test_2_handle, "T2",
			tmosalTaskStarted);
	CHECK_OK(res, "test_2: tmosalTaskCreate()");

	tmosalTaskSleep(1000);
}


/* 3. Different priorities */

static void test_3_task_1(void *unused)
{
	tmosalTaskSleep(500);
	busy_wait_verbose(500, "test_3_task_1");
	tmosalTaskExit();
}

static void test_3_task_2(void *unused)
{
	busy_wait_verbose(800, "test_3_task_2");
	tmosalTaskExit();
}

static void test_3(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalTaskCreate(test_3_task_1, 0, 50, 4096, &handle, "T3a",
			tmosalTaskStarted);
	CHECK_OK(res, "test_3: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_3_task_2, 0, 40, 4096, &handle, "T3b",
			tmosalTaskStarted);
	CHECK_OK(res, "test_3: tmosalTaskCreate() for task 2");

	tmosalTaskSleep(4000);
}


/* 4. Round-robin */

static void test_4_task(void *p)
{
	busy_wait_verbose(500, (p == (void *)1) ? "test_4_task_1" : "test_4_task_2");
	tmosalTaskExit();
}

static void test_4(void)
{
	tmosalTaskHandle_t handle1, handle2;
	tmErrorCode_t res;

	res = tmosalTaskCreate(test_4_task, (void *)1, 40, 4096, &handle1,
		       	"T4a", 0);
	CHECK_OK(res, "test_4: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_4_task, (void *)2, 40, 4096, &handle2,
		       	"T4b", 0);
	CHECK_OK(res, "test_4: tmosalTaskCreate() for task 2");

	tmosalDisableScheduler();

	res = tmosalTaskStart(handle1);
	CHECK_OK(res, "test_4: tmosalTaskStart() for task 1");

	res = tmosalTaskStart(handle2);
	CHECK_OK(res, "test_4: tmosalTaskStart() for task 2");
	
	tmosalEnableScheduler();

	tmosalTaskSleep(2000);
}


/* 5. Delayed task start */

static void test_5_task(void *unused)
{
	printk("test_5_task running\n");
	tmosalTaskExit();
}

static void test_5(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;
	
	res = tmosalTaskCreate(0, 0, 40, 4096, &handle, "T5", 0);
	CHECK_OK(res, "test_5: tmosalTaskCreate()");

	res = tmosalTaskStart(handle);
	CHECK_EXPECTED(res, TMOSAL_ERR_TASK_START, "test_5: first tmosalTaskStart()");

	res = tmosalTaskSetup(handle, test_5_task, 0, 0);
	CHECK_OK(res, "test_5: tmosalTaskSetup()");

	res = tmosalTaskStart(handle);
	CHECK_OK(res, "test_5: second tmosalTaskStart()");

	tmosalTaskSleep(1000);
}


/* 6. Suspend and resume */

static tmosalTaskHandle_t test_6_handle;

static void test_6_task_1(void *unused)
{
	tmErrorCode_t res;

	res = tmosalTaskSuspend();
	CHECK_OK(res, "test_6_task_1: tmosalTaskSuspend()");
	
	printk("test_6_task_1: resumed\n");

	tmosalTaskExit();
}

static void test_6_task_2(void *unused)
{
	tmErrorCode_t res;

	tmosalTaskSleep(1000);
	printk("test_6_task_2: will resume task 1\n");
	res = tmosalTaskResume(test_6_handle);
	CHECK_OK(res, "test_6_task_2: tmosalTaskResume()");

	tmosalTaskExit();
}

static void test_6(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalTaskCreate(test_6_task_1, 0, 40, 4096, &test_6_handle, "T6a",
			tmosalTaskStarted);
	CHECK_OK(res, "test_6: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_6_task_2, 0, 40, 4096, &handle, "T6b",
			tmosalTaskStarted);
	CHECK_OK(res, "test_6: tmosalTaskCreate() for task 2");

	tmosalTaskSleep(4000);
}


/* 7. Suspend/resume with counter */

static tmosalTaskHandle_t test_7_handle;

static void test_7_task_1(void *unused)
{
	tmErrorCode_t res;

	tmosalTaskSleep(1000);
	printk("test_7_task_1: point A\n");

	res = tmosalTaskSuspend();
	CHECK_OK(res, "test_7_task_1: 1st tmosalTaskSuspend()");
	
	printk("test_7_task_1: point B\n");
	
	res = tmosalTaskSuspend();
	CHECK_OK(res, "test_7_task_1: 2nd tmosalTaskSuspend()");
	
	printk("test_7_task_1: point C\n");
	
	tmosalTaskExit();
}

static void test_7_task_2(void *unused)
{
	tmErrorCode_t res;

	printk("test_7_task_2: point D\n");

	res = tmosalTaskResume(test_7_handle);
	CHECK_OK(res, "test_7_task_2: 1st tmosalTaskResume()");
	
	tmosalTaskSleep(2000);
	printk("test_7_task_2: point E\n");

	res = tmosalTaskResume(test_7_handle);
	CHECK_OK(res, "test_7_task_2: 2nd tmosalTaskResume()");
	
	tmosalTaskExit();
}

static void test_7(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalTaskCreate(test_7_task_1, 0, 40, 4096, &test_7_handle, "T7a",
			tmosalTaskStarted | tmosalTaskCounting);
	CHECK_OK(res, "test_7: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_7_task_2, 0, 40, 4096, &handle, "T7b",
			tmosalTaskStarted);
	CHECK_OK(res, "test_7: tmosalTaskCreate() for task 2");

	tmosalTaskSleep(4000);
}


/* 8. Set/get priority */

static tmosalTaskHandle_t test_8_handle;

static void test_8_task_1(void *unused)
{
	busy_wait_verbose(1000, "test_8_task_1");
	tmosalTaskExit();
}

static void test_8_task_2(void *unused)
{
	tmosalTaskPriority_t prio;
	tmErrorCode_t res;

	busy_wait_verbose(500, "test_8_task_2");

	res = tmosalTaskGetPriority(test_8_handle, &prio);
	CHECK_OK(res, "test_8_task_2: 1st tmosalTaskGetPriority()");
	printk("test_8_task_2: prio is %d\n", (int)prio);

	res = tmosalTaskSetPriority(test_8_handle, 50);
	CHECK_OK(res, "test_8_task_2: tmosalTaskSetPriority()");
	
	res = tmosalTaskGetPriority(test_8_handle, &prio);
	CHECK_OK(res, "test_8_task_2: 1st tmosalTaskGetPriority()");
	printk("test_8_task_2: prio is %d\n", (int)prio);

	busy_wait_verbose(500, "test_8_task_2");

	tmosalTaskExit();
}

static void test_8(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalTaskCreate(test_8_task_1, 0, 40, 4096, &handle,
		       	"T8a", 0);
	CHECK_OK(res, "test_8: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_8_task_2, 0, 40, 4096, &test_8_handle, "T8b", 0);
	CHECK_OK(res, "test_8: tmosalTaskCreate() for task 2");

	tmosalDisableScheduler();
	
	res = tmosalTaskStart(handle);
	CHECK_OK(res, "test_4: tmosalTaskStart() for task 1");

	res = tmosalTaskStart(test_8_handle);
	CHECK_OK(res, "test_4: tmosalTaskStart() for task 2");

	tmosalEnableScheduler();

	tmosalTaskSleep(3000);
}


/* 9. Simple semphore test */

static tmosalSemHandle_t test_9_sem;

static void test_9_task(void *unused)
{
	tmErrorCode_t res;

	res = tmosalSemAcquire(test_9_sem, 0);
	CHECK_OK(res, "test_9_task: tmosalSemAcuire()");

	printk("test_9_task: point A\n");
	tmosalTaskSleep(1000);
	printk("test_9_task: point B\n");

	res = tmosalSemRelease(test_9_sem);
	CHECK_OK(res, "test_9_task: tmosalSemRelease()");

	tmosalTaskExit();
}

static void test_9(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalSemCreate(2, &test_9_sem, tmosalSemCreateFlagNone);
	CHECK_OK(res, "test_9: tmosalSemCreate()");
	if (res != TM_OK)
		return;

	res = tmosalTaskCreate(test_9_task, 0, 40, 4096, &handle,
		       	"T9a", tmosalTaskStarted);
	CHECK_OK(res, "test_9: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_9_task, 0, 40, 4096, &handle,
		       	"T9b", tmosalTaskStarted);
	CHECK_OK(res, "test_9: tmosalTaskCreate() for task 2");

	res = tmosalTaskCreate(test_9_task, 0, 40, 4096, &handle,
		       	"T9c", tmosalTaskStarted);
	CHECK_OK(res, "test_9: tmosalTaskCreate() for task 3");

	tmosalTaskSleep(4000);

	res = tmosalSemDestroy(test_9_sem);
	CHECK_OK(res, "test_9: tmosalSemDestroy()");
}


/* 10. Sleeping on semaphore with timeout */

static void test_10(void)
{
	tmosalSemHandle_t sem;
	tmErrorCode_t res;
	Int32 timeout = 1000;

	res = tmosalSemCreate(1, &sem, tmosalSemCreateFlagNone);
	CHECK_OK(res, "test_10: tmosalSemCreate()");
	if (res != TM_OK)
		return;

	res = tmosalSemAcquire(sem, &timeout);
	CHECK_OK(res, "test_10: first tmosalSemAcuire()");

	res = tmosalSemAcquire(sem, &timeout);
	CHECK_EXPECTED(res, TMOSAL_ERR_SEM_TIMEOUT,
			"test_10: second tmosalSemAcuire()");

	res = tmosalSemDestroy(sem);
	CHECK_OK(res, "test_10: tmosalSemDestroy()");
}


/* 11. Waking tasks of different priorities */

static tmosalSemHandle_t test_11_sem;

static void test_11_task(void *param)
{
	tmErrorCode_t res;
	int prio = (int)param;

	res = tmosalSemAcquire(test_11_sem, 0);
	CHECK_OK(res, "test_11_task: tmosalSemAcuire()");

	printk("test_11_task (prio %d): acquired\n", prio);

	res = tmosalSemRelease(test_11_sem);
	CHECK_OK(res, "test_11_task: tmosalSemRelease()");

	tmosalTaskExit();
}

static void test_11(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalSemCreate(1, &test_11_sem, tmosalSemCreateFlagNone);
	CHECK_OK(res, "test_11: tmosalSemCreate()");
	if (res != TM_OK)
		return;

	res = tmosalSemAcquire(test_11_sem, 0);
	CHECK_OK(res, "test_11: tmosalSemAcuire()");
	if (res != TM_OK)
		goto del;

	res = tmosalTaskCreate(test_11_task, (void *)40, 40, 4096, &handle,
		       	"T11a", tmosalTaskStarted);
	CHECK_OK(res, "test_11: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_11_task, (void *)50, 50, 4096, &handle,
		       	"T11b", tmosalTaskStarted);
	CHECK_OK(res, "test_11: tmosalTaskCreate() for task 2");

	res = tmosalTaskCreate(test_11_task, (void *)45, 45, 4096, &handle,
		       	"T11c", tmosalTaskStarted);
	CHECK_OK(res, "test_11: tmosalTaskCreate() for task 3");

	res = tmosalSemRelease(test_11_sem);
	CHECK_OK(res, "test_11: tmosalSemRelease()");

	tmosalTaskSleep(2000);

del:
	res = tmosalSemDestroy(test_11_sem);
	CHECK_OK(res, "test_11: tmosalSemDestroy()");
}


/* 12. Destroying semaphore while task is waiting */

static tmosalSemHandle_t test_12_sem;

static void test_12_task(void *unused)
{
	tmErrorCode_t res;

	res = tmosalSemAcquire(test_12_sem, 0);
	CHECK_EXPECTED(res, TMOSAL_ERR_SEM_DEL,
			"test_12_task: tmosalSemAcuire()");

	tmosalTaskExit();
}

static void test_12(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalSemCreate(1, &test_12_sem, tmosalSemCreateFlagNone);
	CHECK_OK(res, "test_12: tmosalSemCreate()");
	if (res != TM_OK)
		return;

	res = tmosalSemAcquire(test_12_sem, 0);
	CHECK_OK(res, "test_12: tmosalSemAcuire()");
	if (res != TM_OK)
		goto del;

	res = tmosalTaskCreate(test_12_task, 0, 40, 4096, &handle,
		       	"T12", tmosalTaskStarted);
	CHECK_OK(res, "test_12: tmosalTaskCreate()");

	tmosalTaskSleep(1000);

del:
	res = tmosalSemDestroy(test_12_sem);
	CHECK_EXPECTED(res, TMOSAL_ERR_SEM_TASK, "test_12: tmosalSemDestroy()");
}


/* 13. Simple mutex test */

static tmosalMutexHandle_t test_13_mutex;

static void test_13_task(void *unused)
{
	tmErrorCode_t res;

	res = tmosalMutexEnter(test_13_mutex, 0);
	CHECK_OK(res, "test_13_task: tmosalMutexEnter()");

	printk("test_13_task: point A\n");
	tmosalTaskSleep(1000);
	printk("test_13_task: point B\n");

	res = tmosalMutexExit(test_13_mutex);
	CHECK_OK(res, "test_13_task: tmosalMutexLeave()");

	tmosalTaskExit();
}

static void test_13(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalMutexCreate(&test_13_mutex, tmosalMutexCreateFlagNone);
	CHECK_OK(res, "test_13: tmosalMutexCreate()");
	if (res != TM_OK)
		return;

	res = tmosalTaskCreate(test_13_task, 0, 40, 4096, &handle,
		       	"T13a", tmosalTaskStarted);
	CHECK_OK(res, "test_13: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_13_task, 0, 40, 4096, &handle,
		       	"T13b", tmosalTaskStarted);
	CHECK_OK(res, "test_13: tmosalTaskCreate() for task 2");

	tmosalTaskSleep(3000);

	res = tmosalMutexDestroy(test_13_mutex);
	CHECK_OK(res, "test_13: tmosalMutexDestroy()");
}


/* 14. Re-acquiring mutex */

static tmosalMutexHandle_t test_14_mutex;

static void test_14_task(void *unused)
{
	tmErrorCode_t res;
	Int32 timeout = 0;

	res = tmosalMutexEnter(test_14_mutex, &timeout);
	CHECK_OK(res, "test_14_task: first tmosalMutexEnter()");

	res = tmosalMutexEnter(test_14_mutex, &timeout);
	CHECK_OK(res, "test_14_task: second tmosalMutexEnter()");

	res = tmosalMutexExit(test_14_mutex);
	CHECK_OK(res, "test_14_task: first tmosalMutexExit()");

	res = tmosalMutexExit(test_14_mutex);
	CHECK_OK(res, "test_14_task: second tmosalMutexExit()");

	res = tmosalMutexExit(test_14_mutex);
	CHECK_EXPECTED(res, TMOSAL_ERR_MUTEX_NOTOWNED,
			"test_14_task: third tmosalMutexExit()");

	tmosalTaskExit();
}

static void test_14(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalMutexCreate(&test_14_mutex, tmosalMutexCreateFlagNone);
	CHECK_OK(res, "test_14: tmosalMutexCreate()");
	if (res != TM_OK)
		return;

	res = tmosalTaskCreate(test_14_task, 0, 40, 4096, &handle,
		       	"T14", tmosalTaskStarted);
	CHECK_OK(res, "test_14: tmosalTaskCreate()");

	tmosalTaskSleep(2000);

	res = tmosalMutexDestroy(test_14_mutex);
	CHECK_OK(res, "test_14: tmosalMutexDestroy()");
}


/* 15. Sleeping on mutex with timeout */

static tmosalMutexHandle_t test_15_mutex;

static void test_15_task(void *unused)
{
	tmErrorCode_t res;
	Int32 timeout = 1000;

	res = tmosalMutexEnter(test_15_mutex, &timeout);
	if (res == TM_OK)
		printk("test_15_task: tmosalMutexEnter() returned TM_OK\n");
	else if (res == TMOSAL_ERR_MUTEX_TIMEOUT)
		printk("test_15_task: tmosalMutexEnter() returned TMOSAL_ERR_MUTEX_TIMEOUT\n");
	else
		printk("test_15_task: tmosalMutexEnter() returned unexpected code %d\n", (int)res);
	
	if (res == TM_OK) {
		tmosalTaskSleep(2000);
		res = tmosalMutexExit(test_15_mutex);
		CHECK_OK(res, "test_15_task: tmosalMutexLeave()");
	}

	tmosalTaskExit();
}

static void test_15(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalMutexCreate(&test_15_mutex, tmosalMutexCreateFlagNone);
	CHECK_OK(res, "test_15: tmosalMutexCreate()");
	if (res != TM_OK)
		return;

	res = tmosalTaskCreate(test_15_task, 0, 40, 4096, &handle,
		       	"T15a", tmosalTaskStarted);
	CHECK_OK(res, "test_15: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_15_task, 0, 40, 4096, &handle,
		       	"T15b", tmosalTaskStarted);
	CHECK_OK(res, "test_15: tmosalTaskCreate() for task 2");

	tmosalTaskSleep(5000);

	res = tmosalMutexDestroy(test_15_mutex);
	CHECK_OK(res, "test_15: tmosalMutexDestroy()");
}


/* 16. Waiting for event */

static tmosalEventHandle_t test_16_event;

static void test_16_task(void *unused)
{
	tmErrorCode_t res;

	res = tmosalEventWait(test_16_event, 0, 0);
	CHECK_OK(res, "test_16_task: tmosalEventWait()");

	printk("test_16_task: got event\n");
	tmosalTaskExit();
}

static void test_16(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalEventCreate(&test_16_event, tmosalEventCreateFlagNone);
	CHECK_OK(res, "test_16: tmosalEventCreate()");
	if (res != TM_OK)
		return;

	res = tmosalTaskCreate(test_16_task, 0, 40, 4096, &handle,
		       	"T16", tmosalTaskStarted);
	CHECK_OK(res, "test_16: tmosalTaskCreate()");

	tmosalTaskSleep(2000);

	res = tmosalEventSend(test_16_event, 0);
	CHECK_OK(res, "test_16: tmosalEventSend()");

	tmosalTaskSleep(1000);

	res = tmosalEventDestroy(test_16_event);
	CHECK_OK(res, "test_16: tmosalEventDestroy()");
}


/* 17. Waiting for event with timeout */

static tmosalEventHandle_t test_17_event;

static void test_17_task(void *unused)
{
	tmErrorCode_t res;
	Int32 timeout = 1000;

	res = tmosalEventWait(test_17_event, &timeout, 0);
	CHECK_EXPECTED(res, TMOSAL_ERR_EVT_TIMEOUT,
			"test_17_task: tmosalEventWait()");

	tmosalTaskExit();
}

static void test_17(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalEventCreate(&test_17_event, tmosalEventCreateFlagNone);
	CHECK_OK(res, "test_17: tmosalEventCreate()");
	if (res != TM_OK)
		return;

	res = tmosalTaskCreate(test_17_task, 0, 40, 4096, &handle,
		       	"T17", tmosalTaskStarted);
	CHECK_OK(res, "test_17: tmosalTaskCreate()");

	tmosalTaskSleep(2000);

	res = tmosalEventDestroy(test_17_event);
	CHECK_OK(res, "test_17: tmosalEventDestroy()");
}


/* 18. Getting event without wait */

static tmosalEventHandle_t test_18_event;

static void test_18_task(void *unused)
{
	tmErrorCode_t res;

	res = tmosalEventWait(test_18_event, 0, 0);
	CHECK_OK(res, "test_18_task: tmosalEventWait()");

	printk("test_18_task: got event\n");
	tmosalTaskExit();
}

static void test_18(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalEventCreate(&test_18_event, tmosalEventCreateFlagNone);
	CHECK_OK(res, "test_18: tmosalEventCreate()");
	if (res != TM_OK)
		return;

	res = tmosalEventSend(test_18_event, 0);
	CHECK_OK(res, "test_18: tmosalEventSend()");
	if (res != TM_OK)
		goto del;

	res = tmosalTaskCreate(test_18_task, 0, 40, 4096, &handle,
		       	"T18", tmosalTaskStarted);
	CHECK_OK(res, "test_18: tmosalTaskCreate()");

	tmosalTaskSleep(1000);
del:
	res = tmosalEventDestroy(test_18_event);
	CHECK_OK(res, "test_18: tmosalEventDestroy()");
}


/* 19. Several event waiters, with autoclear */

static tmosalEventHandle_t test_19_event;

static void test_19_task(void *unused)
{
	tmErrorCode_t res;

	res = tmosalEventWait(test_19_event, 0, 0);
	CHECK_OK(res, "test_19_task: tmosalEventWait()");

	printk("test_19_task: got event\n");
	tmosalTaskExit();
}

static void test_19(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalEventCreate(&test_19_event, tmosalEventAutoClear);
	CHECK_OK(res, "test_19: tmosalEventCreate()");
	if (res != TM_OK)
		return;

	res = tmosalTaskCreate(test_19_task, 0, 40, 4096, &handle,
		       	"T19a", tmosalTaskStarted);
	CHECK_OK(res, "test_19: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_19_task, 0, 40, 4096, &handle,
		       	"T19b", tmosalTaskStarted);
	CHECK_OK(res, "test_19: tmosalTaskCreate() for task 2");

	tmosalTaskSleep(2000);
	res = tmosalEventSend(test_19_event, 0);
	CHECK_OK(res, "test_19: first tmosalEventSend()");

	tmosalTaskSleep(2000);
	res = tmosalEventSend(test_19_event, 0);
	CHECK_OK(res, "test_19: second tmosalEventSend()");

	res = tmosalEventDestroy(test_19_event);
	CHECK_OK(res, "test_19: tmosalEventDestroy()");
}


/* 20. Several event waiters, without autoclear */

static tmosalEventHandle_t test_20_event;

static void test_20_task(void *unused)
{
	tmErrorCode_t res;

	res = tmosalEventWait(test_20_event, 0, 0);
	CHECK_OK(res, "test_20_task: tmosalEventWait()");

	printk("test_20_task: got event\n");
	tmosalTaskExit();
}

static void test_20(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalEventCreate(&test_20_event, tmosalEventCreateFlagNone);
	CHECK_OK(res, "test_20: tmosalEventCreate()");
	if (res != TM_OK)
		return;

	res = tmosalTaskCreate(test_20_task, 0, 40, 4096, &handle,
		       	"T20a", tmosalTaskStarted);
	CHECK_OK(res, "test_20: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_20_task, 0, 40, 4096, &handle,
		       	"T20b", tmosalTaskStarted);
	CHECK_OK(res, "test_20: tmosalTaskCreate() for task 2");

	tmosalTaskSleep(2000);
	res = tmosalEventSend(test_20_event, 0);
	CHECK_OK(res, "test_20: first tmosalEventSend()");

	res = tmosalEventDestroy(test_20_event);
	CHECK_OK(res, "test_20: tmosalEventDestroy()");
}


/* 21. Event clear */

static tmosalEventHandle_t test_21_event;

static void test_21_task(void *unused)
{
	tmErrorCode_t res;
	Int32 timeout = 0;

	res = tmosalEventWait(test_21_event, 0, 0);
	CHECK_OK(res, "test_21_task: tmosalEventWait()");

	tmosalTaskSleep(1000);
	res = tmosalEventWait(test_21_event, 0, 0);
	CHECK_OK(res, "test_21_task: tmosalEventWait()");

	tmosalTaskSleep(2000);
	res = tmosalEventWait(test_21_event, &timeout, 0);
	CHECK_EXPECTED(res, TMOSAL_ERR_EVT_TIMEOUT,
			"test_21_task: tmosalEventWait()");

	tmosalTaskExit();
}

static void test_21(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalEventCreate(&test_21_event, tmosalEventCreateFlagNone);
	CHECK_OK(res, "test_21: tmosalEventCreate()");
	if (res != TM_OK)
		return;

	res = tmosalTaskCreate(test_21_task, 0, 40, 4096, &handle,
		       	"T21", tmosalTaskStarted);
	CHECK_OK(res, "test_21: tmosalTaskCreate()");

	res = tmosalEventSend(test_21_event, 0);
	CHECK_OK(res, "test_21: first tmosalEventSend()");
	
	tmosalTaskSleep(2000);

	res = tmosalEventReset(test_21_event);
	CHECK_OK(res, "test_21: first tmosalEventReset()");

	tmosalTaskSleep(2000);
	
	res = tmosalEventDestroy(test_21_event);
	CHECK_OK(res, "test_21: tmosalEventDestroy()");
}


/* 22. Simple queue data exchange (no wait) */

static void test_22(void)
{
	tmosalQueueHandle_t queue;
	tmErrorCode_t res;
	int qsize = 3, i;
	int msg[4] = { 11, 22, 33, 44 };
	UInt32 s;
	int buf[4];

	res = tmosalQueueCreate(qsize, 32, &queue, tmosalQueueCreateFlagNone);
	CHECK_OK(res, "test_22: tmosalQueueCreate()");
	if (res != TM_OK)
		return;

	res = tmosalQueueSend(queue, msg, sizeof(msg), 0, 0);
	CHECK_OK(res, "test_22: tmosalQueueSend()");

	res = tmosalQueueGetPacketSize(queue, &s);
	CHECK_OK(res, "test_22: tmosalQueueGetPacketSize()");
	if (res == TM_OK)
		CHECK_SIZE(s, sizeof(msg), "test_22: tmosalQueueGetPacketSize()");

	res = tmosalQueueReceive(queue, buf, &s, 0, 0);
	CHECK_OK(res, "test_22: tmosalQueueReceive()");
	if (res == TM_OK)
		CHECK_SIZE_AND_DATA(s, sizeof(msg), buf, msg, "test_22: tmosalQueueReceive()");

	res = tmosalQueueGetPacketSize(queue, &s);
	CHECK_EXPECTED(res, TMOSAL_ERR_QUE_EMPTY,
			"test_22: tmosalQueueGetPacketSize() on empty queue");

	/* Try to overflow the queue */
	for (i = 0; i <= qsize; i++) {
		msg[0] = i;
		res = tmosalQueueSend(queue, msg, sizeof(msg), 0, 0);
		if (i < qsize)
			CHECK_OK(res, "test_22: tmosalQueueSend()");
		else
			CHECK_EXPECTED(res, TMOSAL_ERR_QUE_TIMEOUT,
					"test_22: tmosalQueueSend() for full queue");
	}

	/* Now try to read entire queue */
	for (i = 0; i < qsize; i++) {
		res = tmosalQueueReceive(queue, buf, &s, 0, 0);
		CHECK_OK(res, "test_22: tmosalQueueReceive()");
		if (res == TM_OK) {
			msg[0] = i;
			CHECK_SIZE_AND_DATA(s, sizeof(msg), buf, msg, "test_22: tmosalQueueReceive()");
		}
	}

	res = tmosalQueueDestroy(queue);
	CHECK_OK(res, "test_22: tmosalQueueDestroy()");
}


/* 23. Waiting on queue */

static int test_23_msg[2] = { 5555, 6666 };
static tmosalQueueHandle_t test_23_queue;

static void test_23_task(void *unused)
{
	tmErrorCode_t res;
	int buf[2];
	UInt32 s;

	res = tmosalQueueReceive(test_23_queue, buf, &s, 0, 0);
	CHECK_OK(res, "test_23_task: tmosalQueueReceive()");
	if (res == TM_OK)
		CHECK_SIZE_AND_DATA(s, sizeof(test_23_msg), buf, test_23_msg,
				"test_23_task: tmosalQueueReceive()");

	tmosalTaskExit();
}

static void test_23(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalQueueCreate(2, 32, &test_23_queue,
			tmosalQueueCreateFlagNone);
	CHECK_OK(res, "test_23: tmosalQueueCreate()");
	if (res != TM_OK)
		return;

	res = tmosalTaskCreate(test_23_task, 0, 40, 4096, &handle,
		       	"T23", tmosalTaskStarted);
	CHECK_OK(res, "test_23: tmosalTaskCreate()");

	tmosalTaskSleep(2000);

	res = tmosalQueueSend(test_23_queue, test_23_msg, sizeof(test_23_msg),
			0, 0);
	CHECK_OK(res, "test_23: tmosalQueueSend()");

	tmosalTaskSleep(2000);

	res = tmosalQueueDestroy(test_23_queue);
	CHECK_OK(res, "test_23: tmosalQueueDestroy()");
}



/* 24. Waiting on queue with timeout */

static int test_24_msg[2] = { 5555, 6666 };
static tmosalQueueHandle_t test_24_queue;

static void test_24_task(void *unused)
{
	tmErrorCode_t res;
	int buf[2];
	UInt32 s;
	Int32 timeout = 1000;

	res = tmosalQueueReceive(test_24_queue, buf, &s, &timeout, 0);
	CHECK_EXPECTED(res, TMOSAL_ERR_QUE_TIMEOUT,
			"test_24_task: tmosalQueueReceive()");

	tmosalTaskExit();
}

static void test_24(void)
{
	tmosalTaskHandle_t handle;
	tmErrorCode_t res;

	res = tmosalQueueCreate(2, 32, &test_24_queue,
			tmosalQueueCreateFlagNone);
	CHECK_OK(res, "test_24: tmosalQueueCreate()");
	if (res != TM_OK)
		return;

	res = tmosalTaskCreate(test_24_task, 0, 40, 4096, &handle,
		       	"T24", tmosalTaskStarted);
	CHECK_OK(res, "test_24: tmosalTaskCreate()");

	tmosalTaskSleep(2000);

	res = tmosalQueueSend(test_24_queue, test_24_msg, sizeof(test_24_msg),
			0, 0);
	CHECK_OK(res, "test_24: tmosalQueueSend()");

	tmosalTaskSleep(2000);

	res = tmosalQueueDestroy(test_24_queue);
	CHECK_OK(res, "test_24: tmosalQueueDestroy()");
}


/* 25. Getting uptime */

static void test_25(void)
{
	tmosalSysTime_t uptime;
	tmErrorCode_t res;
	u64 value;

	res = tmosalGetSysTime(&uptime);
	CHECK_OK(res, "test_25: tmosalGetSysTime()");
	if (res == TM_OK) {
		value = ((u64)(uptime.msHi) << 32) + uptime.msLo;
		do_div(value, 1000);
		printk("test_25: uptime is about %u seconds\n", (u32)value);
	}
}


/* 26. Get/set daytime */

static void test_26(void)
{
	tmosalDateTime_t dt;
	tmErrorCode_t res;

	res = tmosalGetDateTime(&dt);
	CHECK_OK(res, "test_26: tmosalGetDateTime()");
	if (res != TM_OK)
		return;

	printk("test_26: got: Y %d, M %d, D %d, h %d, m %d, s %d, ms %d\n",
			(int)dt.year, (int)dt.month, (int)dt.day, (int)dt.hour,
		       	(int)dt.minute, (int)dt.second, (int)dt.millisecond);

	if (dt.hour % 2)
		dt.hour--;
	else
		dt.hour++;

	printk("test_26: setting: Y %d, M %d, D %d, h %d, m %d, s %d, ms %d\n",
			(int)dt.year, (int)dt.month, (int)dt.day, (int)dt.hour,
		       	(int)dt.minute, (int)dt.second, (int)dt.millisecond);

	res = tmosalSetDateTime(&dt);
	CHECK_OK(res, "test_26: tmosalSetDateTime()");
}


/* 27. Single-shot timer test */

static void test_27_handler(void *unused)
{
	printk("test_27_handler() called\n");
}

static void test_27(void)
{
	tmosalTimerHandle_t timer;
	tmErrorCode_t res;
	tmosalTimerSetup_t ts;
	tmosalTime_t t;

	res = tmosalTimerCreate(&timer, 0);
	CHECK_OK(res, "test_27: tmosalTimerCreate()");
	if (res != TM_OK)
		return;

	ts.pTimerHandler = test_27_handler;
	ts.pContextArgument = 0;
	ts.value.seconds = 3;
	ts.value.milliseconds = 0;
	ts.modulus.seconds = 0;
	ts.modulus.milliseconds = 0;
	res = tmosalTimerInstanceSetup(timer, &ts);
	CHECK_OK(res, "test_27: tmosalTimerInstanceSetup()");
	if (res != TM_OK)
		goto del;

	res = tmosalTimerStart(timer);
	CHECK_OK(res, "test_27: tmosalTimerStart()");
	if (res != TM_OK)
		goto del;

	tmosalTaskSleep(1000);

	res = tmosalTimerGetTime(timer, &t);
	CHECK_OK(res, "test_27: tmosalTimerGetTime()");
	if (res == TM_OK)
		printk("test_27: returned time: %d s, %d ms\n",
				(int)t.seconds, (int)t.milliseconds);

	tmosalTaskSleep(5000);
del:
	res = tmosalTimerDestroy(timer);
	CHECK_OK(res, "test_27: tmosalTimerDestroy()");
}


/* 28. Periodic timer test */

static int test_28_shots;
static tmosalTimerHandle_t test_28_timer;

static void test_28_handler(void *unused)
{
	printk("test_28_handler() called\n");
	if (--test_28_shots <= 0) {
		printk("test_28_handler(): stopping timer\n");
		tmosalTimerStop(test_28_timer);
	}
}

static void test_28(void)
{
	tmosalTimerSetup_t ts;
	tmErrorCode_t res;

	test_28_shots = 5;

	res = tmosalTimerCreate(&test_28_timer, 0);
	CHECK_OK(res, "test_28: tmosalTimerCreate()");
	if (res != TM_OK)
		return;

	ts.pTimerHandler = test_28_handler;
	ts.pContextArgument = 0;
	ts.value.seconds = 3;
	ts.value.milliseconds = 0;
	ts.modulus.seconds = 2;
	ts.modulus.milliseconds = 0;
	res = tmosalTimerInstanceSetup(test_28_timer, &ts);
	CHECK_OK(res, "test_28: tmosalTimerInstanceSetup()");
	if (res != TM_OK)
		goto del;

	res = tmosalTimerStart(test_28_timer);
	CHECK_OK(res, "test_28: tmosalTimerStart()");
	if (res != TM_OK)
		goto del;

	tmosalTaskSleep(13000);
del:
	res = tmosalTimerDestroy(test_28_timer);
	CHECK_OK(res, "test_28: tmosalTimerDestroy()");
}


/* 29. Setting interrupt handler */

static void test_29(void)
{
	tmosalIntHandle_t handle;
	tmErrorCode_t res;

	res = tmosalIntCreate("abc", &handle, tmosalIntCreateFlagNone);
	CHECK_EXPECTED(res, TMOSAL_ERR_INT_INVALID,
			"test_29: trying to get abc interrupt");

	res = tmosalIntCreate("UART1", &handle, tmosalIntCreateFlagNone);
	CHECK_EXPECTED(res, TMOSAL_ERR_INT_OWNED,
			"test_29: trying to get UART1 interrupt");

	res = tmosalIntCreate("UART0", &handle, tmosalIntCreateFlagNone);
	CHECK_OK(res, "test_29: trying to get UART0 interrupt");

	res = tmosalIntDestroy(handle);
	CHECK_OK(res, "test_29: tmosalIntDestroy()");
}


/* 30. Interrupt disable demonstration */

static void test_30(void)
{
	unsigned long jif1, jif2;
	int i;
	UInt32 flags;
	
	jif1 = jiffies;
	for (i = 0; i < 100; i++)
		udelay(1000);
	jif2 = jiffies;

	printk("test_30: ints enabled: jiffies values %s\n",
			jif1 == jif2 ? "don't differ" : "differ");

	tmosalDisableInterrupts(&flags);
	jif1 = jiffies;
	for (i = 0; i < 100; i++)
		udelay(1000);
	jif2 = jiffies;
	tmosalEnableInterrupts(flags);

	printk("test_30: ints disabled: jiffies values %s\n",
			jif1 == jif2 ? "don't differ" : "differ");
}


/* 31. Scheduler disable demonstration */

static void test_31_task_1(void *unused)
{
	tmosalDisableScheduler();
	busy_wait_verbose(500, "test_31_task_1");
	tmosalEnableScheduler();
	tmosalTaskExit();
}

static void test_31_task_2(void *unused)
{
	busy_wait_verbose(500, "test_31_task_2");
	tmosalTaskExit();
}

static void test_31(void)
{
	tmosalTaskHandle_t handle1, handle2;
	tmErrorCode_t res;

	res = tmosalTaskCreate(test_31_task_1, 0, 40, 4096, &handle1,
		       	"T31a", 0);
	CHECK_OK(res, "test_31: tmosalTaskCreate() for task 1");

	res = tmosalTaskCreate(test_31_task_2, 0, 40, 4096, &handle2,
		       	"T31b", 0);
	CHECK_OK(res, "test_31: tmosalTaskCreate() for task 2");

	tmosalDisableScheduler();

	res = tmosalTaskStart(handle1);
	CHECK_OK(res, "test_31: tmosalTaskStart() for task 1");

	res = tmosalTaskStart(handle2);
	CHECK_OK(res, "test_31: tmosalTaskStart() for task 2");
	
	tmosalEnableScheduler();

	tmosalTaskSleep(3000);
}


/* 32. Memory allocation without alignment */

static void test_32(void)
{
	tmErrorCode_t res;
	void *p = 0;

	res = tmmlMalloc(0, 55, &p, 0);
	CHECK_OK(res, "test_32: tmmlMalloc()");
	printk("rest_32: returned address is %08x\n", (unsigned int)p);

	if (p) {
		res = tmmlFree(p);
		CHECK_OK(res, "test_32: tmmlFree()");
	}
}


/* 33. Memory allocation with alignment */

static void test_33(void)
{
	tmErrorCode_t res;
	void *p1 = 0, *p2 = 0;

	res = tmmlMallocAlign(0, 55, &p1, 0, 0x100);
	CHECK_OK(res, "test_33: first tmmlMallocAlign()");
	printk("test_33: returned address is %08x\n", (unsigned int)p1);

	res = tmmlMallocAlign(0, 55, &p2, 0, 0x108);
	CHECK_EXPECTED(res, TMML_ERR_BAD_PARAMETER,
			"test_33: invalid tmmlMallocAlign()");
	
	res = tmmlMallocAlign(0, 55, &p2, tmmlMallocCacheSet, 0x108);
	CHECK_OK(res, "test_33: second tmmlMallocAlign()");
	printk("test_33: returned address is %08x\n", (unsigned int)p2);

	if (p1) {
		res = tmmlFree(p1);
		CHECK_OK(res, "test_33: tmmlFree() first block");
	}

	if (p2) {
		res = tmmlFree(p2);
		CHECK_OK(res, "test_33: tmmlFree() second block");
	}
}


/* 34. Cache flushing demonstration */

static int addr_in_dcache(void *addr)
{
	/* This assumes 8k data cache, 4-way, 16-byte lines */

	unsigned long ia = ((unsigned long)addr & 0x7F0) | 0x80000000;
	int i, ways = 4, step = 0x800;
	unsigned long taglo;
	unsigned long valid_mask = 0x00000080;
	unsigned long tag_mask = 0xFFFFFC00;
	unsigned long expected = __pa(addr) & tag_mask;

	/*printk("checking for addr %08x: searching %08x tag\n",
		       	(unsigned int)addr, (unsigned int)expected);*/
	for (i = 0; i < ways; i++, ia += step) {
		asm volatile("cache 0x5, (%0)" :: "r"(ia));
		asm volatile("nop; nop; nop; nop;");
		taglo = read_c0_taglo();
		/*printk("tag at ia %08x is %08x\n",
			       (unsigned int)ia, (unsigned int)taglo);*/
		if (!(taglo & valid_mask)) {
			/*printk(" ... not valid\n");*/
			continue;
		}
		if ((taglo & tag_mask) != expected) {
			/*printk(" ... not expected tag\n");*/
			continue;
		}
		return 1;
	}
	
	return 0;
}

static inline void *kseg1(void *addr)
{
	return (int *)(((unsigned long)addr & 0x1fffffff) | 0xa0000000);
}

static inline void print_addr_state(void *addr, char *str)
{
	printk("%s: block %s dcache\n", str,
		       	addr_in_dcache(addr) ? "in" : "not in");
}

static void test_34(void)
{
	tmErrorCode_t res;
	volatile int *addr, *kseg1_addr;
	int size = 16;

	res = tmmlMallocAlign(0, size, (void *)&addr, 0, 16);
	CHECK_OK(res, "test_34: tmmlMallocAlign()");
	if (res != TM_OK)
		return;

	kseg1_addr = (volatile int *)kseg1((void *)addr);
	
	*kseg1_addr = 0x11111111;
	
	*addr = 0x22222222;
	print_addr_state((void *)addr, "test_34: after access");
	printk("test_34: *addr=0x%08x, *kseg1_addr=0x%08x\n", *addr, *kseg1_addr);
	
	tmosalFlushWithWB(tmosalCacheTypeData);
	print_addr_state((void *)addr, "test_34: after tmosalCacheFlushWithWB()");
	printk("test_34: *addr=0x%08x, *kseg1_addr=0x%08x\n", *addr, *kseg1_addr);

	*addr = 0x33333333;
	print_addr_state((void *)addr, "test_34: after access");
	printk("test_34: *addr=0x%08x, *kseg1_addr=0x%08x\n", *addr, *kseg1_addr);

	tmosalCacheInvalidateWithWB(tmosalCacheTypeData, (pUInt32)addr, size);
	print_addr_state((void *)addr, "test_34: after tmosalCacheInvalidateWithWB()");
	printk("test_34: *addr=0x%08x, *kseg1_addr=0x%08x\n", *addr, *kseg1_addr);

	*addr = 0x44444444;
	print_addr_state((void *)addr, "test_34: after access");
	printk("test_34: *addr=0x%08x, *kseg1_addr=0x%08x\n", *addr, *kseg1_addr);

	tmosalCacheInvalidateWithWB(tmosalCacheTypeData, (pUInt32)(addr + 32), size);
	print_addr_state((void *)addr, "test_34: after other address flush");
	printk("test_34: *addr=0x%08x, *kseg1_addr=0x%08x\n", *addr, *kseg1_addr);

	res = tmmlFree((void *)addr);
	CHECK_OK(res, "test_34: tmmlFree()");
}

#ifdef PERF_TESTING
static void stat(void)
{
	tmosalStatistic_t st;
	tmErrorCode_t res;

	res = tmosalGetStatistic(&st);
	CHECK_OK(res, "tmosalGetStatistic()");
	
	printk("stat.curQueueNr = %d\n", (int)st.curQueueNr);
	printk("stat.maxQueueNr = %d\n", (int)st.maxQueueNr);
	printk("stat.curMutexNr = %d\n", (int)st.curMutexNr);
	printk("stat.maxMutexNr = %d\n", (int)st.maxMutexNr);
	printk("stat.curEventNr = %d\n", (int)st.curEventNr);
	printk("stat.maxEventNr = %d\n", (int)st.maxEventNr);
	printk("stat.curSemaphoreNr = %d\n", (int)st.curSemaphoreNr);
	printk("stat.maxSemaphoreNr = %d\n", (int)st.maxSemaphoreNr);
	printk("stat.curTaskNr = %d\n", (int)st.curTaskNr);
	printk("stat.maxTaskNr = %d\n", (int)st.maxTaskNr);
	printk("stat.curTimerNr = %d\n", (int)st.curTimerNr);
	printk("stat.maxTimerNr = %d\n", (int)st.maxTimerNr);
}
#endif


static void allocate_many_objects(void)
{
	static tmosalMutexHandle_t ha[100];
	int i;
	tmErrorCode_t res;

	for (i = 0; i < 50; i++) {
		res = tmosalMutexCreate(&ha[2*i], tmosalMutexCreateFlagNone);
		CHECK_OK(res, "allocate_many_objects: tmosalMutexCreate() A");
		printk("Handle: %08x\n", (unsigned int)ha[2*i]);
		res = tmosalMutexCreate(&ha[2*i + 1], tmosalMutexCreateFlagNone);
		CHECK_OK(res, "allocate_many_objects: tmosalMutexCreate() B");
		printk("Handle: %08x\n", (unsigned int)ha[2*i + 1]);
		printk("Will destroy handle: %08x\n", (unsigned int)ha[i]);
		res = tmosalMutexDestroy(ha[i]);
		CHECK_OK(res, "allocate_many_objects: tmosalMutexDestroy() C");
	}

	for (; i < 100; i++) {
		printk("Will destroy handle: %08x\n", (unsigned int)ha[i]);
		res = tmosalMutexDestroy(ha[i]);
		CHECK_OK(res, "allocate_many_objects: tmosalMutexDestroy() D");
	}
}

static void task_exit_wait_task_1(void *unused)
{
	printk("task_exit_wait_task_1: started\n");
	tmosalTaskSleep(1000);
	printk("task_exit_wait_task_1: exitting\n");
	tmosalTaskExit();
}

static void task_exit_wait_task_2(void *unused)
{
	printk("task_exit_wait_task_2: started\n");
	tmosalTaskSleep(2000);
	printk("task_exit_wait_task_2: exitting\n");
	tmosalTaskExit();
}

static void task_exit_wait(void)
{
	tmosalTaskHandle_t handle1, handle2;
	tmErrorCode_t res;

	res = tmosalTaskCreate(task_exit_wait_task_1, 0, 50, 4096, &handle1,
		       	"TEW1", tmosalTaskStarted);
	CHECK_OK(res, "task_exit_wait: tmosalTaskCreate() for task_1");

	printk("task_exit_wait: waiting for task_1 ...\n");
	tmosalTaskWaitForExit(handle1);
	printk("task_exit_wait: wait returned\n");
	

	res = tmosalTaskCreate(task_exit_wait_task_1, 0, 50, 4096, &handle1,
		       	"TEW1", tmosalTaskStarted);
	CHECK_OK(res, "task_exit_wait: tmosalTaskCreate() for task_1");

	printk("task_exit_wait: sleeping for 2 seconds ...\n");
	tmosalTaskSleep(2000);
	printk("task_exit_wait: waken up\n");

	printk("task_exit_wait: waiting for task_1 ...\n");
	tmosalTaskWaitForExit(handle1);
	printk("task_exit_wait: wait returned\n");

	
	res = tmosalTaskCreate(task_exit_wait_task_1, 0, 50, 4096, &handle1,
		       	"TEWa", tmosalTaskStarted);
	CHECK_OK(res, "task_exit_wait: tmosalTaskCreate() for task_1");
	res = tmosalTaskCreate(task_exit_wait_task_2, 0, 50, 4096, &handle2,
		       	"TEWb", tmosalTaskStarted);
	CHECK_OK(res, "task_exit_wait: tmosalTaskCreate() for task_2");

	printk("task_exit_wait: waiting for task_2 ...\n");
	tmosalTaskWaitForExit(handle2);
	printk("task_exit_wait: wait returned\n");


	printk("task_exit_wait: trying to wait for 0x12345678 handle ...\n");
	tmosalTaskWaitForExit(0x12345678);
	printk("task_exit_wait: wait returned\n");
}

static struct {
       	const char *name;
       	void (*routine)(void);
} tests[] = {
	{ "empty", empty },
	{ "1", test_1 }, { "2", test_2 },
	{ "3", test_3 }, { "4", test_4 },
	{ "5", test_5 }, { "6", test_6 },
	{ "7", test_7 }, { "8", test_8 },
	{ "9", test_9 }, { "10", test_10 },
	{ "11", test_11 }, { "12", test_12 },
	{ "13", test_13 }, { "14", test_14 },
	{ "15", test_15 }, { "16", test_16 },
	{ "19", test_19 }, { "20", test_20 },
	{ "17", test_17 }, { "18", test_18 },
	{ "21", test_21 }, { "22", test_22 },
	{ "23", test_23 }, { "24", test_24 },
	{ "25", test_25 }, { "26", test_26 },
	{ "27", test_27 }, { "28", test_28 },
	{ "29", test_29 }, { "30", test_30 },
	{ "31", test_31 }, { "32", test_32 },
	{ "33", test_33 }, { "34", test_34 },
#ifdef PERF_TESTING
	{ "stat", stat },
#endif
	{ "many", allocate_many_objects },
	{ "wait", task_exit_wait },
	{ 0, 0 }
};

static int write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data)
{
	int i, n;
	
	for (n = 0; n < count && buffer[n] != '\n'; n++);

	for (i = 0; tests[i].name; i++) {
		if (n == strlen(tests[i].name) && !strncmp(tests[i].name, buffer, n)) {
			printk(KERN_INFO "osal_test: running test \"%s\"\n", tests[i].name);
			tests[i].routine();
			return count;
		}
	}
	printk(KERN_WARNING "osal_test: requested test not found\n");
	return -EINVAL;
}

static __init int osal_test_init(void)
{
	struct proc_dir_entry *proc = create_proc_entry("testno", 0222, &proc_root);
	if (!proc) {
		printk(KERN_ERR "osal_test: failed to create /proc/testno\n");
		return 0;	/* returning error here seems to confuse busybox,
				   that does no expect to have problems writing
				   to stdout */
	}
	proc->owner = THIS_MODULE;
	proc->write_proc = write_proc;

	printk(KERN_INFO "osal_test: module ready\n");
	return 0;
}

static __exit void osal_test_cleanup(void)
{
	remove_proc_entry("testno", &proc_root);
	printk(KERN_INFO "osal_test: module removed\n");
}

module_init(osal_test_init);
module_exit(osal_test_cleanup);
