#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>

#include <rtl_sched.h>

#define KEYBOARD_INTERRUPT  1

#define EXEC 11
#define ACTIV 22
#define OFFSET 750000
#define NTASKS 4

#define DELAY         10000

#define N_ITERS       10000
pthread_t tasks[NTASKS + 1];

pthread_attr_t attrib;
struct {
    int id;
    int compute;
    int period;
    hrtime_t deadline;
    hrtime_t budget;
    struct sched_param params;
} sched_attrib[NTASKS + 1];

hrtime_t now;

MODULE_PARM(irq_to_request,"i");

static int irq_to_request =  KEYBOARD_INTERRUPT;

void *fun(void *arg)
{
    int id = (int) arg;
    int compute = sched_attrib[id].compute;
    int dummy_loop, x;
    int end = N_ITERS;


    x = pthread_make_periodic_np(pthread_self(), now + OFFSET,
				 sched_attrib[id].period);
    while (end != 0) {
	pthread_wait_np();

	for (dummy_loop = 0; dummy_loop < compute; dummy_loop++) {
	    rtl_delay(DELAY);
	}
	end--;
    }

    pthread_exit(0);
    return (void *) 0;
}

// ----------------- Interrupt Handler -------------------
unsigned my_keyboard_interrupt_handler(unsigned int irq,
				       struct pt_regs *regs)
{

    make_linux_task_cbs_server(gethrtime(),
			       sched_attrib[4].budget,
			       sched_attrib[4].deadline,
			       sched_attrib[4].period,
			       sched_attrib[4].params.sched_priority);
    rtl_global_pend_irq(irq_to_request);

    return 0;
}

int init_module(void)
{
    int x;

    now = gethrtime();

    sched_attrib[0].compute = 80;
    sched_attrib[0].period = 6 * 1000 * 1000;
    sched_attrib[0].params.sched_priority = 10;
    sched_attrib[0].deadline = 6 * 1000 * 1000;

    sched_attrib[1].compute = 240;
    sched_attrib[1].period = 10 * 1000 * 1000;
    sched_attrib[1].params.sched_priority = 10;
    sched_attrib[1].deadline = 10 * 1000 * 1000;

    sched_attrib[2].compute = 300;
    sched_attrib[2].period = 11 * 1000 * 1000;
    sched_attrib[2].params.sched_priority = 10;
    sched_attrib[2].deadline = 11 * 1000 * 1000;

    sched_attrib[3].compute = 350;
    sched_attrib[3].period = 19 * 1000 * 1000;
    sched_attrib[3].params.sched_priority = 10;
    sched_attrib[3].deadline = 19 * 1000 * 1000;


    sched_attrib[4].period = 3 * 1000 * 1000;
    sched_attrib[4].params.sched_priority = 10;
    sched_attrib[4].deadline = 3 * 1000 * 1000;
    sched_attrib[4].budget = 510 * 1000;


    for (x = 0; x < NTASKS; x++) {

	pthread_attr_init(&attrib);

	pthread_attr_setschedparam(&attrib, &sched_attrib[x].params);
	pthread_attr_setdeadline_np(&attrib, sched_attrib[x].deadline);
	pthread_attr_setschedpolicy(&attrib, SCHED_EDF_NP);
	pthread_create(&(tasks[x]), &attrib, fun, (void *) x);

	pthread_attr_destroy(&attrib);
    }

    rtl_request_irq(irq_to_request, my_keyboard_interrupt_handler);
    return 0;
}

void cleanup_module(void)
{
    int x;

    rtl_free_irq(irq_to_request);
    for (x = 0; x < NTASKS; x++) {
	pthread_cancel(tasks[x]);
	pthread_join(tasks[x], NULL);
    }
}
