/*
 *  nameserver.c
 *
 *  The nameserver module manages local name/value pairs that
 *  enables an application and other modules to store and retrieve
 *  values based on a name.
 *
 *  Copyright (C) 2008-2009 Texas Instruments, Inc.
 *
 *  This package is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 *  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 *  WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 *  PURPOSE.
 */
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <syslink/atomic_linux.h>

#include <nameserver.h>
#include <multiproc.h>
#include <nameserver_remote.h>

#define NS_MAX_NAME_LEN  			32
#define NS_MAX_RUNTIME_ENTRY 		(~0)
#define NS_MAX_VALUE_LEN			4

/*
 *  The dynamic name/value table looks like the following. This approach allows
 *  each instance table to have different value and different name lengths.
 *  The names block is allocated on the create. The size of that block is
 *  (max_runtime_entries * max_name_en). That block is sliced and diced up and
 *  given to each table entry.
 *  The same thing is done for the values block.
 *
 *     names                    table                  values
 *   -------------           -------------          -------------
 *   |           |<-\        |   elem    |   /----->|           |
 *   |           |   \-------|   name    |  /       |           |
 *   |           |           |   value   |-/        |           |
 *   |           |           |   len     |          |           |
 *   |           |<-\        |-----------|          |           |
 *   |           |   \       |   elem    |          |           |
 *   |           |    \------|   name    |  /------>|           |
 *   |           |           |   value   |-/        |           |
 *   -------------           |   len     |          |           |
 *                           -------------          |           |
 *                                                  |           |
 *                                                  |           |
 *                                                  -------------
 *
 *  There is an optimization for small values (e.g. <= sizeof(UInt32).
 *  In this case, there is no values block allocated. Instead the value
  *  field is used directly.  This optimization occurs and is managed when
 *  obj->max_value_len <= sizeof(Us3232).
 *
 *  The static create is a little different. The static entries point directly
 *  to a name string (and value). Since it points directly to static items,
 *  this entries cannot be removed.
 *  If max_runtime_entries is non-zero, a names and values block is created.
 *  Here is an example of a table with 1 static entry and 2 dynamic entries
 *
 *                           ------------
 *  this entries cannot be removed.
 *  If max_runtime_entries is non-zero, a names and values block is created.
 *  Here is an example of a table with 1 static entry and 2 dynamic entries
 *
 *                           ------------
 *                           |   elem   |
 *      "myName" <-----------|   name   |----------> someValue
 *                           |   value  |
 *     names                 |   len    |              values
 *   -------------           -------------          -------------
 *   |           |<-\        |   elem    |   /----->|           |
 *   |           |   \-------|   name    |  /       |           |
 *   |           |           |   value   |-/        |           |
 *   |           |           |   len     |          |           |
 *   |           |<-\        |-----------|          |           |
 *   |           |   \       |   elem    |          |           |
 *   |           |    \------|   name    |  /------>|           |
 *   |           |           |   value   |-/        |           |
 *   -------------           |   len     |          |           |
 *                           -------------          |           |
 *                                                  |           |
 *                                                  |           |
 *                                                  -------------
 *
 *  NameServerD uses a freeList and namelist to maintain the empty
 *  and filled-in entries. So when a name/value pair is added, an entry
 *  is pulled off the freeList, filled-in and placed on the namelist.
 *  The reverse happens on a remove.
 *
 *  For static adds, the entries are placed on the namelist statically.
 *
 *  For dynamic creates, the freeList is populated in postInt and there are no
 *  entries placed on the namelist (this happens when the add is called).
 *
 */

/* Macro to make a correct module magic number with refCount */
#define NAMESERVER_MAKE_MAGICSTAMP(x) ((NAMESERVER_MODULEID << 12u) | (x))

/*
 *  A name/value table entry
 */
struct nameserver_entry {
	struct list_head elem; /* List element */
	u32 hash; /* Hash value */
	char *name; /* Name portion of name/value pair */
	u32 len; /* Length of the value field. */
	void *buf; /* Value portion of name/value entry */
	bool collide; /* Does the hash collides? */
	struct nameserver_entry *next; /* Pointer to the next entry,
					used incase of collision only */
};

/*
 * A nameserver instance object
 */
struct nameserver_object {
	struct list_head elem;
	char *name; /* Name of the instance */
	u32 count; /* Counter for entries */
	struct mutex *gate_handle; /* Gate for critical regions */
	struct list_head name_list; /* Filled entries list */
	struct nameserver_params params; /* The parameter structure */
};


/* nameserver module state object */
struct nameserver_module_object {
	struct list_head obj_list;
	struct mutex *list_lock;
	struct nameserver_remote_object **remote_handle_list;
	atomic_t ref_count;
};

/*
 * Variable for holding state of the nameserver module.
 */
struct nameserver_module_object nameserver_state = {
	.obj_list     = LIST_HEAD_INIT(nameserver_state.obj_list),
	.list_lock    = NULL,
	.remote_handle_list = NULL,
};

/*
 * Lookup table for CRC calculation.
 */
static const u32 string_crc_table[256u] = {
  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
  0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
  0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
  0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
  0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
  0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
  0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
  0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
  0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
  0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
  0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
  0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
  0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
  0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
  0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
  0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
  0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
  0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
  0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
  0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
  0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
  0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};

/*
 * ======== nameserver_setup ========
 *  Purpose:
 *  This will calculate hash for a string
 */
static u32 nameserver_string_hash(const char *string)
{
	u32 i;
	u32 hash ;
	u32 len = strlen(string);

	for (i = 0, hash = len; i < len; i++)
		hash = (hash >> 8) ^
			string_crc_table[(hash & 0xff)] ^ string[i];

	return hash;
}

/*
 * ======== nameserver_setup ========
 *  Purpose:
 *  This will setup the nameserver module
 */
int nameserver_setup(void)
{
	struct nameserver_remote_object **list = NULL;
	s32 retval = 0;
	u16 nr_procs = 0;

	/* This sets the ref_count variable if not initialized, upper 16 bits is
	*   written with module Id to ensure correctness of refCount variable
	*/
	atomic_cmpmask_and_set(&nameserver_state.ref_count,
				NAMESERVER_MAKE_MAGICSTAMP(0),
				NAMESERVER_MAKE_MAGICSTAMP(0));

	if (atomic_inc_return(&nameserver_state.ref_count)
				!= NAMESERVER_MAKE_MAGICSTAMP(1)) {
		retval = -EEXIST;
		goto exit;
	}

	nr_procs = multiproc_get_max_processors();
	list = kmalloc(nr_procs * sizeof(struct nameserver_remote_object *),
					GFP_KERNEL);
	if (list == NULL) {
		retval = -ENOMEM;
		goto error;
	}

	memset(list , 0, nr_procs * sizeof(struct nameserver_remote_object *));
	nameserver_state.remote_handle_list = list;
	nameserver_state.list_lock = kmalloc(sizeof(struct mutex), GFP_KERNEL);
	if (nameserver_state.list_lock == NULL) {
		retval = -ENOMEM;
		goto error;
	}

	/* mutex is initialized with state = UNLOCKED */
	mutex_init(nameserver_state.list_lock);
	return 0;

error:
	kfree(list);
	printk(KERN_ERR "nameserver_setup failed, retval: %x\n", retval);
exit:
	return retval;
}
EXPORT_SYMBOL(nameserver_setup);

/*
 * ======== nameserver_destroy ========
 *  Purpose:
 *  This will destroy the nameserver module
 */
int nameserver_destroy(void)
{
	s32 retval = 0;
	struct mutex *lock = NULL;

	if (WARN_ON(atomic_cmpmask_and_lt(&(nameserver_state.ref_count),
				NAMESERVER_MAKE_MAGICSTAMP(0),
				NAMESERVER_MAKE_MAGICSTAMP(1)) == true)) {
		retval = -ENODEV;
		goto exit;
	}

	if (WARN_ON(nameserver_state.list_lock == NULL)) {
		retval = -ENODEV;
		goto exit;
	}

	/* If a nameserver instance exist, do not proceed  */
	if (!list_empty(&nameserver_state.obj_list)) {
		retval = -EBUSY;
		goto exit;
	}

	if (!(atomic_dec_return(&nameserver_state.ref_count)
					== NAMESERVER_MAKE_MAGICSTAMP(0))) {
		retval = -EBUSY;
		goto exit;
	}

	retval = mutex_lock_interruptible(nameserver_state.list_lock);
	if (retval)
		goto exit;

	lock = nameserver_state.list_lock;
	nameserver_state.list_lock = NULL;
	mutex_unlock(lock);
	kfree(lock);
	kfree(nameserver_state.remote_handle_list);
	nameserver_state.remote_handle_list = NULL;
	return 0;

exit:
	printk(KERN_ERR "nameserver_destroy failed, retval: %x\n", retval);
	return retval;
}
EXPORT_SYMBOL(nameserver_destroy);

/*!
 *  Purpose:
 *  Initialize this config-params structure with supplier-specified
 *  defaults before instance creation.
 */
int nameserver_params_init(struct nameserver_params *params)
{
	BUG_ON(params == NULL);
	params->check_existing = true;
	params->gate_handle = NULL;
	params->max_name_len = NS_MAX_NAME_LEN;
	params->max_runtime_entries = NS_MAX_RUNTIME_ENTRY;
	params->max_value_len = NS_MAX_VALUE_LEN;
	params->table_heap = NULL;
	return 0;
}
EXPORT_SYMBOL(nameserver_params_init);

/*
 * ======== nameserver_get_params ========
 *  Purpose:
 *  This will initialize config-params structure with
 *  supplier-specified defaults before instance creation
 */
int nameserver_get_params(void *handle,
		struct nameserver_params *params)
{
	struct nameserver_object *nshandle = NULL;

	BUG_ON(params == NULL);
	if (handle == NULL) {
		params->check_existing       = true;
		params->max_name_len         = NS_MAX_NAME_LEN;
		params->max_runtime_entries  = NS_MAX_RUNTIME_ENTRY;
		params->max_value_len        = NS_MAX_VALUE_LEN;
		params->gate_handle          = NULL;
		params->table_heap    	     = NULL;
	} else {
		nshandle = (struct nameserver_object *)handle;
		params->check_existing 	 = nshandle->params.check_existing;
		params->max_name_len 	 = nshandle->params.max_name_len;
		params->max_runtime_entries  =
					nshandle->params.max_runtime_entries;
		params->max_value_len    = nshandle->params.max_value_len;
		params->gate_handle      = nshandle->params.gate_handle;
		params->table_heap      = nshandle->params.table_heap;
	}
	return 0;
}
EXPORT_SYMBOL(nameserver_get_params);

/*
 * ======== nameserver_get_params ========
 *  Purpose:
 *  This will get the handle of a nameserver instance
 *  from name
 */
void *nameserver_get_handle(const char *name)
{
	struct nameserver_object *obj = NULL;

	BUG_ON(name == NULL);
	list_for_each_entry(obj, &nameserver_state.obj_list, elem) {
		if (strcmp(obj->name, name) == 0)
			goto succes;
	}
	return NULL;

succes:
	return (void *)obj;
}
EXPORT_SYMBOL(nameserver_get_handle);

/*
 * ======== nameserver_create ========
 *  Purpose:
 *  This will create a name server instance
 */
void *nameserver_create(const char *name,
			const struct nameserver_params *params)
{
	struct nameserver_object *new_obj = NULL;
	u32 name_len;
	s32 retval = 0;

	BUG_ON(name == NULL);
	BUG_ON(params == NULL);

	name_len = strlen(name) + 1;
	if (name_len > params->max_name_len) {
		retval = E2BIG;
		goto exit;
	}

	retval = mutex_lock_interruptible(nameserver_state.list_lock);
	if (retval)
		goto exit;

	/* check if the name is already registered or not */
	new_obj = nameserver_get_handle(name);
	if (new_obj != NULL) {
		retval = EEXIST;
		goto error;
	}

	new_obj = kmalloc(sizeof(struct nameserver_object), GFP_KERNEL);
	if (new_obj == NULL) {
		retval = -ENOMEM;
		goto error;
	}

	new_obj->name = kmalloc(name_len, GFP_ATOMIC);
	if (new_obj->name == NULL) {
		retval = -ENOMEM;
		goto error;
	}

	strncpy(new_obj->name, name, name_len);
	memcpy(&new_obj->params, params,
				sizeof(struct nameserver_params));
	if (params->max_value_len < sizeof(u32))
		new_obj->params.max_value_len = sizeof(u32);
	else
		new_obj->params.max_value_len = params->max_value_len;

	new_obj->gate_handle =
				kmalloc(sizeof(struct mutex), GFP_KERNEL);
	if (new_obj->gate_handle == NULL) {
			retval = -ENOMEM;
			goto error_mutex;
	}

	mutex_init(new_obj->gate_handle);
	new_obj->count = 0;
	/* Put in the nameserver instance to local list */
	INIT_LIST_HEAD(&new_obj->name_list);
	list_add_tail(&new_obj->elem, &nameserver_state.obj_list);
	mutex_unlock(nameserver_state.list_lock);
	return (void *)new_obj;

error_mutex:
	kfree(new_obj->name);

error:
	mutex_unlock(nameserver_state.list_lock);
	kfree(new_obj);

exit:
	printk(KERN_ERR "nameserver_create failed retval:%x \n", retval);
	return NULL;
}
EXPORT_SYMBOL(nameserver_create);


/*
 * ======== nameserver_delete ========
 *  Purpose:
 *  This will delete a name server instance
 */
int nameserver_delete(void **handle)
{
	struct nameserver_object *temp_obj = NULL;
	struct mutex *gate_handle = NULL;
	bool localgate = false;
	s32 retval = 0;

	BUG_ON(handle == NULL);
	temp_obj = (struct nameserver_object *) (*handle);
	retval = mutex_lock_interruptible(temp_obj->gate_handle);
	if (retval)
		goto exit;

	/* Do not proceed if an entry in the in the table */
	if (temp_obj->count != 0) {
		retval = -EBUSY;
		goto error;
	}

	retval = mutex_lock_interruptible(nameserver_state.list_lock);
	if (retval)
		goto error;

	list_del(&temp_obj->elem);
	mutex_unlock(nameserver_state.list_lock);
	gate_handle = temp_obj->gate_handle;
	/* free the memory allocated for instance name */
	kfree(temp_obj->name);
	/* Delete the lock handle if created internally */
	if (temp_obj->params.gate_handle == NULL)
		localgate = true;

	/* Free the memory used for handle */
	kfree(temp_obj);
	*handle = NULL;
	mutex_unlock(gate_handle);
	if (localgate == true)
		kfree(gate_handle);
	return 0;

error:
	mutex_unlock(temp_obj->gate_handle);
exit:
	printk(KERN_ERR "nameserver_delete failed retval:%x \n", retval);
	return retval;
}
EXPORT_SYMBOL(nameserver_delete);

/*
 * ======== nameserver_is_entry_found ========
 *  Purpose:
 *  This will  return true if the entry fond in the table
 */
static bool nameserver_is_entry_found(const char *name, u32 hash,
				struct list_head *list,
				struct nameserver_entry **entry)
{
	struct nameserver_entry *node = NULL;
	bool hash_match = false;
	bool name_match = false;


	list_for_each_entry(node, list, elem) {
		/* Hash not matchs, take next node	*/
		if (node->hash == hash)
			hash_match = true;
		else
			continue;
		/* If the name matchs, incase hash is duplicate */
		if (strcmp(node->name, name) == 0)
			name_match = true;

		if (hash_match && name_match) {
			if (entry != NULL)
				*entry = node;
			return true;
		}

		hash_match = false;
		name_match = false;
	}
	return false;
}

/*
 * ======== nameserver_add ========
 *  Purpose:
 *  This will  add an entry into a nameserver instance
 */
void *nameserver_add(void *handle, const char *name,
		void  *buffer, u32 length)
{
	struct nameserver_entry *new_node = NULL;
	struct nameserver_object *temp_obj = NULL;
	bool found = false;
	u32 hash;
	u32 name_len;
	s32 retval = 0;

	BUG_ON(handle == NULL);
	BUG_ON(name == NULL);
	BUG_ON(buffer == NULL);
	if (WARN_ON(length == 0)) {
		retval = -EINVAL;
		goto exit;
	}

	temp_obj = (struct nameserver_object *)handle;
	retval = mutex_lock_interruptible(temp_obj->gate_handle);
	if (retval)
		goto exit;

	if (temp_obj->count >= temp_obj->params.max_runtime_entries) {
		retval = -ENOSPC;
		goto error;
	}

	/* make the null char in to account */
	name_len = strlen(name) + 1;
	if (name_len > temp_obj->params.max_name_len) {
		retval = -E2BIG;
		goto error;
	}

	/* TODO : hash and collide ?? */
	hash = nameserver_string_hash(name);
	found = nameserver_is_entry_found(name, hash,
					&temp_obj->name_list, &new_node);
	if (found == true) {
		retval = -EEXIST;
		goto error;
	}

	new_node = kmalloc(sizeof(struct nameserver_entry), GFP_KERNEL);
	if (new_node == NULL) {
		retval = -ENOMEM;
		goto error;
	}

	new_node->hash    = hash;
	new_node->collide = true;
	new_node->len     = length;
	new_node->next    = NULL;
	new_node->name    = kmalloc(name_len, GFP_KERNEL);
	if (new_node->name == NULL) {
		retval = -ENOMEM;
		goto error;
	}

	new_node->buf  = kmalloc(length, GFP_KERNEL);
	if (new_node->buf == NULL) {
		retval = -ENOMEM;
		goto error1;
	}

	strncpy(new_node->name, name, name_len);
	memcpy(new_node->buf, buffer, length);
	list_add_tail(&new_node->elem, &temp_obj->name_list);
	temp_obj->count++;
	mutex_unlock(temp_obj->gate_handle);
	return new_node;

error1:
	kfree(new_node->name);

error:
	mutex_unlock(temp_obj->gate_handle);
	kfree(new_node);
exit:
	printk(KERN_ERR "nameserver_add failed status: %x \n", retval);
	return NULL;

}
EXPORT_SYMBOL(nameserver_add);

/*
 * ======== nameserver_add_uint32 ========
 *  Purpose:
 *  This will  a Uint32 value into a nameserver instance
 */
void *nameserver_add_uint32(void *handle, const char *name,
			u32 value)
{
	struct nameserver_entry *new_node = NULL;
	BUG_ON(handle == NULL);
	BUG_ON(name == NULL);

	new_node = nameserver_add(handle, name, &value, sizeof(u32));
	return new_node;
}
EXPORT_SYMBOL(nameserver_add_uint32);

/*
 * ======== nameserver_remove ========
 *  Purpose:
 *  This will  remove a name/value pair from a name server
 */
int nameserver_remove(void *handle, const char *name)
{
	struct nameserver_object *temp_obj = NULL;
	struct nameserver_entry *entry = NULL;
	bool found = false;
	u32 hash;
	u32 name_len;
	s32 retval = 0;

	BUG_ON(handle == NULL);
	BUG_ON(name == NULL);

	temp_obj = (struct nameserver_object *)handle;
	name_len = strlen(name) + 1;
	if (name_len > temp_obj->params.max_name_len) {
		retval = -E2BIG;
		goto exit;
	}

	retval = mutex_lock_interruptible(temp_obj->gate_handle);
	if (retval)
		goto exit;

	/* TODO :check collide & hash usage */
	hash = nameserver_string_hash(name);
	found = nameserver_is_entry_found(name, hash,
					&temp_obj->name_list, &entry);
	if (found == false) {
		retval = -ENOENT;
		goto error;
	}

	kfree(entry->buf);
	kfree(entry->name);
	list_del(&entry->elem);
	kfree(entry);
	temp_obj->count--;
	mutex_unlock(temp_obj->gate_handle);
	return 0;

error:
	mutex_unlock(temp_obj->gate_handle);

exit:
	printk(KERN_ERR "nameserver_remove failed status:%x \n", retval);
	return retval;
}
EXPORT_SYMBOL(nameserver_remove);

/*
 * ======== nameserver_remove_entry ========
 *  Purpose:
 *  This will  remove a name/value pair from a name server
 */
int nameserver_remove_entry(void *nshandle, void *nsentry)
{
	struct nameserver_entry *node = NULL;
	struct nameserver_object *handle = NULL;
	s32 retval = 0;

	BUG_ON(nshandle == NULL);
	BUG_ON(nsentry == NULL);

	handle = (struct nameserver_object *)nshandle;
	node = (struct nameserver_entry *)nsentry;
	retval = mutex_lock_interruptible(handle->gate_handle);
	if (retval)
		goto exit;

	kfree(node->buf);
	kfree(node->name);
	list_del(&node->elem);
	kfree(node);
	handle->count--;
	mutex_unlock(handle->gate_handle);
	return 0;

exit:
	printk(KERN_ERR "nameserver_remove_entry failed status:%x \n", retval);
	return retval;
}
EXPORT_SYMBOL(nameserver_remove_entry);


/*
 * ======== nameserver_get_local ========
 *  Purpose:
 *  This will retrieve the value portion of a name/value
 *  pair from local table
 */
int nameserver_get_local(void *handle, const char *name,
			void *buffer, u32 length)
{
	struct nameserver_object *temp_obj = NULL;
	struct nameserver_entry *entry = NULL;
	bool found = false;
	u32 hash;
	s32 retval = 0;

	BUG_ON(handle == NULL);
	BUG_ON(name == NULL);
	BUG_ON(buffer == NULL);
	if (WARN_ON(length == 0)) {
		retval = -EINVAL;
		goto exit;
	}

	temp_obj = (struct nameserver_object *)handle;
	retval = mutex_lock_interruptible(temp_obj->gate_handle);
	if (retval)
		goto exit;

	/* TODO :check collide & hash usage */
	hash = nameserver_string_hash(name);
	found = nameserver_is_entry_found(name, hash,
					&temp_obj->name_list, &entry);
	if (found == false) {
		retval = -ENOENT;
		goto error;
	}

	if (entry->len >= length) {
		memcpy(buffer, entry->buf, length);
		retval = length;
	} else {
		memcpy(buffer, entry->buf, entry->len);
		retval = entry->len;
	}

	mutex_unlock(temp_obj->gate_handle);
	return retval;

error:
	mutex_unlock(temp_obj->gate_handle);

exit:
	printk(KERN_ERR "nameserver_get_local entry not found!\n");
	return retval;
}
EXPORT_SYMBOL(nameserver_get_local);

/*
 * ======== nameserver_get ========
 *  Purpose:
 *  This will etrieve the value portion of a name/value
 *  pair from local table
 */
int nameserver_get(void *handle, const char *name,
		void *buffer, u32 length, u16 proc_id[])
{
	struct nameserver_object *temp_obj = NULL;
	u16 max_proc_id;
	u16 local_proc_id;
	s32 retval = -ENOENT;
	u32 i;

	BUG_ON(handle == NULL);
	BUG_ON(name == NULL);
	BUG_ON(buffer == NULL);
	if (WARN_ON(length == 0)) {
		retval = -EINVAL;
		goto exit;
	}

	temp_obj = (struct nameserver_object *)handle;
	max_proc_id = multiproc_get_max_processors();
	local_proc_id = multiproc_get_id(NULL);
	if (proc_id == NULL) {
		retval = nameserver_get_local(temp_obj, name,
						buffer, length);
		if (retval > 0) /* Got the value */
			goto exit;

		for (i = 0; i < max_proc_id; i++) {
			/* Skip current processor */
			if (i == local_proc_id)
				continue;

			if (nameserver_state.remote_handle_list[i] == NULL)
				continue;

			retval = nameserver_remote_get(
					nameserver_state.remote_handle_list[i],
					temp_obj->name, name, buffer, length);
			if (retval > 0 || ((retval < 0) &&
				(retval != -ENOENT))) /* Got the value */
				break;
		}
		goto exit;
	}

	for (i = 0; i < max_proc_id; i++) {
		/* Skip processor with invalid id */
		if (proc_id[i] == MULTIPROC_INVALIDID)
			continue;

		if (i == local_proc_id) {
			retval = nameserver_get_local(temp_obj,
							name, buffer, length);
			if (retval > 0)
				break;

		} else {
			retval = nameserver_remote_get(
				nameserver_state.remote_handle_list[proc_id[i]],
				temp_obj->name,	name, buffer, length);
			if (retval > 0 || ((retval < 0) &&
				(retval != -ENOENT))) /* Got the value */
				break;
		}
	}

exit:
	if (retval < 0)
		printk(KERN_ERR "nameserver_get failed: status=%x \n", retval);
	return retval;
}
EXPORT_SYMBOL(nameserver_get);

/*
 * ======== nameserver_get ========
 *  Purpose:
 *  This will etrieve the value portion of a name/value
 *  pair from local table
 *
 *  Returns the number of characters that matched with an entry
 *  So if "abc" was an entry and you called match with "abcd", this
 *  function will have the "abc" entry. The return would be 3 since
 *  three characters matched
 *
 */
int nameserver_match(void *handle, const char *name, u32 *value)
{
	struct nameserver_object *temp_obj = NULL;
	struct nameserver_entry *node = NULL;
	s32 retval = 0;
	u32 hash;
	bool found = false;

	BUG_ON(handle == NULL);
	BUG_ON(name == NULL);
	BUG_ON(value == NULL);

	temp_obj = (struct nameserver_object *)handle;
	retval = mutex_lock_interruptible(temp_obj->gate_handle);
	if (retval)
		goto exit;

	hash = nameserver_string_hash(name);
	list_for_each_entry(node, &temp_obj->name_list, elem) {
		if (node->hash == hash) {
			*value = *(u32 *)node->buf;
			found = true;
		}
	}

	if (found == false)
		retval = -ENOENT;

exit:
	printk(KERN_ERR "nameserver_match failed status:%x \n", retval);
	return retval;
}
EXPORT_SYMBOL(nameserver_match);

/*
 * ======== nameserver_register_remote_driver ========
 *  Purpose:
 *  This will register a remote driver for a processor
 */
int nameserver_register_remote_driver(void *handle, u16 proc_id)
{
	struct nameserver_remote_object *temp = NULL;
	s32 retval = 0;
	u16 proc_count;

	BUG_ON(handle == NULL);
	proc_count = multiproc_get_max_processors();
	if (WARN_ON(proc_id >= proc_count)) {
		retval = -EINVAL;
		goto exit;
	}

	temp = (struct nameserver_remote_object *)handle;
	nameserver_state.remote_handle_list[proc_id] = temp;
	return 0;

exit:
	printk(KERN_ERR
			"nameserver_register_remote_driver failed status:%x \n",
			retval);
	return retval;
}
EXPORT_SYMBOL(nameserver_register_remote_driver);

/*
 * ======== nameserver_unregister_remote_driver ========
 *  Purpose:
 *  This will unregister a remote driver for a  processor
 */
int nameserver_unregister_remote_driver(u16 proc_id)
{
	s32 retval = 0;
	u16 proc_count;

	proc_count = multiproc_get_max_processors();
	if (WARN_ON(proc_id >= proc_count)) {
		retval = -EINVAL;
		goto exit;
	}

	nameserver_state.remote_handle_list[proc_id] = NULL;
	return 0;

exit:
	printk(KERN_ERR
		"nameserver_unregister_remote_driver failed status:%x \n",
		retval);
	return retval;
}
EXPORT_SYMBOL(nameserver_unregister_remote_driver);