FreeCalypso > hg > fc-selenite
diff src/g23m-gprs/llc/llc_rxf.c @ 1:d393cd9bb723
src/g23m-*: initial import from Magnetite
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 15 Jul 2018 04:40:46 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/g23m-gprs/llc/llc_rxf.c Sun Jul 15 04:40:46 2018 +0000 @@ -0,0 +1,1205 @@ +/* ++----------------------------------------------------------------------------- +| Project : +| Modul : ++----------------------------------------------------------------------------- +| Copyright 2002 Texas Instruments Berlin, AG +| All rights reserved. +| +| This file is confidential and a trade secret of Texas +| Instruments Berlin, AG +| The receipt of or possession of this file does not convey +| any rights to reproduce or disclose its contents or to +| manufacture, use, or sell anything it may describe, in +| whole, or in part, without the specific written consent of +| Texas Instruments Berlin, AG. ++----------------------------------------------------------------------------- +| Purpose : This modul is part of the entity LLC and implements all +| procedures and functions as described in the +| SDL-documentation (RX-statemachine) ++----------------------------------------------------------------------------- +*/ + +#ifndef LLC_RXF_C +#define LLC_RXF_C +#endif + +#define ENTITY_LLC + +/*==== INCLUDES =============================================================*/ + +#include <string.h> /* to get memcpy() */ + +#include "typedefs.h" /* to get Condat data types */ +#include "vsi.h" /* to get a lot of macros */ +#include "macdef.h" +#include "gprs.h" +#include "gsm.h" /* to get a lot of macros */ +#include "cnf_llc.h" /* to get cnf-definitions */ +#include "mon_llc.h" /* to get mon-definitions */ +#include "prim.h" /* to get the definitions of used SAP and directions */ +#include "llc.h" /* to get the global entity definitions */ + +#include "llc_rxf.h" /* to get local defines */ +#include "llc_rxp.h" /* to get the function rx_cci_decipher_cnf */ +#include "llc_f.h" /* to get global functions, e.g. llc_generate_input */ +#ifndef TI_PS_OP_CIPH_DRIVER +#include "cci_fbsf.h" /* to get functional interface */ +#endif +/*==== CONST ================================================================*/ + +/*==== LOCAL VARS ===========================================================*/ + +/*==== PRIVATE FUNCTIONS ====================================================*/ +#ifndef CF_FAST_EXEC + +GLOBAL UBYTE rx_get_desc_octet (T_desc_list *desc_list, + USHORT offset, + UBYTE *data_ptr); +#endif /* CF_FAST_EXEC */ + +/*==== PUBLIC FUNCTIONS =====================================================*/ + + +/* ++------------------------------------------------------------------------------ +| Function : rx_init ++------------------------------------------------------------------------------ +| Description : This procedure initialises all necessary variables of +| receive_pdu. +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ +#ifndef CF_FAST_EXEC + +GLOBAL void rx_init (void) +{ + TRACE_FUNCTION( "rx_init" ); + + /* + * Initialise service RX with state TLLI_UNASSIGNED. + */ + INIT_STATE (RX, RX_TLLI_UNASSIGNED); + + return; +} /* rx_init() */ + +#endif /* CF_FAST_EXEC */ + + +/* ++------------------------------------------------------------------------------ +| Function : rx_analyse_ctrl_field ++------------------------------------------------------------------------------ +| Description : This procedure analyses the received LLC frame control field +| and sets frame_type according to the frame type (U, UI, I/S). +| protected_mode is set according to the protected mode of the +| frame (U and I/S always protected, UI depends on setting of +| PM bit). The type of protected_mode is the same as in +| CCI_DECIPHER_REQ. ns is set to N(S) for I frames, N(U) for +| UI frames, or an undefined value for S and U frames. ciphering +| is set to TRUE if the frame is ciphered (U never, I/S always, +| UI depends on setting of E bit), otherwise to FALSE. frame_ok +| indicates if the frame contains enough octets to contain a +| known control field (i.e. all necessary information is +| accessible, not the control field is complete!). +| +| Parameters : grlc_unitdata_ind - a valid pointer to a GRLC_UNITDATA_IND +| primitive +| frame_type - a valid pointer to a T_PDU_TYPE variable +| protected_mode - a valid pointer to a UBYTE variable +| sapi - a valid pointer to a UBYTE variable +| ns - a valid pointer to a T_FRAME_NUM variable +| ciphering - a valid pointer to a BOOL variable +| header_size - a valid pointer to a USHORT variable +| frame_ok - a valid pointer to a BOOL variable +| ++------------------------------------------------------------------------------ +*/ + +#ifndef CF_FAST_EXEC + +GLOBAL void rx_analyse_ctrl_field (T_GRLC_UNITDATA_IND *grlc_unitdata_ind, + T_PDU_TYPE *frame_type, + UBYTE *protected_mode, + UBYTE *sapi, + T_FRAME_NUM *ns, + BOOL *ciphering, + USHORT *header_size, + BOOL *frame_ok) +{ + UBYTE first_octet; + UBYTE sec_octet; + UBYTE command_octet; + UBYTE sack_k_octet; + + + TRACE_FUNCTION( "rx_analyse_ctrl_field" ); + + /* + * Check if the frame contains enough octets to access the first octet of + * the control field to find out the type of the frame. + */ + if (grlc_unitdata_ind->desc_list.list_len < CTRL_MIN_OCTETS) + { + *frame_ok = FALSE; + return; + } + + /* + * Assume initially that the frame is ok. + */ + *frame_ok = TRUE; + + /* + * Set first_octet to the value of the first frame octet (offset 0), which + * is the address field. + */ + rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 0, &first_octet); + + *sapi = first_octet & 0x0F; + + /* + * Set sec_octet to the value of the second frame octet (offset 1), which + * is the first octet of the control field. + */ + rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 1, &sec_octet); + + + /* + * Determine frame_type, along with ns, protected_mode, and ciphering, + * depending on the frame type. + */ + if ((sec_octet & I_FRAME_MASK) EQ I_FRAME_ID) + { + /* + * I frame, protected, ciphered, at least 4 octets required to access all + * requested information (1 Address, 3 Control). + */ + *frame_type = I_FRAME; + *protected_mode = CCI_PM_PROTECTED; + *ciphering = TRUE; + + /* + * Check if the frame contains enough octets to access the complete + * I frame control field. + */ + if (grlc_unitdata_ind->desc_list.list_len < I_CTRL_MIN_OCTETS) + { + *frame_ok = FALSE; + return; + } + + /* + * Determine the header_size + */ + *header_size = I_CTRL_MIN_OCTETS; + + /* + * Add bytes in case of SACK-Bitmap (add K+1). Therefore get at first + * the command octet (offset 3). In case of an SACK, get next the k + * octet (offset 4) and add the additional size. + */ + rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 3, &command_octet); + + if ( (command_octet & 0x03) == I_FRAME_SACK ) + { + *header_size += 1; /* k octet */ + + if (grlc_unitdata_ind->desc_list.list_len < *header_size) + { + *frame_ok = FALSE; + return; + } + + rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 4, &sack_k_octet); + + *header_size += (sack_k_octet & 0x1F); /* bitmap size */ + + if (grlc_unitdata_ind->desc_list.list_len < *header_size) + { + *frame_ok = FALSE; + return; + } + } + + /* + * Extract N(S) and store it in ns. + */ + *ns = (((T_FRAME_NUM) + rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 1, NULL)) & 0x1F) << 4; + *ns |= (((T_FRAME_NUM) + rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 2, NULL)) >> 4); + } + else if ((sec_octet & S_FRAME_MASK) EQ S_FRAME_ID) + { + /* + * S frame, protected, not ciphered. No N(S) present, only N(R). + */ + *frame_type = S_FRAME; + *protected_mode = CCI_PM_PROTECTED; + *ciphering = FALSE; + + *header_size = S_CTRL_MIN_OCTETS; + + /* not necessary to add bytes in case of SACK - value is not used */ + } + else if ((sec_octet & UI_FRAME_MASK) EQ UI_FRAME_ID) + { + /* + * UI frame, at least 3 octets required to access all requested + * information (1 Address, 2 Control). + */ + *frame_type = UI_FRAME; + + /* + * Check if the frame contains enough octets to access the complete + * UI frame control field. + */ + if (grlc_unitdata_ind->desc_list.list_len < UI_CTRL_MIN_OCTETS) + { + *frame_ok = FALSE; + return; + } + + /* + * Extract protected mode setting of frame (PM bit). + */ + if (rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 2, NULL) & 0x01) + { + *protected_mode = CCI_PM_PROTECTED; + } + else + { + *protected_mode = CCI_PM_UNPROTECTED; + } + + /* + * Extract ciphering setting of frame (E bit). + */ + if (rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 2, NULL) & 0x02) + { + *ciphering = TRUE; + } + else + { + *ciphering = FALSE; + } + + *header_size = UI_CTRL_MIN_OCTETS; + + /* + * Extract N(U) and store it in ns. + */ + *ns = (((T_FRAME_NUM) + rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 1, NULL)) & 0x07) << 6; + *ns |= (((T_FRAME_NUM) + rx_get_desc_octet (&grlc_unitdata_ind->desc_list, 2, NULL)) >> 2); + } + else if ((sec_octet & U_FRAME_MASK) EQ U_FRAME_ID) + { + /* + * U frame, protected, not ciphered. No N(S) present. + */ + *frame_type = U_FRAME; + *protected_mode = CCI_PM_PROTECTED; + *ciphering = FALSE; + + *header_size = U_CTRL_MIN_OCTETS; + } + + return; +} /* rx_analyse_ctrl_field() */ + +#endif /* CF_FAST_EXEC */ + +/* ++------------------------------------------------------------------------------ +| Function : rx_decipher_req ++------------------------------------------------------------------------------ +| Description : Handles the function rx_decipher_req. This functions sets the +| ciphering parameters and calls the deciphering driver function. +| +| Parameters : +| ++------------------------------------------------------------------------------ +*/ + +#ifndef CF_FAST_EXEC +GLOBAL void rx_decipher_req (T_CCI_DECIPHER_REQ *decipher_req) +{ + T_CIPH_init_cipher_req_parms init_cipher_req_parms; + T_CIPH_cipher_req_parms cipher_req_parms; + T_CIPH_in_data_list in_data_list; + T_CIPH_out_data out_data; + T_CIPH_ck ck; + U16 i; + U8 status; + U16 cnt = 0; + + TRACE_FUNCTION( "rx_decipher_req" ); + +#ifdef LLC_TRACE_CIPHERING + TRACE_EVENT("DOWNLINK CIPHERED DATA"); + llc_trace_desc_list(&decipher_req->desc_list); +#endif + + /* + * Copy pointer to desc's from CIPHER_REQ to the in_data_list + * The in_data array in allocated dynamically in this func. + */ + llc_copy_dl_data_to_list(decipher_req, &in_data_list); + /* + * Set ciphering parameters + */ + cipher_req_parms.gprs_parameters.pm = decipher_req->pm; + cipher_req_parms.gprs_parameters.header_size = decipher_req->header_size; + cipher_req_parms.gprs_parameters.ciphering_input = + decipher_req->ciphering_input; + cipher_req_parms.gprs_parameters.threshold = 0; + init_cipher_req_parms.direction = CIPH_DOWNLINK_DIR; + init_cipher_req_parms.algo = decipher_req->ciphering_algorithm; + init_cipher_req_parms.ptr_ck = & ck; + /* + * Copy ciphering key + */ + for (i = 0; i < 8;i++) { + init_cipher_req_parms.ptr_ck->ck_element[i] = decipher_req->kc.key[i]; + } + + { + /* Use GRLC_DATA_REQ instead of CCI_CIPHER_CNF to avoid PPASS in LLC*/ + PALLOC_SDU (ll_unitdata_ind, LL_UNITDATA_IND, + (USHORT)((decipher_req->desc_list.list_len*8) - FCS_SIZE_BITS)); + + ll_unitdata_ind->sdu.o_buf = 0; + ll_unitdata_ind->sdu.l_buf = 0; + out_data.buf = + (U32)(&ll_unitdata_ind->sdu.buf[ll_unitdata_ind->sdu.o_buf >> 3]); + /* + * Initialize ciphering driver and decipher data + */ +#ifdef TI_PS_OP_CIPH_DRIVER + ciph_init_cipher_req (&init_cipher_req_parms, NULL); + ciph_cipher_req (&cipher_req_parms, &in_data_list, &out_data, &status); +#else + ciph_init_cipher_req_sim (&init_cipher_req_parms, NULL); + ciph_cipher_req_sim (&cipher_req_parms, &in_data_list, &out_data, &status); +#endif + /* + * "Send" DECIPHER_CNF to LLC + */ + ll_unitdata_ind->sdu.l_buf = out_data.len * 8; + ll_unitdata_ind->tlli = decipher_req->reference1; + ll_unitdata_ind->cipher = (UBYTE)decipher_req->reference2; + if (status == CIPH_CIPH_PASS){ + ll_unitdata_ind->sapi = CCI_FCS_PASSED; + } else { + ll_unitdata_ind->sapi = CCI_FCS_FAILED; + } + +#ifdef LLC_TRACE_CIPHERING + TRACE_EVENT("DOWNLINK DECIPHERED DATA"); + llc_trace_sdu(&ll_unitdata_ind->sdu); +#endif + rx_cci_decipher_cnf(ll_unitdata_ind); + } + /* + * Free in_data array from in_data_list allocated in llc_copy_dl_data_to_list + */ + if(in_data_list.ptr_in_data != NULL){ + MFREE(in_data_list.ptr_in_data); + in_data_list.ptr_in_data = NULL; + } + /* + * Free descriptors from the deipher_req->desc_list + */ + if (decipher_req != NULL) + { + T_desc *desc = (T_desc *)decipher_req->desc_list.first; + T_desc *next_desc; + while (desc NEQ NULL){ + next_desc = (T_desc *)desc->next; + MFREE (desc); + desc = next_desc; + /* increase freed partitions counter */ + cnt++; + } + MFREE(decipher_req); + decipher_req = NULL; + } + llc_data->fbs.cci_freed_partition +=cnt; + + /* trace number of freed partitions */ + if(llc_data->fbs.cci_info_trace){ + TRACE_EVENT_P2("INFO CCI: freed partitions %ld, total=%ld", + cnt,llc_data->fbs.cci_freed_partition); + } + +} /* rx_decipher_req */ + +#endif + +/* ++------------------------------------------------------------------------------ +| Function : rx_send_decipher_req ++------------------------------------------------------------------------------ +| Description : This procedure allocates the CCI_DECIPHER_REQ primitive, fills +| in all necessary parameters and sends the primitive to CCI. +| +| Parameters : grlc_unitdata_ind - a valid pointer to a GRLC_UNITDATA_IND +| primitive +| frame_type - indicates (un)acknowledged operation mode +| protected_mode - pm setting for CCI_DECIPHER_REQ +| ns - N(S)/N(U) for deciphering, otherwise +| undefined value +| header_size - size of header bytes (not ciphered bytes) +| ciphering - indicates deciphering (TRUE/FALSE) +| ++------------------------------------------------------------------------------ +*/ +#ifndef CF_FAST_EXEC + +GLOBAL void rx_send_decipher_req (T_GRLC_UNITDATA_IND *grlc_unitdata_ind, + T_PDU_TYPE frame_type, + UBYTE protected_mode, + T_FRAME_NUM ns, + USHORT header_size, + BOOL ciphering) +{ + ULONG oc; + + TRACE_FUNCTION ("rx_send_decipher_req"); + + { + /* + * No need to PPASS GRLC_xDATA_IND, because desc_list contains a pointer + * which can be simply copied. + */ + T_CCI_DECIPHER_REQ *cci_decipher_req; + MALLOC(cci_decipher_req, sizeof(T_CCI_DECIPHER_REQ)); + + /* + * Requires rx_analyse_ctrl_field() to set a correct CCI value. + */ + cci_decipher_req->pm = protected_mode; + + if (ciphering EQ TRUE) + { + cci_decipher_req->reference2 = LL_CIPHER_ON; /* re-use of reference is ok */ + cci_decipher_req->ciphering_algorithm = llc_data->ciphering_algorithm; + memcpy (&cci_decipher_req->kc, &llc_data->kc, sizeof(T_kc)); + + /* + * Calculate the OC which is valid for this ns. This could be the + * current OC or OC + 1 in case of an modulo overflow of ns + */ + switch (frame_type) + { + case I_FRAME: + case S_FRAME: + if (ns >= llc_data->sapi->vr) + oc = llc_data->sapi->oc_i_rx; + else + oc = llc_data->sapi->oc_i_rx + (MAX_SEQUENCE_NUMBER+1); + break; + + default: + if (ns >= llc_data->sapi->vur) + oc = llc_data->sapi->oc_ui_rx; + else + oc = llc_data->sapi->oc_ui_rx + (MAX_SEQUENCE_NUMBER+1); + break; + } + + llc_generate_input (llc_data->current_sapi, frame_type, ns, + &cci_decipher_req->ciphering_input, oc); + + cci_decipher_req->direction = CCI_DIRECTION_DOWNLINK; + } + else /* ciphering EQ FALSE */ + { + cci_decipher_req->reference2 = LL_CIPHER_OFF; + cci_decipher_req->ciphering_algorithm = CCI_CIPHER_NO_ALGORITHM; + } + + cci_decipher_req->header_size = header_size; + + /* + * TLLI must be stored somewhere + */ + cci_decipher_req->reference1 = grlc_unitdata_ind->tlli; + + cci_decipher_req->desc_list = grlc_unitdata_ind->desc_list; + + /* TRACE_EVENT ("CCI thread not available, using functional interface"); */ + + rx_decipher_req(cci_decipher_req); + } + + return; +} /* rx_send_decipher_req() */ + +#endif /* CF_FAST_EXEC */ + +/* ++------------------------------------------------------------------------------ +| Function : rx_interpret_frame ++------------------------------------------------------------------------------ +| Description : This procedure analyses the LLC header and checksum of the +| given frame (U, UI, or I) and sets sapi, pdu_type, command, +| cr_bit, pf_bit, nr, and ns accordingly. The FCS field is not +| included in frame, it has already been stripped off. frame_ok +| is set to TRUE if the frame fulfils the following requirements: +| known PDU type, valid length, valid PD bit, valid SAPI. +| frame_rej indicates a frame rejection condition if any bit of +| W4-W1 is set. If a frame is rejected, frame_rej_ctrl_length +| indicates the length of the control field. If this length +| could not be determined, frame_rej_ctrl_length is set to the +| number of control field octets of the frame. +| +| Parameters : frame - contains the frame to be analysed, must be +| a valid pointer +| sapi - will be set to the SAPI of the frame, must be +| a valid pointer +| pdu_type - will be set to the PDU type of the frame, must be +| a valid pointer +| command - will be set to the command (I/S, U) of the frame +| (if available), must be a valid pointer +| cr_bit - will be set to the C/R bit of the frame, must be +| a valid pointer +| pf_bit - will be set to the P/F bit of the frame (if +| available), must be a valid pointer +| nr - will be set to N(R) / N(U) of the frame (if +| available), must be a valid pointer +| ns - will be set to N(S) of the frame (if available), +| must be a valid pointer +| frame_ok - TRUE if the frame is ok, else FALSE, must be +| a valid pointer +| frame_rej - indicates a frame rejection condition, must be +| a valid pointer +| frame_rej_ctrl_length - number of octets in the rejected +| control field, must be a valid pointer +| ++------------------------------------------------------------------------------ +*/ + +/*#if defined(CF_FAST_EXEC) || defined(_SIMULATION_) || \ + !defined(REL99) || defined(LL_2to1) */ + +GLOBAL void rx_interpret_frame (T_sdu *frame, + UBYTE *sapi, + T_PDU_TYPE *pdu_type, + T_COMMAND *command, + T_BIT *cr_bit, + T_BIT *pf_bit, + T_FRAME_NUM *nr, + T_FRAME_NUM *ns, + BOOL *frame_ok, + UBYTE *frame_rej, + USHORT *frame_rej_ctrl_length, + UBYTE cipher) +{ + USHORT min_length = 0; /* minimum required frame length */ + BOOL check_length; /* check/ignore length at the end */ + + TRACE_FUNCTION( "rx_interpret_frame" ); + + /* + * Preset variables with suspected success, control field length is not + * yet known, so assume complete frame length. + */ + *frame_ok = TRUE; + *frame_rej = FRAME_NOT_REJ; + *frame_rej_ctrl_length = frame->l_buf/8 - 1; + + /* + * Frame length is selectively activated, depending on frame type, command, + * etc. Ignore frame length per default. + */ + check_length = FALSE; + + + /* + * Check if the frame contains enough octets to access the first octet of + * the control field to find out the type of the frame. + */ + if (frame->l_buf/8 < CTRL_MIN_OCTETS) + { + *frame_ok = FALSE; + return; + } + + + /* + * Check if PD bit is set to 0. If it is not, the frame is invalid. + * <R.LLC.XCEPTION.A.001> + */ + if ((frame->buf[frame->o_buf/8] & 0x80) != 0x00) + { + *frame_ok = FALSE; + return; + } + + + /* + * Extract C/R bit. + */ + *cr_bit = (frame->buf[frame->o_buf/8] & 0x40) >> 6; + + + /* + * Extract SAPI and check if the frame contains a reserved SAPI. + * <R.LLC.XCEPTION.A.001> + */ + *sapi = frame->buf[frame->o_buf/8] & 0x0F; + switch (*sapi) /* !!!!! constants or function/macro to determine invalid sapis */ + { + case 0: + case 2: + case 4: + case 6: + case 8: + case 10: + case 12: + case 13: + case 14: + case 15: + { + *frame_ok = FALSE; + return; + } + } + + + /* + * Determine the PDU type of the frame, along with the minimum length + * required for this PDU type to constitute a complete frame. + * Additionally, extract available variables for each PDU type. + */ + if ((frame->buf[(frame->o_buf/8)+1] & I_FRAME_MASK) EQ I_FRAME_ID) + { + /* + * I frame, at least 5 octets (1 Address, 3 Control, 1(++) + * Information, no FCS) + */ + *pdu_type = I_FRAME; + min_length = I_FRAME_MIN_OCTETS_WITHOUT_FCS; + *frame_rej_ctrl_length = I_CTRL_OCTETS; + + /* + * Extract A bit (stored in P/F bit) + */ + *pf_bit = (frame->buf[(frame->o_buf/8)+1] & 0x40) >> 6; + + /* + * Check if the frame contains enough octets to access the complete + * I frame control field. + */ + if (frame->l_buf/8 < I_CTRL_MIN_OCTETS) + { + *frame_ok = FALSE; + return; + } + + /* + * Extract N(S), N(R) + */ + *ns = (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+1] & 0x1F) << 4; + *ns |= (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+2] >> 4); + + *nr = (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+2] & 0x07) << 6; + *nr |= (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+3] >> 2); + + /* + * Determine the command of the I frame (S1 & S2 bits) + */ + switch (frame->buf[(frame->o_buf/8)+3] & 0x03) + { + case I_FRAME_RR: + TRACE_4_PARA("I-RR s:%d len:%d nr:%d ns:%d", *sapi, BYTELEN(frame->l_buf), *nr, *ns); + *command = I_RR; + break; + + case I_FRAME_ACK: + TRACE_4_PARA("I-ACK s:%d len:%d nr:%d ns:%d", *sapi, BYTELEN(frame->l_buf), *nr, *ns); + *command = I_ACK; + break; + + case I_FRAME_RNR: + TRACE_4_PARA("I-RNR s:%d len:%d nr:%d ns:%d", *sapi, BYTELEN(frame->l_buf), *nr, *ns); + *command = I_RNR; + break; + + case I_FRAME_SACK: + TRACE_4_PARA("I-SACK s:%d len:%d nr:%d ns:%d", *sapi, BYTELEN(frame->l_buf), *nr, *ns); + *command = I_SACK; + + /* + * Check if the frame contains enough octets to access the complete + * I frame SACK control field. K is at octet I_CTRL_MIN_OCTETS + 1 + * and there must be at least K+1 octets within the bitmap. Therefore + * the frame must contain a minimum of I_CTRL_MIN_OCTETS+2. + */ + if (frame->l_buf/8 < I_CTRL_MIN_OCTETS+2) + { + *frame_ok = FALSE; + return; + } + + /* + * min_length is being modified to be more accurate for SACK frames, + * according to the Bitmap Length Indicator K in + * frame->buf[(frame->o_buf/8)+4]. + */ + min_length += (frame->buf[(frame->o_buf/8)+4] & 0x1F) + 1; + + check_length = TRUE; + break; + + default: + /* + * Frame rejection condition due to receipt of a command or + * response field that is undefined or not implemented (set + * W3 bit to 1). + * <R.LLC.XCEPTION.A.002> + */ + *frame_ok = FALSE; + *frame_rej = FRAME_REJ_W3; + return; + } + } + else if ((frame->buf[(frame->o_buf/8)+1] & S_FRAME_MASK) EQ S_FRAME_ID) + { + /* + * S frame, fixed 3 octets (1 Address, 2 Control, 0 Information, no FCS) + */ + *pdu_type = S_FRAME; + min_length = S_FRAME_MIN_OCTETS_WITHOUT_FCS; + *frame_rej_ctrl_length = S_CTRL_OCTETS; + + /* + * Extract A bit (stored in P/F bit) + */ + *pf_bit = (frame->buf[(frame->o_buf/8)+1] & 0x20) >> 5; + + /* + * Check if the frame contains enough octets to access the complete + * S frame control field. + */ + if (frame->l_buf/8 < S_CTRL_MIN_OCTETS) + { + *frame_ok = FALSE; + *frame_rej = FRAME_REJ_W1; + *frame_rej_ctrl_length = frame->l_buf/8 - 1; + return; + } + + /* + * Extract N(R) + */ + *nr = (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+1] & 0x07) << 6; + *nr |= (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+2] >> 2); + + /* + * Determine the command of the S frame (S1 & S2 bits) + */ + switch (frame->buf[(frame->o_buf/8)+2] & 0x03) + { + case I_FRAME_RR: + TRACE_2_PARA("S-RR s:%d nr:%d", *sapi, *nr); + *command = I_RR; + check_length = TRUE; + break; + + case I_FRAME_ACK: + TRACE_2_PARA("S-ACK s:%d nr:%d", *sapi, *nr); + *command = I_ACK; + check_length = TRUE; + break; + + case I_FRAME_RNR: + TRACE_2_PARA("S-RNR s:%d nr:%d", *sapi, *nr); + *command = I_RNR; + check_length = TRUE; + break; + + case I_FRAME_SACK: + TRACE_2_PARA("S-SACK s:%d nr:%d", *sapi, *nr); + *command = I_SACK; + /* + * min_length is being modified to be more accurate for SACK frames. + * The S frame SACK format adds a number of up to 32 octets to the + * control field. + */ + min_length += S_FRAME_SACK_MIN_CTRL_OCTETS; + + check_length = FALSE; + break; + + default: + /* + * Frame rejection condition due to receipt of a command or + * response field that is undefined or not implemented (set + * W3 bit to 1). + * <R.LLC.XCEPTION.A.002> + */ + *frame_ok = FALSE; + *frame_rej = FRAME_REJ_W3; + return; + } + } + else if ((frame->buf[(frame->o_buf/8)+1] & UI_FRAME_MASK) EQ UI_FRAME_ID) + { + /* + * UI frame, at least 3 octets (1 Address, 2 Control, 0(++) + * Information, no FCS) + */ + *pdu_type = UI_FRAME; + min_length = UI_FRAME_MIN_OCTETS_WITHOUT_FCS; + *frame_rej_ctrl_length = UI_CTRL_OCTETS; + + /* + * Check if the frame contains enough octets to access the complete + * UI frame control field. + */ + if (frame->l_buf/8 < UI_CTRL_MIN_OCTETS) + { + *frame_ok = FALSE; + return; + } + + /* + * Extract N(U) (is stored in N(R)) + */ + *nr = (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+1] & 0x07) << 6; + *nr |= (T_FRAME_NUM)(frame->buf[(frame->o_buf/8)+2] >> 2); + + TRACE_4_PARA("UI s:%d len:%d nr:%d c:%d", *sapi, BYTELEN(frame->l_buf), *nr, cipher); + } + else if ((frame->buf[(frame->o_buf/8)+1] & U_FRAME_MASK) EQ U_FRAME_ID) + { + /* + * U frame, at least 2 octets (1 Address, 1 Control, 0(++) + * Information, no FCS) + */ + *pdu_type = U_FRAME; + min_length = U_FRAME_MIN_OCTETS_WITHOUT_FCS; + *frame_rej_ctrl_length = U_CTRL_OCTETS; + + /* + * Extract P/F bit + */ + *pf_bit = (frame->buf[(frame->o_buf/8)+1] & 0x10) >> 4; + + /* + * Check if the frame contains enough octets to access the complete + * U frame control field. + * NOTE: + * This check could be omitted, because the U frame control field size + * is only one octet. + */ + if (frame->l_buf/8 < U_CTRL_MIN_OCTETS) + { + *frame_ok = FALSE; + *frame_rej = FRAME_REJ_W1; + return; + } + + /* + * Determine the command of the U frame (M4, M3, M2, M1). + * Adjust the minimum length of the frame, if possible. + */ + switch (frame->buf[(frame->o_buf/8)+1] & 0x0F) + { + case U_FRAME_DM: + TRACE_1_PARA("DM s:%d", *sapi); + *command = U_DM; + /* + * No information field is permitted. + */ + check_length = TRUE; + break; + case U_FRAME_DISC: + TRACE_1_PARA("DISC s:%d", *sapi); + *command = U_DISC; + /* + * No information field is permitted. + */ + check_length = TRUE; + break; + case U_FRAME_UA: + TRACE_2_PARA("UA s:%d len:%d", *sapi, BYTELEN(frame->l_buf)); + *command = U_UA; + /* + * No knowledge if an information field is permitted, because + * this is only the case when UA is the response to SABM. + */ + break; + case U_FRAME_SABM: + TRACE_2_PARA("SABM s:%d len:%d", *sapi, BYTELEN(frame->l_buf)); + *command = U_SABM; + /* + * An information field is permitted, containing XID + * parameters. Therefore the size of the information field + * is not (yet) known. + */ + break; + case U_FRAME_FRMR: + TRACE_1_PARA("FRMR s:%d", *sapi); + *command = U_FRMR; + /* + * FRMR response contains an information field of 10 octets. + * <R.LLC.XCEPTION.A.008> + */ + min_length += U_FRAME_FRMR_INFO_OCTETS; + check_length = TRUE; + break; + case U_FRAME_XID: + if( *cr_bit == SGSN_COMMAND ) + { + TRACE_2_PARA("XID REQ s:%d len:%d", *sapi, BYTELEN(frame->l_buf)); + } + else + { + TRACE_2_PARA("XID RSP s:%d len:%d", *sapi, BYTELEN(frame->l_buf)); + } + *command = U_XID; + /* + * An information field is required, containing XID + * parameters. Therefore the size of the information field + * is not (yet) known. + */ + break; + default: + TRACE_0_INFO("Not supported U frame received"); + /* + * Frame rejection condition due to receipt of a command or + * response field that is undefined or not implemented (set + * W3 bit to 1). + * <R.LLC.XCEPTION.A.002> + */ + *frame_ok = FALSE; + *frame_rej = FRAME_REJ_W3; + return; + } + } + + + /* + * Determine if it is requested to check the frame length exactly + * (check_length EQ TRUE), or just to check the minimum frame length + * (check_length EQ FALSE). + */ + if (check_length AND (frame->l_buf/8 NEQ min_length)) + { + /* + * Actual length of frame doesn't correspond with computed length. + */ + *frame_ok = FALSE; + + /* + * Check if frame rejection condition occured: S or U frame with + * incorrect length (= W1 + W3 bits). frame_rej_ctrl_length has already + * been set above to the correct control field length. + * <R.LLC.XCEPTION.A.002>, <R.LLC.XCEPTION.A.010> + */ + if ((*pdu_type EQ S_FRAME) OR (*pdu_type EQ U_FRAME)) + { + *frame_rej = FRAME_REJ_W1 | FRAME_REJ_W3; + } + return; + } + else if (frame->l_buf/8 < min_length) + { + /* + * Frame doesn't contain enough octets to include the address field, + * control field, information field, and FCS field necessary to constitute + * a complete frame according to the PDU type. + * <R.LLC.XCEPTION.A.001> + */ + *frame_ok = FALSE; + return; + } + + return; +} /* rx_interpret_frame() */ + +/* #endif */ /* CF_FAST_EXEC || _SIMULATION_ */ + + +/* ++------------------------------------------------------------------------------ +| Function : rx_strip_llc_header ++------------------------------------------------------------------------------ +| Description : This procedure strips the LLC header field off of the sdu +| so that the sdu contains only the remaining L3-PDU. +| +| Parameters : sdu - contains the SDU (frame), must be a valid pointer +| pdu_type - contains the PDU type of the frame, must be +| a valid PDU type +| command - contains the command (I/S, U) of the frame, must be +| a valid command for the given PDU type +| ++------------------------------------------------------------------------------ +*/ + +#ifndef CF_FAST_EXEC + +GLOBAL void rx_strip_llc_header (T_sdu *sdu, + T_PDU_TYPE pdu_type, + T_COMMAND command) +{ + UBYTE header_len = 0; + + + TRACE_FUNCTION ("rx_strip_llc_header"); + + switch (pdu_type) + { + case I_FRAME: + /* + * I frame 4 octets. Leave SACK Bitmap in PDU, will be stripped later! + * (1 Address, 3Control). + */ + header_len = I_CTRL_MIN_OCTETS; + break; + case S_FRAME: + /* + * S frame 3 octets. Leave SACK Bitmap in PDU, will be stripped later! + * (1 Address, 2(+32 max) Control). + */ + header_len = S_CTRL_MIN_OCTETS; + break; + case UI_FRAME: + /* + * UI frame, 3 octets (1 Address, 2 Control). + */ + header_len = UI_CTRL_MIN_OCTETS; + break; + case U_FRAME: + /* + * U frame, 2 octets (1 Address, 1 Control). + */ + header_len = U_CTRL_MIN_OCTETS; + break; + default: + TRACE_ERROR ("Unknown PDU type"); + break; + } + + /* + * Adjust the beginning of the PDU using the determined header_len. + */ + sdu->o_buf += header_len * 8; + + /* + * Adjust the length of the PDU by the size of the header field. + */ + sdu->l_buf -= header_len * 8; + + return; +} /* rx_strip_llc_header() */ + +#endif /* CF_FAST_EXEC */ + +/* ++------------------------------------------------------------------------------ +| Function : rx_get_desc_octet ++------------------------------------------------------------------------------ +| Description : This procedure maps the given linear offset to a descriptor +| list and returns the value of the octet at this offset. If +| offset is too large for descriptor list, 0x00 is returned. +| Each descriptor in descriptor list is evaluated until the +| requested offset is reached. The resulting octet value is +| written to the referenced parameter data_ptr (if not set to +| NULL), and is additionally returned. +| +| Parameters : desc_list - a valid pointer to a valid descriptor list +| offset - linear octet offset, beginning with 0 +| data_ptr - a valid pointer to be set to the resulting octet +| value, or NULL +| ++------------------------------------------------------------------------------ +*/ + +#ifndef CF_FAST_EXEC + +GLOBAL UBYTE rx_get_desc_octet (T_desc_list *desc_list, + USHORT offset, + UBYTE *data_ptr) +{ + T_desc *desc; + UBYTE *desc_data; /* local pointer to data octet */ + + /* + * Check if offset is contained in descriptor list. Return 0x00 if not. + */ + if (offset >= desc_list->list_len) + { + TRACE_ERROR ("offset too large for descriptor list"); + return 0x00; + } + + /* + * Search requested data with given linear offset in descriptor list. + * Empty descriptors are skipped. Check for each descriptor if requested + * offset is in descriptor, until it is found. + */ + desc = (T_desc *)desc_list->first; + desc_data = NULL; + do + { + if (offset < desc->len) + { + /* + * Requested data is in current descriptor. Set desc_data to point to + * requested octet with remaining offset (has been decremented by + * already passed descriptor payload). + */ + /*lint -e662 Possible creation of out-of-bounds pointer */ + desc_data = &(desc->buffer[offset]); + } + else + { + /* + * Requested data is not in current descriptor. Remember data payload + * of current descriptor as already passed. + */ + offset -= desc->len; + } + + desc = (T_desc *)desc->next; + } + while ((desc NEQ NULL) AND (desc_data EQ NULL)); + + + if (desc_data EQ NULL) + { + TRACE_ERROR ("descriptor list ended before offset found"); + + if (data_ptr NEQ NULL) + { + *data_ptr = 0; + } + + return 0; + } + else + { + /* + * Store value of found octet in data_ptr, if pointer is valid. + */ + if (data_ptr NEQ NULL) + { + /*lint -e661 Possible access of out-of-bounds pointer */ + *data_ptr = *desc_data; + } + + return *desc_data; + } +} /* rx_get_desc_octet() */ + +#endif /* CF_FAST_EXEC */