//    Copyright 2003 John Kasunich
//
//    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; either version 2 of the License, or
//    (at your option) any later version.
//
//    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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
/*
  shmemtask.c

  Set up a periodic task that increments a heartbeat in shared memory.
*/

#include "rtapi.h"
#include "rtapi_app.h"		/* rtapi_app_main,exit() */
#include "common.h"		/* shmem structure, SHMEM_KEY */

static int module;
static int shmem_task;		/* the task ID */
static int shmem_mem;		/* the shared memory ID */
enum { TIMER_PERIOD_NSEC = 1000000 };	/* timer period, in nanoseconds */
enum { SHMEM_PERIOD_NSEC = 1000000 };	/* task period, in nanoseconds */
enum { SHMEM_STACKSIZE = 1024 };	/* how big the stack is */

static int key = SHMEM_KEY;
static SHMEM_STRUCT *shmem_struct = 0;

/* task code, executed periodically */
void shmem_code(void *arg)
{
    while (1) {
	if (0 != shmem_struct) {
	    shmem_struct->heartbeat++;
	}
	rtapi_wait();
    }

    return;
}

/* part of the Linux kernel module that kicks off the shmem task */
int rtapi_app_main(void)
{
    int retval;
    int shmem_prio;
    long period;

    module = rtapi_init("SHMEMTASK");
    if (module < 0) {
	rtapi_print("shmemtask init: rtapi_init returned %d\n", module);
	return -1;
    }
    /* allocate and initialize the shared memory structure */
    shmem_mem = rtapi_shmem_new(key, module, sizeof(SHMEM_STRUCT));
    if (shmem_mem < 0) {
	rtapi_print("shmemtask init: rtapi_shmem_new returned %d\n",
	    shmem_mem);
	rtapi_exit(module);
	return -1;
    }
    retval = rtapi_shmem_getptr(shmem_mem, (void **) &shmem_struct);
    if (retval < 0) {
	rtapi_print("shmemtask init: rtapi_shmem_getptr returned %d\n",
	    retval);
	rtapi_exit(module);
	return -1;
    }

    shmem_struct->heartbeat = 0;

    /* is timer started? if so, what period? */
    period = rtapi_clock_set_period(0);
    if (period == 0) {
	/* not running, start it */
	rtapi_print("shmemtask init: starting timer with period %ld\n",
	    TIMER_PERIOD_NSEC);
	period = rtapi_clock_set_period(TIMER_PERIOD_NSEC);
	if (period < 0) {
	    rtapi_print
		("shmemtask init: rtapi_clock_set_period failed with %ld\n",
		period);
	    rtapi_exit(module);
	    return -1;
	}
    }
    /* make sure period <= desired period (allow 1% roundoff error) */
    if (period > (TIMER_PERIOD_NSEC + (TIMER_PERIOD_NSEC / 100))) {
	/* timer period too long */
	rtapi_print("shmemtask init: clock period too long: %ld\n", period);
	rtapi_exit(module);
	return -1;
    }
    rtapi_print("shmemtask init: desired clock %ld, actual %ld\n",
	TIMER_PERIOD_NSEC, period);

    /* set the task priority to lowest, since we only have one task */
    shmem_prio = rtapi_prio_lowest();

    /* create the shmem task */
    shmem_task = rtapi_task_new(shmem_code, 0 /* arg */ , shmem_prio, module,
	SHMEM_STACKSIZE, RTAPI_NO_FP);
    if (shmem_task < 0) {
	rtapi_print("shmemtask init: rtapi_task_new returned %d\n",
	    shmem_task);
	rtapi_exit(module);
	return -1;
    }

    /* start the shmem task */
    retval = rtapi_task_start(shmem_task, SHMEM_PERIOD_NSEC);
    if (retval < 0) {
	rtapi_print("shmemtask init: rtapi_task_start returned %d\n", retval);
	rtapi_exit(module);
	return -1;
    }

    rtapi_print("shmemtask init: started shmem task\n");

    return 0;
}

/* part of the Linux kernel module that stops the shmem task */
void rtapi_app_exit(void)
{
    int retval;

    if (0 != shmem_struct) {
	rtapi_print("shmemtask exit: heartbeat is %u\n",
	    shmem_struct->heartbeat);
    }

    retval = rtapi_task_pause(shmem_task);
    if (retval < 0) {
	rtapi_print("shmemtask exit: rtapi_task_stop returned %d\n", retval);
    }
    retval = rtapi_task_delete(shmem_task);
    if (retval < 0) {
	rtapi_print("shmemtask exit: rtapi_task_delete returned %d\n",
	    retval);
    }
    retval = rtapi_shmem_delete(shmem_mem, module);
    if (retval < 0) {
	rtapi_print("shmemtask exit: rtapi_shmem_delete returned %d\n",
	    retval);
    }
    /* Clean up and exit */
    rtapi_exit(module);
}
