/*
 * (llc_main.c) - This module contains main functions to manage station, saps
 * 	and connections of the LLC.
 *
 * 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 LLC_MAIN_C


#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <net/cm_types.h>
#include <net/cm_mm.h>
#include <net/os_if.h>
#include <net/cm_dll.h>
#include <net/cm_frame.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_conn.h>
#include <net/lan_hdrs.h>          /* get size of MAC address */
#include <net/llc_main.h>
#include <net/llc_evnt.h>
#include <net/llc_actn.h>
#include <net/llc_stat.h>
#include <net/llc_glob.h>
#include <net/llc_c_st.h>          /* special "#ifndef...#endif" */
#include <net/llc_c_ev.h>          /* special "#ifndef...#endif" */
#include <net/llc_s_ev.h>          /* special "#ifndef...#endif" */
#include <net/llc_s_st.h>          /* special "#ifndef...#endif" */
#include <net/llc_c_ac.h>          /* special "#ifndef...#endif" */
#include <net/llc_mac.h>
#include <net/os_if.h>
#include <net/llc_dbg.h>

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

#define SAPS_SIZE		Max_nbr_saps * sizeof(sap_t)
#define CONNS_SIZE 		Max_nbr_connections * sizeof(connection_t) 
#define SAPS_EVENTS_SIZE 	Max_nbr_saps * 5 * sizeof(sap_state_event_t)
#define CONNS_EVENTS_SIZE	Max_nbr_connections * 5 * sizeof(conn_state_event_t)
#define CONNS_TIMERS_SIZE       Max_nbr_connections * 5 * sizeof(struct timer_list)
#define STATION_EVENTS_SIZE     5 * sizeof(station_event_t) 
#define STATION_TIMER_SIZE      sizeof(struct timer_list)  
#define SAPS_SUBPOOL_SIZE       (Max_nbr_saps * sizeof(memory_pool_t))
#define CONNS_SUBPOOL_SIZE      (Max_nbr_connections * sizeof(memory_pool_t))
#define SAPS_MEMORY		(SAPS_SIZE + SAPS_EVENTS_SIZE + SAPS_SUBPOOL_SIZE)
#define CONNS_MEMORY		(CONNS_SIZE + CONNS_EVENTS_SIZE + CONNS_TIMERS_SIZE +\
                                 CONNS_SUBPOOL_SIZE)
#define STATION_MEMORY          (STATION_EVENTS_SIZE + STATION_TIMER_SIZE)
#define CONN_MEMORY             (5 * sizeof(conn_state_event_t) + 5*sizeof(struct timer_list))
#define SAP_MEMORY              (5 * sizeof(sap_state_event_t))
#define SUBPOOL_SIZE		(CONNS_EVENTS_SIZE + CONNS_TIMERS_SIZE + SAPS_EVENTS_SIZE + SAPS_SUBPOOL_SIZE + CONNS_SUBPOOL_SIZE)


/* static function prototypes */

static us16 find_sap_match (void * this, void * match_value);
static us16 init_all_saps_match (void * sap_ptr, void * match);
static us16 init_all_conn_match (void * conn_ptr, void * match);
static us16 service_all_sap_events_match (void *sap_ptr, void *match);
static us16 service_all_conn_events_match (void *conn_ptr, void *match);
static us16 station_service_events (station_t * station);
static us16 station_rtn_event (station_t * station,
					station_state_event_t * event);
static us16 station_send_pdus (station_t * station);
static us16 station_next_state (station_t * station,
					station_state_event_t * event);
static us16 build_free_list (mph_t mph, dll_t * list, us16 list_size,
						us32 member_size);
static us16 execute_station_transition_actions (station_t * station,
	station_state_transition_t * transition, station_state_event_t * event);
static station_state_transition_t * find_station_transition (station_t * station,
					station_state_event_t * event);
static us16 rtn_all_conns(sap_t *sap);


/* static global data */
                       
static us16            Module_init = NO;
static mph_t           Llc_memory_pool;

/* resource pools */
static dll_t		Available_conns;        /* free connections only */
static dll_t		Available_saps;         /* free saps only */
static station_t	Station;                /* only one of its kind */
us16			Max_nbr_saps;
us16			Max_nbr_connections;
prim_if_block_t		Ind_prim,Cfm_prim;
prim_data_u		Ind_data_prim,Cfm_data_prim;



/*
 * Function : llc_init
 * 
 * Description:
 *  Initializes the LLC station, saps and connections and their queues.
 *  Allocates the memory needed by LLC. Initializes the LLC global
 *  parameters. And ...
 *
 * Parameters :
 *
 * Returns :
 *  0, on success
 *  nonzero, otherwise
 */

us16
llc_init ()
{
	us16			rc;
	us32			access_value;
	us32			pool_size;
	us32			nbr_net_devices = 0;
	struct device *		netdevs[11];
	station_state_event_t *	event;

	if (Module_init == YES) {
		return (0);
	}
	access_max_nbr_saps (ACTION_GET, &access_value);
	Max_nbr_saps = (us16) access_value;
	access_max_nbr_connections (ACTION_GET, &access_value);
	Max_nbr_connections = (us16) access_value;
	pool_size = (us32)(SAPS_MEMORY + CONNS_MEMORY + STATION_MEMORY)/1024 + 3; 
	rc = mm_get_pool (&Llc_memory_pool, pool_size);
	if (!rc) {
		rc = mm_get_subpool (Llc_memory_pool, &Station.mp_handle, 
					(us32)(SUBPOOL_SIZE/1024)+2,K_BYTE);
		if (!rc) {
			dll_initialize (&Station.sap_list);
			rc = build_free_list (Llc_memory_pool, &Available_saps,
					Max_nbr_saps, (us32) sizeof (sap_t));
			if (!rc) {
				rc = build_free_list (Llc_memory_pool,
					&Available_conns,
					Max_nbr_connections,
					(us32) sizeof (connection_t));
				if (!rc) {
					rc = build_free_list (Station.mp_handle,
						&Station.avail_events, 5,
						(us32) sizeof(station_state_event_t));
					if (!rc) {
						dll_initialize (&Station.event_q);
						dll_initialize (&Station.mac_pdu_q);
					}
				}
			}
		}
	}
	if (!rc) {
		dll_match (&Available_saps, init_all_saps_match,
				NULLPTR, (void **) NULLPTR,
				(DLL_MATCH_PEEK | DLL_MATCH_ONE));
		dll_match (&Available_conns, init_all_conn_match,
				NULLPTR, (void **) NULLPTR,
				(DLL_MATCH_PEEK | DLL_MATCH_ONE));
		Station.mac_type = ETH_P_802_2;
		access_all_devices(ACTION_GET, (void **) netdevs, &nbr_net_devices);
		memcpy(Station.mac_sa, netdevs[0]->dev_addr, MAC_ADDR_LEN);
		rc = os_tmr_create (Station.mp_handle, &Station.ack_timer);
		if (!rc) {
			os_tmr_set (Station.ack_timer, SECS_TO_CT(3));
		}
		/* initialize the station component */
		Station.maximum_retry = 1;
		Station.state = STATION_STATE_DOWN;
		dll_remove (&Station.avail_events, (void **) &event, DLL_WHERE_HEAD);
		event->type = STATION_EV_TYPE_SIMPLE;
		event->data.a.event = STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK;
		rc = station_next_state (&Station, event);
		if (!rc) {
			Module_init = YES;
		}
	}
	build_offset_table();    
	Ind_prim.data = &Ind_data_prim;
	Cfm_prim.data = &Cfm_data_prim;
	return rc;
}


/*
 * Function : llc_exit
 * 
 * Description: 
 *  Frees the memory allocated for LLC and turns the LLC activation
 *  flag off.
 *
 * Parameters :
 *
 * Returns :
 *  0, on success
 *  nonzero, otherwise
 */

us16
llc_exit ()
{
	us16	rc;

	if (Module_init == NO) {
		return 0;
	}
	rc = mm_rtn_pool (Llc_memory_pool);
	Module_init = NO;
	return rc;
}

/*
 * Function : llc_check_init
 * 
 * Description: 
 *  Checks the activation status of LLC
 *
 * Parameters :
 *
 * Returns :
 *  NO(0)  if LLC is not active
 *  YES(1) if LLC is active
 */

us16
llc_check_init ()
{
	return (Module_init);
}

/*
 * Function : llc_sap_get
 * 
 * Description: 
 *  Allocates a SAP from the available saps list and initializes it.
 *
 * Parameters :
 *  sap : Address of a place for the sap
 *
 * Returns :
 *  0, on success
 *  nonzero, otherwise
 */

us16
llc_sap_get (sap_t ** sap)
{
	us16		rc;
	sap_t *		this_sap;	/* initialized ok */

	if (Module_init == NO) {
		return (1);
	}
	rc = dll_remove (&Available_saps, (void **) &this_sap, DLL_WHERE_HEAD);
	if (!rc) {
		this_sap->state = SAP_STATE_ACTIVE;
		memcpy (this_sap->local_dl_addr.mac, Station.mac_sa, MAC_ADDR_LEN);
		*sap = this_sap;
	}
	return (rc);
}

/*
 * Function : llc_rtn_sap
 * 
 * Description: 
 *  Returns a sap to the available saps list and its connections (if any)
 *  to the available connections list.
 *
 * Parameters :
 *  sap : Address of the sap
 *
 * Returns :
 *  0, on success
 *  nonzero, otherwise
 */

us16
llc_rtn_sap (sap_t * sap)
{
	us16		rc;
	station_t *	station = (station_t *) sap->parent_station;

	if (Module_init == NO) {
		return (1);
	}
	rtn_all_conns(sap); 
	rc = dll_remove_this (&station->sap_list, (void *) sap);
	if(!rc) {
		dll_add (&Available_saps, (void *) sap, DLL_WHERE_TAIL);
	}
	return (rc);
}

/*
 * Function : llc_sap_save
 * 
 * Description: 
 *  Adds a sap to the LLC's station sap list.
 *
 * Parameters :
 *  sap : Address of the sap
 *
 * Returns :
 *  Always 0
 */

us16
llc_sap_save (sap_t * sap)
{
	us16	rc;

	if (Module_init == NO) {
		return (1);
	}
	rc = dll_add (&Station.sap_list, (void *) sap, DLL_WHERE_TAIL);
	return (0);
}



/*
 * Function : llc_sap_find
 * 
 * Description: 
 *  Searchs for a sap in the sap list of the LLC's station
 *  upon the sap ID.
 *
 * Parameters :
 *  sap : Address of a place for the found sap (if found).
 *
 * Returns :
 *  0, on success
 *  nonzero, otherwise
 */

us16
llc_sap_find (us8 sap_value, sap_t ** sap)
{
	us8 *	sap_p = &sap_value;

	if (Module_init == NO) {
		return (1);
	}
	return (dll_match (&Station.sap_list, find_sap_match, (void *) sap_p,
			(void **) sap, (DLL_MATCH_PEEK | DLL_MATCH_ONE)));
}

/*
 * Function : llc_connection_get
 * 
 * Description: 
 *  Allocates a connection from the available connections list
 *  and initializez it.
 *
 * Parameters :
 *  connection : Address of a place for the connection
 *
 * Returns :
 *  0, on success
 *  nonzero, otherwise
 */

us16
llc_connection_get (connection_t ** connection)
{
	us16		rc;
	connection_t *	this_conn;

	/* allocate an available connection; initialize it; return it */
	if (Module_init == NO) {
		return (1);
	}
	rc = dll_remove (&Available_conns, (void **) &this_conn, DLL_WHERE_HEAD);
	if (!rc) {
		this_conn->state = CONN_STATE_ADM;
		this_conn->vR = 0;
		this_conn->vS = 0;
		this_conn->p_flag = 0;
		this_conn->f_flag = 0;
		this_conn->s_flag = 0;  
		this_conn->ack_pf = 0;       
		this_conn->first_pdu_Ns=0;
		this_conn->npta=0; 
		this_conn->data_flag = 0;
		this_conn->remote_busy_flag = 0; 
		this_conn->cause_flag = 0; 
		this_conn->retry_count = 0;
		this_conn->pf_cycle_running = NO;  
		this_conn->ack_running = NO;
		this_conn->ack_must_be_send = NO;       
		this_conn->inc_step=YES;             
		this_conn->dec_step=YES;                   
		this_conn->inc_cntr=2;            
		this_conn->dec_cntr=2;                   
		this_conn->connect_step=YES;               
		this_conn->reject_sent_running = NO;
		this_conn->busy_state_running = NO;
		this_conn->X = 0;
		this_conn->busy = 0;
		this_conn->failed_data_req = 0 ;
		this_conn->last_nr = 0;
		dll_initialize (&this_conn->event_q);
		dll_initialize (&this_conn->mac_pdu_q);
		dll_initialize (&this_conn->nwk_pdu_q);
		dll_initialize (&this_conn->pdu_unack_q);
		dll_initialize (&this_conn->log_q);
		this_conn->n2 = 2;  /* max retransmit */
		this_conn->k = 2;   /* tx window size = will adjust dynamically */
		this_conn->rw = 128;  /* rx window size (optional and equal to tx_window of remote LLC )*/
		this_conn->local_dev = NULL;  /* rx windoe size */
		*connection = this_conn;
	}
	else {
		FDBG_ERR_MSG((KERN_ERR "llc_connection_get : no connection available"));
	}
	return (rc);
}

/*
 * Function : llc_connection_rtn
 * 
 * Description: 
 *  Returns a connection to available connections list and
 *  deinitializes it.
 *
 * Parameters :
 *  conn : Address of the connection
 *
 * Returns :
 *  0, on success
 *  nonzero, otherwise
 */

us16
llc_connection_rtn (connection_t * conn)
{
	us16			rc;
	frame_t *		frame;
	conn_state_event_t *	event;

	if (Module_init == NO) {
		return (1);
	}
	conn->state = CONN_OUT_OF_SVC;
	/* stop all (possibly) running timers */
	conn_ac_stop_all_timers(conn, event);
	
	/* handle return of frames on lists */
	for (; !dll_remove (&conn->mac_pdu_q, (void **) &frame, DLL_WHERE_HEAD) ||
		!dll_remove (&conn->pdu_unack_q, (void **) &frame, DLL_WHERE_HEAD) ||
		!dll_remove (&conn->nwk_pdu_q, (void **) &frame, DLL_WHERE_HEAD);) {
		frame_skb_free (frame);
	}
	for (; !dll_remove (&conn->event_q, (void **) &event, DLL_WHERE_HEAD) ;) {
		/* if an event contains a frame, decide what to do with it */
		if (event->type == CONN_EV_TYPE_PDU) {
			if ( (event->data.pdu.frame)->free_frame_flag == YES ) {
				frame_skb_free (event->data.pdu.frame);
			}
		}           
		dll_add (&conn->avail_events, (void *) event, DLL_WHERE_TAIL);
	}
	memset(&conn->remote_dl_addr,0,sizeof(address_t));
	rc = dll_add (&Available_conns, conn, DLL_WHERE_TAIL);
	dll_initialize (&conn->event_q);
	dll_initialize (&conn->mac_pdu_q);
	dll_initialize (&conn->nwk_pdu_q);
	dll_initialize (&conn->pdu_unack_q);
	return (rc);
}


/*
 * Function : llc_connection_reset
 * 
 * Description: 
 *  Resets a connection to the out of service state. stops its timers
 *  and frees any frames in the queues of the connection.
 *
 * Parameters :
 *  conn : Address of the connection
 *
 * Returns :
 *  Always 0
 */

us16
llc_connection_reset(connection_t * conn)
{
	frame_t *	frame;
	conn_state_event_t *event=NULL;

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

	conn_ac_stop_all_timers(conn, event);
	for (; !dll_remove (&conn->mac_pdu_q, (void **) &frame, DLL_WHERE_HEAD) ||
		!dll_remove (&conn->pdu_unack_q, (void **) &frame, DLL_WHERE_HEAD) ||
		!dll_remove (&conn->nwk_pdu_q, (void **) &frame, DLL_WHERE_HEAD) ;) {
		frame_skb_free (frame);
	}
	dll_initialize (&conn->event_q);
	dll_initialize (&conn->mac_pdu_q);
	dll_initialize (&conn->nwk_pdu_q);
	dll_initialize (&conn->pdu_unack_q);
	conn->remote_busy_flag = 0; 
	conn->cause_flag = 0; 
	conn->retry_count = 0;
	conn->p_flag = 0;
	conn->f_flag = 0;
	conn->s_flag = 0;  
	conn->ack_pf = 0;       
	conn->first_pdu_Ns=0;
	conn->ack_must_be_send = NO;       
	conn->inc_step=YES;             
	conn->dec_step=YES;                   
	conn->inc_cntr=2;            
	conn->dec_cntr=2;                   
	conn->X = 0;
	conn->busy = 0;
	conn->failed_data_req = 0 ;
	conn->last_nr = 0;
	return 0;
}

/*
 * Function : rtn_all_conns
 * 
 * Description: 
 *  Closes all connections of a sap and returns them to the available
 *  connections list.
 *
 * Parameters :
 *  sap : Address of the sap to close its connections
 *
 * Returns :
 *  0, if all connections are closed successfully
 *  nonzero, otherwise
 */

us16
rtn_all_conns(sap_t *sap)
{
	us16		rc = 1;
	prim_data_u	prim_data;
	prim_if_block_t	prim;
	connection_t *	conn;

	while (dll_remove(&sap->connection_list,(void **)&conn,DLL_WHERE_HEAD) == 0 ) {
		prim.sap = (us32) sap;
		prim_data.disc.connect_handle = (us32)conn;
		prim.primitive = DISCONNECT_PRIM;
		prim.data = (prim_data_u *)&prim_data;
		conn->state = CONN_STATE_TEMP;
		rc = sap->request (&prim);
	}
	return rc;
}

/*
 * Function : station_get
 * 
 * Description: 
 *  Returns the address of the global Station.
 *
 * Parameters :
 *  Address of a place to copy the global station to it.
 *
 * Returns :
 *  Always 0
 */

us16
station_get (station_t ** station)
{
	if (Module_init == NO) {
		//return 1;
	}
	*station = &Station;
	return 0;
}

/*
 * Function : station_get_event
 * 
 * Description: 
 *  Allocates an event from the available event list of a station.
 *
 * Parameters :
 *  station : Address of the station
 *  event : Address of a place for the allocated event
 *
 * Returns :
 *  0, on success
 *  nonzero, otherwise
 */

us16
station_get_event (station_t * station, void ** event)
{
	if (Module_init == NO) {
		//return 1;
	}
	return (dll_remove (&station->avail_events, event, DLL_WHERE_HEAD));
}

/*
 * Function : station_send_event
 * 
 * Description: 
 *  Queues an event (on the station event queue) for handling by the 
 *  station state machine and attempts to process any queued-up events.
 *
 * Parameters :
 *  station : Address of the station
 *  event : Address of the event
 *
 * Returns :
 *  Always 0
 */

us16
station_send_event (station_t * station, void * event)
{
	if (Module_init == NO) {
		//return 1;
	}
	dll_add (&station->event_q, event, DLL_WHERE_TAIL);
	station_service_events (station);
	return 0;
}

/*
 * Function : station_send_pdu
 * 
 * Description: 
 *  Queues a PDU to send to the MAC layer.
 *
 * Parameters :
 *  station : Address of the station
 *  pdu_frame -	Address of the PDU
 *
 * Returns :
 *  Always 0
 */

us16
station_send_pdu (station_t * station, frame_t * pdu_frame)
{
	if (Module_init == NO) {
		//return 1;
	}
	dll_add (&station->mac_pdu_q, (void *) pdu_frame, DLL_WHERE_TAIL);
	station_send_pdus (station);
	return 0;
}

/*
 * Function : llc_service_io
 * 
 * Description: 
 *  This function is called by the PDU router (which is turn in called
 *  by the LLC lower layer MAC-level interface via an OS-level signal);
 *  send as many queued UNIT DATA PDUs to MAC layer as possible; 
 *  process as many queued events for all three component level.
 *
 * Parameters :
 *  station : Address of the station
 *
 * Returns :
 *  Always 0
 */

us16
llc_service_io (station_t * station)
{
	if (Module_init == NO) {
		return 1;
	}
	dll_match (&station->sap_list, service_all_sap_events_match,
		NULLPTR, (void **) NULLPTR, (DLL_MATCH_PEEK | DLL_MATCH_ONE));
	station_service_events (station);
	return 0;
}

/*
 * Function : station_send_pdus
 * 
 * Description: 
 *  Tries to send any PDUs queued in the station mac_pdu_q to the MAC
 *  layer.
 *
 * Parameters :
 *  station : Address of the station
 *
 * Returns :
 *  Always 0
 */

static us16
station_send_pdus (station_t * station)
{
	us16		rc;
	frame_t *	pdu;

	do {
		/* leave PDU queued until it has been sent to MAC layer */
		rc = dll_peek (&station->mac_pdu_q, (void **) &pdu, DLL_WHERE_HEAD);
		if (!rc) {   
			pdu->free_frame_flag = YES;
			dll_remove (&station->mac_pdu_q, (void **) &pdu,
							DLL_WHERE_HEAD);
			rc = mac_send_pdu (pdu);
			if (rc) {
				FDBG_ERR_MSG(("station_send_pdus failed: can't send
						pdu\n"));
			}
		}
	} while (!rc);
	return 0;
}

/*
 * Function : station_rtn_event
 * 
 * Description: 
 *  Returns an event to the station's available events list and
 *  frees the memories allocated for that event
 *
 * Parameters :
 *  station : Address of the station
 *  event : Address of the event
 *
 * Returns :
 *  0, on success
 *  nonzero, on failure
 */

static us16
station_rtn_event (station_t * station, station_state_event_t * event)
{
	frame_t *	ev_frame = event->data.pdu.frame;

	if (event->type == STATION_EV_TYPE_PDU) {
		frame_skb_free(ev_frame);
	}
	return (!dll_add (&station->avail_events, event, DLL_WHERE_TAIL));
}

/*
 * Function : station_service_events
 * 
 * Description: 
 *  Get an event from the station event queue (if any); attempt to
 *  service the event; if event serviced, get the next event (if any)
 *  on the event queue; if event not service, re-queue the event on
 *  the event queue and attempt to service the next event; when serviced
 *  all events in queue, finished; if don't transition to different
 *  state, just service all events once; if transition to new state,
 *  service all events again.
 *
 * Parameters :
 *  station : Address of the station
 *
 * Returns :
 *  Always 0
 */

static us16
station_service_events (station_t * station)
{
	us16			i;
	us16			rc;
	us16			nbr_events;
	station_state_event_t *	event;

	nbr_events = dll_query (&station->event_q);
	for (i = 0; i < nbr_events; i++) {
		rc = dll_remove (&station->event_q, (void **) &event,
							DLL_WHERE_HEAD);
		if (!rc) {
			rc = station_next_state (station, event);
			if (rc) {
				FDBG_ERR_MSG(("ERROR : station_service_events
					failed\n"));	
			}
		}
	}
	return 0;
}

/*
 * Function : station_next_state
 * 
 * Description: 
 *  Processes an event, executes any transitions related to that event
 *  and updates the state of the station.
 *
 * Parameters :
 *  station : Address of the station
 *  event : Address of the event
 *
 * Returns :
 *  0, on success
 *  nonzero, on failure
 */

static us16
station_next_state (station_t * station, station_state_event_t * event)
{
	us16				rc = 1;
	station_state_transition_t *	transition;

	if (station->state <= NBR_STATION_STATES) {
		transition = find_station_transition (station, event);
		if (transition) {
			/*
			 * got the state to which we next transition; perform the
			 * actions associated with this transition before actually
			 * transitioning to the next state
			 */
			rc = execute_station_transition_actions (station,
							transition, event);
			if (!rc) {
				/*
				 * transition station to next state if all actions
				 * execute successfully; done; wait for next event
				 */
				station->state = transition->next_state;
			}
		} else {
			/* 
			 * event not recognized in current state; re-queue it for
			 * processing again at a later time; return failure
			 */
			rc = 0;
		}
	}
	station_rtn_event (station, event);
	return rc;
}


/*
 * Function : find_station_transition
 * 
 * Description: 
 *  Search thru events of the current state of the station until list
 *  exhausted or it's obvious that the event is not valid for the
 *  current state.
 *
 * Parameters :
 *  station : Address of the station
 *  event : Address of the event
 *
 * Returns :
 *  Address of the transition, if found any
 *  NULL, if no transition found
 */

static station_state_transition_t *
find_station_transition (station_t * station, station_state_event_t * event)
{
	us16				i;
	station_state_t *		curr_state;   
	station_state_transition_t **	next_transition;

	curr_state = &Station_state_table [station->state - 1];
	for (i = 0, next_transition = curr_state->transitions;
                                           next_transition [i]->event; i++) {
		if (!next_transition [i]->event (station, event)) {
			return (next_transition [i]);
		}
	}
	return ((station_state_transition_t *) NULL);
}

/*
 * Function : execute_station_transition_actions
 * 
 * Description: 
 *  Executes actions of a transition of the station state machine.
 *
 * Parameters :
 *  station : Address of the station
 *  transition : Address of the transition
 *  event : Address of the event that caused the transition
 *
 * Returns :
 *  0, if all actions complete successfully
 *  nonzero, otherwise
 */

static us16
execute_station_transition_actions (station_t * station,
				station_state_transition_t * transition,
				station_state_event_t * event)
{
	us16			rc = 0;
	station_action_t *	next_action;

	for (next_action = transition->event_actions;
				next_action && *next_action; next_action++) {
		if ((*next_action) (station, event)) {
			FDBG_ERR_MSG(("one station action failed\n"));
			rc = 1;
		}
	}
	return rc;
}

/*
 * Function : init_all_saps_match
 * 
 * Description: 
 *  This function is used for initializing saps, thru dll_match function.
 *  It initializes a sap, allocates required memory for it and builds
 *  the queues of the sap.
 *  This function is passed as a match function to dll_match and in this
 *  way, it is called for every entry in a sap list.
 *
 * Parameters :
 *  sap_ptr : Address of a sap to initialize
 *  match : This parameter in the match functions should point to
 *		a value that the first parameter must match with it
 *		(for the function return TRUE). But in this function
 *		it is not used.
 *
 * Returns :
 *  Always 1. Because this is not a real match function.
 */

static us16
init_all_saps_match (void * sap_ptr, void * match)
{
	us16	rc;
	sap_t *	this_sap = (sap_t *) sap_ptr;

	rc = mm_get_subpool (Station.mp_handle, &this_sap->mp_handle, SAP_MEMORY, BYTE);
	if (!rc) {
		rc = build_free_list (this_sap->mp_handle,
					&this_sap->avail_events, 5,
					(us32) sizeof(sap_state_event_t));
		if (!rc) {
			dll_initialize (&this_sap->event_q);
			dll_initialize (&this_sap->mac_pdu_q);
			dll_initialize (&this_sap->nwk_pdu_q);
		}
	}
	return 1;
}


/*
 * Function : init_all_conn_match
 * 
 * Description: 
 *  This function plays the same role as the init_all_saps_match;
 *  But for connections not saps.
 *
 * Parameters :
 *  conn_ptr : Address of the connection to initialize
 *  match : Like the `match` parameter of the init_all_saps_match
 *
 * Returns :
 *  Always 1.
 */

static us16
init_all_conn_match (void * conn_ptr, void * match)
{
	us16		rc;
	connection_t *	this_conn = (connection_t *) conn_ptr;

	memset (this_conn, 0, sizeof(connection_t));
	rc = mm_get_subpool (Station.mp_handle, &this_conn->mp_handle, 
						CONN_MEMORY, BYTE);
	if (!rc) {
		rc = build_free_list (this_conn->mp_handle,
					&this_conn->avail_events, 5,
					(us32) sizeof(conn_state_event_t));
		if (!rc) {
			dll_initialize (&this_conn->event_q);
			dll_initialize (&this_conn->mac_pdu_q);
			dll_initialize (&this_conn->nwk_pdu_q);
			dll_initialize (&this_conn->pdu_unack_q);
			dll_initialize (&this_conn->log_q);
			if (os_tmr_create(this_conn->mp_handle,&this_conn->ack_timer) ||
				os_tmr_create(this_conn->mp_handle,&this_conn->pf_cycle_timer) ||
				os_tmr_create(this_conn->mp_handle,&this_conn->reject_sent_timer) ||
				os_tmr_create(this_conn->mp_handle,&this_conn->busy_state_timer)
				/*os_tmr_create(this_conn->mp_handle,&this_conn->sendack_timer) */) {
				FDBG_ERR_MSG(("\nllc_init : timer creation failed\n")); 
			}
		} else {
			FDBG_ERR_MSG(("\ncan't generate event for connection"));
		}
	} else {
		FDBG_ERR_MSG(("\ncan't allocate memory for connection\n"));
	}
	return 1;
}

	/* 
	* service events for all connections this SAP manages, and
	* then the SAP itself
	*/
/*
 * Function : service_all_sap_events_match
 * 
 * Description: 
 *  Services all events in the event queue of a sap.
 *  This is also a virtual match function, for use with dll_match to
 *  service events of all saps.
 *
 * Parameters :
 *  sap_ptr : Address of the sap to service its events
 *  match : Not used; like other virtual match functions
 *
 * Returns :
 *  Always 1.
 */

static us16
service_all_sap_events_match (void * sap_ptr, void * match)
{
	sap_t *	sap = (sap_t *) sap_ptr;

	dll_match (&sap->event_q, service_all_conn_events_match,
		NULLPTR, (void **) NULLPTR, (DLL_MATCH_PEEK | DLL_MATCH_ONE));
	return 1;
}

   /* 
    * 
    */
/*
 * Function : service_all_conn_events_match
 * 
 * Description: 
 *  Services PDUs queued for sending to MAC, events queued for servicing,
 *  and primitives queued for sending to network layer of
 *  a given connection. (Don't see the function please)
 *
 * Parameters :
 *  conn_ptr : Address of the connection
 *  match : Not used (this is a virtual match function)
 *
 * Returns :
 *  Always 1
 */

static us16
service_all_conn_events_match (void * conn_ptr, void * match)
{
	return 1;
}

/*
 * Function : build_free_list
 * 
 * Description: 
 *  Creates a list of entries with member_size
 *
 * Parameters :
 *  mph : Handle of the memory pool to allocate members from it
 *  list : Address of the list to build
 *  list_size -	Number of entries in the list
 *  member_size : Size of an indivisual list entry
 *
 * Returns :
 *  0, on success
 *  nonzero, otherwise
 */

static us16
build_free_list (mph_t mph, dll_t * list, us16 list_size, us32 member_size)
{
	us8 *	member;
	us16	rc = 0;
	us16	i;

	dll_initialize (list);
	for (i = 0; (i < list_size) && !rc; i++) {
		member = mm_malloc (mph, member_size);
		if (member) {
			memset (member, 0, member_size);
			rc = dll_add (list, member, DLL_WHERE_HEAD);
		} else {
			rc = 1;
		}
	}
	return rc;
}


/*
 * Function : find_sap_match
 * 
 * Description: 
 *  Match function for finding a sap based on its sap ID.
 *
 * Parameters :
 *  this : Address of a sap
 *  match_value : Pointer to ID value of sap
 *
 * Returns :
 *  0, if matched
 *  nonzero, if not matched
 */

static us16
find_sap_match (void * this, void * match_value)
{
	us8	sap_value = *((us8 *) match_value);
	us16	rc = 1;
	sap_t *	sap = (sap_t *) this;

	if (sap->local_dl_addr.lsap == sap_value) {
		rc = 0;
	}
	return rc;
}
