/*
 * (C) Finite State Machine Labs Inc. 2000 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 <sys/types.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

#ifndef	hrtime_t
typedef long long int hrtime_t;
#endif				/* hrtime_t */

/* FIFO number to use */
#define	FIFO		0
/* number of tests to run */
#define NUM_TESTS	32
/* amount of time each test should take */
#define HANGTIME	50000000
#define HANGTIME_MS	HANGTIME / 1000000
#define HANGTIME_US	HANGTIME / 1000

/* module unload and loading commands */
#define	MODULE_LOAD	"/sbin/insmod regression/thread_time.o fifo=%d times=%d wait=%Ld"
#define MODULE_UNLOAD	"/sbin/rmmod thread_time"

char *construct_filename(int);
int stat_test(const char *);
int load_module(int, int, hrtime_t);
int unload_module();
void my_sahandler(int);
int sig_handler_setup();

int main(void)
{
	char *filename;
	int retval, filedes, all_times_sz, i;
	hrtime_t all_times[NUM_TESTS];
	hrtime_t cur_drift, avg_drift, diff_time, worst_drift;
	struct pollfd fifo_poll;

	avg_drift = 0;
	worst_drift = 0;
	all_times_sz = (sizeof(hrtime_t) * (NUM_TESTS + 1));

	unload_module();

	sig_handler_setup();

	if ((retval = load_module(FIFO, NUM_TESTS, HANGTIME)) < 0) {
		return (retval);
	}

	if ((filename = construct_filename(FIFO)) == NULL) {
		unload_module();
		return (-1);
	}

	if ((retval = stat_test(filename)) < 0) {
		unload_module();
		free(filename);
		return (retval);
	}

	if ((filedes = open(filename, O_RDONLY | O_NONBLOCK)) < 0) {
		fprintf(stderr, "open (%s, O_RDONLY): %s\n", filename,
			strerror(errno));
		unload_module();
		free(filename);
		return (errno);
	}

	fifo_poll.fd = filedes;
	fifo_poll.events = POLLIN;

	i = 0;
	while (i < all_times_sz) {
		if ((poll(&fifo_poll, 1, 2 * HANGTIME_MS * NUM_TESTS)) <=
		    0) {
			fprintf(stderr, "poll (&fifo_poll, 1, %d): %s\n",
				HANGTIME_MS * NUM_TESTS, strerror(errno));
			close(filedes);
			free(filename);
			unload_module();
			return (errno);
		}

		if (
		    (retval =
		     read(filedes, all_times + i, all_times_sz - i)) < 0) {
			fprintf(stderr, "read (%d, all_times, %d): %s\n",
				filedes, all_times_sz, strerror(errno));
			close(filedes);
			free(filename);
			unload_module();
			return (errno);
		}
		i += retval;
	}
	close(filedes);
	free(filename);
	unload_module();

	for (i = 1; i < NUM_TESTS; i++) {
		diff_time = all_times[i] - all_times[i - 1];
		cur_drift =
		    (diff_time <
		     HANGTIME) ? (HANGTIME -
				  diff_time) : (diff_time - HANGTIME);
		avg_drift += cur_drift;
		worst_drift =
		    (worst_drift < cur_drift) ? cur_drift : worst_drift;
	}

	avg_drift /= NUM_TESTS;
	/* check to see if the worst case time was 50000 nanoseconds (50
	 * microseconds) or more */

	fprintf(stderr,
		"avg_drift: %d.%02d us\tworst_drift: %d.%02d us\n",
		(long) avg_drift / 1000, (long) (avg_drift % 1000) / 10,
		(long) worst_drift / 1000,
		(long) (worst_drift % 1000) / 10);
	if (worst_drift >= 50000) {
		return (-1);
	}

	return (0);
}

int unload_module()
{
	if ((system(MODULE_UNLOAD)) != 0) {
		fprintf(stderr, "system (%s): %s\n", MODULE_UNLOAD,
			strerror(errno));
		return (errno);
	}

	return (0);
}

int load_module(int fifo_nr, int num_tests, hrtime_t wait)
{
	int i = strlen(MODULE_LOAD) + 32;
	char command[i];

	if (
	    (snprintf
	     (command, i, MODULE_LOAD, fifo_nr, num_tests, wait)) < 0) {
		fprintf(stderr, "snprintf (): %s", strerror(errno));
		return (errno);
	}

	if ((system(command)) != 0) {
		fprintf(stderr, "system (%s): %s\n", command,
			strerror(errno));
		return (errno);
	}

	return (0);
}

char *construct_filename(int i)
{
	char *filename;

	if ((filename = (char *) calloc(11, sizeof(char))) == NULL) {
		fprintf(stderr, "calloc (11, sizeof (char)): %s\n",
			strerror(errno));
		return (NULL);
	}

	if ((snprintf(filename, 11, "/dev/rtf%d", i)) < 0) {
		fprintf(stderr,
			"snprintf (filename, 11, /dev/rtf%d): %s",
			i, strerror(errno));
		free(filename);
		return (NULL);
	}

	return (filename);
}

int stat_test(const char *filename)
{
	struct stat file_stats;

	if (stat(filename, &file_stats) != 0) {
		fprintf(stderr, "stat (%s, &file_stats): %s\n",
			filename, strerror(errno));
		return (errno);
	}

	if (!(S_ISCHR(file_stats.st_mode))) {
		fprintf(stderr, "%s is not a character device.\n",
			filename);
		return (-1);
	}

	return (0);
}

int sig_handler_setup(void)
{
	struct sigaction *my_action;
	struct sigaction *old_action;

	if (
	    (my_action =
	     (struct sigaction *) calloc(1,
					 sizeof(struct sigaction)))
	    == NULL) {
		fprintf(stderr, "calloc (1, %d): %s\n",
			sizeof(struct sigaction), strerror(errno));
		return (errno);
	}

	my_action->sa_handler = &my_sahandler;

	if (
	    (old_action =
	     (struct sigaction *) calloc(1,
					 sizeof(struct sigaction)))
	    == NULL) {
		fprintf(stderr, "calloc (1, %d): %s\n",
			sizeof(struct sigaction), strerror(errno));
		return (errno);
	}

	sigaction(SIGHUP, my_action, old_action);
	sigaction(SIGINT, my_action, old_action);
	sigaction(SIGQUIT, my_action, old_action);

	return (0);
}

void my_sahandler(int whatever)
{
	fprintf(stderr, "my_sahandler: received signal %d\n", whatever);
	unload_module();
	exit(whatever);
}
