/* 
 * tables.c
 * Copyright (C) 2002, AVM GmbH. All rights reserved.
 * 
 * This Software is  free software. You can redistribute and/or
 * modify such free software under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * The free software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this Software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, or see
 * http://www.opensource.org/licenses/lgpl-license.html
 * 
 * Contact: AVM GmbH, Alt-Moabit 95, 10559 Berlin, Germany, email: info@avm.de
 */

#include <linux/skbuff.h>
#include <linux/capi.h>
#include <linux/kernelcapi.h>
#include "defs.h"
#include "main.h"
#include "tools.h"
#include "driver.h"
#include "queue.h"
#include "tables.h"

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
void table_init (appltab_t ** tab) {

	if (NULL != (*tab = (appltab_t *) hmalloc (sizeof (appltab_t)))) {
		(*tab)->appl_root  = NULL;
		(*tab)->appl_count = 0;
	}
} /* table_init */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
void table_exit (appltab_t ** tab) {
	appl_t *	appp;
	appl_t *	tmp;
    
	assert (*tab);
	appp = (*tab)->appl_root;
	while (appp != NULL) {
		tmp = appp->succ;
		capi_card->count--;
		remove_appl (*tab, appp);
		if (appp->data != NULL) {
			hfree (appp->data);
		}
		appp = tmp;
	}
	hfree (*tab);
	*tab = NULL;
} /* table_exit */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
appl_t * create_appl (
	appltab_t *	tab, 
	unsigned	id, 
	unsigned	ncount, 
	unsigned	bcount, 
	unsigned	bsize
) {
	appl_t *	appp;

	if (NULL == (appp = (appl_t *) hmalloc (sizeof (appl_t)))) {
		lprintf (KERN_ERR, "Not enough memory for application record.\n");
		return NULL;
	}
	appp->id         = id;
	appp->ncci_count = ncount;
	appp->blk_count  = bcount;
	appp->blk_size   = bsize;
	appp->data       = NULL;
	appp->attr       = 0;
	appp->dying      = FALSE;
	appp->nncci      = 0;
	appp->root       = NULL;
	appp->pred       = NULL;
	lock ();
	appp->succ       = tab->appl_root;
	tab->appl_root = appp;
	if (NULL != appp->succ) {
		appp->succ->pred = appp;
	}
	tab->appl_count++;
	unlock ();
	return appp;
} /* create_appl */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
void remove_appl (appltab_t * tab, appl_t * appp) {
	ncci_t *	nccip;
	ncci_t *	tmp;

	assert (appp);
	lock ();
	if (appp->pred != NULL) {
		appp->pred->succ = appp->succ;
	} else {
		tab->appl_root = appp->succ;
	}
	if (appp->succ != NULL) {
		appp->succ->pred = appp->pred;
	}
	if (appp->data != NULL) {
		hfree (appp->data);
		appp->data = NULL;
	}
	nccip = appp->root;
	tab->appl_count--;
	unlock ();
	while (nccip != NULL) {
		tmp = nccip->succ;
		remove_ncci (tab, appp, nccip);
		nccip = tmp;
	}
	hfree (appp);
} /* remove_appl */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
appl_t * search_appl (appltab_t * tab, unsigned id) {
	appl_t * appp;

	lock ();
	appp = tab->appl_root;
	while (appp != NULL) {
		if (appp->id == id) {
			break;
		}
		appp = appp->succ;
	}
	unlock ();
	info (appp);
	return appp;
} /* search_appl */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
appl_t * get_appl (appltab_t * tab, unsigned ix) {
	appl_t *	appp = NULL;

	assert (ix < tab->appl_count);
	lock ();
	if (ix < tab->appl_count) {
		appp = tab->appl_root;
		while (ix > 0) {
			appp = appp->succ;
			--ix;
		}
	}
	unlock ();
	return appp;
} /* get_appl */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
appl_t * next_appl (appltab_t * tab, appl_t * appp) {

	UNUSED_ARG (tab);
	assert (appp);
	return appp->succ;
} /* next_appl */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
appl_t * first_appl (appltab_t * tab) {

	return tab->appl_root;
} /* first_appl */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
int handle_message (queue_t * q, appl_t * appp, struct sk_buff * msg) {
	int ok;

	UNUSED_ARG (appp);
	assert (q != NULL);
	assert (msg != NULL);
	if (!(ok = queue_put (q, msg))) {
		lprintf (KERN_ERR, "Message queue overflow. Message lost...\n");
		KFREE_SKB (msg);
	}
	return ok;
} /* handle_message */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
ncci_t * create_ncci (	
	appltab_t *		tab, 
	appl_t *		appp, 
	NCCI_t			ncci, 
	unsigned		wsize, 
	unsigned		bsize
) {
	ncci_t *		tmp;
	unsigned char **	data;

	UNUSED_ARG (tab);
#if defined (LOG_NCCI)
	log ("Creating new NCCI 0x%08x\n", ncci);
#endif
	if (NULL == (tmp = (ncci_t *) hmalloc (sizeof (ncci_t)))) {
		lprintf (KERN_ERR, "Failed to allocate NCCI record.\n");
		return NULL;
	}
	data = (unsigned char **) hcalloc (sizeof (unsigned char *) 
							* appp->blk_count);
	if (NULL == data) {
		lprintf (KERN_ERR, "Failed to allocate data buffer directory.\n");
		hfree (tmp);
		return NULL;
	}
	tmp->ncci     = ncci;
	tmp->appl     = appp->id;
	tmp->win_size = wsize;
	tmp->blk_size = bsize;
	tmp->data     = data;
	tmp->pred     = NULL;
	lock ();
	tmp->succ     = appp->root;
	appp->root    = tmp;
	if (NULL != tmp->succ) {
		tmp->succ->pred = tmp;
	}
	appp->nncci++;
	unlock ();
	return tmp;
} /* create_ncci */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
void remove_ncci (appltab_t * tab, appl_t * appp, ncci_t * nccip) {
	unsigned	i;

	UNUSED_ARG (tab);
	assert (appp);
	assert (nccip);
	if (nccip != NULL) {
#if defined (LOG_NCCI)
	log ("Removing NCCI 0x%08x\n", nccip->ncci);
#endif
		lock ();
		for (i = 0; i < appp->blk_count; i++) {
			if (nccip->data[i] != NULL) {
				hfree (nccip->data[i]);
			}
		}
		hfree (nccip->data);
		if (nccip->succ != NULL) {
			nccip->succ->pred = nccip->pred;
		}
		if (nccip->pred != NULL) {
			nccip->pred->succ = nccip->succ;
		} else {
			appp->root = nccip->succ;
		}
		hfree (nccip);
		appp->nncci--;
		unlock ();
	}
} /* remove_ncci */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
unsigned char * ncci_data_buffer (
	appltab_t *	tab,
	appl_t *	appp,
	NCCI_t		ncci,
	unsigned	index
) {
	ncci_t *	nccip;

        UNUSED_ARG (tab);
        if (NULL == (nccip = locate_ncci (appp, ncci))) {
                log ("Data buffer request failed. NCCI not found.\n");
                return NULL;
        }
        lock ();
        if (index >= appp->blk_count) {
                unlock ();
                log ("Data buffer index out of range.\n");
                return NULL;
        }
        if (nccip->data[index] == NULL) {
                if (NULL == (nccip->data[index] = (unsigned char *) hmalloc (appp->blk_size))) {
                        lprintf (KERN_ERR, "Not enough memory for data buffer.\n");
                }
        }
        unlock ();
        return nccip->data[index];
} /* ncci_data_buffer */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/
ncci_t * locate_ncci (appl_t * appp, NCCI_t ncci) {
	ncci_t *	tmp;

	assert (appp);
	lock ();
	tmp = appp->root;
	while ((tmp != NULL) && (tmp->ncci != ncci)) {
		tmp = tmp->succ;
	}
	unlock ();
#if defined (LOG_NCCI)
	if (tmp == NULL) {
		log ("Failed to find NCCI 0x%08x\n", ncci);
	}
#endif
	info (tmp);
	return tmp;
} /* locate_ncci */

/*---------------------------------------------------------------------------*\
\*---------------------------------------------------------------------------*/

