/*
 * Copyright (c) 2004-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: dagipf.c 7095 2007-07-04 03:38:52Z vladimir $
 */

/** \file
 * \brief DAG IP packet filtering.
 *
 * This is a stub for doxygen.
 *
 * Old "CAM entry" format:
 * -----------------------
 * 
 * 0   7:0      protocol
 *      23:8     Ethertype
 *      31:24    TCP flags
 * 
 * 1   63:32    source address
 * 
 * 2   95:64    destination address
 * 
 * 3   111:96   destination port
 *      127:112  source port
 * 
 * The actual 4-byte words are loaded in reverse order, i.e.
 * 
 * 0 <- 3
 * 1 <- 2
 * 2 <- 1
 * 3 <- 0
 * 
 * The top 4 bits in each 36-bit entry contain the port and ruleset:
 * 
 * 3:2  ruleset
 * 1:0  port
 * 
 * 
 *
 */

/* File header. */
#include "dagipf.h"

/* IP filtering headers. */
#include "dagipf_types.h"

/* Endace headers. */
#include "dagapi.h"
#include "dagreg.h"
#include "dag_smbus.h"
#include "dagutil.h"

#if defined (_WIN32)
#include "Windows.h"
#endif /* _WIN32 */


/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagipf.c 7095 2007-07-04 03:38:52Z vladimir $";


#define MAGIC_NUMBER 0x13578642
#define STRINGS_PER_BLOCK 680
#define MAX_TIMEOUT 1000
#define CSR_NAP_NANOS 2000
#define REGISTER_NAP_NANOS 1000
#define CAM_ENTRIES_36 65536
#define CAM_ENTRIES_72 (CAM_ENTRIES_36 / 2)
#define CAM_ENTRIES_144 (CAM_ENTRIES_36 / 4)
#define CAM256_ENTRIES_72 65536
#define CAM256_ENTRIES_144 (CAM256_ENTRIES_72/2)
#define CAM256_ENTRIES_288 (CAM256_ENTRIES_72/4)
#define CAM256_ENTRIES_576 (CAM256_ENTRIES_72/8)

/* Tags are needed to differentiate:
 *   72 bit searches: Payload vs Register B
 *   36 bit searches: Registers C, D, E
 */
#define REGISTER_P_TAG 0x0
#define REGISTER_B_TAG 0x1
#define REGISTER_C_TAG 0x2
#define REGISTER_D_TAG 0x3
#define REGISTER_E_TAG 0x4

/* FIXME: Enable content searching when it is finished. */
#define HEADER_FILTER_ONLY 1

/* 1 => use nanosleeps, 0 => don't use nanosleeps. 
 * Nanosleeps were present because the dagcam code had them.
 * Further investigation has shown them to be unnecessary, so they are disabled.
 */
#define USE_NANOSLEEPS 0

/* Check CAM BIST. Not fully tested. */
#define DO_BIST 0

/* Temporary method for providing persistent storage for port/ruleset configuration. 
 * Has been superseded by the use of shared memory.
 */
#if !defined(STATE_FILE_HACK)
#define STATE_FILE_HACK 0
#endif /* !defined(STATE_FILE_HACK) */

/* Read back CAM registers after they've been written. */
#define READ_REGISTERS 1

/* number of retries when checking busy bit in CAM read/write */
#define TIMEOUT 10 /* FIXME - what value is needed here??? */
static const char* uErrorStrings[] = 
{
	"no error",
	"feature unimplemented",
	"coprocessor unavailable or not present",
	"invalid state",
	"invalid search register",
	"invalid parameter",
	"too many searches",
	"CAM overflow - CAM memory is full",
	"out of memory",
    "TCAM verification failed"
};


typedef struct string_block string_block_t;
struct string_block
{
	uint32_t entries;
	string_block_t* next;
	const char* search[STRINGS_PER_BLOCK];
	uint16_t search_length[STRINGS_PER_BLOCK];
};


/* Max currently supported is (2 ports) x (2 rulesets per port) i.e. active/inactive per port. */
#define PORT_CONFIG_BUCKETS 4
typedef struct cam_bucket_t cam_bucket_t;
struct cam_bucket_t
{
	uint32_t size;           /* Maximum number of entries in this bucket. */
	uint8_t port;            /* Which port this bucket of entries applies to. */
	uint8_t ruleset;         /* Which ruleset this bucket of entries applies to. */
	uint16_t pattern_count;  /* How many patterns are currently in this bucket. */
	pattern144_t* patterns;  /* Patterns for this bucket. */
};


typedef struct
{
	uint64_t value; /* Only least significant 36 bits are used. */

} cam_word_t;

typedef struct FilterStateHeader
{
	uint32_t magic_number;
	int dagfd;                                 /* File descriptor for the DAG. */
	uint32_t flags;                            /* Flags that modify behaviour. */
	u_char* dagiom;                            /* I/O memory for the DAG. */
	volatile uint32_t* copro_register_base;    /* Register space for the coprocessor. */
	volatile uint32_t* cam_control_status_reg; /* Control status register for CAM. */
	volatile uint32_t* cam_register_base;      /* Register space for CAM. */
	volatile uint32_t* smb_register_base;      /* SMBus controller (fan). */
	uint16_t next_location;                    /* Next free 36-bit word index in CAM memory. */
	uint8_t wide_bus;                          /* Wide bus flag (read from CAM control status register). */
	coprocessor_t copro_type;                  /* copro type */
	uint8_t synchronized;                      /* User input is in sync with CAM and coprocessor. */
    uint8_t module_version;                    /** Firmware module version */
    uint8_t cam_busy;

	/* User input: payload searches. */
	string_block_t* string_head;

	/* User input: header search templates. */
	search_record144_t* register_a_search;

	/* User input: port and ruleset configuration. */
	uint8_t ports;
	uint8_t rulesets;

	/* User input: search patterns. */
	cam_bucket_t search_patterns[PORT_CONFIG_BUCKETS]; 

	/* Coprocessor state. */
	ip_copro_registers_t registers;	
	char* header_mask_memory[4];
	char* header_colour_memory;
	char* string_colour_memory;
	char* output_memory;
	cam_word_t cam_memory[CAM_ENTRIES_36]; /* Model contents of SiberCore 2Mbit CAM (entries are 36 bits each). */

} FilterStateHeader, *FilterStateHeaderPtr;


/* State for shared memory.  Use 32-bit ints for all fields to prevent packing/alignment problems.
 * We can't allocate less than a page (typically 4K) of shared memory anyway, 
 * so there's no point using minimum-sized types.
 */
typedef struct 
{
	uint32_t magic_number;
	uint32_t port_count;
	uint32_t ruleset_count;

} shm_state_t;


/* Key and magic number for shared memory accesses, both randomly generated. */
#define SHM_MAGIC_NUMBER 0x3a0aa782
#define SHM_KEY 0x397d9d17


/* Internal routines. */
static uint32_t valid_header(FilterStateHeaderPtr header);
static void dispose_header(FilterStateHeaderPtr header);
static string_block_t* new_string_block(void);
static void dispose_string_block(string_block_t* search_string_array);
static void enable_coprocessor(FilterStateHeaderPtr header);
static void disable_coprocessor(FilterStateHeaderPtr header);
static void enable_datapath(FilterStateHeaderPtr header);
static void disable_datapath(FilterStateHeaderPtr header);
static void write_cam_csr(FilterStateHeaderPtr header, uint32_t read_count, uint32_t write_count, uint32_t go);
static void write_cam_register(FilterStateHeaderPtr header, uint32_t addr, uint64_t value);
static void write_256cam_value(FilterStateHeaderPtr header, uint32_t addr,bit72_word_t value,uint8_t space);
static bit72_word_t read_256cam_value(FilterStateHeaderPtr header, uint32_t addr, uint8_t space_val);
static dagipf_error_t load_coprocessor_state(FilterStateHeaderPtr header);
static dagipf_error_t load_coprocessor256_state(FilterStateHeaderPtr header);
static uint32_t install_in_cam144(FilterStateHeaderPtr header, uint32_t search_count, pattern144_t* searches, uint32_t cam_location);
static uint32_t install_in_256cam144(FilterStateHeaderPtr header, uint32_t search_count, pattern144_t* searches, uint32_t cam_location);
static int verify_256cam144(FilterStateHeaderPtr header, uint32_t search_count, pattern144_t* searches, uint32_t cam_location);
static void install_register_mapping144(FilterStateHeaderPtr header, search_record144_t* search);
static void configure_rulesets(FilterStateHeaderPtr header, uint8_t ports, uint8_t rulesets);
#if STATE_FILE_HACK
static void write_state_file(uint8_t ports, uint8_t rulesets);
static void read_state_file(uint8_t* ports, uint8_t* rulesets);
#endif /* STATE_FILE_HACK */
static void write_state_shm(uint8_t ports, uint8_t rulesets);
static void read_state_shm(uint8_t* ports, uint8_t* rulesets);


/* Implementation of internal routines. */
static uint32_t
valid_header(FilterStateHeaderPtr header)
{
	if (header && (header->magic_number == MAGIC_NUMBER))
	{
		return 1;
	}

	assert(0);
	return 0;
}


static void
dispose_header(FilterStateHeaderPtr header)
{
	uint32_t region;

	for (region = 0; region < 4; region++)
	{
		if (header->header_mask_memory[region])
		{
			free(header->header_mask_memory[region]);
		}
	}

	while (header->string_head)
	{
		string_block_t* current = header->string_head;

		header->string_head = current->next;
		dispose_string_block(current);
	}

	dagipf_dispose_144bit_search(header->register_a_search);

	if (header->header_colour_memory)
	{
		free(header->header_colour_memory);
	}

	if (header->string_colour_memory)
	{
		free(header->string_colour_memory);
	}

	if (header->output_memory)
	{
		free(header->output_memory);
	}

	memset(header, 0xec /* Big, odd, distinctive. */, sizeof(FilterStateHeader));
	free(header);
}


static string_block_t*
new_string_block(void)
{
	string_block_t* result = (string_block_t*) malloc(sizeof(string_block_t));

	if (result)
	{
		memset(result, 0, sizeof(string_block_t));
	}

	return result;
}


static void
dispose_string_block(string_block_t* search_string_array)
{
	uint32_t entry;

	for (entry = 0; entry < search_string_array->entries; entry++)
	{
		if (search_string_array->search[entry])
		{
			free((char*) search_string_array->search[entry]);
		}
	}

	memset(search_string_array, 0, sizeof(string_block_t));
	free(search_string_array);	
}


static void
enable_coprocessor(FilterStateHeaderPtr header)
{
    /* Start coprocessor by setting GO bit high. */
    header->copro_register_base[0] |= 0x01;
}


static void
disable_coprocessor(FilterStateHeaderPtr header)
{
    /* Stop coprocessor by setting GO bit low. */
    header->copro_register_base[0] &= ~0x01;
}


static void
enable_datapath(FilterStateHeaderPtr header)
{
	uint32_t value = ((volatile uint32_t*) header->dagiom)[35]; /* Byte 0x8c = decimal 140 = word 35 */

	/* Start datapath through the coprocessor. */
	if ((value & 0x00040000) == 0)
	{
		value |= 0x00040000;
		((volatile uint32_t*) header->dagiom)[35] = value;
	}
}


static void
disable_datapath(FilterStateHeaderPtr header)
{
	uint32_t value = ((volatile uint32_t*) header->dagiom)[35]; /* Byte 0x8c = decimal 140 = word 35 */

	/* Stop datapath through the coprocessor. */
	if ((value & 0x00040000) != 0)
	{
		value &= 0xfffbffff;
		((volatile uint32_t*) header->dagiom)[35] = value;
	}
}


static void 
write_cam_csr(FilterStateHeaderPtr header, uint32_t read_count, uint32_t write_count, uint32_t go)
{
    volatile uint32_t* csr = header->cam_control_status_reg;
	uint32_t timeout;
	uint32_t data;
	
	data = ((read_count & 0x0f) << 24) | ((write_count & 0x0f) << 20) |
		((header->wide_bus & 0x01) << 8) | ((header->cam_busy & 0x01) << 4) | (go & 0x01);

	/* Wait for CAM to be ready. */
	dagutil_nanosleep(CSR_NAP_NANOS);
	timeout = MAX_TIMEOUT;
	while (((*csr >> 13) & 1) == 0) 
	{
		if (timeout == 0)
		{
			break;
		}

		timeout--;
		dagutil_nanosleep(CSR_NAP_NANOS);
	}

	if (timeout == 0)
	{
		dagutil_verbose("*** CSR: Pre Timeout waiting for READY ***\n");
	}

	/* Write data. */
	*csr = data;
	dagutil_nanosleep(CSR_NAP_NANOS);
	if (go)
	{
		timeout = MAX_TIMEOUT;
		while (((*csr >> 13) & 1) == 0)
		{
			if (timeout == 0)
			{
				break;
			}

			timeout--;
			dagutil_nanosleep(CSR_NAP_NANOS);
		}
		
		if (timeout < 1)
		{
			dagutil_verbose("*** CSR: Post Timeout waiting for READY ***\n");
		}

		dagutil_nanosleep(CSR_NAP_NANOS);

		if (0 < dagutil_get_verbosity())
		{
			if ((*csr) & 0x08)
			{
				uint8_t read_check = ((*csr) >> 16) & 0x0f;
				uint8_t expected_reads = ((*csr) >> 24) & 0x0f;
	
				if (read_check != expected_reads)
				{
					dagutil_verbose("*** CSR: expected reads = %u, actual reads = %u ***\n", expected_reads, read_check);
				}
				
				dagutil_verbose("CSR (0x%08x): %c%c%c%c%c timeout:%c ready:%c read valid:%c\n",
					   (*csr),
					   ((*csr) & 0x02) ? 'i' : '_', ((*csr) & 0x04) ? 'b' : '_',
					   ((*csr) & 0x20) ? 'W' : '_', ((*csr) & 0x40) ? 'R' : '_',
					   ((*csr) & 0x80) ? 'w' : '_',
					   ((*csr) & 0x8000) ? '1' : '0',
					   ((*csr) & 0x2000) ? '1' : '0',
					   ((*csr) & 0x1000) ? '1' : '0');
			}
		}
	}

	data = ((header->wide_bus & 0x01) << 8) | ((header->cam_busy & 0x01) << 4);
	*csr = data;
	dagutil_nanosleep(CSR_NAP_NANOS);
}


static void 
write_cam_register(FilterStateHeaderPtr header, uint32_t addr, uint64_t value)
{
	/* Write the lowest 36 bits of value into the given address. */
	volatile uint32_t* regs = header->cam_register_base;
	uint32_t lowbits = (uint32_t) (value & 0xffffffffLL);
	uint32_t highbits = (uint32_t) ((value >> 32) & 0x0f);
	
	addr = 2 * (addr & 0x07);

	/* [This comment block comes from the original dagcam code]
	 * Fails with any of the three dagutil_nanosleeps missing.
	 * Fails with all three dagutil_nanosleeps at 200.
	 * "looks good" with all three dagutil_nanosleeps at 500.
	 */
#if USE_NANOSLEEPS
	dagutil_nanosleep(REGISTER_NAP_NANOS);
#endif /* USE_NANOSLEEPS */

	*(regs + addr) = lowbits;
#if USE_NANOSLEEPS
	dagutil_nanosleep(REGISTER_NAP_NANOS);
#endif /* USE_NANOSLEEPS */

	*(regs + addr + 1) = highbits;
#if USE_NANOSLEEPS
	dagutil_nanosleep(REGISTER_NAP_NANOS);
#endif /* USE_NANOSLEEPS */
}

static void
write_256cam_value(FilterStateHeaderPtr header, uint32_t addr, bit72_word_t value, uint8_t space_val)
{
	unsigned int val_indx = 0;
	uint8_t inst = 0x1;
	int timeout = TIMEOUT;

    dagutil_verbose_level(2, "Writing to CAM - addr:%08x data:%02x %08" PRIx64 " %08llx space:%d\n", 
                          addr, value.highbits, value.lowbits >>32, value.lowbits & 0xffffffffLL, space_val);
	//Initialize value array (72-bit)
	*(header->cam_register_base + SC256_VAL_ARRAY + val_indx) = (value.lowbits & 0xffffffff); 
	val_indx++;
	
	*(header->cam_register_base + SC256_VAL_ARRAY + val_indx) = ((value.lowbits >> 32) & 0xffffffff);
	val_indx++;

	*(header->cam_register_base + SC256_VAL_ARRAY + val_indx) = (value.highbits & 0xff); 

	*(header->cam_register_base + SC256_CSR0) = 0; 
	*(header->cam_register_base + SC256_CSR1) = (((inst & 0x7) << 28 ) | ((space_val & 0x3) << 20) | (addr & 0xfffff)); 
	(*(header->cam_register_base + SC256_CSR2)) = 0x00000001; // Set 'action' bit

	while ((timeout > 0) && (*(header->cam_register_base + SC256_CSR2) & BIT28))
	{
#if USE_NANOSLEEPS
	dagutil_nanosleep(110);
#endif
		timeout--;
	}
    if (0 == timeout) {
        dagutil_verbose_level(1, "timeout in %s\n", __FUNCTION__); 
    }
}

static  bit72_word_t
read_256cam_value(FilterStateHeaderPtr header, uint32_t addr, uint8_t space_val)
{
	bit72_word_t value;
	uint8_t inst = 0; /* read */
	int timeout = TIMEOUT;

	*(header->cam_register_base + SC256_CSR0) = 0; 
	*(header->cam_register_base + SC256_CSR1) = (((inst & 0x7) << 28 ) | ((space_val & 0x3) << 20) | (addr & 0xfffff)); 
	(*(header->cam_register_base + SC256_CSR2)) = 0x00000001; // Set 'action' bit

	while ((timeout > 0) && (*(header->cam_register_base + SC256_CSR2) & BIT28))
	{
#if USE_NANOSLEEPS
	dagutil_nanosleep(110);
#endif
		timeout--;
	}
    if (0 == timeout) {
        dagutil_verbose_level(1, "timeout in %s\n", __FUNCTION__); 
    }

    value.lowbits  = (*(header->cam_register_base + SC256_RES_ARRAY) & 0xffffffffLL);
    value.lowbits |= (*(header->cam_register_base + SC256_RES_ARRAY + 1) & 0xffffffffLL ) << 32;
    value.highbits = (*(header->cam_register_base + SC256_RES_ARRAY + 2) & 0xffLL);

    dagutil_verbose_level(2, "Read from CAM  - addr:%08x data:%02x %08" PRIx64 " %08llx space:%d\n", 
                          addr, value.highbits, value.lowbits >>32, value.lowbits & 0xffffffffLL, space_val);

    return value;
}

#if defined (__linux__) || defined (__FreeBSD__) || (defined(__APPLE__) && defined(__ppc__)) || (defined(__SVR4) && defined(__sun))
static inline uint32_t
#elif defined (_WIN32)
static __inline uint32_t
#endif
create_mem_select(mem_select_msb_t space, uint16_t region, uint32_t address)
{
	/* 2 bits from space, 10 bits from region, 20 bits from address. */
	return ((space & 0x3) << 30) | ((region & 0x03ff) << 20) | (address & 0x000fffff);
}


static dagipf_error_t
load_coprocessor_state(FilterStateHeaderPtr header)
{
	uint32_t cam_location = 0; /* Tracks which 36-bit word in the CAM we are writing to. */
	uint32_t tags;

	if (header->synchronized)
	{
		/* Nothing to do. */
		return ERR_NOERR;
	}

	header->synchronized = 1;

	/* Synchronize CAM and coprocessor state with the user state in the header. */

	/* Install tags for differentiating searches. */
	tags = (REGISTER_P_TAG << 16) | (REGISTER_B_TAG << 12) | (REGISTER_C_TAG << 8) | 
		(REGISTER_D_TAG << 4) | REGISTER_E_TAG;
	header->copro_register_base[2] = tags;

	/* Set up search control register. */
	header->copro_register_base[1] = *((uint32_t*) (&header->registers.search_control));

	if (header->register_a_search)
	{
		search_record144_t* search_record = header->register_a_search;
		uint8_t bucket;

        /* version 2 has hardwired search/output register mapping */
        if (0 == header->module_version ) {
            /* Load 144 bit searches. */
            install_register_mapping144(header, search_record); 
        }

		/* Install search values in CAM. */
		for (bucket = 0; bucket < PORT_CONFIG_BUCKETS; bucket++)
		{
			if (header->search_patterns[bucket].size > 0)
			{
				/* Set initial CAM location. */
				cam_location = 4 * bucket * header->search_patterns[bucket].size;

				/* Install CAM entries. */
				cam_location = install_in_cam144(header, header->search_patterns[bucket].pattern_count, header->search_patterns[bucket].patterns, cam_location);
				if (cam_location > CAM_ENTRIES_36)
				{
					return ERR_CAM_OVERFLOW;
				}
			}
		}
	}

	header->synchronized = 1;

#if !HEADER_FILTER_ONLY
	if (header->string_head)
	{
		/* FIXME: install payload search strings. */
	}
#endif /* HEADER_FILTER_ONLY */
	
	return ERR_NOERR;
}

static dagipf_error_t
load_coprocessor256_state(FilterStateHeaderPtr header)
{
	uint32_t cam_location = 0; /* Tracks which 36-bit word in the CAM we are writing to. */
	uint32_t tmp_cam_location = 0;
	
	if (header->synchronized)
	{
		/* Nothing to do. */
		return ERR_NOERR;
	}

	header->synchronized = 1;

	/* Synchronize CAM and coprocessor state with the user state in the header. */

	if (header->register_a_search)
	{
		search_record144_t* search_record = header->register_a_search;
		uint8_t bucket;

        /* version 2 has hardwired search/output register mapping */
        if (0 == header->module_version ) {
            /* Load 144 bit searches. */
            install_register_mapping144(header, search_record);
        }

		/* Install search values in CAM. */
		for (bucket = 0; bucket < PORT_CONFIG_BUCKETS; bucket++)
		{
			if (header->search_patterns[bucket].size > 0)
			{
				/* Set initial CAM location. */
				cam_location =  2 * bucket * header->search_patterns[bucket].size;
                tmp_cam_location = cam_location;

				/* Install CAM entries. */
				cam_location = install_in_256cam144(header, 
                                                    header->search_patterns[bucket].pattern_count,
                                                    header->search_patterns[bucket].patterns,
                                                    cam_location);
				if (cam_location > CAM256_ENTRIES_72)
				{
					return ERR_CAM_OVERFLOW;
				}
                if (0)
                {
                    /* read back data from CAM */
                    verify_256cam144(header, 
                                     header->search_patterns[bucket].pattern_count,
                                     header->search_patterns[bucket].patterns,
                                     tmp_cam_location);
                }
			}
		}
	}

	header->synchronized = 1;

#if !HEADER_FILTER_ONLY
	if (header->string_head)
	{
		/* FIXME: install payload search strings. */
	}
#endif /* HEADER_FILTER_ONLY */
	
	return ERR_NOERR;
}

/**
 * Install pattern colour, snap length and action in SRAM.
 */
static void
install_in_sram(FilterStateHeaderPtr header,
                pattern144_t* pattern,
                 uint32_t cam_location,
                uint32_t rule_number) {
    uint32_t address, data;

    switch(header->module_version) {
    case 0:
        address = create_mem_select(COLOUR_CONTROL_MEMORY, SRAM_A, cam_location);
        data = (pattern->colour & 0x0000ffff) | ((pattern->snaplength << 16) & 0xffff0000);
        /* 1) Put address into Memory Select/Address register. */
        header->copro_register_base[3] = address;
        dagutil_verbose_level(2, "colour memory: %08x <- %08x\n", address, data);
        /* 2) Write the colour, snap length and action. */
        header->copro_register_base[5] = (pattern->action & 0x0000000f);
        header->copro_register_base[4] = data;
        break;
    case 2:
/*         address = (rule_number << 1) & 0x0000ffff; */
        address = cam_location;
        data = pattern->colour; // already has color and action
        /* 1) Put address into Address register. */
        header->copro_register_base[2] = address;
        dagutil_verbose_level(2, "Colour memory: %08x <- %08x\n", address, data);
        /* 2) Write the colour and action. */
        header->copro_register_base[3] = data; 
        
        while ( !(header->copro_register_base[2] & 0x80000000) ) {
            /* busy waiting (might add some delay later on)*/
        }
        break;

    default:
        dagutil_panic("Unsupported firmware version");
    }
}

static pattern144_t
read_sram(FilterStateHeaderPtr header,
             uint32_t address) {
    uint32_t data = 0;
    pattern144_t pattern;

    memset(&pattern, 0, sizeof(pattern144_t));

    switch(header->module_version) {
    case 0:
        /* TODO - implement reading */
        break;
    case 2:
        header->copro_register_base[2] = address;
        while ( !(header->copro_register_base[2] & 0x80000000) ) {
            /* busy waiting (might add some delay later on)*/
        }
        data = header->copro_register_base[3]; 
        dagutil_verbose_level(2, "reading colour memory - addr:%08x data:%08x\n", address, data);
        pattern.colour = data & 0x0000ffff;
        break;

    default:
        dagutil_panic("Unsupported firmware version");
    }
    return pattern;
}

static uint32_t
install_in_cam144(FilterStateHeaderPtr header, uint32_t count, pattern144_t* searches, uint32_t cam_location)
{
	uint32_t index;

	for (index = 0; index < count; index++)
	{
		pattern144_t* pattern = &searches[index];

		if (cam_location > (CAM_ENTRIES_36 - 4))
		{
			break;
		}

		/* Configure Entry Mask Register. */
		write_cam_register(header, 0, 0x008220020LL); /* Load Entry Mask Register opcode. 
														 Width = 144 bits, Entry Mask Register = 0. */
		write_cam_register(header, 1, pattern->mask[3]);
		write_cam_register(header, 2, pattern->mask[2]);
		write_cam_register(header, 3, pattern->mask[1]);
		write_cam_register(header, 4, pattern->mask[0]);
		write_cam_csr(header, 0, 5, 1);

		/* Write Entry. */
		write_cam_register(header, 0, 0x000800120LL); /* Write Entry opcode.  Width = 144 bits, Entry Mask Register = 0. */
		write_cam_register(header, 1, cam_location);  /* Address. */
		write_cam_register(header, 2, pattern->value[3]);
		write_cam_register(header, 3, pattern->value[2]);
		write_cam_register(header, 4, pattern->value[1]);
		write_cam_register(header, 5, pattern->value[0]);

		write_cam_csr(header, 0, 6, 1);

        install_in_sram(header, pattern, cam_location, index);

		cam_location += 4;
	}

	return cam_location;
}

static uint32_t
install_in_256cam144(FilterStateHeaderPtr header, uint32_t count, pattern144_t* searches, uint32_t cam_location)
{
	uint32_t index;
	bit72_word_t bit72word;
	uint8_t mask_space;


#if TARGET_LINUX
	long long start_ticks;
	long long end_ticks;
	double microseconds;
	rdtscll(start_ticks);
#endif /* TARGET_LINUX */

	for (index = 0; index < count; index++)
	{
		pattern144_t* pattern = &searches[index];

		if (cam_location > (CAM256_ENTRIES_72 - 2))
		{
			break;
		}

        install_in_sram(header, pattern, cam_location, index);

		mask_space = 0x1; //Mask space

		bit72word.lowbits = (pattern->mask[2] << 36) | (pattern->mask[3] & 0xfffffffffLL);
		bit72word.highbits = pattern->mask[2] >> 28;
		write_256cam_value(header, cam_location, bit72word, mask_space);

		mask_space = 0x0; //Data space

		bit72word.lowbits =  (pattern->value[2] << 36) | (pattern->value[3] & 0xfffffffffLL);
		bit72word.highbits = pattern->value[2] >> 28;
		write_256cam_value(header, cam_location, bit72word, mask_space);

		cam_location++;

		mask_space = 0x1; //Mask space

		bit72word.lowbits = (pattern->mask[0] << 36)  | (pattern->mask[1] & 0xfffffffffLL); 
		bit72word.highbits = pattern->mask[0] >> 28;
		write_256cam_value(header, cam_location, bit72word, mask_space);

		mask_space = 0x0; //Data space

		bit72word.lowbits = (pattern->value[0]  << 36)  | (pattern->value[1] & 0xfffffffffLL);
		bit72word.highbits = pattern->value[0] >> 28;
		write_256cam_value(header, cam_location, bit72word, mask_space);

		cam_location++;

	}
#if TARGET_LINUX
	rdtscll(end_ticks);
	end_ticks -= start_ticks;
	microseconds = tsc_to_microseconds(end_ticks);
	printf("install_in_256cam144(): Time to load %u items into CAM was %.6f microseconds.\n", count, microseconds);
#endif /* TARGET_LINUX */

	return cam_location;
}


static int 
verify_256cam144(FilterStateHeaderPtr header, uint32_t count, pattern144_t* searches, uint32_t cam_location)
{
	uint32_t index;
	bit72_word_t bit72word;
	uint8_t mask_space;
    int color_failed = 0;
    int cam_failed = 0;
    int result = 1;
    pattern144_t data;

#if TARGET_LINUX
	long long start_ticks;
	long long end_ticks;
	double microseconds;
	rdtscll(start_ticks);
#endif /* TARGET_LINUX */

    dagutil_verbose("Verifying colour memory and CAM content\n");
	for (index = 0; index < count; index++)
	{
		pattern144_t* pattern = &searches[index];

		if (cam_location > (CAM256_ENTRIES_72 - 2))
		{
			break;
		}

        data = read_sram(header, cam_location);
        switch(header->module_version) {
        case 0:
            if (data.colour != pattern->colour ||
                data.action != pattern->action ||
                data.snaplength != pattern->snaplength ) 
            {
                dagutil_verbose_level(2, "expected - color:%04x action:%02x snap:%04x\n",
                                      pattern->colour, pattern->action, pattern->snaplength);
                dagutil_verbose_level(2, "  actual - color:%04x action:%02x snap:%04x\n",
                                      data.colour, data.action, data.snaplength);
                color_failed = 1;
            }
        break;
        case 2:
            if (data.colour != pattern->colour) 
            {
                dagutil_verbose_level(2, "expected - color:%04x\n", pattern->colour);
                dagutil_verbose_level(2, "  actual - color:%04x\n", data.colour);
                color_failed = 1;
            }
        break;
        }
		mask_space = 0x1; //Mask space
        bit72word = read_256cam_value(header, cam_location, mask_space);

        if (bit72word.lowbits != ((pattern->mask[2] << 36) | (pattern->mask[3] & 0xfffffffffLL)) ||
            bit72word.highbits != pattern->mask[2] >> 28) 
        {
            dagutil_verbose_level(2 ,"expected data:%09llx%09llx\n", 
                                  pattern->mask[2] & 0xfffffffffLL, pattern->mask[3] & 0xfffffffffLL);
            dagutil_verbose_level(2 ,"A actual data:%02x%016"PRIx64"\n", 
                                  bit72word.highbits,bit72word.lowbits );
            cam_failed = 1;
        }

		mask_space = 0x0; //Data space
        bit72word = read_256cam_value(header, cam_location, mask_space);

        if (bit72word.lowbits != ((pattern->value[2] << 36) | (pattern->value[3] & 0xfffffffffLL)) ||
            bit72word.highbits != pattern->value[2] >> 28) 
        {
            dagutil_verbose_level(2 ,"expected data:%09llx%09llx\n", 
                                  pattern->value[2] & 0xfffffffffLL, pattern->value[3] & 0xfffffffffLL);
            dagutil_verbose_level(2 ,"B actual data:%02x%016"PRIx64"\n", 
                                  bit72word.highbits,bit72word.lowbits );
            cam_failed = 1;
        }

		cam_location++;

		mask_space = 0x1; //Mask space
        bit72word = read_256cam_value(header, cam_location, mask_space);

		if (bit72word.lowbits != ((pattern->mask[0] << 36)  | (pattern->mask[1] & 0xfffffffffLL)) ||
            bit72word.highbits != pattern->mask[0] >> 28)
        {
            dagutil_verbose_level(2 ,"expected data:%09llx%09llx\n", 
                                  pattern->mask[0] & 0xfffffffffLL, pattern->mask[1] & 0xfffffffffLL);
            dagutil_verbose_level(2 ,"C actual data:%02x%016"PRIx64"\n", 
                                  bit72word.highbits,bit72word.lowbits );
            cam_failed = 1;
        }

		mask_space = 0x0; //Data space
        bit72word = read_256cam_value(header, cam_location, mask_space);

		if (bit72word.lowbits != ((pattern->value[0] << 36)  | (pattern->value[1] & 0xfffffffffLL)) ||
            bit72word.highbits != pattern->value[0] >> 28)
        {
            dagutil_verbose_level(2 ,"expected data:%09llx%09llx\n", 
                                  pattern->value[0] & 0xfffffffffLL, pattern->value[1] & 0xfffffffffLL);
            dagutil_verbose_level(2 ,"D actual data:%02x%016"PRIx64"\n", 
                                  bit72word.highbits,bit72word.lowbits );
            cam_failed = 1;
        }

		cam_location++;

    }
    if (color_failed) {
        dagutil_verbose("Color memory verification has failed!\n");
        result = 1;
    }
    if (cam_failed) 
    {
        dagutil_verbose("CAM verification has failed!\n");
        result = 1;
    }
    if (!color_failed && !cam_failed) {
        dagutil_verbose("Color memory and CAM verification was successful\n");
        result = 0;
    }
    return result;
}

static void
install_register_mapping144(FilterStateHeaderPtr header, search_record144_t* search_record)
{
	uint32_t index;
	uint16_t strip0[16];
	uint32_t strip1[16];
	uint32_t strip2[16];
	uint32_t strip3[16];
	uint32_t strip4[16];

	memset(strip0, 0, sizeof(uint16_t) * 16);
	memset(strip1, 0, sizeof(uint32_t) * 16);
	memset(strip2, 0, sizeof(uint32_t) * 16);
	memset(strip3, 0, sizeof(uint32_t) * 16);
	memset(strip4, 0, sizeof(uint32_t) * 16);

	/* Create values to be put in HMA memory. */
	for (index = 0; index < 8; index++)
	{
		int8_t word;

		/* Which destination strip?    
		 * Which destination nibble?  index 
		 * Which source word?         search_record->register_mapping[index].word 
		 * Which source word nibble?  search_record->register_mapping[index].nibble 
		 * Strip(search_record->register_mapping[index].word) = 
		 *           (strip_tag + index) << 4 * search_record->register_mapping[0].nibble 
		 */

		if (index < 4)
		{
			/* Strip 0 contains only 16 bits. */
			word = search_record->register_mapping[index].word;
			if (word >= 0)
			{
				uint8_t nibble = search_record->register_mapping[index].nibble & 0x07;

				strip0[(uint8_t)word] |= (0x8 + (7 - nibble)) << 4 * (3 - index);
			}
		}

		word = search_record->register_mapping[index + 4].word;
		if (word >= 0)
		{
			uint8_t nibble = search_record->register_mapping[index + 4].nibble & 0x07;

			strip1[(uint8_t)word] |= (0x8 + (7 - nibble)) << 4 * (7 - index);
		}

		word = search_record->register_mapping[index + 12].word;
		if (word >= 0)
		{
			uint8_t nibble = search_record->register_mapping[index + 12].nibble & 0x07;

			strip2[(uint8_t)word] |= (0x8 + (7 - nibble)) << 4 * (7 - index);
		}

		word = search_record->register_mapping[index + 20].word;
		if (word >= 0)
		{
			uint8_t nibble = search_record->register_mapping[index + 20].nibble & 0x07;

			strip3[(uint8_t)word] |= (0x8 + (7 - nibble)) << 4 * (7 - index);
		}

		word = search_record->register_mapping[index + 28].word;
		if (word >= 0)
		{
			uint8_t nibble = search_record->register_mapping[index + 28].nibble & 0x07;

			strip4[(uint8_t)word] |= (0x8 + (7 - nibble)) << 4 * (7 - index);
		}
	}

	/* Put values in HMA memory. */
	for (index = 0; index < 16; index++)
	{
		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_0, index);
		
		/* Write the value. */
		header->copro_register_base[4] = strip0[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_0, index) + 16;

		/* Write the value. */
		header->copro_register_base[4] = strip0[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_0, index) + 32;

		/* Write the value. */
		header->copro_register_base[4] = strip0[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_0, index) + 48;

		/* Write the value. */
		header->copro_register_base[4] = strip0[index];

		if (strip0[index])
		{
			dagutil_verbose("writing 0x%08x to strip0[%u]\n", strip0[index], index);
		}

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_1, index);

		/* Write the value. */
		header->copro_register_base[4] = strip1[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_1, index) + 16;

		/* Write the value. */
		header->copro_register_base[4] = strip1[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_1, index) + 32;

		/* Write the value. */
		header->copro_register_base[4] = strip1[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_1, index) + 48;

		/* Write the value. */
		header->copro_register_base[4] = strip1[index];

		if (strip1[index])
		{
			dagutil_verbose("writing 0x%08x to strip1[%u]\n", strip1[index], index);
		}

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_2, index);

		/* Write the value. */
		header->copro_register_base[4] = strip2[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_2, index) + 16;

		/* Write the value. */
		header->copro_register_base[4] = strip2[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_2, index) + 32;

		/* Write the value. */
		header->copro_register_base[4] = strip2[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_2, index) + 48;

		/* Write the value. */
		header->copro_register_base[4] = strip2[index];

		if (strip2[index])
		{
			dagutil_verbose("writing 0x%08x to strip2[%u]\n", strip2[index], index);
		}

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_3, index);

		/* Write the value. */
		header->copro_register_base[4] = strip3[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_3, index) + 16;

		/* Write the value. */
		header->copro_register_base[4] = strip3[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_3, index) + 32;

		/* Write the value. */
		header->copro_register_base[4] = strip3[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_3, index) + 48;

		/* Write the value. */
		header->copro_register_base[4] = strip3[index];

		if (strip3[index])
		{
			dagutil_verbose("writing 0x%08x to strip3[%u]\n", strip3[index], index);
		}

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_4, index);

		/* Write the value. */
		header->copro_register_base[4] = strip4[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_4, index) + 16;

		/* Write the value. */
		header->copro_register_base[4] = strip4[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_4, index) + 32;

		/* Write the value. */
		header->copro_register_base[4] = strip4[index];

		/* Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(HMA_MEMORY, STRIP_4, index) + 48;

		/* Write the value. */
		header->copro_register_base[4] = strip4[index];

		if (strip4[index])
		{
			dagutil_verbose("writing 0x%08x to strip4[%u]\n", strip4[index], index);
		}
	}
}


static void
configure_rulesets(FilterStateHeaderPtr header, uint8_t ports, uint8_t rulesets)
{
	uint32_t bucket_size;
	uint8_t buckets;
	uint8_t port_index;
	uint8_t ruleset_index;
	uint32_t index;

	/* Invalidate and free any existing search patterns. */
	for (index = 0; index < PORT_CONFIG_BUCKETS; index++)
	{
		
		if (header->search_patterns[index].size != 0)
		{
			if (header->search_patterns[index].pattern_count > 0)
			{
				assert(header->search_patterns[index].patterns != NULL);

				free(header->search_patterns[index].patterns);
			}
		}
		else
		{
			assert(header->search_patterns[index].size == 0);
			assert(header->search_patterns[index].pattern_count == 0);
			assert(header->search_patterns[index].port == 0);
			assert(header->search_patterns[index].ruleset == 0);
			assert(header->search_patterns[index].patterns == NULL);
		}

		header->search_patterns[index].size = 0;
		header->search_patterns[index].pattern_count = 0;
		header->search_patterns[index].port = 0;
		header->search_patterns[index].ruleset = 0;
		header->search_patterns[index].patterns = NULL;
		
	}

	/* Install the new port configuration. */
	buckets = ports * rulesets;
	
	if (header->copro_type == kCoprocessorSC128)
	{
		bucket_size = CAM_ENTRIES_144 / buckets;
	}
	else
	{
		bucket_size = CAM256_ENTRIES_144 / buckets;
	}
	
	header->ports = ports;
	header->rulesets = rulesets;

	index = 0;
	for (port_index = 0; port_index < ports; port_index++)
	{
		for (ruleset_index = 0; ruleset_index < rulesets; ruleset_index++)
		{
			header->search_patterns[index].size = bucket_size;
			header->search_patterns[index].port = port_index;
			header->search_patterns[index].ruleset = ruleset_index;
			index++;
		}
	}

}


#if STATE_FILE_HACK
static void
write_state_file(uint8_t ports, uint8_t rulesets)
{
	FILE* state_file = fopen("/var/run/dagipf_state.txt", "w");
	
	if (state_file)
	{
		time_t result = time(NULL);

		fprintf(state_file, "# Coprocessor state file generated by DAG IPF library at %s\n", asctime(localtime(&result)));
		fprintf(state_file, "ports = \"%u\"\n", ports);
		fprintf(state_file, "rulesets = \"%u\"\n", rulesets);
		
		fclose(state_file);
	}
}


static void
read_state_file(uint8_t* ports, uint8_t* rulesets)
{
	FILE* state_file = fopen("/var/run/dagipf_state.txt", "r");

	/* Set defaults. */
	*ports = 1;
	*rulesets = 1;

	if (state_file)
	{
		int file_ports = 1;
		int file_rulesets = 1;
		char line[LINE_MAX];

		while (fgets(line, LINE_MAX, state_file))
		{
			const char* keyword;
			char tokbuffer[LINE_MAX];
				
			strcpy(tokbuffer, line);

			keyword = strtok(tokbuffer, " \"=\t\n");
			if (keyword && (keyword[0] != '#') && (keyword[0] != '%'))
			{
				const char* value = strtok(NULL, " \"=\t\n");

				if (strcmp(keyword, "ports") == 0)
				{
					file_ports = (int) strtol(value, NULL, 10);
				}
				else if (strcmp(keyword, "rulesets") == 0)
				{
					file_rulesets = (int) strtol(value, NULL, 10);
				}
				else
				{
					/* Unknown token, ignore. */
				}
			}
		}

		if (file_ports <= 2)
		{
			*ports = file_ports; 
		}

		if (file_rulesets <= 2)
		{
			*rulesets = file_rulesets; 
		}
	}
}
#endif /* STATE_FILE_HACK */


static void
write_state_shm(uint8_t ports, uint8_t rulesets)
{
	/* If shared memory does not exist, create it. */
#if defined (__linux__) || defined (__FreeBSD__) || (defined(__APPLE__) && defined(__ppc__))
	int shared_segment_size = getpagesize();
	int segment_id = shmget(SHM_KEY, shared_segment_size, IPC_CREAT | S_IRUSR | S_IWUSR);

	if (-1 != segment_id)
	{
		/* Attach the shared memory segment. */
		char* shared_memory = (char*) shmat(segment_id, 0, 0);
		shm_state_t* state = (shm_state_t*) shared_memory;

		assert((uintptr_t)-1 != (uintptr_t)shared_memory);
			
		/* Write the values into the shared memory area. */
		state->port_count = ports;
		state->ruleset_count = rulesets;
		state->magic_number = SHM_MAGIC_NUMBER;
			
		/* Detach the shared memory segment. */
		shmdt(shared_memory);
		
		dagutil_verbose("stored %d ports and %d rulesets\n", ports, rulesets);
	}
	else
	{
		dagutil_verbose("could not store ports and rulesets\n");
	}
#elif defined (_WIN32)
	FILE *fw=NULL;
	char str[25];
	fw=fopen("temp.dat","w");
	if(fw!=NULL)
	{
		fprintf(fw,"ports: %x",ports);
		fprintf(fw," rulesets: %x",rulesets);
		fprintf(fw," magic_number: %x",SHM_MAGIC_NUMBER);
		fclose(fw);
	}
	else
	{
		fprintf(stderr,"\nCannot open file for writing");
	}
#endif
}


static void
read_state_shm(uint8_t* ports, uint8_t* rulesets)
{
#if defined (__linux__) || defined (__FreeBSD__) || (defined(__APPLE__) && defined(__ppc__))
	int shared_segment_size = getpagesize();
	int segment_id = shmget(SHM_KEY, shared_segment_size, S_IRUSR | S_IWUSR);

	if (-1 != segment_id)
	{
		/* Attach the shared memory segment. */
		char* shared_memory = (char*) shmat(segment_id, 0, 0);
		shm_state_t* state = (shm_state_t*) shared_memory;
			
		assert((uintptr_t)-1 != (uintptr_t)shared_memory);
		assert(SHM_MAGIC_NUMBER == state->magic_number);

		if (state->port_count <= 2)
		{
			*ports = state->port_count; 
		}

		if (state->ruleset_count <= 2)
		{
			*rulesets = state->ruleset_count; 
		}
			
		/* Detach the shared memory segment. */
		shmdt(shared_memory);
	}
	else
	{
		/* Shared memory does not exist, assume 1 port and 1 ruleset. */
		*ports = 1;
		*rulesets = 1;
	}
#elif defined (_WIN32)
	shm_state_t* state = NULL;
	FILE *fr = fopen("temp.dat", "r");
	char str[25];

	if (fr != NULL)
	{
		state = (shm_state_t*) malloc(sizeof(shm_state_t));
		fscanf(fr,"%s",str);
		fscanf(fr,"%x",&state->port_count);
		fscanf(fr,"%s",str);
		fscanf(fr,"%x",&state->ruleset_count);
		fscanf(fr,"%s",str);
		fscanf(fr,"%x",&state->magic_number);
		
		assert(SHM_MAGIC_NUMBER == state->magic_number);
		if (state->port_count <= 2)
		{
			*ports = state->port_count;
		}

		if (state->ruleset_count <= 2)
		{
			*rulesets = state->ruleset_count; 
		}
	}
	else
	{
		fprintf(stderr,"\nCannot open file for reading");
		*ports = 1;
		*rulesets = 1;
	}
#endif
	
	dagutil_verbose("retrieved %d ports and %d rulesets\n", *ports, *rulesets);
}


/** \brief Initialize packet filtering
 *
 * \param dagfd    file descriptor
 * \param flags    don't know
 * \param cptype   coprocessor type
 * \return   pointer to the filter's state
 */

FilterStatePtr
dagipf_init(int dagfd, uint32_t flags, coprocessor_t cptype)
{
	FilterStateHeaderPtr ipf_state = NULL;
	uint8_t* dagiom = NULL;
	dag_reg_t copro_register_base_info[DAG_REG_MAX_ENTRIES];
	dag_reg_t cam_register_base_info[DAG_REG_MAX_ENTRIES];
	dag_reg_t smb_register_base_info[DAG_REG_MAX_ENTRIES];
	uint32_t smb_base = 0;
	uint32_t smb_count = 0;
	uint32_t region;

	if ((flags & DAGIPF_FLAG_NOHARDWARE) == 0)
	{
		dag_reg_t* regs;

		/* Check that the dagfd corresponds to a DAG with a Copro and the IP filtering image. */
		dagiom = dag_iom(dagfd);
		if (dagiom == NULL)
		{
			fprintf(stderr, "dagipf_init(): dag_iom() failed: %s\n", strerror(errno));
			return NULL;
		}
		
		regs = dag_regs(dagfd);

		/* Find the CAM I/O locations. */
		if (cptype == kCoprocessorSC128)
		{
			if (-1 == dag_reg_find((char*) dagiom, DAG_REG_CAM_MAINT, cam_register_base_info))
			{
				fprintf(stderr, "dagipf_init(): failed to find CAM: %s\n", strerror(errno));
				return NULL;
			}
	
			/* Find the SMBus controller. */
			if ((0 != dag_reg_table_find(regs, 0, DAG_REG_SMB, smb_register_base_info, &smb_count)) || (0 == smb_count))
			{
				fprintf(stderr, "dagipf_init(): failed to find SMB: %s\n", strerror(errno));
				return NULL;
			}
			
			smb_base = dagutil_smb_init(dagiom, smb_register_base_info, smb_count);
		}
		else if ((cptype == kCoprocessorSC256) ||
                 (cptype == kCoprocessorSC256C)||
                 (cptype == kCoprocessorBuiltin))
		{
			/* Find the CAM I/O locations. */
			if (-1 == dag_reg_find((char*) dagiom, DAG_REG_IDT_TCAM, cam_register_base_info))
			{
				fprintf(stderr, "dagipf_init(): failed to find CAM: %s\n", strerror(errno));
				return NULL;
			}

			// &&& DAG_REG_SMB or equivalent module has to be supported (?)
		}

		if (-1 == dag_reg_find((char*) dagiom, DAG_REG_PPF, copro_register_base_info))
		{
			fprintf(stderr, "dagipf_init(): failed to find coprocessor: %s\n", strerror(errno));
			return NULL;
		}
	}

	ipf_state = (FilterStateHeaderPtr) malloc(sizeof(FilterStateHeader));
	if (!ipf_state)
	{
		return NULL;
	}

	memset(ipf_state, 0, sizeof(FilterStateHeader));

	for (region = 0; region < 4; region++)
	{
		ipf_state->header_mask_memory[region] = malloc(HEADER_MASK_REGION_BYTES);
		if (!ipf_state->header_mask_memory[region])
		{
			goto fail;
		}
		memset(ipf_state->header_mask_memory[region], 0, HEADER_MASK_REGION_BYTES);
	}

	ipf_state->header_colour_memory = malloc(COLOUR_MEMORY_BYTES);
	if (!ipf_state->header_colour_memory)
	{
		goto fail;
	}
	memset(ipf_state->header_colour_memory, 0, COLOUR_MEMORY_BYTES);

	ipf_state->string_colour_memory = malloc(COLOUR_MEMORY_BYTES);
	if (!ipf_state->string_colour_memory)
	{
		goto fail;
	}
	memset(ipf_state->string_colour_memory, 0, COLOUR_MEMORY_BYTES);

	ipf_state->output_memory = malloc(OUTPUT_MEMORY_BYTES);
	if (!ipf_state->output_memory)
	{
		goto fail;
	}
	memset(ipf_state->output_memory, 0, OUTPUT_MEMORY_BYTES);

	ipf_state->dagfd = dagfd;
	ipf_state->flags = flags;
	ipf_state->dagiom = dagiom;
	ipf_state->copro_type = cptype;
	if ((flags & DAGIPF_FLAG_NOHARDWARE) == 0)
	{
		/* Values retrieved via the register enumeration API. */
		ipf_state->copro_register_base = (volatile uint32_t*) (dagiom + copro_register_base_info[0].addr);
		ipf_state->cam_register_base = (volatile uint32_t*) (dagiom + cam_register_base_info[0].addr);

		if (cptype == kCoprocessorSC128)
		{
			ipf_state->cam_control_status_reg = (volatile uint32_t*) (dagiom + cam_register_base_info[0].addr + 0x80);
			
			if (0 != smb_base)
			{
				ipf_state->smb_register_base = (volatile uint32_t*) (dagiom + smb_base);
			}
			else
			{
				ipf_state->smb_register_base = 0;
			}
		}

		dagutil_verbose("dagiom: 0x%p\n", dagiom);
		dagutil_verbose("Copro register base: 0x%p (offset 0x%x)\n", ipf_state->copro_register_base, (unsigned int)((uint8_t*)ipf_state->copro_register_base - (uint8_t*)dagiom));
		dagutil_verbose("CAM register base: 0x%p (offset 0x%x)\n", ipf_state->cam_register_base, (unsigned int)((uint8_t*) ipf_state->cam_register_base - (uint8_t*) dagiom));

		if (cptype == kCoprocessorSC128)
		{
			dagutil_verbose("CAM CS register: 0x%p (offset 0x%x)\n", ipf_state->cam_control_status_reg, (unsigned int)((uint8_t*) ipf_state->cam_control_status_reg - (uint8_t*) dagiom));
			dagutil_verbose("SMB register base: 0x%p (offset 0x%x)\n", ipf_state->smb_register_base, (unsigned int)((uint8_t*) ipf_state->smb_register_base - (uint8_t*) dagiom));  
		}
	
		dagutil_verbose("Copro register base: addr=0x%x, module=%u, flags=%u, version=%u\n", copro_register_base_info[0].addr, copro_register_base_info[0].module, copro_register_base_info[0].flags, copro_register_base_info[0].version);
		dagutil_verbose("CAM register base: addr=0x%x, module=%u, flags=%u, version=%u\n", cam_register_base_info[0].addr, cam_register_base_info[0].module, cam_register_base_info[0].flags, cam_register_base_info[0].version);

        ipf_state->module_version = copro_register_base_info[0].version;

		if (cptype == kCoprocessorSC128)
		{
			dagutil_verbose("SMB register base: addr=0x%x, module=%u, flags=%u, version=%u\n", smb_register_base_info[0].addr, smb_register_base_info[0].module, smb_register_base_info[0].flags, smb_register_base_info[0].version);
	
			ipf_state->wide_bus = (*ipf_state->cam_control_status_reg) & (1 << 8);
	
			dagutil_verbose("CAM is in %u-bit mode.\n", (1 + ipf_state->wide_bus) * 18);
	
			(*ipf_state->copro_register_base) &= 0xfffffffc; /* Turn off AUTO-INCREMENT and GO bits. */
		}
	}

	/* Initialise port and ruleset configuration. */
	ipf_state->ports = 1;
	ipf_state->rulesets = 1;
	
	if (cptype == kCoprocessorSC128)
	{
		ipf_state->search_patterns[0].size = CAM_ENTRIES_144;
	}
	else if ((cptype == kCoprocessorSC256) ||
             (cptype == kCoprocessorSC256C)||
             (cptype == kCoprocessorBuiltin))
	{
		ipf_state->search_patterns[0].size = CAM256_ENTRIES_144; 
	}
	
	ipf_state->magic_number = MAGIC_NUMBER;

	/* Read state. */
	{
		uint8_t ports = 1;
		uint8_t rulesets = 1;
		
#if STATE_FILE_HACK
		uint8_t file_ports = 1;
		uint8_t file_rulesets = 1;
		
		read_state_file(&file_ports, &file_rulesets);
#endif /* STATE_FILE_HACK */

		read_state_shm(&ports, &rulesets);

#if STATE_FILE_HACK
		assert(file_ports == ports);
		assert(file_rulesets == rulesets);
#endif /* STATE_FILE_HACK */

		/* Configure ports and rulesets. */
		if ((ports != 1) || (rulesets != 1))
		{
			/* Not the default configuration. */
			configure_rulesets(ipf_state, ports, rulesets);
		}
	}

    ipf_state->cam_busy = 1;
	return (FilterStatePtr) ipf_state;

fail:
	dispose_header(ipf_state);
	return NULL;
}



dagipf_error_t
dagipf_dispose(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
	
	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	dispose_header(header);

	return ERR_NOERR;
}


/* Return user-friendly string describing the error. */
const char*
dagipf_strerror(dagipf_error_t dagipf_err)
{
	if (dagipf_err <= ERR_VERIFY_FAILED)
	{
		return uErrorStrings[dagipf_err];
	}

	assert(0);
	return "unknown error code";
}


/* Utility routines. */
dagipf_error_t
dagipf_copro_enable(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (valid_header(header))
	{
		enable_coprocessor(header);
		return ERR_NOERR;
	}

	return ERR_INVALID_STATE;
}


dagipf_error_t
dagipf_copro_disable(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (valid_header(header))
	{
		disable_coprocessor(header);
		return ERR_NOERR;
	}

	return ERR_INVALID_STATE;
}


dagipf_error_t
dagipf_datapath_enable(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (valid_header(header))
	{
		enable_datapath(header);
		return ERR_NOERR;
	}

	return ERR_INVALID_STATE;
}


dagipf_error_t
dagipf_datapath_disable(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (valid_header(header))
	{
		disable_datapath(header);
		return ERR_NOERR;
	}

	return ERR_INVALID_STATE;
}


dagipf_error_t
dagipf_configure_rulesets(FilterStatePtr ipf_state, uint8_t ports, uint8_t rulesets)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if (ports > 2)
	{
		return ERR_INVALID_PARAMETER;
	}

	if (rulesets > 2)
	{
		return ERR_INVALID_PARAMETER;
	}

	if ((ports != header->ports) || (rulesets != header->rulesets))
	{
		/* Changing the configuration. */
		configure_rulesets(header, ports, rulesets);
	}


	/* Write state. */
#if STATE_FILE_HACK
	write_state_file(ports, rulesets);
#endif /* STATE_FILE_HACK */

	write_state_shm(ports, rulesets);

	return ERR_NOERR;
}


dagipf_error_t
dagipf_get_port_count(FilterStatePtr ipf_state, uint8_t* count)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	assert(NULL != count);

	*count = 0;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	*count = header->ports;
	return ERR_NOERR;
}


dagipf_error_t
dagipf_get_port_ruleset_count(FilterStatePtr ipf_state, uint8_t port, uint8_t* count)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	assert(NULL != count);

	*count = 0;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if (port >= header->ports)
	{
		return ERR_INVALID_PARAMETER;
	}

	/* Currently we only support the same number of rulesets for each port. */
	*count = header->rulesets;
	return ERR_NOERR;
}


dagipf_error_t
dagipf_get_port_ruleset(FilterStatePtr ipf_state, uint8_t port, uint8_t* ruleset)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
	uint32_t port_bit = 0;
	uint32_t main_control_reg;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if (port >= header->ports)
	{
		return ERR_INVALID_PARAMETER;
	}

	/* Read current ruleset configuration. */
	if ((header->flags & DAGIPF_FLAG_NOHARDWARE) == 0)
	{
		main_control_reg = header->copro_register_base[0];

        switch (dagipf_get_version(ipf_state)) {
        case 0:
            /* bank select is in the second least significant nibble (bits 7-4). */
            port_bit = 1 << (4 + port); 
            break;
        case 2:
            port_bit = 1 << (16 + port);
            break;
        default:
            assert(0);
        }
		if (main_control_reg & port_bit)
		{
			*ruleset = 1;
		}
		else
		{
			*ruleset = 0;
		}
	}
	else
	{
		*ruleset = 0;
	}

	return ERR_NOERR;
}


dagipf_error_t
dagipf_set_port_ruleset(FilterStatePtr ipf_state, uint8_t port, uint8_t ruleset)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
	uint32_t port_bit = 0;
	uint32_t main_control_reg;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if (port >= header->ports)
	{
		return ERR_INVALID_PARAMETER;
	}

	if (ruleset >= header->rulesets)
	{
		return ERR_INVALID_PARAMETER;
	}

	/* Read current ruleset configuration. */
	if ((header->flags & DAGIPF_FLAG_NOHARDWARE) == 0)
	{
		uint32_t target = 0;

		main_control_reg = header->copro_register_base[0];

        switch (dagipf_get_version(ipf_state)) {
        case 0:
            /* bank select is in the second least significant nibble (bits 7-4). */
            port_bit = 1 << (4 + port); 
            break;
        case 2:
            port_bit = 1 << (16 + port);
            break;
        default:
            assert(0);
        }

		if (0 != ruleset)
		{
			target = port_bit;
		}
		
		if ((main_control_reg & port_bit) != target)
		{
			/* Set desired ruleset configuration. */

			if (ruleset == 0)
			{
				/* Set port ruleset bit to zero. */
				main_control_reg &= ~port_bit;
			}
			else
			{
				/* Set port ruleset bit to one. */
				main_control_reg |= port_bit;
			}

			/* Write ruleset configuration back to coprocessor. */
			header->copro_register_base[0] = main_control_reg;
		}
		
		/* Verify setting. */
		main_control_reg = header->copro_register_base[0];
		assert((main_control_reg & port_bit) == target);
	}

	return ERR_NOERR;
}

dagipf_error_t
dagipf_activate(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
	volatile uint32_t* csr = NULL;
	dagipf_error_t result;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if ((header->flags & DAGIPF_FLAG_NOHARDWARE) == 1)
	{
		return ERR_NOERR;
	}

	/* Stop coprocessor. */
	disable_coprocessor(header);

	csr = header->cam_control_status_reg;

	/********** BEGIN code adapted from dagcam.c **********/

	*csr = (1 << 31) | (1 << 4);        /* RST and BUSY (BUSY prevents searches) */
	header->cam_busy = 1;
	header->wide_bus = 0;

#if DO_BIST
	/* NFA initialization. */
	write_cam_register(header, 0, 0x000f00000LL); /* Initiate BIST opcode. */
	write_cam_register(header, 1, 0x000000808LL); /* Initiate NFA data. */
	write_cam_register(header, 2, 0x10000000bLL); 
	write_cam_csr(header, 0, 3, 1);

	/* Wait for NFA to complete. */
	write_cam_register(header, 0, 0x000f10000LL); /* Read BIST status opcode. */
	write_cam_csr(header, 2, 1, 1);
	while (0x000f0000 != (header->cam_register_base[2] & 0x000f0000))
	{
	    printf("BIST Status Register: 0x%01x%08x (waiting for NFA to complete)\n", header->cam_register_base[3] & 0x0f, header->cam_register_base[2]);
	    dagutil_microsleep(500 * 1000);
	    write_cam_register(header, 0, 0x000f10000LL); /* Read BIST status opcode. */
	    write_cam_csr(header, 2, 1, 1);
	}

	printf("BIST Status Register: 0x%01x%08x (should be 0x----f----)\n", header->cam_register_base[3] & 0x0f, header->cam_register_base[2]);

	/* Finish NFA initialization. */
	write_cam_register(header, 0, 0x000f00000LL); /* Initiate BIST opcode. */
	write_cam_register(header, 1, 0x000000000LL); /* Finish NFA data. */
	write_cam_register(header, 2, 0x000000000LL); 
	write_cam_csr(header, 0, 3, 1);

	/* Initiate BIST. */
	write_cam_register(header, 0, 0x000f00000LL); /* Initiate BIST opcode. */
	write_cam_register(header, 1, 0x000000707LL); /* Initiate BIST data. */
	write_cam_register(header, 2, 0x100000004LL); 
	write_cam_csr(header, 0, 3, 1);
	
	/* Wait for BIST to complete. */
	write_cam_register(header, 0, 0x000f10000LL); /* Read BIST status opcode. */
	write_cam_csr(header, 2, 1, 1);
	while (0x000f0707 != (header->cam_register_base[2] & 0x000fffff))
	{
	    printf("BIST Status Register: 0x%01x%08x (waiting for BIST to finish)\n", header->cam_register_base[3] & 0x0f, header->cam_register_base[2]);
	    dagutil_microsleep(500 * 1000);
	    write_cam_register(header, 0, 0x000f10000LL); /* Read BIST status opcode. */
	    write_cam_csr(header, 2, 1, 1);
	}
	
	printf("BIST Status Register: 0x%01x%08x (should be 0xfffff0707)\n", header->cam_register_base[3] & 0x0f, header->cam_register_base[2]);
#endif /* DO_BIST */

	/* Load Configuration Register */
	write_cam_register(header, 0, 0x008000030LL); /* Load Configuration Register opcode. */
	write_cam_register(header, 1, 0x000000416LL); /* Data input transfer = DDR, 
													 maintenance port width = 18 bit, 
													 search input bus width = 36 bit, 
													 search mode = 144 bit variable-width */
	write_cam_csr(header, 0, 2, 1);
#if USE_NANOSLEEPS
	dagutil_nanosleep(4 * CSR_NAP_NANOS); /* paranoia */
#endif /* USE_NANOSLEEPS */

	/* Read back Configuration Register. */
#if READ_REGISTERS
	write_cam_register(header, 0, 0x000010000LL); /* Read Configuration Register opcode. */
	write_cam_csr(header, 1, 1, 1);
	printf("Configuration Register: 0x%01x%08x (should be 0x000000416)\n", header->cam_register_base[1] & 0x0f, 
		   header->cam_register_base[0]);
#endif /* READ_REGISTERS */

	/* Load Thread Configuration Register. */
	write_cam_register(header, 0, 0x000040000LL); /* Load Thread Configuration Register opcode. */
	write_cam_register(header, 1, 0x000000000LL);
	write_cam_csr(header, 0, 2, 1);
#if USE_NANOSLEEPS
	dagutil_nanosleep(4 * CSR_NAP_NANOS); /* paranoia */
#endif /* USE_NANOSLEEPS */

	/* Read back Thread Configuration Register. */
#if READ_REGISTERS
	write_cam_register(header, 0, 0x000050000LL); /* Read Thread Configuration Register opcode. */
	write_cam_csr(header, 1, 1, 1);
	printf("Thread Configuration Register: 0x%01x%08x (should be 0x000000000)\n", header->cam_register_base[1] & 0x0f, 
		   header->cam_register_base[0]);
#endif /* READ_REGISTERS */

	/* Core Reset to invalidate entire table */
	write_cam_register(header, 0, 0x000400000LL); /* Core Reset opcode. */ 
	write_cam_csr(header, 0, 1, 1);
#if USE_NANOSLEEPS
	dagutil_nanosleep(4 * CSR_NAP_NANOS); /* paranoia */
#endif /* USE_NANOSLEEPS */

	/* Load GMR[0] for 144-bit searches */
	write_cam_register(header, 0, 0x000200020LL); /* Load Global Comparand Mask Register opcode. 
													 Register = 0, width = 144 bits. */
	write_cam_register(header, 1, 0xfffffffffLL);
	write_cam_register(header, 2, 0xfffffffffLL);
	write_cam_register(header, 3, 0xfffffffffLL);
	write_cam_register(header, 4, 0xfffffffffLL);
	write_cam_csr(header, 0, 5, 1);
#if USE_NANOSLEEPS
	dagutil_nanosleep(4 * CSR_NAP_NANOS); /* paranoia */
#endif /* USE_NANOSLEEPS */

	/* Read back Global Comparand Mask Register. */
#if READ_REGISTERS
	write_cam_register(header, 0, 0x000210020LL); /* Read Global Comparand Mask Register opcode. */
	write_cam_csr(header, 4, 1, 1);
	printf("Global Comparand Mask Register: 0x%01x%08x (should be 0xfffffffff)\n", header->cam_register_base[1] & 0x0f, 
		   header->cam_register_base[0]);
	printf("Global Comparand Mask Register: 0x%01x%08x (should be 0xfffffffff)\n", header->cam_register_base[3] & 0x0f, 
		   header->cam_register_base[2]);
	printf("Global Comparand Mask Register: 0x%01x%08x (should be 0xfffffffff)\n", header->cam_register_base[5] & 0x0f, 
		   header->cam_register_base[4]);
	printf("Global Comparand Mask Register: 0x%01x%08x (should be 0xfffffffff)\n", header->cam_register_base[7] & 0x0f, 
		   header->cam_register_base[6]);
#endif /* READ_REGISTERS */

	/* Load GMR[1] for 72-bit searches */
	write_cam_register(header, 0, 0x000200011LL); /* Load Global Comparand Mask Register opcode. 
													 Register = 1, width = 72 bits. */
	write_cam_register(header, 1, 0xfffffffffLL);
	write_cam_register(header, 2, 0xfffffffffLL);
	write_cam_register(header, 3, 0x000000000LL);
	write_cam_register(header, 4, 0x000000000LL);
	write_cam_csr(header, 0, 3, 1);
#if USE_NANOSLEEPS
	dagutil_nanosleep(4 * CSR_NAP_NANOS); /* paranoia */
#endif /* USE_NANOSLEEPS */

#if READ_REGISTERS
	write_cam_register(header, 0, 0x000210011LL); /* Read Global Comparand Mask Register opcode. */
	write_cam_csr(header, 2, 1, 1);
	printf("Global Comparand Mask Register: 0x%01x%08x (should be 0xfffffffff)\n", header->cam_register_base[1] & 0x0f, 
		   header->cam_register_base[0]);
	printf("Global Comparand Mask Register: 0x%01x%08x (should be 0xfffffffff)\n", header->cam_register_base[3] & 0x0f, 
		   header->cam_register_base[2]);
#endif /* READ_REGISTERS */

	/* Load GMR[2] for 36-bit searches */
	write_cam_register(header, 0, 0x000200002LL); /* Load Global Comparand Mask Register opcode. 
													 Register = 2, width = 36 bits. */
	write_cam_register(header, 1, 0xfffffffffLL);
	write_cam_register(header, 2, 0x000000000LL);
	write_cam_csr(header, 0, 2, 1);
#if USE_NANOSLEEPS
	dagutil_nanosleep(4 * CSR_NAP_NANOS); /* paranoia */
#endif /* USE_NANOSLEEPS */

#if READ_REGISTERS
	write_cam_register(header, 0, 0x000210002LL); /* Read Global Comparand Mask Register opcode. */
	write_cam_csr(header, 1, 1, 1);
	printf("Global Comparand Mask Register: 0x%01x%08x (should be 0xfffffffff)\n", header->cam_register_base[1] & 0x0f, 
		   header->cam_register_base[0]);
#endif /* READ_REGISTERS */

	/* Load GMR[3] for 36-bit searches */
	write_cam_register(header, 0, 0x000200003LL); /* Load Global Comparand Mask Register opcode. 
													 Register = 3, width = 36 bits. */
	write_cam_register(header, 1, 0xfffffffffLL);
	write_cam_csr(header, 0, 2, 1);

#if READ_REGISTERS
	write_cam_register(header, 0, 0x000210003LL); /* Read Global Comparand Mask Register opcode. */
	write_cam_csr(header, 1, 1, 1);
	printf("Global Comparand Mask Register: 0x%01x%08x (should be 0xfffffffff)\n", header->cam_register_base[1] & 0x0f, 
		   header->cam_register_base[0]);
#endif /* READ_REGISTERS */

	/* Load GMR[4] for 36-bit searches */
	write_cam_register(header, 0, 0x000200004LL); /* Load Global Comparand Mask Register opcode. 
													 Register = 4, width = 36 bits. */
	write_cam_register(header, 1, 0xfffffffffLL);
	write_cam_csr(header, 0, 2, 1);

#if READ_REGISTERS
	write_cam_register(header, 0, 0x000210004LL); /* Read Global Comparand Mask Register opcode. */
	write_cam_csr(header, 1, 1, 1);
	printf("Global Comparand Mask Register: 0x%01x%08x (should be 0xfffffffff)\n", header->cam_register_base[1] & 0x0f, 
		   header->cam_register_base[0]);
#endif /* READ_REGISTERS */

	/* Load GMR[8] for 72-bit searches */
	write_cam_register(header, 0, 0x000200018LL); /* Load Global Comparand Mask Register opcode. 
													 Register = 8, width = 72 bits. */
	write_cam_register(header, 1, 0xfffffffffLL);
	write_cam_register(header, 2, 0xfffffffffLL);
	write_cam_csr(header, 0, 3, 1);

	/* Load EMR[0] for 144-bit searches */
	write_cam_register(header, 0, 0x000220020LL); /* Load Entry Mask Register opcode. 
													 Register = 0, width = 144 bits. */
	write_cam_register(header, 1, 0xfffffffffLL);
	write_cam_register(header, 2, 0xfffffffffLL);
	write_cam_register(header, 3, 0xfffffffffLL);
	write_cam_register(header, 4, 0xfffffffffLL);
	write_cam_csr(header, 0, 5, 1);

	/* Load EMR[1] for 72-bit searches */
	write_cam_register(header, 0, 0x000220011LL); /* Load Entry Mask Register opcode. 
													 Register = 1, width = 72 bits. */
	write_cam_register(header, 1, 0xfffffffffLL);
	write_cam_register(header, 2, 0xfffffffffLL);
	write_cam_register(header, 3, 0x000000000LL);
	write_cam_register(header, 4, 0x000000000LL);
	write_cam_csr(header, 0, 3, 1);

	/* Load EMR[2] for 36-bit searches */
	write_cam_register(header, 0, 0x000220002LL); /* Load Entry Mask Register opcode. 
													 Register = 2, width = 36 bits. */
	write_cam_register(header, 1, 0x000fff000LL);
	write_cam_register(header, 2, 0x000000000LL);
	write_cam_csr(header, 0, 2, 1);

	/*
	 * CAM should now be ready to run searches on IP packets.
	 */

	/* blank the array */
	write_cam_register(header, 0, 0x000000000LL);
	write_cam_register(header, 1, 0x000000000LL);
	write_cam_register(header, 2, 0x000000000LL);
	write_cam_register(header, 3, 0x000000000LL);
	write_cam_register(header, 4, 0x000000000LL);

	/* update busy but don't perform any operation */
	header->cam_busy = 0;
	write_cam_csr(header, 0, 0, 0);
#if USE_NANOSLEEPS
	dagutil_nanosleep(CSR_NAP_NANOS);
#endif /* USE_NANOSLEEPS */
	/********** END code adapted from dagcam.c **********/


	/* Fan controller: note that the second parameter to dagutil_start_copro_fan() is an offset from the iom start. */
	if (0 != header->smb_register_base)
	{
		dagutil_start_copro_fan(header->dagiom, (unsigned int) ((uint8_t*) header->smb_register_base - (uint8_t*) header->dagiom));
	}
	
	/* Load state into copro. */
	result = load_coprocessor_state(header);

	/* Start coprocessor. */
	enable_coprocessor(header);

	header->synchronized = 1;

	return result;
}

dagipf_error_t
dagipf_activate256(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
	
	dagipf_error_t result;
	bit72_word_t bit72word;
	int val_indx = 0;
	uint8_t reg_space = 3;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if ((header->flags & DAGIPF_FLAG_NOHARDWARE) == 1)
	{
		return ERR_NOERR;
	}

		
	//////////////////////////////////////////////////////////////////////////////
	/* Copro initialisation */
		
		*(header->cam_register_base + SC256_CSR2) = 0x20000000; // Inhibit search port
		*(header->cam_register_base + SC256_CSR2) = 0x80000000; // Apply TCAM reset 
		dagutil_microsleep(1000); // Wait for 1ms for the TCAM to be ready (after reset)

#if VERBOSE
		printf("\nCSR0 0x%x\n",*(header->cam_register_base + SC256_CSR0));
		printf("\nCSR1 0x%x\n",*(header->cam_register_base + SC256_CSR1));
		printf("\nCSR2 0x%x\n",*(header->cam_register_base + SC256_CSR2));
#endif

		// Initialize the entire DATA space using burst write 

		for(val_indx = 0; val_indx < 3; val_indx++)
		{
				*(header->cam_register_base + SC256_VAL_ARRAY + val_indx) = 0; //Initialize value array (72-bit) to zero 
		}

		(*(header->cam_register_base + SC256_CSR0)) = 0xffff; 
		(*(header->cam_register_base + SC256_CSR1)) = 0x10000000; // inst = write, accs = data space
		(*(header->cam_register_base + SC256_CSR2)) = 0x00000002; // Set 'burst action' bit
		dagutil_nanosleep(90);

		while(*(header->cam_register_base + SC256_CSR2)& 0x2);
		
		//Read random DATA space entry (some random entry)
		(*(header->cam_register_base + SC256_CSR0)) = 0; 
		(*(header->cam_register_base + SC256_CSR1)) = 0xc012; 
		(*(header->cam_register_base + SC256_CSR2)) = 0x00000001; // Set 'action' bit
		dagutil_nanosleep(90);

#if VERBOSE
		printf("Value array => DATA space 0x%08x\n",*(header->cam_register_base + SC256_RES_ARRAY + 0));
		printf("Value array => DATA space 0x%08x\n",*(header->cam_register_base + SC256_RES_ARRAY + 1));
		printf("Value array => DATA space 0x%08x\n",*(header->cam_register_base + SC256_RES_ARRAY + 2));
#endif
		
		// Initialize the entire MASK space using burst write 
		*(header->cam_register_base + SC256_VAL_ARRAY + 0) = 0xffffffff; //Initialize value array (72-bit) to zero 
		*(header->cam_register_base + SC256_VAL_ARRAY + 1) = 0xffffffff; //Initialize value array (72-bit) to zero
		*(header->cam_register_base + SC256_VAL_ARRAY + 2) = 0xff; //Initialize value array (72-bit) to zero
		

		*(header->cam_register_base + SC256_CSR0) = 0xffff; 
		*(header->cam_register_base + SC256_CSR1) = 0x10100000; // inst = write, accs = mask space
		*(header->cam_register_base + SC256_CSR2) = 0x00000002; // Set 'action' bit
		dagutil_nanosleep(90);

		while(*(header->cam_register_base + SC256_CSR2)& 0x2);
			
		//Read random MASK space entry
		*(header->cam_register_base + SC256_CSR0) = 0; 
		*(header->cam_register_base + SC256_CSR1) = 0x0100eee; 
		*(header->cam_register_base + SC256_CSR2) = 0x00000001; // Set 'action' bit
		dagutil_nanosleep(110);

#if VERBOSE
		printf("Value array => MASK space 0x%08x\n",*(header->cam_register_base + SC256_RES_ARRAY + 0));
		printf("Value array => MASK space 0x%08x\n",*(header->cam_register_base + SC256_RES_ARRAY + 1));
		printf("Value array => MASK space 0x%08x\n",*(header->cam_register_base + SC256_RES_ARRAY + 2));
#endif

		//Load registers

		// Load LAR
		bit72word.lowbits = 0xffff0000;
		bit72word.highbits = 0;
		write_256cam_value(header, LAR, bit72word, reg_space);

		// Load SSR0
		bit72word.lowbits = 0xffff;
		bit72word.highbits = 0;
		write_256cam_value(header, SSR0, bit72word, reg_space);

		// Load SSR1
		bit72word.lowbits = 0;
		bit72word.highbits = 0;
		write_256cam_value(header, SSR1, bit72word, reg_space);
		
			
	/* Fan controller. */
	/* dagutil_start_copro_fan(header->dagiom, (uint32_t) ((uint32_t) header->smb_register_base - (uint32_t) header->dagiom)); */

	/* Load state into copro. */
	result = load_coprocessor256_state(header);

	/* Enable the CAM */
	
	// Load SCR
	bit72word.lowbits = 0x00000081;
	bit72word.highbits = 0;
	write_256cam_value(header, SCR, bit72word, reg_space);

	*(header->cam_register_base + SC256_CSR2) = 0x40000000; // Enable search port
	dagutil_nanosleep(90);
		
	header->synchronized = 1;

	return result;
}

dagipf_error_t
dagipf_reset(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
	uint32_t region;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	/* Clear state.  Doesn't affect the copro until next dagipf_activate() call. */
	dagipf_clear_header_searches(ipf_state);
	dagipf_clear_payload_searches(ipf_state);

	memset(&header->registers, 0, sizeof(ip_copro_registers_t));

	for (region = 0; region < 4; region++)
	{
		memset(header->header_mask_memory[region], 0, HEADER_MASK_REGION_BYTES);
	}
	memset(header->header_colour_memory, 0, COLOUR_MEMORY_BYTES);
	memset(header->string_colour_memory, 0, COLOUR_MEMORY_BYTES);
	memset(header->output_memory, 0, OUTPUT_MEMORY_BYTES);
	memset(header->cam_memory, 0, CAM_ENTRIES_36 * sizeof(cam_word_t));

	header->synchronized = 0;
	return ERR_NOERR;
}


dagipf_error_t
dagipf_set_ip_length_range(FilterStatePtr ipf_state, uint16_t ip_min, uint16_t ip_max)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if (ip_max < ip_min)
	{
		return ERR_INVALID_PARAMETER;
	}

	if (ip_min || ip_max)
	{
		/* Enable IP length filtering. */
		header->registers.ip_length.length_min = ip_min;
		header->registers.ip_length.length_max = ip_max;
		header->registers.search_control.length_check = 1;
	}
	else
	{
		/* Disable IP length filtering. */
		header->registers.ip_length.length_min = 0;
		header->registers.ip_length.length_max = 0;
		header->registers.search_control.length_check = 0;
	}

	header->synchronized = 0;
	return ERR_NOERR;
}


dagipf_error_t
dagipf_set_ttl_range(FilterStatePtr ipf_state, uint8_t ttl_min, uint8_t ttl_max)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if (ttl_max < ttl_min)
	{
		assert(0);
		return ERR_INVALID_PARAMETER;
	}

	if (ttl_min || ttl_max)
	{
		/* Enable TTL filtering. */
		header->registers.ttl.ttl_min = ttl_min;
		header->registers.ttl.ttl_max = ttl_max;
		header->registers.search_control.ttl_check = 1;
	}
	else
	{
		/* Disable TTL filtering. */
		header->registers.ttl.ttl_min = 0;
		header->registers.ttl.ttl_max = 0;
		header->registers.search_control.ttl_check = 0;
	}

	header->synchronized = 0;
	return ERR_NOERR;
}


dagipf_error_t
dagipf_enable_search_register(FilterStatePtr ipf_state, ipf_search_register_t search_reg)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if ((search_reg < SEARCH_REGISTER_A) || (search_reg > SEARCH_REGISTER_E))
	{
		assert(0);
		return ERR_INVALID_SEARCH_REGISTER;
	}

	if (search_reg == SEARCH_REGISTER_E)
	{
		header->registers.search_control.search_check_0 = 1;
	}
	else if (search_reg == SEARCH_REGISTER_D)
	{
		header->registers.search_control.search_check_1 = 1;
	}
	else if (search_reg == SEARCH_REGISTER_C)
	{
		header->registers.search_control.search_check_2 = 1;
	}
	else if (search_reg == SEARCH_REGISTER_B)
	{
		header->registers.search_control.search_check_3 = 1;
	}
	else if (search_reg == SEARCH_REGISTER_A)
	{
		header->registers.search_control.search_check_4 = 1;
	}

	header->synchronized = 0;
	return ERR_NOERR;
}


dagipf_error_t
dagipf_disable_search_register(FilterStatePtr ipf_state, ipf_search_register_t search_reg)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if ((search_reg < SEARCH_REGISTER_A) || (search_reg > SEARCH_REGISTER_E))
	{
		assert(0);
		return ERR_INVALID_SEARCH_REGISTER;
	}

	if (search_reg == SEARCH_REGISTER_E)
	{
		header->registers.search_control.search_check_0 = 0;
	}
	else if (search_reg == SEARCH_REGISTER_D)
	{
		header->registers.search_control.search_check_1 = 0;
	}
	else if (search_reg == SEARCH_REGISTER_C)
	{
		header->registers.search_control.search_check_2 = 0;
	}
	else if (search_reg == SEARCH_REGISTER_B)
	{
		header->registers.search_control.search_check_3 = 0;
	}
	else if (search_reg == SEARCH_REGISTER_A)
	{
		header->registers.search_control.search_check_4 = 0;
	}

	header->synchronized = 0;
	return ERR_NOERR;
}


dagipf_error_t
dagipf_clear_payload_searches(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

#if HEADER_FILTER_ONLY
	return ERR_NOT_IMPLEMENTED;
#endif /* HEADER_FILTER_ONLY */

	if (header->string_head)
	{
		while (header->string_head)
		{
			string_block_t* current = header->string_head;

			header->string_head = current->next;
			dispose_string_block(current);
		}

		header->synchronized = 0;
	}

	return ERR_NOERR;
}


dagipf_error_t
dagipf_clear_header_searches(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	dagipf_dispose_144bit_search(header->register_a_search);
	header->register_a_search = NULL;

	header->synchronized = 0;

	return ERR_NOERR;
}


dagipf_error_t
dagipf_add_payload_search(FilterStatePtr ipf_state, const char* search_string, uint16_t length, uint16_t colour)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
	char* new_search = NULL;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if (!length || !search_string || (search_string[0] == '\0'))
	{
		return ERR_INVALID_PARAMETER;
	}

#if HEADER_FILTER_ONLY
	return ERR_NOT_IMPLEMENTED;
#endif /* HEADER_FILTER_ONLY */

	if (!header->string_head || (header->string_head->entries == STRINGS_PER_BLOCK))
	{
		/* Allocate a new block of search strings. */
		string_block_t* new_block = new_string_block();

		if (!new_block)
		{
			return ERR_OUT_OF_MEMORY;
		}

		new_block->next = header->string_head;
		header->string_head = new_block;
	}

	/* Allocate memory for the new search. */
	new_search = malloc(length);
	if (!new_search)
	{
		return ERR_OUT_OF_MEMORY;
	}

	/* Copy contents of the new search across. */
	memcpy(new_search, search_string, length);
	header->string_head->search[header->string_head->entries] = (const char*) new_search;
	header->string_head->search_length[header->string_head->entries] = length;
	header->string_head->entries++;

	header->synchronized = 0;
	return ERR_NOERR;
}


search_record144_t*
dagipf_new_144bit_search(FilterStatePtr ipf_state, uint32_t values)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
	search_record144_t* result = NULL;
	uint32_t index;

	if (0 == valid_header(header))
	{
		return NULL;
	}

	result = (search_record144_t*) malloc(sizeof(search_record144_t));
	if (result)
	{
		memset(result, 0, sizeof(search_record144_t));
		result->length = values;
		
		if (values == 0)
		{
			result->searches = NULL;
		}
		else
		{
			result->searches = (pattern144_t*) malloc(sizeof(pattern144_t) * values);
			if (result->searches)
			{
				memset(result->searches, 0, sizeof(pattern144_t) * values);
			}
			else
			{
				free(result);
				return NULL;
			}
		}
	}

	/* Configure register mapping. */
	for (index = 0; index < 36; index++)
	{
		result->register_mapping[index].word = -1;
	}

	return result;
}


void
dagipf_dispose_144bit_search(search_record144_t* search)
{
	if (search)
	{
		if (search->searches)
		{
			free(search->searches);
			search->searches = NULL;
		}

		free(search);
	}
}


/* Add a header search.  The search_record144_t is owned by the IP filtering module after this call. */
dagipf_error_t
dagipf_add_144bit_header_search(FilterStatePtr ipf_state, search_record144_t* search)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
	uint32_t index;

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if (!search)
	{
		assert(0);
		return ERR_INVALID_PARAMETER;
	}

	for (index = 0; index < search->length; index++)
	{
		pattern144_t* pattern = &search->searches[index];
		int chunk;

		for (chunk = 0; chunk < 4; chunk++)
		{
			/* The value and mask uint64_t's should only contain data in 36 least significant bits. */
			if ((pattern->value[chunk] & 0xfffffff000000000LL) != 0)
			{
				assert(0);
				return ERR_INVALID_PARAMETER;
			}

			if ((pattern->mask[chunk] & 0xfffffff000000000LL) != 0)
			{
				assert(0);
				return ERR_INVALID_PARAMETER;
			}
		}
	}

	/* If search data is passed in search, then assume that a single port, single ruleset configuration
	 * has been specified, and add the entries to the first ruleset.  This is compatible with how the
	 * API used to work.  There is a potential bug here should this assumption fail, but since this
	 * function should not be called with searches when using multiple rulesets with multiple ports
	 * (dagipf_add_144bit_searches() should be called, as this function has no means to specify ruleset and
	 * port in an API context) this should not be a problem.
	 */
	
	if (search->searches && (search->length > 0))
	{
		header->search_patterns[0].patterns = search->searches;
		header->search_patterns[0].pattern_count = search->length;

		/* Write state. */
#if STATE_FILE_HACK
		write_state_file(1, 1);
#endif /* STATE_FILE_HACK */
		
		write_state_shm(1, 1);
	}

	/* Remember the user's input. */
	if (header->register_a_search)
	{
		dagipf_dispose_144bit_search(header->register_a_search);
	}
	
	header->register_a_search = search;
	header->synchronized = 0;

	return ERR_NOERR;
}


dagipf_error_t
dagipf_add_144bit_searches(FilterStatePtr ipf_state, uint8_t port, uint8_t ruleset, 
						   uint32_t search_count, pattern144_t* searches, bool verify)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
	uint64_t top_nibble;
	uint32_t index;
	uint32_t cam_location = 0;
	uint32_t tmp_cam_location = 0;
    int result;

	if (0 == valid_header(header))
	{
		assert(0);
		return ERR_INVALID_STATE;
	}

	if ((port >= header->ports) || (ruleset >= header->rulesets) || (search_count == 0) || (searches == NULL))
	{
		assert(0);
		return ERR_INVALID_PARAMETER;
	}

	if (search_count > header->search_patterns[0].size)
	{
		assert(0); /* Too many searches to fit in a bucket. */
		return ERR_TOO_MANY_SEARCHES;
	}

    if (0 == dagipf_get_version(ipf_state)) {
        /* Validate input. */
        for (index = 0; index < search_count; index++)
        {
            pattern144_t* pattern = &searches[index];
            int chunk;

            for (chunk = 0; chunk < 4; chunk++)
            {
                /* The value and mask uint64_t's should only contain data in 32 least significant bits. 
                 * Used to be the 36 least significant bits, but we've taken the top nibble for port and ruleset.
                 */
                if ((pattern->value[chunk] & 0xffffffff00000000LL) != 0)
                {
                    assert(0);
                    return ERR_INVALID_PARAMETER;
                }

                if ((pattern->mask[chunk] & 0xffffffff00000000LL) != 0)
                {
                    assert(0);
                    return ERR_INVALID_PARAMETER;
                }
            }
        }

        /* Set the correct port and ruleset in the top nibble. 
         * Ruleset is the most significant bit, port occupies the least significant 2 bits.
         */
        top_nibble = (ruleset << 3) | (port & 0x3);
        top_nibble = top_nibble << 32;
        for (index = 0; index < search_count; index++)
        {
            pattern144_t* pattern = &searches[index];

            pattern->value[0] &= 0x0ffffffffLL;
            pattern->value[0] |= top_nibble;

            pattern->mask[0] &= 0x0ffffffffLL;
            pattern->mask[0] |= 0xb00000000LL;
        }
    }

	/* Install and remember the user's input. */
	for (index = 0; index < PORT_CONFIG_BUCKETS; index++)
	{
		if ((header->search_patterns[index].port == port) && (header->search_patterns[index].ruleset == ruleset))
		{
			if (header->copro_type == kCoprocessorSC128)
			{
				cam_location = 4 * index * header->search_patterns[index].size;
			}
			else if ((header->copro_type == kCoprocessorSC256) ||
                     (header->copro_type == kCoprocessorSC256C)||
                     (header->copro_type == kCoprocessorBuiltin))
			{
				 cam_location = 2 * index * header->search_patterns[index].size;
			}

			/* Store the patterns. */
			header->search_patterns[index].patterns = searches;
			header->search_patterns[index].pattern_count = search_count;

			/* Install the CAM entries. */
			if ((header->flags & DAGIPF_FLAG_NOHARDWARE) == 0)
			{
				if (header->copro_type == kCoprocessorSC128)
				{
					cam_location = install_in_cam144(header, search_count, searches, cam_location);
				}
				else if ((header->copro_type == kCoprocessorSC256) ||
                         (header->copro_type == kCoprocessorSC256C)||
                         (header->copro_type == kCoprocessorBuiltin))
				{
                    tmp_cam_location = cam_location;
					cam_location = install_in_256cam144(header, search_count, searches, cam_location);
                    if (verify)
                    {
                        result = verify_256cam144(header, search_count, searches, tmp_cam_location);
                        if (result) {
                            return ERR_VERIFY_FAILED;
                        }
                    }
				}
			}

			break;
		}
	}

	return ERR_NOERR;
}


pattern144_t*
dagipf_new_search_array(unsigned int count)
{
	pattern144_t* result = NULL;
	
	assert(0 != count);
	
	result = (pattern144_t*) malloc(sizeof(pattern144_t) * count);
	if (NULL != result)
	{
		memset(result, 0, sizeof(pattern144_t) * count);
	}
	
	return result;
}


void
dagipf_dispose_search_array(pattern144_t* searches)
{
	assert(NULL != searches);
	
	if (NULL != searches)
	{
		free((char*) searches);
	}
}


/* Configure the mapping of the Results Register into the packet. 
 * The result_mapping_t* is still owned by the caller after this call. 
 */
dagipf_error_t
dagipf_add_results_mapping(FilterStatePtr ipf_state, result_mapping_t* mapping)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
	uint32_t index;
	uint32_t omm[RESULT_REGISTER_BITS];

	if (0 == valid_header(header))
	{
		return ERR_INVALID_STATE;
	}

	if (!mapping)
	{
		assert(0);
		return ERR_INVALID_PARAMETER;
	}

	/* Create a model of the Output Mask Memory based on user input. */
	memset(&omm, 0, sizeof(uint32_t) * RESULT_REGISTER_BITS);
	for (index = 0; index < RESULT_REGISTER_BITS; index++)
	{
		if (mapping->bit_mapping[index].flags & RESULT_FLAG_MAPPED)
		{
			/* This bit from the Results Register is being mapped into the output packet. */
			uint8_t output_word = mapping->bit_mapping[index].output_word & 0x0f;
			uint8_t word_bit = mapping->bit_mapping[index].word_bit;
			uint8_t omm_index = output_word * 8 + word_bit / 4;
			uint8_t word_byte = word_bit % 4;

			/* MSB in the byte is set (this turns overwrite on for the corresponding packet bit).
			 * The remaining 7 bits indicate which bit from Result Register is being mapped. 
			 */
			omm[omm_index] |= (0x80 + index) << (word_byte * 8);
		}
	}

	if ((header->flags & DAGIPF_FLAG_NOHARDWARE) == 0)
	{
		/* Store the model to the real Output Mask Memory. */
		for (index = 0; index < 127; index++)
		{
			/* 1) Put address into Memory Select/Address register. */
			header->copro_register_base[3] = create_mem_select(OUTPUT_MASK_MEMORY, (uint16_t) 0, index);
		
			/* 2) Write the contents of the Output Mask Memory location. */
			header->copro_register_base[4] = omm[index];
		}
	}

	/* Copy the output memory configuration to the internal state. */
	for (index = 0; index < OUTPUT_MEMORY_BYTES; index++)
	{
		header->output_memory[index] = ((uint8_t*) omm)[index];
	}

	header->synchronized = 0;
	return ERR_NOERR;
}

search_record144_t*
dagipf_retrieve_search144(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

	if (valid_header(header))
	{
		return header->register_a_search;
	}

	return NULL;
}

void display_copro_color_memory(FilterStatePtr ipf_state, uint32_t addr)
{
		FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

    switch(header->module_version) {
    case 0:
 		/* 1) Put address into Memory Select/Address register. */
		header->copro_register_base[3] = create_mem_select(COLOUR_CONTROL_MEMORY, SRAM_A, addr);
		
		/* 2) Read the colour, snap length and action. */
		printf("Color memory bits (35:32) %x\n",header->copro_register_base[5]);
		printf("Color memory bits (31:0) %x\n",header->copro_register_base[4]);
        break;
    case 2:
        /* 1) Put address into Address register. */
        header->copro_register_base[2] = addr & 0x0007fff;
		/* 2) Read the colour and action. */
		printf("Color memory bits (15:0) %x\n", header->copro_register_base[3]);
        break;

    default:
        dagutil_panic("Unsupported firmware version");
    }
}

int
dagipf_get_version(FilterStatePtr ipf_state) {
    FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

    return  header->module_version;
}

/****** functions for IPFv2 *******/

/**
 * Set linktype to Ethernet - IPFv2 only
 */
dagipf_error_t
dagipf_set_linktype_eth(FilterStatePtr ipf_state, uint8_t value)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;
    
    if (value) {
        header->copro_register_base[0] |= (value & 0x01) << 4;
    } else {
        header->copro_register_base[0] &= ~((value & 0x01) << 4);
    }
    dagutil_verbose("IPFv2 linktype eth:%d\n", value);

	return ERR_NOERR;
}

/**
 * Embed the colur information in the loss counter field - IPFv2 only
 */
dagipf_error_t
dagipf_set_color_lctr(FilterStatePtr ipf_state, uint8_t value)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

    if (value) {
        header->copro_register_base[0] |= (value & 0x01) << 8;
    } else {
        header->copro_register_base[0] &= ~((value & 0x01) << 8);
    }
    dagutil_verbose("IPFv2 color lctr:%d\n", value);
    
	return ERR_NOERR;
}

/**
 * Enable overwriting the RX error field in the ERF header - IPFv2 only
 */
dagipf_error_t
dagipf_set_rx_error(FilterStatePtr ipf_state, uint8_t value)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

    if (value) {
        header->copro_register_base[0] |= (value & 0x01) << 12;
    } else {
        header->copro_register_base[0] &= ~((value & 0x01) << 12);
    }
    dagutil_verbose("IPFv2 rx error:%d\n", value);
    
	return ERR_NOERR;
}

/**
 * Enable packet dropping - IPFv2 only
 */
dagipf_error_t
dagipf_enable_drop(FilterStatePtr ipf_state, uint8_t value)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

    if (value) {
        header->copro_register_base[0] |= (value & 0x01) << 1;
    } else {
        header->copro_register_base[0] &= ~((value & 0x01) << 1);
    }

    dagutil_verbose("IPFv2 packet drop:%d\n", value);
	return ERR_NOERR;
}

/**
 * Reset control register of IPFv2 - IPFv2 only
 */
dagipf_error_t
dagipf_reset_control(FilterStatePtr ipf_state)
{
	FilterStateHeaderPtr header = (FilterStateHeaderPtr) ipf_state;

    header->copro_register_base[0] = 0;
	return ERR_NOERR;
}
