/*
 * (cm_dll.c)- doubly-linked list manager
 *
 * Copyright (c) 1997 by Procom Technology,Inc.
 *
 * This program can be redistributed or modified under the terms of the 
 * GNU General Public License as published by the Free Software Foundation.
 * This program is distributed without any warranty or implied warranty
 * of merchantability or fitness for a particular purpose.
 *
 * See the GNU General Public License for more details.
 *
 */
 

#define CM_DLL_C

#include <linux/kernel.h>
#include <net/cm_types.h>
#include <net/cm_dll.h>
#include <net/cm_mm.h>
#include <net/cm_bp.h>
#include <net/stk_rsrc.h>
#include <net/llc_if.h>
#include <net/llc_glob.h>
#include <net/llc_dbg.h>


#ifdef CM_DLL_DBG
  #define  DBG_MSG(body) { printk body; }
#else
  #define  DBG_MSG(body)  ;
#endif

/* static functions */
                       
static void *          list_remove_member (dll_t *list, dll_entry_t *this);
static void *          list_remove_last_member (dll_t *list);
static void *          list_remove_head (dll_t *list);
static void *          list_remove_tail (dll_t *list);
static void *          list_remove_middle (dll_entry_t *this);
static void *          list_add_member (dll_t *list, void *member, us8 where);
static void *          list_add_first (dll_t *list, void *member);
static void *          list_add_head (dll_t *list, void *member);
static void *          list_add_tail (dll_t *list, void *member);

static dll_entry_t *   list_search (dll_t *list, void *member);



/* static global data */

static us32 Module_init = NO;

static mph_t List_memory_pool;

static buffer_pool_t *Dll_buffer_pool;

/*
 * Function: dll_init
 * 
 * Description: 
 *  Allocated memory pool for DLL list entries and creates a buffer table in it
 *  for managing them. DLL_MEMORY_POOL_SIZE and DLL_MAX_LIST_ENTERIES defined 
 *  in stk_rsrc.h determine memory pool size, and entries in buffer table 
 *  respectively.
 *
 * Parameters:
 *  none
 *
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
dll_init ()
{
	us16 rc;


	if (Module_init == YES) {
		return (0);
	}
   
	rc = mm_get_pool (&List_memory_pool, DLL_MEMORY_POOL_SIZE);
	if (!rc) {
		rc = bp_create (List_memory_pool, (us32) (sizeof(dll_entry_t)),
				(us32) DLL_MAX_LIST_BUFFERS, &Dll_buffer_pool);
		if (!rc) {
			Module_init = YES;
		}
	}

	if (rc) {
		FDBG_ERR_MSG((KERN_ERR "\n dll_init : failed\n"));
		mm_rtn_pool (List_memory_pool);
	}
	return (rc);
}


/*
 * Function: dll_exit
 * 
 * Description: 
 *  Returns the memory allocated for Doubly Linked List (DLL) entries to 
 *  system free memory pool.
 *
 * Parameters:
 *  none
 *
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
dll_exit ()
{
	if (Module_init == NO) {
		return (0);
	}

	Module_init = NO;

	mm_rtn_pool (List_memory_pool);
   
	return (0);
}


/*
 * Function: dll_initialize
 * 
 * Description: 
 *  Initializes fields of a new DLL named list. This should be called right 
 *  after defining any dll_t variable and prior to calling access routine.
 *
 * Parameters:
 *  dll_t *list : This is a new DLL that should be initialized.
 *
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
dll_initialize (dll_t *list)
{
	if (Module_init == NO) {
		return (1);
	}

	list->head = list->tail = NULLPTR;
	list->size = 0;

	return (0);
}


/*
 * Function: dll_add
 * 
 * Description: 
 *  Allocates a dll_entry_t from List_memory_pool, adds "member" to that entry
 *  and links the entry to "list". The values for "where" argument defined in
 *  cm_dll.h are DLL_WHERE_HEAD, and DLL_WHERE_TAIL.
 *
 * Parameters:
 *  dll_t *list : This is a doubly linked list that member is added to it.
 *  void *member : This is an entry that added to list.
 *  us8 where : This argument determines location of member in the list.
 *
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
dll_add (dll_t *list, void *member, us8 where)
{
	void *new = NULLPTR;

	if (Module_init == NO) {
		return (1);
	}

	if (!list->size) {
		new = list_add_first (list, member);
	} else {   
		switch (where) {
			case DLL_WHERE_HEAD:

				new = list_add_head (list, member);
				break;
   
			case DLL_WHERE_TAIL:

				new = list_add_tail (list, member);
				break;
		}
	}

	return ((new ? 0 : 1));   
}


/*
 * Function: dll_match
 * 
 * Description: 
 *  Calls "match_func" on every "dll_entry_t" in "list" and returns a pointer 
 *  to user data in matched "dll_entry_t" in "return_list". The option 
 *  argument adjusts the behavior of this function, which its values may be 
 *  the logical or of the (DLL_MATCH_ALL, DLL_MATCH_ONE, DLL_MATCH_DELETE, 
 *  DLL_MATCH_PEEK) defined in "cm_dll.h" .
 * 
 * Parameters:
 *  dll_t *list : This argument is the input list 
 *  us16 (*match_func)(void *this, void *match_value) - This argument is a 
 *  	function which matches "match_value" with "this".
 *  void *match_value : This argument is a pointer to the value which may be
 *  	matched.
 *  void **return_this : This argument is a list of matched entries.
 *  us8 options : This argument adjusts the behavior of "match_func" function.
 *
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
dll_match (dll_t *list, us16 (*match_func)(void *this, void *match_value),
		void *match_value, void **return_this, us8 options)
{
	us16 rc = 1;

	us32 nbr_removed = 0;

	dll_entry_t *next;

	if (Module_init == NO) {
		return (1);
	}

	if ((options & DLL_MATCH_PEEK) && (options & DLL_MATCH_ALL)) {
		return (1);
	}

	next = list->head;

	while (next) {
		if (!match_func (next->this, match_value)) {
			if (options & DLL_MATCH_DELETE) {
				*return_this = next->this;
				list_remove_member (list, next);
				nbr_removed++;
			} else if (options & DLL_MATCH_PEEK) {
				*return_this = next->this;
			}

			if (options & DLL_MATCH_ONE) {
				return (0);
			}
		}

		next = next->next;
	}

	if (nbr_removed) {
		rc = 0;
	}

	if (return_this) {
		*return_this = NULLPTR;
	}

	return (rc);
}


/*
 * Function: dll_remove
 * 
 * Description: 
 *  Removes an entry from DLL pointed by "list" and sets the "member" to the
 *  user pointer in that entry. The values for "where" argument defined in 
 *  "cm_dll.h" are DLL_WHERE_HEAD, DLL_WHERE_TAIL. It also frees the buffer 
 *  entry in "DLL_buffer_pool".
 * 
 * Parameters:
 *  dll_t *list : This argument is a pointer to input list which the entry 
 *  	must be removed from it.
 *  void **member : This argument points to entry which must be removed.
 *  us8 where : This argument is location of entry which must be removed.
 *
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
dll_remove (dll_t *list, void **member, us8 where)
{
	if (Module_init == NO) {
		return (1);
	}

	*member = NULL;

	if (list->size) {

		if (list->size == 1) {
			*member =  list_remove_last_member (list);
		} else {   
			switch (where) {
				case DLL_WHERE_HEAD:
					*member = list_remove_head (list);
					break;
       
				case DLL_WHERE_TAIL:
					*member = list_remove_tail (list);
					break;
			}
		}
	}

	return ((*member ? 0 : 1));
}


/*
 * Function: dll_query
 * 
 * Description: 
 *  Returns the size of "list" (count of "list" entries).
 * 
 * Parameters:
 *  dll_t *list : This argument is a DLL input list which it's size was 
 *  	queried.
 * 
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
dll_query (dll_t *list)
{

	if (Module_init == NO) {
		return ((-1));
	}

	return (list->size);
}


/*
 * Function: dll_remove_this
 * 
 * Description: 
 *  Removes the "list" entry that contains "member" as user data.
 * 
 * Parameters:
 *  dll_t *list : This argument is a DLL input list which one of it's entries 
 *  	that contains "member" as user data, will be removed.
 *  void *member : This argument used to remove proper entry of list.
 * 
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
dll_remove_this (dll_t *list, void *member)
{
	dll_entry_t *this;

	if (Module_init == NO) {
		return (1);
	}

	this = list_search (list, member);
	if (this) {
		list_remove_member (list, this);
	}

	return ((this ? 0 : 1));
}


/*
 * Function: dll_move
 * 
 * Description: 
 *  Removes the dll_entry_t  containing "member" from "src_list" and adds 
 *  it to the "where" position of "dest_list". In other words, it is moved 
 *  from one list to another. The values for "where" arguments defined in 
 *  "cm_dll.h" are DLL_WHERE_HEAD and DLL_WHERE_TAIL.
 * 
 * Parameters:
 *  dll_t *src_list : This argument is source list which "member" is removed
 *  	from it.
 *  dll_t *dest_list : This argument is destination list which "member" is 
 *  	added to it.
 *  void *member : This argument is a DLL entry which must be moved.
 *  us8 dest_where : This argument is position of "dest_list" which "member"
 *  	must be added at it to "dest_list".
 * 
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
dll_move (dll_t *src_list, dll_t *dest_list, void *member, us8 dest_where)
{
	dll_entry_t *this;

	if (Module_init == NO) {
		return (1);
	}

	this = list_search (src_list, member);
	if (this) {
		this = list_add_member (dest_list, this->this, dest_where);
		if (this) {
			list_remove_member (src_list, this);
		}
	}

	return ((this ? 0 : 1));
}


/*
 * Function: dll_peek
 * 
 * Description: 
 *  Sets "member" to user data in dll_entry_t located at "where" of the 
 *  "list". It does not remove entry from list. The value for "where" 
 *  argument defined in "cm_dll.h" are DLL_WHERE_HEAD and DLL_WHERE_TAIL.
 *  
 * Parameters:
 *  dll_t *list : This argument points to list which member is set from it.
 *  void **member : This argument points to an entry which must be set to user
 *  	data in dll_entry_t located at where.
 *  us8 where : This argument is position of dll_entry_t of list which given 
 *  	member must be set to it.
 * 
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
dll_peek (dll_t *list, void **member, us8 where)
{
	if (Module_init == NO) {
		return (1);
	}
	*member = NULLPTR;

	if (list->size) {
		if (where == DLL_WHERE_HEAD) {
			*member = list->head->this;
		} else if (where == DLL_WHERE_TAIL) {
			*member = list->tail->this;
		}
	} 

	return ((*member ? 0 : 1));
}


/*
 * Function: list_remove_member
 * 
 * Description: 
 *  Removes given entry from the given list.
 *  
 * Parameters:
 *  dll_t *list : This argument is a DLL list which one of it's entries must 
 *  	be removed.
 *  dll_entry_t *this : This argument is pointer of entry which must be 
 *  	removed.
 * 
 * Returns:
 *  a pointer to removed member : success
 *  NULLPTR : failure
 */

static void * 
list_remove_member (dll_t *list, dll_entry_t *this)
{
	void *rtn;

	if (list->size == 1) {
		return (list_remove_last_member (list));
	
	} else if (list->head == this) {
		return (list_remove_head (list));
	
	} else if (list->tail == this) {
		return (list_remove_tail (list));
	
	} else {
		rtn = list_remove_middle (this);
	
		if (rtn) {
			list->size--;
		}
		return (rtn);
	}
}


/*
 * Function: list_remove_last_member
 * 
 * Description: 
 *  Removes the last member in the list. The list should be empty after
 *  removing of this member.
 *  
 * Parameters:
 *  dll_t *list : This argument is a pointer to list which it's last member
 *  	must be removed.
 * 
 * Returns:
 *  a pointer to removed member : success
 *  NULLPTR : failure
 */

static void * 
list_remove_last_member (dll_t *list)
{  
	void *removed_member;

	dll_entry_t *this;

	this = list->head;
	removed_member = this->this;
	list->head = list->tail = NULLPTR;
	list->size = 0;
	bp_free (Dll_buffer_pool, (us8 *) this);
	
	return (removed_member);   
}



/*
 * Function: list_remove_head
 * 
 * Description: 
 *  Removes head of given DLL list.
 *  
 * Parameters:
 *  dll_t *list : This argument is a pointer to list which it's head must be 
 *  	removed.
 * 
 * Returns:
 *  a pointer to removed member : success
 *  NULLPTR : failure
 */

static void * 
list_remove_head (dll_t *list)
{  
	void *removed_member;

	dll_entry_t *this;


	this = list->head;
	removed_member = this->this;
	list->head = list->head->next;
	list->head->prev = NULLPTR;
	list->size--;
	bp_free (Dll_buffer_pool, (us8 *) this);

	return (removed_member);
}


/*
 * Function: list_remove_tail
 * 
 * Description: 
 *  Removes tail of given DLL list.
 *  
 * Parameters:
 *  dll_t *list : This argument is a pointer to list which it's tail must be 
 *  	removed.
 * 
 * Returns:
 *  a pointer to removed member : success
 *  NULLPTR : failure
 */

static void * 
list_remove_tail (dll_t *list)
{  
	void *removed_member;

	dll_entry_t *this;


	this = list->tail;
	removed_member = this->this;
	list->tail = list->tail->prev;
	list->tail->next = NULLPTR;
	list->size--;
	bp_free (Dll_buffer_pool, (us8 *) this);

	return (removed_member);
}



/*
 * Function: list_remove_middle
 * 
 * Description: 
 *  Removes entry of given DLL list which it's pointer is given.
 *  
 * Parameters:
 *  dll_entry_t *this : This argument points to entry of list which must be
 *  	removed.
 * 
 * Returns:
 *  a pointer to removed member : success
 *  NULLPTR : failure
 */

static void * 
list_remove_middle (dll_entry_t *this)
{
	void *removed_member;

	removed_member = this->this;
	this->prev->next = this->next;
	this->next->prev = this->prev;
	bp_free (Dll_buffer_pool, (us8 *) this);

	return (removed_member);
}


/*
 * Function: list_add_member
 * 
 * Description: 
 *  Adds given member to given list at given positon("where").
 *  
 * Parameters:
 *  dll_t *list : This argument points to list which a member must be added 
 *  	to it.
 *  void *member : This argument points to member which must be added to list.
 *  us8 where : This argument is position of list at which member must 
 *  	be added.
 * 
 * Returns:
 *  a pointer to added member : success
 *  NULLPTR : failure
 */

static void * 
list_add_member (dll_t *list, void *member, us8 where)
{
	if (!list->size) {
		return (list_add_first (list, member));
	} else {
		if (where == DLL_WHERE_HEAD) {
			return (list_add_head (list, member));
		} else if (where == DLL_WHERE_TAIL) {
			return (list_add_tail (list, member));
		}
	}

	return (NULLPTR);
}


/*
 * Function: list_add_first
 * 
 * Description: 
 *  Adds given member as first member of given list.
 *  
 * Parameters:
 *  dll_t *list : This argument is a pointer to list which member must be 
 *  	added to it.
 *  void *member : This argument is pointer to an entry which must be added 
 *  	to the given list.
 * 
 * Returns:
 *  a pointer to added member : success
 *  NULLPTR : failure
 */

static void * 
list_add_first (dll_t *list, void *member)
{
	dll_entry_t *new;

	bp_alloc (Dll_buffer_pool, (us8 **) &new);
	if (new) {   
		new->this = member;
		new->next = new->prev = NULLPTR;
		list->head = list->tail = new;
		list->size = 1;
	} 

	return (new);
}


/*
 * Function: list_add_head
 * 
 * Description: 
 *  Adds given member at head of given list.
 *  
 * Parameters:
 *  dll_t *list : This argument is a pointer to list which member must be 
 *  	added to it.
 *  void *member : This argument is pointer to an entry which must be added 
 *  	to the given list.
 * 
 * Returns:
 *  a pointer to added member : success
 *  NULLPTR : failure
 */

static void * 
list_add_head (dll_t *list, void *member)
{
	dll_entry_t *new;

	bp_alloc (Dll_buffer_pool, (us8 **) &new);
	if (new) {   
		new->this = member;
		new->prev = NULLPTR;
		new->next = list->head;
		list->head->prev = new;
		list->head = new;         
		list->size++;
	} else {
		FDBG_ERR_MSG((KERN_ERR "list_add_head(called from dll_add) 
			failed\n"));
	}
	return (new);
}


/*
 * Function: list_add_tail
 * 
 * Description: 
 *  Adds given member at tail of given list.
 *  
 * Parameters:
 *  dll_t *list : This argument is a pointer to list which member must be 
 *  	added to it.
 *  void *member : This argument is pointer to an entry which must be added 
 *  	to the given list.
 * 
 * Returns:
 *  a pointer to added member : success
 *  NULLPTR : failure
 */

static void * 
list_add_tail (dll_t *list, void *member)
{
	dll_entry_t *new;

	bp_alloc (Dll_buffer_pool, (us8 **) &new);
	if (new) {   
		new->this = member;
		new->prev = list->tail;
		new->next = NULLPTR;
		list->tail->next = new;
		list->tail = new;
		list->size++;
	} else {
		FDBG_ERR_MSG((KERN_ERR "list_add_tail(called from dll_add) 
			failed\n"));
	}

	return (new);
}


/*
 * Function: list_add_tail
 * 
 * Description: 
 *  Searches the list in a linear fashion, from head to tail, until it hits
 *  the end of list or find the member in the list.
 *  
 * Parameters:
 *  dll_t *list : This argument is a pointer to list which it's member must 
 *  	be found.
 *  void *member : This argument is pointer of member which may be found in 
 *  	the given list.
 * 
 * Returns:
 *  a pointer to found member : success
 *  NULLPTR : failure
 */

static dll_entry_t * 
list_search (dll_t *list, void *member)
{
	dll_entry_t *dll_entry;

	for (dll_entry = list->head; dll_entry; dll_entry= dll_entry->next) {
		if (dll_entry->this == member) {
			return (dll_entry);
		}
	}

	return (NULLPTR);
}
