/*
 * (C) Finite State Machine Labs Inc. 2001 business@fsmlabs.com
 *
 * Released under the terms of GPL 2.
 * Open RTLinux makes use of a patented process described in
 * US Patent 5,995,745. Use of this process is governed
 * by the Open RTLinux Patent License which can be obtained from
 * www.fsmlabs.com/PATENT or by sending email to
 * licensequestions@fsmlabs.com
 */

#include <rtl.h>
#include <pthread.h>
#include <rtl_sched.h>
#include <rtl_time.h>
#include <rtl_fifo.h>

#include "switch_test.h"

#define MODULE_NAME	"switch_time"

MODULE_AUTHOR("Nathan Paul Simons <npsimons@fsmlabs.com>");
MODULE_DESCRIPTION
    ("RTLinux context switch time regression test kernel module");

int fifo_nr = 1, fifo_sz = 8192;

MODULE_PARM(fifo_sz, "i");
MODULE_PARM_DESC(fifo_sz, "Size of FIFO.");
MODULE_PARM(fifo_nr, "i");
MODULE_PARM_DESC(fifo_nr,
		 "RTL-FIFO number to create (ie, 0 -> /dev/rtf0)");

hrtime_t time1[num_tests], time2[num_tests], elapsed[num_tests];
static pthread_t thread0, thread1, thread2;

char *rtl_strerr(int thiserr)
{
	switch (thiserr) {
	case EINVAL:
		return "EINVAL";
	case EDEADLK:
		return "EDEADLK";
	case EPERM:
		return "EPERM";
	case -ENODEV:
		return "-ENODEV";
	case -EINVAL:
		return "-EINVAL";
	case -EPERM:
		return "-EPERM";
	case -ESRCH:
		return "-ESRCH";
	case -EFAULT:
		return "-EFAULT";
	case -EBUSY:
		return "-EBUSY";
	case -ENOMEM:
		return "-ENOMEM";
	case -ENOSPC:
		return "-ENOSPC";
	case -EAGAIN:
		return "-EAGAIN";
	default:
		return "unknown error";
	}
}

void *handler1(void *arg)
{
	int i;

	for (i = 0; i < num_tests; i++) {
		pthread_suspend_np(pthread_self());
		time1[i] = clock_gethrtime(CLOCK_REALTIME);
		pthread_wakeup_np(thread2);
	}

	return NULL;
}

void *handler2(void *arg)
{
	int i;

	for (i = 0; i < num_tests; i++) {
		pthread_suspend_np(pthread_self());
		time2[i] = clock_gethrtime(CLOCK_REALTIME);
	}

	return NULL;
}

void *starter(void *arg)
{
	int retval, i, copy_size;
	struct sched_param my_sparam;

	copy_size = (sizeof(hrtime_t) * (num_tests));

	my_sparam.sched_priority = 3;

	if (
	    (retval =
	     pthread_setschedparam(pthread_self(), SCHED_FIFO,
				   &my_sparam)) != 0) {
		rtl_printf("%s: pthread_setschedparam(): %s\n",
			   MODULE_NAME, rtl_strerr(retval));
		return NULL;
	}

	pthread_make_periodic_np(pthread_self(),
				 clock_gethrtime(CLOCK_REALTIME), 500000);

	for (i = 0; i < num_tests; i++) {
		pthread_wakeup_np(thread1);

		pthread_wait_np();
	}

	if ((retval = rtf_put(fifo_nr, time1, copy_size)) < copy_size) {
		rtl_printf("%s: rtf_put(%d, time1, %d): %s\n", MODULE_NAME,
			   fifo_nr, copy_size, rtl_strerr(retval));
		return (void *) retval;
	}

	if ((retval = rtf_put(fifo_nr, time2, copy_size)) < copy_size) {
		rtl_printf("%s: rtf_put(%d, time2, %d): %s\n", MODULE_NAME,
			   fifo_nr, copy_size, rtl_strerr(retval));
		return (void *) retval;
	}

	return NULL;
}

int init_module(void)
{
	int retval;

	rtf_destroy(fifo_nr);

	if ((retval = rtf_create(fifo_nr, fifo_sz)) != 0) {
		rtl_printf("%s: rtf_create(%d, %d): %s\n", MODULE_NAME,
			   fifo_nr, fifo_sz, rtl_strerr(retval));
		return retval;
	}

	if ((retval = pthread_create(&thread1, NULL, handler1, 0))) {
		rtl_printf
		    ("%s: pthread_create(&thread1, NULL, handler1, 0): %s\n",
		     MODULE_NAME, rtl_strerr(retval));
		return retval;
	}

	if ((retval = pthread_create(&thread2, NULL, handler2, 0))) {
		rtl_printf
		    ("%s: pthread_create(&thread2, NULL, handler2, 0): %s\n",
		     MODULE_NAME, rtl_strerr(retval));
		return retval;
	}

/*	pthread_wakeup_np(thread1); */
	if ((retval = pthread_create(&thread0, NULL, starter, 0))) {
		rtl_printf
		    ("%s: pthread_create(&thread0, NULL, starter, 0): %s\n",
		     MODULE_NAME, rtl_strerr(retval));
		return retval;
	}

	return 0;
}

void cleanup_module(void)
{
	rtf_destroy(fifo_nr);

/*	elapsed = time2 - time1;
	rtl_printf("time1: %u    time2: %u    elapsed: %u\n", time1,
		   time2, elapsed); */
}
