/*
 * (cm_bp.c)-manages pool of re-usable, same-sized memory blocks
 * 
 * A buffer table manager is created within an existing memory pool. No
 * 'bp_destroy()' is required when the memory pool is freed, so is
 * everything within it. No module initialization is required. This 
 * works with memory pools, so memory pool module should be initialized 
 * first.
 *
 * 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_BP_C

#include <linux/kernel.h>
#include <linux/sched.h>
#include <net/cm_types.h>
#include <net/cm_mm.h>
#include <net/cm_bp.h>
#include <net/llc_dbg.h>

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

/* 
 * Function: bp_create
 * 
 * Description: 
 *  Creates buffer pool in memory pool, creates a table of how_many_buffers 
 *  count buffers in memory pool pointed by memory_pool_handle. Buffer length   
 *  is buffer_size bytes. If how_many_buffers is zero, it allocates all 
 *  available memory in memory_pool_handle for buffers and adjusts values.
 *  The buffer address is returned in buf_tbl. The buffer allocation table is
 *  initialized to address of the actual buffers.
 *
 * Parameters:
 *  mph_t memory_pool_handle : This argument points to memory pool.
 *  us32 buffer_size : This argument is buffer length in bytes.
 *  us32 how_many_buffers : This argument is number of buffers.
 *  buffer_pool_t **buf_tbl : The buffer address is returned in this.
 *  
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
bp_create (mph_t memory_pool_handle, us32 buffer_size,
		us32 how_many_buffers, buffer_pool_t **buf_tbl)
{
	us8 *start;
	us16 rc;
	us32 i;
	us32 memory_avail;
	us32 memory_reqd;
	us32 table_size;
	buffer_pool_t *bt;

	buffer_size = (buffer_size & 1 ? ++buffer_size : buffer_size);
	rc = mm_query_pool (memory_pool_handle, &memory_avail);
	
	if (!rc) {
		memory_avail -= sizeof(buffer_pool_t);
		if (!how_many_buffers) {
			how_many_buffers = memory_avail / (buffer_size + 
							sizeof (us8 *));
		}
	
		table_size = (how_many_buffers * sizeof (us8 *));
		memory_reqd = (buffer_size * how_many_buffers) + table_size;
	
		if (memory_reqd <= memory_avail) {
		
			bt = (buffer_pool_t *) mm_malloc (memory_pool_handle,
								memory_reqd);
			if (bt) {
				bt->table = (us8 **) ((us8 *) bt + 
						sizeof (buffer_pool_t));
				bt->start_memory = start = (us8 *) bt->table + 
								table_size;

				for (i = 0; i < how_many_buffers; i++) {
					bt->table [i] = start;
					start += buffer_size;
				} 
				bt->buffer_size = buffer_size;
				bt->number_buffers = how_many_buffers;
				*buf_tbl = bt;
				rc = 0;
			} else {
				rc = 1; /* memory allocation error */
			}
		} else {
			rc = 1; /* not enough available memory */
		}
	} else {            
		rc = 1; /* unable to query memory pool */
	}
	if (rc) {
		FDBG_ERR_MSG((KERN_ERR "\nbp_create failed,(buffer_size=%ld),
			(nmbr_buffers=%ld)\n", buffer_size,how_many_buffers));
	}
	return (rc);
}


/* 
 * Function: bp_alloc
 * 
 * Description: 
 *  Allocates a buffer from buffer pool, tbl, and returns its address in 
 *  buffer_allocated. The allocation table of buffer pool is adjusted to 
 *  represent the allocation.
 *  
 * Parameters:
 *  buffer_pool_t *tbl : This is the address of buffer pool.
 *  us8 **buffer_allocated : This is the address of allocated buffer.
 *  
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
bp_alloc (buffer_pool_t *tbl, us8 **buffer_allocated)
{
	us8 **entry = tbl->table;
	us8 done = NO;
	us16 rc = 1;
	us32 i;
	us32 size = tbl->number_buffers;
	unsigned long flags;

	for (i = 0; (i < size) && (done == NO); i++) {
		save_flags(flags);
		cli();
		if (entry [i]) {
			*buffer_allocated = entry [i];
			entry [i] = NULLPTR;
			restore_flags(flags);
			done = YES;
			rc = 0;
		} else {
			restore_flags(flags);
		}
	}
	if (rc) {
		FDBG_ERR_MSG((KERN_ERR "bp_alloc failed \n"));
	}
	return (rc);
}


/* 
 * Function: bp_free
 * 
 * Description: 
 *  Returns a previously allocated buffer pointed by buffer_to_free to the 
 *  buffer pool, tbl. It does not check the validity of buffer_to_free address.
 *  
 * Parameters:
 *  buffer_pool_t *tbl : This is the address of buffer pool.
 *  us8 **buffer_to_free : This is the address of previously allocated buffer 
 *  		   	   that should be freed.
 *  
 * Returns:
 *  0 : success.
 *  1 : failure.
 */

us16 
bp_free (buffer_pool_t *tbl, us8 *buffer_to_free)
{
	us8 **entry = tbl->table;
	us8 done = NO;
	us16 rc = 1;
	us32 i;
	us32 size = tbl->number_buffers;
	unsigned long flags;
   
	for (i = 0; (i < size) && (done == NO); i++) {
		save_flags(flags);
		cli();
		if (!entry [i]) {
			entry [i] = buffer_to_free;
			restore_flags(flags);
			done = YES;
			rc = 0;
		} else {
			restore_flags(flags);
		}
	}

	if (rc) {
		FDBG_ERR_MSG((KERN_ERR "bp_free failed,buffer_to_free =%p \n",
						buffer_to_free));
	}
	return (rc);
}
