/*
 * (cm_frame.c)- manages frame manipulations.
 *
 * 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_FRAME_C                             

#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/byteorder.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <net/sock.h>
#include <net/cm_types.h>
#include <net/llc_if.h>
#include <net/llc_glob.h>
#include <net/cm_mm.h>
#include <net/os_if.h>
#include <net/cm_dll.h>
#include <net/cm_frame.h>          
#include <net/stk_rsrc.h>
#include <net/llc_dbg.h>

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

/* static functions */

static us16 frame_create (frame_t **frame_created, dll_t *list);

/* global static data */

static us16 Module_init = NO;

static mph_t Frame_memory_pool;

static dll_t Frame_available_list;

/*
 * Function: frame_init
 *
 * Description:
 *  Allocates a memory pool of size FRAME_MEMORY_POOL_SIZE defined in 
 *  "stkrsrc.h" and assigns the handle to "Frame_memory_Pool". Then it
 *  creates FRAME_LIST_SIZE number of frames in pool.These frames are 
 *  put in "Frame_available_list".
 *
 * Parameters:
 *  None
 *
 * Returns:
 *  0 : success
 *  1 : failure
 */
 
us16 
frame_init ()
{
	us16 i;
	us16 rc;


	frame_t *created_frame;


	if (Module_init == YES) {
		return (0);
	}


	rc = mm_get_pool (&Frame_memory_pool, FRAME_MEMORY_POOL_SIZE);
	if (!rc) {
		rc = dll_initialize (&Frame_available_list);
		if (!rc) {
			for (i = 0; (i < FRAME_LIST_SIZE) && !rc; i++) {
				rc = frame_create (&created_frame, 
						&Frame_available_list);
			}
		}
	}                   

	if (!rc) {
		Module_init = YES;
	} else {
		FDBG_ERR_MSG((KERN_ERR "\n Frame_init : failed\n"));
	}
	return (rc);
}


/*
 * Function: frame_exit
 *
 * Description:
 *  Deallocates Frame memory pool.
 *
 * Parameters:
 *  None
 *
 * Returns:
 *  0 : success
 *  1 : failure
 */

us16 
frame_exit ()
{

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

	Module_init = NO;

	mm_rtn_pool (Frame_memory_pool);

	return (0);
}


/*
 * Function: frame_allocate
 *
 * Description:
 *  Removes a frame from Frame_available_list and sets new_frame to its 
 *  address. It does not initialize frame structure fields.
 *
 * Parameters:
 *  frame_t **new_frame : This argument points to a new frame , after this 
 *  	function call.
 *				
 * Returns:
 *  0 : success
 *  1 : failure
 */

us16 
frame_allocate (frame_t **new_frame)
{
	us16 rc;
	unsigned long flags;

	save_flags(flags);
	cli();
	rc = dll_remove (&Frame_available_list, (void **) new_frame, 
				DLL_WHERE_HEAD);
	restore_flags(flags);
	return rc;
}


/*
 * Function: frame_pdu_allocate
 *
 * Description:
 *  Similar to frame_allocate, except that it allocates an sk_buff for frame and
 *  initializes sk_buff and frame_t fields. It sets new_frame to address of allocated frame.
 *
 * Parameters:
 *  frame_t **new_frame : This argument points to a new frame , after this 
 *  	function call.
 *				
 * Returns:
 *  0 : success
 *  1 : failure
 */

us16 
frame_pdu_allocate (frame_t **new_frame)
{
	us16 rc;
	struct sk_buff *skb;
	frame_t *frame;
	unsigned long flags;
	struct device *netdevs[11];


	save_flags(flags);
	cli();
	rc = dll_remove (&Frame_available_list, (void **) &frame, DLL_WHERE_HEAD);
	restore_flags(flags);
	if (!rc) {
		skb = alloc_skb (128, GFP_ATOMIC);
		if (skb) {
			skb->free = 1;
			frame->skb = skb;
			skb_reserve(skb, 50);
			frame->data_size = 0;
			frame->mac_type = ETH_P_802_2;
			((struct sk_buff *)frame->skb)->protocol = ETH_P_802_2;
			netdevs[0] = dev_base->next;
			frame->dev = netdevs[0];
			frame->mac_hdr = skb->head;
			*new_frame = frame;           
		} else {
			save_flags(flags);
			cli();
			dll_add (&Frame_available_list, (void *) frame, DLL_WHERE_TAIL);
			restore_flags(flags);
			rc = 1;
		}
	}

	if(rc){
		FDBG_ERR_MSG((KERN_ERR "\nframe_pdu_allocate : failed\n")); 
	}
	return (rc);           
}


/*
 * Function: frame_free
 *
 * Description:
 *  Stops frame timer and reinitializes frame fields and returns frame to dll_t 
 *  Frame_available_list.
 *
 * Parameters:
 *  frame_t *frame : This argument points to frame which must be freed.
 *				
 * Returns:
 *  0 : success
 *  1 : failure
 */

us16 
frame_free (frame_t *frame)
{
	us16 rc;
	unsigned long flags;

	memset (frame, 0, sizeof (frame_t));
	save_flags(flags);
	cli();
	rc = dll_add (&Frame_available_list, (void *) frame, DLL_WHERE_TAIL);
	restore_flags(flags);
	if(rc){
		FDBG_ERR_MSG((KERN_ERR "\nframe_free : failed,
			frame=%p\n",frame)); 
	}
	return (rc);
}



/*
 * Function: frame_create
 *
 * Description:
 *  Creates a frame and adds it to given list.
 *
 * Parameters:
 *  frame_t *frame : This argument points to frame which must be created.
 *  dll_t *frame_list : This argument points to list which created frame 
 *  	is added to it.
 *				
 * Returns:
 *  0 : success
 *  1 : failure
 */

static us16 
frame_create (frame_t **frame_created, dll_t *frame_list)
{                      
	us16 rc;

	frame_t *frame;

	frame = (frame_t *) mm_malloc (Frame_memory_pool, sizeof(frame_t));
	if (frame) {
		memset (frame, 0, sizeof(frame_t));

		rc = dll_add (frame_list, (void *) frame, DLL_WHERE_HEAD);
		if (!rc) {
			*frame_created = frame;
		}
	} else {
		rc = 1;
	}

	if(rc){
		FDBG_ERR_MSG((KERN_ERR "\nframe_create : failed\n")); 
	}

	return (rc);
}


/*
 * Function: frame_skb_free
 *
 * Description:
 *  Frees frame "sk_buff"s. 
 *
 * Parameters:
 *  frame_t *frame : This argument points to frame which it's sk_buff must be
 *  released.
 *				
 * Returns:
 *  0 : success
 *  1 : failure
 */

us16 
frame_skb_free(frame_t *frame)
{
	us16 rc = 0;

	if (frame) {
		if (frame->skb) {
			((struct sk_buff *)frame->skb)->free = 1;
			kfree_skb(frame->skb,FREE_WRITE);
		}
		rc = frame_free(frame);
	} else {
		FDBG_FRAME_IS_NULL("frame_skb_free :");
		rc = 1;
	}
	return (rc);
}

