/*******************************************************************************
 OpenAirInterface
 Copyright(c) 1999 - 2014 Eurecom

 OpenAirInterface is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.


 OpenAirInterface is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with OpenAirInterface.The full GNU General Public License is
 included in this distribution in the file called "COPYING". If not,
 see <http://www.gnu.org/licenses/>.

 Contact Information
 OpenAirInterface Admin: openair_admin@eurecom.fr
 OpenAirInterface Tech : openair_tech@eurecom.fr
 OpenAirInterface Dev  : openair4g-devel@lists.eurecom.fr

 Address      : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.

 *******************************************************************************/
/*****************************************************************************
Source    usim_data.c

Version   0.1

Date    2012/10/31

Product   USIM data generator

Subsystem USIM data generator main process

Author    Frederic Maurel

Description Implements the utility used to generate data stored in the
    USIM application

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

#include "network.h"

#include "usim_api.h"
#include "memory.h"
#include "network.h"

#include <stdio.h>  // perror, printf
#include <stdlib.h> // exit
#include <string.h> // memset, memcpy, strncpy


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

#define KSI     USIM_KSI_NOT_AVAILABLE
#define KSI_ASME    USIM_KSI_NOT_AVAILABLE
#define INT_ALGO    USIM_INT_EIA2
#define ENC_ALGO    USIM_ENC_EEA0
#define SECURITY_ALGORITHMS (ENC_ALGO | INT_ALGO)

#define MIN_TAC     0x0000
#define MAX_TAC     0xFFFE

#define DEFAULT_TMSI    0x0000000D
#define DEFAULT_P_TMSI    0x0000000D
#define DEFAULT_M_TMSI    0x0000000D
#define DEFAULT_LAC   0xFFFE
#define DEFAULT_RAC   0x01
#define DEFAULT_TAC   0x0001

#define DEFAULT_MME_ID    0x0102
#define DEFAULT_MME_CODE  0x0F

#define PRINT_PLMN_DIGIT(d) if ((d) != 0xf) printf("%u", (d))

#define PRINT_PLMN(plmn)    \
    PRINT_PLMN_DIGIT((plmn).MCCdigit1); \
    PRINT_PLMN_DIGIT((plmn).MCCdigit2); \
    PRINT_PLMN_DIGIT((plmn).MCCdigit3); \
    PRINT_PLMN_DIGIT((plmn).MNCdigit1); \
    PRINT_PLMN_DIGIT((plmn).MNCdigit2); \
    PRINT_PLMN_DIGIT((plmn).MNCdigit3)

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

static void _display_usage(const char* command);

static void _display_usim_data(const usim_data_t* data);

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

int main (int argc, const char* argv[])
{
  int rc;
  usim_data_t usim_data;

  unsigned char gen_data;

  /*
   * Read command line parameters
   */
  if (argc != 2) {
    fprintf(stderr, "Invalid parameter\n");
    _display_usage(argv[0]);
    exit(EXIT_FAILURE);
  } else if ( (strcmp(argv[1], "--gen") == 0) ||
              (strcmp(argv[1], "-g") == 0) ) {
    /* Generate USIM data files */
    gen_data = TRUE;
  } else if ( (strcmp(argv[1], "--print") == 0) ||
              (strcmp(argv[1], "-p") == 0) ) {
    /* Display content of USIM data files */
    gen_data = FALSE;
  } else {
    /* Display usage */
    _display_usage(argv[0]);
    exit(EXIT_SUCCESS);
  }

  if (gen_data) {
    /*
     * Initialize USIM data
     */
    memset(&usim_data, 0, sizeof(usim_data_t));

#if (SELECTED_PLMN == FCT1)
    /*
     * International Mobile Subscriber Identity
     * IMSI = MCC + MNC + MSIN = 310 (USA) + 028 (UNKNOWN) + 90832150
     */
#warning "IMSI 310.028.90832150"
    usim_data.imsi.length = 8;
    usim_data.imsi.u.num.parity = EVEN_PARITY;      // Parity: even
    usim_data.imsi.u.num.digit1 = 3;                // MCC digit 1
    usim_data.imsi.u.num.digit2 = 1;                // MCC digit 2
    usim_data.imsi.u.num.digit3 = 0;                // MCC digit 3
    usim_data.imsi.u.num.digit4 = 0;                // MNC digit 1
    usim_data.imsi.u.num.digit5 = 2;                // MNC digit 2
    usim_data.imsi.u.num.digit6 = 8;                // MNC digit 3
    usim_data.imsi.u.num.digit7 = 9;
    usim_data.imsi.u.num.digit8 = 0;
    usim_data.imsi.u.num.digit9 = 8;
    usim_data.imsi.u.num.digit10 = 3;
    usim_data.imsi.u.num.digit11 = 2;
    usim_data.imsi.u.num.digit12 = 1;
    usim_data.imsi.u.num.digit13 = 5;
    usim_data.imsi.u.num.digit14 = 0;
    usim_data.imsi.u.num.digit15 = 0b1111;
#endif
#if (SELECTED_PLMN == SFR1)

    /*
     * International Mobile Subscriber Identity
     * IMSI = MCC + MNC + MSIN = 208 (France) + 10 (SFR) + 00001234
     */
#warning "IMSI 208.10.00001234"

    usim_data.imsi.length = 8;
    usim_data.imsi.u.num.parity = EVEN_PARITY;      // Parity: even
    usim_data.imsi.u.num.digit1 = 2;                // MCC digit 1
    usim_data.imsi.u.num.digit2 = 0;                // MCC digit 2
    usim_data.imsi.u.num.digit3 = 8;                // MCC digit 3
    usim_data.imsi.u.num.digit4 = 1;                // MNC digit 1
    usim_data.imsi.u.num.digit5 = 0;                // MNC digit 2
    usim_data.imsi.u.num.digit6 = 0;//0b1111;     // MNC digit 3
    usim_data.imsi.u.num.digit7 = 0;
    usim_data.imsi.u.num.digit8 = 0;
    usim_data.imsi.u.num.digit9 = 0;
    usim_data.imsi.u.num.digit10 = 0;
    usim_data.imsi.u.num.digit11 = 1;
    usim_data.imsi.u.num.digit12 = 2;
    usim_data.imsi.u.num.digit13 = 3;
    usim_data.imsi.u.num.digit14 = 4;
    usim_data.imsi.u.num.digit15 = 0b1111;
#endif
#if (SELECTED_PLMN == OAI_LTEBOX)
#warning "IMSI 208.93.00001111"
    /*
     * International Mobile Subscriber Identity
     * IMSI = MCC + MNC + MSIN = 208 (France) + 10 (SFR) + 00001234
     */
    #warning "IMSI 208.93.0100001110"
    usim_data.imsi.length = 8;
    usim_data.imsi.u.num.parity = ODD_PARITY;      // Parity: even
    usim_data.imsi.u.num.digit1 = 2;                // MCC digit 1
    usim_data.imsi.u.num.digit2 = 0;                // MCC digit 2
    usim_data.imsi.u.num.digit3 = 8;                // MCC digit 3
    usim_data.imsi.u.num.digit4 = 9;                // MNC digit 1
    usim_data.imsi.u.num.digit5 = 3;                // MNC digit 2
    usim_data.imsi.u.num.digit6 = 0;     // MNC digit 3
    usim_data.imsi.u.num.digit7 = 1;
    usim_data.imsi.u.num.digit8 = 0;
    usim_data.imsi.u.num.digit9 = 0;
    usim_data.imsi.u.num.digit10 = 0;
    usim_data.imsi.u.num.digit11 = 0;
    usim_data.imsi.u.num.digit12 = 1;
    usim_data.imsi.u.num.digit13 = 1;
    usim_data.imsi.u.num.digit14 = 1;
    usim_data.imsi.u.num.digit15 = 1;
#endif
#if (SELECTED_PLMN == TEST1)
#warning "IMSI 001.01.000001234"
    usim_data.imsi.length = 8;
    usim_data.imsi.u.num.parity = 0x0;  // Type of identity = IMSI, even
    usim_data.imsi.u.num.digit1 = 0;    // MCC digit 1
    usim_data.imsi.u.num.digit2 = 0;    // MCC digit 2
    usim_data.imsi.u.num.digit3 = 1;    // MCC digit 3
    usim_data.imsi.u.num.digit4 = 0;    // MNC digit 1
    usim_data.imsi.u.num.digit5 = 1;    // MNC digit 2
    usim_data.imsi.u.num.digit6 = 0;
    usim_data.imsi.u.num.digit7 = 0;
    usim_data.imsi.u.num.digit8 = 0;
    usim_data.imsi.u.num.digit9 = 0;
    usim_data.imsi.u.num.digit10 = 0;
    usim_data.imsi.u.num.digit11 = 1;
    usim_data.imsi.u.num.digit12 = 2;
    usim_data.imsi.u.num.digit13 = 3;
    usim_data.imsi.u.num.digit14 = 4;
    usim_data.imsi.u.num.digit15 = 0xF;
#endif
    /*
     * Ciphering and Integrity Keys
     */
    usim_data.keys.ksi = KSI;
    memset(&usim_data.keys.ck, 0, USIM_CK_SIZE);
    memset(&usim_data.keys.ik, 0, USIM_IK_SIZE);

    /*
     * Higher Priority PLMN search period
     */
    usim_data.hpplmn = 0x00;  /* Disable timer */

    /*
     * List of Forbidden PLMNs
     */
    for (int i = 0; i < USIM_FPLMN_MAX; i++) {
      memset(&usim_data.fplmn[i], 0xff, sizeof(plmn_t));
    }

    /*
     * Location Information
     */
    usim_data.loci.tmsi = DEFAULT_TMSI;
    usim_data.loci.lai.plmn = network_records[SELECTED_PLMN].plmn;
    usim_data.loci.lai.lac = DEFAULT_LAC;
    usim_data.loci.status = USIM_LOCI_NOT_UPDATED;
    /*
     * Packet Switched Location Information
     */
    usim_data.psloci.p_tmsi = DEFAULT_P_TMSI;
    usim_data.psloci.signature[0] = 0x01;
    usim_data.psloci.signature[1] = 0x02;
    usim_data.psloci.signature[2] = 0x03;
    usim_data.psloci.rai.plmn = network_records[SELECTED_PLMN].plmn;
    usim_data.psloci.rai.lac = DEFAULT_LAC;
    usim_data.psloci.rai.rac = DEFAULT_RAC;
    usim_data.psloci.status = USIM_PSLOCI_NOT_UPDATED;
    /*
     * Administrative Data
     */
    usim_data.ad.UE_Operation_Mode = USIM_NORMAL_MODE;
    usim_data.ad.Additional_Info = 0xffff;
    usim_data.ad.MNC_Length = 2;
    /*
     * EPS NAS security context
     */
    usim_data.securityctx.length = 52;
    usim_data.securityctx.KSIasme.type = USIM_KSI_ASME_TAG;
    usim_data.securityctx.KSIasme.length = 1;
    usim_data.securityctx.KSIasme.value[0] = KSI_ASME;
    usim_data.securityctx.Kasme.type = USIM_K_ASME_TAG;
    usim_data.securityctx.Kasme.length = USIM_K_ASME_SIZE;
    memset(usim_data.securityctx.Kasme.value, 0,
           usim_data.securityctx.Kasme.length);
    usim_data.securityctx.ulNAScount.type = USIM_UL_NAS_COUNT_TAG;
    usim_data.securityctx.ulNAScount.length = USIM_UL_NAS_COUNT_SIZE;
    memset(usim_data.securityctx.ulNAScount.value, 0,
           usim_data.securityctx.ulNAScount.length);
    usim_data.securityctx.dlNAScount.type = USIM_DL_NAS_COUNT_TAG;
    usim_data.securityctx.dlNAScount.length = USIM_DL_NAS_COUNT_SIZE;
    memset(usim_data.securityctx.dlNAScount.value, 0,
           usim_data.securityctx.dlNAScount.length);
    usim_data.securityctx.algorithmID.type = USIM_INT_ENC_ALGORITHMS_TAG;
    usim_data.securityctx.algorithmID.length = 1;
    usim_data.securityctx.algorithmID.value[0] = SECURITY_ALGORITHMS;
    /*
     * Subcriber's Number
     */
    usim_data.msisdn.length = 7;
    usim_data.msisdn.number.ext = 1;
    usim_data.msisdn.number.ton = MSISDN_TON_UNKNOWKN;
    usim_data.msisdn.number.npi = MSISDN_NPI_ISDN_TELEPHONY;
    usim_data.msisdn.number.digit[0].msb = 3;
    usim_data.msisdn.number.digit[0].lsb = 3;
    usim_data.msisdn.number.digit[1].msb = 6;
    usim_data.msisdn.number.digit[1].lsb = 1;
    usim_data.msisdn.number.digit[2].msb = 1;
    usim_data.msisdn.number.digit[2].lsb = 1;
    usim_data.msisdn.number.digit[3].msb = 2;
    usim_data.msisdn.number.digit[3].lsb = 3;
    usim_data.msisdn.number.digit[4].msb = 4;
    usim_data.msisdn.number.digit[4].lsb = 5;
    usim_data.msisdn.number.digit[5].msb = 6;
    usim_data.msisdn.number.digit[5].lsb = 0xf;
    usim_data.msisdn.number.digit[6].msb = 0xf;
    usim_data.msisdn.number.digit[6].lsb = 0xf;
    usim_data.msisdn.number.digit[7].msb = 0xf;
    usim_data.msisdn.number.digit[7].lsb = 0xf;
    usim_data.msisdn.number.digit[8].msb = 0xf;
    usim_data.msisdn.number.digit[8].lsb = 0xf;
    usim_data.msisdn.number.digit[9].msb = 0xf;
    usim_data.msisdn.number.digit[9].lsb = 0xf;
    usim_data.msisdn.conf1_record_id = 0xff;  /* Not used */
    usim_data.msisdn.ext1_record_id = 0xff;   /* Not used */

    /*
     * PLMN Network Name and Operator PLMN List
     */
    for (int i = TEST1; i < VDF1; i++) {
      network_record_t record = network_records[i];
      usim_data.pnn[i].fullname.type = USIM_PNN_FULLNAME_TAG;
      usim_data.pnn[i].fullname.length = strlen(record.fullname);
      strncpy((char*)usim_data.pnn[i].fullname.value, record.fullname,
              usim_data.pnn[i].fullname.length);
      usim_data.pnn[i].shortname.type = USIM_PNN_SHORTNAME_TAG;
      usim_data.pnn[i].shortname.length = strlen(record.shortname);
      strncpy((char*)usim_data.pnn[i].shortname.value, record.shortname,
              usim_data.pnn[i].shortname.length);
      usim_data.opl[i].plmn = record.plmn;
      usim_data.opl[i].start = record.tac_start;
      usim_data.opl[i].end = record.tac_end;
      usim_data.opl[i].record_id = i;
    }

    for (int i = VDF2; i < USIM_OPL_MAX; i++) {
      memset(&usim_data.opl[i].plmn, 0xff, sizeof(plmn_t));
    }

    /*
     * List of Equivalent HPLMNs
     */
    usim_data.ehplmn[0] = network_records[SFR2].plmn;
    usim_data.ehplmn[1] = network_records[SFR3].plmn;
    /*
     * Home PLMN Selector with Access Technology
     */
    usim_data.hplmn.plmn = network_records[SELECTED_PLMN].plmn;
    usim_data.hplmn.AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);

    /*
     * List of user controlled PLMN selector with Access Technology
     */
    for (int i = 0; i < USIM_PLMN_MAX; i++) {
      memset(&usim_data.plmn[i], 0xff, sizeof(plmn_t));
    }

    /*
     * List of operator controlled PLMN selector with Access Technology
     */
    usim_data.oplmn[0].plmn = network_records[VDF1].plmn;
    usim_data.oplmn[0].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
    usim_data.oplmn[1].plmn = network_records[VDF2].plmn;
    usim_data.oplmn[1].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
    usim_data.oplmn[2].plmn = network_records[VDF3].plmn;
    usim_data.oplmn[2].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
    usim_data.oplmn[3].plmn = network_records[VDF4].plmn;
    usim_data.oplmn[3].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
    usim_data.oplmn[4].plmn = network_records[VDF5].plmn;
    usim_data.oplmn[4].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);

    for (int i = 5; i < USIM_OPLMN_MAX; i++) {
      memset(&usim_data.oplmn[i], 0xff, sizeof(plmn_t));
    }

    /*
     * EPS Location Information
     */
    usim_data.epsloci.guti.gummei.plmn = network_records[SELECTED_PLMN].plmn;
    usim_data.epsloci.guti.gummei.MMEgid = DEFAULT_MME_ID;
    usim_data.epsloci.guti.gummei.MMEcode = DEFAULT_MME_CODE;
    usim_data.epsloci.guti.m_tmsi = DEFAULT_M_TMSI;
    usim_data.epsloci.tai.plmn = usim_data.epsloci.guti.gummei.plmn;
    usim_data.epsloci.tai.tac = DEFAULT_TAC;
    usim_data.epsloci.status = USIM_EPSLOCI_UPDATED;
    /*
     * Non-Access Stratum configuration
     */
    usim_data.nasconfig.NAS_SignallingPriority.type = USIM_NAS_SIGNALLING_PRIORITY_TAG;
    usim_data.nasconfig.NAS_SignallingPriority.length = 1;
    usim_data.nasconfig.NAS_SignallingPriority.value[0] = 0x00;
    usim_data.nasconfig.NMO_I_Behaviour.type = USIM_NMO_I_BEHAVIOUR_TAG;
    usim_data.nasconfig.NMO_I_Behaviour.length = 1;
    usim_data.nasconfig.NMO_I_Behaviour.value[0] = 0x00;
    usim_data.nasconfig.AttachWithImsi.type = USIM_ATTACH_WITH_IMSI_TAG;
    usim_data.nasconfig.AttachWithImsi.length = 1;
#if defined(START_WITH_GUTI)
    usim_data.nasconfig.AttachWithImsi.value[0] = 0x00;
#else
    usim_data.nasconfig.AttachWithImsi.value[0] = 0x01;
#endif
    usim_data.nasconfig.MinimumPeriodicSearchTimer.type = USIM_MINIMUM_PERIODIC_SEARCH_TIMER_TAG;
    usim_data.nasconfig.MinimumPeriodicSearchTimer.length = 1;
    usim_data.nasconfig.MinimumPeriodicSearchTimer.value[0] = 0x00;
    usim_data.nasconfig.ExtendedAccessBarring.type = USIM_EXTENDED_ACCESS_BARRING_TAG;
    usim_data.nasconfig.ExtendedAccessBarring.length = 1;
    usim_data.nasconfig.ExtendedAccessBarring.value[0] = 0x00;
    usim_data.nasconfig.Timer_T3245_Behaviour.type = USIM_TIMER_T3245_BEHAVIOUR_TAG;
    usim_data.nasconfig.Timer_T3245_Behaviour.length = 1;
    usim_data.nasconfig.Timer_T3245_Behaviour.value[0] = 0x00;

    /*
     * Write USIM application data
     */
    rc = usim_api_write(&usim_data);

    if (rc != RETURNok) {
      perror("ERROR\t: usim_api_write() failed");
      exit(EXIT_FAILURE);
    }
  }

  /*
   * Read USIM application data
   */
  memset(&usim_data, 0, sizeof(usim_data_t));
  rc = usim_api_read(&usim_data);

  if (rc != RETURNok) {
    perror("ERROR\t: usim_api_read() failed");
    exit(EXIT_FAILURE);
  }

  /*
   * Display USIM application data
   */
  printf("\nUSIM data:\n\n");
  _display_usim_data(&usim_data);

  /*
   * Display USIM file location
   */
  char* path = memory_get_path("USIM_DIR", ".usim.nvram");
  printf("\nUSIM data file: %s\n", path);
  free(path);

  exit(EXIT_SUCCESS);
}

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

/*
 * Displays command line usage
 */
static void _display_usage(const char* command)
{
  fprintf(stderr, "usage: %s [OPTION]\n", command);
  fprintf(stderr, "\t[--gen|-g]\tGenerate the USIM data file\n");
  fprintf(stderr, "\t[--print|-p]\tDisplay the content of the USIM data file\n");
  fprintf(stderr, "\t[--help|-h]\tDisplay this usage\n");
  const char* path = getenv("USIM_DIR");

  if (path != NULL) {
    fprintf(stderr, "USIM_DIR = %s\n", path);
  } else {
    fprintf(stderr, "USIM_DIR environment variable is not defined\n");
  }
}

/*
 * Displays USIM application data
 */
static void _display_usim_data(const usim_data_t* data)
{
  int digits;

  printf("Administrative Data:\n");
  printf("\tUE_Operation_Mode\t= 0x%.2x\n", data->ad.UE_Operation_Mode);
  printf("\tAdditional_Info\t\t= 0x%.4x\n", data->ad.Additional_Info);
  printf("\tMNC_Length\t\t= %d\n\n", data->ad.MNC_Length);

  printf("IMSI:\n");
  printf("\tlength\t= %d\n", data->imsi.length);
  printf("\tparity\t= %s\n", data->imsi.u.num.parity == EVEN_PARITY ? "Even" : "Odd");
  digits = (data->imsi.length * 2) - 1 - (data->imsi.u.num.parity == EVEN_PARITY ? 1 : 0);
  printf("\tdigits\t= %d\n", digits);
  
  printf("\tdigits\t= %u%u%u%u%u%x%u%u%u%u",
         data->imsi.u.num.digit1, // MCC digit 1
         data->imsi.u.num.digit2, // MCC digit 2
         data->imsi.u.num.digit3, // MCC digit 3
         data->imsi.u.num.digit4, // MNC digit 1
         data->imsi.u.num.digit5, // MNC digit 2
         data->imsi.u.num.digit6==0xf?0:data->imsi.u.num.digit6, // MNC digit 3
         data->imsi.u.num.digit7,
         data->imsi.u.num.digit8,
         data->imsi.u.num.digit9,
         data->imsi.u.num.digit10);

  if (digits >= 11)
    printf("%x", data->imsi.u.num.digit11);

  if (digits >= 12)
    printf("%x", data->imsi.u.num.digit12);

  if (digits >= 13)
    printf("%x", data->imsi.u.num.digit13);

  if (digits >= 14)
    printf("%x", data->imsi.u.num.digit14);

  if (digits >= 15)
    printf("%x", data->imsi.u.num.digit15);

  printf("\n\n");

  printf("Ciphering and Integrity Keys:\n");
  printf("\tKSI\t: 0x%.2x\n", data->keys.ksi);
  char key[USIM_CK_SIZE + 1];
  key[USIM_CK_SIZE] = '\0';
  memcpy(key, data->keys.ck, USIM_CK_SIZE);
  printf("\tCK\t: \"%s\"\n", key);
  memcpy(key, data->keys.ik, USIM_IK_SIZE);
  printf("\tIK\t: \"%s\"\n", key);

  printf("EPS NAS security context:\n");
  printf("\tKSIasme\t: 0x%.2x\n", data->securityctx.KSIasme.value[0]);
  char kasme[USIM_K_ASME_SIZE + 1];
  kasme[USIM_K_ASME_SIZE] = '\0';
  memcpy(kasme, data->securityctx.Kasme.value, USIM_K_ASME_SIZE);
  printf("\tKasme\t: \"%s\"\n", kasme);
  printf("\tulNAScount\t: 0x%.8x\n",
         *(uint32_t*)data->securityctx.ulNAScount.value);
  printf("\tdlNAScount\t: 0x%.8x\n",
         *(uint32_t*)data->securityctx.dlNAScount.value);
  printf("\talgorithmID\t: 0x%.2x\n\n",
         data->securityctx.algorithmID.value[0]);

  printf("MSISDN\t= %u%u%u %u%u%u%u %u%u%u%u\n\n",
         data->msisdn.number.digit[0].msb,
         data->msisdn.number.digit[0].lsb,
         data->msisdn.number.digit[1].msb,
         data->msisdn.number.digit[1].lsb,
         data->msisdn.number.digit[2].msb,
         data->msisdn.number.digit[2].lsb,
         data->msisdn.number.digit[3].msb,
         data->msisdn.number.digit[3].lsb,
         data->msisdn.number.digit[4].msb,
         data->msisdn.number.digit[4].lsb,
         data->msisdn.number.digit[5].msb);

  for (int i = 0; i < USIM_PNN_MAX; i++) {
    printf("PNN[%d]\t= {%s, %s}\n", i,
           data->pnn[i].fullname.value, data->pnn[i].shortname.value);
  }

  printf("\n");

  for (int i = 0; i < USIM_OPL_MAX; i++) {
    printf("OPL[%d]\t= ", i);
    PRINT_PLMN(data->opl[i].plmn);
    printf(", TAC = [%.4x - %.4x], record_id = %d\n",
           data->opl[i].start, data->opl[i].end, data->opl[i].record_id);
  }

  printf("\n");

  printf("HPLMN\t\t= ");
  PRINT_PLMN(data->hplmn.plmn);
  printf(", AcT = 0x%x\n\n", data->hplmn.AcT);

  for (int i = 0; i < USIM_FPLMN_MAX; i++) {
    printf("FPLMN[%d]\t= ", i);
    PRINT_PLMN(data->fplmn[i]);
    printf("\n");
  }

  printf("\n");

  for (int i = 0; i < USIM_EHPLMN_MAX; i++) {
    printf("EHPLMN[%d]\t= ", i);
    PRINT_PLMN(data->ehplmn[i]);
    printf("\n");
  }

  printf("\n");

  for (int i = 0; i < USIM_PLMN_MAX; i++) {
    printf("PLMN[%d]\t\t= ", i);
    PRINT_PLMN(data->plmn[i].plmn);
    printf(", AcTPLMN = 0x%x", data->plmn[i].AcT);
    printf("\n");
  }

  printf("\n");

  for (int i = 0; i < USIM_OPLMN_MAX; i++) {
    printf("OPLMN[%d]\t= ", i);
    PRINT_PLMN(data->oplmn[i].plmn);
    printf(", AcTPLMN = 0x%x", data->oplmn[i].AcT);
    printf("\n");
  }

  printf("\n");

  printf("HPPLMN\t\t= 0x%.2x (%d minutes)\n\n", data->hpplmn, data->hpplmn);

  printf("LOCI:\n");
  printf("\tTMSI = 0x%.4x\n", data->loci.tmsi);
  printf("\tLAI\t: PLMN = ");
  PRINT_PLMN(data->loci.lai.plmn);
  printf(", LAC = 0x%.2x\n", data->loci.lai.lac);
  printf("\tstatus\t= %d\n\n", data->loci.status);

  printf("PSLOCI:\n");
  printf("\tP-TMSI = 0x%.4x\n", data->psloci.p_tmsi);
  printf("\tsignature = 0x%x 0x%x 0x%x\n",
         data->psloci.signature[0],
         data->psloci.signature[1],
         data->psloci.signature[2]);
  printf("\tRAI\t: PLMN = ");
  PRINT_PLMN(data->psloci.rai.plmn);
  printf(", LAC = 0x%.2x, RAC = 0x%.1x\n",
         data->psloci.rai.lac, data->psloci.rai.rac);
  printf("\tstatus\t= %d\n\n", data->psloci.status);

  printf("EPSLOCI:\n");
  printf("\tGUTI\t: GUMMEI\t: (PLMN = ");
  PRINT_PLMN(data->epsloci.guti.gummei.plmn);
  printf(", MMEgid = 0x%.2x, MMEcode = 0x%.1x)",
         data->epsloci.guti.gummei.MMEgid,
         data->epsloci.guti.gummei.MMEcode);
  printf(", M-TMSI = 0x%.4x\n", data->epsloci.guti.m_tmsi);
  printf("\tTAI\t: PLMN = ");
  PRINT_PLMN(data->epsloci.tai.plmn);
  printf(", TAC = 0x%.2x\n",
         data->epsloci.tai.tac);
  printf("\tstatus\t= %d\n\n", data->epsloci.status);

  printf("NASCONFIG:\n");
  printf("\tNAS_SignallingPriority\t\t: 0x%.2x\n",
         data->nasconfig.NAS_SignallingPriority.value[0]);
  printf("\tNMO_I_Behaviour\t\t\t: 0x%.2x\n",
         data->nasconfig.NMO_I_Behaviour.value[0]);
  printf("\tAttachWithImsi\t\t\t: 0x%.2x\n",
         data->nasconfig.AttachWithImsi.value[0]);
  printf("\tMinimumPeriodicSearchTimer\t: 0x%.2x\n",
         data->nasconfig.MinimumPeriodicSearchTimer.value[0]);
  printf("\tExtendedAccessBarring\t\t: 0x%.2x\n",
         data->nasconfig.ExtendedAccessBarring.value[0]);
  printf("\tTimer_T3245_Behaviour\t\t: 0x%.2x\n",
         data->nasconfig.Timer_T3245_Behaviour.value[0]);
}