/*
 * Copyright (c) 2002-2006 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: dagio.c 11716 2009-08-06 02:47:33Z jomi $
 *
 * Reads ERF records directly from a DAG card.
 */

/* dagconvert headers. */
#include "inputs.h"
#include "utils.h"


/*
 * Input stream 
 */
#define DAGNAME_BUFSIZE 128

#if defined(__linux__) || defined(__FreeBSD__) || (defined(__SVR4) && defined(__sun)) || defined(__APPLE__)
static char uDagname[DAGNAME_BUFSIZE] = "/dev/dag0";
#elif defined(_WIN32)
static char uDagname[DAGNAME_BUFSIZE] = "\\\\.\\dag0";
#endif /* _WIN32 */
static int uDagfd;
static int uDagStream;

/*
 * Next payload.
 */
#if defined(__linux__) || defined(__FreeBSD__) || (defined(__SVR4) && defined(__sun)) || defined(__APPLE__)

static void *uPayload = NULL;
static void *uDagmem;

#elif defined(_WIN32)

static char *uPayload = NULL;
static char *uDagmem;

#endif /* Platform-specific code. */

static uint32_t uBottom;
static uint32_t uTop;

/* to be used with dag_ advance_ stream */
static uint8_t *uBottomPtr = NULL;
static uint8_t *uTopPtr = NULL;
/* for keeping the previous implementation . version 0 uses dag_offset, verseion 1 uses dag_rx_stream_next_record */
static int uDagConvertAPIMode = 1;
typedef dag_record_t*  (*funct_ptr_get_next_header_t)(void);
funct_ptr_get_next_header_t function_get_next_header = NULL;

static dag_record_t* get_next_dag_header_version0(void);
static dag_record_t* get_next_dag_header_version1(void);
static dag_record_t* get_next_dag_header_version2(void);
/*
 * Set the stream from which input is obtained.
 */
void
set_dag_input_version0(const char* name, int dagstream, char *args)
{
	struct timeval maxwait, poll;
	/* Setup device parameters. */
	uDagfd = dag_open((char*) name);
	if (uDagfd < 0)
		dagutil_panic("dag_open %s: %s\n", name, strerror(errno));

	if (dag_configure(uDagfd, args == NULL ? "" : args) < 0)
		dagutil_panic("dag_configure %s: %s\n", name, strerror(errno));

	uDagmem = dag_mmap(uDagfd);
	if (uDagmem == MAP_FAILED)
		dagutil_panic("dag_mmap %s: %s\n", name, strerror(errno));

	if (dag_start(uDagfd) < 0)
		dagutil_panic("dag_start %s: %s\n", name, strerror(errno));

	/*
	 * Important! You have to ensure uBottom is properly
	 * initialized to zero on startup, it won't give you
	 * a compiler warning if you make this mistake!
	 */
	uBottom = 0;
	uTop = 0;

	strncpy(uDagname, name, DAGNAME_BUFSIZE);
	
	/*
         * Initialise DAG Polling parameters.
         */
        timerclear(&maxwait);
        maxwait.tv_usec = 100 * 1000; /* 100ms timeout */
        timerclear(&poll);
        poll.tv_usec = 10 * 1000; /* 10ms poll interval */
	

        /* 16 bytes minimum data to return */
        dag_set_stream_poll(uDagfd, dagstream, 16, &maxwait, &poll);

}

void
set_dag_input_version1(const char* name, int dagstream, char *args)
{
	struct timeval maxwait, poll;
	/* Setup device parameters. */
    	if ((uDagfd = dag_open((char*) name)) < 0)
		dagutil_panic("dag_open %s: %s\n", name, strerror(errno));

	if (dag_configure(uDagfd, args == NULL ? "" : args) < 0)
		dagutil_panic("dag_configure %s: %s\n", name, strerror(errno));

	if (dag_attach_stream(uDagfd, dagstream, 0, 0) < 0)
		dagutil_panic("dag_attach_stream %s:%u: %s\n", name, dagstream, strerror(errno));

	if (dag_start_stream(uDagfd, dagstream) < 0)
		dagutil_panic("dag_start_stream %s:%u: %s\n", name, dagstream, strerror(errno));

	strncpy(uDagname, name, DAGNAME_BUFSIZE);
    	uDagStream = dagstream;
	
	/*
         * Initialise DAG Polling parameters.
         */
        timerclear(&maxwait);
        maxwait.tv_usec = 100 * 1000; /* 100ms timeout */
        timerclear(&poll);
        poll.tv_usec = 10 * 1000; /* 10ms poll interval */


        /* 16 bytes minimum data to return */
        dag_set_stream_poll(uDagfd, dagstream, 16, &maxwait, &poll);

}
void
set_dag_input(const char* name, int dagstream, char *args)
{
	switch ( uDagConvertAPIMode)
    {
        case 0:
             /* This version of APIs only support stream 0 capture */
            if ( dagstream != 0 )
                dagutil_panic("This API version supports only stream0 \n");
            function_get_next_header = &get_next_dag_header_version0;
            return set_dag_input_version0(name, dagstream, args);
        case 1:
            function_get_next_header = &get_next_dag_header_version1;
            return set_dag_input_version1(name, dagstream, args);
        case 2:
            function_get_next_header = &get_next_dag_header_version2;
            /*  initialize is the same as version 1 */
            return set_dag_input_version1(name, dagstream, args);
        default:
            dagutil_warning("Unknown API mode for dagconvert?Using the default \n");
            function_get_next_header = &get_next_dag_header_version1;
            return set_dag_input_version1(name, dagstream, args); 
    }
}
/*
 * Called when the DAG input is no longer needed so that any 
 * special shutdown tasks can be performed.
 */
void
close_dag_input(int verbose)
{
    switch (uDagConvertAPIMode)
    {
    case 0:
	   if ((dag_stop(uDagfd) < 0) && verbose)
		  dagutil_panic("dag_stop %s: %s\n", uDagname, strerror(errno));
        break;
    case 1:
            dag_stop_stream(uDagfd, uDagStream);
            dag_detach_stream(uDagfd, uDagStream);
            break;
    default:
            /* should not come here at all - assuming the version mode was 1*/
            dagutil_warning("Unknown API mode for dagconvert. Assuming it was default mode \n");
            dag_stop_stream(uDagfd, uDagStream);
            dag_detach_stream(uDagfd, uDagStream);
            break;
	}
	if ((dag_close(uDagfd) < 0) && verbose)
		dagutil_panic("dag_close %s: %s\n", uDagname, strerror(errno));
}

/*
 * Get pointer to the ERF header for the next packet
 * in the input stream. Returns null if no further
 * packets are available.
 */
dag_record_t *
get_next_dag_header_version0(void)
{
	register dag_record_t *record;
	int rlen;
	//int diff;

#ifdef PEDANTIC
	if (uDagfd < 0)
	{
		fprintf(stderr, "DAG not set up.\n");
		return NULL;
	}
#endif /* PEDANTIC */

	
	if (uTop - uBottom < dag_record_size)
	{
		uTop = dag_offset(uDagfd, (int*) &uBottom, 0);
	}

	if ((uTop - uBottom) < dag_record_size) return NULL;
	
	record = (dag_record_t *) (uDagmem + uBottom);
	rlen = ntohs(record->rlen);
	if (rlen == 20)
	{
		/* DS error truncates the packet, but the packet has effectively been padded to 28 bytes by the card. */
		rlen = 28;
	}

	while ((signed)(uTop - uBottom) < rlen)
	{
		uTop = dag_offset(uDagfd, (int*) &uBottom, 0);
		record = (dag_record_t *)(uDagmem + uBottom);
		rlen = ntohs(record->rlen);
		if (rlen == 20)
		{
			/* DS error truncates the packet, but the packet has effectively been padded to 28 bytes by the card. */
			rlen = 28;
		}
	}

	uPayload = ((char *) record) + dag_record_size;
	uBottom += rlen;

	return record;
}

dag_record_t *
get_next_dag_header_version1(void)
{
    register dag_record_t *record;
    int rlen;
    
    if (uTopPtr - uBottomPtr < dag_record_size)
    {
        uTopPtr = dag_advance_stream(uDagfd, uDagStream,  &uBottomPtr);
    }
    
    if ((uTopPtr - uBottomPtr) < dag_record_size) return NULL;
    
    record = (dag_record_t *) uBottomPtr;
    rlen = ntohs(record->rlen);
    if (rlen == 20)
    {
        /* DS error truncates the packet, but the packet has effectively been padded to 28 bytes by the card. */
        rlen = 28;
    }
    
    while ((uTopPtr - uBottomPtr) < rlen)
    {
        uTopPtr = dag_advance_stream(uDagfd, uDagStream,  &uBottomPtr);
        record = (dag_record_t *) uBottomPtr;
        rlen = ntohs(record->rlen);
        if (rlen == 20)
        {
            /* DS error truncates the packet, but the packet has effectively been padded to 28 bytes by the card. */
            rlen = 28;
        }
    }
    
    uPayload = ((char *) record) + dag_record_size;
    uBottomPtr += rlen;
    
    return record;
}

static dag_record_t *
get_next_dag_header_version2(void)
{
	register dag_record_t *record = NULL;
	int rlen;

    record = (dag_record_t*) dag_rx_stream_next_record(uDagfd, uDagStream);
    if (record) 
    {
        rlen = ntohs(record->rlen);

        if (rlen == 20)
        {
            rlen = 28;
            printf("warning: len change 20->28\n");
        }
        uPayload = ((char *) record) + dag_record_size;
    }
	return record;
}

dag_record_t *
get_next_dag_header(void)
{
	return function_get_next_header();
}

/*
 * Returns a pointer to the payload of the most recent
 * packet. Returns null if there is no current packet.
 */
void *
get_dag_payload(void)
{
	return uPayload;
}

void set_capture_api_version( int version )
{
    if ( version >= 0 ) 
        uDagConvertAPIMode = version;

}
