/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.0  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

/*****************************************************************************

Source    as_message.c

Version   0.1

Date    2012/11/06

Product   NAS stack

Subsystem Application Programming Interface

Author    Frederic Maurel

Description Defines the messages supported by the Access Stratum sublayer
    protocol (usually RRC and S1AP for E-UTRAN) and functions used
    to encode and decode

*****************************************************************************/

#include "as_message.h"
#include "commonDef.h"
#include "nas_log.h"

#include <string.h> // memcpy
#include <stdlib.h> // free

/****************************************************************************/
/****************  E X T E R N A L    D E F I N I T I O N S  ****************/
/****************************************************************************/

/****************************************************************************/
/*******************  L O C A L    D E F I N I T I O N S  *******************/
/****************************************************************************/

/****************************************************************************/
/******************  E X P O R T E D    F U N C T I O N S  ******************/
/****************************************************************************/

/****************************************************************************
 **                                                                        **
 ** Name:  as_message_decode()                                       **
 **                                                                        **
 ** Description: Decode AS message and accordingly fills data structure    **
 **                                                                        **
 ** Inputs:  buffer:  Pointer to the buffer containing the       **
 **       message                                    **
 **      length:  Number of bytes that should be decoded     **
 **    Others:  None                                       **
 **                                                                        **
 ** Outputs:   msg:   AS message structure to be filled          **
 **      Return:  The AS message identifier when the buffer  **
 **       has been successfully decoded;             **
 **       RETURNerror otherwise.                     **
 **    Others:  None                                       **
 **                                                                        **
 ***************************************************************************/
int as_message_decode(const char* buffer, as_message_t* msg, int length)
{
  LOG_FUNC_IN;

  int bytes;
  Byte_t** data = NULL;

  /* Get the message type */
  msg->msgID = *(uint16_t*)(buffer);
  bytes = sizeof(uint16_t);

  switch (msg->msgID) {
  case AS_NAS_ESTABLISH_REQ:
    /* NAS signalling connection establish request */
    bytes += sizeof(nas_establish_req_t) - sizeof(Byte_t*);
    data = &msg->msg.nas_establish_req.initialNasMsg.data;
    break;

  case AS_NAS_ESTABLISH_IND:
    /* NAS signalling connection establishment indication */
    bytes += sizeof(nas_establish_ind_t) - sizeof(Byte_t*);
    data = &msg->msg.nas_establish_ind.initialNasMsg.data;
    break;

  case AS_NAS_ESTABLISH_RSP:
    /* NAS signalling connection establishment response */
    bytes += sizeof(nas_establish_rsp_t) - sizeof(Byte_t*);
    data = &msg->msg.nas_establish_rsp.nasMsg.data;
    break;

  case AS_NAS_ESTABLISH_CNF:
    /* NAS signalling connection establishment confirm */
    bytes += sizeof(nas_establish_cnf_t) - sizeof(Byte_t*);
    data = &msg->msg.nas_establish_cnf.nasMsg.data;
    break;

  case AS_UL_INFO_TRANSFER_REQ:
    /* Uplink L3 data transfer request */
    bytes += sizeof(ul_info_transfer_req_t) - sizeof(Byte_t*);
    data = &msg->msg.ul_info_transfer_req.nasMsg.data;
    break;

  case AS_UL_INFO_TRANSFER_IND:
    /* Uplink L3 data transfer indication */
    bytes += sizeof(ul_info_transfer_ind_t) - sizeof(Byte_t*);
    data = &msg->msg.ul_info_transfer_ind.nasMsg.data;
    break;

  case AS_DL_INFO_TRANSFER_REQ:
    /* Downlink L3 data transfer request */
    bytes += sizeof(dl_info_transfer_req_t) - sizeof(Byte_t*);
    data = &msg->msg.dl_info_transfer_req.nasMsg.data;
    break;

  case AS_DL_INFO_TRANSFER_IND:
    /* Downlink L3 data transfer indication */
    bytes += sizeof(dl_info_transfer_ind_t) - sizeof(Byte_t*);
    data = &msg->msg.dl_info_transfer_ind.nasMsg.data;
    break;

  case AS_BROADCAST_INFO_IND:
  case AS_CELL_INFO_REQ:
  case AS_CELL_INFO_CNF:
  case AS_CELL_INFO_IND:
  case AS_PAGING_REQ:
  case AS_PAGING_IND:
  case AS_NAS_RELEASE_REQ:
  case AS_UL_INFO_TRANSFER_CNF:
  case AS_DL_INFO_TRANSFER_CNF:
  case AS_NAS_RELEASE_IND:
  case AS_RAB_ESTABLISH_REQ:
  case AS_RAB_ESTABLISH_IND:
  case AS_RAB_ESTABLISH_RSP:
  case AS_RAB_ESTABLISH_CNF:
  case AS_RAB_RELEASE_REQ:
  case AS_RAB_RELEASE_IND:
    /* Messages without dedicated NAS information */
    bytes = length;
    break;

  default:
    bytes = 0;
    LOG_TRACE(WARNING, "NET-API   - AS message 0x%x is not valid",
              msg->msgID);
    break;
  }

  if (bytes > 0) {
    if (data) {
      /* Set the pointer to dedicated NAS information */
      *data = (Byte_t*)(buffer + bytes);
    }

    /* Decode the message */
    memcpy(msg, (as_message_t*)buffer, bytes);
    LOG_FUNC_RETURN (msg->msgID);
  }

  LOG_TRACE(WARNING, "NET-API   - Failed to decode AS message 0x%x",
            msg->msgID);
  LOG_FUNC_RETURN (RETURNerror);
}

/****************************************************************************
 **                                                                        **
 ** Name:  as_message_encode()                                       **
 **                                                                        **
 ** Description: Encode AS message                                         **
 **                                                                        **
 ** Inputs:  msg:   AS message structure to encode             **
 **      length:  Maximal capacity of the output buffer      **
 **    Others:  None                                       **
 **                                                                        **
 ** Outputs:   buffer:  Pointer to the encoded data buffer         **
 **      Return:  The number of characters in the buffer     **
 **       when data have been successfully encoded;  **
 **       RETURNerror otherwise.                     **
 **    Others:  None                                       **
 **                                                                        **
 ***************************************************************************/
int as_message_encode(char* buffer, as_message_t* msg, int length)
{
  LOG_FUNC_IN;

  int bytes = sizeof(msg->msgID);
  as_nas_info_t* nas_msg = NULL;

  switch (msg->msgID) {
  case AS_BROADCAST_INFO_IND:
    /* Broadcast information */
    bytes += sizeof(broadcast_info_ind_t);
    break;

  case AS_CELL_INFO_REQ:
    /* Cell information request */
    bytes += sizeof(cell_info_req_t);
    break;

  case AS_CELL_INFO_CNF:
    /* Cell information response */
    bytes += sizeof(cell_info_cnf_t);
    break;

  case AS_CELL_INFO_IND:
    /* Cell information indication */
    bytes += sizeof(cell_info_ind_t);
    break;

  case AS_PAGING_REQ:
    /* Paging information request */
    bytes += sizeof(paging_req_t);
    break;

  case AS_PAGING_IND:
    /* Paging information indication */
    bytes += sizeof(paging_ind_t);
    break;

  case AS_NAS_ESTABLISH_REQ:
    /* NAS signalling connection establish request */
    bytes += sizeof(nas_establish_req_t) - sizeof(Byte_t*);
    nas_msg = &msg->msg.nas_establish_req.initialNasMsg;
    break;

  case AS_NAS_ESTABLISH_IND:
    /* NAS signalling connection establish indication */
    bytes += sizeof(nas_establish_ind_t) - sizeof(Byte_t*);
    nas_msg = &msg->msg.nas_establish_ind.initialNasMsg;
    break;

  case AS_NAS_ESTABLISH_RSP:
    /* NAS signalling connection establish response */
    bytes += sizeof(nas_establish_rsp_t) - sizeof(Byte_t*);
    nas_msg = &msg->msg.nas_establish_rsp.nasMsg;
    break;

  case AS_NAS_ESTABLISH_CNF:
    /* NAS signalling connection establish confirm */
    bytes += sizeof(nas_establish_cnf_t) - sizeof(Byte_t*);
    nas_msg = &msg->msg.nas_establish_cnf.nasMsg;
    break;

  case AS_NAS_RELEASE_REQ:
    /* NAS signalling connection release request */
    bytes += sizeof(nas_release_req_t);
    break;

  case AS_NAS_RELEASE_IND:
    /* NAS signalling connection release indication */
    bytes += sizeof(nas_release_ind_t);
    break;

  case AS_UL_INFO_TRANSFER_REQ:
    /* Uplink L3 data transfer request */
    bytes += sizeof(ul_info_transfer_req_t) - sizeof(Byte_t*);
    nas_msg = &msg->msg.ul_info_transfer_req.nasMsg;
    break;

  case AS_UL_INFO_TRANSFER_CNF:
    /* Uplink L3 data transfer confirm */
    bytes += sizeof(ul_info_transfer_cnf_t);
    break;

  case AS_UL_INFO_TRANSFER_IND:
    /* Uplink L3 data transfer indication */
    bytes += sizeof(ul_info_transfer_ind_t) - sizeof(Byte_t*);
    nas_msg = &msg->msg.ul_info_transfer_ind.nasMsg;
    break;

  case AS_DL_INFO_TRANSFER_REQ:
    /* Downlink L3 data transfer */
    bytes += sizeof(dl_info_transfer_req_t) - sizeof(Byte_t*);
    nas_msg = &msg->msg.dl_info_transfer_req.nasMsg;
    break;

  case AS_DL_INFO_TRANSFER_CNF:
    /* Downlink L3 data transfer confirm */
    bytes += sizeof(dl_info_transfer_cnf_t);
    break;

  case AS_DL_INFO_TRANSFER_IND:
    /* Downlink L3 data transfer indication */
    bytes += sizeof(dl_info_transfer_ind_t) - sizeof(Byte_t*);
    nas_msg = &msg->msg.dl_info_transfer_ind.nasMsg;
    break;

  case AS_RAB_ESTABLISH_REQ:
    /* Radio Access Bearer establishment request */
    bytes += sizeof(rab_establish_req_t);
    break;

  case AS_RAB_ESTABLISH_IND:
    /* Radio Access Bearer establishment indication */
    bytes += sizeof(rab_establish_ind_t);
    break;

  case AS_RAB_ESTABLISH_RSP:
    /* Radio Access Bearer establishment response */
    bytes += sizeof(rab_establish_rsp_t);
    break;

  case AS_RAB_ESTABLISH_CNF:
    /* Radio Access Bearer establishment confirm */
    bytes += sizeof(rab_establish_cnf_t);
    break;

  case AS_RAB_RELEASE_REQ:
    /* Radio Access Bearer release request */
    bytes += sizeof(rab_release_req_t);
    break;

  case AS_RAB_RELEASE_IND:
    /* Radio Access Bearer release indication */
    bytes += sizeof(rab_release_ind_t);
    break;

  default:
    LOG_TRACE(WARNING, "NET-API   - AS message 0x%x is not valid",
              msg->msgID);
    bytes = length;
    break;
  }

  if (length > bytes) {
    /* Encode the AS message */
    memcpy(buffer, (unsigned char*)msg, bytes);

    if ( nas_msg && (nas_msg->length > 0) ) {
      /* Copy the NAS message */
      memcpy(buffer + bytes, nas_msg->data, nas_msg->length);
      bytes += nas_msg->length;
      /* Release NAS message memory */
      free(nas_msg->data);
      nas_msg->length = 0;
      nas_msg->data = NULL;
    }

    LOG_FUNC_RETURN (bytes);
  }

  LOG_TRACE(WARNING, "NET-API   - Failed to encode AS message 0x%x",
            msg->msgID);
  LOG_FUNC_RETURN (RETURNerror);
}

/****************************************************************************/
/*********************  L O C A L    F U N C T I O N S  *********************/
/****************************************************************************/

