/*
 * (llc_cmd_rsp.c)- This module includes llc commands and responses implementation.
 *
 * 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_CMD_RSP_C

#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/llc_main.h>
#include <net/llc_glob.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_ac.h>
#include <net/llc_c_st.h>
#include <net/llc_pdu.h>
#include <net/llc_lm.h>
#include <net/lan_hdrs.h>
#include <net/llc_dbg.h>

/* ---------------------------------------------------------------------- */

us16
conn_ac_send_disc_c(connection_t * conn, conn_state_event_t * event, int p_bit)
{
	us16		rc;
	frame_t *	pdu_frame;
	sap_t *		sap;

	rc = frame_pdu_allocate (&pdu_frame);
	pdu_frame->free_frame_flag = YES;   // FORTEST
	pdu_frame->data_size = LLC_PDU_LEN_U;
	sap = (sap_t *) conn->parent_sap;

	((struct sk_buff *)pdu_frame->skb)->data =
				((struct sk_buff *)pdu_frame->skb)->head;
	((struct sk_buff *)pdu_frame->skb)->tail =
				((struct sk_buff *)pdu_frame->skb)->head;
	((struct sk_buff *)pdu_frame->skb)->len = 0;
	skb_reserve((struct sk_buff *)pdu_frame->skb, 50);
           
	/* initialize PDU header for DISC command PDU */
	rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_U,
				sap->local_dl_addr.lsap, 
				conn->remote_dl_addr.lsap, LLC_PDU_CMD);
	if (!rc) {   
		rc = pdu_init_as_disc_cmd (pdu_frame, p_bit);
		if (!rc) {
			rc = lan_hdrs_init (pdu_frame,
				((station_t *) sap->parent_station)->mac_type,
				((station_t *) sap->parent_station)->mac_sa,
				conn->remote_dl_addr.mac);
			if (!rc) {
				rc = conn_send_pdu (conn, pdu_frame);
			}
		}
	}
	return (rc);   
}



us16
conn_ac_send_dm_r(connection_t * conn, conn_state_event_t * event, int f_bit)
{
	us16		rc;
	frame_t *	pdu_frame;
	sap_t *		sap;

	rc = frame_pdu_allocate (&pdu_frame);
	if (!rc) {
		pdu_frame->data_size = LLC_PDU_LEN_U;
		sap = (sap_t *) conn->parent_sap;
		if (!rc) {   
			rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_U,
				sap->local_dl_addr.lsap, 
				conn->remote_dl_addr.lsap, LLC_PDU_RSP);
			if (!rc) {   
				rc = pdu_init_as_dm_rsp (pdu_frame, f_bit);
				if (!rc) {
					rc = lan_hdrs_init (pdu_frame,
						((station_t *) sap->parent_station)->mac_type,
						((station_t *) sap->parent_station)->mac_sa,
						conn->remote_dl_addr.mac);
					if (!rc) {
						rc = conn_send_pdu (conn, pdu_frame);
					}
				}
			}
		}
	}
	return (rc);
}


us16
conn_ac_send_rej_c(connection_t * conn, conn_state_event_t * event, int p_bit)
{
	us16		rc = 1;
	frame_t *	pdu_frame;
	sap_t *		sap;

	rc = frame_pdu_allocate (&pdu_frame);
	if (!rc) {
		pdu_frame->data_size = LLC_PDU_LEN_S;
		sap = (sap_t *) conn->parent_sap;
		rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_S,
				sap->local_dl_addr.lsap, 
				conn->remote_dl_addr.lsap, LLC_PDU_CMD);
		if (!rc) {
			rc = pdu_init_as_rej_cmd (pdu_frame, p_bit, conn->vR);
			if (!rc) {
				rc = lan_hdrs_init (pdu_frame,
					((station_t *) sap->parent_station)->mac_type,
					((station_t *) sap->parent_station)->mac_sa,
					conn->remote_dl_addr.mac);
				if (!rc) {
					rc = conn_send_pdu (conn, pdu_frame);
				}
			}
		}
	}
	return (rc);
}

us16
conn_ac_send_sabme_c(connection_t * conn, conn_state_event_t * event, int p_bit)
{
	us16		rc = 1;
	frame_t *	pdu_frame;
	sap_t *		sap;

	rc = frame_pdu_allocate (&pdu_frame);
	if (!rc) {           
		pdu_frame->data_size = LLC_PDU_LEN_U;
		sap = (sap_t *) conn->parent_sap;
		rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_U,
				sap->local_dl_addr.lsap, 
				conn->remote_dl_addr.lsap, LLC_PDU_CMD);
		if (!rc) {
			rc = pdu_init_as_sabme_cmd (pdu_frame, p_bit);
			if (!rc) {
				rc = lan_hdrs_init (pdu_frame,
					((station_t *) sap->parent_station)->mac_type,
					((station_t *) sap->parent_station)->mac_sa,
					conn->remote_dl_addr.mac);
				if (!rc) {
					rc = conn_send_pdu (conn, pdu_frame);
				}
			}
		}
	}
	return (rc);
}



us16
conn_ac_send_ua_r(connection_t * conn, conn_state_event_t * event, int f_bit)
{
	us16		rc = 1;
	frame_t *	pdu_frame;
	sap_t *		sap;

	rc = frame_pdu_allocate (&pdu_frame);
	if (!rc) {           
		pdu_frame->data_size = LLC_PDU_LEN_S;
			((struct sk_buff *)pdu_frame->skb)->data = ((struct sk_buff *)pdu_frame->skb)->head;
			((struct sk_buff *)pdu_frame->skb)->tail = ((struct sk_buff *)pdu_frame->skb)->head;
			((struct sk_buff *)pdu_frame->skb)->len = 0;
			skb_reserve((struct sk_buff *)pdu_frame->skb, 50);
		sap = (sap_t *) conn->parent_sap;
		rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_U,
				sap->local_dl_addr.lsap, 
				conn->remote_dl_addr.lsap, LLC_PDU_RSP);
		if (!rc) {
			rc = pdu_init_as_ua_rsp (pdu_frame, f_bit);
			if (!rc) {
				rc = lan_hdrs_init (pdu_frame,
					((station_t *) sap->parent_station)->mac_type,
					((station_t *) sap->parent_station)->mac_sa,
					conn->remote_dl_addr.mac);
				if (!rc) {
					rc = conn_send_pdu (conn, pdu_frame);
				}
			}
		}
	}
	return (rc);
}


us16
conn_ac_send_rr_c(connection_t * conn, conn_state_event_t * event, int p_bit)
{
	us16		rc = 0;
	frame_t *	pdu_frame;
	sap_t *		sap;
   
	rc = frame_pdu_allocate (&pdu_frame);
	if (!rc) {           
		pdu_frame->data_size = LLC_PDU_LEN_S;
		sap = (sap_t *) conn->parent_sap;
		rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_S,
				sap->local_dl_addr.lsap, 
				conn->remote_dl_addr.lsap, LLC_PDU_CMD);
		if (!rc) {
			rc = pdu_init_as_rr_cmd (pdu_frame, p_bit, conn->vR);
			if (!rc) {
				rc = lan_hdrs_init (pdu_frame,
					((station_t *) sap->parent_station)->mac_type,
					((station_t *) sap->parent_station)->mac_sa,
					conn->remote_dl_addr.mac);
				if (!rc) {
					rc = conn_send_pdu (conn, pdu_frame);
				}
			}
		}
	}
	return (rc);
}


us16
conn_ac_send_rr_r(connection_t * conn, conn_state_event_t * event, int f_bit)
{
	us16		rc = 1;
	frame_t *	pdu_frame;
	sap_t *		sap;

	rc = frame_pdu_allocate (&pdu_frame);
	pdu_frame->free_frame_flag = YES;   // FORTEST
	if (!rc) {           
		pdu_frame->data_size = LLC_PDU_LEN_S;
		sap = (sap_t *) conn->parent_sap;
		rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_S,
				sap->local_dl_addr.lsap, 
				conn->remote_dl_addr.lsap, LLC_PDU_RSP);
		if (!rc) {
			rc = pdu_init_as_rr_rsp (pdu_frame, f_bit, conn->vR);
			if (!rc) {
				rc = lan_hdrs_init (pdu_frame,
					((station_t *) sap->parent_station)->mac_type,
					((station_t *) sap->parent_station)->mac_sa,
					conn->remote_dl_addr.mac);
				if (!rc) {
					rc = conn_send_pdu (conn, pdu_frame);
				}
			}
		}
	}
	return (rc);
}

us16
conn_ac_send_rej_r(connection_t * conn, conn_state_event_t * event, int f_bit)
{
	us16		rc = 1;
	frame_t *	pdu_frame;
	sap_t *		sap;

	rc = frame_pdu_allocate (&pdu_frame);
	if (!rc) {           
		pdu_frame->data_size = LLC_PDU_LEN_S;
		sap = (sap_t *) conn->parent_sap;
		rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_S,
				sap->local_dl_addr.lsap, 
				conn->remote_dl_addr.lsap, LLC_PDU_RSP);
		if (!rc) {
			rc = pdu_init_as_rej_rsp (pdu_frame, f_bit, conn->vR);
			if (!rc) {
				rc = lan_hdrs_init (pdu_frame,
					((station_t *) sap->parent_station)->mac_type,
					((station_t *) sap->parent_station)->mac_sa,
					conn->remote_dl_addr.mac);
				if (!rc) {
					rc = conn_send_pdu (conn, pdu_frame);
				}
			}
		}
	}
	return (rc);
}

us16
conn_ac_send_frmr_r(connection_t * conn, conn_state_event_t * event, int f_bit)
{
	us16		rc = 1;
	pdu_sn_t *	rx_pdu=NULL;
	frame_t *	pdu_frame;
	sap_t *	sap;

	rc = frame_pdu_allocate (&pdu_frame);
	if (!rc) {   
		pdu_frame->data_size = LLC_PDU_LEN_U + sizeof(frmr_info_t);
		sap = (sap_t *) conn->parent_sap;
		rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_U,
				sap->local_dl_addr.lsap, 
				conn->remote_dl_addr.lsap, LLC_PDU_RSP);
		if (!rc) {
			rc = pdu_init_as_frmr_rsp (pdu_frame, rx_pdu, f_bit, conn->vS, conn->vR, 0);
			if (!rc) {
				rc = lan_hdrs_init (pdu_frame,
					((station_t *) sap->parent_station)->mac_type,
					((station_t *) sap->parent_station)->mac_sa,
					conn->remote_dl_addr.mac);
				if (!rc) {
					rc = conn_send_pdu (conn, pdu_frame);
				}
			}
		}
	}
	return (rc);
}

us16
conn_ac_send_rnr_c(connection_t * conn, conn_state_event_t * event, int p_bit)
{
	us16		rc = 0;
	frame_t *	pdu_frame;
	sap_t *		sap;

	rc = frame_pdu_allocate (&pdu_frame);
	if (!rc) {           
		pdu_frame->data_size = LLC_PDU_LEN_S;
		sap = (sap_t *) conn->parent_sap;
		rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_S,
				sap->local_dl_addr.lsap, 
				conn->remote_dl_addr.lsap, LLC_PDU_CMD);
		if (!rc) {
			rc = pdu_init_as_rnr_cmd (pdu_frame, p_bit, conn->vR);
			if (!rc) {
				rc = lan_hdrs_init (pdu_frame,
					((station_t *) sap->parent_station)->mac_type,
					((station_t *) sap->parent_station)->mac_sa,
					conn->remote_dl_addr.mac);
				if (!rc) {
					rc = conn_send_pdu (conn, pdu_frame);
				}
			}
		}
	}
	return (rc);
}



us16
conn_ac_send_rnr_r(connection_t * conn, conn_state_event_t * event, int f_bit)
{
	us16		rc = 1;
	frame_t *	pdu_frame;
	sap_t *		sap;

	rc = frame_pdu_allocate (&pdu_frame);
	if (!rc) {           
		pdu_frame->data_size = LLC_PDU_LEN_S;
		sap = (sap_t *) conn->parent_sap;
		rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_S,
				sap->local_dl_addr.lsap, 
				conn->remote_dl_addr.lsap, LLC_PDU_RSP);
		if (!rc) {
			rc = pdu_init_as_rnr_rsp (pdu_frame, f_bit, conn->vR);
			if (!rc) {
				rc = lan_hdrs_init (pdu_frame,
					((station_t *) sap->parent_station)->mac_type,
					((station_t *) sap->parent_station)->mac_sa,
					conn->remote_dl_addr.mac);
				if (!rc) {
					rc = conn_send_pdu (conn, pdu_frame);
				}
			}
		}
	}
	return (rc);
}

us16
conn_ac_send_i_c(connection_t * conn, conn_state_event_t * event, int p_bit)
{
	us16		rc = 1;
	frame_t *	pdu_frame;
	sap_t *		sap;
	us32		nbr_net_devices = 0;
	struct device *	netdevs[11];

	rc = frame_allocate(&pdu_frame);
	if (!rc) {
		pdu_frame->skb = (struct sk_buff *) alloc_skb (128, GFP_KERNEL);
		if (pdu_frame->skb) {
			skb_reserve(pdu_frame->skb, 50);
			access_all_devices (ACTION_GET, (void **) netdevs, &nbr_net_devices);
			pdu_frame->mac_type = ETH_P_802_2;
			((struct sk_buff *) pdu_frame->skb)->protocol = ETH_P_802_2;
			pdu_frame->dev = netdevs[0];
			sap = (sap_t *) conn->parent_sap;
			rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_I,
					sap->local_dl_addr.lsap, 
					conn->remote_dl_addr.lsap, LLC_PDU_CMD);
			if (!rc) {
				rc = pdu_init_as_i_cmd (pdu_frame, p_bit, conn->vS, conn->vR);
				if (!rc) {
					rc = lan_hdrs_init (pdu_frame,
						((station_t *) sap->parent_station)->mac_type,
						((station_t *) sap->parent_station)->mac_sa,
						conn->remote_dl_addr.mac);
					if (!rc) {
						rc = conn_send_pdu (conn, pdu_frame);
					}
				}
			}
		}
	}
	return (rc);
}

us16
conn_ac_send_i_r(connection_t * conn, conn_state_event_t * event, int f_bit)
{
	us16		rc = 1;
	frame_t *	pdu_frame;
	sap_t *		sap;
	us32		nbr_net_devices = 0;
	struct device *	netdevs[11];

	rc = frame_allocate(&pdu_frame);
	if (!rc) {
		pdu_frame->skb = (struct sk_buff *) alloc_skb (128, GFP_KERNEL);
		if (pdu_frame->skb) {
			skb_reserve(pdu_frame->skb, 50);
			access_all_devices (ACTION_GET, (void **) netdevs, &nbr_net_devices);
			pdu_frame->mac_type = ETH_P_802_2;
			((struct sk_buff *) pdu_frame->skb)->protocol = ETH_P_802_2;
			pdu_frame->dev = netdevs[0];
			sap = (sap_t *) conn->parent_sap;
			rc = pdu_header_init (pdu_frame, LLC_PDU_TYPE_I,
					sap->local_dl_addr.lsap, 
					conn->remote_dl_addr.lsap, LLC_PDU_RSP);
			if (!rc) {
				rc = pdu_init_as_i_cmd (pdu_frame, f_bit, conn->vS, conn->vR);
				if (!rc) {
					rc = lan_hdrs_init (pdu_frame,
						((station_t *) sap->parent_station)->mac_type,
						((station_t *) sap->parent_station)->mac_sa,
						conn->remote_dl_addr.mac);
					if (!rc) {
						rc = conn_send_pdu (conn, pdu_frame);
					}
				}
			}
		}
	}
	return (rc);
}
