/*
 * (llc_pdu.c) - access to PDU internals
 *
 * 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_PDU_C

#include <asm/byteorder.h>
#include <linux/skbuff.h>
#include <net/cm_types.h>
#include <net/cm_mm.h>
#include <net/cm_dll.h>
#include <net/cm_frame.h>
#include <net/llc_pdu.h>
#include <net/lan_hdrs.h>
#include <net/llc_if.h>
#include <net/llc_glob.h>
#include <net/llc_dbg.h>

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

static us16        pdu_decode_pdu_type (frame_t *pdu_frame, us8 *type);
static int         get_llc_hdr_length (us8 pdu_type);
static us8 	   pdu_get_pf_bit(pdu_sn_t *pdu);

/*
 * Function : pdu_header_init
 * 
 * Description : 
 *  this function sets  DSAP, SSAP and command/Response bit in LLC header.
 * 
 * Parameters :
 *  frame_t *pdu_frame : input frame that header must be set into it.
 *  us8 pdu_type : type of PDU ( U,I or S).
 *  us8 ssap : source sap.
 *  us8 dsap : destination sap.
 *  us8 cr : command/response bit (0 or 1).
 * 
 * Returns : 
 *  always 0.
 */
    

us16 
pdu_header_init (frame_t *pdu_frame, us8 pdu_type,
                                               us8 ssap, us8 dsap, us8 cr)
{
	pdu_frame->llc_hdr = skb_push ((struct sk_buff *) pdu_frame->skb,
						get_llc_hdr_length (pdu_type));
	((pdu_un_t *) pdu_frame->llc_hdr)->dsap = dsap;
	((pdu_un_t *) pdu_frame->llc_hdr)->ssap = ssap;
	((pdu_un_t *) pdu_frame->llc_hdr)->ssap |= cr;
	return (0);
}
   


us16 
pdu_set_cmd_rsp (frame_t *pdu_frame, us8 pdu_type)
{
	((pdu_un_t *) pdu_frame->llc_hdr)->ssap |= pdu_type;
	return (0);
}


/*
 * Function : pdu_set_pf_bit
 * 
 * Description : 
 *  This function sets poll/final bit in LLC header (based on type of PDU).
 *  in I or S pdus, p/f bit is rigth bit of fourth byte in header. in U pdus
 *  p/f bit is fifth bit of third byte.
 * 
 * Parameters :
 *  Frame_t *pdu_frame : input frame that p/f bit must be set into it.
 *  us8 bit_value : poll/final bit (0 or 1).
 * 
 * Returns : 
 *  Always 0.
 */

us16 
pdu_set_pf_bit (frame_t *pdu_frame, us8 bit_value)
{
	us8 pdu_type;
	us16 rc=0;

	rc = pdu_decode_pdu_type (pdu_frame, &pdu_type);
	if (!rc){
        	switch (pdu_type){
			case LLC_PDU_TYPE_I:
			case LLC_PDU_TYPE_S:
				((pdu_sn_t *) pdu_frame->llc_hdr)->ctrl_2 =
                        	(((pdu_sn_t *) pdu_frame->llc_hdr)->ctrl_2 &
				0xFE) | bit_value;
                   		break;

			case LLC_PDU_TYPE_U:
				((pdu_un_t *) pdu_frame->llc_hdr)->ctrl_1 |=
				 (((pdu_un_t *) pdu_frame->llc_hdr)->ctrl_1 & 
				 0xEF) | (bit_value<<4);
				break;
			default:
				FDBG_ERR_MSG((KERN_ERR "pdu_set_pf_bit : 
					invalide pdu type,type=%d\n", pdu_type));
                   		break;
		}
	}
	return (0);
}


/*
 * Function : pdu_decode_pf_bit
 * 
 * Description : 
 *  This function extracts poll/final bit from LLC header (based on type of PDU).
 *  In I or S pdus, p/f bit is rigth bit of fourth byte in header. In U pdus
 *  p/f bit is fifth bit of third byte.
 * 
 * Parameters :
 *  frame_t *pdu_frame : input frame that p/f bit must be extracted from it.
 *  us8 *pf_bit : poll/final bit (0 or 1).
 * 
 * Returns : 
 *   Always 0.
 */

us16 
pdu_decode_pf_bit (frame_t *pdu_frame, us8 *pf_bit)
{
	us8 pdu_type;
	us16 rc=1;

	rc = pdu_decode_pdu_type (pdu_frame, &pdu_type);
	if (!rc){
		switch (pdu_type){
			case LLC_PDU_TYPE_I:
			case LLC_PDU_TYPE_S:
				*pf_bit = ((pdu_sn_t *) pdu_frame->llc_hdr)->ctrl_2 &
                                	LLC_S_PF_BIT_MASK;
                   		break;
			case LLC_PDU_TYPE_U:
				*pf_bit = (((pdu_un_t *) pdu_frame->llc_hdr)->ctrl_1 &
                                                        LLC_U_PF_BIT_MASK) >> 4;
				break;
			default:
				FDBG_ERR_MSG((KERN_ERR "pdu_decode_pf_bit : 
					invalide pdu type,type=%d\n", pdu_type));
				break;
		}
	}
	return (0);
}

/*
 * Function : pdu_decode_cr_bit
 * 
 * Description : 
 *  This function extracts command/response bit from LLC header. this bit is
 *  right bit of source SAP.
 * 
 * Parameters :
 *  frame_t *pdu_frame : input frame that c/r bit must be extracted from it.
 *  us8 *cr_bit : command/response bit (0 or 1).
 * 
 * Returns : 
 *   Always 0.
 */

us16 
pdu_decode_cr_bit (frame_t *pdu_frame, us8 *cr_bit)
{
	*cr_bit = ((pdu_un_t *) pdu_frame->llc_hdr)->ssap & 
						LLC_PDU_CMD_RSP_MASK;
	return (0);
}


/*
 * Function : pdu_decode_sa
 * 
 * Description : 
 *  This function extracts source address(MAC) of input frame.
 * 
 * Parameters :
 *  frame_t *pdu_frame : input frame that source address must be extracted 
 *  from it.
 *  us8 *sa : pointer to source address ( 6 byte array ).
 * 
 * Returns : 
 *   Always 0.
 */

us16 
pdu_decode_sa (frame_t *pdu_frame, us8 *sa)
{
	switch (pdu_frame->mac_type){
		case ETH_P_802_2:
			memcpy (sa, ((ieee_802_3_mac_hdr_t *) pdu_frame->mac_hdr)->sa,
                        	MAC_ADDR_LEN);
               		break;
		case ETH_P_TR_802_2:
			memcpy (sa, ((token_ring_mac_hdr_t *) pdu_frame->mac_hdr)->sa,
        	                MAC_ADDR_LEN);
               		break;
		default:
			FDBG_ERR_MSG((KERN_ERR "pdu_decode_sa : invalid mac type,type=%d\n",
                                                        pdu_frame->mac_type));
			break;
	}
	return (0);
}

/*
 * Function : pdu_decode_da
 * 
 * Description : 
 *  This function extracts destination address(MAC) of input frame.
 * 
 * Parameters :
 *  frame_t *pdu_frame : input frame that destination address must be extracted 
 *  from it.
 *  us8 *sa : pointer to destination address ( 6 byte array ).
 * 
 * Returns : 
 *   Always 0.
 */

us16 
pdu_decode_da (frame_t *pdu_frame, us8 *da)
{
	switch (pdu_frame->mac_type){
		case ETH_P_802_2:
			memcpy (da, ((ieee_802_3_mac_hdr_t *) pdu_frame->mac_hdr)->da,
                        	MAC_ADDR_LEN);
               		break;
		case ETH_P_TR_802_2:
			memcpy (da, ((token_ring_mac_hdr_t *) pdu_frame->mac_hdr)->da,
                        	MAC_ADDR_LEN);
               		break;

          	default:
	       		FDBG_ERR_MSG((KERN_ERR "\npdu_decode_da : invalid mac type,type=%d\n",
                                                        pdu_frame->mac_type));
               		break;
	}
	return (0);
}


/*
 * Function : pdu_decode_dsap
 * 
 * Description : 
 *  This function extracts destination SAP of input frame. right bit of DSAP
 *  designates individual/group SAP.
 * 
 * Parameters :
 *  frame_t *pdu_frame : input frame that destination SAP must be extracted 
 *  from it.
 *  us8 *dsap : destination SAP (output arqument).
 * 
 * Returns : 
 *   Always 0.
 */

us16 
pdu_decode_dsap (frame_t *pdu_frame, us8 *dsap)
{
	*dsap = ((pdu_un_t *) pdu_frame->llc_hdr)->dsap & 0xFE;
	return (0);
}


/*
 * Function : pdu_decode_ssap
 * 
 * Description : 
 *  This function extracts source SAP of input frame. right bit of SSAP
 *  is command/response bit.
 * 
 * Parameters :
 *  frame_t *pdu_frame : input frame that source SAP must be extracted 
 *  from it.
 *  us8 *ssap : source SAP (output arqument).
 * 
 * Returns : 
 *   Always 0.
 */

us16 
pdu_decode_ssap (frame_t *pdu_frame, us8 *ssap)
{
	*ssap = ((pdu_un_t *) pdu_frame->llc_hdr)->ssap & 0xFE;
	return (0);
}


/*
 * Function : pdu_init_as_ui_cmd
 * 
 * Description : 
 *  This function sets third byte of LLC header as a UI PDU. 
 * 
 * Parameters :
 *  frame_t *pdu_frame : input frame that header must be set into it. 
 * 
 * Returns : 
 *   Always 0.
 */

us16 
pdu_init_as_ui_cmd (frame_t *pdu_frame)
{
	pdu_un_t *pdu;

	pdu = (pdu_un_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_U;
	pdu->ctrl_1 |= LLC_1_PDU_CMD_UI;
	return (0);
}

/*
 * Function : pdu_init_as_xid_cmd
 * 
 * Description : 
 *  This function sets third,fourth,fifth and sixth bytes of LLC header as a 
 *  XID PDU. 
 * 
 * Parameters :
 *  frame_t *pdu_frame : input frame that header must be set into it. 
 * 
 * Returns : 
 *   Always 0.
 */

us16 
pdu_init_as_xid_cmd (frame_t *pdu_frame, us8 svcs_supported, us8 rx_window)
{
	pdu_un_t *pdu;
	xid_info_t * xid_info;

 	pdu = (pdu_un_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_U;
	pdu->ctrl_1 |= LLC_1_PDU_CMD_XID;
	pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;

	xid_info = (xid_info_t *) (((us8 *) &pdu->ctrl_1) + 1);
	xid_info->fmt_id = XID_FMT_ID;    /* 0x81*/
	xid_info->type = svcs_supported;
	xid_info->rw = (rx_window << 1);  /* size of recieve window */

	skb_put(pdu_frame->skb,3);
	return (0);
}

/*
 * Function: pdu_init_as_test_cmd
 *
 * Description :
 *  This function is not used anywhere; because this implementation of LLC
 *  don't sends TEST commands by itself. That only responds to receiving
 *  TEST commands.
 *
 * Parameters:
 *  pdu_frame -	Address of the frame to build
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_test_cmd (frame_t *pdu_frame)
{
	pdu_un_t *pdu;

	pdu = (pdu_un_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_U;
	pdu->ctrl_1 |= LLC_1_PDU_CMD_TEST;
	pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;
	return 0;
}

/*
 * Function: pdu_init_as_disc_cmd
 *
 * Description :
 *  Builds a pdu frame as a DISC command.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  p_bit : The P bit to set in the PDU
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_disc_cmd (frame_t *pdu_frame, us8 p_bit)
{
	pdu_un_t *pdu;

	pdu = (pdu_un_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_U;
	pdu->ctrl_1 |= LLC_2_PDU_CMD_DISC;
	pdu->ctrl_1 |= (((p_bit & 1) << 4) & LLC_U_PF_BIT_MASK);
	return (0);
}


/*
 * Function: pdu_init_as_i_cmd
 *
 * Description :
 *  Builds a pdu frame as an I command.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  p_bit : The P bit to set in the PDU
 *  ns : The sequence number of the data PDU
 *  nr : The seq. number of the expected I PDU from the remote
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_i_cmd (frame_t *pdu_frame, us8 p_bit,us8 ns,us8 nr)
{
	pdu_sn_t *pdu;

	pdu = (pdu_sn_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_I;
	pdu->ctrl_2 = 0;
	pdu->ctrl_2 |= (p_bit & LLC_I_PF_BIT_MASK); /* p/f bit */
	pdu->ctrl_1 |= ((ns << 1) & 0xFE);   /* set N(S) in bits 2..8 */
	pdu->ctrl_2 |= ((nr << 1) & 0xFE);   /* set N(R) in bits 10..16 */
	return (0);
}

/*
 * Function: pdu_init_as_rej_cmd
 *
 * Description :
 *  Builds a pdu frame as a REJ command.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  p_bit : The P bit to set in the PDU
 *  nr : The seq. number of the expected I PDU from the remote
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_rej_cmd (frame_t *pdu_frame, us8 p_bit, us8 nr)
{
	pdu_sn_t *          pdu;

	pdu = (pdu_sn_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_S;
	pdu->ctrl_1 |= LLC_2_PDU_CMD_REJ;
	pdu->ctrl_2 = 0;
	pdu->ctrl_2 |= (p_bit & LLC_S_PF_BIT_MASK);
	pdu->ctrl_1 &= 0x0F;    /* setting bits 5..8 to zero(reserved) */
	pdu->ctrl_2 |= ((nr << 1)&0xFE); /* set N(R) in bits 10..16 */ 
	return (0);
}


/*
 * Function: pdu_init_as_rnr_cmd
 *
 * Description :
 *  Builds a pdu frame as an RNR command.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  p_bit : The P bit to set in the PDU
 *  nr : The seq. number of the expected I PDU from the remote
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_rnr_cmd (frame_t *pdu_frame, us8 p_bit, us8 nr)
{
	pdu_sn_t *pdu;

	pdu = (pdu_sn_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_S;
	pdu->ctrl_1 |= LLC_2_PDU_CMD_RNR;
	pdu->ctrl_2 = 0;
	pdu->ctrl_2 |= (p_bit & LLC_S_PF_BIT_MASK);
	pdu->ctrl_1 &= 0x0F;    /* setting bits 5..8 to zero(reserved) */
	pdu->ctrl_2 |= ((nr << 1)&0xFE); /* set N(R) in bits 10..16 */ 
	return (0);
}


/*
 * Function: pdu_init_as_rr_cmd
 *
 * Description :
 *  Builds a pdu frame as an RR command.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  p_bit : The P bit to set in the PDU
 *  nr : The seq. number of the expected I PDU from the remote
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_rr_cmd (frame_t *pdu_frame, us8 p_bit, us8 nr)
{
	pdu_sn_t *pdu;

	pdu = (pdu_sn_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_S;
	pdu->ctrl_1 |= LLC_2_PDU_CMD_RR;
	pdu->ctrl_2 = (p_bit & LLC_S_PF_BIT_MASK);
	pdu->ctrl_1 &= 0x0F;    /* setting bits 5..8 to zero(reserved) */
	pdu->ctrl_2 |= ((nr << 1)&0xFE); /* set N(R) in bits 10..16 */ 
	return (0);
}


/*
 * Function: pdu_init_as_sabme_cmd
 *
 * Description :
 *  Builds a pdu frame as an SABME command.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  p_bit : The P bit to set in the PDU
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_sabme_cmd (frame_t *pdu_frame, us8 p_bit)
{
	pdu_un_t *pdu;

	pdu = (pdu_un_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_U;
	pdu->ctrl_1 |= LLC_2_PDU_CMD_SABME;
	pdu->ctrl_1 |= (((p_bit & 1) << 4) & LLC_U_PF_BIT_MASK);
	return (0);
}


/*
 * Function: pdu_init_as_dm_rsp
 *
 * Description :
 *  Builds a pdu frame as a DM response.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  f_bit : The F bit to set in the PDU
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_dm_rsp (frame_t *pdu_frame, us8 f_bit)
{
	pdu_un_t *pdu;

	pdu = (pdu_un_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_U;
	pdu->ctrl_1 |= LLC_2_PDU_RSP_DM;
	pdu->ctrl_1 |= (((f_bit & 1) << 4) & LLC_U_PF_BIT_MASK);
	return (0);
}


/*
 * Function: pdu_init_as_xid_rsp
 *
 * Description :
 *  Builds a pdu frame as an XID response.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  svcs_supported : The class of the LLC (I or II)
 *  rx_window : The size of the receive window of the LLC
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_xid_rsp (frame_t * pdu_frame, us8 svcs_supported, us8 rx_window)
{
	pdu_un_t *pdu;
	xid_info_t *xid_info;

	pdu = (pdu_un_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_U;
	pdu->ctrl_1 |= LLC_1_PDU_CMD_XID;
	pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;

	xid_info = (xid_info_t *) (((us8 *) &pdu->ctrl_1) + 1);
	xid_info->fmt_id = XID_FMT_ID;
	xid_info->type = svcs_supported;
	xid_info->rw = (rx_window << 1);
	skb_put(pdu_frame->skb,3);
	return (0);
}

/*
 * Function: pdu_init_as_test_rsp
 *
 * Description :
 *  Builds a pdu frame as a TEST response.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  ev_frame : The received TEST command PDU frame
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_test_rsp (frame_t *pdu_frame, frame_t *ev_frame)
{
	pdu_un_t *	pdu;
	us16		data_size=0; 

	pdu = (pdu_un_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_U;
	pdu->ctrl_1 |= LLC_1_PDU_CMD_TEST;
	pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;
	if (ev_frame->mac_type == ETH_P_802_2) {
		data_size = ntohs(((ieee_802_3_mac_hdr_t *)
					ev_frame->mac_hdr)->lpdu_len)-3;
		memcpy(((us8 *)pdu_frame->llc_hdr)+3, ((us8 *)ev_frame->llc_hdr)+3,
							data_size);
		skb_put(pdu_frame->skb, data_size);
	}
	return 0;
}

/*
 * Function: pdu_init_as_frmr_rsp
 *
 * Description :
 *  Builds a pdu frame as a FRMR response.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  prev_pdu : The rejected PDU frame
 *  f_bit : The F bit to set in the PDU
 *  vs : The current send state variable value for the
 	 data link connection at the rejecting LLC
 *  vr : The current receive state variable value for the
 	 data link connection at the rejecting LLC
 *  vzyxw : This field is completely described in the
 	    IEEE Std 802.2 document (Page 55)
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_frmr_rsp (frame_t * pdu_frame, void  * prev_pdu, us8 f_bit, 
                           			us8 vs, us8 vr, us8 vzyxw)
{
	pdu_sn_t *	pdu;
	frmr_info_t *	frmr_info;
	us8		prev_pf=0;
	us8 *		ctrl;

	pdu = (pdu_sn_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_U;
	pdu->ctrl_1 |= LLC_2_PDU_RSP_FRMR;
	pdu->ctrl_1 |= ((f_bit &1 )<<4) & LLC_U_PF_BIT_MASK;

	frmr_info = (frmr_info_t *) &(pdu->ctrl_2);
	ctrl = (us8 *)&((pdu_sn_t *)prev_pdu)->ctrl_1;
	FRMR_INFO_SET_REJ_CNTRL(frmr_info,ctrl);
	FRMR_INFO_SET_Vs(frmr_info, vs);
	FRMR_INFO_SET_Vr(frmr_info, vr);
	prev_pf = pdu_get_pf_bit(prev_pdu);
	FRMR_INFO_SET_C_R_BIT(frmr_info, prev_pf);
	FRMR_INFO_SET_INVALID_PDU_CTRL_IND(frmr_info, vzyxw); 
	FRMR_INFO_SET_INVALID_PDU_INFO_IND(frmr_info, vzyxw); 
	FRMR_INFO_SET_PDU_INFO_2LONG_IND(frmr_info, vzyxw); 
	FRMR_INFO_SET_PDU_INVALID_Nr_IND(frmr_info, vzyxw); 
	FRMR_INFO_SET_PDU_INVALID_Ns_IND(frmr_info, vzyxw); 
	skb_put(pdu_frame->skb,5);
	return (0);
}

/*
 * Function: pdu_init_as_rr_rsp
 *
 * Description :
 *  Builds a pdu frame as an RR response.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  f_bit : The F bit to set in the PDU
 *  nr : The seq. number of the expected data PDU from the remote
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_rr_rsp (frame_t *pdu_frame, us8 f_bit, us8 nr)
{
	pdu_sn_t *pdu;

	pdu = (pdu_sn_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_S;
	pdu->ctrl_1 |= LLC_2_PDU_RSP_RR;
	pdu->ctrl_2 = 0;
	pdu->ctrl_2 |= (f_bit & LLC_S_PF_BIT_MASK);
	pdu->ctrl_1 &= 0x0F;    /* setting bits 5..8 to zero(reserved) */
	pdu->ctrl_2 |= ((nr << 1)&0xFE);  /* set N(R) in bits 10..16 */ 
	return (0);
}


/*
 * Function: pdu_init_as_rej_rsp
 *
 * Description :
 *  Builds a pdu frame as a REJ response.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  f_bit : The F bit to set in the PDU
 *  nr : The seq. number of the expected data PDU from the remote
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_rej_rsp (frame_t *pdu_frame, us8 f_bit, us8 nr)
{
	pdu_sn_t *pdu;

	pdu = (pdu_sn_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_S;
	pdu->ctrl_1 |= LLC_2_PDU_RSP_REJ;
	pdu->ctrl_2 = 0;
	pdu->ctrl_2 |= (f_bit & LLC_S_PF_BIT_MASK);
	pdu->ctrl_1 &= 0x0F;    // setting bits 5..8 to zero(reserved)
	pdu->ctrl_2 |= ((nr << 1)&0xFE);  /* set N(R) in bits 10..16 */ 
	return (0);
}


/*
 * Function: pdu_init_as_rnr_rsp
 *
 * Description :
 *  Builds a pdu frame as an RNR response.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  f_bit : The F bit to set in the PDU
 *  nr : The seq. number of the expected data PDU from the remote
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_rnr_rsp (frame_t *pdu_frame, us8 f_bit, us8 nr)
{
	pdu_sn_t *pdu;

	pdu = (pdu_sn_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_S;
	pdu->ctrl_1 |= LLC_2_PDU_RSP_RNR;
	pdu->ctrl_2 = 0;
	pdu->ctrl_2 |= (f_bit & LLC_S_PF_BIT_MASK);
	pdu->ctrl_1 &= 0x0F;    /* setting bits 5..8 to zero(reserved) */
	pdu->ctrl_2 |= ((nr << 1)&0xFE);  /* set N(R) in bits 10..16 */ 
	return (0);
}


/*
 * Function: pdu_init_as_ua_rsp
 *
 * Description :
 *  Builds a pdu frame as a UA response.
 *
 * Parameters:
 *  pdu_frame : Address of the frame to build
 *  f_bit : The F bit to set in the PDU
 *
 * Returns:
 *  Always 0
 */

us16 
pdu_init_as_ua_rsp (frame_t *pdu_frame, us8 f_bit)
{
	pdu_un_t *pdu;

	pdu = (pdu_un_t *) pdu_frame->llc_hdr;
	pdu->ctrl_1 = LLC_PDU_TYPE_U;
	pdu->ctrl_1 |= LLC_2_PDU_RSP_UA;
	pdu->ctrl_1 |= (((f_bit & 1) << 4) & LLC_U_PF_BIT_MASK);
	return (0);
}

/*
 * Function : pdu_decode_pdu_type
 * 
 * Description : 
 *  This function designates type of PDU (I,S or U). 
 * 
 * Parameters :
 *  frame_t *pdu_frame : input frame that type of it must be designated.
 *  us8 *type : type of PDU ( output argument ).
 * 
 * Returns : 
 *   Always 0.
 */

static us16 
pdu_decode_pdu_type (frame_t *pdu_frame, us8 *type)
{
	pdu_un_t *pdu;

	pdu = (pdu_un_t *) pdu_frame->llc_hdr;
	if (pdu->ctrl_1 & 1) {
		if ((pdu->ctrl_1 & LLC_PDU_TYPE_U)==LLC_PDU_TYPE_U) {
			*type = LLC_PDU_TYPE_U;
		} else {
			*type = LLC_PDU_TYPE_S;
		}
	} else {
		*type = LLC_PDU_TYPE_I;
	}
	return (0);
}

/*
 * Function : decode_pdu_type
 * 
 * Description : 
 *  This function designates which component of LLC must handle this PDU. 
 * 
 * Parameters :
 *  frame_t *pdu_frame : input PDU.
 *  us8 *destination : destination component.
 * 
 * Returns : 
 *   Always 0.
 */

us16 
decode_pdu_type (frame_t *frame, us8 *destination)
{
	us8 type;           
	pdu_sn_t *pdu = (pdu_sn_t *) frame->llc_hdr;
   
	switch (pdu->ctrl_1 & LLC_PDU_TYPE_MASK) {
		case LLC_PDU_TYPE_U:        
			switch (LLC_U_PDU_CMD(pdu)) {
				case LLC_1_PDU_CMD_XID:        
				case LLC_1_PDU_CMD_UI:
				case LLC_1_PDU_CMD_TEST:
					type = DEST_SAP;
					break;

				case LLC_2_PDU_CMD_SABME:       
				case LLC_2_PDU_CMD_DISC:
				case LLC_2_PDU_RSP_UA:
				case LLC_2_PDU_RSP_DM:
				case LLC_2_PDU_RSP_FRMR:
					type = DEST_CONN;
					break;

				default:
					type = DEST_INVALID;
					break;
           		}
			break;
		default:                    /* I-PDU or S-PDU type */
			type = DEST_CONN;
			break;
 	}
	*destination = type;
	return (0);
}

/*
 * Function : get_llc_hdr_len 
 * 
 * Description : 
 *  This function designates LLC header length of PDU. header length for I and S
 *  PDU is 4 and for U is 3 bytes.
 * 
 * Parameters :
 *  us8 pdu_type : type of PDU.
 * 
 * Returns : 
 *  Length of header. 
 */

static int 
get_llc_hdr_length (us8 pdu_type)
{
	int rtn_val=0;
  
	switch (pdu_type) {
		case LLC_PDU_TYPE_I:
		case LLC_PDU_TYPE_S:

			rtn_val = 4;
			break;

		case LLC_PDU_TYPE_U:

			rtn_val = 3;
			break;
	}
	return (rtn_val);
}

/*
 * Function : pdu_get_pf_bit 
 * 
 * Description : 
 *  This function extracts p/f bit of input PDU. at first examines type of PDU
 *  and then extracts p/f bit.
 * 
 * Parameters :
 *  pdu_sn_t *pdu : pointer to LLC header.
 * 
 * Returns : 
 *  p/f bit. 
 */

static us8 
pdu_get_pf_bit(pdu_sn_t *pdu)
{
	us8 pdu_type;
	us8 pf_bit;

	if (pdu->ctrl_1 & 1) {
		if ((pdu->ctrl_1 & LLC_PDU_TYPE_U)==LLC_PDU_TYPE_U) {
			pdu_type = LLC_PDU_TYPE_U;
		} else {
			pdu_type = LLC_PDU_TYPE_S;
		}
	} else {
		pdu_type = LLC_PDU_TYPE_I;
	}
	switch (pdu_type) {
		case LLC_PDU_TYPE_I:
		case LLC_PDU_TYPE_S:
			pf_bit = pdu->ctrl_2 & LLC_S_PF_BIT_MASK;
			break;
		case LLC_PDU_TYPE_U:
			pf_bit = (pdu->ctrl_1 & LLC_U_PF_BIT_MASK) >> 4;
			break;
		default:
			pf_bit = 0;
			FDBG_ERR_MSG((KERN_ERR "pdu_decode_pf_bit : 
                                         invalid pdu type,type=%d\n", pdu_type));
			break;
	}
	return (pf_bit);
}
