// Developed from comedi7.c
/*----------------------------- include files --------------------------------*/
#include <rtai.h>
#include <rtai_sched.h>
#include <rtai_fifos.h>

#include <asm/io.h>
#include <math.h>
#include <linux/module.h>

// in comedi-0.7.7x
#include <linux/comedi.h>
#include <linux/comedilib.h>

#include <linux/string.h>

#define INTERVAL		1000000		// interval in ns
#define TASK_PRIORITY 	1
#define STACK_SIZE 		15000
#define FIFO 			0
#define N_CHANS 		50



/*-------------------------------- macros ------------------------------------*/
#define KEEP_STATIC_INLINE /* nam2num macro requires this definition */

#ifndef MODULE

#define  MODULE

#endif

#ifndef __KERNEL__

#define  __KERNEL__

#endif

#ifndef CONFIG_PCI

#error This kernel does not support PCI.

#endif

MODULE_LICENSE("GPL");




/*---------------------------- global variables ------------------------------*/
comedi_t *device;

static RT_TASK MainLoop;

unsigned range = 0;
unsigned aref = AREF_GROUND;
unsigned int chanlist[N_CHANS];




char *cmd_src(int src,char *buf)
{
	buf[0]=0;

	if(src&TRIG_NONE)strcat(buf,"none|");
	if(src&TRIG_NOW)strcat(buf,"now|");
	if(src&TRIG_FOLLOW)strcat(buf, "follow|");
	if(src&TRIG_TIME)strcat(buf, "time|");
	if(src&TRIG_TIMER)strcat(buf, "timer|");
	if(src&TRIG_COUNT)strcat(buf, "count|");
	if(src&TRIG_EXT)strcat(buf, "ext|");
	if(src&TRIG_INT)strcat(buf, "int|");
#ifdef TRIG_OTHER
	if(src&TRIG_OTHER)strcat(buf, "other|");
#endif

	if(strlen(buf)==0){
		sprintf(buf,"unknown(0x%08x)",src);
	}else{
		buf[strlen(buf)-1]=0;
	}

	return buf;
}



void dump_cmd(comedi_cmd *cmd)
{
	char buf[100];

	printk("\nflags:      %x\n", cmd->flags);
	printk("start:      %-8s %d\n",
		cmd_src(cmd->start_src,buf),
		cmd->start_arg);

	printk("scan_begin: %-8s %d\n",
		cmd_src(cmd->scan_begin_src,buf),
		cmd->scan_begin_arg);

	printk("convert:    %-8s %d\n",
		cmd_src(cmd->convert_src,buf),
		cmd->convert_arg);

	printk("scan_end:   %-8s %d\n",
		cmd_src(cmd->scan_end_src,buf),
		cmd->scan_end_arg);

	printk("stop:       %-8s %d\n",
		cmd_src(cmd->stop_src,buf),
		cmd->stop_arg);
}



unsigned WriteCount = 0;	// keeps track of how many bytes of data the module has written to FIFO

void TestFunc(int t)
{
#define SIZE	10000
char chBuf[SIZE], *pchComediBuf;

comedi_cmd c, *cmd = &c;
int i, j, ret, ofs;
int comedi_buf_size;
void* ptr;

	// prepare the comedi device
	device = comedi_open("/dev/comedi0");	// open a COMEDI device
	comedi_lock(device, 0);					// open a subdevice
	comedi_map(device, 0, &ptr);			// mmap the data buffer
	pchComediBuf = (char*)ptr;

	/* Set up channel list */
	for(i = 0; i < N_CHANS; i++)
	{
		chanlist[i] = CR_PACK(i, range, aref);
	}

	// set up the command
	cmd->subdev = 0;
	cmd->flags = 0;
	cmd->start_src			= TRIG_NOW;
	cmd->start_arg			= 0;
	cmd->scan_begin_src		= TRIG_TIMER;
	cmd->scan_begin_arg		= 0;	// For NI-PCI6071E
	cmd->convert_src		= TRIG_TIMER;
	cmd->convert_arg		= 0;
	cmd->scan_end_src		= TRIG_COUNT;
	cmd->scan_end_arg		= N_CHANS;
	cmd->stop_src			= TRIG_NONE;
	cmd->stop_arg			= 0;
	cmd->chanlist			= chanlist;
	cmd->chanlist_len		= N_CHANS;
	cmd->data				= 0;	// data and data_len are NOT used.
	cmd->data_len			= 0;

	if((ret = comedi_command_test(device, cmd)) < 0)
	{
		comedi_perror("First call to comedi_command_test");
		return;
	}
	printk("1st call to comedi_command_test returned %d\n", ret);

	if((ret = comedi_command_test(device, cmd)) < 0)
	{
		comedi_perror("Second call to comedi_command_test");
		return;
	}
	printk("2nd call to comedi_command_test returned %d\n", ret);

	if((ret = comedi_command_test(device, cmd)) < 0)
	{
		comedi_perror("Second call to comedi_command_test");
		return;
	}

	if(ret)
	{
		printk("3rd call to comedi_command_test returned %d\n", ret);
		return;
	}
	else
		printk("3rd call to comedi_command_test returned %d\n", ret);

	dump_cmd(cmd);

	printk("Streaming buffer size = %d\n", comedi_buf_size = comedi_get_buffer_size(device, 0));

	// purge the existing data from the buffer
	ret = comedi_get_buffer_contents(device, 0);
	printk("comedi_get_buffer_contents returned %d\n", ret);
	comedi_mark_buffer_read(device, 0, ret);

	// begin the sampling
	comedi_command(device, cmd);

	// Finally execute the command.
	while(1)
	{
		rt_task_wait_period();			// wait for INTERVAL ns

		// how many bytes have been acquired?
		if((ret = comedi_get_buffer_contents(device, 0)) != 0)
		{

			if(ret > sizeof chBuf) rt_printk("Data size exceeds the buffer (%d bytes)\n", ret);

			ofs = comedi_get_buffer_offset(device, 0);

			// copy the data to the buffer
			for(j = 0; j < ret; j++)
			{
				chBuf[j] = pchComediBuf[(ofs + j) % comedi_buf_size];
			}

			rtf_put(FIFO, chBuf, ret); WriteCount += ret;

			comedi_mark_buffer_read(device, 0, ret); // mark the buffer as read
		}

	}
}



int init_module(void)
{
RTIME tick_period;

	printk("-------------------Module K2Am.o loading-------------------\n");
	rt_set_periodic_mode();
	rt_task_init(&MainLoop, TestFunc, 1, STACK_SIZE, TASK_PRIORITY, 1, 0);
	rtf_create(FIFO, 8000); rtf_reset(FIFO);
	tick_period = start_rt_timer(nano2count(INTERVAL));

	printk("tick_period = %lld\n", tick_period);
	rt_task_make_periodic(&MainLoop, rt_get_time() + 2 * tick_period, tick_period);

	return 0;
}



void cleanup_module(void)
{
	comedi_cancel(device, 0);	// cancel the ongoing command

	printk("-------------------Module K2Am.o exiting-------------------\n");

	stop_rt_timer();
	rt_task_delete(&MainLoop);
	rtf_destroy(FIFO);

	// how many bytes have been written?
	printk("WriteCount = %d bytes\n", WriteCount);

	comedi_mark_buffer_read(device, 0, comedi_get_buffer_contents(device, 0));
	comedi_unmap(device, 0);	// unmap the data buffer
	comedi_unlock(device, 0);	// comedi_unlock fails without the preceding call to comedi_cancel
	comedi_close(device);		// close the COMEDI device

	return;
}

