/*
 * dm_ramwatch.c - This program is meant to watch RAM on the device and
 *                 see if the card stops running.  If so, a memory dump 
 *                 is taken.  
 *                 
 *                 In addition, a non-intrusive memory dump can be taken
 *                 at to-the-minute intervals.
 *                 
 *                 Also, a single memory dump can be taken with the 
 *                 -s option
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/time.h>

#include "feptypes.h"
#include "triglbl.h"
#include "dxbglbl.h"
#include "fepglbl.h"
#include "hou2mmap.h"

typedef struct {
	uint32 offset;		/* Offset of memory area to save */
	uint32 length;		/* Length of memory area to save */
} CHUNKENTRY;

void usage(void);

char Device[512] = "/dev/dg/dgdm/ram0";
char Outram[512] = "/tmp/ram";

int check_heartbeat(GLOBAL_FEP_DATA *gfd);

int main(int argc, char *argv[])
{
	int verbose=0;
	FILE *outfd;
	char *vaddr;
	ulong gco;
	GLOBAL_FEP_DATA *gfd;
	COREINFO *ci;
	int devicefd, c, nodecount, i, j, gapwords;
	CHUNKENTRY *chunklist;
	long currentoffset, rc, memsize=0;
	unsigned int core_constant = CORE_CONSTANT;
	char junkbuf[4096];
	int interval = 1;
	int single = 0;

	while((c=getopt(argc, argv, "vsi:d:o:")) != EOF) {
		switch(c) {
			case 'd':
				strcpy(Device, optarg);
				break;
			case 'o':
				strcpy(Outram, optarg);
				break;
			case 's':
				single = 1;
				break;
			case 'v':
				verbose = 1;
				break;
			case 'i':
				interval = strtoul(optarg, NULL, 0);
				break;
			default:
				usage();
		}
	}


	if((devicefd=open(Device, O_RDONLY)) < 0) {
		fprintf(stderr, "Can't open '%s': %s\n", Device, 
			strerror(errno));
		exit(1);
	}


	if(verbose)
		printf("Board's memory size in bytes = %ld\n", 8*1024*1024);

	vaddr = (uchar *) mmap((caddr_t) 0, 1024*1024, PROT_READ, 
					MAP_SHARED, devicefd, 0x00000000);

	if(vaddr == (void *)-1) {
		fprintf(stderr, "Can't mmap '%s': %s\n", 
			Device, strerror(errno));
		exit(2);
	}

	/*
	 *	Get pointer to global common area
	 */
	gco = * (ulong *) (vaddr + GLOBAL_DATA_OFFSET);

	if(gco == 0) {
		fprintf(stderr, "Error: Invalid Global Data Offset value!\n");
		exit(3);
	}

	gfd = (GLOBAL_FEP_DATA *) (vaddr + gco);

	if(gfd->common.magic != MAGIC_FEP) {
		fprintf(stderr, "Error: Global FEP Data Magic incorrect!\n");
		exit(4);
	}

	if (single) {
		dump_memory(Outram, devicefd);
		exit(0);
	}

	while (1) { 
		if (! check_heartbeat(gfd))  {
			fprintf(stderr, "adapter stopped\n");
			dump_memory(Outram, devicefd);
			exit(-1);
			break;
		}
		sleep (interval * 60);
	}

	exit(0);

}

int check_heartbeat(GLOBAL_FEP_DATA *gfd) 
{
	int heartbeat, newbeat;

	heartbeat = gfd->common.heartbeat;

	take_a_nap(100000);

	newbeat = gfd->common.heartbeat;

	if (newbeat != heartbeat)
		return(1);
	else
		return(0);
}

void usage(void)
{
	fprintf(stderr, 
"usage: dm_ramwatch [ -s ] [-i interval] [ -d <device> ] [ -o <ram_outfile> ]
	interval    -- is zero (not used) by default
	ram_device  -- is /dev/dg/dgdm/ram0 by default
	ram_outfile -- is the file that will contain any memory dumps (/tmp/ram)
	-s          --  single memory dump to ram_outfile \n");
	exit(1);
}


take_a_nap(int naptime)
{
	long num = 0;
	struct timeval timeval1 = {0,0};
  
	timeval1.tv_usec = naptime;
	select(1,0,0,0,&timeval1);
}

dump_memory(char *dumpfilename, int devicefd)
{
	char *vaddr;
	int i, len = 8*1024*1024;
	FILE *outfd;
	int chunks = 1024; 
	int chunksize = len / chunks; 

	vaddr = (uchar *) mmap((caddr_t) 0, len, PROT_READ, 
					MAP_SHARED, devicefd, 0x00000000);

	/*
	 * stdio fopen() used for its buffering ability
	 */
	if((outfd=fopen(dumpfilename, "w")) == NULL) {
		fprintf(stderr, "Can't open '%s': %s\n", 
			dumpfilename, strerror(errno));
		exit(1);
	}

	for (i = 0 ; i < chunks ; i++ ) {
		fwrite(vaddr + (i * chunksize), 1, chunksize, outfd);
		take_a_nap(1000);
	}

	fclose(outfd);

}
