patch-2.1.132 linux/net/irda/irqueue.c

Next file: linux/net/irda/irsysctl.c
Previous file: linux/net/irda/irproc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.131/linux/net/irda/irqueue.c linux/net/irda/irqueue.c
@@ -0,0 +1,770 @@
+/*********************************************************************
+ *                
+ * Filename:      irqueue.c
+ * Version:       0.3
+ * Description:   General queue implementation
+ * Status:        Experimental.
+ * Author:        Dag Brattli <dagb@cs.uit.no>
+ * Created at:    Tue Jun  9 13:29:31 1998
+ * Modified at:   Mon Dec 14 20:11:07 1998
+ * Modified by:   Dag Brattli <dagb@cs.uit.no>
+ * 
+ *     Copyright (C) 1998, Aage Kvalnes <aage@cs.uit.no>
+ *     Copyright (C) 1998, Dag Brattli, 
+ *     All Rights Reserved.
+ *
+ *     This code is taken from the Vortex Operating System written by Aage
+ *     Kvalnes. Aage has agreed that this code can use the GPL licence,
+ *     although he does not use that licence in his own code.
+ *     
+ *     This copyright does however _not_ include the ELF hash() function
+ *     which I currently don't know which licence or copyright it
+ *     has. Please inform me if you know.
+ *      
+ *     This program is free software; you can redistribute it and/or 
+ *     modify it under the terms of the GNU General Public License as 
+ *     published by the Free Software Foundation; either version 2 of 
+ *     the License, or (at your option) any later version.
+ *  
+ *     Neither Dag Brattli nor University of Tromsų admit liability nor
+ *     provide warranty for any of this software. This material is 
+ *     provided "AS-IS" and at no charge.
+ *     
+ ********************************************************************/
+
+#include <net/irda/irda.h>
+#include <net/irda/irqueue.h>
+#include <net/irda/irmod.h>
+
+static QUEUE *dequeue_general( QUEUE **queue, QUEUE* element);
+static __u32 hash( char* name);
+
+/*
+ * Function hashbin_create ( type, name )
+ *
+ *    Create hashbin!
+ *
+ */
+hashbin_t *hashbin_new( int type)
+{
+	hashbin_t* hashbin;
+	
+	DEBUG( 4, "hashbin_create()\n");
+	
+	/*
+	 * Allocate new hashbin
+	 */
+	hashbin = kmalloc( sizeof(hashbin_t), GFP_ATOMIC);
+
+	/*
+	 * Initialize structure
+	 */
+	memset( hashbin, 0, sizeof(hashbin_t));
+	hashbin->hb_type = type;
+	hashbin->magic = HB_MAGIC;
+	
+	return hashbin;
+}
+
+/*
+ * Function hashbin_clear (hashbin, free_func)
+ *
+ *    Remove all entries from the hashbin, see also the comments in 
+ *    hashbin_delete() below
+ */
+int hashbin_clear( hashbin_t* hashbin, FREE_FUNC free_func)
+{
+	QUEUE* queue;
+	int i;
+	
+	ASSERT( hashbin != NULL, return -1;);
+	ASSERT( hashbin->magic == HB_MAGIC, return -1;);
+
+	/*
+	 * Free the entries in the hashbin
+	 */
+	for ( i = 0; i < HASHBIN_SIZE; i ++ ) {
+		queue = dequeue_first( (QUEUE**) &hashbin->hb_queue[ i]);
+		while( queue ) {
+			if ( free_func)
+				(*free_func)( queue );
+			queue = dequeue_first( 
+				(QUEUE**) &hashbin->hb_queue[ i]);
+		}
+	}
+	hashbin->hb_size = 0;
+
+	return 0;
+}
+
+
+/*
+ * Function hashbin_delete (hashbin, free_func)
+ *
+ *    Destroy hashbin, the free_func can be a user supplied special routine 
+ *    for deallocating this structure if it's complex. If not the user can 
+ *    just supply kfree, which should take care of the job.
+ */
+int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
+{
+	int i;
+	QUEUE* queue;
+
+	DEBUG( 4, "hashbin_destroy()\n");
+	
+	/*
+	 *  Free the entries in the hashbin, TODO: use hashbin_clear when
+	 *  it has been shown to work
+	 */
+	for ( i = 0; i < HASHBIN_SIZE; i ++ ) {
+		queue = dequeue_first( (QUEUE**) &hashbin->hb_queue[ i ] );
+		while( queue ) {
+			if ( free_func)
+				(*free_func)( queue );
+			queue = dequeue_first( 
+				(QUEUE**) &hashbin->hb_queue[ i ]);
+		}
+	}
+	
+	/*
+	 *  Free the hashbin structure
+	 */
+	hashbin->magic = ~HB_MAGIC;
+	kfree( hashbin );
+
+	return 0;
+}
+
+/*
+ * Function hashbin_lock (hashbin, hashv, name)
+ *
+ *    Lock the hashbin
+ *
+ */
+void hashbin_lock( hashbin_t* hashbin, __u32 hashv, char* name, 
+		   unsigned long flags)
+{
+	int bin;
+	
+	DEBUG( 0, "hashbin_lock\n");
+
+	ASSERT( hashbin != NULL, return;);
+	ASSERT( hashbin->magic == HB_MAGIC, return;);
+
+	/*
+	 * Locate hashbin
+	 */
+	if ( name )
+		hashv = hash( name );
+	bin = GET_HASHBIN( hashv);
+	
+	/* Synchronize */
+	if ( hashbin->hb_type & HB_GLOBAL ) {
+
+		spin_lock_irqsave( &hashbin->hb_mutex[ bin], flags);
+	} else {
+		save_flags( flags);
+		cli();
+	}
+}
+
+/*
+ * Function hashbin_unlock (hashbin, hashv, name)
+ *
+ *    Unlock the hashbin
+ *
+ */
+void hashbin_unlock( hashbin_t* hashbin, __u32 hashv, char* name, 
+		     unsigned long flags)
+{
+	int bin;
+
+	DEBUG( 0, "hashbin_unlock()\n");
+
+	ASSERT( hashbin != NULL, return;);
+	ASSERT( hashbin->magic == HB_MAGIC, return;);
+	
+	/*
+	 * Locate hashbin
+	 */
+	if ( name )
+		hashv = hash( name );
+	bin = GET_HASHBIN( hashv );
+	
+	/* Release lock */
+	if ( hashbin->hb_type & HB_GLOBAL) {
+		spin_unlock_irq( &hashbin->hb_mutex[ bin]);
+	} else if ( hashbin->hb_type & HB_LOCAL) {
+		restore_flags( flags);
+	}
+}
+
+/*
+ * Function hashbin_insert (hashbin, entry, name)
+ *
+ *    Insert an entry into the hashbin
+ *
+ */
+void hashbin_insert( hashbin_t* hashbin, QUEUE* entry, __u32 hashv, 
+		     char* name)
+{
+	unsigned long flags = 0;
+	int bin;
+
+	DEBUG( 4, "hashbin_insert()\n");
+
+	ASSERT( hashbin != NULL, return;);
+	ASSERT( hashbin->magic == HB_MAGIC, return;);
+
+	/*
+	 * Locate hashbin
+	 */
+	if ( name )
+		hashv = hash( name );
+	bin = GET_HASHBIN( hashv );
+
+	/* Synchronize */
+	if ( hashbin->hb_type & HB_GLOBAL ) {
+		spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags);
+
+	} else if ( hashbin->hb_type & HB_LOCAL ) {
+		save_flags(flags);
+		cli();
+	} /* Default is no-lock  */
+	
+	/*
+	 * Store name and key
+	 */
+	entry->q_hash = hashv;
+	if ( name )
+		strncpy( entry->q_name, name, 32);
+	
+	/*
+	 * Insert new entry first
+	 * TODO: Perhaps allow sorted lists?
+	 *       -> Merge sort if a sorted list should be created
+	 */
+	if ( hashbin->hb_type & HB_SORTED) {
+	} else {
+		enqueue_first( (QUEUE**) &hashbin->hb_queue[ bin ],
+			       entry);
+	}
+	hashbin->hb_size++;
+
+	/* Release lock */
+	if ( hashbin->hb_type & HB_GLOBAL) {
+
+		spin_unlock_irq( &hashbin->hb_mutex[ bin]);
+
+	} else if ( hashbin->hb_type & HB_LOCAL) {
+		restore_flags( flags);
+	}
+}
+
+/*
+ * Function hashbin_find (hashbin, hashv, name)
+ *
+ *    Find item with the given hashv or name
+ *
+ */
+void* hashbin_find( hashbin_t* hashbin, __u32 hashv, char* name )
+{
+	int bin, found = FALSE;
+	unsigned long flags = 0;
+	QUEUE* entry;
+
+	DEBUG( 4, "hashbin_find()\n");
+
+	ASSERT( hashbin != NULL, return NULL;);
+	ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
+
+	/*
+	 * Locate hashbin
+	 */
+	if ( name )
+		hashv = hash( name );
+	bin = GET_HASHBIN( hashv );
+	
+	/* Synchronize */
+	if ( hashbin->hb_type & HB_GLOBAL ) {
+		spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags);
+
+	} else if ( hashbin->hb_type & HB_LOCAL ) {
+		save_flags(flags);
+		cli();
+	} /* Default is no-lock  */
+	
+	/*
+	 * Search for entry
+	 */
+	entry = hashbin->hb_queue[ bin];
+	if ( entry ) {
+		do {
+			/*
+			 * Check for key
+			 */
+			if ( entry->q_hash == hashv ) {
+				/*
+				 * Name compare too?
+				 */
+				if ( name ) {
+					if ( strcmp( entry->q_name, name ) == 0 ) {
+						found = TRUE;
+						break;
+					}
+				} else {
+					found = TRUE;
+					break;
+				}
+			}
+			entry = entry->q_next;
+		} while ( entry != hashbin->hb_queue[ bin ] );
+	}
+	
+	/* Release lock */
+	if ( hashbin->hb_type & HB_GLOBAL) {
+		spin_unlock_irq( &hashbin->hb_mutex[ bin]);
+
+	} else if ( hashbin->hb_type & HB_LOCAL) {
+		restore_flags( flags);
+	}
+	
+	if ( found ) 
+		return entry;
+	else
+		return NULL;
+}
+
+void *hashbin_remove_first( hashbin_t *hashbin)
+{
+	unsigned long flags;
+	QUEUE *entry = NULL;
+
+	save_flags(flags);
+	cli();
+
+	entry = hashbin_get_first( hashbin);
+	if ( entry != NULL)
+		hashbin_remove( hashbin, entry->q_hash, NULL);
+
+	restore_flags( flags);
+
+	return entry;
+}
+
+
+/*
+ * Function hashbin_remove (hashbin, name)
+ *
+ *    Remove entry with the given name
+ *
+ */
+void* hashbin_remove( hashbin_t* hashbin, __u32 hashv, char* name)
+{
+	int   bin, found = FALSE;
+	unsigned long flags = 0;
+	QUEUE* entry;
+
+	DEBUG( 4, "hashbin_remove()\n");
+
+	ASSERT( hashbin != NULL, return NULL;);
+	ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
+	
+	/*
+	 * Locate hashbin
+	 */
+	if ( name )
+		hashv = hash( name );
+	bin = GET_HASHBIN( hashv );
+
+	if ( hashbin->hb_type & HB_GLOBAL ) {
+		spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags);
+
+	} else if ( hashbin->hb_type & HB_LOCAL ) {
+		save_flags(flags);
+		cli();
+	} /* Default is no-lock  */
+
+	/*
+	 * Search for entry
+	 */
+	entry = hashbin->hb_queue[ bin ];
+	if ( entry ) {
+		do {
+			/*
+			 * Check for key
+			 */
+			if ( entry->q_hash == hashv ) {
+				/*
+				 * Name compare too?
+				 */
+				if ( name ) {
+					if ( strcmp( entry->q_name, name) == 0)
+					{
+						found = TRUE;
+						break;
+					}
+				} else {
+					found = TRUE;
+					break;
+				}
+			}
+			entry = entry->q_next;
+		} while ( entry != hashbin->hb_queue[ bin ] );
+	}
+	
+	/*
+	 * If entry was found, dequeue it
+	 */
+	if ( found ) {
+		dequeue_general( (QUEUE**) &hashbin->hb_queue[ bin ],
+				 (QUEUE*) entry );
+		hashbin->hb_size--;
+
+		/*
+		 *  Check if this item is the currently selected item, and in
+		 *  that case we must reset hb_current
+		 */
+		if ( entry == hashbin->hb_current)
+			hashbin->hb_current = NULL;
+	}
+
+	/* Release lock */
+	if ( hashbin->hb_type & HB_GLOBAL) {
+		spin_unlock_irq( &hashbin->hb_mutex[ bin]);
+
+	} else if ( hashbin->hb_type & HB_LOCAL) {
+		restore_flags( flags);
+	}
+       
+	
+	/* Return */
+	if ( found ) 
+		return entry;
+	else
+		return NULL;
+	
+}
+
+/*
+ * Function hashbin_get_first (hashbin)
+ *
+ *    Get a pointer to first element in hashbin, this function must be
+ *    called before any calls to hashbin_get_next()!
+ *
+ */
+QUEUE *hashbin_get_first( hashbin_t* hashbin) 
+{
+	QUEUE *entry;
+	int i;
+
+	ASSERT( hashbin != NULL, return NULL;);
+	ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
+
+	if ( hashbin == NULL)
+		return NULL;
+
+	for ( i = 0; i < HASHBIN_SIZE; i ++ ) {
+		entry = hashbin->hb_queue[ i];
+		if ( entry) {
+			hashbin->hb_current = entry;
+			return entry;
+		}
+	}
+	/*
+	 *  Did not find any item in hashbin
+	 */
+	return NULL;
+}
+
+/*
+ * Function hashbin_get_next (hashbin)
+ *
+ *    Get next item in hashbin. A series of hashbin_get_next() calls must
+ *    be started by a call to hashbin_get_first(). The function returns
+ *    NULL when all items have been traversed
+ * 
+ */
+QUEUE *hashbin_get_next( hashbin_t *hashbin)
+{
+	QUEUE* entry;
+	int bin;
+	int i;
+
+	ASSERT( hashbin != NULL, return NULL;);
+	ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
+
+	if ( hashbin->hb_current == NULL) {
+		ASSERT( hashbin->hb_current != NULL, return NULL;);
+		return NULL;
+	}	
+	entry = hashbin->hb_current->q_next;
+	bin = GET_HASHBIN( entry->q_hash);
+
+	/*  
+	 *  Make sure that we are not back at the beginning of the queue
+	 *  again 
+	 */
+	if ( entry != hashbin->hb_queue[ bin ]) {
+		hashbin->hb_current = entry;
+
+		return entry;
+	}
+
+	/*
+	 *  Check that this is not the last queue in hashbin
+	 */
+	if ( bin >= HASHBIN_SIZE)
+		return NULL;
+	
+	/*
+	 *  Move to next queue in hashbin
+	 */
+	bin++;
+	for ( i = bin; i < HASHBIN_SIZE; i++ ) {
+		entry = hashbin->hb_queue[ i];
+		if ( entry) {
+			hashbin->hb_current = entry;
+			
+			return entry;
+		}
+	}
+	return NULL;
+}
+
+/*
+ * Function enqueue_last (queue, proc)
+ *
+ *    Insert item into end of queue.
+ *
+ */
+static void __enqueue_last( QUEUE **queue, QUEUE* element)
+{
+	DEBUG( 4, __FUNCTION__ "()\n");
+
+	/*
+	 * Check if queue is empty.
+	 */
+	if ( *queue == NULL ) {
+		/*
+		 * Queue is empty.  Insert one element into the queue.
+		 */
+		element->q_next = element->q_prev = *queue = element;
+		
+	} else {
+		/*
+		 * Queue is not empty.  Insert element into end of queue.
+		 */
+		element->q_prev         = (*queue)->q_prev;
+		element->q_prev->q_next = element;
+		(*queue)->q_prev        = element;
+		element->q_next         = *queue;
+	}	
+}
+
+inline void enqueue_last( QUEUE **queue, QUEUE* element)
+{
+	unsigned long flags;
+	
+        save_flags(flags);
+        cli();
+
+        __enqueue_last( queue, element);
+
+        restore_flags(flags);
+}
+
+/*
+ * Function enqueue_first (queue, proc)
+ *
+ *    Insert item first in queue.
+ *
+ */
+void enqueue_first(QUEUE **queue, QUEUE* element)
+{
+	
+	DEBUG( 4, "enqueue_first()\n");
+
+	/*
+	 * Check if queue is empty.
+	 */
+	if ( *queue == NULL ) {
+		/*
+		 * Queue is empty.  Insert one element into the queue.
+		 */
+		element->q_next = element->q_prev = *queue = element;
+		
+	} else {
+		/*
+		 * Queue is not empty.  Insert element into front of queue.
+		 */
+		element->q_next          = (*queue);
+		(*queue)->q_prev->q_next = element;
+		element->q_prev          = (*queue)->q_prev;
+		(*queue)->q_prev         = element;
+		(*queue)                 = element;
+	}
+}
+
+/*
+ * Function enqueue_queue (queue, list)
+ *
+ *    Insert a queue (list) into the start of the first queue
+ *
+ */
+void enqueue_queue( QUEUE** queue, QUEUE** list )
+{
+	QUEUE* tmp;
+	
+	/*
+	 * Check if queue is empty
+	 */ 
+	if ( *queue ) {
+		(*list)->q_prev->q_next  = (*queue);
+		(*queue)->q_prev->q_next = (*list); 
+		tmp                      = (*list)->q_prev;
+		(*list)->q_prev          = (*queue)->q_prev;
+		(*queue)->q_prev         = tmp;
+	} else {
+		*queue                   = (*list); 
+	}
+	
+	(*list) = NULL;
+}
+
+/*
+ * Function enqueue_second (queue, proc)
+ *
+ *    Insert item behind head of queue.
+ *
+ */
+#if 0
+static void enqueue_second(QUEUE **queue, QUEUE* element)
+{
+	DEBUG( 0, "enqueue_second()\n");
+
+	/*
+	 * Check if queue is empty.
+	 */
+	if ( *queue == NULL ) {
+		/*
+		 * Queue is empty.  Insert one element into the queue.
+		 */
+		element->q_next = element->q_prev = *queue = element;
+		
+	} else {
+		/*
+		 * Queue is not empty.  Insert element into ..
+		 */
+		element->q_prev = (*queue);
+		(*queue)->q_next->q_prev = element;
+		element->q_next = (*queue)->q_next;
+		(*queue)->q_next = element;
+	}
+}
+#endif
+
+/*
+ * Function dequeue (queue)
+ *
+ *    Remove first entry in queue
+ *
+ */
+QUEUE *dequeue_first(QUEUE **queue)
+{
+	QUEUE *ret;
+
+	DEBUG( 4, "dequeue_first()\n");
+	
+	/*
+	 * Set return value
+	 */
+	ret =  *queue;
+	
+	if ( *queue == NULL ) {
+		/*
+		 * Queue was empty.
+		 */
+	} else if ( (*queue)->q_next == *queue ) {
+		/* 
+		 *  Queue only contained a single element. It will now be
+		 *  empty.  
+		 */
+		*queue = NULL;
+	} else {
+		/*
+		 * Queue contained several element.  Remove the first one.
+		 */
+		(*queue)->q_prev->q_next = (*queue)->q_next;
+		(*queue)->q_next->q_prev = (*queue)->q_prev;
+		*queue = (*queue)->q_next;
+	}
+	
+	/*
+	 * Return the removed entry (or NULL of queue was empty).
+	 */
+	return ret;
+}
+
+/*
+ * Function dequeue_general (queue, element)
+ *
+ *
+ */
+static QUEUE *dequeue_general(QUEUE **queue, QUEUE* element)
+{
+	QUEUE *ret;
+	
+	DEBUG( 4, "dequeue_general()\n");
+	
+	/*
+	 * Set return value
+	 */
+	ret =  *queue;
+		
+	if ( *queue == NULL ) {
+		/*
+		 * Queue was empty.
+		 */
+	} if ( (*queue)->q_next == *queue ) {
+		/* 
+		 *  Queue only contained a single element. It will now be
+		 *  empty.  
+		 */
+		*queue = NULL;
+		
+	} else {
+		/*
+		 *  Remove specific element.
+		 */
+		element->q_prev->q_next = element->q_next;
+		element->q_next->q_prev = element->q_prev;
+		if ( (*queue) == element)
+			(*queue) = element->q_next;
+	}
+	
+	/*
+	 * Return the removed entry (or NULL of queue was empty).
+	 */
+	return ret;
+}
+
+/*
+ * Function hash (name)
+ *
+ *    This function hash the input string 'name' using the ELF hash
+ *    function for strings.
+ */
+static __u32 hash( char* name)
+{
+	__u32 h = 0;
+	__u32 g;
+	
+	while(*name) {
+		h = (h<<4) + *name++;
+		if ((g = (h & 0xf0000000)))
+			h ^=g>>24;
+		h &=~g;
+	}
+	return h;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov