/* NAT_pseudo_dev.c  -  NAT core comp. & driver (Linux kernel module)	*/

#include <linux/kernel.h>   /* Code runs in the Linux kernel            */
#include <linux/module.h>   /* The code runs as a kernel module         */
#include <linux/fs.h>       /* NAT pseudo device is a character device  */
#include <asm/uaccess.h>    /* Needed for communication with user space */

#include <ix_rm.h>          /* Code uses the IXA Resource Manager       */
#undef LINUX                /* Prevents the compiler from complaining   */
#include <ix_cc.h>          /* Code uses the IXA CCI                    */
#include <ix_cci.h>

#include "NAT_shared_defs.h"
#include "NAT_types.h"
#include "NAT_scratch_rings.h"

#define ME_MASK 0x33      /* System uses microengines 0, 1, 4 and 5 */
#define CONTEXT_MASK 255    /* Context mask -- enable all contexts */

#define HASH_MULT_W0 0x12345678 /* Hash multiplier -- word 0 */
#define HASH_MULT_W1 0x87654321 /* Hash multiplier -- word 1 */
#define HASH_MULT_W2 0x56781234 /* Hash multiplier -- word 2 */
#define HASH_MULT_W3 0x43218765 /* Hash multiplier -- word 3 */

/* Macro to log error message and terminate resource manager */
#define panic(...) { printk("%s: ",NAT_DRIVER_NAME);\
                     printk(__VA_ARGS__);\
                     ix_rm_term();\
                     unregister_chrdev(NAT_major, NAT_DRIVER_NAME);\
                     return(-1); }

/* Macro to clear block of kernel memory */
#define bzero(buf,size) ix_ossl_memset(buf,0,size)

/* Macro to convert microengine sequence number */
/* into microengine ID                          */
#define ME_ID(i) ((i%ME_CL_SZ)|((i/ME_CL_SZ)<<4))

/* Macro to convert SRAM offset and memory channel into */
/*   microengine addressing                             */
#define ME_SRAM_ADDR(offset,memChan) (offset|(memChan<<30))


/* Module parameter -- a UOF file name */
static char * Uof_file;
/* Module parameter -- Linux major device number for NAT pseudo device */
static unsigned int NAT_major = NAT_DEF_MAJOR_NUMBER;

MODULE_AUTHOR("Internetworking Lab, CS, Purdue University");
MODULE_DESCRIPTION("NAT pseudo-device driver for IXP2XXX");
MODULE_PARM(Uof_file, "s");
MODULE_PARM(NAT_major,"i");

/* Static gateway IP address  */
unsigned int gateway_ip= GW_IP;

/* Static network interface configuration */
net_if iface_table[PORTS_NUM]={
	{0xC0A80002 /* 192.168.0.2 */,
	 0x01010101,0x01010000 /* 01:01:01:01:01:01 */},
	{0x0A000001 /* 10.0.0.1 */,
	 0x02020202,0x02020000 /* 02:02:02:02:02:02 */} };

/* External procedures  */

extern ix_error nat_pkt_handler(ix_buffer_handle,ix_uint32,void*); 
extern ix_error resolve_arp(unsigned int);

/* Local procedures    */

static int patch_microblocks(ix_buffer_free_list_info);
static int create_scr_rings();
static int init_hash();
static ix_error nat_table_timer(void*);
static ix_error exe_init_f(ix_exe_handle,void**);
static ix_error exe_fini_f(ix_exe_handle,void*);
static ix_error  cc_init_f(ix_cc_handle,void**);
static ix_error  cc_fini_f(ix_cc_handle,void*);
static ix_exe_handle exeHandle;
static ix_cc_handle ccHandle;
static ix_event_handle eveHandle;
static int nat_open(struct inode *, struct file *);
static int nat_release(struct inode *, struct file *);
static int nat_ioctl(struct inode *, struct file *,
                      unsigned int, unsigned long);

/* Verbosity level */
int verb=SILENT;

/* Pointers to various run-time data structures */
nat_entry *f_nat_table, *r_nat_table;
unsigned int *f_index,*r_index;
arp_entry *arp_table;
unsigned char *f_timer, *r_timer;
void *rx_cntr, *tx_cntr;

/* Scratch rings */
ix_hw_ring_handle rxToNatRing, txScrRing[4];

/* List of free buffers */
ix_buffer_free_list_handle hwFreeList = 0;

/* Operations for the NAT pseudo-device */
static struct file_operations nat_fops = {
	ioctl:		nat_ioctl,
	open:		nat_open,
	release:	nat_release
};

static int nat_open(struct inode *inode, struct file *filp)
{
	MOD_INC_USE_COUNT;
	return 0;
}
static int nat_release(struct inode *inode, struct file *filp)
{
	MOD_DEC_USE_COUNT;
	return 0;
}

static int nat_ioctl(struct inode *inode, struct file *fp,
                       unsigned int cmd, unsigned long buf)
{
	switch (cmd) {
		case SILENT:
			verb = SILENT;
			break;
		case VERBOSE:
			verb = VERBOSE;
			break;
		case GET_ARP_TABLE:
			if ((char *)buf != NULL)
				return copy_to_user((char *)buf,arp_table,
				        ARP_TABLE_SIZE*sizeof(arp_entry));
			break;
		case GET_NAT_TABLE:
			if ((char *)buf != NULL)
				return copy_to_user((char *)buf,
				        (void*)f_nat_table,
				        NAT_TABLE_SIZE*sizeof(nat_entry));
				break;
		case GET_TIMER_TABLE:
			if ((char *)buf != NULL)
				return copy_to_user((char *)buf,
				        (void*)f_timer, 2*NAT_TABLE_SIZE);
			break;
		case GET_RX_COUNTER:
			if ((char *)buf != NULL)
				return copy_to_user((char *)buf,rx_cntr,
				                    RX_CNTR_SIZE);
				break;
		case GET_TX_COUNTER:
			if ((char *)buf != NULL)
				return copy_to_user((char *)buf,tx_cntr,
				                    TX_CNTR_SIZE);
				break;
		case CLR_RX_COUNTER:
			bzero(rx_cntr,RX_CNTR_SIZE);
			break;
		case CLR_TX_COUNTER:
			bzero(tx_cntr,TX_CNTR_SIZE);
			break;
		default:
			return INVALID_CMD;
	}
	return 0;
}

int init_module()
{
	ix_error err;
	ix_buffer_free_list_info hwFreeListInfo;
	int i;

	if (Uof_file == NULL) {
		printk("%s: no microcode file specified!\n",
		       NAT_DRIVER_NAME);
		return -1;
	}

	/* Register the pseudo-device with Linux */
	if (register_chrdev(NAT_major, NAT_DRIVER_NAME, &nat_fops) < 0) {
		printk("%s: can't get major number %d\n",
		       NAT_DRIVER_NAME, NAT_major);
		return -1;
	}

	/* Initialize Intel's Resource Manager */
	printk("%s: Initializing Resource Manager\n", NAT_DRIVER_NAME);
	err=ix_rm_init(0);
	if (err != IX_SUCCESS) {
		printk("Error: ix_rm_init failed\n");
		return -1;
	}

	/* Register the exception packet handler */
	printk("%s: Setting packet receive mode (to callback)\n",
	       NAT_DRIVER_NAME);
	err = ix_rm_packet_set_receive_mode(NAT_CC_ID,
	                       IX_COMM_ID_MODE_CALLBACK);
	if (err != IX_SUCCESS)
		panic("ix_rm_packet_set_receive_mode failed\n");
	printk("%s: Registering packet handler\n", NAT_DRIVER_NAME);
	err = ix_rm_packet_handler_register(NAT_CC_ID, nat_pkt_handler,
	                                    NULL);
	if (err != IX_SUCCESS)
		panic("ix_rm_packet_handler_register failed\n");

	/* Allocate a free buffer list */
	err = ix_rm_hw_buffer_free_list_create(NUM_BUFFERS,
	                 sizeof(ix_hw_buffer_meta), BUF_SIZE,
	                 BUF_SRAM_CHAN, BUF_DRAM_CHAN,&hwFreeList);
	if (err != IX_SUCCESS)
		panic("ix_rm_hw_buffer_free_list_create failed\n");
	/* Read freelist info (it will be needed later) */
	err = ix_rm_buffer_free_list_get_info(hwFreeList,
	                                      &hwFreeListInfo);
	if (err != IX_SUCCESS)
		panic("ix_rm_hw_buffer_free_list_get_info failed\n");

	/* Allocate RX counters (SRAM, channel 0) */
	err = ix_rm_mem_alloc(IX_MEMORY_TYPE_SRAM, 0,
	                      RX_CNTR_SIZE, &rx_cntr);
	if (err != IX_SUCCESS)
		panic("ix_rm_mem_alloc failed for RX counters\n");
	/* Clear RX counters */
	bzero(rx_cntr,RX_CNTR_SIZE);

	/* Allocate TX counters (SRAM, channel 1) */
	err = ix_rm_mem_alloc(IX_MEMORY_TYPE_SRAM, 1,
	                      TX_CNTR_SIZE, &tx_cntr);
	if (err != IX_SUCCESS)
		panic("ix_rm_mem_alloc failed for TX counters\n");
	/* Clear TX counters */
	bzero(tx_cntr,TX_CNTR_SIZE);

	/* Allocate the NAT table in DRAM */
	err = ix_rm_mem_alloc(IX_MEMORY_TYPE_DRAM, 0,
	                      2*NAT_TABLE_SIZE*sizeof(nat_entry),
	                      (void**)&f_nat_table);
	if (err != IX_SUCCESS)
		panic("ix_rm_mem_alloc failed for NAT table\n");
	/* Clear the NAT table */
	bzero((void*)f_nat_table,2*NAT_TABLE_SIZE*sizeof(nat_entry));
	/* Set the base address for reverse NAT table */
	r_nat_table=f_nat_table+NAT_TABLE_SIZE;

	/* Allocate the NAT index table in DRAM */
	err = ix_rm_mem_alloc(IX_MEMORY_TYPE_DRAM, 0,
	                      2*NAT_TABLE_SIZE*sizeof(unsigned int),
	                      (void**)&f_index);
	if (err != IX_SUCCESS)
		panic("ix_rm_mem_alloc failed for NAT index table\n");
	/* Clear the NAT index table */
	bzero((void*)f_index,2*NAT_TABLE_SIZE*sizeof(unsigned int));
	/* Set the base for the reverse NAT index table */
	r_index=f_index+NAT_TABLE_SIZE;

	/* Allocate the timer table in SRAM */
	err = ix_rm_mem_alloc(IX_MEMORY_TYPE_SRAM, 0, NAT_TABLE_SIZE,
	                      (void**)&f_timer);
	if (err != IX_SUCCESS)
		panic("ix_rm_mem_alloc failed for timer table\n");
	/* Clear the timer table */
	bzero((void*)f_timer,2*NAT_TABLE_SIZE);
	/* Set the base address for the reverse timer table */
	r_timer=f_timer+NAT_TABLE_SIZE;

	/* Allocate the ARP table in DRAM */
	err = ix_rm_mem_alloc(IX_MEMORY_TYPE_SRAM, 1,
	          ARP_TABLE_SIZE*sizeof(arp_entry), (void**)&arp_table);
	if (err != IX_SUCCESS)
		panic("ix_rm_mem_alloc failed for ARP table\n");
	/* Clear the ARP table */
	bzero((void*)arp_table,ARP_TABLE_SIZE*sizeof(arp_entry));

	/* Reset the microengines */
	printk("%s: Resetting all microengines\n", NAT_DRIVER_NAME);
	ix_rm_ueng_reset_all();

	/* Get the microcode from the UOF file */
	printk("%s: Setting ucode\n", NAT_DRIVER_NAME);
	err = ix_rm_ueng_set_ucode(Uof_file);
	if (err != IX_SUCCESS)
		panic("ix_rm_ueng_set_ucode failed\n");

	/* Patch the microcode symbols before actually */
	/* loading microcode.                          */
	if (patch_microblocks(hwFreeListInfo) < 0) 
		return(-1);

	/* Create Scratch rings */
	if (create_scr_rings() < 0)
		return(-1);

	/* Load microcode into microengines */
	printk("%s: Loading ucode\n", NAT_DRIVER_NAME);
	err = ix_rm_ueng_load();
	if (err != IX_SUCCESS)
		panic("ix_rm_ueng_load failed\n");

	/* Initialize hash unit */
	if (init_hash() < 0)
		return(-1);

	/* Start the assigned microengines */
	for (i=0;i<ME_NUM;i++) {
		if ((ME_MASK>>i)&0x1) {
			printk("%s: Starting ME%i\n",NAT_DRIVER_NAME, i);
			err = ix_rm_ueng_start(ME_ID(i),CONTEXT_MASK);
			if (err != IX_SUCCESS)
			   panic("ix_rm_ueng_start failed for ME %i\n",i);
		}
	}

	/* Resolve an ARP entry for the gateway */
	if (resolve_arp(GW_IP) != IX_SUCCESS)
		panic("can't resolve ARP entry for the gateway\n");

	/* Create an execution engine (i.e., a kernel thread) for the */
	/* NAT timer aging procedure                                  */
	err=ix_cci_init(); /* Initialize Intel's CCI */
	if (err != IX_SUCCESS)
		panic("ix_cci_init failed\n");
	printk("%s: Creating timer thread\n",NAT_DRIVER_NAME);
	err=ix_cci_exe_run(NULL,exe_init_f,exe_fini_f,"NAT timer",
	                   &exeHandle);
	if (err != IX_SUCCESS) {
		ix_cci_fini();
		panic("ix_cci_exe_run failed\n");
	}
	return 0;
}

/* Cleanup */
void cleanup_module()
{
	ix_error err;
	int i;

	/* Stop each of the assigned microengines */
	for (i=0;i<ME_NUM;i++) {
		if ((ME_MASK>>i)&0x1) {
			printk("%s: Stopping ME%i\n",NAT_DRIVER_NAME,i);
			err = ix_rm_ueng_stop(ME_ID(i));
			if (err != IX_SUCCESS)
			    printk(
			         "%s: ix_rm_ueng_stop failed for ME %i\n",
			         NAT_DRIVER_NAME,i);
		}
	}

	/* Unregister the packet handler */
	ix_rm_packet_handler_unregister(NAT_CC_ID);

	/* Terminate the timer thread */
	printk("%s: Stopping timer thread\n",NAT_DRIVER_NAME);
	ix_cci_exe_shutdown(exeHandle);
	ix_cci_fini();

	/* Terminate the Resource Manager */
	ix_rm_term();

	/* unregister pseudo-device */
	unregister_chrdev(NAT_major, NAT_DRIVER_NAME);
}


/* NAT table management: periodically go through the timer table */
/* and update (age) each of the  timers                          */
ix_error nat_table_timer(void* dummy)
{
	int i;
	for (i=0;i<2*NAT_TABLE_SIZE;i++)
		if (f_nat_table[i].valid)
			f_timer[i]=f_timer[i]>>1;
	return(IX_SUCCESS);
}

ix_error exe_init_f(ix_exe_handle exeHandle,void** ppContext)
{
	ix_cc_init_context dummy;
	return ix_cci_cc_create(exeHandle,cc_init_f,cc_fini_f,
				(void*)&dummy,&ccHandle);
}

ix_error exe_fini_f(ix_exe_handle exeHandle,void* pContext)
{
	return ix_cci_cc_destroy(ccHandle);
}

ix_error  cc_init_f(ix_cc_handle ccHandle,void** ppContext)
{
	/* Age each timer every AGING_INTERVAL */
	return ix_cci_cc_add_event_handler(ccHandle,AGING_INTERVAL,
		nat_table_timer,IX_EVENT_TYPE_PERIODIC,1,&eveHandle);
}

ix_error  cc_fini_f(ix_cc_handle ccHandle,void* pContext)
{
	return ix_cci_cc_remove_event_handler(ccHandle,eveHandle);
}

/******************************************************************/
/* Patch the microcode symbols before actually loading microcode. */
/*                                                                */
/* The imported variables that must be patched are:               */
/* BUF_FREE_LIST0   -- get from freelist allocation,              */
/*                     used by all microblocks                    */
/* BUF_SRAM_BASE    -- get from freelist allocation,              */
/*                     used by all microblocks                    */
/* DL_REL_BASE      -- compute from freelist allocation           */
/*                     parameters, used by all microblocks        */
/* FREE_LIST_ID     -- get from freelist allocation,              */
/*                     used by RX microblock only                 */
/* PACKET_COUNTERS_SRAM_BASE -- get from memory allocation for    */
/*                              RX counters, used by              */
/*                              RX microblock only                */
/* PACKET_TX_COUNTER_BASE    -- get from memory allocation for    */
/*                              TX counters, used by              */
/*                              TX microblock only                */
/* ARP_TABLE_BASE   -- get from memory allocation,                */
/*                     used by NAT microblock only                */
/* NAT_TABLE_BASE   -- get from memory allocation,                */
/*                     used by NAT microblock only                */
/* TIMER_TABLE_BASE -- get from memory allocation,                */
/*                     used by NAT microblock only                */
/* GATEWAY_IP_ADDR  -- gateway IP address, hardcoded,             */
/*                     used by NAT microblock only                */
/* IF0_IP, IF1_IP,                                                */
/* IF0_ETH_W0, IF0_ETH_W1,                                        */
/* IF1_ETH_W0, IF1_ETH_W1 -- interface settings from interface    */
/*                           table, used by NAT microblock only   */
/******************************************************************/
int patch_microblocks(ix_buffer_free_list_info   hwFreeListInfo) 
{
	ix_error err;
	ix_imported_symbol importSymbols[15];
	ix_uint32       memChan;
	ix_uint32       offset;

	/* Set common symbols */
	importSymbols[0].m_Name="BUF_FREE_LIST0";
	importSymbols[0].m_Value =  hwFreeListInfo.m_FreeListInfo;

	importSymbols[1].m_Name="BUF_SRAM_BASE";
	err = ix_rm_get_phys_offset( hwFreeListInfo.m_pMetaBaseAddress,
	                             NULL,&memChan,&offset,NULL);
	if (err != IX_SUCCESS)
		panic("ix_rm_get_phys_offset failed for %s\n",
		      importSymbols[1].m_Name);
	importSymbols[1].m_Value = ME_SRAM_ADDR(offset,memChan);

	importSymbols[2].m_Name = "DL_REL_BASE";
	err = ix_rm_get_phys_offset(hwFreeListInfo.m_pDataBaseAddress,
	                            NULL,&memChan,&offset,NULL);
	if (err != IX_SUCCESS)
		panic("ix_rm_get_phys_offset failed for %s\n",
		      importSymbols[2].m_Name);
	importSymbols[2].m_Value = offset -
	     ((importSymbols[1].m_Value*hwFreeListInfo.m_DataElementSize)/
	        hwFreeListInfo.m_MetaElementSize);
	/* Set RX specific symbols */
	importSymbols[3].m_Name="PACKET_COUNTERS_SRAM_BASE";
	err = ix_rm_get_phys_offset(rx_cntr,NULL,&memChan,&offset,NULL);
	if (err != IX_SUCCESS)
		panic("ix_rm_get_phys_offset failed for %s\n",
		      importSymbols[3].m_Name);
	importSymbols[3].m_Value = ME_SRAM_ADDR(offset,memChan);

	importSymbols[4].m_Name="FREE_LIST_ID";
	importSymbols[4].m_Value =  hwFreeListInfo.m_FreeListInfo1;

	/* Patch ME 0x00 -- RX microblock */
	err = ix_rm_ueng_patch_symbols(0x00,5,importSymbols);
	if (err != IX_SUCCESS)
	     panic("ix_rm_ueng_patch_symbols failed for RX microblock\n");

	/* Set NAT specific symbols */
	importSymbols[3].m_Name="NAT_TABLE_BASE";
	err = ix_rm_get_phys_offset((void*)f_nat_table,
	                             NULL,&memChan,&offset,NULL);
	if (err != IX_SUCCESS)
		panic("ix_rm_get_phys_offset failed for %s\n",
		       importSymbols[3].m_Name);
	importSymbols[3].m_Value = offset;
	importSymbols[4].m_Name="ARP_TABLE_BASE";
	err = ix_rm_get_phys_offset((void*)arp_table,
	                            NULL,&memChan,&offset,NULL);
	if (err != IX_SUCCESS)
		panic("ix_rm_get_phys_offset failed for %s\n",
		      importSymbols[4].m_Name);
	importSymbols[4].m_Value = ME_SRAM_ADDR(offset,memChan);
	importSymbols[5].m_Name="GATEWAY_IP_ADDR";
	importSymbols[5].m_Value=gateway_ip;
	importSymbols[6].m_Name="IF0_IP";
	importSymbols[6].m_Value=iface_table[0].ip_addr;
	importSymbols[7].m_Name="IF0_ETH_W0";
	importSymbols[7].m_Value=iface_table[0].eth_w0;
	importSymbols[8].m_Name="IF0_ETH_W1";
	importSymbols[8].m_Value=iface_table[0].eth_w1;
	importSymbols[9].m_Name="IF1_IP";
	importSymbols[9].m_Value=iface_table[1].ip_addr;
	importSymbols[10].m_Name="IF1_ETH_W0";
	importSymbols[10].m_Value=iface_table[1].eth_w0;
	importSymbols[11].m_Name="IF1_ETH_W1";
	importSymbols[11].m_Value=iface_table[1].eth_w1;
	importSymbols[12].m_Name="TIMER_TABLE_BASE";
	err = ix_rm_get_phys_offset((void*)f_timer,
	                            NULL,&memChan,&offset,NULL);
	if (err != IX_SUCCESS)
		panic("ix_rm_get_phys_offset failed for %s\n",
		      importSymbols[12].m_Name);
	importSymbols[12].m_Value = ME_SRAM_ADDR(offset,memChan);

	/* Patch ME 0x01 -- NAT microblock */
	err = ix_rm_ueng_patch_symbols(0x01,13,importSymbols);
	if (err != IX_SUCCESS)
	    panic("ix_rm_ueng_patch_symbols failed for NAT microblock\n");
	/* Patch ME 0x11 -- NAT microblock */
	err = ix_rm_ueng_patch_symbols(0x11,13,importSymbols);
	if (err != IX_SUCCESS)
	    panic("ix_rm_ueng_patch_symbols failed for NAT microblock\n");

	/* Set counter base for TX */
	importSymbols[3].m_Name="PACKET_TX_COUNTER_BASE";
	err = ix_rm_get_phys_offset((void*)tx_cntr,
	                            NULL,&memChan,&offset,NULL);
	if (err != IX_SUCCESS)
		panic("ix_rm_get_phys_offset failed for %s\n",
		      importSymbols[3].m_Name);
	importSymbols[3].m_Value = ME_SRAM_ADDR(offset,memChan);

	/* Patch ME 0x10 -- TX microblock */
	err = ix_rm_ueng_patch_symbols(0x10,4,importSymbols);
	if (err != IX_SUCCESS)
	   panic("ix_rm_ueng_patch_symbols failed for TX microblock\n");
	return(1);
}

/* Function to create RX and TX Scratch memory rings */
int create_scr_rings() 
{
	ix_error err;
	err = ix_rm_hw_scratch_ring_create(0,
	                   PKT_RX_TO_NAT_SCR_RING_SIZE,
	                   PKT_RX_TO_NAT_SCR_RING,  &rxToNatRing);
	if (err != IX_SUCCESS)
	  panic("ix_rm_hw_scratch_ring_create failed for Rx->Nat ring\n");

	err = ix_rm_hw_scratch_ring_create(0,
	                   PACKET_TX_SCR_RING_0_SIZE,
	                   PACKET_TX_SCR_RING_0, &txScrRing[0]);
	if (err != IX_SUCCESS)
	     panic("ix_rm_hw_scratch_ring_create failed for TX 0 ring\n");
	err = ix_rm_hw_scratch_ring_create(0,
	                   PACKET_TX_SCR_RING_1_SIZE,
	                   PACKET_TX_SCR_RING_1, &txScrRing[1]);
	if (err != IX_SUCCESS)
	     panic("ix_rm_hw_scratch_ring_create failed for TX 1 ring\n");
	err = ix_rm_hw_scratch_ring_create(0,
	                   PACKET_TX_SCR_RING_2_SIZE,
	                   PACKET_TX_SCR_RING_2, &txScrRing[2]);
	if (err != IX_SUCCESS)
	     panic("ix_rm_hw_scratch_ring_create failed for TX 2 ring\n");
	err = ix_rm_hw_scratch_ring_create(0,
	                   PACKET_TX_SCR_RING_3_SIZE,
	                   PACKET_TX_SCR_RING_3, &txScrRing[3]);
	if (err != IX_SUCCESS)
	     panic("ix_rm_hw_scratch_ring_create failed for TX 3 ring\n");
	return(1);
}

/* Function to initialize the 128-bit and 48-bit hash multipliers */
int init_hash() 
{
	ix_error err;
	ix_hash_multiplier_128 hash128m;
	ix_hash_multiplier_48 hash48m;
	hash128m.m_LW0=HASH_MULT_W0;
	hash128m.m_LW1=HASH_MULT_W1;
	hash128m.m_LW2=HASH_MULT_W2;
	hash128m.m_LW3=HASH_MULT_W3;
	printk("%s: Setting hash 128 multiplier to 0x%08X%08X%08X%08X\n",
	      NAT_DRIVER_NAME,
	      (unsigned int)hash128m.m_LW3, (unsigned int)hash128m.m_LW2,
	      (unsigned int)hash128m.m_LW1, (unsigned int)hash128m.m_LW0);
	err=ix_rm_hash_128_multiplier_set(&hash128m);
	if (err != IX_SUCCESS)
		panic("ix_rm_hash_128_multiplier_set failed\n");
	if (err != IX_SUCCESS)
		panic("ix_rm_hash_64_multiplier_set failed\n");
	hash48m.m_LW0=HASH_MULT_W0;
	hash48m.m_LW1=HASH_MULT_W1;
	printk("%s: Setting hash 48 multiplier to 0x%08X%08X\n",
	     NAT_DRIVER_NAME,
	     (unsigned int)hash48m.m_LW1, (unsigned int)hash48m.m_LW0);
	err=ix_rm_hash_48_multiplier_set(&hash48m);
	if (err != IX_SUCCESS)
		panic("ix_rm_hash_48_multiplier_set failed\n");
	return(1);
}
