diff --git a/lib/examples/CMakeLists.txt b/lib/examples/CMakeLists.txt index 652a2c195..178bb5fe8 100644 --- a/lib/examples/CMakeLists.txt +++ b/lib/examples/CMakeLists.txt @@ -32,10 +32,10 @@ target_link_libraries(synch_file srslte_phy) if(RF_FOUND) add_executable(pdsch_ue pdsch_ue.c) - target_link_libraries(pdsch_ue srslte_phy srslte_rf pthread) + target_link_libraries(pdsch_ue srslte_phy srslte_common srslte_rf pthread) add_executable(pdsch_enodeb pdsch_enodeb.c) - target_link_libraries(pdsch_enodeb srslte_phy srslte_rf pthread) + target_link_libraries(pdsch_enodeb srslte_phy srslte_common srslte_rf pthread) else(RF_FOUND) add_definitions(-DDISABLE_RF) diff --git a/lib/examples/pdsch_enodeb.c b/lib/examples/pdsch_enodeb.c index 158d672ea..81785d9db 100644 --- a/lib/examples/pdsch_enodeb.c +++ b/lib/examples/pdsch_enodeb.c @@ -35,7 +35,7 @@ #include #include #include - +#include "srslte/common/gen_mch_tables.h" #include "srslte/srslte.h" @@ -79,10 +79,10 @@ char mimo_type_str[32] = "single"; uint32_t nof_tb = 1; uint32_t multiplex_pmi = 0; uint32_t multiplex_nof_layers = 1; - +uint8_t mbsfn_sf_mask = 32; int mbsfn_area_id = -1; char *rf_args = ""; -float rf_amp = 0.8, rf_gain = 70.0, rf_freq = 2400000000; +float rf_amp = 0.8, rf_gain = 60.0, rf_freq = 2400000000; float output_file_snr = +INFINITY; @@ -123,7 +123,7 @@ int prbset_orig = 0; #define DATA_BUFF_SZ 1024*1024 -uint8_t *data[2], data2[DATA_BUFF_SZ]; +uint8_t *data_mbms, *data[2], data2[DATA_BUFF_SZ]; uint8_t data_tmp[DATA_BUFF_SZ]; void usage(char *prog) { @@ -145,7 +145,7 @@ void usage(char *prog) { printf("\t-x Transmission mode[single|diversity|cdd|multiplex] [Default %s]\n", mimo_type_str); printf("\t-b Precoding Matrix Index (multiplex mode only)* [Default %d]\n", multiplex_pmi); printf("\t-w Number of codewords/layers (multiplex mode only)* [Default %d]\n", multiplex_nof_layers); - printf("\t-u listen TCP port for input data (-1 is random) [Default %d]\n", net_port); + printf("\t-u listen TCP/UDP port for input data (if mbsfn is active then the stream is over mbsfn only) (-1 is random) [Default %d]\n", net_port); printf("\t-v [set srslte_verbose to debug, default none]\n"); printf("\t-s output file SNR [Default %f]\n", output_file_snr); printf("\n"); @@ -154,7 +154,7 @@ void usage(char *prog) { void parse_args(int argc, char **argv) { int opt; - while ((opt = getopt(argc, argv, "aglfmoncpvutxbwMs")) != -1) { + while ((opt = getopt(argc, argv, "aglfmoncpvutxbwMsB")) != -1) { switch (opt) { case 'a': @@ -206,6 +206,9 @@ void parse_args(int argc, char **argv) { case 's': output_file_snr = atof(argv[optind]); break; + case 'B': + mbsfn_sf_mask = atoi(argv[optind]); + break; default: usage(argv[0]); exit(-1); @@ -256,6 +259,8 @@ void base_init() { } bzero(data[i], sizeof(uint8_t) * SOFTBUFFER_SIZE); } + data_mbms = srslte_vec_malloc(sizeof(uint8_t) * SOFTBUFFER_SIZE); + /* init memory */ for (i = 0; i < SRSLTE_MAX_PORTS; i++) { @@ -301,7 +306,7 @@ void base_init() { } if (net_port > 0) { - if (srslte_netsource_init(&net_source, "0.0.0.0", net_port, SRSLTE_NETSOURCE_TCP)) { + if (srslte_netsource_init(&net_source, "127.0.0.1", net_port, SRSLTE_NETSOURCE_UDP)) { fprintf(stderr, "Error creating input UDP socket at port %d\n", net_port); exit(-1); } @@ -659,15 +664,21 @@ void *net_thread_fnc(void *arg) { n = srslte_netsource_read(&net_source, &data2[rpm], DATA_BUFF_SZ-rpm); if (n > 0) { // FIXME: I assume that both transport blocks have same size in case of 2 tb are active - int nbytes = 1 + (pdsch_cfg.grant.mcs[0].tbs + pdsch_cfg.grant.mcs[1].tbs - 1) / 8; + + int nbytes = 1 + (((mbsfn_area_id > -1)?(pmch_cfg.grant.mcs[0].tbs):(pdsch_cfg.grant.mcs[0].tbs + pdsch_cfg.grant.mcs[1].tbs)) - 1) / 8; rpm += n; INFO("received %d bytes. rpm=%d/%d\n",n,rpm,nbytes); wpm = 0; while (rpm >= nbytes) { // wait for packet to be transmitted sem_wait(&net_sem); - memcpy(data[0], &data2[wpm], nbytes / (size_t) 2); - memcpy(data[1], &data2[wpm], nbytes / (size_t) 2); + if(mbsfn_area_id > -1){ + memcpy(data_mbms, &data2[wpm], nbytes); + } + else{ + memcpy(data[0], &data2[wpm], nbytes / (size_t) 2); + memcpy(data[1], &data2[wpm], nbytes / (size_t) 2); + } INFO("Sent %d/%d bytes ready\n", nbytes, rpm); rpm -= nbytes; wpm += nbytes; @@ -714,6 +725,11 @@ int main(int argc, char **argv) { parse_args(argc, argv); + uint8_t mch_table[10]; + bzero(&mch_table[0], sizeof(uint8_t)*10); + if(mbsfn_area_id < -1) { + generate_mcch_table(mch_table, mbsfn_sf_mask); + } N_id_2 = cell.id % 3; sf_n_re = 2 * SRSLTE_CP_NORM_NSYMB * cell.nof_prb * SRSLTE_NRE; sf_n_samples = 2 * SRSLTE_SLOT_LEN(srslte_symbol_sz(cell.nof_prb)); @@ -739,10 +755,15 @@ int main(int argc, char **argv) { exit(-1); } if(mbsfn_area_id > -1) { - if(srslte_refsignal_mbsfn_init(&mbsfn_refs, cell, mbsfn_area_id)) { + if(srslte_refsignal_mbsfn_init(&mbsfn_refs, cell.nof_prb)) { fprintf(stderr, "Error initializing equalizer\n"); exit(-1); } + if (srslte_refsignal_mbsfn_set_cell(&mbsfn_refs, cell, mbsfn_area_id)) { + fprintf(stderr, "Error initializing MBSFNR signal\n"); + exit(-1); + } + } if(srslte_refsignal_cs_set_cell(&csr_refs, cell)){ @@ -801,7 +822,7 @@ int main(int argc, char **argv) { exit(-1); } } - + pmch_cfg.grant.mcs[0].tbs = 1096; /* Initiate valid DCI locations */ for (i=0;i -1){ + if(mch_table[sf_idx] == 1 && mbsfn_area_id > -1){ srslte_refsignal_mbsfn_put_sf(cell, 0,csr_refs.pilots[0][sf_idx], mbsfn_refs.pilots[0][sf_idx], sf_symbols[0]); } else { for (i = 0; i < cell.nof_ports; i++) { @@ -857,10 +878,10 @@ int main(int argc, char **argv) { } /* Transmit PDCCH + PDSCH only when there is data to send */ - if (net_port > 0) { + if ((net_port > 0) && (mch_table[sf_idx] == 1 && mbsfn_area_id > -1)) { send_data = net_packet_ready; if (net_packet_ready) { - INFO("Transmitting packet\n"); + INFO("Transmitting packet from port\n"); } } else { INFO("SF: %d, Generating %d random bits\n", sf_idx, pdsch_cfg.grant.mcs[0].tbs + pdsch_cfg.grant.mcs[1].tbs); @@ -878,9 +899,8 @@ int main(int argc, char **argv) { send_data = false; } } - if (send_data) { - if(sf_idx != 1 || mbsfn_area_id < 0) { // PDCCH + PDSCH + if(mch_table[sf_idx] == 0 || mbsfn_area_id < 0) { // PDCCH + PDSCH srslte_dci_format_t dci_format; switch(pdsch_cfg.mimo_type) { case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: @@ -932,41 +952,46 @@ int main(int argc, char **argv) { } } } - net_packet_ready = false; - sem_post(&net_sem); + if(mbsfn_area_id < 0){ + net_packet_ready = false; + sem_post(&net_sem); + } } }else{ // We're sending MCH on subframe 1 - PDCCH + PMCH /* Encode PDCCH */ - INFO("Putting DCI to location: n=%d, L=%d\n", locations[sf_idx][0].ncce, locations[sf_idx][0].L); - srslte_dci_msg_pack_pdsch(&ra_dl, SRSLTE_DCI_FORMAT1, &dci_msg, cell.nof_prb, cell.nof_ports, false); - if (srslte_pdcch_encode(&pdcch, &dci_msg, locations[sf_idx][0], M_CRNTI, sf_symbols, sf_idx, cfi)) { - fprintf(stderr, "Error encoding DCI message\n"); - exit(-1); - } + //INFO("Putting DCI to location: n=%d, L=%d\n", locations[sf_idx][0].ncce, locations[sf_idx][0].L); + //srslte_dci_msg_pack_pdsch(&ra_dl, SRSLTE_DCI_FORMAT1, &dci_msg, cell.nof_prb, cell.nof_ports, false); + //if (srslte_pdcch_encode(&pdcch, &dci_msg, locations[sf_idx][0], M_CRNTI, sf_symbols, sf_idx, cfi)) { + // fprintf(stderr, "Error encoding DCI message\n"); + // exit(-1); + // } /* Configure pmch_cfg parameters */ srslte_ra_dl_grant_t grant; grant.tb_en[0] = true; grant.tb_en[1] = false; + grant.mcs[0].idx = 2; - grant.mcs[0].mod = SRSLTE_MOD_QPSK; grant.nof_prb = cell.nof_prb; grant.sf_type = SRSLTE_SF_MBSFN; - grant.Qm[0] = srslte_mod_bits_x_symbol(grant.mcs[0].mod); srslte_dl_fill_ra_mcs(&grant.mcs[0], cell.nof_prb); + grant.Qm[0] = srslte_mod_bits_x_symbol(grant.mcs[0].mod); for(int i = 0; i < 2; i++){ for(int j = 0; j < grant.nof_prb; j++){ grant.prb_idx[i][j] = true; } } + for(int i = 0; i < grant.mcs[0].tbs/8;i++) + { + data_mbms[i] = i%255; + } - if (srslte_pmch_cfg(&pmch_cfg, cell, &grant, cfi, sf_idx)) { fprintf(stderr, "Error configuring PMCH\n"); exit(-1); - } + } /* Encode PMCH */ - if (srslte_pmch_encode(&pmch, &pmch_cfg, softbuffers[0], data[0], mbsfn_area_id, sf_symbols)) { + if (srslte_pmch_encode(&pmch, &pmch_cfg, softbuffers[0], data_mbms, mbsfn_area_id, sf_symbols)) { fprintf(stderr, "Error encoding PDSCH\n"); exit(-1); } @@ -984,13 +1009,14 @@ int main(int argc, char **argv) { } /* Transform to OFDM symbols */ - if(sf_idx != 1 || mbsfn_area_id < 0){ + if(mch_table[sf_idx] == 0 || mbsfn_area_id < 0){ for (i = 0; i < cell.nof_ports; i++) { srslte_ofdm_tx_sf(&ifft[i]); } }else{ srslte_ofdm_tx_sf(&ifft_mbsfn); } + /* send to file or usrp */ if (output_file_name) { @@ -1006,10 +1032,10 @@ int main(int argc, char **argv) { } usleep(1000); } else { -#ifndef DISABLE_RF +#ifndef DISABLE_RF float norm_factor = (float) cell.nof_prb/15/sqrtf(pdsch_cfg.grant.nof_prb); for (i = 0; i < cell.nof_ports; i++) { - srslte_vec_sc_prod_cfc(output_buffer[i], rf_amp * norm_factor, output_buffer[i], SRSLTE_SF_LEN_PRB(cell.nof_prb)); + srslte_vec_sc_prod_cfc(output_buffer[i], 0.01, output_buffer[i], SRSLTE_SF_LEN_PRB(cell.nof_prb)); } srslte_rf_send_multi(&rf, (void**) output_buffer, sf_n_samples, true, start_of_burst, false); start_of_burst=false; diff --git a/lib/examples/pdsch_ue.c b/lib/examples/pdsch_ue.c index fc99fdc9a..fe35a6db2 100644 --- a/lib/examples/pdsch_ue.c +++ b/lib/examples/pdsch_ue.c @@ -36,6 +36,7 @@ #include #include #include +#include "srslte/common/gen_mch_tables.h" #include #include "srslte/phy/io/filesink.h" #include "srslte/srslte.h" @@ -103,6 +104,7 @@ typedef struct { int decimate; int32_t mbsfn_area_id; uint8_t non_mbsfn_region; + uint8_t mbsfn_sf_mask; int verbose; }prog_args_t; @@ -138,6 +140,7 @@ void args_default(prog_args_t *args) { args->cpu_affinity = -1; args->mbsfn_area_id = -1; args->non_mbsfn_region = 2; + args->mbsfn_sf_mask = 32; } void usage(prog_args_t *args, char *prog) { @@ -185,7 +188,7 @@ void usage(prog_args_t *args, char *prog) { void parse_args(prog_args_t *args, int argc, char **argv) { int opt; args_default(args); - while ((opt = getopt(argc, argv, "aAoglipPcOCtdDFRnvrfuUsSZyWMN")) != -1) { + while ((opt = getopt(argc, argv, "aAoglipPcOCtdDFRnvrfuUsSZyWMNB")) != -1) { switch (opt) { case 'i': args->input_file_name = argv[optind]; @@ -275,6 +278,9 @@ void parse_args(prog_args_t *args, int argc, char **argv) { case 'N': args->non_mbsfn_region = atoi(argv[optind]); break; + case 'B': + args->mbsfn_sf_mask = atoi(argv[optind]); + break; default: usage(args, argv[0]); exit(-1); @@ -364,8 +370,11 @@ int main(int argc, char **argv) { go_exit = true; } } - - + uint8_t mch_table[10]; + bzero(&mch_table[0], sizeof(uint8_t)*10); + if(prog_args.mbsfn_area_id < -1) { + generate_mcch_table(mch_table, prog_args.mbsfn_sf_mask); + } if(prog_args.cpu_affinity > -1) { cpu_set_t cpuset; @@ -385,7 +394,7 @@ int main(int argc, char **argv) { } if (prog_args.net_port > 0) { - if (srslte_netsink_init(&net_sink, prog_args.net_address, prog_args.net_port, SRSLTE_NETSINK_TCP)) { + if (srslte_netsink_init(&net_sink, prog_args.net_address, prog_args.net_port, SRSLTE_NETSINK_UDP)) { fprintf(stderr, "Error initiating UDP socket to %s:%d\n", prog_args.net_address, prog_args.net_port); exit(-1); } @@ -668,7 +677,7 @@ int main(int argc, char **argv) { decode_pdsch = true; } else { /* We are looking for SIB1 Blocks, search only in appropiate places */ - if ((sfidx == 5 && (sfn%2)==0) || sfidx == 1) { + if ((sfidx == 5 && (sfn%2)==0) ||mch_table[sfidx] == 1) { decode_pdsch = true; } else { decode_pdsch = false; @@ -677,7 +686,7 @@ int main(int argc, char **argv) { gettimeofday(&t[1], NULL); if (decode_pdsch) { - if(sfidx != 1 || prog_args.mbsfn_area_id < 0){ // Not an MBSFN subframe + if(mch_table[sfidx] == 0 || prog_args.mbsfn_area_id < 0){ // Not an MBSFN subframe if (cell.nof_ports == 1) { /* Transmission mode 1 */ n = srslte_ue_dl_decode(&ue_dl, data, 0, sfn*10+srslte_ue_sync_get_sfidx(&ue_sync), acks); @@ -864,6 +873,8 @@ int main(int argc, char **argv) { PRINT_LINE_ADVANCE_CURSOR(); ue_dl.pdsch_pkt_errors = 0; ue_dl.pdsch_pkts_total = 0; + ue_dl.pmch_pkt_errors = 0; + ue_dl.pmch_pkts_total = 0; /* ue_dl.pkt_errors = 0; ue_dl.pkts_total = 0; @@ -944,7 +955,7 @@ int main(int argc, char **argv) { plot_real_t p_sync, pce; -plot_scatter_t pscatequal, pscatequal_pdcch; +plot_scatter_t pscatequal, pscatequal_pdcch, pscatequal_pmch; float tmp_plot[110*15*2048]; float tmp_plot2[110*15*2048]; @@ -963,6 +974,15 @@ void *plot_thread_run(void *arg) { plot_scatter_setYAxisScale(&pscatequal, -4, 4); plot_scatter_addToWindowGrid(&pscatequal, (char*)"pdsch_ue", 0, 0); + + + + plot_scatter_init(&pscatequal_pmch); + plot_scatter_setTitle(&pscatequal_pmch, "PMCH - Equalized Symbols"); + plot_scatter_setXAxisScale(&pscatequal_pmch, -4, 4); + plot_scatter_setYAxisScale(&pscatequal_pmch, -4, 4); + + plot_scatter_addToWindowGrid(&pscatequal_pmch, (char*)"pdsch_ue", 0, 1); if (!prog_args.disable_plots_except_constellation) { plot_real_init(&pce); @@ -979,7 +999,7 @@ void *plot_thread_run(void *arg) { plot_scatter_setXAxisScale(&pscatequal_pdcch, -4, 4); plot_scatter_setYAxisScale(&pscatequal_pdcch, -4, 4); - plot_real_addToWindowGrid(&pce, (char*)"pdsch_ue", 0, 1); + plot_real_addToWindowGrid(&pce, (char*)"pdsch_ue", 0, 2); plot_real_addToWindowGrid(&pscatequal_pdcch, (char*)"pdsch_ue", 1, 0); plot_real_addToWindowGrid(&p_sync, (char*)"pdsch_ue", 1, 1); } @@ -988,6 +1008,7 @@ void *plot_thread_run(void *arg) { sem_wait(&plot_sem); uint32_t nof_symbols = ue_dl.pdsch_cfg.nbits[0].nof_re; + uint32_t nof_symbols_pmch = ue_dl.pmch_cfg.nbits[0].nof_re; if (!prog_args.disable_plots_except_constellation) { for (i = 0; i < nof_re; i++) { tmp_plot[i] = 20 * log10f(cabsf(ue_dl.sf_symbols[i])); @@ -1031,6 +1052,7 @@ void *plot_thread_run(void *arg) { plot_scatter_setNewData(&pscatequal, ue_dl.pdsch.d[0], nof_symbols); + plot_scatter_setNewData(&pscatequal_pmch, ue_dl.pmch.d, nof_symbols_pmch); if (plot_sf_idx == 1) { if (prog_args.net_port_signal > 0) { srslte_netsink_write(&net_sink_signal, &sf_buffer[srslte_ue_sync_sf_len(&ue_sync)/7], diff --git a/lib/include/srslte/asn1/liblte_rrc.h b/lib/include/srslte/asn1/liblte_rrc.h index 22038d130..15afb3cab 100644 --- a/lib/include/srslte/asn1/liblte_rrc.h +++ b/lib/include/srslte/asn1/liblte_rrc.h @@ -213,17 +213,111 @@ LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_subframe_config_ie(uint8 LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT *mbsfn_subfr_cnfg); /********************************************************************* - IE Name: PMCH Info List + IE Name: TMGI - Description: Specifies configuration of all PMCHs of an MBSFN area + Description: Temporary Mobile Group Identity (PLMN + MBMS service ID) Document Reference: 36.331 v10.0.0 Section 6.3.7 *********************************************************************/ // Defines // Enums // Structs +typedef struct{ + uint16 mcc; + uint16 mnc; +}LIBLTE_RRC_PLMN_IDENTITY_STRUCT; +typedef struct{ + LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id_r9; + uint8 plmn_index_r9; + bool plmn_id_explicit; + uint32 serviceid_r9; +}LIBLTE_RRC_TMGI_R9_STRUCT; // Functions -// FIXME +LIBLTE_ERROR_ENUM liblte_rrc_pack_tmgi_r9_ie(LIBLTE_RRC_TMGI_R9_STRUCT *tmgi, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_tmgi_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_TMGI_R9_STRUCT *tmgi); + +/********************************************************************* + IE Name: MBMS Session Info + + Description: Information about an individual MBMS session + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_TMGI_R9_STRUCT tmgi_r9; + uint8 sessionid_r9; + bool sessionid_r9_present; + uint8 logicalchannelid_r9; +}LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbms_session_info_r9_ie(LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *mbms_session_info, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbms_session_info_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *mbms_session_info); + +/********************************************************************* + IE Name: PMCH Config + + Description: Contains configuration parameters of the sessions + carried by a PMCH + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +// Defines + +// Enums +typedef enum{ + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF8 = 0, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF16, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF32, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF64, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF128, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF256, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF512, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF1024, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_N_ITEMS, +}LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_ENUM; +static const char liblte_rrc_mch_scheduling_period_r9_text[LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_N_ITEMS][20] = {"8", "16", "32", "64", "128", "256", "512", "1024"}; +static const uint16 liblte_rrc_mch_scheduling_period_r9_num[LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_N_ITEMS] = {8, 16, 32, 64, 128, 256, 512, 1024}; + +// Structs +typedef struct{ + uint16 sf_alloc_end_r9; + uint8 datamcs_r9; + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_ENUM mch_schedulingperiod_r9; +}LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_pmch_config_r9_ie(LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT *pmch_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pmch_config_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT *pmch_cnfg); + +/********************************************************************* + IE Name: PMCH Info + + Description: Specifies configuration of PMCH of an MBSFN area + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_SESSION_PER_PMCH 29 +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT pmch_config_r9; + LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT mbms_sessioninfolist_r9[LIBLTE_RRC_MAX_SESSION_PER_PMCH]; + uint8 mbms_sessioninfolist_r9_size; +}LIBLTE_RRC_PMCH_INFO_R9_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_pmch_info_r9_ie(LIBLTE_RRC_PMCH_INFO_R9_STRUCT *pmch_info, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pmch_info_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_PMCH_INFO_R9_STRUCT *pmch_info); /********************************************************************* IE Name: C-RNTI @@ -2227,10 +2321,6 @@ LIBLTE_ERROR_ENUM liblte_rrc_unpack_csfb_registration_param_1xrtt_v920_ie(uint8 // Defines // Enums // Structs -typedef struct{ - uint16 mcc; - uint16 mnc; -}LIBLTE_RRC_PLMN_IDENTITY_STRUCT; typedef struct{ LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id; uint32 cell_id; @@ -5135,7 +5225,7 @@ typedef struct{ LIBLTE_RRC_UE_TIMERS_AND_CONSTANTS_STRUCT ue_timers_and_constants; LIBLTE_RRC_ARFCN_VALUE_EUTRA_STRUCT arfcn_value_eutra; LIBLTE_RRC_UL_BW_STRUCT ul_bw; - LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT mbsfn_subfr_cnfg[LIBLTE_RRC_MAX_MBSFN_ALLOCATIONS]; + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT mbsfn_subfr_cnfg_list[LIBLTE_RRC_MAX_MBSFN_ALLOCATIONS]; LIBLTE_RRC_TIME_ALIGNMENT_TIMER_ENUM time_alignment_timer; uint32 mbsfn_subfr_cnfg_list_size; uint8 additional_spectrum_emission; @@ -5545,7 +5635,7 @@ LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_9_ie(uint8 // Structs typedef struct{ LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT mbsfn_area_info_list_r9[LIBLTE_RRC_MAX_MBSFN_AREAS]; - LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT mbms_notification_config; + LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT mbsfn_notification_config; uint8 mbsfn_area_info_list_r9_size; }LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT; // Functions @@ -5771,18 +5861,18 @@ typedef enum{ LIBLTE_RRC_SIB_TYPE_11, LIBLTE_RRC_SIB_TYPE_12_v920, LIBLTE_RRC_SIB_TYPE_13_v920, - LIBLTE_RRC_SIB_TYPE_SPARE_5, - LIBLTE_RRC_SIB_TYPE_SPARE_4, - LIBLTE_RRC_SIB_TYPE_SPARE_3, - LIBLTE_RRC_SIB_TYPE_SPARE_2, - LIBLTE_RRC_SIB_TYPE_SPARE_1, + LIBLTE_RRC_SIB_TYPE_14_v1130, + LIBLTE_RRC_SIB_TYPE_15_v1130, + LIBLTE_RRC_SIB_TYPE_16_v1130, + LIBLTE_RRC_SIB_TYPE_17_v1250, + LIBLTE_RRC_SIB_TYPE_18_v1250, LIBLTE_RRC_SIB_TYPE_N_ITEMS, }LIBLTE_RRC_SIB_TYPE_ENUM; -static const char liblte_rrc_sib_type_text[LIBLTE_RRC_SIB_TYPE_N_ITEMS][20] = { "3", "4", "5", "6", - "7", "8", "9", "10", - "11", "12", "13", "SPARE", - "SPARE", "SPARE", "SPARE", "SPARE"}; -static const uint8 liblte_rrc_sib_type_num[LIBLTE_RRC_SIB_TYPE_N_ITEMS] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 0, 0, 0}; +static const char liblte_rrc_sib_type_text[LIBLTE_RRC_SIB_TYPE_N_ITEMS][20] = { "3", "4", "5", "6", + "7", "8", "9", "10", + "11", "12_v920", "13_v920", "14_v1130", + "15_v1130", "16_v1130", "17_v1250", "18_v1250"}; +static const uint8 liblte_rrc_sib_type_num[LIBLTE_RRC_SIB_TYPE_N_ITEMS] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; // Structs typedef struct{ LIBLTE_RRC_PLMN_IDENTITY_STRUCT id; @@ -5846,14 +5936,20 @@ typedef enum{ LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_11, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_12, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_14, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_15, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_16, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_17, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_18, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1, // Intentionally not first LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_N_ITEMS, }LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_ENUM; static const char liblte_rrc_sys_info_block_type_text[LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_N_ITEMS][20] = { "2", "3", "4", "5", "6", "7", "8", "9", - "10", "11", "12", "13", - "1"}; -static const uint8 liblte_rrc_sys_info_block_type_num[LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_N_ITEMS] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1}; + "10", "11", "12", "13", + "14", "15", "16", "17", + "18", "1"}; +static const uint8 liblte_rrc_sys_info_block_type_num[LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_N_ITEMS] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 1}; // Structs typedef union{ LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT sib1; @@ -6551,10 +6647,37 @@ LIBLTE_ERROR_ENUM liblte_rrc_unpack_measurement_report_msg(LIBLTE_BIT_MSG_STRUCT Document Reference: 36.331 v10.0.0 Section 6.2.2 *********************************************************************/ // Defines +#define LIBLTE_RRC_MAX_PMCH_PER_MBSFN 15 + // Enums +typedef enum{ + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF4 = 0, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF8, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF16, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF32, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF64, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF128, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF256, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_N_ITEMS, +}LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_ENUM; +static const char liblte_rrc_mbsfn_common_sf_alloc_period_r9_text[LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_N_ITEMS][20] = {"4", "8", "16", "32", + "64", "128", "256"}; +static const uint32 liblte_rrc_mbsfn_common_sf_alloc_period_r9_num[LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_N_ITEMS] = {4, 8, 16, 32, + 64, 128, 256}; // Structs +typedef struct{ + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT commonsf_allocpatternlist_r9[LIBLTE_RRC_MAX_MBSFN_ALLOCATIONS]; + uint8 commonsf_allocpatternlist_r9_size; + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_ENUM commonsf_allocperiod_r9; + LIBLTE_RRC_PMCH_INFO_R9_STRUCT pmch_infolist_r9[LIBLTE_RRC_MAX_PMCH_PER_MBSFN]; + uint8 pmch_infolist_r9_size; +}LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT; + // Functions -// FIXME +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbsfn_area_configuration_r9_msg(LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT *mbsfn_area_cnfg, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_area_configuration_r9_msg(LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT *msg, + LIBLTE_RRC_PAGING_STRUCT *mbsfn_area_cnfg); /********************************************************************* Message Name: Master Information Block @@ -6783,8 +6906,12 @@ LIBLTE_ERROR_ENUM liblte_rrc_unpack_bcch_dlsch_msg(LIBLTE_BIT_MSG_STRUCT // Defines // Enums // Structs +typedef LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT LIBLTE_RRC_MCCH_MSG_STRUCT; // Functions -// FIXME +LIBLTE_ERROR_ENUM liblte_rrc_pack_mcch_msg(LIBLTE_RRC_MCCH_MSG_STRUCT *mcch_msg, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mcch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_MCCH_MSG_STRUCT *mcch_msg); /********************************************************************* Message Name: PCCH Message diff --git a/lib/include/srslte/common/common.h b/lib/include/srslte/common/common.h index 749765f17..3313d0eb4 100644 --- a/lib/include/srslte/common/common.h +++ b/lib/include/srslte/common/common.h @@ -44,6 +44,8 @@ #define SRSLTE_N_DRB 8 #define SRSLTE_N_RADIO_BEARERS 11 +#define SRSLTE_N_MCH_LCIDS 32 + #define HARQ_DELAY_MS 4 #define MSG3_DELAY_MS 2 // Delay added to HARQ_DELAY_MS #define TTI_RX(tti) (tti>HARQ_DELAY_MS?((tti-HARQ_DELAY_MS)%10240):(10240+tti-HARQ_DELAY_MS)) diff --git a/lib/include/srslte/common/gen_mch_tables.h b/lib/include/srslte/common/gen_mch_tables.h new file mode 100644 index 000000000..2e5ea04b0 --- /dev/null +++ b/lib/include/srslte/common/gen_mch_tables.h @@ -0,0 +1,52 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE 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 Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef GEN_MCH_TALBES_H +#define GEN_MCH_TALBES_H + +/****************************************************************************** + * Common mch table generation - used in phch_common of UE and ENB for MBMS + *****************************************************************************/ +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +void generate_frame_mch_table(uint8_t *table, uint8_t alloc); +void generate_mch_table(uint8_t *table, uint32_t sf_alloc, uint8_t num_frames); +void generate_mcch_table(uint8_t *table, uint32_t sf_alloc); + +#ifdef __cplusplus +} +#endif // __cplusplus + + +#endif // SECURITY_H diff --git a/lib/include/srslte/common/mac_pcap.h b/lib/include/srslte/common/mac_pcap.h index b403c8c49..35c953228 100644 --- a/lib/include/srslte/common/mac_pcap.h +++ b/lib/include/srslte/common/mac_pcap.h @@ -50,6 +50,7 @@ public: void write_dl_sirnti(uint8_t *pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti); void write_dl_bch(uint8_t *pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti); void write_dl_pch(uint8_t *pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti); + void write_dl_mch(uint8_t *pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti); private: bool enable_write; diff --git a/lib/include/srslte/common/pdu.h b/lib/include/srslte/common/pdu.h index e9ee1f0e9..848db29e8 100644 --- a/lib/include/srslte/common/pdu.h +++ b/lib/include/srslte/common/pdu.h @@ -34,10 +34,10 @@ #include /* MAC PDU Packing/Unpacking functions. Section 6 of 36.321 */ - +class subh; namespace srslte { - + template class pdu { @@ -147,10 +147,11 @@ public: } } + protected: std::vector subheaders; uint32_t pdu_len; - uint32_t rem_len; + uint32_t rem_len; int cur_idx; int nof_subheaders; uint32_t max_subheaders; @@ -159,11 +160,10 @@ protected: uint32_t total_sdu_len; uint32_t sdu_offset_start; int last_sdu_idx; - -private: + /* Prepares the PDU for parsing or writing by setting the number of subheaders to 0 and the pdu length */ - void init_(uint8_t *buffer_tx_ptr, uint32_t pdu_len_bytes, bool is_ulsch) { + virtual void init_(uint8_t *buffer_tx_ptr, uint32_t pdu_len_bytes, bool is_ulsch) { nof_subheaders = 0; pdu_len = pdu_len_bytes; rem_len = pdu_len; @@ -180,55 +180,80 @@ private: } }; + + +typedef enum { + SCH_SUBH_TYPE = 0, + MCH_SUBH_TYPE = 1, + RAR_SUBH_TYPE = 2 +} subh_type; + template class subh { -public: - +public: + subh(){} + virtual ~subh(){} + virtual bool read_subheader(uint8_t** ptr) = 0; virtual void read_payload(uint8_t **ptr) = 0; virtual void write_subheader(uint8_t** ptr, bool is_last) = 0; virtual void write_payload(uint8_t **ptr) = 0; virtual void fprint(FILE *stream) = 0; - + pdu* parent; -private: +private: virtual void init() = 0; }; - class sch_subh : public subh { +public: + sch_subh(subh_type type_ = SCH_SUBH_TYPE) { + lcid = 0; + nof_bytes = 0; + payload = NULL; + nof_mch_sched_ce = 0; + cur_mch_sched_ce = 0; + F_bit = false; + type = type_; + parent = NULL; + bzero(&w_payload_ce, sizeof(w_payload_ce)); + } -public: + virtual ~sch_subh(){} typedef enum { - PHR_REPORT = 26, - CRNTI = 27, - CON_RES_ID = 28, - TRUNC_BSR = 28, - TA_CMD = 29, - SHORT_BSR = 29, - DRX_CMD = 30, - LONG_BSR = 30, - PADDING = 31, - SDU = 0 - } cetype; - + PHR_REPORT = 26, + CRNTI = 27, + CON_RES_ID = 28, + MTCH_MAX_LCID = 28, + TRUNC_BSR = 28, + TA_CMD = 29, + SHORT_BSR = 29, + DRX_CMD = 30, + LONG_BSR = 30, + MCH_SCHED_INFO = 30, + PADDING = 31, + SDU = 0 + } cetype; + // Size of MAC CEs const static int MAC_CE_CONTRES_LEN = 6; // Reading functions bool is_sdu(); - cetype ce_type(); + bool is_var_len_ce(); + cetype ce_type(); uint32_t size_plus_header(); void set_payload_size(uint32_t size); - - bool read_subheader(uint8_t** ptr); - void read_payload(uint8_t **ptr); + + bool read_subheader(uint8_t** ptr); + void read_payload(uint8_t **ptr); + uint32_t get_sdu_lcid(); - int get_payload_size(); + uint32_t get_payload_size(); uint32_t get_header_size(bool is_last); uint8_t* get_sdu_ptr(); @@ -237,10 +262,13 @@ public: uint8_t get_ta_cmd(); float get_phr(); int get_bsr(uint32_t buff_size[4]); + + bool get_next_mch_sched_info(uint8_t *lcid, uint16_t *mtch_stop); // Writing functions - void write_subheader(uint8_t** ptr, bool is_last); - void write_payload(uint8_t **ptr); + void write_subheader(uint8_t** ptr, bool is_last); + void write_payload(uint8_t **ptr); + int set_sdu(uint32_t lcid, uint32_t nof_bytes, uint8_t *payload); int set_sdu(uint32_t lcid, uint32_t requested_bytes, read_pdu_interface *sdu_itf); bool set_c_rnti(uint16_t crnti); @@ -251,46 +279,62 @@ public: void set_padding(); void set_padding(uint32_t padding_len); - void init(); - void fprint(FILE *stream); + void init(); + void fprint(FILE *stream); + + bool set_next_mch_sched_info(uint8_t lcid, uint16_t mtch_stop); + +protected: + + static const int MAX_CE_PAYLOAD_LEN = 8; + uint32_t lcid; + int nof_bytes; + uint8_t* payload; + uint8_t w_payload_ce[64]; + uint8_t nof_mch_sched_ce; + uint8_t cur_mch_sched_ce; + bool F_bit; + subh_type type; private: - static const int MAX_CE_PAYLOAD_LEN = 8; - uint32_t lcid; - uint32_t nof_bytes; - uint8_t* payload; - uint8_t w_payload_ce[8]; - bool F_bit; uint32_t sizeof_ce(uint32_t lcid, bool is_ul); static uint8_t buff_size_table(uint32_t buffer_size); static uint8_t phr_report_table(float phr_value); }; + class sch_pdu : public pdu { public: + sch_pdu(uint32_t max_subh): pdu(max_subh) {} - sch_pdu(uint32_t max_subh) : pdu(max_subh) {} - void parse_packet(uint8_t *ptr); uint8_t* write_packet(); uint8_t* write_packet(srslte::log *log_h); - bool has_space_ce(uint32_t nbytes); + bool has_space_ce(uint32_t nbytes, bool var_len=false); bool has_space_sdu(uint32_t nbytes); int get_pdu_len(); int rem_size(); int get_sdu_space(); static uint32_t size_header_sdu(uint32_t nbytes); - bool update_space_ce(uint32_t nbytes); - bool update_space_sdu(uint32_t nbytes); + bool update_space_ce(uint32_t nbytes, bool var_len=false); + bool update_space_sdu(uint32_t nbytes); void fprint(FILE *stream); + }; class rar_subh : public subh { public: + rar_subh() { + bzero(&grant, sizeof(grant)); + ta = 0; + temp_rnti = 0; + preamble = 0; + parent = NULL; + } static const uint32_t RAR_GRANT_LEN = 20; @@ -333,11 +377,50 @@ public: bool write_packet(uint8_t* ptr); void fprint(FILE *stream); -private: +private: bool has_backoff_indicator; uint8_t backoff_indicator; + +}; + +class mch_subh : public sch_subh +{ +public: + mch_subh():sch_subh(MCH_SUBH_TYPE){} + + // Size of MAC CEs + const static int MAC_CE_CONTRES_LEN = 6; }; -} // namespace srslte +class mch_pdu : public sch_pdu +{ +public: + mch_pdu(uint32_t max_subh) : sch_pdu(max_subh) {} + +private: + + /* Prepares the PDU for parsing or writing by setting the number of subheaders to 0 and the pdu length */ + virtual void init_(uint8_t *buffer_tx_ptr, uint32_t pdu_len_bytes, bool is_ulsch) { + nof_subheaders = 0; + pdu_len = pdu_len_bytes; + rem_len = pdu_len; + pdu_is_ul = is_ulsch; + buffer_tx = buffer_tx_ptr; + sdu_offset_start = max_subheaders*2 + 13; // Assuming worst-case 2 bytes per sdu subheader + all possible CE + total_sdu_len = 0; + last_sdu_idx = -1; + reset(); + for (uint32_t i=0;i MAC shared between different RATs */ @@ -445,6 +476,9 @@ public: /* RRC configures a logical channel */ virtual void setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority, int PBR_x_tti, uint32_t BSD) = 0; + /* Instructs the MAC to start receiving an MCH */ + virtual void mch_start_rx(uint32_t lcid) = 0; + virtual uint32_t get_current_tti() = 0; virtual void set_config(mac_cfg_t *mac_cfg) = 0; @@ -466,8 +500,6 @@ public: }; - - /** PHY interface * */ @@ -540,12 +572,12 @@ public: class phy_interface_mac : public phy_interface_mac_common { public: + /* Configure PRACH using parameters written by RRC */ virtual void configure_prach_params() = 0; virtual void prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm) = 0; - virtual int prach_tx_tti() = 0; - + virtual int prach_tx_tti() = 0; /* Indicates the transmission of a SR signal in the next opportunity */ virtual void sr_send() = 0; virtual int sr_last_tx_tti() = 0; @@ -555,6 +587,9 @@ public: virtual void pdcch_dl_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1) = 0; virtual void pdcch_ul_search_reset() = 0; virtual void pdcch_dl_search_reset() = 0; + + virtual void set_mch_period_stop(uint32_t stop) = 0; + }; class phy_interface_rrc @@ -572,10 +607,19 @@ public: LIBLTE_RRC_TDD_CONFIG_STRUCT tdd_cnfg; LIBLTE_RRC_ANTENNA_PORTS_COUNT_ENUM ant_info; } phy_cfg_common_t; + + typedef struct { + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT mbsfn_subfr_cnfg; + LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT mbsfn_notification_cnfg; + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT mbsfn_area_info; + LIBLTE_RRC_MCCH_MSG_STRUCT mcch; + } phy_cfg_mbsfn_t; + typedef struct { LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT dedicated; phy_cfg_common_t common; + phy_cfg_mbsfn_t mbsfn; bool enable_64qam; } phy_cfg_t; @@ -589,6 +633,9 @@ public: virtual void set_config_common(phy_cfg_common_t *common) = 0; virtual void set_config_tdd(LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd) = 0; virtual void set_config_64qam_en(bool enable) = 0; + virtual void set_config_mbsfn_sib2(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2) = 0; + virtual void set_config_mbsfn_sib13(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13) = 0; + virtual void set_config_mbsfn_mcch(LIBLTE_RRC_MCCH_MSG_STRUCT *mcch) = 0; /* Measurements interface */ virtual void meas_reset() = 0; diff --git a/lib/include/srslte/phy/ch_estimation/refsignal_dl.h b/lib/include/srslte/phy/ch_estimation/refsignal_dl.h index 6167d8d4e..06809186e 100644 --- a/lib/include/srslte/phy/ch_estimation/refsignal_dl.h +++ b/lib/include/srslte/phy/ch_estimation/refsignal_dl.h @@ -93,8 +93,10 @@ SRSLTE_API uint32_t srslte_refsignal_cs_v(uint32_t port_id, SRSLTE_API uint32_t srslte_refsignal_cs_nof_symbols(uint32_t port_id); -SRSLTE_API int srslte_refsignal_mbsfn_init(srslte_refsignal_t *q, srslte_cell_t cell, - uint16_t mbsfn_area_id); +SRSLTE_API int srslte_refsignal_mbsfn_init(srslte_refsignal_t *q, uint32_t max_prb); + +SRSLTE_API int srslte_refsignal_mbsfn_set_cell(srslte_refsignal_t * q, + srslte_cell_t cell, uint16_t mbsfn_area_id); SRSLTE_API int srslte_refsignal_mbsfn_get_sf(srslte_cell_t cell, uint32_t port_id, diff --git a/lib/include/srslte/phy/dft/ofdm.h b/lib/include/srslte/phy/dft/ofdm.h index 6f31ed3ab..c28d41bdd 100644 --- a/lib/include/srslte/phy/dft/ofdm.h +++ b/lib/include/srslte/phy/dft/ofdm.h @@ -92,7 +92,7 @@ SRSLTE_API int srslte_ofdm_rx_init_mbsfn(srslte_ofdm_t *q, srslte_cp_t cp_type, cf_t *in_buffer, cf_t *out_buffer, - uint32_t nof_prb); + uint32_t max_prb); SRSLTE_API int srslte_ofdm_rx_init(srslte_ofdm_t *q, srslte_cp_t cp_type, diff --git a/lib/include/srslte/phy/enb/enb_dl.h b/lib/include/srslte/phy/enb/enb_dl.h index 2b11bd6e3..5e0d2bf5e 100644 --- a/lib/include/srslte/phy/enb/enb_dl.h +++ b/lib/include/srslte/phy/enb/enb_dl.h @@ -69,15 +69,20 @@ typedef struct SRSLTE_API { cf_t *slot1_symbols[SRSLTE_MAX_PORTS]; srslte_ofdm_t ifft[SRSLTE_MAX_PORTS]; + + srslte_ofdm_t ifft_mbsfn; srslte_pbch_t pbch; srslte_pcfich_t pcfich; srslte_regs_t regs; srslte_pdcch_t pdcch; srslte_pdsch_t pdsch; + srslte_pmch_t pmch; srslte_phich_t phich; srslte_refsignal_t csr_signal; - srslte_pdsch_cfg_t pdsch_cfg; + srslte_refsignal_t mbsfnr_signal; + srslte_pdsch_cfg_t pdsch_cfg; + srslte_pdsch_cfg_t pmch_cfg; srslte_ra_dl_dci_t dl_dci; srslte_dci_format_t dci_format; @@ -134,6 +139,8 @@ SRSLTE_API void srslte_enb_dl_prepare_power_allocation(srslte_enb_dl_t *q); SRSLTE_API void srslte_enb_dl_set_amp(srslte_enb_dl_t *q, float amp); +SRSLTE_API void srslte_enb_dl_set_non_mbsfn_region(srslte_enb_dl_t *q, uint8_t non_mbsfn_region); + SRSLTE_API void srslte_enb_dl_clear_sf(srslte_enb_dl_t *q); SRSLTE_API void srslte_enb_dl_put_sync(srslte_enb_dl_t *q, @@ -157,8 +164,13 @@ SRSLTE_API void srslte_enb_dl_put_phich(srslte_enb_dl_t *q, SRSLTE_API void srslte_enb_dl_put_base(srslte_enb_dl_t *q, uint32_t tti); +SRSLTE_API void srslte_enb_dl_put_mbsfn_base(srslte_enb_dl_t *q, + uint32_t tti); + SRSLTE_API void srslte_enb_dl_gen_signal(srslte_enb_dl_t *q); +SRSLTE_API void srslte_enb_dl_gen_signal_mbsfn(srslte_enb_dl_t *q); + SRSLTE_API int srslte_enb_dl_add_rnti(srslte_enb_dl_t *q, uint16_t rnti); @@ -174,6 +186,12 @@ SRSLTE_API int srslte_enb_dl_put_pdsch(srslte_enb_dl_t *q, uint8_t *data[SRSLTE_MAX_CODEWORDS], srslte_mimo_type_t mimo_type); +SRSLTE_API int srslte_enb_dl_put_pmch(srslte_enb_dl_t *q, + srslte_ra_dl_grant_t *grant, + srslte_softbuffer_tx_t *softbuffer, + uint32_t sf_idx, + uint8_t *data_mbms); + SRSLTE_API int srslte_enb_dl_put_pdcch_dl(srslte_enb_dl_t *q, srslte_ra_dl_dci_t *grant, srslte_dci_format_t format, diff --git a/lib/include/srslte/phy/phch/pmch.h b/lib/include/srslte/phy/phch/pmch.h index 7d352e62a..22f1458a4 100644 --- a/lib/include/srslte/phy/phch/pmch.h +++ b/lib/include/srslte/phy/phch/pmch.h @@ -94,7 +94,7 @@ SRSLTE_API int srslte_pmch_init_multi(srslte_pmch_t *q, SRSLTE_API void srslte_pmch_free(srslte_pmch_t *q); - +SRSLTE_API int srslte_pmch_set_cell(srslte_pmch_t *q, srslte_cell_t cell); SRSLTE_API int srslte_pmch_set_area_id(srslte_pmch_t *q, uint16_t area_id); diff --git a/lib/include/srslte/upper/pdcp.h b/lib/include/srslte/upper/pdcp.h index 436f35edd..d4cca957a 100644 --- a/lib/include/srslte/upper/pdcp.h +++ b/lib/include/srslte/upper/pdcp.h @@ -57,7 +57,9 @@ public: void reestablish(); void reset(); void write_sdu(uint32_t lcid, byte_buffer_t *sdu); + void write_sdu_mch(uint32_t lcid, byte_buffer_t *sdu); void add_bearer(uint32_t lcid, srslte_pdcp_config_t cnfg = srslte_pdcp_config_t()); + void add_bearer_mrb(uint32_t lcid, srslte_pdcp_config_t cnfg = srslte_pdcp_config_t()); void config_security(uint32_t lcid, uint8_t *k_enc, uint8_t *k_int, @@ -72,9 +74,11 @@ public: // RLC interface void write_pdu(uint32_t lcid, byte_buffer_t *sdu); + void write_pdu_mch(uint32_t lcid, byte_buffer_t *sdu); void write_pdu_bcch_bch(byte_buffer_t *sdu); void write_pdu_bcch_dlsch(byte_buffer_t *sdu); void write_pdu_pcch(byte_buffer_t *sdu); + private: srsue::rlc_interface_pdcp *rlc; @@ -83,10 +87,12 @@ private: log *pdcp_log; pdcp_entity pdcp_array[SRSLTE_N_RADIO_BEARERS]; + pdcp_entity pdcp_array_mrb[SRSLTE_N_MCH_LCIDS]; uint32_t lcid; // default LCID that is maintained active by PDCP instance uint8_t direction; bool valid_lcid(uint32_t lcid); + bool valid_mch_lcid(uint32_t lcid); }; } // namespace srslte diff --git a/lib/include/srslte/upper/rlc.h b/lib/include/srslte/upper/rlc.h index 05472e0e8..888dfa4f1 100644 --- a/lib/include/srslte/upper/rlc.h +++ b/lib/include/srslte/upper/rlc.h @@ -63,17 +63,21 @@ public: // PDCP interface void write_sdu(uint32_t lcid, byte_buffer_t *sdu); - + void write_sdu_mch(uint32_t lcid, byte_buffer_t *sdu); bool rb_is_um(uint32_t lcid); // MAC interface uint32_t get_buffer_state(uint32_t lcid); uint32_t get_total_buffer_state(uint32_t lcid); + uint32_t get_total_mch_buffer_state(uint32_t lcid); int read_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes); + int read_pdu_mch(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes); + int get_increment_sequence_num(); void write_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes); void write_pdu_bcch_bch(uint8_t *payload, uint32_t nof_bytes); void write_pdu_bcch_dlsch(uint8_t *payload, uint32_t nof_bytes); void write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes); + void write_pdu_mch(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes); // RRC interface void reestablish(); @@ -81,7 +85,8 @@ public: void empty_queue(); void add_bearer(uint32_t lcid); void add_bearer(uint32_t lcid, srslte_rlc_config_t cnfg); - + void add_bearer_mrb(uint32_t lcid); + void add_bearer_mrb_enb(uint32_t lcid); private: void reset_metrics(); @@ -92,13 +97,16 @@ private: srslte::mac_interface_timers *mac_timers; srsue::ue_interface *ue; srslte::rlc_entity rlc_array[SRSLTE_N_RADIO_BEARERS]; + srslte::rlc_um rlc_array_mrb[SRSLTE_N_MCH_LCIDS]; uint32_t default_lcid; long ul_tput_bytes[SRSLTE_N_RADIO_BEARERS]; long dl_tput_bytes[SRSLTE_N_RADIO_BEARERS]; + long dl_tput_bytes_mrb[SRSLTE_N_MCH_LCIDS]; struct timeval metrics_time[3]; bool valid_lcid(uint32_t lcid); + bool valid_lcid_mrb(uint32_t lcid); }; } // namespace srsue diff --git a/lib/include/srslte/upper/rlc_entity.h b/lib/include/srslte/upper/rlc_entity.h index cd30d5999..eca470709 100644 --- a/lib/include/srslte/upper/rlc_entity.h +++ b/lib/include/srslte/upper/rlc_entity.h @@ -47,12 +47,12 @@ class rlc_entity { public: rlc_entity(); - void init(rlc_mode_t mode, - log *rlc_entity_log_, - uint32_t lcid_, - srsue::pdcp_interface_rlc *pdcp_, - srsue::rrc_interface_rlc *rrc_, - mac_interface_timers *mac_timers_); + void init(rlc_mode_t mode, + log *rlc_entity_log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + mac_interface_timers *mac_timers_); void configure(srslte_rlc_config_t cnfg); void reset(); diff --git a/lib/include/srslte/upper/rlc_interface.h b/lib/include/srslte/upper/rlc_interface.h index a28d3d9bd..94fd2abcc 100644 --- a/lib/include/srslte/upper/rlc_interface.h +++ b/lib/include/srslte/upper/rlc_interface.h @@ -73,6 +73,7 @@ typedef struct { uint32_t rx_window_size; uint32_t rx_mod; // Rx counter modulus uint32_t tx_mod; // Tx counter modulus + bool is_mrb; // Whether this is a multicast bearer } srslte_rlc_um_config_t; @@ -122,6 +123,21 @@ public: break; } } + + // Factory for MCH + static srslte_rlc_config_t mch_config() + { + srslte_rlc_config_t cfg; + cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_UNI_DL; + cfg.um.t_reordering = 0; + cfg.um.rx_sn_field_length = RLC_UMD_SN_SIZE_5_BITS; + cfg.um.rx_window_size = 0; + cfg.um.rx_mod = 32; + cfg.um.tx_sn_field_length = RLC_UMD_SN_SIZE_5_BITS; + cfg.um.tx_mod = 32; + cfg.um.is_mrb = true; + return cfg; + } }; } // namespace srslte diff --git a/lib/include/srslte/upper/rlc_tm.h b/lib/include/srslte/upper/rlc_tm.h index d3a8aa1ae..ecb79e653 100644 --- a/lib/include/srslte/upper/rlc_tm.h +++ b/lib/include/srslte/upper/rlc_tm.h @@ -41,11 +41,11 @@ class rlc_tm { public: rlc_tm(); - void init(log *rlc_entity_log_, - uint32_t lcid_, - srsue::pdcp_interface_rlc *pdcp_, - srsue::rrc_interface_rlc *rrc_, - mac_interface_timers *mac_timers); + void init(log *rlc_entity_log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + mac_interface_timers *mac_timers); void configure(srslte_rlc_config_t cnfg); void reset(); void stop(); diff --git a/lib/include/srslte/upper/rlc_tx_queue.h b/lib/include/srslte/upper/rlc_tx_queue.h index 3913670e8..08edae0fe 100644 --- a/lib/include/srslte/upper/rlc_tx_queue.h +++ b/lib/include/srslte/upper/rlc_tx_queue.h @@ -75,6 +75,10 @@ public: return queue.try_pop(msg); } + void resize(uint32_t capacity) + { + queue.resize(capacity); + } uint32_t size() { return (uint32_t) queue.size(); @@ -102,10 +106,10 @@ public: } private: - bool is_empty() { return queue.empty(); } + bool is_empty() { return queue.empty(); } block_queue queue; - uint32_t unread_bytes; + uint32_t unread_bytes; }; } // namespace srslte diff --git a/lib/include/srslte/upper/rlc_um.h b/lib/include/srslte/upper/rlc_um.h index d41192d6d..0cd4b8c95 100644 --- a/lib/include/srslte/upper/rlc_um.h +++ b/lib/include/srslte/upper/rlc_um.h @@ -50,17 +50,18 @@ class rlc_um { public: rlc_um(); - ~rlc_um(); - void init(log *rlc_entity_log_, - uint32_t lcid_, - srsue::pdcp_interface_rlc *pdcp_, - srsue::rrc_interface_rlc *rrc_, - mac_interface_timers *mac_timers_); + ~rlc_um(); + void init(log *rlc_entity_log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + mac_interface_timers *mac_timers_); void configure(srslte_rlc_config_t cnfg); void reset(); void stop(); void empty_queue(); + bool is_mrb(); rlc_mode_t get_mode(); uint32_t get_bearer(); @@ -73,7 +74,7 @@ public: uint32_t get_total_buffer_state(); int read_pdu(uint8_t *payload, uint32_t nof_bytes); void write_pdu(uint8_t *payload, uint32_t nof_bytes); - + int get_increment_sequence_num(); // Timeout callback interface void timer_expired(uint32_t timeout_id); @@ -86,11 +87,12 @@ private: uint32_t lcid; srsue::pdcp_interface_rlc *pdcp; srsue::rrc_interface_rlc *rrc; - mac_interface_timers *mac_timers; + mac_interface_timers *mac_timers; // TX SDU buffers rlc_tx_queue tx_sdu_queue; byte_buffer_t *tx_sdu; + byte_buffer_t tx_sdu_temp; // Rx window std::map rx_window; @@ -136,6 +138,8 @@ private: void reassemble_rx_sdus(); bool inside_reordering_window(uint16_t sn); void debug_state(); + + std::string rb_name(); }; /**************************************************************************** diff --git a/lib/src/asn1/liblte_rrc.cc b/lib/src/asn1/liblte_rrc.cc index 8e489cd9f..eb264a0ec 100644 --- a/lib/src/asn1/liblte_rrc.cc +++ b/lib/src/asn1/liblte_rrc.cc @@ -327,13 +327,218 @@ LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_subframe_config_ie(uint8 } /********************************************************************* - IE Name: PMCH Info List + IE Name: TMGI - Description: Specifies configuration of all PMCHs of an MBSFN area + Description: Temporary Mobile Group Identity (PLMN + MBMS service ID) Document Reference: 36.331 v10.0.0 Section 6.3.7 *********************************************************************/ -// FIXME +LIBLTE_ERROR_ENUM liblte_rrc_pack_tmgi_r9_ie(LIBLTE_RRC_TMGI_R9_STRUCT *tmgi, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(tmgi != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(tmgi->plmn_id_explicit?1:0, ie_ptr, 1); + if(tmgi->plmn_id_explicit){ + liblte_rrc_pack_plmn_identity_ie(&tmgi->plmn_id_r9, ie_ptr); + }else{ + liblte_value_2_bits(tmgi->plmn_index_r9-1, ie_ptr, 3); + } + liblte_value_2_bits(tmgi->serviceid_r9, ie_ptr, 24); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_tmgi_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_TMGI_R9_STRUCT *tmgi) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + tmgi != NULL) + { + tmgi->plmn_id_explicit = liblte_bits_2_value(ie_ptr, 1); + if(tmgi->plmn_id_explicit){ + liblte_rrc_unpack_plmn_identity_ie(ie_ptr, &tmgi->plmn_id_r9); + }else{ + tmgi->plmn_index_r9 = liblte_bits_2_value(ie_ptr, 3) + 1; + } + tmgi->serviceid_r9 = liblte_bits_2_value(ie_ptr, 24); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: MBMS Session Info + + Description: Information about an individual MBMS session + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbms_session_info_r9_ie(LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *mbms_session_info, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(mbms_session_info != NULL && + ie_ptr != NULL) + { + // ext + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(mbms_session_info->sessionid_r9_present?1:0, ie_ptr, 1); + liblte_rrc_pack_tmgi_r9_ie(&mbms_session_info->tmgi_r9, ie_ptr); + if(mbms_session_info->sessionid_r9_present){ + liblte_value_2_bits(mbms_session_info->sessionid_r9, ie_ptr, 8); + } + liblte_value_2_bits(mbms_session_info->logicalchannelid_r9, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbms_session_info_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *mbms_session_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + mbms_session_info != NULL) + { + // ext + bool ext = liblte_bits_2_value(ie_ptr, 1); + + mbms_session_info->sessionid_r9_present = liblte_bits_2_value(ie_ptr, 1); + liblte_rrc_unpack_tmgi_r9_ie(ie_ptr, &mbms_session_info->tmgi_r9); + if(mbms_session_info->sessionid_r9_present){ + mbms_session_info->sessionid_r9 = liblte_bits_2_value(ie_ptr, 8); + } + mbms_session_info->logicalchannelid_r9 = liblte_bits_2_value(ie_ptr, 5); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PMCH Config + + Description: Contains configuration parameters of the sessions + carried by a PMCH + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_pmch_config_r9_ie(LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT *pmch_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(pmch_cnfg != NULL && + ie_ptr != NULL) + { + // ext + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(pmch_cnfg->sf_alloc_end_r9, ie_ptr, 11); + liblte_value_2_bits(pmch_cnfg->datamcs_r9, ie_ptr, 5); + liblte_value_2_bits(pmch_cnfg->mch_schedulingperiod_r9, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pmch_config_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT *pmch_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + pmch_cnfg != NULL) + { + // ext + bool ext = liblte_bits_2_value(ie_ptr, 1); + + pmch_cnfg->sf_alloc_end_r9 = liblte_bits_2_value(ie_ptr, 11); + pmch_cnfg->datamcs_r9 = liblte_bits_2_value(ie_ptr, 5); + pmch_cnfg->mch_schedulingperiod_r9 = (LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_ENUM)liblte_bits_2_value(ie_ptr, 3); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + + +/********************************************************************* + IE Name: PMCH Info + + Description: Specifies configuration of PMCH of an MBSFN area + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_pmch_info_r9_ie(LIBLTE_RRC_PMCH_INFO_R9_STRUCT *pmch_info, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(pmch_info != NULL && + ie_ptr != NULL) + { + // ext + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_rrc_pack_pmch_config_r9_ie(&pmch_info->pmch_config_r9, ie_ptr); + liblte_value_2_bits(pmch_info->mbms_sessioninfolist_r9_size, ie_ptr, 5); + for(i=0; imbms_sessioninfolist_r9_size; i++){ + liblte_rrc_pack_mbms_session_info_r9_ie(&pmch_info->mbms_sessioninfolist_r9[i], ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pmch_info_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_PMCH_INFO_R9_STRUCT *pmch_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + pmch_info != NULL) + { + // ext + bool ext = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_pmch_config_r9_ie(ie_ptr, &pmch_info->pmch_config_r9); + pmch_info->mbms_sessioninfolist_r9_size = liblte_bits_2_value(ie_ptr, 5); + for(i=0; imbms_sessioninfolist_r9_size; i++){ + liblte_rrc_unpack_mbms_session_info_r9_ie(ie_ptr, &pmch_info->mbms_sessioninfolist_r9[i]); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} /********************************************************************* IE Name: C-RNTI @@ -8896,7 +9101,7 @@ LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_2_ie(LIBLTE_RRC_SYS_INFO_B liblte_value_2_bits(sib2->mbsfn_subfr_cnfg_list_size - 1, ie_ptr, 3); for(i=0; imbsfn_subfr_cnfg_list_size; i++) { - liblte_rrc_pack_mbsfn_subframe_config_ie(&sib2->mbsfn_subfr_cnfg[i], ie_ptr); + liblte_rrc_pack_mbsfn_subframe_config_ie(&sib2->mbsfn_subfr_cnfg_list[i], ie_ptr); } } @@ -8994,7 +9199,7 @@ LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_2_ie(uint8 sib2->mbsfn_subfr_cnfg_list_size = liblte_bits_2_value(ie_ptr, 3) + 1; for(i=0; imbsfn_subfr_cnfg_list_size; i++) { - liblte_rrc_unpack_mbsfn_subframe_config_ie(ie_ptr, &sib2->mbsfn_subfr_cnfg[i]); + liblte_rrc_unpack_mbsfn_subframe_config_ie(ie_ptr, &sib2->mbsfn_subfr_cnfg_list[i]); } }else{ sib2->mbsfn_subfr_cnfg_list_size = 0; @@ -10288,7 +10493,7 @@ LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_13_ie(LIBLTE_RRC_SYS_INFO_ { liblte_rrc_pack_mbsfn_area_info_ie(&sib13->mbsfn_area_info_list_r9[i], ie_ptr); } - liblte_rrc_pack_mbsfn_notification_config_ie(&sib13->mbms_notification_config, ie_ptr); + liblte_rrc_pack_mbsfn_notification_config_ie(&sib13->mbsfn_notification_config, ie_ptr); err = LIBLTE_SUCCESS; } @@ -10317,7 +10522,7 @@ LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_13_ie(uint8 { liblte_rrc_unpack_mbsfn_area_info_ie(ie_ptr, &sib13->mbsfn_area_info_list_r9[i]); } - liblte_rrc_unpack_mbsfn_notification_config_ie(ie_ptr, &sib13->mbms_notification_config); + liblte_rrc_unpack_mbsfn_notification_config_ie(ie_ptr, &sib13->mbsfn_notification_config); liblte_rrc_consume_noncrit_extension(ext_ind, __func__, ie_ptr); @@ -13001,9 +13206,82 @@ LIBLTE_ERROR_ENUM liblte_rrc_unpack_measurement_report_msg(LIBLTE_BIT_MSG_STRUCT Description: Contains the MBMS control information applicable for an MBSFN area - Document Reference: 36.331 v10.0.0 Section 6.2.2 + Document Reference: 36.331 v10.0.0 Section 6.2.2 *********************************************************************/ -// FIXME +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbsfn_area_configuration_r9_msg(LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT *mbsfn_area_cnfg, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint32 i; + + if(mbsfn_area_cnfg != NULL && + msg != NULL) + { + // Non-critical extension + liblte_value_2_bits(0, &msg_ptr, 1); + + // commonsf_allocpatternlist_r9 + liblte_value_2_bits(mbsfn_area_cnfg->commonsf_allocpatternlist_r9_size-1, &msg_ptr, 3); + for(i=0; icommonsf_allocpatternlist_r9_size; i++){ + liblte_rrc_pack_mbsfn_subframe_config_ie(&mbsfn_area_cnfg->commonsf_allocpatternlist_r9[i], &msg_ptr); + } + + // commonsf_allocperiod_r9 + liblte_value_2_bits(mbsfn_area_cnfg->commonsf_allocperiod_r9, &msg_ptr, 3); + + // pmch_infolist_r9 + liblte_value_2_bits(mbsfn_area_cnfg->pmch_infolist_r9_size, &msg_ptr, 4); + for(i=0; ipmch_infolist_r9_size; i++){ + liblte_rrc_pack_pmch_info_r9_ie(&mbsfn_area_cnfg->pmch_infolist_r9[i], &msg_ptr); + } + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_area_configuration_r9_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT *mbsfn_area_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint32 i; + bool ext; + + if(msg != NULL && + mbsfn_area_cnfg != NULL) + { + // Non-critical extension + ext = liblte_bits_2_value(&msg_ptr, 1); + liblte_rrc_warning_not_handled(ext, __func__); + + // commonsf_allocpatternlist_r9 + mbsfn_area_cnfg->commonsf_allocpatternlist_r9_size = liblte_bits_2_value(&msg_ptr, 3) + 1; + for(i=0; icommonsf_allocpatternlist_r9_size; i++){ + liblte_rrc_unpack_mbsfn_subframe_config_ie(&msg_ptr, &mbsfn_area_cnfg->commonsf_allocpatternlist_r9[i]); + } + + // commonsf_allocperiod_r9 + mbsfn_area_cnfg->commonsf_allocperiod_r9 = (LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_ENUM)liblte_bits_2_value(&msg_ptr, 3); + + // pmch_infolist_r9 + mbsfn_area_cnfg->pmch_infolist_r9_size = liblte_bits_2_value(&msg_ptr, 4); + for(i=0; ipmch_infolist_r9_size; i++){ + liblte_rrc_unpack_pmch_info_r9_ie(&msg_ptr, &mbsfn_area_cnfg->pmch_infolist_r9[i]); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + /********************************************************************* Message Name: Master Information Block @@ -13104,7 +13382,7 @@ LIBLTE_ERROR_ENUM liblte_rrc_unpack_dl_information_transfer_msg(LIBLTE_BIT_MSG_S liblte_bits_2_value(&msg_ptr, 2); // Optional indicator - liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__);; + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); // Dedicated info type choice dl_info_transfer->dedicated_info_type = (LIBLTE_RRC_DL_INFORMATION_TRANSFER_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 2); @@ -13373,6 +13651,66 @@ LIBLTE_ERROR_ENUM liblte_rrc_unpack_bcch_dlsch_msg(LIBLTE_BIT_MSG_STRUCT return(err); } +/********************************************************************* + Message Name: MCCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE on the MCCH logical + channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mcch_msg(LIBLTE_RRC_MCCH_MSG_STRUCT *mcch_msg, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 ext = false; + + if(mcch_msg != NULL && + msg != NULL) + { + // MCCH choice + liblte_value_2_bits(0, &msg_ptr, 1); + + err = liblte_rrc_pack_mbsfn_area_configuration_r9_msg(mcch_msg, + &global_msg); + if(global_msg.N_bits <= (LIBLTE_MAX_MSG_SIZE_BITS - 1)) + { + memcpy(msg_ptr, global_msg.msg, global_msg.N_bits); + msg->N_bits = global_msg.N_bits + 1; + }else{ + msg->N_bits = 0; + err = LIBLTE_ERROR_INVALID_INPUTS; + } + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mcch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_MCCH_MSG_STRUCT *mcch_msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint32 N_bits_used; + + if(msg != NULL && + mcch_msg != NULL) + { + // MCCH choice + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__);; + + if((msg->N_bits-(msg_ptr-msg->msg)) <= (LIBLTE_MAX_MSG_SIZE_BITS - 1)) + { + memcpy(global_msg.msg, msg_ptr, msg->N_bits-(msg_ptr-msg->msg)); + err = liblte_rrc_unpack_mbsfn_area_configuration_r9_msg(&global_msg, + mcch_msg); + } + } + + return(err); +} + /********************************************************************* Message Name: PCCH Message diff --git a/lib/src/common/gen_mch_tables.c b/lib/src/common/gen_mch_tables.c new file mode 100644 index 000000000..510bf2658 --- /dev/null +++ b/lib/src/common/gen_mch_tables.c @@ -0,0 +1,62 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE 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 Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + + +#include "srslte/common/gen_mch_tables.h" + + +/****************************************************************************** + * Key Generation + *****************************************************************************/ + +void generate_frame_table(uint8_t *table, uint8_t alloc) +{ + table[1] = (alloc >> 5) & 0x01; + table[2] = (alloc >> 4) & 0x01; + table[3] = (alloc >> 3) & 0x01; + table[6] = (alloc >> 2) & 0x01; + table[7] = (alloc >> 1) & 0x01; + table[8] = (alloc >> 0) & 0x01; +} + +void generate_mch_table(uint8_t *table, uint32_t sf_alloc, uint8_t num_frames) +{ + if(num_frames == 1){ + uint8_t alloc = (sf_alloc) & 0x3F; + generate_frame_table(table, alloc); + } else if(num_frames == 4){ + for(uint32_t j=0; j<4; j++){ + uint8_t alloc = (sf_alloc >> 6*(3-j)) & 0x3F; + generate_frame_table(&table[j*10], alloc); + } + } +} + +void generate_mcch_table(uint8_t *table, uint32_t sf_alloc) +{ + uint8_t alloc = (sf_alloc) & 0x3F; + generate_frame_table(table, alloc); +} diff --git a/lib/src/common/mac_pcap.cc b/lib/src/common/mac_pcap.cc index caca254a3..1e378b9e2 100644 --- a/lib/src/common/mac_pcap.cc +++ b/lib/src/common/mac_pcap.cc @@ -94,6 +94,10 @@ void mac_pcap::write_dl_pch(uint8_t* pdu, uint32_t pdu_len_bytes, bool crc_ok, u { pack_and_write(pdu, pdu_len_bytes, 0, crc_ok, tti, SRSLTE_PRNTI, DIRECTION_DOWNLINK, P_RNTI); } +void mac_pcap::write_dl_mch(uint8_t* pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti) +{ + pack_and_write(pdu, pdu_len_bytes, 0, crc_ok, tti, SRSLTE_MRNTI, DIRECTION_DOWNLINK, M_RNTI); +} void mac_pcap::write_dl_sirnti(uint8_t* pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti) { pack_and_write(pdu, pdu_len_bytes, 0, crc_ok, tti, SRSLTE_SIRNTI, DIRECTION_DOWNLINK, SI_RNTI); diff --git a/lib/src/common/pdu.cc b/lib/src/common/pdu.cc index 09fae2774..d36b67f9f 100644 --- a/lib/src/common/pdu.cc +++ b/lib/src/common/pdu.cc @@ -40,60 +40,15 @@ static uint32_t btable[64] = { namespace srslte { - - + void sch_pdu::fprint(FILE* stream) { fprintf(stream, "MAC SDU for UL/DL-SCH. "); pdu::fprint(stream); } -void sch_subh::fprint(FILE* stream) -{ - if (is_sdu()) { - fprintf(stream, "SDU LCHID=%d, SDU nof_bytes=%d\n", lcid, nof_bytes); - } else { - if (parent->is_ul()) { - switch(lcid) { - case CRNTI: - fprintf(stream, "C-RNTI CE\n"); - break; - case PHR_REPORT: - fprintf(stream, "PHR\n"); - break; - case TRUNC_BSR: - fprintf(stream, "Truncated BSR CE\n"); - break; - case SHORT_BSR: - fprintf(stream, "Short BSR CE\n"); - break; - case LONG_BSR: - fprintf(stream, "Long BSR CE\n"); - break; - case PADDING: - fprintf(stream, "PADDING\n"); - } - } else { - switch(lcid) { - case CON_RES_ID: - fprintf(stream, "Contention Resolution ID CE: 0x%lx\n", get_con_res_id()); - break; - case TA_CMD: - fprintf(stream, "Time Advance Command CE: %d\n", get_ta_cmd()); - break; - case DRX_CMD: - fprintf(stream, "DRX Command CE: Not implemented\n"); - break; - case PADDING: - fprintf(stream, "PADDING\n"); - } - } - } -} - void sch_pdu::parse_packet(uint8_t *ptr) { - pdu::parse_packet(ptr); // Correct size for last SDU @@ -157,7 +112,8 @@ uint8_t* sch_pdu::write_packet(srslte::log *log_h) onetwo_padding = rem_len; rem_len = 0; } - + + /* Determine the header size and CE payload size */ uint32_t header_sz = 0; uint32_t ce_payload_sz = 0; @@ -173,12 +129,12 @@ uint8_t* sch_pdu::write_packet(srslte::log *log_h) header_sz += onetwo_padding; } if (ce_payload_sz + header_sz >= sdu_offset_start) { - fprintf(stderr, "Writting PDU: header sz + ce_payload_sz >= sdu_offset_start (%d>=%d). pdu_len=%d, total_sdu_len=%d\n", + fprintf(stderr, "Writing PDU: header sz + ce_payload_sz >= sdu_offset_start (%d>=%d). pdu_len=%d, total_sdu_len=%d\n", header_sz + ce_payload_sz, sdu_offset_start, pdu_len, total_sdu_len); return NULL; } - /* Start writting header and CE payload before the start of the SDU payload */ + /* Start writing header and CE payload before the start of the SDU payload */ uint8_t *ptr = &buffer_tx[sdu_offset_start-header_sz-ce_payload_sz]; uint8_t *pdu_start_ptr = ptr; @@ -213,7 +169,6 @@ uint8_t* sch_pdu::write_packet(srslte::log *log_h) subheaders[i].write_payload(&ptr); } } - // Set padding to zeros (if any) if (rem_len > 0) { bzero(&pdu_start_ptr[pdu_len-rem_len], rem_len*sizeof(uint8_t)); @@ -277,19 +232,22 @@ uint32_t sch_pdu::size_header_sdu(uint32_t nbytes) return 3; } } -bool sch_pdu::has_space_ce(uint32_t nbytes) + +bool sch_pdu::has_space_ce(uint32_t nbytes, bool var_len) { - if (rem_len >= nbytes + 1) { + uint32_t head_len = var_len ? size_header_sdu(nbytes) : 1; + if (rem_len >= nbytes + head_len) { return true; } else { return false; } } -bool sch_pdu::update_space_ce(uint32_t nbytes) +bool sch_pdu::update_space_ce(uint32_t nbytes, bool var_len) { + uint32_t head_len = var_len ? size_header_sdu(nbytes) : 1; if (has_space_ce(nbytes)) { - rem_len -= nbytes + 1; + rem_len -= nbytes + head_len; return true; } else { return false; @@ -336,18 +294,22 @@ int sch_pdu::get_sdu_space() void sch_subh::init() { - lcid = 0; - nof_bytes = 0; - payload = NULL; + lcid = 0; + nof_bytes = 0; + payload = NULL; + nof_mch_sched_ce = 0; + cur_mch_sched_ce = 0; } sch_subh::cetype sch_subh::ce_type() { - if (lcid >= PHR_REPORT) { - return (cetype) lcid; - } else { - return SDU; + if (lcid >= PHR_REPORT && type == SCH_SUBH_TYPE) { + return (cetype)lcid; + } + if(lcid >= MCH_SCHED_INFO && type == MCH_SUBH_TYPE) { + return (cetype)lcid; } + return (cetype)SDU; } void sch_subh::set_payload_size(uint32_t size) { @@ -355,48 +317,65 @@ void sch_subh::set_payload_size(uint32_t size) { } uint32_t sch_subh::size_plus_header() { - if (is_sdu()) { + if (is_sdu() || is_var_len_ce()) { return sch_pdu::size_header_sdu(nof_bytes) + nof_bytes; - } else { - return nof_bytes + 1; } + // All others are 1-byte headers + return 1 + nof_bytes; } uint32_t sch_subh::sizeof_ce(uint32_t lcid, bool is_ul) { - if (is_ul) { - switch(lcid) { - case PHR_REPORT: - return 1; - case CRNTI: - return 2; - case TRUNC_BSR: - return 1; - case SHORT_BSR: - return 1; - case LONG_BSR: - return 3; - case PADDING: - return 0; - } - } else { - switch(lcid) { - case CON_RES_ID: - return 6; - case TA_CMD: - return 1; - case DRX_CMD: - return 0; - case PADDING: - return 0; - } + if (type == SCH_SUBH_TYPE) { + if (is_ul) { + switch(lcid) { + case PHR_REPORT: + return 1; + case CRNTI: + return 2; + case TRUNC_BSR: + return 1; + case SHORT_BSR: + return 1; + case LONG_BSR: + return 3; + case PADDING: + return 0; + } + } else { + switch(lcid) { + case CON_RES_ID: + return 6; + case TA_CMD: + return 1; + case DRX_CMD: + return 0; + case PADDING: + return 0; + } + } + } + if (type == MCH_SUBH_TYPE) { + switch (lcid) { + case MCH_SCHED_INFO: + return nof_mch_sched_ce*2; + case PADDING: + return 0; + } } return 0; } + bool sch_subh::is_sdu() { return ce_type() == SDU; } + +bool sch_subh::is_var_len_ce() +{ + return (MCH_SCHED_INFO == ce_type()) && (MCH_SUBH_TYPE == type); +} + uint16_t sch_subh::get_c_rnti() { if (payload) { @@ -405,6 +384,7 @@ uint16_t sch_subh::get_c_rnti() return (uint16_t) w_payload_ce[0]<<8 | w_payload_ce[1]; } } + uint64_t sch_subh::get_con_res_id() { if (payload) { @@ -416,6 +396,7 @@ uint64_t sch_subh::get_con_res_id() return 0; } } + float sch_subh::get_phr() { if (payload) { @@ -453,6 +434,21 @@ int sch_subh::get_bsr(uint32_t buff_size[4]) } } +bool sch_subh::get_next_mch_sched_info(uint8_t *lcid_, uint16_t *mtch_stop) +{ + if(payload) { + nof_mch_sched_ce = nof_bytes/2; + if(cur_mch_sched_ce < nof_mch_sched_ce) { + *lcid_ = (payload[cur_mch_sched_ce*2]&0xF8) >> 3; + *mtch_stop = ((uint16_t)(payload[cur_mch_sched_ce*2]&0x07)) << 8; + *mtch_stop += payload[cur_mch_sched_ce*2+1]; + cur_mch_sched_ce++; + return true; + } + } + return false; +} + uint8_t sch_subh::get_ta_cmd() { if (payload) { @@ -461,42 +457,49 @@ uint8_t sch_subh::get_ta_cmd() return 0; } } + uint32_t sch_subh::get_sdu_lcid() { return lcid; } -int sch_subh::get_payload_size() + + +uint32_t sch_subh::get_payload_size() + { return nof_bytes; } + uint32_t sch_subh::get_header_size(bool is_last) { if (!is_last) { - // For all subheaders, size can be 1, 2 or 3 bytes if (is_sdu()) { - return sch_pdu::size_header_sdu(get_payload_size()); - } else { - return 1; + return sch_pdu::size_header_sdu(nof_bytes); + } + if (lcid == MCH_SCHED_INFO && type == MCH_SUBH_TYPE) { + return sch_pdu::size_header_sdu(nof_bytes); } + return 1; // All others are 1-byte } else { - // Last subheader (CE or SDU) has always 1 byte header - return 1; + return 1; // Last subheader (CE or SDU) has always 1 byte header } } + uint8_t* sch_subh::get_sdu_ptr() { return payload; } + void sch_subh::set_padding(uint32_t padding_len) { lcid = PADDING; nof_bytes = padding_len; } + void sch_subh::set_padding() { set_padding(0); } - bool sch_subh::set_bsr(uint32_t buff_size[4], sch_subh::cetype format) { uint32_t nonzero_lcg=0; @@ -579,6 +582,20 @@ bool sch_subh::set_ta_cmd(uint8_t ta_cmd) } } +bool sch_subh::set_next_mch_sched_info(uint8_t lcid_, uint16_t mtch_stop) +{ + if (((sch_pdu*)parent)->has_space_ce(2, true)) { + w_payload_ce[nof_mch_sched_ce*2] = (lcid_&0x1F) << 3 | (uint8_t) ((mtch_stop&0x0700)>>8); + w_payload_ce[nof_mch_sched_ce*2+1] = (uint8_t) (mtch_stop&0xff); + nof_mch_sched_ce++; + lcid = MCH_SCHED_INFO; + ((sch_pdu*)parent)->update_space_ce(2, true); + nof_bytes += 2; + return true; + } + return false; +} + int sch_subh::set_sdu(uint32_t lcid_, uint32_t requested_bytes, read_pdu_interface *sdu_itf) { if (((sch_pdu*)parent)->has_space_sdu(requested_bytes)) { @@ -598,7 +615,7 @@ int sch_subh::set_sdu(uint32_t lcid_, uint32_t requested_bytes, read_pdu_interfa // Save final number of written bytes nof_bytes = sdu_sz; - if(nof_bytes > requested_bytes) { + if(nof_bytes > (int32_t)requested_bytes) { return -1; } } @@ -634,7 +651,7 @@ void sch_subh::write_subheader(uint8_t** ptr, bool is_last) { *(*ptr) = (uint8_t) (is_last?0:(1<<5)) | ((uint8_t) lcid & 0x1f); *ptr += 1; - if (is_sdu()) { + if (is_sdu() || is_var_len_ce()) { // MAC SDU: R/R/E/LCID/F/L subheader // 2nd and 3rd octet if (!is_last) { @@ -656,8 +673,8 @@ void sch_subh::write_payload(uint8_t** ptr) if (is_sdu()) { // SDU is written directly during subheader creation } else { - nof_bytes = sizeof_ce(lcid, parent->is_ul()); - memcpy(*ptr, w_payload_ce, nof_bytes*sizeof(uint8_t)); + nof_bytes = sizeof_ce(lcid, parent->is_ul()); + memcpy(*ptr, w_payload_ce, nof_bytes*sizeof(uint8_t)); } *ptr += nof_bytes; } @@ -668,7 +685,7 @@ bool sch_subh::read_subheader(uint8_t** ptr) bool e_bit = (bool) (*(*ptr) & 0x20)?true:false; lcid = (uint8_t) *(*ptr) & 0x1f; *ptr += 1; - if (is_sdu()) { + if (is_sdu() || is_var_len_ce()) { if (e_bit) { F_bit = (bool) (*(*ptr) & 0x80)?true:false; nof_bytes = (uint32_t)*(*ptr) & 0x7f; @@ -686,12 +703,64 @@ bool sch_subh::read_subheader(uint8_t** ptr) } return e_bit; } + void sch_subh::read_payload(uint8_t** ptr) { payload = *ptr; *ptr += nof_bytes; } +void sch_subh::fprint(FILE* stream) +{ + if (is_sdu()) { + fprintf(stream, "SDU LCHID=%d, SDU nof_bytes=%d\n", lcid, nof_bytes); + } else if (type == SCH_SUBH_TYPE) { + if (parent->is_ul()) { + switch(lcid) { + case CRNTI: + fprintf(stream, "C-RNTI CE\n"); + break; + case PHR_REPORT: + fprintf(stream, "PHR\n"); + break; + case TRUNC_BSR: + fprintf(stream, "Truncated BSR CE\n"); + break; + case SHORT_BSR: + fprintf(stream, "Short BSR CE\n"); + break; + case LONG_BSR: + fprintf(stream, "Long BSR CE\n"); + break; + case PADDING: + fprintf(stream, "PADDING\n"); + } + } else { + switch(lcid) { + case CON_RES_ID: + fprintf(stream, "Contention Resolution ID CE: 0x%lx\n", get_con_res_id()); + break; + case TA_CMD: + fprintf(stream, "Time Advance Command CE: %d\n", get_ta_cmd()); + break; + case DRX_CMD: + fprintf(stream, "DRX Command CE: Not implemented\n"); + break; + case PADDING: + fprintf(stream, "PADDING\n"); + } + } + } else if (type == MCH_SUBH_TYPE) { + switch(lcid) { + case MCH_SCHED_INFO: + fprintf(stream, "MCH Scheduling Info CE\n"); + break; + case PADDING: + fprintf(stream, "PADDING\n"); + } + } +} + uint8_t sch_subh::buff_size_table(uint32_t buffer_size) { if (buffer_size == 0) { return 0; @@ -719,12 +788,6 @@ uint8_t sch_subh::phr_report_table(float phr_value) return (uint8_t) floor(phr_value+23); } - - - - - - void rar_pdu::fprint(FILE* stream) { fprintf(stream, "MAC PDU for RAR. "); @@ -734,26 +797,22 @@ void rar_pdu::fprint(FILE* stream) pdu::fprint(stream); } - -void rar_subh::fprint(FILE* stream) -{ - fprintf(stream, "RAPID: %d, Temp C-RNTI: %d, TA: %d, UL Grant: ", preamble, temp_rnti, ta); - srslte_vec_fprint_hex(stream, grant, 20); -} - rar_pdu::rar_pdu(uint32_t max_rars_) : pdu(max_rars_) { backoff_indicator = 0; has_backoff_indicator = false; } + uint8_t rar_pdu::get_backoff() { return backoff_indicator; } + bool rar_pdu::has_backoff() { return has_backoff_indicator; } + void rar_pdu::set_backoff(uint8_t bi) { has_backoff_indicator = true; @@ -786,7 +845,11 @@ bool rar_pdu::write_packet(uint8_t* ptr) return true; } - +void rar_subh::fprint(FILE* stream) +{ + fprintf(stream, "RAPID: %d, Temp C-RNTI: %d, TA: %d, UL Grant: ", preamble, temp_rnti, ta); + srslte_vec_fprint_hex(stream, grant, 20); +} void rar_subh::init() { @@ -794,38 +857,47 @@ void rar_subh::init() ta = 0; temp_rnti = 0; } + uint32_t rar_subh::get_rapid() { return preamble; } + void rar_subh::get_sched_grant(uint8_t grant_[RAR_GRANT_LEN]) { memcpy(grant_, grant, sizeof(uint8_t)*RAR_GRANT_LEN); } + uint32_t rar_subh::get_ta_cmd() { return ta; } + uint16_t rar_subh::get_temp_crnti() { return temp_rnti; } + void rar_subh::set_rapid(uint32_t rapid) { preamble = rapid; } + void rar_subh::set_sched_grant(uint8_t grant_[RAR_GRANT_LEN]) { memcpy(grant, grant_, sizeof(uint8_t)*RAR_GRANT_LEN); } + void rar_subh::set_ta_cmd(uint32_t ta_) { ta = ta_; } + void rar_subh::set_temp_crnti(uint16_t temp_rnti_) { temp_rnti = temp_rnti_; } + // Section 6.2.2 void rar_subh::write_subheader(uint8_t** ptr, bool is_last) { @@ -873,150 +945,3 @@ bool rar_subh::read_subheader(uint8_t** ptr) } } - - - -//int main() -//{ -// /* Test 1st message: CCCH + Short BSR + PHR */ -// uint8_t buffer[10240]; -// uint8_t ccch_payload[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60}; -// uint32_t bsr_st[4] = {1, 2, 3, 4}; -// srsue::sch_pdu pdu(10); -// uint8_t *ptr; - -// printf("------- CCCH + Short BSR + PHR no padding ----------\n"); -// bzero(buffer, 10240); -// pdu.init_tx(buffer, 11, true); -// printf("Available space: %d\n", pdu.rem_size()); -// pdu.new_subh(); -// pdu.get()->set_sdu(0,6,ccch_payload); -// pdu.new_subh(); -// pdu.get()->set_phr(10); -// pdu.new_subh(); -// pdu.get()->set_bsr(bsr_st, srsue::sch_subh::SHORT_BSR); -// printf("Remaining space: %d\n", pdu.rem_size()); -// ptr = pdu.write_packet(); -// srslte_vec_fprint_byte(stdout, ptr, pdu.get_pdu_len()); -// printf("\n"); - -// /* Test single SDU: SDU 15 + 1 byte header */ -// printf("------- Single SDU no padding ----------\n"); -// uint8_t dlsch_payload[15] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; -// bzero(buffer, 10240); -// pdu.init_tx(buffer, 16, true); -// printf("Available space: %d\n", pdu.rem_size()); -// pdu.new_subh(); -// pdu.get()->set_sdu(1, 15, dlsch_payload); -// printf("Remaining space: %d\n", pdu.rem_size()); -// ptr = pdu.write_packet(); -// srslte_vec_fprint_byte(stdout, ptr, pdu.get_pdu_len()); -// printf("\n"); - -// /* Test multiple SDU + multiword padding: SDU 8 + SDU 2 byte*/ -// printf("------- Multiple SDU + multiword padding ----------\n"); -// uint8_t dlsch_payload1[8] = {1,2,3,4,5,6,7,8}; -// uint8_t dlsch_payload2[2] = {0xA, 0xB}; -// bzero(buffer, 10240); -// pdu.init_tx(buffer, 18, true); -// printf("Available space: %d\n", pdu.rem_size()); -// pdu.new_subh(); -// pdu.get()->set_sdu(2, 8, dlsch_payload1); -// pdu.new_subh(); -// pdu.get()->set_sdu(3, 2, dlsch_payload2); -// printf("Remaining space: %d\n", pdu.rem_size()); -// ptr = pdu.write_packet(); -// srslte_vec_fprint_byte(stdout, ptr, pdu.get_pdu_len()); -// printf("\n"); - -// printf("------- Multiple SDU + 2word padding ----------\n"); -// bzero(buffer, 10240); -// pdu.init_tx(buffer, 15, true); -// printf("Available space: %d\n", pdu.rem_size()); -// pdu.new_subh(); -// pdu.get()->set_sdu(2, 8, dlsch_payload1); -// pdu.new_subh(); -// pdu.get()->set_sdu(3, 2, dlsch_payload2); -// printf("Remaining space: %d\n", pdu.rem_size()); -// ptr = pdu.write_packet(); -// srslte_vec_fprint_byte(stdout, ptr, pdu.get_pdu_len()); -// printf("\n"); - -// printf("------- Multiple SDU + 1word padding ----------\n"); -// bzero(buffer, 10240); -// pdu.init_tx(buffer, 14, true); -// printf("Available space: %d\n", pdu.rem_size()); -// pdu.new_subh(); -// pdu.get()->set_sdu(2, 8, dlsch_payload1); -// pdu.new_subh(); -// pdu.get()->set_sdu(3, 2, dlsch_payload2); -// printf("Remaining space: %d\n", pdu.rem_size()); -// ptr = pdu.write_packet(); -// srslte_vec_fprint_byte(stdout, ptr, pdu.get_pdu_len()); -// printf("\n"); - -// printf("------- Multiple SDU + 0word padding ----------\n"); -// bzero(buffer, 10240); -// pdu.init_tx(buffer, 13, true); -// printf("Available space: %d\n", pdu.rem_size()); -// pdu.new_subh(); -// pdu.get()->set_sdu(2, 8, dlsch_payload1); -// pdu.new_subh(); -// pdu.get()->set_sdu(3, 2, dlsch_payload2); -// printf("Remaining space: %d\n", pdu.rem_size()); -// ptr = pdu.write_packet(); -// srslte_vec_fprint_byte(stdout, ptr, pdu.get_pdu_len()); -// printf("\n"); - -// printf("------- Multiple SDU + no space ----------\n"); -// bzero(buffer, 10240); -// pdu.init_tx(buffer, 12, true); -// printf("Available space: %d\n", pdu.rem_size()); -// pdu.new_subh(); -// pdu.get()->set_sdu(2, 8, dlsch_payload1); -// pdu.new_subh(); -// if (pdu.get()->set_sdu(3, 2, dlsch_payload2) < 0) { -// pdu.del_subh(); -// } -// printf("Remaining space: %d\n", pdu.rem_size()); -// ptr = pdu.write_packet(); -// srslte_vec_fprint_byte(stdout, ptr, pdu.get_pdu_len()); -// printf("\n"); - -// /* CE only */ -// printf("------- CE only ----------\n"); -// bzero(buffer, 10240); -// pdu.init_tx(buffer, 125, true); -// printf("Available space: %d\n", pdu.rem_size()); -// pdu.new_subh(); -// pdu.get()->set_phr(15); -// pdu.new_subh(); -// pdu.get()->set_bsr(bsr_st, srsue::sch_subh::SHORT_BSR); -// printf("Remaining space: %d\n", pdu.rem_size()); -// ptr = pdu.write_packet(); -// srslte_vec_fprint_byte(stdout, ptr, pdu.get_pdu_len()); -// printf("\n"); - -// /* Another test */ -// printf("------- Another test ----------\n"); -// uint8_t dlsch_payload3[602]; -// bzero(buffer, 10240); -// pdu.init_tx(buffer, 75, true); -// printf("Available space: %d\n", pdu.rem_size()); -// pdu.new_subh(); -// pdu.get()->set_bsr(bsr_st, srsue::sch_subh::SHORT_BSR); -// pdu.new_subh(); -// pdu.get()->set_sdu(3, 2, dlsch_payload3); -// pdu.new_subh(); -// pdu.get()->set_sdu(3, 66, dlsch_payload3); -// pdu.new_subh(); -// printf("Remaining space: %d\n", pdu.rem_size()); -// ptr = pdu.write_packet(); -// //srslte_vec_fprint_byte(stdout, ptr, pdu.get_pdu_len()); -// printf("\n"); - -// return 0; -//} - - - diff --git a/lib/src/phy/ch_estimation/chest_dl.c b/lib/src/phy/ch_estimation/chest_dl.c index 238cd1a18..578bd177b 100644 --- a/lib/src/phy/ch_estimation/chest_dl.c +++ b/lib/src/phy/ch_estimation/chest_dl.c @@ -124,7 +124,7 @@ int srslte_chest_dl_init(srslte_chest_dl_t *q, uint32_t max_prb) perror("malloc"); goto clean_exit; } - + q->pilot_recv_signal = srslte_vec_malloc(sizeof(cf_t) * pilot_vec_size); if (!q->pilot_recv_signal) { perror("malloc"); @@ -154,7 +154,7 @@ int srslte_chest_dl_init(srslte_chest_dl_t *q, uint32_t max_prb) q->noise_alg = SRSLTE_NOISE_ALG_REFS; q->rsrp_neighbour = false; - + q->average_subframe = false; q->smooth_filter_auto = false; q->smooth_filter_len = 3; srslte_chest_dl_set_smooth_filter3_coeff(q, 0.1); @@ -212,9 +212,12 @@ int srslte_chest_dl_set_mbsfn_area_id(srslte_chest_dl_t *q, uint16_t mbsfn_area_ if (mbsfn_area_id < SRSLTE_MAX_MBSFN_AREA_IDS) { if(!q->mbsfn_refs[mbsfn_area_id]) { q->mbsfn_refs[mbsfn_area_id] = calloc(1, sizeof(srslte_refsignal_t)); + if(srslte_refsignal_mbsfn_init(q->mbsfn_refs[mbsfn_area_id], q->cell.nof_prb)) { + return SRSLTE_ERROR; + } } if(q->mbsfn_refs[mbsfn_area_id]) { - if(srslte_refsignal_mbsfn_init(q->mbsfn_refs[mbsfn_area_id], q->cell, mbsfn_area_id)) { + if(srslte_refsignal_mbsfn_set_cell(q->mbsfn_refs[mbsfn_area_id], q->cell, mbsfn_area_id)) { return SRSLTE_ERROR; } } @@ -267,11 +270,10 @@ static float estimate_noise_pilots(srslte_chest_dl_t *q, uint32_t port_id, srslt const float weight = 1.0f; float sum_power = 0.0f; uint32_t count = 0; - uint32_t npilots = SRSLTE_REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id); - uint32_t nsymbols = - (ch_mode == SRSLTE_SF_MBSFN) ? srslte_refsignal_mbsfn_nof_symbols() : srslte_refsignal_cs_nof_symbols(port_id); + uint32_t npilots = (ch_mode == SRSLTE_SF_MBSFN)?SRSLTE_REFSIGNAL_NUM_SF_MBSFN(q->cell.nof_prb, port_id):SRSLTE_REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id); + uint32_t nsymbols = (ch_mode == SRSLTE_SF_MBSFN) ? srslte_refsignal_mbsfn_nof_symbols() : srslte_refsignal_cs_nof_symbols(port_id); uint32_t nref = npilots / nsymbols; - uint32_t fidx = srslte_refsignal_cs_fidx(q->cell, 0, port_id, 0); + uint32_t fidx = (ch_mode == SRSLTE_SF_MBSFN)?srslte_refsignal_mbsfn_fidx(1):srslte_refsignal_cs_fidx(q->cell, 0, port_id, 0); cf_t *input2d[nsymbols + 2]; cf_t *tmp_noise = q->tmp_noise; @@ -330,7 +332,7 @@ static float estimate_noise_pss(srslte_chest_dl_t *q, cf_t *input, cf_t *ce) { /* Get PSS from received signal */ srslte_pss_get_slot(input, q->tmp_pss, q->cell.nof_prb, q->cell.cp); - + /* Get channel estimates for PSS position */ srslte_pss_get_slot(ce, q->tmp_pss_noisy, q->cell.nof_prb, q->cell.cp); @@ -339,7 +341,7 @@ static float estimate_noise_pss(srslte_chest_dl_t *q, cf_t *input, cf_t *ce) /* Substract received signal */ srslte_vec_sub_ccc(q->tmp_pss_noisy, q->tmp_pss, q->tmp_pss_noisy, SRSLTE_PSS_LEN); - + /* Compute average power */ float power = q->cell.nof_ports*srslte_vec_avg_power_cf(q->tmp_pss_noisy, SRSLTE_PSS_LEN)/sqrt(2); return power; @@ -348,14 +350,14 @@ static float estimate_noise_pss(srslte_chest_dl_t *q, cf_t *input, cf_t *ce) /* Uses the 5 empty transmitted SC before and after the SSS and PSS sequences for noise estimation */ static float estimate_noise_empty_sc(srslte_chest_dl_t *q, cf_t *input) { int k_sss = (SRSLTE_CP_NSYMB(q->cell.cp) - 2) * q->cell.nof_prb * SRSLTE_NRE + q->cell.nof_prb * SRSLTE_NRE / 2 - 31; - float noise_power = 0; + float noise_power = 0; noise_power += srslte_vec_avg_power_cf(&input[k_sss-5], 5); // 5 empty SC before SSS noise_power += srslte_vec_avg_power_cf(&input[k_sss+62], 5); // 5 empty SC after SSS int k_pss = (SRSLTE_CP_NSYMB(q->cell.cp) - 1) * q->cell.nof_prb * SRSLTE_NRE + q->cell.nof_prb * SRSLTE_NRE / 2 - 31; noise_power += srslte_vec_avg_power_cf(&input[k_pss-5], 5); // 5 empty SC before PSS noise_power += srslte_vec_avg_power_cf(&input[k_pss+62], 5); // 5 empty SC after PSS - - return noise_power; + + return noise_power; } #define cesymb(i) ce[SRSLTE_RE_IDX(q->cell.nof_prb,i,0)] @@ -497,8 +499,9 @@ void srslte_chest_dl_set_smooth_filter_auto(srslte_chest_dl_t *q, bool enable) { uint32_t srslte_chest_dl_interleave_pilots(srslte_chest_dl_t *q, cf_t *input, cf_t *tmp, cf_t *output, uint32_t port_id, srslte_sf_t ch_mode) { uint32_t nsymbols = (ch_mode == SRSLTE_SF_MBSFN)?srslte_refsignal_mbsfn_nof_symbols(port_id):srslte_refsignal_cs_nof_symbols(port_id); uint32_t nref = (ch_mode == SRSLTE_SF_MBSFN)?6*q->cell.nof_prb:2*q->cell.nof_prb; - - if (srslte_refsignal_cs_fidx(q->cell, 0, port_id, 0) < 3) { + uint32_t fidx = (ch_mode == SRSLTE_SF_MBSFN)?srslte_refsignal_mbsfn_fidx(1):srslte_refsignal_cs_fidx(q->cell, 0, port_id, 0); + + if (fidx < 3) { srslte_vec_interleave(input, &input[nref], tmp, nref); for (int l = 2; l < nsymbols - 1; l += 2) { srslte_vec_interleave_add(&input[l * nref], &input[(l + 1) * nref], tmp, nref); @@ -549,9 +552,16 @@ static void average_pilots(srslte_chest_dl_t *q, cf_t *input, cf_t *output, uint } } + + uint32_t skip = (ch_mode == SRSLTE_SF_MBSFN)?2*q->cell.nof_prb:0; + + if(ch_mode == SRSLTE_SF_MBSFN){ + memcpy(&output[0],&input[0],skip*sizeof(cf_t)); + } + // Average in the frequency domain for (int l=0;lsmooth_filter, &output[l*nref], nref, q->smooth_filter_len); + srslte_conv_same_cf(&input[l*nref + skip], q->smooth_filter, &output[l*nref + skip], nref, q->smooth_filter_len); } } @@ -660,11 +670,10 @@ int srslte_chest_dl_estimate_port_mbsfn(srslte_chest_dl_t *q, cf_t *input, cf_t srslte_vec_prod_conj_ccc(q->pilot_recv_signal, q->csr_refs.pilots[port_id/2][sf_idx], q->pilot_estimates, (2*q->cell.nof_prb)); - srslte_vec_prod_conj_ccc(q->pilot_recv_signal+(2*q->cell.nof_prb), q->mbsfn_refs[mbsfn_area_id]->pilots[port_id/2][sf_idx], - q->pilot_estimates+(2*q->cell.nof_prb), SRSLTE_REFSIGNAL_NUM_SF_MBSFN(q->cell.nof_prb, port_id)-(2*q->cell.nof_prb)); + srslte_vec_prod_conj_ccc(&q->pilot_recv_signal[(2*q->cell.nof_prb)], q->mbsfn_refs[mbsfn_area_id]->pilots[port_id/2][sf_idx], + &q->pilot_estimates[(2*q->cell.nof_prb)], SRSLTE_REFSIGNAL_NUM_SF_MBSFN(q->cell.nof_prb, port_id)-(2*q->cell.nof_prb)); - chest_interpolate_noise_est(q, input, ce, sf_idx, port_id, rxant_id, SRSLTE_SF_MBSFN); return 0; diff --git a/lib/src/phy/ch_estimation/refsignal_dl.c b/lib/src/phy/ch_estimation/refsignal_dl.c index 308be6877..14e078aed 100644 --- a/lib/src/phy/ch_estimation/refsignal_dl.c +++ b/lib/src/phy/ch_estimation/refsignal_dl.c @@ -120,6 +120,7 @@ inline uint32_t srslte_refsignal_cs_nsymbol(uint32_t l, srslte_cp_t cp, uint32_t return 1+l*SRSLTE_CP_NSYMB(cp); } } + inline uint32_t srslte_refsignal_mbsfn_nsymbol(uint32_t l) { uint32_t ret = 0; @@ -176,22 +177,21 @@ free_and_exit: } -int srslte_refsignal_mbsfn_init(srslte_refsignal_t * q, srslte_cell_t cell, uint16_t mbsfn_area_id) +int srslte_refsignal_mbsfn_init(srslte_refsignal_t * q, uint32_t max_prb) { int ret = SRSLTE_ERROR_INVALID_INPUTS; uint32_t i, p; - if (q != NULL && - srslte_cell_isvalid(&cell)) + if (q != NULL) { ret = SRSLTE_ERROR; bzero(q, sizeof(srslte_refsignal_t)); - q->cell = cell; + q->type = SRSLTE_SF_MBSFN; - q->mbsfn_area_id = mbsfn_area_id; + for (p=0;p<2;p++) { for (i=0;ipilots[p][i] = srslte_vec_malloc(sizeof(cf_t) * q->cell.nof_prb * 18); + q->pilots[p][i] = srslte_vec_malloc(sizeof(cf_t) * max_prb * 18); if (!q->pilots[p][i]) { perror("malloc"); goto free_and_exit; @@ -199,9 +199,7 @@ int srslte_refsignal_mbsfn_init(srslte_refsignal_t * q, srslte_cell_t cell, uint } } - if(srslte_refsignal_mbsfn_gen_seq(q, q->cell, q->mbsfn_area_id)) { - goto free_and_exit; - } + ret = SRSLTE_SUCCESS; } @@ -212,7 +210,24 @@ free_and_exit: return ret; } +int srslte_refsignal_mbsfn_set_cell(srslte_refsignal_t * q, srslte_cell_t cell, uint16_t mbsfn_area_id){ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + q->cell = cell; + q->mbsfn_area_id = mbsfn_area_id; + if(srslte_refsignal_mbsfn_gen_seq(q, q->cell, q->mbsfn_area_id)) { + goto free_and_exit; + } + + ret = SRSLTE_SUCCESS; + + free_and_exit: + if (ret == SRSLTE_ERROR) { + srslte_refsignal_free(q); + } + return ret; +} /** Allocates memory for the 20 slots in a subframe diff --git a/lib/src/phy/dft/ofdm.c b/lib/src/phy/dft/ofdm.c index 977f37892..06da6b41a 100644 --- a/lib/src/phy/dft/ofdm.c +++ b/lib/src/phy/dft/ofdm.c @@ -149,6 +149,7 @@ int srslte_ofdm_replan_(srslte_ofdm_t *q, srslte_cp_t cp, int symbol_sz, int nof q->symbol_sz = (uint32_t) symbol_sz; q->nof_symbols = SRSLTE_CP_NSYMB(cp); + q->nof_symbols_mbsfn = SRSLTE_CP_NSYMB(SRSLTE_CP_EXT); q->cp = cp; q->nof_re = (uint32_t) nof_prb * SRSLTE_NRE; q->nof_guards = ((symbol_sz - q->nof_re) / 2); @@ -246,14 +247,15 @@ int srslte_ofdm_rx_init(srslte_ofdm_t *q, srslte_cp_t cp, cf_t *in_buffer, cf_t return srslte_ofdm_init_(q, cp, in_buffer, out_buffer, symbol_sz, max_prb, SRSLTE_DFT_FORWARD); } -int srslte_ofdm_rx_init_mbsfn(srslte_ofdm_t *q, srslte_cp_t cp, cf_t *in_buffer, cf_t *out_buffer, uint32_t nof_prb) +int srslte_ofdm_rx_init_mbsfn(srslte_ofdm_t *q, srslte_cp_t cp, cf_t *in_buffer, cf_t *out_buffer, uint32_t max_prb) { - int symbol_sz = srslte_symbol_sz(nof_prb); + int symbol_sz = srslte_symbol_sz(max_prb); if (symbol_sz < 0) { - fprintf(stderr, "Error: Invalid nof_prb=%d\n", nof_prb); + fprintf(stderr, "Error: Invalid nof_prb=%d\n", max_prb); return -1; } - return srslte_ofdm_init_mbsfn_(q, cp, in_buffer, out_buffer, symbol_sz, nof_prb, SRSLTE_DFT_FORWARD, SRSLTE_SF_MBSFN); + q->max_prb = max_prb; + return srslte_ofdm_init_mbsfn_(q, cp, in_buffer, out_buffer, symbol_sz, max_prb, SRSLTE_DFT_FORWARD, SRSLTE_SF_MBSFN); } @@ -292,7 +294,7 @@ int srslte_ofdm_tx_init_mbsfn(srslte_ofdm_t *q, srslte_cp_t cp, cf_t *in_buffer, fprintf(stderr, "Error: Invalid nof_prb=%d\n", nof_prb); return -1; } - + q->max_prb = nof_prb; ret = srslte_ofdm_init_mbsfn_(q, cp, in_buffer, out_buffer, symbol_sz, nof_prb, SRSLTE_DFT_BACKWARD, SRSLTE_SF_MBSFN); if (ret == SRSLTE_SUCCESS) { @@ -559,7 +561,6 @@ void srslte_ofdm_tx_slot(srslte_ofdm_t *q, int slot_in_sf) { void srslte_ofdm_tx_slot_mbsfn(srslte_ofdm_t *q, cf_t *input, cf_t *output) { uint32_t i, cp_len; - for(i=0;inof_symbols_mbsfn;i++) { cp_len = ( i>(q->non_mbsfn_region-1) )?SRSLTE_CP_LEN_EXT(q->symbol_sz):SRSLTE_CP_LEN_NORM(i, q->symbol_sz); memcpy(&q->tmp[q->nof_guards], input, q->nof_re * sizeof(cf_t)); diff --git a/lib/src/phy/enb/enb_dl.c b/lib/src/phy/enb/enb_dl.c index 9d33da26c..6d92db83a 100644 --- a/lib/src/phy/enb/enb_dl.c +++ b/lib/src/phy/enb/enb_dl.c @@ -69,6 +69,13 @@ int srslte_enb_dl_init(srslte_enb_dl_t *q, cf_t *out_buffer[SRSLTE_MAX_PORTS], u goto clean_exit; } } + + + if (srslte_ofdm_tx_init_mbsfn(&q->ifft_mbsfn, SRSLTE_CP_EXT, q->sf_symbols[0], out_buffer[0], max_prb)) { + fprintf(stderr, "Error initiating FFT \n"); + goto clean_exit; + } + if (srslte_pbch_init(&q->pbch)) { fprintf(stderr, "Error creating PBCH object\n"); @@ -82,7 +89,15 @@ int srslte_enb_dl_init(srslte_enb_dl_t *q, cf_t *out_buffer[SRSLTE_MAX_PORTS], u fprintf(stderr, "Error creating PHICH object\n"); goto clean_exit; } + int mbsfn_area_id = 1; + + + if (srslte_pmch_init(&q->pmch, max_prb)) { + fprintf(stderr, "Error creating PMCH object\n"); + } + srslte_pmch_set_area_id(&q->pmch, mbsfn_area_id); + if (srslte_pdcch_init_enb(&q->pdcch, max_prb)) { fprintf(stderr, "Error creating PDCCH object\n"); goto clean_exit; @@ -97,7 +112,11 @@ int srslte_enb_dl_init(srslte_enb_dl_t *q, cf_t *out_buffer[SRSLTE_MAX_PORTS], u fprintf(stderr, "Error initializing CSR signal (%d)\n",ret); goto clean_exit; } - + + if (srslte_refsignal_mbsfn_init(&q->mbsfnr_signal, max_prb)) { + fprintf(stderr, "Error initializing CSR signal (%d)\n",ret); + goto clean_exit; + } ret = SRSLTE_SUCCESS; } else { @@ -117,15 +136,16 @@ void srslte_enb_dl_free(srslte_enb_dl_t *q) for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { srslte_ofdm_tx_free(&q->ifft[i]); } + srslte_ofdm_tx_free(&q->ifft_mbsfn); srslte_regs_free(&q->regs); srslte_pbch_free(&q->pbch); srslte_pcfich_free(&q->pcfich); srslte_phich_free(&q->phich); srslte_pdcch_free(&q->pdcch); srslte_pdsch_free(&q->pdsch); - + srslte_pmch_free(&q->pmch); srslte_refsignal_free(&q->csr_signal); - + srslte_refsignal_free(&q->mbsfnr_signal); for (int i=0;isf_symbols[i]) { free(q->sf_symbols[i]); @@ -159,6 +179,15 @@ int srslte_enb_dl_set_cell(srslte_enb_dl_t *q, srslte_cell_t cell) return SRSLTE_ERROR; } } + + if (srslte_ofdm_tx_set_prb(&q->ifft_mbsfn, SRSLTE_CP_EXT, q->cell.nof_prb)) { + fprintf(stderr, "Error re-planning ifft_mbsfn\n"); + return SRSLTE_ERROR; + } + + srslte_ofdm_set_non_mbsfn_region(&q->ifft_mbsfn, 2); + //srslte_ofdm_set_normalize(&q->ifft_mbsfn, true); + if (srslte_pbch_set_cell(&q->pbch, q->cell)) { fprintf(stderr, "Error creating PBCH object\n"); return SRSLTE_ERROR; @@ -181,11 +210,21 @@ int srslte_enb_dl_set_cell(srslte_enb_dl_t *q, srslte_cell_t cell) fprintf(stderr, "Error creating PDSCH object\n"); return SRSLTE_ERROR; } + + if (srslte_pmch_set_cell(&q->pmch, q->cell)) { + fprintf(stderr, "Error creating PMCH object\n"); + return SRSLTE_ERROR; + } if (srslte_refsignal_cs_set_cell(&q->csr_signal, q->cell)) { fprintf(stderr, "Error initializing CSR signal (%d)\n",ret); return SRSLTE_ERROR; } + int mbsfn_area_id = 1; + if (srslte_refsignal_mbsfn_set_cell(&q->mbsfnr_signal, q->cell, mbsfn_area_id)) { + fprintf(stderr, "Error initializing MBSFNR signal (%d)\n",ret); + return SRSLTE_ERROR; + } /* Generate PSS/SSS signals */ srslte_pss_generate(q->pss_signal, cell.id%3); srslte_sss_generate(q->sss_signal0, q->sss_signal5, cell.id); @@ -201,6 +240,11 @@ int srslte_enb_dl_set_cell(srslte_enb_dl_t *q, srslte_cell_t cell) +void srslte_enb_dl_set_non_mbsfn_region(srslte_enb_dl_t *q, uint8_t non_mbsfn_region) +{ + srslte_ofdm_set_non_mbsfn_region(&q->ifft_mbsfn, non_mbsfn_region); +} + void srslte_enb_dl_set_amp(srslte_enb_dl_t *q, float amp) { q->tx_amp = amp; @@ -334,17 +378,30 @@ void srslte_enb_dl_put_base(srslte_enb_dl_t *q, uint32_t tti) } +void srslte_enb_dl_put_mbsfn_base(srslte_enb_dl_t *q, uint32_t tti) +{ + uint32_t sf_idx1 = tti%10; + srslte_enb_dl_put_pcfich(q, sf_idx1); + srslte_refsignal_mbsfn_put_sf(q->cell, 0,q->csr_signal.pilots[0][sf_idx1], q->mbsfnr_signal.pilots[0][sf_idx1], q->sf_symbols[0]); +} + void srslte_enb_dl_gen_signal(srslte_enb_dl_t *q) { // TODO: PAPR control float norm_factor = (float) sqrt(q->cell.nof_prb)/15/sqrt(q->ifft[0].symbol_sz); - for (int i = 0; i < q->cell.nof_ports; i++) { - srslte_ofdm_tx_sf(&q->ifft[i]); - srslte_vec_sc_prod_cfc(q->ifft[i].out_buffer, q->tx_amp*norm_factor, q->ifft[i].out_buffer, (uint32_t) SRSLTE_SF_LEN_PRB(q->cell.nof_prb)); + srslte_ofdm_tx_sf(&q->ifft[i]); + srslte_vec_sc_prod_cfc(q->ifft[i].out_buffer, norm_factor, q->ifft[i].out_buffer, (uint32_t) SRSLTE_SF_LEN_PRB(q->cell.nof_prb)); } } +void srslte_enb_dl_gen_signal_mbsfn(srslte_enb_dl_t *q) +{ + float norm_factor = (float) sqrt(q->cell.nof_prb)/15/sqrt(q->ifft_mbsfn.symbol_sz); + srslte_ofdm_tx_sf(&q->ifft_mbsfn); + srslte_vec_sc_prod_cfc(q->ifft_mbsfn.out_buffer, norm_factor, q->ifft_mbsfn.out_buffer, (uint32_t) SRSLTE_SF_LEN_PRB(q->cell.nof_prb)); +} + int srslte_enb_dl_add_rnti(srslte_enb_dl_t *q, uint16_t rnti) { return srslte_pdsch_set_rnti(&q->pdsch, rnti); @@ -438,7 +495,23 @@ int srslte_enb_dl_put_pdsch(srslte_enb_dl_t *q, srslte_ra_dl_grant_t *grant, srs return SRSLTE_SUCCESS; } - +int srslte_enb_dl_put_pmch(srslte_enb_dl_t *q, srslte_ra_dl_grant_t *grant, srslte_softbuffer_tx_t *softbuffer, uint32_t sf_idx, uint8_t *data_mbms) +{ + /* Encode PMCH */ + + int mbsfn_area_id = 1; + if (srslte_pmch_cfg(&q->pmch_cfg, q->cell, grant, q->cfi, sf_idx)) { + fprintf(stderr, "Error configuring PMCH\n"); + return SRSLTE_ERROR; + } + /* Encode PMCH */ + if (srslte_pmch_encode(&q->pmch, &q->pmch_cfg, softbuffer, data_mbms, mbsfn_area_id, q->sf_symbols)) { + fprintf(stderr, "Error encoding PDSCH\n"); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} void srslte_enb_dl_save_signal(srslte_enb_dl_t *q, srslte_softbuffer_tx_t *softbuffer, uint8_t *data, uint32_t tti, uint32_t rv_idx, uint16_t rnti, uint32_t cfi) { diff --git a/lib/src/phy/phch/pmch.c b/lib/src/phy/phch/pmch.c index 941d98a2f..c5d4ad24c 100644 --- a/lib/src/phy/phch/pmch.c +++ b/lib/src/phy/phch/pmch.c @@ -264,12 +264,29 @@ void srslte_pmch_free(srslte_pmch_t *q) { } +int srslte_pmch_set_cell(srslte_pmch_t *q, srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + srslte_cell_isvalid(&cell)) + { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + q->max_re = q->cell.nof_prb * MAX_PMCH_RE; + + INFO("PMCH: Cell config PCI=%d, %d ports, %d PRBs, max_symbols: %d\n", q->cell.nof_ports, + q->cell.id, q->cell.nof_prb, q->max_re); + + ret = SRSLTE_SUCCESS; + } + return ret; +} /* Precalculate the scramble sequences for a given MBSFN area ID. This function takes a while * to execute. */ int srslte_pmch_set_area_id(srslte_pmch_t *q, uint16_t area_id) { - uint32_t i; + uint32_t i; if (!q->seqs[area_id]) { q->seqs[area_id] = calloc(1, sizeof(srslte_pmch_seq_t)); if (q->seqs[area_id]) { @@ -347,7 +364,6 @@ int srslte_pmch_decode_multi(srslte_pmch_t *q, data != NULL && cfg != NULL) { - INFO("Decoding PMCH SF: %d, MBSFN area ID: 0x%x, Mod %s, TBS: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d, C_prb=%d, cfi=%d\n", cfg->sf_idx, area_id, srslte_mod_string(cfg->grant.mcs[0].mod), cfg->grant.mcs[0].tbs, cfg->nbits[0].nof_re, cfg->nbits[0].nof_bits, 0, cfg->grant.nof_prb, cfg->nbits[0].lstart-1); @@ -362,6 +378,7 @@ int srslte_pmch_decode_multi(srslte_pmch_t *q, /* extract symbols */ n = srslte_pmch_get(q, sf_symbols[j], q->symbols[j], cfg->nbits[0].lstart); if (n != cfg->nbits[0].nof_re) { + fprintf(stderr, "PMCH 1 extract symbols error expecting %d symbols but got %d, lstart %d\n", cfg->nbits[0].nof_re, n, cfg->nbits[0].lstart); return SRSLTE_ERROR; } @@ -416,7 +433,6 @@ int srslte_pmch_encode(srslte_pmch_t *q, /* Set pointers for layermapping & precoding */ cf_t *x[SRSLTE_MAX_LAYERS]; int ret = SRSLTE_ERROR_INVALID_INPUTS; - if (q != NULL && cfg != NULL) { for (i=0;icell.nof_ports;i++) { diff --git a/lib/src/phy/ue/ue_dl.c b/lib/src/phy/ue/ue_dl.c index 98ff5c9fd..7bcbbf578 100644 --- a/lib/src/phy/ue/ue_dl.c +++ b/lib/src/phy/ue/ue_dl.c @@ -199,8 +199,10 @@ int srslte_ue_dl_set_cell(srslte_ue_dl_t *q, srslte_cell_t cell) if (q != NULL && srslte_cell_isvalid(&cell)) { - q->pkt_errors = 0; - q->pkts_total = 0; + q->pdsch_pkt_errors = 0; + q->pdsch_pkts_total = 0; + q->pmch_pkt_errors = 0; + q->pmch_pkts_total = 0; q->pending_ul_dci_rnti = 0; if (q->cell.id != cell.id || q->cell.nof_prb == 0) { @@ -218,6 +220,12 @@ int srslte_ue_dl_set_cell(srslte_ue_dl_t *q, srslte_cell_t cell) return SRSLTE_ERROR; } } + + if (srslte_ofdm_rx_set_prb(&q->fft_mbsfn, SRSLTE_CP_EXT, q->cell.nof_prb)) { + fprintf(stderr, "Error resizing MBSFN FFT\n"); + return SRSLTE_ERROR; + } + if (srslte_chest_dl_set_cell(&q->chest, q->cell)) { fprintf(stderr, "Error resizing channel estimator\n"); return SRSLTE_ERROR; @@ -237,9 +245,15 @@ int srslte_ue_dl_set_cell(srslte_ue_dl_t *q, srslte_cell_t cell) } if (srslte_pdsch_set_cell(&q->pdsch, q->cell)) { - fprintf(stderr, "Error creating PDSCH object\n"); + fprintf(stderr, "Error resizing PDSCH object\n"); + return SRSLTE_ERROR; + } + + if (srslte_pmch_set_cell(&q->pmch, q->cell)) { + fprintf(stderr, "Error resizing PMCH object\n"); return SRSLTE_ERROR; } + q->current_rnti = 0; } ret = SRSLTE_SUCCESS; @@ -644,6 +658,7 @@ int srslte_ue_dl_decode_mbsfn(srslte_ue_dl_t * q, noise_estimate, q->current_mbsfn_area_id, data); + if (ret == SRSLTE_ERROR) { q->pmch_pkt_errors++; } else if (ret == SRSLTE_ERROR_INVALID_INPUTS) { diff --git a/lib/src/phy/utils/vector.c b/lib/src/phy/utils/vector.c index d3d836b21..6c55ddefb 100644 --- a/lib/src/phy/utils/vector.c +++ b/lib/src/phy/utils/vector.c @@ -139,7 +139,7 @@ void srslte_vec_fprint_c(FILE *stream, cf_t *x, const uint32_t len) { int i; fprintf(stream, "["); for (i=0;i= SRSLTE_N_RADIO_BEARERS) { @@ -114,6 +120,21 @@ void pdcp::add_bearer(uint32_t lcid, srslte_pdcp_config_t cfg) } } + +void pdcp::add_bearer_mrb(uint32_t lcid, srslte_pdcp_config_t cfg) +{ + if(lcid >= SRSLTE_N_RADIO_BEARERS) { + pdcp_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_RADIO_BEARERS, lcid); + return; + } + if (!pdcp_array_mrb[lcid].is_active()) { + pdcp_array_mrb[lcid].init(rlc, rrc, gw, pdcp_log, lcid, cfg); + pdcp_log->info("Added bearer %s\n", rrc->get_rb_name(lcid).c_str()); + } else { + pdcp_log->warning("Bearer %s already configured. Reconfiguration not supported\n", rrc->get_rb_name(lcid).c_str()); + } +} + void pdcp::config_security(uint32_t lcid, uint8_t *k_enc, uint8_t *k_int, @@ -175,6 +196,15 @@ void pdcp::write_pdu_pcch(byte_buffer_t *sdu) rrc->write_pdu_pcch(sdu); } +void pdcp::write_pdu_mch(uint32_t lcid, byte_buffer_t *sdu) +{ + if(0 == lcid) { + rrc->write_pdu_mch(lcid, sdu); + } else { + gw->write_pdu_mch(lcid, sdu); + } +} + /******************************************************************************* Helpers *******************************************************************************/ @@ -191,4 +221,17 @@ bool pdcp::valid_lcid(uint32_t lcid) return true; } +bool pdcp::valid_mch_lcid(uint32_t lcid) +{ + if(lcid >= SRSLTE_N_MCH_LCIDS) { + pdcp_log->error("Radio bearer id must be in [0:%d] - %d", SRSLTE_N_RADIO_BEARERS, lcid); + return false; + } + if(!pdcp_array_mrb[lcid].is_active()) { + pdcp_log->error("PDCP entity for logical channel %d has not been activated\n", lcid); + return false; + } + return true; +} + } // namespace srsue diff --git a/lib/src/upper/rlc.cc b/lib/src/upper/rlc.cc index e771c34a2..5b26add1b 100644 --- a/lib/src/upper/rlc.cc +++ b/lib/src/upper/rlc.cc @@ -100,6 +100,16 @@ void rlc::get_metrics(rlc_metrics_t &m) } } + // Add multicast metrics + for (int i=0;iinfo("MCH_LCID=%d, RX throughput: %4.6f Mbps.\n", + i, + (dl_tput_bytes_mrb[i]*8/(double)1e6)/secs); + } + } + memcpy(&metrics_time[1], &metrics_time[2], sizeof(struct timeval)); reset_metrics(); } @@ -139,6 +149,12 @@ void rlc::write_sdu(uint32_t lcid, byte_buffer_t *sdu) rlc_array[lcid].write_sdu(sdu); } } +void rlc::write_sdu_mch(uint32_t lcid, byte_buffer_t *sdu) +{ + if(valid_lcid_mrb(lcid)) { + rlc_array_mrb[lcid].write_sdu(sdu); + } +} bool rlc::rb_is_um(uint32_t lcid) { return rlc_array[lcid].get_mode()==RLC_MODE_UM; @@ -165,6 +181,15 @@ uint32_t rlc::get_total_buffer_state(uint32_t lcid) } } +uint32_t rlc::get_total_mch_buffer_state(uint32_t lcid) +{ + if(valid_lcid_mrb(lcid)) { + return rlc_array_mrb[lcid].get_total_buffer_state(); + } else { + return 0; + } +} + int rlc::read_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) { if(valid_lcid(lcid)) { @@ -174,6 +199,15 @@ int rlc::read_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) return 0; } +int rlc::read_pdu_mch(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) +{ + if(valid_lcid_mrb(lcid)) { + ul_tput_bytes[lcid] += nof_bytes; + return rlc_array_mrb[lcid].read_pdu(payload, nof_bytes); + } + return 0; +} + void rlc::write_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) { if(valid_lcid(lcid)) { @@ -227,6 +261,14 @@ void rlc::write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes) } } +void rlc::write_pdu_mch(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) +{ + if(valid_lcid_mrb(lcid)) { + dl_tput_bytes_mrb[lcid] += nof_bytes; + rlc_array_mrb[lcid].write_pdu(payload, nof_bytes); + } +} + /******************************************************************************* RRC interface *******************************************************************************/ @@ -283,8 +325,29 @@ void rlc::add_bearer(uint32_t lcid, srslte_rlc_config_t cnfg) } else { rlc_log->warning("Bearer %s already created.\n", rrc->get_rb_name(lcid).c_str()); } - rlc_array[lcid].configure(cnfg); + rlc_array[lcid].configure(cnfg); + +} + +void rlc::add_bearer_mrb(uint32_t lcid) +{ + // 36.321 Table 6.2.1-4 + if(lcid >= SRSLTE_N_MCH_LCIDS) { + rlc_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_MCH_LCIDS, lcid); + return; + } + rlc_array_mrb[lcid].init(rlc_log, lcid, pdcp, rrc, mac_timers); + rlc_array_mrb[lcid].configure(srslte_rlc_config_t::mch_config()); +} +void rlc::add_bearer_mrb_enb(uint32_t lcid) +{ + if(lcid >= SRSLTE_N_MCH_LCIDS) { + rlc_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_MCH_LCIDS, lcid); + return; + } + rlc_array_mrb[lcid].init(rlc_log,lcid,pdcp,rrc,mac_timers); + rlc_array_mrb[lcid].configure(srslte_rlc_config_t::mch_config()); } /******************************************************************************* @@ -301,5 +364,16 @@ bool rlc::valid_lcid(uint32_t lcid) return true; } +bool rlc::valid_lcid_mrb(uint32_t lcid) +{ + if(lcid >= SRSLTE_N_MCH_LCIDS) { + return false; + } + if(!rlc_array_mrb[lcid].is_mrb()) { + return false; + } + return true; +} + } // namespace srsue diff --git a/lib/src/upper/rlc_am.cc b/lib/src/upper/rlc_am.cc index 461d5141d..a29f54216 100644 --- a/lib/src/upper/rlc_am.cc +++ b/lib/src/upper/rlc_am.cc @@ -81,11 +81,10 @@ rlc_am::~rlc_am() pool->deallocate(tx_sdu); } } - -void rlc_am::init(srslte::log *log_, - uint32_t lcid_, - srsue::pdcp_interface_rlc *pdcp_, - srsue::rrc_interface_rlc *rrc_, +void rlc_am::init(srslte::log *log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, srslte::mac_interface_timers *mac_timers) { log = log_; diff --git a/lib/src/upper/rlc_um.cc b/lib/src/upper/rlc_um.cc index 45240261c..f31492207 100644 --- a/lib/src/upper/rlc_um.cc +++ b/lib/src/upper/rlc_um.cc @@ -26,6 +26,8 @@ #include "srslte/upper/rlc_um.h" +#include +#include #define RX_MOD_BASE(x) (x-vr_uh-cfg.rx_window_size)%cfg.rx_mod @@ -42,6 +44,7 @@ rlc_um::rlc_um() : tx_sdu_queue(32) bzero(&cfg, sizeof(srslte_rlc_um_config_t)); tx_sdu = NULL; + rx_sdu = NULL; pool = byte_buffer_pool::get_instance(); @@ -54,7 +57,7 @@ rlc_um::rlc_um() : tx_sdu_queue(32) vr_ur_in_rx_sdu = 0; - mac_timers = NULL; + mac_timers = NULL; pdu_lost = false; } @@ -63,11 +66,10 @@ rlc_um::~rlc_um() { stop(); } - -void rlc_um::init(srslte::log *log_, - uint32_t lcid_, - srsue::pdcp_interface_rlc *pdcp_, - srsue::rrc_interface_rlc *rrc_, +void rlc_um::init(srslte::log *log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, srslte::mac_interface_timers *mac_timers_) { log = log_; @@ -82,24 +84,27 @@ void rlc_um::init(srslte::log *log_, void rlc_um::configure(srslte_rlc_config_t cnfg_) { cfg = cnfg_.um; - + if(cnfg_.um.is_mrb){ + tx_sdu_queue.resize(512); + } switch(cnfg_.rlc_mode) { case LIBLTE_RRC_RLC_MODE_UM_BI: log->warning("%s configured in %s mode: " "t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits\n", - rrc->get_rb_name(lcid).c_str(), liblte_rrc_rlc_mode_text[cnfg_.rlc_mode], + rb_name().c_str(), liblte_rrc_rlc_mode_text[cnfg_.rlc_mode], cfg.t_reordering, rlc_umd_sn_size_num[cfg.rx_sn_field_length], rlc_umd_sn_size_num[cfg.rx_sn_field_length]); break; case LIBLTE_RRC_RLC_MODE_UM_UNI_UL: log->warning("%s configured in %s mode: tx_sn_field_length=%u bits\n", rrc->get_rb_name(lcid).c_str(), liblte_rrc_rlc_mode_text[cnfg_.rlc_mode], + rlc_umd_sn_size_num[cfg.rx_sn_field_length]); break; case LIBLTE_RRC_RLC_MODE_UM_UNI_DL: log->warning("%s configured in %s mode: " "t_reordering=%d ms, rx_sn_field_length=%u bits\n", - rrc->get_rb_name(lcid).c_str(), liblte_rrc_rlc_mode_text[cnfg_.rlc_mode], + rb_name().c_str(), liblte_rrc_rlc_mode_text[cnfg_.rlc_mode], cfg.t_reordering, rlc_umd_sn_size_num[cfg.rx_sn_field_length]); break; default: @@ -116,6 +121,11 @@ void rlc_um::empty_queue() { } } +bool rlc_um::is_mrb() +{ + return cfg.is_mrb; +} + void rlc_um::stop() { reset(); @@ -184,6 +194,7 @@ uint32_t rlc_um::get_buffer_state() { // Bytes needed for tx SDUs uint32_t n_sdus = tx_sdu_queue.size(); + uint32_t n_bytes = tx_sdu_queue.size_bytes(); if(tx_sdu) { @@ -197,7 +208,7 @@ uint32_t rlc_um::get_buffer_state() // Room needed for fixed header? if(n_bytes > 0) - n_bytes += 3; + n_bytes += (cfg.is_mrb)?2:3; return n_bytes; } @@ -209,9 +220,10 @@ uint32_t rlc_um::get_total_buffer_state() int rlc_um::read_pdu(uint8_t *payload, uint32_t nof_bytes) { + int r; log->debug("MAC opportunity - %d bytes\n", nof_bytes); pthread_mutex_lock(&mutex); - int r = build_data_pdu(payload, nof_bytes); + r = build_data_pdu(payload, nof_bytes); pthread_mutex_unlock(&mutex); return r; } @@ -235,7 +247,7 @@ void rlc_um::timer_expired(uint32_t timeout_id) // 36.322 v10 Section 5.1.2.2.4 log->info("%s reordering timeout expiry - updating vr_ur and reassembling\n", - rrc->get_rb_name(lcid).c_str()); + rb_name().c_str()); log->warning("Lost PDU SN: %d\n", vr_ur); pdu_lost = true; @@ -300,7 +312,7 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes) { pool->deallocate(pdu); log->warning("%s Cannot build a PDU - %d bytes available, %d bytes required for header\n", - rrc->get_rb_name(lcid).c_str(), nof_bytes, head_len); + rb_name().c_str(), nof_bytes, head_len); return 0; } @@ -310,7 +322,7 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes) uint32_t space = pdu_space-head_len; to_move = space >= tx_sdu->N_bytes ? tx_sdu->N_bytes : space; log->debug("%s adding remainder of SDU segment - %d bytes of %d remaining\n", - rrc->get_rb_name(lcid).c_str(), to_move, tx_sdu->N_bytes); + rb_name().c_str(), to_move, tx_sdu->N_bytes); memcpy(pdu_ptr, tx_sdu->msg, to_move); last_li = to_move; pdu_ptr += to_move; @@ -321,6 +333,7 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes) { log->debug("%s Complete SDU scheduled for tx. Stack latency: %ld us\n", rrc->get_rb_name(lcid).c_str(), tx_sdu->get_latency_us()); + pool->deallocate(tx_sdu); tx_sdu = NULL; } @@ -339,7 +352,7 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes) uint32_t space = pdu_space-head_len; to_move = space >= tx_sdu->N_bytes ? tx_sdu->N_bytes : space; log->debug("%s adding new SDU segment - %d bytes of %d remaining\n", - rrc->get_rb_name(lcid).c_str(), to_move, tx_sdu->N_bytes); + rb_name().c_str(), to_move, tx_sdu->N_bytes); memcpy(pdu_ptr, tx_sdu->msg, to_move); last_li = to_move; pdu_ptr += to_move; @@ -350,6 +363,7 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes) { log->debug("%s Complete SDU scheduled for tx. Stack latency: %ld us\n", rrc->get_rb_name(lcid).c_str(), tx_sdu->get_latency_us()); + pool->deallocate(tx_sdu); tx_sdu = NULL; } @@ -360,14 +374,16 @@ int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes) header.fi |= RLC_FI_FIELD_NOT_END_ALIGNED; // Last byte does not correspond to last byte of SDU // Set SN + header.sn = vt_us; vt_us = (vt_us + 1)%cfg.tx_mod; // Add header and TX - log->debug("%s packing PDU with length %d\n", rrc->get_rb_name(lcid).c_str(), pdu->N_bytes); + log->debug("%s packing PDU with length %d\n", rb_name().c_str(), pdu->N_bytes); rlc_um_write_data_pdu_header(&header, pdu); memcpy(payload, pdu->msg, pdu->N_bytes); uint32_t ret = pdu->N_bytes; + log->debug("%s returning length %d\n", rrc->get_rb_name(lcid).c_str(), pdu->N_bytes); pool->deallocate(pdu); @@ -382,20 +398,20 @@ void rlc_um::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes) rlc_um_read_data_pdu_header(payload, nof_bytes, cfg.rx_sn_field_length, &header); log->info_hex(payload, nof_bytes, "RX %s Rx data PDU SN: %d", - rrc->get_rb_name(lcid).c_str(), header.sn); + rb_name().c_str(), header.sn); if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_uh-cfg.rx_window_size) && RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ur)) { log->info("%s SN: %d outside rx window [%d:%d] - discarding\n", - rrc->get_rb_name(lcid).c_str(), header.sn, vr_ur, vr_uh); + rb_name().c_str(), header.sn, vr_ur, vr_uh); return; } it = rx_window.find(header.sn); if(rx_window.end() != it) { log->info("%s Discarding duplicate SN: %d\n", - rrc->get_rb_name(lcid).c_str(), header.sn); + rb_name().c_str(), header.sn); return; } @@ -486,9 +502,13 @@ void rlc_um::reassemble_rx_sdus() log->warning("Dropping remainder of lost PDU (lower edge middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)\n", vr_ur, vr_ur_in_rx_sdu); rx_sdu->reset(); } else { - log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d (lower edge middle segments)", rrc->get_rb_name(lcid).c_str(), vr_ur, i); + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d (lower edge middle segments)", rb_name().c_str(), vr_ur, i); rx_sdu->set_timestamp(); - pdcp->write_pdu(lcid, rx_sdu); + if(cfg.is_mrb){ + pdcp->write_pdu_mch(lcid, rx_sdu); + } else { + pdcp->write_pdu(lcid, rx_sdu); + } rx_sdu = pool_allocate; if (!rx_sdu) { log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); @@ -499,6 +519,7 @@ void rlc_um::reassemble_rx_sdus() } // Handle last segment + if (rx_sdu->N_bytes > 0 || rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { log->debug("Writing last segment in SDU buffer. Lower edge vr_ur=%d, Buffer size=%d, segment size=%d\n", vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes); @@ -514,7 +535,11 @@ void rlc_um::reassemble_rx_sdus() } else { log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (lower edge last segments)", rrc->get_rb_name(lcid).c_str(), vr_ur); rx_sdu->set_timestamp(); - pdcp->write_pdu(lcid, rx_sdu); + if(cfg.is_mrb){ + pdcp->write_pdu_mch(lcid, rx_sdu); + } else { + pdcp->write_pdu(lcid, rx_sdu); + } rx_sdu = pool_allocate; if (!rx_sdu) { log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); @@ -569,9 +594,13 @@ void rlc_um::reassemble_rx_sdus() log->warning("Dropping remainder of lost PDU (update vr_ur middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)\n", vr_ur, vr_ur_in_rx_sdu); rx_sdu->reset(); } else { - log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments)", rrc->get_rb_name(lcid).c_str(), vr_ur, i); + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments)", rb_name().c_str(), vr_ur, i); rx_sdu->set_timestamp(); - pdcp->write_pdu(lcid, rx_sdu); + if(cfg.is_mrb){ + pdcp->write_pdu_mch(lcid, rx_sdu); + } else { + pdcp->write_pdu(lcid, rx_sdu); + } rx_sdu = pool_allocate; if (!rx_sdu) { log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); @@ -608,9 +637,13 @@ void rlc_um::reassemble_rx_sdus() log->warning("Dropping remainder of lost PDU (update vr_ur last segments)\n"); rx_sdu->reset(); } else { - log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (update vr_ur last segments)", rrc->get_rb_name(lcid).c_str(), vr_ur); + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (update vr_ur last segments)", rb_name().c_str(), vr_ur); rx_sdu->set_timestamp(); - pdcp->write_pdu(lcid, rx_sdu); + if(cfg.is_mrb){ + pdcp->write_pdu_mch(lcid, rx_sdu); + } else { + pdcp->write_pdu(lcid, rx_sdu); + } rx_sdu = pool_allocate; if (!rx_sdu) { log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); @@ -632,6 +665,9 @@ clean_up_rx_window: bool rlc_um::inside_reordering_window(uint16_t sn) { + if(cfg.rx_window_size == 0) { + return true; + } if(RX_MOD_BASE(sn) >= RX_MOD_BASE(vr_uh-cfg.rx_window_size) && RX_MOD_BASE(sn) < RX_MOD_BASE(vr_uh)) { @@ -644,8 +680,18 @@ bool rlc_um::inside_reordering_window(uint16_t sn) void rlc_um::debug_state() { log->debug("%s vt_us = %d, vr_ur = %d, vr_ux = %d, vr_uh = %d \n", - rrc->get_rb_name(lcid).c_str(), vt_us, vr_ur, vr_ux, vr_uh); + rb_name().c_str(), vt_us, vr_ur, vr_ux, vr_uh); + +} +std::string rlc_um::rb_name() { + if(cfg.is_mrb) { + std::stringstream ss; + ss << "MRB" << lcid; + return ss.str(); + } else { + return rrc->get_rb_name(lcid); + } } /**************************************************************************** @@ -662,7 +708,6 @@ void rlc_um_read_data_pdu_header(uint8_t *payload, uint32_t nof_bytes, rlc_umd_s { uint8_t ext; uint8_t *ptr = payload; - // Fixed part if(RLC_UMD_SN_SIZE_5_BITS == sn_size) { @@ -709,7 +754,6 @@ void rlc_um_write_data_pdu_header(rlc_umd_pdu_header_t *header, byte_buffer_t *p { uint32_t i; uint8_t ext = (header->N_li > 0) ? 1 : 0; - // Make room for the header uint32_t len = rlc_um_packed_length(header); pdu->msg -= len; diff --git a/lib/test/asn1/CMakeLists.txt b/lib/test/asn1/CMakeLists.txt index 6c52d1972..0f59d0417 100644 --- a/lib/test/asn1/CMakeLists.txt +++ b/lib/test/asn1/CMakeLists.txt @@ -18,6 +18,10 @@ # and at http://www.gnu.org/licenses/. # -add_executable(rrc_meas_test rrc_meas_test.cc) -target_link_libraries(rrc_meas_test srslte_common srslte_phy srslte_asn1) -add_test(rrc_meas_test rrc_meas_test) +add_executable(srslte_asn1_rrc_mcch_test srslte_asn1_rrc_mcch_test.cc) +target_link_libraries(srslte_asn1_rrc_mcch_test srslte_asn1 srslte_common) +add_test(srslte_asn1_rrc_mcch_test srslte_asn1_rrc_mcch_test) + +add_executable(srslte_asn1_rrc_meas_test srslte_asn1_rrc_meas_test.cc) +target_link_libraries(srslte_asn1_rrc_meas_test srslte_common srslte_phy srslte_asn1) +add_test(srslte_asn1_rrc_meas_test srslte_asn1_rrc_meas_test) diff --git a/lib/test/asn1/srslte_asn1_rrc_mcch_test.cc b/lib/test/asn1/srslte_asn1_rrc_mcch_test.cc new file mode 100644 index 000000000..c3a4035c0 --- /dev/null +++ b/lib/test/asn1/srslte_asn1_rrc_mcch_test.cc @@ -0,0 +1,160 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE 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 Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include "srslte/asn1/liblte_rrc.h" +#include "srslte/common/log_filter.h" +#include "srslte/phy/utils/bit.h" + +void pack_test() +{ + srslte::log_filter log1("RRC"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(1024); + + uint32_t known_reference_len = 30; + uint8_t known_reference[256] = {0x0d, 0x8f, 0xdf, 0xff, 0xff, 0xff, 0xe2, 0x2f, + 0xfc, 0x38, 0x5e, 0x61, 0xec, 0xa8, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x20, 0x05, 0xe6, 0x1e, + 0xca, 0x80, 0x00, 0x00, 0x40, 0x42}; + + LIBLTE_BYTE_MSG_STRUCT byte_buf; + LIBLTE_BIT_MSG_STRUCT bit_buf; + LIBLTE_RRC_MCCH_MSG_STRUCT mcch_msg; + + mcch_msg.commonsf_allocpatternlist_r9_size = 2; + mcch_msg.commonsf_allocpatternlist_r9[0].radio_fr_alloc_offset = 4; + mcch_msg.commonsf_allocpatternlist_r9[0].radio_fr_alloc_period = LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N32; + mcch_msg.commonsf_allocpatternlist_r9[0].subfr_alloc_num_frames = LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE; + mcch_msg.commonsf_allocpatternlist_r9[0].subfr_alloc = 0x3F; + mcch_msg.commonsf_allocpatternlist_r9[1].radio_fr_alloc_offset = 7; + mcch_msg.commonsf_allocpatternlist_r9[1].radio_fr_alloc_period = LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N8; + mcch_msg.commonsf_allocpatternlist_r9[1].subfr_alloc_num_frames = LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_FOUR; + mcch_msg.commonsf_allocpatternlist_r9[1].subfr_alloc = 0xFFFFFF; + + mcch_msg.commonsf_allocperiod_r9 = LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF256; + + mcch_msg.pmch_infolist_r9_size = 2; + + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size = 1; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].logicalchannelid_r9 = 1; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9_present = true; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9 = 1; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_explicit = true; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mcc = 0xF987; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mnc = 0xF654; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.serviceid_r9 = 1; + mcch_msg.pmch_infolist_r9[0].pmch_config_r9.datamcs_r9 = 16; + mcch_msg.pmch_infolist_r9[0].pmch_config_r9.mch_schedulingperiod_r9 = LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF1024; + mcch_msg.pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9 = 1535; + + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9_size = 1; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].logicalchannelid_r9 = 2; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].sessionid_r9_present = true; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].sessionid_r9 = 2; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_explicit = true; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mcc = 0xF987; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mnc = 0xF654; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.serviceid_r9 = 2; + mcch_msg.pmch_infolist_r9[1].pmch_config_r9.datamcs_r9 = 8; + mcch_msg.pmch_infolist_r9[1].pmch_config_r9.mch_schedulingperiod_r9 = LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF8; + mcch_msg.pmch_infolist_r9[1].pmch_config_r9.sf_alloc_end_r9 = 0; + + liblte_rrc_pack_mcch_msg(&mcch_msg, &bit_buf); + liblte_pack(bit_buf.msg, bit_buf.N_bits, byte_buf.msg); + byte_buf.N_bytes = (bit_buf.N_bits+7)/8; + + //log1.info_hex(byte_buf.msg, byte_buf.N_bytes, "MCCH packed message:"); + + assert(byte_buf.N_bytes == known_reference_len); + for(uint32 i=0; i +#include +#include +#include "srslte/common/log_filter.h" +#include "srslte/asn1/liblte_rrc.h" + + +void basic_test() { + srslte::log_filter log1("RRC"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(128); + + LIBLTE_BIT_MSG_STRUCT bit_buf; + LIBLTE_BIT_MSG_STRUCT bit_buf2; + LIBLTE_BYTE_MSG_STRUCT byte_buf; + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + + uint32_t rrc_message_len = 18; + uint8_t rrc_message[] = {0x08, 0x10, 0x49, 0x3C, 0x0D, 0x97, 0x89, 0x83, + 0xC0, 0x84, 0x20, 0x82, 0x08, 0x21, 0x00, 0x01, + 0xBC, 0x48}; + + srslte_bit_unpack_vector(rrc_message, bit_buf.msg, rrc_message_len*8); + bit_buf.N_bits = rrc_message_len*8; + liblte_rrc_unpack_ul_dcch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &ul_dcch_msg); + + assert(ul_dcch_msg.msg_type == LIBLTE_RRC_UL_DCCH_MSG_TYPE_MEASUREMENT_REPORT); + LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *rep = &ul_dcch_msg.msg.measurement_report; + assert(rep->meas_id == 1); + assert(rep->pcell_rsrp_result == 73); + assert(rep->pcell_rsrq_result == 15); + assert(rep->have_meas_result_neigh_cells); + assert(rep->meas_result_neigh_cells_choice == LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA); + LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA_STRUCT *eutra = &rep->meas_result_neigh_cells.eutra; + assert(eutra->n_result == 1); + assert(eutra->result_eutra_list[0].phys_cell_id == 357); + assert(eutra->result_eutra_list[0].have_cgi_info); + assert(eutra->result_eutra_list[0].cgi_info.have_plmn_identity_list); + assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.plmn_id.mcc == 0xF898); + assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.plmn_id.mnc == 0xFF78); + assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.cell_id == 0x1084104); + assert(eutra->result_eutra_list[0].cgi_info.tracking_area_code == 0x1042); + assert(eutra->result_eutra_list[0].cgi_info.have_plmn_identity_list); + assert(eutra->result_eutra_list[0].cgi_info.n_plmn_identity_list == 1); + assert(eutra->result_eutra_list[0].cgi_info.plmn_identity_list[0].mcc == 0xFFFF); + assert(eutra->result_eutra_list[0].cgi_info.plmn_identity_list[0].mnc == 0xFF00); + assert(eutra->result_eutra_list[0].meas_result.have_rsrp); + assert(eutra->result_eutra_list[0].meas_result.rsrp_result == 60); + assert(eutra->result_eutra_list[0].meas_result.have_rsrp); + assert(eutra->result_eutra_list[0].meas_result.rsrq_result == 18); + + liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf2); + srslte_bit_pack_vector(bit_buf2.msg, byte_buf.msg, bit_buf2.N_bits); + byte_buf.N_bytes = (bit_buf2.N_bits+7)/8; + log1.info_hex(byte_buf.msg, byte_buf.N_bytes, "UL_DCCH Packed message\n"); + + for(uint32_t i=0; iN_bytes == 1); + assert(*(tester.sdus[i]->msg) == i); + } +} + + + // This test checks the reassembly routines when a PDU // is lost that contains the beginning of SDU segment. // The PDU that contains the end of this SDU _also_ contains // a segment of another SDU. @@ -460,13 +531,14 @@ void reassmble_test2() } } - int main(int argc, char **argv) { basic_test(); byte_buffer_pool::get_instance()->cleanup(); loss_test(); byte_buffer_pool::get_instance()->cleanup(); + basic_mbsfn_test(); + byte_buffer_pool::get_instance()->cleanup(); reassmble_test(); byte_buffer_pool::get_instance()->cleanup(); @@ -474,3 +546,4 @@ int main(int argc, char **argv) { reassmble_test2(); byte_buffer_pool::get_instance()->cleanup(); } + diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index 9b62d80ca..935d3fe50 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -157,6 +157,7 @@ nof_ctrl_symbols = 3 #link_failure_nof_err = 50 #rrc_inactivity_timer = 10000 #max_prach_offset_us = 30 +#enable_mbsfn = false ##################################################################### # Manual RF calibration diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h index 3ab6e9a4c..93e9436d9 100644 --- a/srsenb/hdr/enb.h +++ b/srsenb/hdr/enb.h @@ -126,6 +126,7 @@ typedef struct { mac_args_t mac; uint32_t rrc_inactivity_timer; float metrics_period_secs; + bool enable_mbsfn; bool print_buffer_state; }expert_args_t; @@ -213,6 +214,7 @@ private: int parse_sib3(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *data); int parse_sib4(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data); int parse_sib9(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *data); + int parse_sib13(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *data); int parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_common); int parse_rr(all_args_t *args, rrc_cfg_t *rrc_cfg); int parse_drb(all_args_t *args, rrc_cfg_t *rrc_cfg); diff --git a/srsenb/hdr/mac/mac.h b/srsenb/hdr/mac/mac.h index 5f541b443..61aa033fd 100644 --- a/srsenb/hdr/mac/mac.h +++ b/srsenb/hdr/mac/mac.h @@ -83,7 +83,8 @@ public: int get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res); int get_ul_sched(uint32_t tti, ul_sched_t *ul_sched_res); - + int get_mch_sched(bool is_mcch, dl_sched_t *dl_sched_res); + void build_mch_sched(uint32_t tbs); void rl_failure(uint16_t rnti); void rl_ok(uint16_t rnti); void tti_clock(); @@ -114,7 +115,7 @@ public: uint32_t get_current_tti(); void get_metrics(mac_metrics_t metrics[ENB_METRICS_MAX_USERS]); - + void write_mcch(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13, LIBLTE_RRC_MCCH_MSG_STRUCT *mcch); private: static const int MAX_LOCATIONS = 20; @@ -141,7 +142,12 @@ private: sched scheduler; dl_metric_rr sched_metric_dl_rr; ul_metric_rr sched_metric_ul_rr; - + sched_interface::cell_cfg_t cell_config; + + + sched_interface::dl_pdu_mch_t mch; + + /* Map of active UEs */ std::map ue_db; uint16_t last_rnti; @@ -171,6 +177,16 @@ private: srslte_softbuffer_tx_t pcch_softbuffer_tx; srslte_softbuffer_tx_t rar_softbuffer_tx; + const static int mcch_payload_len = 3000; //TODO FIND OUT MAX LENGTH + int current_mcch_length; + uint8_t mcch_payload_buffer[mcch_payload_len]; + LIBLTE_RRC_MCCH_MSG_STRUCT mcch; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT sib13; + + const static int mtch_payload_len = 10000; + uint8_t mtch_payload_buffer[mtch_payload_len]; + /* Functions for MAC Timers */ srslte::timers timers_db; void setup_timers(); diff --git a/srsenb/hdr/mac/ue.h b/srsenb/hdr/mac/ue.h index 165c1ea3e..e74c224b5 100644 --- a/srsenb/hdr/mac/ue.h +++ b/srsenb/hdr/mac/ue.h @@ -43,7 +43,7 @@ class ue : public srslte::read_pdu_interface, { public: - ue() : mac_msg_dl(20), mac_msg_ul(20), conres_id_available(false), + ue() : mac_msg_dl(20), mch_mac_msg_dl(10), mac_msg_ul(20), conres_id_available(false), dl_ri_counter(0), dl_pmi_counter(0), conres_id(0), @@ -89,6 +89,7 @@ public: void config(uint16_t rnti, uint32_t nof_prb, sched_interface *sched, rrc_interface_mac *rrc_, rlc_interface_mac *rlc, srslte::log *log_h); uint8_t* generate_pdu(uint32_t tb_idx, sched_interface::dl_sched_pdu_t pdu[sched_interface::MAX_RLC_PDU_LIST], uint32_t nof_pdu_elems, uint32_t grant_size); + uint8_t* generate_mch_pdu(sched_interface::dl_pdu_mch_t sched, uint32_t nof_pdu_elems, uint32_t grant_size); srslte_softbuffer_tx_t* get_tx_softbuffer(uint32_t harq_process, uint32_t tb_idx); srslte_softbuffer_rx_t* get_rx_softbuffer(uint32_t tti); @@ -114,9 +115,9 @@ public: bool is_phy_added; - + int read_pdu(uint32_t lcid, uint8_t *payload, uint32_t requested_bytes); private: - int read_pdu(uint32_t lcid, uint8_t *payload, uint32_t requested_bytes); + void allocate_sdu(srslte::sch_pdu *pdu, uint32_t lcid, uint32_t sdu_len); bool process_ce(srslte::sch_subh *subh); void allocate_ce(srslte::sch_pdu *pdu, uint32_t lcid); @@ -152,6 +153,7 @@ private: // For UL there are multiple buffers per PID and are managed by pdu_queue srslte::pdu_queue pdus; srslte::sch_pdu mac_msg_dl, mac_msg_ul; + srslte::mch_pdu mch_mac_msg_dl; rlc_interface_mac *rlc; rrc_interface_mac* rrc; diff --git a/srsenb/hdr/phy/phch_common.h b/srsenb/hdr/phy/phch_common.h index 6adc926f3..8cebcb042 100644 --- a/srsenb/hdr/phy/phch_common.h +++ b/srsenb/hdr/phy/phch_common.h @@ -30,12 +30,12 @@ #include #include "srslte/interfaces/enb_interfaces.h" #include "srslte/interfaces/enb_metrics_interface.h" - +#include "srslte/common/gen_mch_tables.h" #include "srslte/common/log.h" #include "srslte/common/threads.h" #include "srslte/common/thread_pool.h" #include "srslte/radio/radio.h" - +#include namespace srsenb { typedef struct { @@ -48,6 +48,26 @@ typedef struct { bool pregenerate_signals; } phy_args_t; +typedef enum{ + SUBFRAME_TYPE_REGULAR = 0, + SUBFRAME_TYPE_MBSFN, + SUBFRAME_TYPE_N_ITEMS, +} subframe_type_t; +static const char subframe_type_text[SUBFRAME_TYPE_N_ITEMS][20] = {"Regular", "MBSFN"}; + +/* Subframe config */ + +typedef struct { + subframe_type_t sf_type; + uint8_t mbsfn_area_id; + uint8_t non_mbsfn_region_length; + uint8_t mbsfn_mcs; + bool mbsfn_encode; + bool is_mcch; +} subframe_cfg_t; + + + class phch_common { public: @@ -119,6 +139,12 @@ public: srslte_mod_t ue_db_get_last_ul_mod(uint16_t rnti, uint32_t tti); void ue_db_set_last_ul_tbs(uint16_t rnti, uint32_t tti, int tbs); int ue_db_get_last_ul_tbs(uint16_t rnti, uint32_t tti); + + void configure_mbsfn(phy_interface_rrc::phy_cfg_mbsfn_t *cfg); + void build_mch_table(); + void build_mcch_table(); + void get_sf_config(subframe_cfg_t *cfg, uint32_t phy_tti); + private: std::vector tx_mutex; @@ -129,6 +155,17 @@ private: uint32_t nof_mutex; uint32_t max_mutex; + phy_interface_rrc::phy_cfg_mbsfn_t mbsfn; + bool sib13_configured; + bool mcch_configured; + uint8_t mch_table[40]; + uint8_t mcch_table[10]; + + uint8_t mch_sf_idx_lut[10]; + bool is_mch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti); + bool is_mcch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti); + + }; } // namespace srsenb diff --git a/srsenb/hdr/phy/phch_worker.h b/srsenb/hdr/phy/phch_worker.h index 9821a2d1e..a04c9232e 100644 --- a/srsenb/hdr/phy/phch_worker.h +++ b/srsenb/hdr/phy/phch_worker.h @@ -76,6 +76,7 @@ private: void work_imp(); int encode_pdsch(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants); + int encode_pmch(srslte_enb_dl_pdsch_t *grant, srslte_ra_dl_grant_t *phy_grant); int decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch); int encode_phich(srslte_enb_dl_phich_t *acks, uint32_t nof_acks); int encode_pdcch_dl(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants); @@ -96,7 +97,7 @@ private: uint32_t t_rx, t_tx_dl, t_tx_ul; srslte_enb_dl_t enb_dl; srslte_enb_ul_t enb_ul; - + srslte_softbuffer_tx_t temp_mbsfn_softbuffer; srslte_timestamp_t tx_time; // Class to store user information diff --git a/srsenb/hdr/phy/phy.h b/srsenb/hdr/phy/phy.h index 74b28be11..d20079135 100644 --- a/srsenb/hdr/phy/phy.h +++ b/srsenb/hdr/phy/phy.h @@ -49,6 +49,7 @@ typedef struct { LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT srs_ul_cnfg; } phy_cfg_t; + class phy : public phy_interface_mac, public phy_interface_rrc { @@ -62,6 +63,9 @@ public: /* MAC->PHY interface */ int add_rnti(uint16_t rnti); void rem_rnti(uint16_t rnti); + + /*RRC-PHY interface*/ + void configure_mbsfn(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13, LIBLTE_RRC_MCCH_MSG_STRUCT mcch); static uint32_t tti_to_SFN(uint32_t tti); static uint32_t tti_to_subf(uint32_t tti); @@ -73,7 +77,7 @@ public: void get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]); private: - + phy_rrc_cfg_t phy_rrc_config; uint32_t nof_workers; const static int MAX_WORKERS = 4; @@ -84,7 +88,7 @@ private: const static int WORKERS_THREAD_PRIO = 0; srslte::radio *radio_handler; - + srslte::log *log_h; srslte::thread_pool workers_pool; std::vector workers; phch_common workers_common; diff --git a/srsenb/hdr/upper/gtpu.h b/srsenb/hdr/upper/gtpu.h index 892068757..c097b190a 100644 --- a/srsenb/hdr/upper/gtpu.h +++ b/srsenb/hdr/upper/gtpu.h @@ -89,11 +89,15 @@ private: srslte::byte_buffer_pool *pool; bool running; bool run_enable; + + bool mch_running; + bool mch_run_enable; std::string gtp_bind_addr; std::string mme_addr; srsenb::pdcp_interface_gtpu *pdcp; srslte::log *gtpu_log; + pthread_t mch_thread; typedef struct{ uint32_t teids_in[SRSENB_N_RADIO_BEARERS]; @@ -105,9 +109,22 @@ private: // Socket file descriptors int snk_fd; int src_fd; + int m1u_sd; + + //Init functions + bool init_m1u(srslte::log *gtpu_log_); + //Threading void run_thread(); - + void run_mch_thread(); + + int mch_lcid_counter; + + static void *mch_thread_routine(void *_this) + { + ((srsenb::gtpu*)_this)->run_mch_thread(); + return _this; + } pthread_mutex_t mutex; /**************************************************************************** diff --git a/srsenb/hdr/upper/pdcp.h b/srsenb/hdr/upper/pdcp.h index 45907e1f8..c249b856e 100644 --- a/srsenb/hdr/upper/pdcp.h +++ b/srsenb/hdr/upper/pdcp.h @@ -44,7 +44,8 @@ public: void stop(); // pdcp_interface_rlc - void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu); + void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu); + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu){} // pdcp_interface_rrc void reset(uint16_t rnti); @@ -77,7 +78,8 @@ private: uint16_t rnti; srsenb::gtpu_interface_pdcp *gtpu; // gw_interface_pdcp - void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu); + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu); + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu){} }; class user_interface_rrc : public srsue::rrc_interface_pdcp @@ -90,6 +92,7 @@ private: void write_pdu_bcch_bch(srslte::byte_buffer_t *pdu); void write_pdu_bcch_dlsch(srslte::byte_buffer_t *pdu); void write_pdu_pcch(srslte::byte_buffer_t *pdu); + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu){} std::string get_rb_name(uint32_t lcid); }; diff --git a/srsenb/hdr/upper/rlc.h b/srsenb/hdr/upper/rlc.h index 98c3edfb3..2b0752cf1 100644 --- a/srsenb/hdr/upper/rlc.h +++ b/srsenb/hdr/upper/rlc.h @@ -32,6 +32,13 @@ #ifndef SRSENB_RLC_H #define SRSENB_RLC_H +typedef struct { + uint32_t lcid; + uint32_t plmn; + uint16_t mtch_stop; + uint8_t *payload; +}mch_service_t; + namespace srsenb { class rlc : public rlc_interface_mac, @@ -51,6 +58,7 @@ public: void rem_user(uint16_t rnti); void add_bearer(uint16_t rnti, uint32_t lcid); void add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_rlc_config_t cnfg); + void add_bearer_mrb(uint16_t rnti, uint32_t lcid); // rlc_interface_pdcp void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu); @@ -73,7 +81,8 @@ private: void write_pdu(uint32_t lcid, srslte::byte_buffer_t *sdu); void write_pdu_bcch_bch(srslte::byte_buffer_t *sdu); void write_pdu_bcch_dlsch(srslte::byte_buffer_t *sdu); - void write_pdu_pcch(srslte::byte_buffer_t *sdu); + void write_pdu_pcch(srslte::byte_buffer_t *sdu); + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu){} void max_retx_attempted(); std::string get_rb_name(uint32_t lcid); uint16_t rnti; @@ -85,7 +94,8 @@ private: }; std::map users; - + std::vector mch_services; + mac_interface_rlc *mac; pdcp_interface_rlc *pdcp; rrc_interface_rlc *rrc; diff --git a/srsenb/hdr/upper/rrc.h b/srsenb/hdr/upper/rrc.h index 871c9ebb3..b2b0b37b8 100644 --- a/srsenb/hdr/upper/rrc.h +++ b/srsenb/hdr/upper/rrc.h @@ -78,6 +78,7 @@ typedef struct { typedef struct { LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_STRUCT sibs[LIBLTE_RRC_MAX_SIB]; LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT mac_cnfg; + LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT pusch_cfg; LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT antenna_info; LIBLTE_RRC_PDSCH_CONFIG_P_A_ENUM pdsch_cfg; @@ -85,6 +86,7 @@ typedef struct { rrc_cfg_cqi_t cqi_cfg; rrc_cfg_qci_t qci_cfg[MAX_NOF_QCI]; srslte_cell_t cell; + bool enable_mbsfn; uint32_t inactivity_timeout_ms; }rrc_cfg_t; @@ -138,6 +140,9 @@ public: void stop(); void get_metrics(rrc_metrics_t &m); + //rrc_interface_phy + void configure_mbsfn_sibs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13); + // rrc_interface_mac void rl_failure(uint16_t rnti); void add_user(uint16_t rnti); @@ -351,7 +356,8 @@ private: sr_sched_t sr_sched; sr_sched_t cqi_sched; - + LIBLTE_RRC_MCCH_MSG_STRUCT mcch; + bool enable_mbms; rrc_cfg_t cfg; uint32_t nof_si_messages; LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2; diff --git a/srsenb/sib.conf.example b/srsenb/sib.conf.example index f23d18981..02ad7c776 100644 --- a/srsenb/sib.conf.example +++ b/srsenb/sib.conf.example @@ -111,6 +111,17 @@ sib2 = additional_spectrum_emission = 1; }; + mbsfnSubframeConfigList = + { + radioframeAllocationPeriod = "1"; + subframeAllocationNumFrames = "1"; + radioframeAllocationOffset = 0; + subframeAllocation = 63; + + }; + + mbsfnSubframeConfigListLength = 0; + time_alignment_timer = "INFINITY"; // use "sf500", "sf750", etc. }; diff --git a/srsenb/src/enb.cc b/srsenb/src/enb.cc index fdf959a95..2f22a8e5e 100644 --- a/srsenb/src/enb.cc +++ b/srsenb/src/enb.cc @@ -206,6 +206,7 @@ bool enb::init(all_args_t *args_) return false; } rrc_cfg.inactivity_timeout_ms = args->expert.rrc_inactivity_timer; + rrc_cfg.enable_mbsfn = args->expert.enable_mbsfn; // Copy cell struct to rrc and phy memcpy(&rrc_cfg.cell, &cell_cfg, sizeof(srslte_cell_t)); diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index af2ade941..b4a4e67fb 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -210,7 +210,39 @@ int enb::parse_sib2(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUC ("time_alignment_timer", &data->time_alignment_timer, liblte_rrc_time_alignment_timer_text, LIBLTE_RRC_TIME_ALIGNMENT_TIMER_N_ITEMS) ); - + + + sib2.add_field( + new parser::field + ("mbsfnSubframeConfigListLength", &data->mbsfn_subfr_cnfg_list_size) + ); + + + parser::section mbsfnSubframeConfigList("mbsfnSubframeConfigList"); + sib2.add_subsection(&mbsfnSubframeConfigList); + + mbsfnSubframeConfigList.add_field( + new parser::field + ("subframeAllocation", &data->mbsfn_subfr_cnfg_list[0].subfr_alloc) + ); + + mbsfnSubframeConfigList.add_field( + new parser::field + ("radioframeAllocationOffset", &data->mbsfn_subfr_cnfg_list[0].radio_fr_alloc_offset) + ); + + mbsfnSubframeConfigList.add_field( + new parser::field_enum_str + ("subframeAllocationNumFrames", &data->mbsfn_subfr_cnfg_list[0].subfr_alloc_num_frames, + liblte_rrc_subframe_allocation_num_frames_text,LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_N_ITEMS) + ); + + mbsfnSubframeConfigList.add_field( + new parser::field_enum_str + ("radioframeAllocationPeriod", &data->mbsfn_subfr_cnfg_list[0].radio_fr_alloc_period, + liblte_rrc_radio_frame_allocation_period_text, LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N_ITEMS) + ); + parser::section freqinfo("freqInfo"); sib2.add_subsection(&freqinfo); freqinfo.add_field( @@ -701,6 +733,7 @@ uint32_t HexToBytes(const std::string& str, uint8_t *char_value, uint32_t buff_l return i/2; } + int enb::parse_sib9(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *data) { parser::section sib9("sib9"); @@ -729,6 +762,86 @@ int enb::parse_sib9(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUC } } +int enb::parse_sib13(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *data) +{ + parser::section sib13("sib13"); + + sib13.add_field( + new parser::field + ("mbsfn_area_info_list_size", &data->mbsfn_area_info_list_r9_size) + ); + + parser::section mbsfn_notification_config("mbsfn_notification_config"); + sib13.add_subsection(&mbsfn_notification_config); + + + mbsfn_notification_config.add_field( + new parser::field_enum_str + ("mbsfn_notification_repetition_coeff", &data->mbsfn_notification_config.repetition_coeff, liblte_rrc_notification_repetition_coeff_r9_text,LIBLTE_RRC_NOTIFICATION_REPETITION_COEFF_R9_N_ITEMS) + ); + + mbsfn_notification_config.add_field( + new parser::field + ("mbsfn_notification_offset", &data->mbsfn_notification_config.offset) + ); + + mbsfn_notification_config.add_field( + new parser::field + ("mbsfn_notification_sf_index", &data->mbsfn_notification_config.sf_index) + ); + + + parser::section mbsfn_area_info_list("mbsfn_area_info_list"); + sib13.add_subsection(&mbsfn_area_info_list); + + mbsfn_area_info_list.add_field( + new parser::field_enum_str + ("non_mbsfn_region_length", &data->mbsfn_area_info_list_r9[0].non_mbsfn_region_length, + liblte_rrc_non_mbsfn_region_length_text,LIBLTE_RRC_NON_MBSFN_REGION_LENGTH_N_ITEMS) + ); + + mbsfn_area_info_list.add_field( + new parser::field_enum_str + ("mcch_repetition_period", &data->mbsfn_area_info_list_r9[0].mcch_repetition_period_r9, + liblte_rrc_mcch_repetition_period_r9_text,LIBLTE_RRC_MCCH_REPETITION_PERIOD_N_ITEMS) + ); + + + mbsfn_area_info_list.add_field( + new parser::field_enum_str + ("mcch_modification_period", &data->mbsfn_area_info_list_r9[0].mcch_modification_period_r9, + liblte_rrc_mcch_modification_period_r9_text,LIBLTE_RRC_MCCH_MODIFICATION_PERIOD_N_ITEMS) + ); + + mbsfn_area_info_list.add_field( + new parser::field_enum_str + ("signalling_mcs", &data->mbsfn_area_info_list_r9[0].signalling_mcs_r9, + liblte_rrc_mcch_signalling_mcs_r9_text,LIBLTE_RRC_MCCH_SIGNALLING_MCS_N_ITEMS) + ); + + + mbsfn_area_info_list.add_field( + new parser::field + ("mbsfn_area_id", &data->mbsfn_area_info_list_r9[0].mbsfn_area_id_r9) + ); + + mbsfn_area_info_list.add_field( + new parser::field + ("notification_indicator", &data->mbsfn_area_info_list_r9[0].notification_indicator_r9) + ); + + mbsfn_area_info_list.add_field( + new parser::field + ("mcch_offset", &data->mbsfn_area_info_list_r9[0].mcch_offset_r9) + ); + + mbsfn_area_info_list.add_field( + new parser::field + ("sf_alloc_info", &data->mbsfn_area_info_list_r9[0].sf_alloc_info_r9) + ); + return parser::parse_section(filename, &sib13); +} + int enb::parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_common) { LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1 = &rrc_cfg->sibs[0].sib.sib1; @@ -741,7 +854,8 @@ int enb::parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_ rrc_cfg->sibs[3].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4; LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *sib9 = &rrc_cfg->sibs[8].sib.sib9; rrc_cfg->sibs[8].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9; - + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13 = &rrc_cfg->sibs[12].sib.sib13; + rrc_cfg->sibs[12].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13; // Read SIB1 configuration from file bzero(sib1, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT)); @@ -765,7 +879,7 @@ int enb::parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_ if (parse_sib2(args->enb_files.sib_config, sib2)) { return -1; } - + // SRS not yet supported sib2->rr_config_common_sib.srs_ul_cnfg.present = false; if (sib2->ul_bw.present) { @@ -817,7 +931,13 @@ int enb::parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_ return -1; } } - + + if (sib_is_present(sib1->sched_info, sib1->N_sched_info, LIBLTE_RRC_SIB_TYPE_13_v920)) { + bzero(sib13, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT)); + if (parse_sib13(args->enb_files.sib_config, sib13)) { + return -1; + } + } // Copy PHY common configuration bzero(phy_config_common, sizeof(phy_cfg_t)); memcpy(&phy_config_common->prach_cnfg, &sib2->rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT)); diff --git a/srsenb/src/mac/mac.cc b/srsenb/src/mac/mac.cc index ba0e2f1e7..38c6ac43a 100644 --- a/srsenb/src/mac/mac.cc +++ b/srsenb/src/mac/mac.cc @@ -149,10 +149,19 @@ void mac::start_pcap(srslte::mac_pcap* pcap_) *******************************************************/ int mac::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) { - if (ue_db.count(rnti)) { - return scheduler.dl_rlc_buffer_state(rnti, lc_id, tx_queue, retx_queue); + if (ue_db.count(rnti)) { + if(rnti != SRSLTE_MRNTI){ + return scheduler.dl_rlc_buffer_state(rnti, lc_id, tx_queue, retx_queue); + } else { + for(uint32_t i = 0; i < mch.num_mtch_sched; i++){ + if(lc_id == mch.mtch_sched[i].lcid){ + mch.mtch_sched[i].lcid_buffer_size = tx_queue; + } + } + return 0; + } } else { - Error("User rnti=0x%x not found\n", rnti); + Error("User rnti=0x%x not found- this\n", rnti); return -1; } } @@ -230,6 +239,7 @@ int mac::ue_rem(uint16_t rnti) int mac::cell_cfg(sched_interface::cell_cfg_t* cell_cfg) { + memcpy(&this->cell_config, cell_cfg, sizeof(sched_interface::cell_cfg_t)); return scheduler.cell_cfg(cell_cfg); } @@ -238,14 +248,14 @@ void mac::get_metrics(mac_metrics_t metrics[ENB_METRICS_MAX_USERS]) int cnt=0; for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { ue *u = iter->second; - u->metrics_read(&metrics[cnt]); - cnt++; + if(iter->first != SRSLTE_MRNTI) { + u->metrics_read(&metrics[cnt]); + cnt++; + } } } - - /******************************************************** * * PHY interface @@ -439,7 +449,6 @@ int mac::rach_detected(uint32_t tti, uint32_t preamble_idx, uint32_t time_adv) // Register new user in RRC rrc_h->add_user(last_rnti); - // Trigger scheduler RACH scheduler.dl_rach_info(tti, ra_id, last_rnti, 7); @@ -578,6 +587,96 @@ int mac::get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res) return SRSLTE_SUCCESS; } +void mac::build_mch_sched(uint32_t tbs) +{ + int sfs_per_sched_period = mcch.pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9; + int bytes_per_sf = tbs/8 - 6;// leave 6 bytes for header + + int total_space_avail_bytes = sfs_per_sched_period*bytes_per_sf; + + int total_bytes_to_tx = 0; + + + // calculate total bytes to be scheduled + for (uint32_t i = 0; i < mch.num_mtch_sched; i++) { + total_bytes_to_tx += mch.mtch_sched[i].lcid_buffer_size; + mch.mtch_sched[i].stop = 0; + } + + int last_mtch_stop = 0; + + if(total_bytes_to_tx >= total_space_avail_bytes){ + for(uint32_t i = 0; i < mch.num_mtch_sched;i++){ + double ratio = mch.mtch_sched[i].lcid_buffer_size/total_bytes_to_tx; + float assigned_sfs = floor(sfs_per_sched_period*ratio); + mch.mtch_sched[i].stop = last_mtch_stop + (uint32_t)assigned_sfs; + last_mtch_stop = mch.mtch_sched[i].stop; + } + }else { + for(uint32_t i = 0; i < mch.num_mtch_sched;i++){ + float assigned_sfs = ceil(((float)mch.mtch_sched[i].lcid_buffer_size)/((float)bytes_per_sf)); + mch.mtch_sched[i].stop = last_mtch_stop + (uint32_t)assigned_sfs; + last_mtch_stop = mch.mtch_sched[i].stop; + } + } +} + +int mac::get_mch_sched(bool is_mcch, dl_sched_t *dl_sched_res) +{ + + srslte_ra_mcs_t mcs; + srslte_ra_mcs_t mcs_data; + mcs.idx = this->sib13.mbsfn_area_info_list_r9[0].signalling_mcs_r9; + mcs_data.idx = this->mcch.pmch_infolist_r9[0].pmch_config_r9.datamcs_r9; + srslte_dl_fill_ra_mcs(&mcs, this->cell_config.cell.nof_prb); + srslte_dl_fill_ra_mcs(&mcs_data, this->cell_config.cell.nof_prb); + if(is_mcch){ + + build_mch_sched(mcs_data.tbs); + mch.mcch_payload = mcch_payload_buffer; + mch.current_sf_allocation_num = 1; + Info("MCH Sched Info: LCID: %d, Stop: %d, tti is %d \n", mch.mtch_sched[0].lcid, mch.mtch_sched[0].stop, tti); + for(uint32_t i = 0; i < mch.num_mtch_sched; i++) { + mch.pdu[i].lcid = srslte::sch_subh::MCH_SCHED_INFO; + // mch.mtch_sched[i].lcid = 1+i; + } + + mch.pdu[mch.num_mtch_sched].lcid = 0; + mch.pdu[mch.num_mtch_sched].nbytes = current_mcch_length; + dl_sched_res->sched_grants[0].rnti = SRSLTE_MRNTI; + dl_sched_res->sched_grants[0].data[0] = ue_db[SRSLTE_MRNTI]->generate_mch_pdu(mch, mch.num_mtch_sched + 1, mcs.tbs/8); + + } else { + uint32_t current_lcid = 1; + uint32_t mtch_index = 0; + uint32_t mtch_stop = mch.mtch_sched[mch.num_mtch_sched -1].stop; + + for(uint32_t i = 0;i < mch.num_mtch_sched;i++) { + if(mch.current_sf_allocation_num <= mch.mtch_sched[i].stop){ + current_lcid = mch.mtch_sched[i].lcid; + mtch_index = i; + break; + } + } + if(mch.current_sf_allocation_num <= mtch_stop) { + int requested_bytes = (mcs_data.tbs/8 > (int)mch.mtch_sched[mtch_index].lcid_buffer_size)?(mch.mtch_sched[mtch_index].lcid_buffer_size):((mcs_data.tbs/8) - 2); + int bytes_received = ue_db[SRSLTE_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); + mch.pdu[0].lcid = current_lcid; + mch.pdu[0].nbytes = bytes_received; + mch.mtch_sched[0].mtch_payload = mtch_payload_buffer; + dl_sched_res->sched_grants[0].rnti = SRSLTE_MRNTI; + if(bytes_received){ + dl_sched_res->sched_grants[0].data[0] = ue_db[SRSLTE_MRNTI]->generate_mch_pdu(mch, 1, mcs_data.tbs/8); + } + } else { + //TRANSMIT NOTHING + } + mch.current_sf_allocation_num++; + } + + return SRSLTE_SUCCESS; +} + uint8_t* mac::assemble_rar(sched_interface::dl_sched_rar_grant_t* grants, uint32_t nof_grants, int rar_idx, uint32_t pdu_len) { uint8_t grant_buffer[64]; @@ -605,7 +704,7 @@ uint8_t* mac::assemble_rar(sched_interface::dl_sched_rar_grant_t* grants, uint32 } uint8_t* mac::assemble_si(uint32_t index) -{ +{ rlc_h->read_pdu_bcch_dlsch(index, bcch_dlsch_payload); return bcch_dlsch_payload; } @@ -675,9 +774,6 @@ void mac::tti_clock() timers_thread.tti_clock(); } - - - /******************************************************** * * Interface for upper layer timers @@ -700,7 +796,6 @@ srslte::timers::timer* mac::timer_get(uint32_t timer_id) } - /******************************************************** * * Class to run timers with normal priority @@ -790,8 +885,33 @@ bool mac::process_pdus() } +void mac::write_mcch(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13, LIBLTE_RRC_MCCH_MSG_STRUCT *mcch) +{ + bzero(&mcch_payload_buffer[0],sizeof(uint8_t)*3000); + + + LIBLTE_BIT_MSG_STRUCT bitbuffer; + liblte_rrc_pack_mcch_msg(mcch, &bitbuffer); + memcpy(&this->mcch ,mcch, sizeof(LIBLTE_RRC_MCCH_MSG_STRUCT)); + mch.num_mtch_sched = this->mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size; + for(uint32_t i = 0; i < mch.num_mtch_sched; i++){ + mch.mtch_sched[i].lcid = this->mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[i].logicalchannelid_r9; + } + memcpy(&this->sib2,sib2, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT)); + memcpy(&this->sib2,sib13, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT)); // TODO: consolidate relevant parts into 1 struct + current_mcch_length = floor(bitbuffer.N_bits/8); + if(bitbuffer.N_bits%8 != 0) { + current_mcch_length++; + } + int rlc_header_len = 1; + current_mcch_length = current_mcch_length + rlc_header_len; + srslte_bit_pack_vector(&bitbuffer.msg[0], &mcch_payload_buffer[rlc_header_len], bitbuffer.N_bits); + ue_db[SRSLTE_MRNTI] = new ue; + ue_db[SRSLTE_MRNTI]->config(SRSLTE_MRNTI, cell.nof_prb, &scheduler, rrc_h, rlc_h, log_h); + rrc_h->add_user(SRSLTE_MRNTI); +} } diff --git a/srsenb/src/mac/scheduler.cc b/srsenb/src/mac/scheduler.cc index 75d4cbaa3..2c3dee85a 100644 --- a/srsenb/src/mac/scheduler.cc +++ b/srsenb/src/mac/scheduler.cc @@ -545,7 +545,7 @@ int sched::dl_sched_bc(dl_sched_bc_t bc[MAX_BC_LIST]) } uint32_t n_sf = (current_tti-pending_sibs[i].window_start); if ((i == 0 && (sfn%2) == 0 && sf_idx == 5) || - (i > 0 && n_sf >= (cfg.si_window_ms/nof_tx)*pending_sibs[i].n_tx && sf_idx==1)) + (i > 0 && n_sf >= (cfg.si_window_ms/nof_tx)*pending_sibs[i].n_tx && sf_idx==9)) { uint32_t rv = get_rvidx(pending_sibs[i].n_tx); @@ -775,10 +775,10 @@ int sched::dl_sched(uint32_t tti, sched_interface::dl_sched_res_t* sched_result) /* Schedule Broadcast data */ sched_result->nof_bc_elems += dl_sched_bc(sched_result->bc); - + /* Schedule RAR */ sched_result->nof_rar_elems += dl_sched_rar(sched_result->rar); - + /* Schedule pending RLC data */ sched_result->nof_data_elems += dl_sched_data(sched_result->data); diff --git a/srsenb/src/mac/ue.cc b/srsenb/src/mac/ue.cc index 0ddd5afea..697bc8b8d 100644 --- a/srsenb/src/mac/ue.cc +++ b/srsenb/src/mac/ue.cc @@ -173,7 +173,7 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srslte::pdu_queue::channe if (mac_msg_ul.get()->get_sdu_lcid() == 0) { uint8_t *x = mac_msg_ul.get()->get_sdu_ptr(); uint32_t sum = 0; - for (int i = 0; i < mac_msg_ul.get()->get_payload_size(); i++) { + for (uint32_t i = 0; i < mac_msg_ul.get()->get_payload_size(); i++) { sum += x[i]; } if (sum == 0) { @@ -200,7 +200,7 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srslte::pdu_queue::channe // Save contention resolution if lcid == 0 if (mac_msg_ul.get()->get_sdu_lcid() == 0 && route_pdu) { int nbytes = srslte::sch_subh::MAC_CE_CONTRES_LEN; - if (mac_msg_ul.get()->get_payload_size() >= nbytes) { + if (mac_msg_ul.get()->get_payload_size() >= (uint32_t)nbytes) { uint8_t *ue_cri_ptr = (uint8_t *) &conres_id; uint8_t *pkt_ptr = mac_msg_ul.get()->get_sdu_ptr(); // Warning here: we want to include the for (int i = 0; i < nbytes; i++) { @@ -329,7 +329,7 @@ void ue::allocate_sdu(srslte::sch_pdu *pdu, uint32_t lcid, uint32_t total_sdu_le while(sdu_len > 3 && n > 0) { if (pdu->new_subh()) { // there is space for a new subheader log_h->debug("SDU: set_sdu(), lcid=%d, sdu_len=%d, sdu_space=%d\n", lcid, sdu_len, sdu_space); - n = pdu->get()->set_sdu(lcid, sdu_len, this); + n = pdu->get()->set_sdu(lcid, sdu_len, this); if (n > 0) { // new SDU could be added sdu_len -= n; log_h->debug("SDU: rnti=0x%x, lcid=%d, nbytes=%d, rem_len=%d\n", @@ -390,6 +390,29 @@ uint8_t* ue::generate_pdu(uint32_t tb_idx, sched_interface::dl_sched_pdu_t pdu[s return ret; } +uint8_t* ue::generate_mch_pdu(sched_interface::dl_pdu_mch_t sched, uint32_t nof_pdu_elems , uint32_t grant_size) +{ + uint8_t *ret = NULL; + pthread_mutex_lock(&mutex); + mch_mac_msg_dl.init_tx(tx_payload_buffer[0],grant_size); + + for(uint32_t i = 0; i set_next_mch_sched_info(sched.mtch_sched[i].lcid,sched.mtch_sched[i].stop); + } else if (sched.pdu[i].lcid == 0) { + mch_mac_msg_dl.new_subh(); + mch_mac_msg_dl.get()->set_sdu(0, sched.pdu[i].nbytes, sched.mcch_payload); + } else if (sched.pdu[i].lcid <= srslte::mch_subh::MTCH_MAX_LCID) { + mch_mac_msg_dl.new_subh(); + mch_mac_msg_dl.get()->set_sdu(sched.pdu[i].lcid, sched.pdu[i].nbytes,sched.mtch_sched[i].mtch_payload); + } + } + ret = mch_mac_msg_dl.write_packet(log_h); + pthread_mutex_unlock(&mutex); + return ret; +} + /******* METRICS interface ***************/ diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index a4d7532f0..3d4af3501 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -183,6 +183,10 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { ("expert.rrc_inactivity_timer", bpo::value(&args->expert.rrc_inactivity_timer)->default_value(10000), "Inactivity timer in ms") + + ("expert.enable_mbsfn", + bpo::value(&args->expert.enable_mbsfn)->default_value(false), + "enables mbms in the enodeb") ("expert.print_buffer_state", bpo::value(&args->expert.print_buffer_state)->default_value(false), diff --git a/srsenb/src/phy/phch_common.cc b/srsenb/src/phy/phch_common.cc index ce8026ca5..5a5cd7f36 100644 --- a/srsenb/src/phy/phch_common.cc +++ b/srsenb/src/phy/phch_common.cc @@ -26,7 +26,7 @@ #include "srslte/common/threads.h" #include "srslte/common/log.h" - +#include #include "srsenb/hdr/phy/txrx.h" #include @@ -57,6 +57,8 @@ bool phch_common::init(srslte_cell_t *cell_, srslte::radio* radio_h_, mac_interf mac = mac_; memcpy(&cell, cell_, sizeof(srslte_cell_t)); + + is_first_of_burst = true; is_first_tx = true; for (uint32_t i=0;imcch_offset_r9; + period = liblte_rrc_mcch_repetition_period_r9_num[area_info->mcch_repetition_period_r9]; + + if((sfn%period == offset) && mcch_table[sf] > 0) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + cfg->mbsfn_mcs = liblte_rrc_mcch_signalling_mcs_r9_num[area_info->signalling_mcs_r9]; + cfg->mbsfn_encode = true; + cfg->is_mcch = true; + return true; + } + } + return false; +} +bool phch_common::is_mch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti) +{ + uint32_t sfn; // System Frame Number + uint8_t sf; // Subframe + uint8_t offset; + uint8_t period; + + sfn = phy_tti/10; + sf = phy_tti%10; + + // Set some defaults + cfg->mbsfn_area_id = 0; + cfg->non_mbsfn_region_length = 1; + cfg->mbsfn_mcs = 2; + cfg->mbsfn_encode = false; + cfg->is_mcch = false; + // Check for MCCH + if(is_mcch_subframe(cfg, phy_tti)) { + return true; + } + + // Not MCCH, check for MCH + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT *subfr_cnfg = &mbsfn.mbsfn_subfr_cnfg; + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT *area_info = &mbsfn.mbsfn_area_info; + + offset = subfr_cnfg->radio_fr_alloc_offset; + period = liblte_rrc_radio_frame_allocation_period_num[subfr_cnfg->radio_fr_alloc_period]; + + if(LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE == subfr_cnfg->subfr_alloc_num_frames) { + if((sfn%period == offset) && (mch_table[sf] > 0)) { + if(sib13_configured) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + if(mcch_configured) { + // Iterate through PMCH configs to see which one applies in the current frame + LIBLTE_RRC_MCCH_MSG_STRUCT *mcch = &mbsfn.mcch; + uint32_t sf_alloc_idx = sfn%liblte_rrc_mbsfn_common_sf_alloc_period_r9_num[mcch->commonsf_allocperiod_r9]; + for(uint32_t i=0; ipmch_infolist_r9_size; i++) { + //if(sf_alloc_idx < mch_period_stop) { + cfg->mbsfn_mcs = mcch->pmch_infolist_r9[i].pmch_config_r9.datamcs_r9; + cfg->mbsfn_encode = true; + //} + } + + } + } + return true; + } + }else if(LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_FOUR == subfr_cnfg->subfr_alloc_num_frames) { + uint8_t idx = sfn%period; + if((idx >= offset) && (idx < offset+4)) { + if(mch_table[(idx*10)+sf] > 0){ + if(sib13_configured) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + // TODO: check for MCCH configuration, set MCS and decode + + } + return true; + } + } + } + + return false; +} +void phch_common::get_sf_config(subframe_cfg_t *cfg, uint32_t phy_tti) +{ + if(is_mch_subframe(cfg, phy_tti)) { + cfg->sf_type = SUBFRAME_TYPE_MBSFN; + }else{ + cfg->sf_type = SUBFRAME_TYPE_REGULAR; + } } +} \ No newline at end of file diff --git a/srsenb/src/phy/phch_worker.cc b/srsenb/src/phy/phch_worker.cc index 9982841f0..d4e8cc684 100644 --- a/srsenb/src/phy/phch_worker.cc +++ b/srsenb/src/phy/phch_worker.cc @@ -140,7 +140,14 @@ void phch_worker::init(phch_common* phy_, srslte::log *log_h_) for (int i=0;i<10;i++) { add_rnti(1+i); } - + + + if (srslte_softbuffer_tx_init(&temp_mbsfn_softbuffer, phy->cell.nof_prb)) { + fprintf(stderr, "Error initiating soft buffer\n"); + exit(-1); + } + + srslte_softbuffer_tx_reset(&temp_mbsfn_softbuffer); srslte_pucch_set_threshold(&enb_ul.pucch, 0.8); srslte_sch_set_max_noi(&enb_ul.pusch.ul_sch, phy->params.pusch_max_its); srslte_enb_dl_set_amp(&enb_dl, phy->params.tx_amplitude); @@ -159,7 +166,7 @@ void phch_worker::stop() { running = false; pthread_mutex_lock(&mutex); - + srslte_softbuffer_tx_free(&temp_mbsfn_softbuffer); srslte_enb_dl_free(&enb_dl); srslte_enb_ul_free(&enb_ul); for (int p = 0; p < SRSLTE_MAX_PORTS; p++) { @@ -351,9 +358,11 @@ void phch_worker::work_imp() if (!running) { return; } - + subframe_cfg_t sf_cfg; + phy->get_sf_config(&sf_cfg, tti_tx_dl);// TODO difference between tti_tx_dl and t_tx_dl pthread_mutex_lock(&mutex); + mac_interface_phy::ul_sched_t *ul_grants = phy->ul_grants; mac_interface_phy::dl_sched_t *dl_grants = phy->dl_grants; mac_interface_phy *mac = phy->mac; @@ -377,9 +386,19 @@ void phch_worker::work_imp() decode_pucch(); // Get DL scheduling for the TX TTI from MAC - if (mac->get_dl_sched(tti_tx_dl, &dl_grants[t_tx_dl]) < 0) { - Error("Getting DL scheduling from MAC\n"); - goto unlock; + + if(sf_cfg.sf_type == SUBFRAME_TYPE_REGULAR) { + if (mac->get_dl_sched(tti_tx_dl, &dl_grants[t_tx_dl]) < 0) { + Error("Getting DL scheduling from MAC\n"); + goto unlock; + } + } else { + dl_grants[t_tx_dl].cfi = sf_cfg.non_mbsfn_region_length; + srslte_enb_dl_set_non_mbsfn_region(&enb_dl, sf_cfg.non_mbsfn_region_length); + if(mac->get_mch_sched(sf_cfg.is_mcch, &dl_grants[t_tx_dl])){ + Error("Getting MCH packets from MAC\n"); + goto unlock; + } } if (dl_grants[t_tx_dl].cfi < 1 || dl_grants[t_tx_dl].cfi > 3) { @@ -396,18 +415,30 @@ void phch_worker::work_imp() // Put base signals (references, PBCH, PCFICH and PSS/SSS) into the resource grid srslte_enb_dl_clear_sf(&enb_dl); srslte_enb_dl_set_cfi(&enb_dl, dl_grants[t_tx_dl].cfi); - srslte_enb_dl_put_base(&enb_dl, tti_tx_dl); - - // Put UL/DL grants to resource grid. PDSCH data will be encoded as well. - encode_pdcch_dl(dl_grants[t_tx_dl].sched_grants, dl_grants[t_tx_dl].nof_grants); + + if(sf_cfg.sf_type == SUBFRAME_TYPE_REGULAR) { + srslte_enb_dl_put_base(&enb_dl, tti_tx_dl); + } else if (sf_cfg.mbsfn_encode){ + srslte_enb_dl_put_mbsfn_base(&enb_dl, tti_tx_dl); + } + + if(sf_cfg.sf_type == SUBFRAME_TYPE_REGULAR) { + // Put UL/DL grants to resource grid. PDSCH data will be encoded as well. + encode_pdcch_dl(dl_grants[t_tx_dl].sched_grants, dl_grants[t_tx_dl].nof_grants); + encode_pdsch(dl_grants[t_tx_dl].sched_grants, dl_grants[t_tx_dl].nof_grants); + }else { + srslte_ra_dl_grant_t phy_grant; + phy_grant.mcs[0].idx = sf_cfg.mbsfn_mcs; + encode_pmch(&dl_grants[t_tx_dl].sched_grants[0], &phy_grant); + } + encode_pdcch_ul(ul_grants[t_tx_ul].sched_grants, ul_grants[t_tx_ul].nof_grants); - encode_pdsch(dl_grants[t_tx_dl].sched_grants, dl_grants[t_tx_dl].nof_grants); - // Put pending PHICH HARQ ACK/NACK indications into subframe encode_phich(ul_grants[t_tx_ul].phich, ul_grants[t_tx_ul].nof_phich); // Prepare for receive ACK for DL grants in t_tx_dl+4 phy->ue_db_clear(TTIMOD(TTI_TX(t_tx_dl))); + for (uint32_t i=0;iworker_end(tx_mutex_cnt, signal_buffer_tx, SRSLTE_SF_LEN_PRB(phy->cell.nof_prb), tx_time); #ifdef DEBUG_WRITE_FILE @@ -837,6 +872,25 @@ int phch_worker::encode_pdcch_dl(srslte_enb_dl_pdsch_t *grants, uint32_t nof_gra return 0; } + +int phch_worker::encode_pmch(srslte_enb_dl_pdsch_t *grant, srslte_ra_dl_grant_t *phy_grant) +{ + + phy_grant->tb_en[0] = true; + phy_grant->tb_en[1] = false; + phy_grant->nof_prb = enb_dl.cell.nof_prb; + phy_grant->sf_type = SRSLTE_SF_MBSFN; + srslte_dl_fill_ra_mcs(&phy_grant->mcs[0], enb_dl.cell.nof_prb); + phy_grant->Qm[0] = srslte_mod_bits_x_symbol(phy_grant->mcs[0].mod); + for(int i = 0; i < 2; i++){ + for(uint32_t j = 0; j < phy_grant->nof_prb; j++){ + phy_grant->prb_idx[i][j] = true; + } + } + srslte_enb_dl_put_pmch(&enb_dl, phy_grant, &temp_mbsfn_softbuffer, sf_tx, &grant->data[0][0]); + return SRSLTE_SUCCESS; +} + int phch_worker::encode_pdsch(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants) { /* Scales the Resources Elements affected by the power allocation (p_b) */ @@ -1002,18 +1056,6 @@ void phch_worker::ue::metrics_ul(uint32_t mcs, float rssi, float sinr, uint32_t } - - - - - - - - - - - - void phch_worker::start_plot() { #ifdef ENABLE_GUI diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index 120a3c7b5..ee7c1e890 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -95,7 +95,9 @@ bool phy::init(phy_args_t *args, mac_interface_phy *mac, srslte::log_filter* log_h) { + std::vector log_vec; + this->log_h = log_h; for (int i=0;inof_phy_threads;i++) { log_vec.push_back(log_h); } @@ -114,7 +116,7 @@ bool phy::init(phy_args_t *args, radio_handler = radio_handler_; nof_workers = args->nof_phy_threads; - + this->log_h = (srslte::log*)log_vec[0]; workers_common.params = *args; workers_common.init(&cfg->cell, radio_handler, mac); @@ -227,6 +229,28 @@ void phy::set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICAT } } +void phy::configure_mbsfn(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13, LIBLTE_RRC_MCCH_MSG_STRUCT mcch) +{ + if(sib2->mbsfn_subfr_cnfg_list_size > 1) { + Warning("SIB2 has %d MBSFN subframe configs - only 1 supported\n", sib2->mbsfn_subfr_cnfg_list_size); + } + if(sib2->mbsfn_subfr_cnfg_list_size > 0) { + memcpy(&phy_rrc_config.mbsfn.mbsfn_subfr_cnfg, &sib2->mbsfn_subfr_cnfg_list[0], sizeof(LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT)); + } + + memcpy(&phy_rrc_config.mbsfn.mbsfn_notification_cnfg, &sib13->mbsfn_notification_config, sizeof(LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT)); + if(sib13->mbsfn_area_info_list_r9_size > 1) { + Warning("SIB13 has %d MBSFN area info elements - only 1 supported\n", sib13->mbsfn_area_info_list_r9_size); + } + if(sib13->mbsfn_area_info_list_r9_size > 0) { + memcpy(&phy_rrc_config.mbsfn.mbsfn_area_info, &sib13->mbsfn_area_info_list_r9[0], sizeof(LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT)); + } + + memcpy(&phy_rrc_config.mbsfn.mcch, &mcch, sizeof(LIBLTE_RRC_MCCH_MSG_STRUCT)); + + workers_common.configure_mbsfn(&phy_rrc_config.mbsfn); +} + // Start GUI void phy::start_plot() { ((phch_worker) workers[0]).start_plot(); diff --git a/srsenb/src/upper/gtpu.cc b/srsenb/src/upper/gtpu.cc index 0b0df8de2..67c7be319 100644 --- a/srsenb/src/upper/gtpu.cc +++ b/srsenb/src/upper/gtpu.cc @@ -92,241 +92,355 @@ bool gtpu::init(std::string gtp_bind_addr_, std::string mme_addr_, srsenb::pdcp_ return false; } + //Setup M1-u + init_m1u(gtpu_log_); + + mch_lcid_counter = 1; // Setup a thread to receive packets from the src socket start(THREAD_PRIO); - return true; + pthread_create(&mch_thread ,NULL ,mch_thread_routine , this); + return true; } -void gtpu::stop() +bool gtpu::init_m1u(srslte::log* gtpu_log_) { - if (run_enable) { - run_enable = false; - // Wait thread to exit gracefully otherwise might leave a mutex locked - int cnt=0; - while(running && cnt<100) { +struct sockaddr_in bindaddr; +// Set up sink socket +m1u_sd = socket(AF_INET, SOCK_DGRAM, 0); +if (m1u_sd < 0) { + gtpu_log->error("Failed to create M1-U sink socket\n"); + return false; +} + +/* Bind socket */ +bzero((char *)&bindaddr, sizeof(struct sockaddr_in)); +bindaddr.sin_family = AF_INET; +bindaddr.sin_addr.s_addr = htonl(INADDR_ANY); //Multicast sockets require bind to INADDR_ANY +bindaddr.sin_port = htons(GTPU_PORT+1); +size_t addrlen = sizeof(bindaddr); + +if (bind(m1u_sd, (struct sockaddr *) &bindaddr, sizeof(bindaddr)) < 0) { + gtpu_log->error("Failed to bind multicast socket\n"); + return false; +} + +/* Send an ADD MEMBERSHIP message via setsockopt */ +struct ip_mreq mreq; +mreq.imr_multiaddr.s_addr = inet_addr("239.255.0.1"); //Multicast address of the service +mreq.imr_interface.s_addr = inet_addr("127.0.1.200"); //Address of the IF the socket will listen to. +if (setsockopt(m1u_sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + gtpu_log->error("Register musticast group for M1-U\n"); + return false; +} +gtpu_log->info("M1-U initialized\n"); +return true; +} + +void gtpu::run_mch_thread(){ + +byte_buffer_t *pdu; + +mch_run_enable = true; +int n; +socklen_t addrlen; +sockaddr_in src_addr; + +bzero((char *)&src_addr, sizeof(src_addr)); +src_addr.sin_family = AF_INET; +src_addr.sin_addr.s_addr = htonl(INADDR_ANY); +src_addr.sin_port = htons(GTPU_PORT+1); +addrlen = sizeof(src_addr); + +pdu = pool->allocate(); +mch_running=true; + +pthread_mutex_lock(&mutex); +uint16_t lcid = mch_lcid_counter; +mch_lcid_counter++; +pthread_mutex_unlock(&mutex); + +while(mch_run_enable) { + + pdu->reset(); + do{ + n = recvfrom(m1u_sd, pdu->msg, SRSENB_MAX_BUFFER_SIZE_BYTES - SRSENB_BUFFER_HEADER_OFFSET, 0, (struct sockaddr *) &src_addr, &addrlen); + } while (n == -1 && errno == EAGAIN); + + pdu->N_bytes = (uint32_t) n; + + + gtpu_header_t header; + gtpu_read_header(pdu, &header); + + uint16_t rnti = 0xFFFD; + + pthread_mutex_lock(&mutex); + bool user_exists = (rnti_bearers.count(rnti) > 0); + pthread_mutex_unlock(&mutex); + + if(!user_exists) { + gtpu_log->error("Unrecognized RNTI for DL PDU: 0x%x - dropping packet\n", rnti); + continue; + } + + if(lcid == 0 || lcid >= SRSENB_N_RADIO_BEARERS) { + gtpu_log->error("Invalid LCID for DL PDU: %d - dropping packet\n", lcid); + continue; + } + + pdcp->write_sdu(rnti, lcid, pdu); + do { + pdu = pool_allocate; + if (!pdu) { + gtpu_log->console("GTPU Buffer pool empty. Trying again...\n"); usleep(10000); - cnt++; - } - if (running) { - thread_cancel(); } - wait_thread_finish(); - } + } while(!pdu); +} +mch_running=false; +} - if (snk_fd) { - close(snk_fd); +void gtpu::stop() +{ +if (run_enable) { + run_enable = false; + if(mch_run_enable) + mch_run_enable = false; + + // Wait thread to exit gracefully otherwise might leave a mutex locked + int cnt=0; + while(running && cnt<100) { + usleep(10000); + cnt++; } - if (src_fd) { - close(src_fd); + if (running) { + thread_cancel(); + if(mch_running) + pthread_cancel(mch_thread); } + wait_thread_finish(); + pthread_join(mch_thread, NULL); + +} + +if (snk_fd) { + close(snk_fd); +} +if (src_fd) { + close(src_fd); +} } // gtpu_interface_pdcp void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* pdu) { - gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "TX PDU, RNTI: 0x%x, LCID: %d, n_bytes=%d", rnti, lcid, pdu->N_bytes); - gtpu_header_t header; - header.flags = 0x30; - header.message_type = 0xFF; - header.length = pdu->N_bytes; - header.teid = rnti_bearers[rnti].teids_out[lcid]; - - struct sockaddr_in servaddr; - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = htonl(rnti_bearers[rnti].spgw_addrs[lcid]); - servaddr.sin_port = htons(GTPU_PORT); - - gtpu_write_header(&header, pdu); - if (sendto(snk_fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in))<0) { - perror("sendto"); - } +gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "TX PDU, RNTI: 0x%x, LCID: %d, n_bytes=%d", rnti, lcid, pdu->N_bytes); +gtpu_header_t header; +header.flags = 0x30; +header.message_type = 0xFF; +header.length = pdu->N_bytes; +header.teid = rnti_bearers[rnti].teids_out[lcid]; + +struct sockaddr_in servaddr; +servaddr.sin_family = AF_INET; +servaddr.sin_addr.s_addr = htonl(rnti_bearers[rnti].spgw_addrs[lcid]); +servaddr.sin_port = htons(GTPU_PORT); + +gtpu_write_header(&header, pdu); +if (sendto(snk_fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in))<0) { + perror("sendto"); +} - pool->deallocate(pdu); +pool->deallocate(pdu); } // gtpu_interface_rrc void gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, uint32_t *teid_in) { - // Allocate a TEID for the incoming tunnel - rntilcid_to_teidin(rnti, lcid, teid_in); - gtpu_log->info("Adding bearer for rnti: 0x%x, lcid: %d, addr: 0x%x, teid_out: 0x%x, teid_in: 0x%x\n", rnti, lcid, addr, teid_out, *teid_in); - - // Initialize maps if it's a new RNTI - if(rnti_bearers.count(rnti) == 0) { - for(int i=0;iinfo("Adding bearer for rnti: 0x%x, lcid: %d, addr: 0x%x, teid_out: 0x%x, teid_in: 0x%x\n", rnti, lcid, addr, teid_out, *teid_in); + +// Initialize maps if it's a new RNTI +if(rnti_bearers.count(rnti) == 0) { + for(int i=0;iinfo("Removing bearer for rnti: 0x%x, lcid: %d\n", rnti, lcid); +gtpu_log->info("Removing bearer for rnti: 0x%x, lcid: %d\n", rnti, lcid); - rnti_bearers[rnti].teids_in[lcid] = 0; - rnti_bearers[rnti].teids_out[lcid] = 0; +rnti_bearers[rnti].teids_in[lcid] = 0; +rnti_bearers[rnti].teids_out[lcid] = 0; - // Remove RNTI if all bearers are removed - bool rem = true; - for(int i=0;ierror("Fatal Error: Couldn't allocate buffer in gtpu::run_thread().\n"); - return; +byte_buffer_t *pdu = pool_allocate; + +if (!pdu) { + gtpu_log->error("Fatal Error: Couldn't allocate buffer in gtpu::run_thread().\n"); + return; +} +run_enable = true; + +running=true; +while(run_enable) { + + pdu->reset(); + gtpu_log->debug("Waiting for read...\n"); + int n = 0; + do{ + n = recv(src_fd, pdu->msg, SRSENB_MAX_BUFFER_SIZE_BYTES - SRSENB_BUFFER_HEADER_OFFSET, 0); + } while (n == -1 && errno == EAGAIN); + + if (n < 0) { + gtpu_log->error("Failed to read from socket\n"); } - run_enable = true; - running=true; - while(run_enable) { + pdu->N_bytes = (uint32_t) n; - pdu->reset(); - gtpu_log->debug("Waiting for read...\n"); - int n = 0; - do{ - n = recv(src_fd, pdu->msg, SRSENB_MAX_BUFFER_SIZE_BYTES - SRSENB_BUFFER_HEADER_OFFSET, 0); - } while (n == -1 && errno == EAGAIN); - if (n < 0) { - gtpu_log->error("Failed to read from socket\n"); - } + gtpu_header_t header; + gtpu_read_header(pdu, &header); - pdu->N_bytes = (uint32_t) n; - - gtpu_header_t header; - gtpu_read_header(pdu, &header); - - uint16_t rnti = 0; - uint16_t lcid = 0; - teidin_to_rntilcid(header.teid, &rnti, &lcid); - - pthread_mutex_lock(&mutex); - bool user_exists = (rnti_bearers.count(rnti) > 0); - pthread_mutex_unlock(&mutex); - - if(!user_exists) { - gtpu_log->error("Unrecognized RNTI for DL PDU: 0x%x - dropping packet\n", rnti); - continue; - } + uint16_t rnti = 0; + uint16_t lcid = 0; + teidin_to_rntilcid(header.teid, &rnti, &lcid); - if(lcid < SRSENB_N_SRB || lcid >= SRSENB_N_RADIO_BEARERS) { - gtpu_log->error("Invalid LCID for DL PDU: %d - dropping packet\n", lcid); - continue; - } + pthread_mutex_lock(&mutex); + bool user_exists = (rnti_bearers.count(rnti) > 0); + pthread_mutex_unlock(&mutex); - gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "RX GTPU PDU rnti=0x%x, lcid=%d, n_bytes=%d", rnti, lcid, pdu->N_bytes); + if(!user_exists) { + gtpu_log->error("Unrecognized RNTI for DL PDU: 0x%x - dropping packet\n", rnti); + continue; + } - pdcp->write_sdu(rnti, lcid, pdu); - do { - pdu = pool_allocate; - if (!pdu) { - gtpu_log->console("GTPU Buffer pool empty. Trying again...\n"); - usleep(10000); - } - } while(!pdu); + if(lcid < SRSENB_N_SRB || lcid >= SRSENB_N_RADIO_BEARERS) { + gtpu_log->error("Invalid LCID for DL PDU: %d - dropping packet\n", lcid); + continue; } - running=false; + + gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "RX GTPU PDU rnti=0x%x, lcid=%d, n_bytes=%d", rnti, lcid, pdu->N_bytes); + + pdcp->write_sdu(rnti, lcid, pdu); + + do { + pdu = pool_allocate; + if (!pdu) { + gtpu_log->console("GTPU Buffer pool empty. Trying again...\n"); + usleep(10000); + } + } while(!pdu); +} +running=false; } /**************************************************************************** - * Header pack/unpack helper functions - * Ref: 3GPP TS 29.281 v10.1.0 Section 5 - ***************************************************************************/ +* Header pack/unpack helper functions +* Ref: 3GPP TS 29.281 v10.1.0 Section 5 +***************************************************************************/ bool gtpu::gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu) { - if(header->flags != 0x30) { - gtpu_log->error("gtpu_write_header - Unhandled header flags: 0x%x\n", header->flags); - return false; - } - if(header->message_type != 0xFF) { - gtpu_log->error("gtpu_write_header - Unhandled message type: 0x%x\n", header->message_type); - return false; - } - if(pdu->get_headroom() < GTPU_HEADER_LEN) { - gtpu_log->error("gtpu_write_header - No room in PDU for header\n"); - return false; - } +if(header->flags != 0x30) { + gtpu_log->error("gtpu_write_header - Unhandled header flags: 0x%x\n", header->flags); + return false; +} +if(header->message_type != 0xFF) { + gtpu_log->error("gtpu_write_header - Unhandled message type: 0x%x\n", header->message_type); + return false; +} +if(pdu->get_headroom() < GTPU_HEADER_LEN) { + gtpu_log->error("gtpu_write_header - No room in PDU for header\n"); + return false; +} - pdu->msg -= GTPU_HEADER_LEN; - pdu->N_bytes += GTPU_HEADER_LEN; +pdu->msg -= GTPU_HEADER_LEN; +pdu->N_bytes += GTPU_HEADER_LEN; - uint8_t *ptr = pdu->msg; +uint8_t *ptr = pdu->msg; - *ptr = header->flags; - ptr++; - *ptr = header->message_type; - ptr++; - uint16_to_uint8(header->length, ptr); - ptr += 2; - uint32_to_uint8(header->teid, ptr); +*ptr = header->flags; +ptr++; +*ptr = header->message_type; +ptr++; +uint16_to_uint8(header->length, ptr); +ptr += 2; +uint32_to_uint8(header->teid, ptr); - return true; +return true; } bool gtpu::gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header) { - uint8_t *ptr = pdu->msg; - - pdu->msg += GTPU_HEADER_LEN; - pdu->N_bytes -= GTPU_HEADER_LEN; - - header->flags = *ptr; - ptr++; - header->message_type = *ptr; - ptr++; - uint8_to_uint16(ptr, &header->length); - ptr += 2; - uint8_to_uint32(ptr, &header->teid); - - if(header->flags != 0x30) { - gtpu_log->error("gtpu_read_header - Unhandled header flags: 0x%x\n", header->flags); - return false; - } - if(header->message_type != 0xFF) { - gtpu_log->error("gtpu_read_header - Unhandled message type: 0x%x\n", header->message_type); - return false; - } +uint8_t *ptr = pdu->msg; + +pdu->msg += GTPU_HEADER_LEN; +pdu->N_bytes -= GTPU_HEADER_LEN; + +header->flags = *ptr; +ptr++; +header->message_type = *ptr; +ptr++; +uint8_to_uint16(ptr, &header->length); +ptr += 2; +uint8_to_uint32(ptr, &header->teid); + +if(header->flags != 0x30) { + gtpu_log->error("gtpu_read_header - Unhandled header flags: 0x%x\n", header->flags); + return false; +} +if(header->message_type != 0xFF) { + gtpu_log->error("gtpu_read_header - Unhandled message type: 0x%x\n", header->message_type); + return false; +} - return true; +return true; } /**************************************************************************** - * TEID to RNIT/LCID helper functions - ***************************************************************************/ +* TEID to RNIT/LCID helper functions +***************************************************************************/ void gtpu::teidin_to_rntilcid(uint32_t teidin, uint16_t *rnti, uint16_t *lcid) { - *lcid = teidin & 0xFFFF; - *rnti = (teidin >> 16) & 0xFFFF; +*lcid = teidin & 0xFFFF; +*rnti = (teidin >> 16) & 0xFFFF; } void gtpu::rntilcid_to_teidin(uint16_t rnti, uint16_t lcid, uint32_t *teidin) { - *teidin = (rnti << 16) | lcid; +*teidin = (rnti << 16) | lcid; } - + } // namespace srsenb diff --git a/srsenb/src/upper/pdcp.cc b/srsenb/src/upper/pdcp.cc index 99289ec4d..e6f4f40f5 100644 --- a/srsenb/src/upper/pdcp.cc +++ b/srsenb/src/upper/pdcp.cc @@ -76,7 +76,11 @@ void pdcp::rem_user(uint16_t rnti) void pdcp::add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_pdcp_config_t cfg) { if (users.count(rnti)) { - users[rnti].pdcp->add_bearer(lcid, cfg); + if(rnti != SRSLTE_MRNTI){ + users[rnti].pdcp->add_bearer(lcid, cfg); + } else { + users[rnti].pdcp->add_bearer_mrb(lcid, cfg); + } } } @@ -110,7 +114,11 @@ void pdcp::write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu) void pdcp::write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu) { if (users.count(rnti)) { - users[rnti].pdcp->write_sdu(lcid, sdu); + if(rnti != SRSLTE_MRNTI){ + users[rnti].pdcp->write_sdu(lcid, sdu); + }else { + users[rnti].pdcp->write_sdu_mch(lcid, sdu); + } } else { pool->deallocate(sdu); } @@ -123,6 +131,7 @@ void pdcp::user_interface_gtpu::write_pdu(uint32_t lcid, srslte::byte_buffer_t * void pdcp::user_interface_rlc::write_sdu(uint32_t lcid, srslte::byte_buffer_t* sdu) { + rlc->write_sdu(rnti, lcid, sdu); } diff --git a/srsenb/src/upper/rlc.cc b/srsenb/src/upper/rlc.cc index 72d269c6c..0624dbca4 100644 --- a/srsenb/src/upper/rlc.cc +++ b/srsenb/src/upper/rlc.cc @@ -105,6 +105,13 @@ void rlc::add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_rlc_config_t c } } +void rlc::add_bearer_mrb(uint16_t rnti, uint32_t lcid) +{ + if (users.count(rnti)) { + users[rnti].rlc->add_bearer_mrb_enb(lcid); + } +} + void rlc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) { rrc->read_pdu_pcch(payload, buffer_size); @@ -112,16 +119,27 @@ void rlc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) int rlc::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) { - int ret = users[rnti].rlc->read_pdu(lcid, payload, nof_bytes); - - // In the eNodeB, there is no polling for buffer state from the scheduler, thus - // communicate buffer state every time a PDU is read - uint32_t tx_queue = users[rnti].rlc->get_total_buffer_state(lcid); - uint32_t retx_queue = 0; - log_h->debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); - mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); - - return ret; + int ret; + uint32_t tx_queue; + if(users.count(rnti)){ + if(rnti != SRSLTE_MRNTI){ + ret = users[rnti].rlc->read_pdu(lcid, payload, nof_bytes); + tx_queue = users[rnti].rlc->get_total_buffer_state(lcid); + }else{ + ret = users[rnti].rlc->read_pdu_mch(lcid, payload, nof_bytes); + tx_queue = users[rnti].rlc->get_total_mch_buffer_state(lcid); + } + // In the eNodeB, there is no polling for buffer state from the scheduler, thus + // communicate buffer state every time a PDU is read + + uint32_t retx_queue = 0; + log_h->debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); + mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); + return ret; + + }else{ + return SRSLTE_ERROR; + } } void rlc::write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) @@ -146,12 +164,19 @@ void rlc::read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t *payload) void rlc::write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu) { + + uint32_t tx_queue; if (users.count(rnti)) { - users[rnti].rlc->write_sdu(lcid, sdu); - + if(rnti != SRSLTE_MRNTI){ + users[rnti].rlc->write_sdu(lcid, sdu); + tx_queue = users[rnti].rlc->get_total_buffer_state(lcid); + }else { + users[rnti].rlc->write_sdu_mch(lcid, sdu); + tx_queue = users[rnti].rlc->get_total_mch_buffer_state(lcid); + } // In the eNodeB, there is no polling for buffer state from the scheduler, thus // communicate buffer state every time a new SDU is written - uint32_t tx_queue = users[rnti].rlc->get_total_buffer_state(lcid); + uint32_t retx_queue = 0; mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); log_h->info("Buffer state: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc index 8abb1ed08..19a8dc073 100644 --- a/srsenb/src/upper/rrc.cc +++ b/srsenb/src/upper/rrc.cc @@ -30,6 +30,7 @@ #include "srslte/srslte.h" #include "srslte/asn1/liblte_mme.h" + using srslte::byte_buffer_t; using srslte::bit_buffer_t; @@ -57,6 +58,11 @@ void rrc::init(rrc_cfg_t *cfg_, pool = srslte::byte_buffer_pool::get_instance(); memcpy(&cfg, cfg_, sizeof(rrc_cfg_t)); + + if(cfg.sibs[12].sib_type == LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13 && cfg_->enable_mbsfn) { + configure_mbsfn_sibs(&cfg.sibs[1].sib.sib2,&cfg.sibs[12].sib.sib13); + } + nof_si_messages = generate_sibs(); config_mac(); @@ -69,6 +75,53 @@ void rrc::init(rrc_cfg_t *cfg_, start(RRC_THREAD_PRIO); } +void rrc::configure_mbsfn_sibs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13) +{ + + // Temp assignment of MCCH, this will eventually come from a cfg file + mcch.pmch_infolist_r9_size = 1; + mcch.commonsf_allocpatternlist_r9_size = 1; + mcch.commonsf_allocperiod_r9 = LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF64; + mcch.commonsf_allocpatternlist_r9[0].radio_fr_alloc_offset = 0; + mcch.commonsf_allocpatternlist_r9[0].radio_fr_alloc_period = LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N1; + mcch.commonsf_allocpatternlist_r9[0].subfr_alloc = 32+31; + mcch.commonsf_allocpatternlist_r9[0].subfr_alloc_num_frames = LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE; + + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size = 1; + + + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].logicalchannelid_r9 = 1; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9 = 0; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9_present = true; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_explicit = true; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mcc = 0; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mnc = 3; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_index_r9 = 0; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.serviceid_r9 = 0; + + if(mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size > 1) { + + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].logicalchannelid_r9 = 2; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].sessionid_r9 = 1; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].sessionid_r9_present = true; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].tmgi_r9.plmn_id_explicit = true; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].tmgi_r9.plmn_id_r9.mcc = 0; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].tmgi_r9.plmn_id_r9.mnc = 3; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].tmgi_r9.plmn_index_r9 = 0; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].tmgi_r9.serviceid_r9 = 1; + + } + mcch.pmch_infolist_r9[0].pmch_config_r9.datamcs_r9 = 10; + mcch.pmch_infolist_r9[0].pmch_config_r9.mch_schedulingperiod_r9 = LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF64; + mcch.pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9 = 64*6; + + + + phy->configure_mbsfn(sib2,sib13,mcch); + mac->write_mcch(sib2,sib13,&mcch); + +} + rrc::activity_monitor::activity_monitor(rrc* parent_) { running = true; @@ -108,11 +161,14 @@ void rrc::get_metrics(rrc_metrics_t &m) m.n_ues = 0; for(std::map::iterator iter=users.begin(); m.n_ues < ENB_METRICS_MAX_USERS &&iter!=users.end(); ++iter) { ue *u = (ue*) &iter->second; - m.ues[m.n_ues++].state = u->get_state(); + if(iter->first != SRSLTE_MRNTI){ + m.ues[m.n_ues++].state = u->get_state(); + } } pthread_mutex_unlock(&user_mutex); } + uint32_t rrc::generate_sibs() { // nof_messages includes SIB2 by default, plus all configured SIBs @@ -218,6 +274,7 @@ void rrc::add_user(uint16_t rnti) { pthread_mutex_lock(&user_mutex); if (users.count(rnti) == 0) { + users[rnti].parent = this; users[rnti].rnti = rnti; rlc->add_user(rnti); @@ -226,6 +283,22 @@ void rrc::add_user(uint16_t rnti) } else { rrc_log->error("Adding user rnti=0x%x (already exists)\n", rnti); } + + if(rnti == SRSLTE_MRNTI){ + srslte::srslte_pdcp_config_t cfg; + cfg.is_control = false; + cfg.is_data = true; + cfg.direction = SECURITY_DIRECTION_DOWNLINK; + uint32_t teid_in = 1; + + for(uint32_t i = 0; i add_bearer_mrb(SRSLTE_MRNTI,lcid); + pdcp->add_bearer(SRSLTE_MRNTI,lcid,cfg); + gtpu->add_bearer(SRSLTE_MRNTI,lcid, 1, 1, &teid_in); + } + } + pthread_mutex_unlock(&user_mutex); } @@ -666,25 +739,28 @@ void rrc::activity_monitor::run_thread() pthread_mutex_lock(&parent->user_mutex); uint16_t rem_rnti = 0; for(std::map::iterator iter=parent->users.begin(); rem_rnti == 0 && iter!=parent->users.end(); ++iter) { - ue *u = (ue*) &iter->second; - uint16_t rnti = (uint16_t) iter->first; + if(iter->first != SRSLTE_MRNTI){ + ue *u = (ue*) &iter->second; + uint16_t rnti = (uint16_t) iter->first; - if (parent->cnotifier && u->is_connected() && !u->connect_notified) { - parent->cnotifier->user_connected(rnti); - u->connect_notified = true; - } - - if (u->is_timeout()) { - parent->rrc_log->info("User rnti=0x%x timed out. Exists in s1ap=%s\n", rnti, parent->s1ap->user_exists(rnti)?"yes":"no"); - rem_rnti = rnti; - } - } + if (parent->cnotifier && u->is_connected() && !u->connect_notified) { + parent->cnotifier->user_connected(rnti); + u->connect_notified = true; + } + + if (u->is_timeout()) { + parent->rrc_log->info("User rnti=0x%x timed out. Exists in s1ap=%s\n", rnti, parent->s1ap->user_exists(rnti)?"yes":"no"); + rem_rnti = rnti; + } + } + } pthread_mutex_unlock(&parent->user_mutex); if (rem_rnti) { if (parent->s1ap->user_exists(rem_rnti)) { parent->s1ap->user_inactivity(rem_rnti); } else { - parent->rem_user(rem_rnti); + if(rem_rnti != SRSLTE_MRNTI) + parent->rem_user(rem_rnti); } } } @@ -1240,7 +1316,7 @@ void rrc::ue::send_connection_setup(bool is_setup) sched_cfg.pucch_cfg.N_cs = parent->sib2.rr_config_common_sib.pucch_cnfg.n_cs_an; sched_cfg.pucch_cfg.n_rb_2 = parent->sib2.rr_config_common_sib.pucch_cnfg.n_rb_cqi; sched_cfg.pucch_cfg.n1_pucch_an = parent->sib2.rr_config_common_sib.pucch_cnfg.n1_pucch_an; - + // Configure MAC parent->mac->ue_cfg(rnti, &sched_cfg); diff --git a/srsenb/test/upper/ip_test.cc b/srsenb/test/upper/ip_test.cc index 9361a9901..b6480d288 100644 --- a/srsenb/test/upper/ip_test.cc +++ b/srsenb/test/upper/ip_test.cc @@ -174,6 +174,7 @@ public: void write_pdu_bcch_bch(srslte::byte_buffer_t *sdu) {} void write_pdu_bcch_dlsch(srslte::byte_buffer_t *sdu) {} void write_pdu_pcch(srslte::byte_buffer_t *sdu) {} + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu){} void max_retx_attempted(){} void add_user(uint16_t rnti) {} void release_user(uint16_t rnti) {} diff --git a/srsepc/hdr/mbms-gw/mbms-gw.h b/srsepc/hdr/mbms-gw/mbms-gw.h new file mode 100644 index 000000000..2beca7dcb --- /dev/null +++ b/srsepc/hdr/mbms-gw/mbms-gw.h @@ -0,0 +1,101 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE 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 Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/****************************************************************************** + * File: mbms-gw.h + * Description: Top-level MBMS-GW class. Creates and links all + * interfaces and helpers. + *****************************************************************************/ + +#ifndef MBMS_GW_H +#define MBMS_GW_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/logger_file.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/common/threads.h" +#include "srslte/asn1/gtpc.h" + +namespace srsepc{ + + +const uint16_t GTPU_RX_PORT = 2152; + +typedef struct { + std::string name; + std::string sgi_mb_if_addr; + std::string m1u_multi_addr; +} mbms_gw_args_t; + +struct pseudo_hdr +{ + uint32_t src_addr; + uint32_t dst_addr; + uint8_t placeholder; + uint8_t protocol; + uint16_t udp_len; +}; + +class mbms_gw: + public thread +{ +public: + static mbms_gw* get_instance(void); + static void cleanup(void); + int init(mbms_gw_args_t* args, srslte::log_filter *mbms_gw_log); + void stop(); + void run_thread(); + +private: + + /* Methods */ + mbms_gw(); + virtual ~mbms_gw(); + static mbms_gw *m_instance; + + srslte::error_t init_sgi_mb_if(mbms_gw_args_t *args); + srslte::error_t init_m1_u(mbms_gw_args_t *args); + void handle_sgi_md_pdu(srslte::byte_buffer_t *msg); + uint16_t in_cksum(uint16_t *iphdr, int count); + + /* Members */ + bool m_running; + srslte::byte_buffer_pool *m_pool; + srslte::log_filter *m_mbms_gw_log; + + bool m_sgi_mb_up; + int m_sgi_mb_if; + + bool m_m1u_up; + int m_m1u; + struct sockaddr_in m_m1u_multi_addr; +}; + +} // namespace srsepc + +#endif // SGW_H diff --git a/srsepc/mbms.conf.example b/srsepc/mbms.conf.example new file mode 100644 index 000000000..393be7648 --- /dev/null +++ b/srsepc/mbms.conf.example @@ -0,0 +1,39 @@ +##################################################################### +# srsEPC configuration file +##################################################################### + +##################################################################### +# MBMS-GW configuration +# +# name: MBMS-GW name +# sgi_mb_if_addr: SGi-mb interface IP address +# m1u_addr: Multicast group for eNBs (FIXME this should be setup with M2/M3) +# +##################################################################### +[mbms_gw] +name = srsmbmsgw01 +sgi_mb_if_addr = 172.16.1.1 +m1u_multi_addr = 239.255.0.1 + +#################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. s1ap_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. s1ap_hex_limit = 32 +# +# Logging layers: mbms_gw, all +# Logging levels: debug, info, warning, error, none +# +# filename: File path to use for log output. Can be set to stdout +# to print logs to standard output +##################################################################### +[log] +all_level = debug +all_hex_limit = 32 +filename = /tmp/mbms.log diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index e6f4c2538..9fb56ce30 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -1,7 +1,27 @@ +# +# Copyright 2013-2017 Software Radio Systems Limited +# +# This file is part of srsLTE +# +# srsLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsLTE 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 Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# add_subdirectory(mme) add_subdirectory(hss) add_subdirectory(spgw) +add_subdirectory(mbms-gw) # Link libstdc++ and libgcc if(BUILD_STATIC) @@ -26,11 +46,22 @@ target_link_libraries(srsepc srsepc_mme ${LIBCONFIGPP_LIBRARIES} ${SCTP_LIBRARIES}) +add_executable(srsmbms mbms-gw/main.cc ) +target_link_libraries(srsmbms srsepc_mbms_gw + srslte_upper + srslte_common + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES} + ${SEC_LIBRARIES} + ${LIBCONFIGPP_LIBRARIES} + ${SCTP_LIBRARIES}) if (RPATH) set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") endif (RPATH) install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) +install(TARGETS srsmbms DESTINATION ${RUNTIME_DIR}) ######################################################################## # Option to run command after build (useful for remote builds) diff --git a/srsepc/src/mbms-gw/CMakeLists.txt b/srsepc/src/mbms-gw/CMakeLists.txt new file mode 100644 index 000000000..412753b57 --- /dev/null +++ b/srsepc/src/mbms-gw/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 Software Radio Systems Limited +# +# This file is part of srsLTE +# +# srsLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsLTE 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 Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsepc_mbms_gw STATIC ${SOURCES}) +install(TARGETS srsepc_mbms_gw DESTINATION ${LIBRARY_DIR}) diff --git a/srsepc/src/mbms-gw/main.cc b/srsepc/src/mbms-gw/main.cc new file mode 100644 index 000000000..379d56a36 --- /dev/null +++ b/srsepc/src/mbms-gw/main.cc @@ -0,0 +1,216 @@ +/** + * + * \section COPYRIGHT + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE 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 Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include +#include +#include +#include +#include +#include +#include "srsepc/hdr/mbms-gw/mbms-gw.h" + +using namespace std; +using namespace srsepc; +namespace bpo = boost::program_options; + +bool running = true; + +void +sig_int_handler(int signo){ + running = false; +} + +typedef struct { + std::string mbms_gw_level; + int mbms_gw_hex_limit; + std::string all_level; + int all_hex_limit; + std::string filename; +}log_args_t; + +typedef struct { + mbms_gw_args_t mbms_gw_args; + log_args_t log_args; +}all_args_t; + +srslte::LOG_LEVEL_ENUM +level(std::string l) +{ + boost::to_upper(l); + if("NONE" == l){ + return srslte::LOG_LEVEL_NONE; + }else if("ERROR" == l){ + return srslte::LOG_LEVEL_ERROR; + }else if("WARNING" == l){ + return srslte::LOG_LEVEL_WARNING; + }else if("INFO" == l){ + return srslte::LOG_LEVEL_INFO; + }else if("DEBUG" == l){ + return srslte::LOG_LEVEL_DEBUG; + }else{ + return srslte::LOG_LEVEL_NONE; + } +} + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +string config_file; + +void +parse_args(all_args_t *args, int argc, char* argv[]) { + + string mbms_gw_name; + string mbms_gw_sgi_mb_if_addr; + string mbms_gw_m1u_multi_addr; + + string log_filename; + + // Command line only options + bpo::options_description general("General options"); + general.add_options() + ("help,h", "Produce help message") + ("version,v", "Print version information and exit") + ; + + // Command line or config file options + bpo::options_description common("Configuration options"); + common.add_options() + + ("mbms_gw.name", bpo::value(&mbms_gw_name)->default_value("srsmbmsgw01"), "MBMS-GW Name") + ("mbms_gw.sgi_mb_if_addr", bpo::value(&mbms_gw_sgi_mb_if_addr)->default_value("172.16.1.1"), "SGi-mb TUN interface Address") + ("mbms_gw.m1u_multi_addr", bpo::value(&mbms_gw_m1u_multi_addr)->default_value("239.255.0.1"), "M1-u GTPu destination multicast address") + + ("log.all_level", bpo::value(&args->log_args.all_level)->default_value("info"), "ALL log level") + ("log.all_hex_limit", bpo::value(&args->log_args.all_hex_limit)->default_value(32), "ALL log hex dump limit") + + ("log.filename", bpo::value(&args->log_args.filename)->default_value("/tmp/mbms.log"),"Log filename") + ; + + // Positional options - config file location + bpo::options_description position("Positional options"); + position.add_options() + ("config_file", bpo::value< string >(&config_file), "MBMS-GW configuration file") + ; + bpo::positional_options_description p; + p.add("config_file", -1); + + // these options are allowed on the command line + bpo::options_description cmdline_options; + cmdline_options.add(common).add(position).add(general); + + // parse the command line and store result in vm + bpo::variables_map vm; + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm); + bpo::notify(vm); + + // help option was given - print usage and exit + if (vm.count("help")) { + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + cout << common << endl << general << endl; + exit(0); + } + + //Parsing Config File + if (!vm.count("config_file")) { + cout << "Error: Configuration file not provided" << endl; + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + exit(0); + } else { + cout << "Reading configuration file " << config_file << "..." << endl; + ifstream conf(config_file.c_str(), ios::in); + if(conf.fail()) { + cout << "Failed to read configuration file " << config_file << " - exiting" << endl; + exit(1); + } + bpo::store(bpo::parse_config_file(conf, common), vm); + bpo::notify(vm); + } + + args->mbms_gw_args.name = mbms_gw_name; + args->mbms_gw_args.sgi_mb_if_addr = mbms_gw_sgi_mb_if_addr; + args->mbms_gw_args.m1u_multi_addr = mbms_gw_m1u_multi_addr; + // Apply all_level to any unset layers + if (vm.count("log.all_level")) { + if(!vm.count("log.mbms_gw_level")) { + args->log_args.mbms_gw_level = args->log_args.all_level; + } + } + + // Apply all_hex_limit to any unset layers + if (vm.count("log.all_hex_limit")) { + if(!vm.count("log.mbms_gw_hex_limit")) { + args->log_args.mbms_gw_hex_limit = args->log_args.all_hex_limit; + } + } + return; +} + + + +int +main (int argc,char * argv[] ) +{ + cout << endl <<"--- Software Radio Systems MBMS ---" << endl << endl; + signal(SIGINT, sig_int_handler); + signal(SIGTERM, sig_int_handler); + signal(SIGKILL, sig_int_handler); + + all_args_t args; + parse_args(&args, argc, argv); + + srslte::logger_stdout logger_stdout; + srslte::logger_file logger_file; + srslte::logger *logger; + + /*Init logger*/ + if (!args.log_args.filename.compare("stdout")) { + logger = &logger_stdout; + } else { + logger_file.init(args.log_args.filename); + logger_file.log("\n--- Software Radio Systems MBMS log ---\n\n"); + logger = &logger_file; + } + + srslte::log_filter mbms_gw_log; + mbms_gw_log.init("MBMS",logger); + mbms_gw_log.set_level(level(args.log_args.mbms_gw_level)); + mbms_gw_log.set_hex_limit(args.log_args.mbms_gw_hex_limit); + + mbms_gw *mbms_gw = mbms_gw::get_instance(); + if(mbms_gw->init(&args.mbms_gw_args,&mbms_gw_log)) { + cout << "Error initializing MBMS-GW" << endl; + exit(1); + } + + mbms_gw->start(); + while(running) { + sleep(1); + } + + mbms_gw->stop(); + mbms_gw->cleanup(); + + cout << std::endl <<"--- exiting ---" << endl; + return 0; +} diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc new file mode 100644 index 000000000..a3d18a68d --- /dev/null +++ b/srsepc/src/mbms-gw/mbms-gw.cc @@ -0,0 +1,385 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE 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 Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "srsepc/hdr/mbms-gw/mbms-gw.h" +#include "srslte/upper/gtpu.h" + +namespace srsepc{ + +mbms_gw* mbms_gw::m_instance = NULL; +pthread_mutex_t mbms_gw_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + +const uint16_t MBMS_GW_BUFFER_SIZE = 2500; + +mbms_gw::mbms_gw(): + m_running(false), + m_sgi_mb_up(false) +{ + return; +} + +mbms_gw::~mbms_gw() +{ + return; +} + +mbms_gw* +mbms_gw::get_instance(void) +{ + pthread_mutex_lock(&mbms_gw_instance_mutex); + if(NULL == m_instance) { + m_instance = new mbms_gw(); + } + pthread_mutex_unlock(&mbms_gw_instance_mutex); + return(m_instance); +} + +void +mbms_gw::cleanup(void) +{ + pthread_mutex_lock(&mbms_gw_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } + pthread_mutex_unlock(&mbms_gw_instance_mutex); +} + +int +mbms_gw::init(mbms_gw_args_t* args, srslte::log_filter *mbms_gw_log) +{ + srslte::error_t err; + m_pool = srslte::byte_buffer_pool::get_instance(); + + //Init log + m_mbms_gw_log = mbms_gw_log; + + err = init_sgi_mb_if(args); + if (err != srslte::ERROR_NONE) + { + m_mbms_gw_log->console("Error initializing SGi-MB.\n"); + m_mbms_gw_log->error("Error initializing SGi-MB.\n"); + return -1; + } + err = init_m1_u(args); + if (err != srslte::ERROR_NONE) + { + m_mbms_gw_log->console("Error initializing SGi-MB.\n"); + m_mbms_gw_log->error("Error initializing SGi-MB.\n"); + return -1; + } + m_mbms_gw_log->info("MBMS GW Initiated\n"); + m_mbms_gw_log->console("MBMS GW Initiated\n"); + return 0; +} + +void +mbms_gw::stop() +{ + if(m_running) + { + if(m_sgi_mb_up) + { + close(m_sgi_mb_if); + m_mbms_gw_log->info("Closed SGi-MB interface\n"); + } + m_running = false; + thread_cancel(); + wait_thread_finish(); + } + return; +} + +srslte::error_t +mbms_gw::init_sgi_mb_if(mbms_gw_args_t *args) +{ + char dev[IFNAMSIZ] = "sgi_mb"; + struct ifreq ifr; + + if(m_sgi_mb_up) + { + return(srslte::ERROR_ALREADY_STARTED); + } + + + // Construct the TUN device + m_sgi_mb_if = open("/dev/net/tun", O_RDWR); + m_mbms_gw_log->info("TUN file descriptor = %d\n", m_sgi_mb_if); + if(m_sgi_mb_if < 0) + { + m_mbms_gw_log->error("Failed to open TUN device: %s\n", strerror(errno)); + return(srslte::ERROR_CANT_START); + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ-1); + ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1]='\0'; + + if(ioctl(m_sgi_mb_if, TUNSETIFF, &ifr) < 0) + { + m_mbms_gw_log->error("Failed to set TUN device name: %s\n", strerror(errno)); + close(m_sgi_mb_if); + return(srslte::ERROR_CANT_START); + } + + // Bring up the interface + int sgi_mb_sock = socket(AF_INET, SOCK_DGRAM, 0); + if(sgi_mb_sock<0) + { + m_mbms_gw_log->error("Failed to bring up socket: %s\n", strerror(errno)); + close(m_sgi_mb_if); + return(srslte::ERROR_CANT_START); + } + + if(ioctl(sgi_mb_sock, SIOCGIFFLAGS, &ifr) < 0) + { + m_mbms_gw_log->error("Failed to bring up interface: %s\n", strerror(errno)); + close(m_sgi_mb_if); + close(sgi_mb_sock); + return(srslte::ERROR_CANT_START); + } + + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + if(ioctl(sgi_mb_sock, SIOCSIFFLAGS, &ifr) < 0) + { + m_mbms_gw_log->error("Failed to set socket flags: %s\n", strerror(errno)); + close(sgi_mb_sock); + close(m_sgi_mb_if); + return(srslte::ERROR_CANT_START); + } + + //Set IP of the interface + struct sockaddr_in *addr = (struct sockaddr_in*)&ifr.ifr_addr; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = inet_addr(args->sgi_mb_if_addr.c_str()); + addr->sin_port = 0; + + if (ioctl(sgi_mb_sock, SIOCSIFADDR, &ifr) < 0) { + m_mbms_gw_log->error("Failed to set TUN interface IP. Address: %s, Error: %s\n", args->sgi_mb_if_addr.c_str(), strerror(errno)); + close(m_sgi_mb_if); + close(sgi_mb_sock); + return srslte::ERROR_CANT_START; + } + + ifr.ifr_netmask.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0"); + if (ioctl(sgi_mb_sock, SIOCSIFNETMASK, &ifr) < 0) { + m_mbms_gw_log->error("Failed to set TUN interface Netmask. Error: %s\n", strerror(errno)); + close(m_sgi_mb_if); + close(sgi_mb_sock); + return srslte::ERROR_CANT_START; + } + + m_sgi_mb_up = true; + close(sgi_mb_sock); + return(srslte::ERROR_NONE); +} + +srslte::error_t +mbms_gw::init_m1_u(mbms_gw_args_t *args) +{ + int addrlen; + struct sockaddr_in addr; + m_m1u = socket(AF_INET, SOCK_DGRAM, 0); + if(m_m1u<0) + { + m_mbms_gw_log->error("Failed to open socket: %s\n", strerror(errno)); + return srslte::ERROR_CANT_START; + } + m_m1u_up = true; + + /* set no loopback */ + char loopch = 0; + if(setsockopt(m_m1u, IPPROTO_IP, IP_MULTICAST_LOOP, (char*)&loopch, sizeof(char))<0){ + m_mbms_gw_log->error("Failed to disable loopback: %s\n", strerror(errno)); + return srslte::ERROR_CANT_START; + } else { + m_mbms_gw_log->debug("Loopback disabled\n"); + } + + /* Set local interface for outbound multicast packets*/ + /* The IP must be associated with a local multicast capable interface */ + struct in_addr local_if; + local_if.s_addr = inet_addr("127.0.1.200"); + if(setsockopt(m_m1u, IPPROTO_IP, IP_MULTICAST_IF, (char*)&local_if, sizeof(struct in_addr))<0){ + perror("Error setting multicast interface.\n"); + } else { + printf("Multicast interface specified.\n"); + } + + bzero(&m_m1u_multi_addr,sizeof(m_m1u_multi_addr)); + m_m1u_multi_addr.sin_family = AF_INET; + m_m1u_multi_addr.sin_port = htons(GTPU_RX_PORT+1); + m_m1u_multi_addr.sin_addr.s_addr = inet_addr(args->m1u_multi_addr.c_str()); + m_mbms_gw_log->info("Initialized M1-U\n"); + + return srslte::ERROR_NONE; +} + +void +mbms_gw::run_thread() +{ + //Mark the thread as running + m_running=true; + srslte::byte_buffer_t *msg; + msg = m_pool->allocate(); + + + uint8_t seq = 0; + while(m_running) + { + msg->reset(); + int n; + do{ + n = read(m_sgi_mb_if, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES); + }while(n == -1 && errno == EAGAIN); + + if(n<0){ + m_mbms_gw_log->error("Error reading from TUN interface. Error: %s\n", strerror(errno)); + } + else{ + msg->N_bytes = n; + handle_sgi_md_pdu(msg); + } + } + m_pool->deallocate(msg); + return; +} + +void +mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) +{ + uint8_t version; + srslte::gtpu_header_t header; + in_addr_t baddr = inet_addr("172.16.0.255"); + in_addr_t saddr = inet_addr("172.16.0.254"); + + //Setup GTP-U header + header.flags = 0x30; + header.message_type = 0xFF; + header.length = msg->N_bytes; + header.teid = 0xAAAA; //FIXME Harcoded TEID for now + + //Sanity Check IP packet + if(msg->N_bytes < 20) + { + m_mbms_gw_log->error("IPv4 min len: %d, drop msg len %d\n", 20, msg->N_bytes); + return; + } + + //IP+UDP Headers + struct iphdr *iph = (struct iphdr *) msg->msg; + struct udphdr *udph = (struct udphdr *) (msg->msg + iph->ihl*4); + if(iph->version != 4) + { + m_mbms_gw_log->warning("IPv6 not supported yet.\n"); + return; + } + + //Replace Destination IP with broadcast address + iph->daddr = baddr; + + //Replace Source IP with address in same subnet + iph->saddr = saddr; + + //Replace IP cheksum + iph->check = 0; + iph->check = in_cksum((uint16_t*)msg->msg,4*(msg->msg[0] & 0x0F)); + + //Set Pseudo Header + struct pseudo_hdr phdr; + phdr.src_addr = iph->saddr; + phdr.dst_addr = iph->daddr; + phdr.protocol = IPPROTO_UDP; + phdr.placeholder = 0; + phdr.udp_len = udph->len; + + //Set Pseudo Datagram + udph->check = 0; + int psize = sizeof(struct pseudo_hdr) + ntohs(udph->len); + uint8_t * pseudo_dgram = (uint8_t*) malloc(psize); + memcpy(pseudo_dgram, &phdr,sizeof(struct pseudo_hdr)); + memcpy(pseudo_dgram+sizeof(pseudo_hdr),udph,ntohs(udph->len)); + + //Recompute UDP checksum + udph->check = in_cksum((uint16_t*) pseudo_dgram, psize); + free(pseudo_dgram); + + //Write GTP-U header into packet + if(!srslte::gtpu_write_header(&header, msg)) + { + m_mbms_gw_log->console("Error writing GTP-U header on PDU\n"); + } + + int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, + (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); + if(n<0){ + m_mbms_gw_log->console("Error writting to M1-U socket.\n"); + } + else{ + m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); + } +} + +uint16_t +mbms_gw::in_cksum(uint16_t *iphdr, int count) +{ + + //RFC 1071 + uint32_t sum = 0; + uint16_t padd = 0; + uint16_t result; + while(count > 1) + { + sum+= *iphdr++; + count -= 2; + } + if( count > 0 ) + { + padd = * (uint8_t *) iphdr; + sum += padd; + } + /*Fold 32-bit sum to 16-bit*/ + // while(sum>>16) + // sum = (sum & 0xffff) + (sum >> 16); + sum = (sum>>16)+(sum & 0xFFFF); + sum = sum + (sum >> 16); + result = (uint16_t) ~sum; + return result; +} + +} //namespace srsepc diff --git a/srsue/hdr/mac/demux.h b/srsue/hdr/mac/demux.h index 410865c55..c20b8ebc2 100644 --- a/srsue/hdr/mac/demux.h +++ b/srsue/hdr/mac/demux.h @@ -58,7 +58,7 @@ public: bool get_uecrid_successful(); void process_pdu(uint8_t *pdu, uint32_t nof_bytes, srslte::pdu_queue::channel_t channel, uint32_t tstamp); - + void mch_start_rx(uint32_t lcid); private: const static int MAX_PDU_LEN = 150*1024/8; // ~ 150 Mbps const static int NOF_BUFFER_PDUS = 64; // Number of PDU buffers per HARQ pid @@ -69,9 +69,13 @@ private: void *uecrid_callback_arg; srslte::sch_pdu mac_msg; + srslte::mch_pdu mch_mac_msg; srslte::sch_pdu pending_mac_msg; - + uint8_t mch_lcids[SRSLTE_N_MCH_LCIDS]; void process_sch_pdu(srslte::sch_pdu *pdu); + void process_mch_pdu(srslte::mch_pdu *pdu); + + bool process_ce(srslte::sch_subh *subheader); bool is_uecrid_successful; diff --git a/srsue/hdr/mac/mac.h b/srsue/hdr/mac/mac.h index 4f5014532..f2800f3b1 100644 --- a/srsue/hdr/mac/mac.h +++ b/srsue/hdr/mac/mac.h @@ -65,17 +65,23 @@ public: void new_grant_ul_ack(mac_grant_t grant, bool ack, tb_action_ul_t *action); void harq_recv(uint32_t tti, bool ack, tb_action_ul_t *action); void new_grant_dl(mac_grant_t grant, tb_action_dl_t *action); + void new_mch_dl(srslte_ra_dl_grant_t phy_grant, tb_action_dl_t *action); void tb_decoded(bool ack, uint32_t tb_idx, srslte_rnti_type_t rnti_type, uint32_t harq_pid); void bch_decoded_ok(uint8_t *payload, uint32_t len); - void pch_decoded_ok(uint32_t len); + void pch_decoded_ok(uint32_t len); + void mch_decoded_ok(uint32_t len); + void process_mch_pdu(uint32_t len); + + void set_mbsfn_config(uint32_t nof_mbsfn_services); - /******** Interface from RLC (RLC -> MAC) ****************/ + /******** Interface from RRC (RRC -> MAC) ****************/ void bcch_start_rx(); void bcch_start_rx(int si_window_start, int si_window_length); void pcch_start_rx(); void clear_rntis(); void setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority, int PBR_x_tti, uint32_t BSD); + void mch_start_rx(uint32_t lcid); void reconfiguration(); void reset(); void wait_uplink(); @@ -119,6 +125,7 @@ private: rlc_interface_mac *rlc_h; rrc_interface_mac *rrc_h; srslte::log *log_h; + mac_interface_phy::mac_phy_cfg_mbsfn_t phy_mbsfn_cfg; // MAC configuration mac_cfg_t config; @@ -147,6 +154,13 @@ private: srslte_softbuffer_rx_t pch_softbuffer; uint8_t pch_payload_buffer[pch_payload_buffer_sz]; + /* Buffers for MCH reception (not included in DL HARQ) */ + const static uint32_t mch_payload_buffer_sz = SRSLTE_MAX_BUFFER_SIZE_BYTES; + srslte_softbuffer_rx_t mch_softbuffer; + uint8_t mch_payload_buffer[mch_payload_buffer_sz]; + srslte::mch_pdu mch_msg; + + /* Functions for MAC Timers */ uint32_t timer_alignment; diff --git a/srsue/hdr/phy/phch_common.h b/srsue/hdr/phy/phch_common.h index 6fce44c1c..239fafa10 100644 --- a/srsue/hdr/phy/phch_common.h +++ b/srsue/hdr/phy/phch_common.h @@ -27,6 +27,9 @@ #ifndef SRSUE_PHCH_COMMON_H #define SRSUE_PHCH_COMMON_H +#define TX_MODE_CONTINUOUS 1 + + #include #include #include @@ -34,12 +37,12 @@ #include "srslte/interfaces/ue_interfaces.h" #include "srslte/radio/radio.h" #include "srslte/common/log.h" +#include "srslte/common/gen_mch_tables.h" #include "phy_metrics.h" namespace srsue { - class chest_feedback_itf { public: @@ -48,139 +51,188 @@ public: virtual void set_cfo(float cfo) = 0; }; -/* Subclass that manages variables common to all workers */ -class phch_common { -public: - /* Common variables used by all phy workers */ - phy_interface_rrc::phy_cfg_t *config; - phy_args_t *args; - rrc_interface_phy *rrc; - mac_interface_phy *mac; - - /* Power control variables */ - float pathloss; - float cur_pathloss; - float p0_preamble; - float cur_radio_power; - float cur_pusch_power; - float avg_rsrp; - float avg_rsrp_dbm; - float avg_rsrq_db; - float avg_rssi_dbm; - float last_radio_rssi; - float rx_gain_offset; - float avg_snr_db_cqi; - float avg_noise; - - uint32_t pcell_report_period; - - // Save last TBS for mcs>28 cases - int last_dl_tbs[2*HARQ_DELAY_MS][SRSLTE_MAX_CODEWORDS]; - uint32_t last_dl_tti[2*HARQ_DELAY_MS]; - - int last_ul_tbs[2*HARQ_DELAY_MS]; - uint32_t last_ul_tti[2*HARQ_DELAY_MS]; - srslte_mod_t last_ul_mod[2*HARQ_DELAY_MS]; - - uint8_t last_ri; - uint8_t last_pmi; - - phch_common(uint32_t max_mutex = 3); - void init(phy_interface_rrc::phy_cfg_t *config, - phy_args_t *args, - srslte::log *_log, - srslte::radio *_radio, - rrc_interface_phy *rrc, - mac_interface_phy *_mac); - - /* For RNTI searches, -1 means now or forever */ - void set_ul_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); - uint16_t get_ul_rnti(uint32_t tti); - srslte_rnti_type_t get_ul_rnti_type(); - - void set_dl_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); - uint16_t get_dl_rnti(uint32_t tti); - srslte_rnti_type_t get_dl_rnti_type(); - - void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]); - bool get_pending_rar(uint32_t tti, srslte_dci_rar_grant_t *rar_grant = NULL); - - void reset_pending_ack(uint32_t tti); - void set_pending_ack(uint32_t tti, uint32_t I_lowest, uint32_t n_dmrs); - bool get_pending_ack(uint32_t tti); - bool get_pending_ack(uint32_t tti, uint32_t *I_lowest, uint32_t *n_dmrs); - bool is_any_pending_ack(); - - void worker_end(uint32_t tti, bool tx_enable, cf_t *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); - - void set_nof_mutex(uint32_t nof_mutex); - - bool sr_enabled; - int sr_last_tx_tti; - - srslte::radio* get_radio(); - - void set_cell(const srslte_cell_t &c); - uint32_t get_nof_prb(); - void set_dl_metrics(const dl_metrics_t &m); - void get_dl_metrics(dl_metrics_t &m); - void set_ul_metrics(const ul_metrics_t &m); - void get_ul_metrics(ul_metrics_t &m); - void set_sync_metrics(const sync_metrics_t &m); - void get_sync_metrics(sync_metrics_t &m); - - void reset_ul(); - void reset(); - -private: - - std::vector tx_mutex; - - bool is_first_of_burst; - srslte::radio *radio_h; - float cfo; - srslte::log *log_h; - - - bool ul_rnti_active(uint32_t tti); - bool dl_rnti_active(uint32_t tti); - uint16_t ul_rnti, dl_rnti; - srslte_rnti_type_t ul_rnti_type, dl_rnti_type; - int ul_rnti_start, ul_rnti_end, dl_rnti_start, dl_rnti_end; - - float time_adv_sec; - - srslte_dci_rar_grant_t rar_grant; - bool rar_grant_pending; - uint32_t rar_grant_tti; - - typedef struct { - bool enabled; - uint32_t I_lowest; - uint32_t n_dmrs; - } pending_ack_t; - pending_ack_t pending_ack[TTIMOD_SZ]; - - bool is_first_tx; - - uint32_t nof_workers; - uint32_t nof_mutex; - uint32_t max_mutex; - - srslte_cell_t cell; +typedef enum{ + SUBFRAME_TYPE_REGULAR = 0, + SUBFRAME_TYPE_MBSFN, + SUBFRAME_TYPE_N_ITEMS, +} subframe_type_t; +static const char subframe_type_text[SUBFRAME_TYPE_N_ITEMS][20] = {"Regular", "MBSFN"}; - dl_metrics_t dl_metrics; - uint32_t dl_metrics_count; - bool dl_metrics_read; - ul_metrics_t ul_metrics; - uint32_t ul_metrics_count; - bool ul_metrics_read; - sync_metrics_t sync_metrics; - uint32_t sync_metrics_count; - bool sync_metrics_read; -}; - +/* Subframe config */ + +typedef struct { + subframe_type_t sf_type; + uint8_t mbsfn_area_id; + uint8_t non_mbsfn_region_length; + uint8_t mbsfn_mcs; + bool mbsfn_decode; + bool is_mcch; +} subframe_cfg_t; + + +/* Subclass that manages variables common to all workers */ + class phch_common { + public: + + /* Common variables used by all phy workers */ + phy_interface_rrc::phy_cfg_t *config; + phy_args_t *args; + rrc_interface_phy *rrc; + mac_interface_phy *mac; + srslte_ue_ul_t ue_ul; + + /* Power control variables */ + float pathloss; + float cur_pathloss; + float p0_preamble; + float cur_radio_power; + float cur_pusch_power; + float avg_rsrp; + float avg_rsrp_cqi; + float avg_rsrp_dbm; + float avg_rsrp_sync_dbm; + float avg_rsrq_db; + float avg_rssi_dbm; + float last_radio_rssi; + float rx_gain_offset; + float avg_snr_db_cqi; + float avg_snr_db_sync; + + float avg_noise; + bool pcell_meas_enabled; + + uint32_t pcell_report_period; + bool pcell_first_measurement; + + // Save last TBS for mcs>28 cases + int last_dl_tbs[2*HARQ_DELAY_MS][SRSLTE_MAX_CODEWORDS]; + uint32_t last_dl_tti[2*HARQ_DELAY_MS]; + + int last_ul_tbs[2*HARQ_DELAY_MS]; + uint32_t last_ul_tti[2*HARQ_DELAY_MS]; + srslte_mod_t last_ul_mod[2*HARQ_DELAY_MS]; + uint8_t last_ri; + uint8_t last_pmi; + + phch_common(uint32_t max_mutex = 3); + void init(phy_interface_rrc::phy_cfg_t *config, + phy_args_t *args, + srslte::log *_log, + srslte::radio *_radio, + rrc_interface_phy *rrc, + mac_interface_phy *_mac); + + /* For RNTI searches, -1 means now or forever */ + void set_ul_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); + uint16_t get_ul_rnti(uint32_t tti); + srslte_rnti_type_t get_ul_rnti_type(); + + void set_dl_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); + uint16_t get_dl_rnti(uint32_t tti); + srslte_rnti_type_t get_dl_rnti_type(); + + void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]); + bool get_pending_rar(uint32_t tti, srslte_dci_rar_grant_t *rar_grant = NULL); + + void reset_pending_ack(uint32_t tti); + void set_pending_ack(uint32_t tti, uint32_t I_lowest, uint32_t n_dmrs); + bool get_pending_ack(uint32_t tti); + bool get_pending_ack(uint32_t tti, uint32_t *I_lowest, uint32_t *n_dmrs); + bool is_any_pending_ack(); + + void worker_end(uint32_t tti, bool tx_enable, cf_t *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); + + void set_nof_mutex(uint32_t nof_mutex); + + bool sr_enabled; + int sr_last_tx_tti; + + + srslte::radio* get_radio(); + + void set_cell(const srslte_cell_t &c); + uint32_t get_nof_prb(); + void set_dl_metrics(const dl_metrics_t &m); + void get_dl_metrics(dl_metrics_t &m); + void set_ul_metrics(const ul_metrics_t &m); + void get_ul_metrics(ul_metrics_t &m); + void set_sync_metrics(const sync_metrics_t &m); + void get_sync_metrics(sync_metrics_t &m); + + void reset_ul(); + void reset(); + + // MBSFN helpers + void build_mch_table(); + void build_mcch_table(); + void set_mcch(); + void get_sf_config(subframe_cfg_t *cfg, uint32_t phy_tti); + void set_mch_period_stop(uint32_t stop); + + private: + + bool have_mtch_stop; + pthread_mutex_t mtch_mutex; + pthread_cond_t mtch_cvar; + + + + std::vector tx_mutex; + + bool is_first_of_burst; + srslte::radio *radio_h; + float cfo; + srslte::log *log_h; + + + bool ul_rnti_active(uint32_t tti); + bool dl_rnti_active(uint32_t tti); + uint16_t ul_rnti, dl_rnti; + srslte_rnti_type_t ul_rnti_type, dl_rnti_type; + int ul_rnti_start, ul_rnti_end, dl_rnti_start, dl_rnti_end; + + float time_adv_sec; + + srslte_dci_rar_grant_t rar_grant; + bool rar_grant_pending; + uint32_t rar_grant_tti; + + typedef struct { + bool enabled; + uint32_t I_lowest; + uint32_t n_dmrs; + } pending_ack_t; + pending_ack_t pending_ack[TTIMOD_SZ]; + + bool is_first_tx; + + uint32_t nof_workers; + uint32_t nof_mutex; + uint32_t max_mutex; + + srslte_cell_t cell; + + dl_metrics_t dl_metrics; + uint32_t dl_metrics_count; + bool dl_metrics_read; + ul_metrics_t ul_metrics; + uint32_t ul_metrics_count; + bool ul_metrics_read; + sync_metrics_t sync_metrics; + uint32_t sync_metrics_count; + bool sync_metrics_read; + + // MBSFN + bool sib13_configured; + bool mcch_configured; + uint32_t mch_period_stop; + uint8_t mch_table[40]; + uint8_t mcch_table[10]; + + bool is_mch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti); + bool is_mcch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti); + }; } // namespace srsue #endif // SRSUE_PDCH_COMMON_H diff --git a/srsue/hdr/phy/phch_worker.h b/srsue/hdr/phy/phch_worker.h index 14687da73..ff167d20e 100644 --- a/srsue/hdr/phy/phch_worker.h +++ b/srsue/hdr/phy/phch_worker.h @@ -89,9 +89,10 @@ private: /* Internal methods */ - bool extract_fft_and_pdcch_llr(); - void compute_ri(uint8_t *ri, uint8_t *pmi, float *sinr); + void compute_ri(uint8_t *ri, uint8_t *pmi, float *sinr); + bool extract_fft_and_pdcch_llr(subframe_cfg_t sf_cfg); + /* ... for DL */ bool decode_pdcch_ul(mac_interface_phy::mac_grant_t *grant); bool decode_pdcch_dl(mac_interface_phy::mac_grant_t *grant); @@ -105,6 +106,11 @@ private: uint32_t pid, bool acks[SRSLTE_MAX_CODEWORDS]); + bool decode_pmch(srslte_ra_dl_grant_t *grant, + uint8_t *payload, + srslte_softbuffer_rx_t* softbuffer, + uint16_t mbsfn_area_id); + /* ... for UL */ void encode_pusch(srslte_ra_ul_grant_t *grant, uint8_t *payload, uint32_t current_tx_nb, srslte_softbuffer_tx_t *softbuffer, uint32_t rv, uint16_t rnti, bool is_from_rar); diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index a1a7c2a66..38bcb6d8b 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -120,7 +120,7 @@ public: void pdcch_dl_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1); void pdcch_ul_search_reset(); void pdcch_dl_search_reset(); - + /* Get/Set PHY parameters interface from RRC */ void get_config(phy_cfg_t *phy_cfg); void set_config(phy_cfg_t *phy_cfg); @@ -128,7 +128,13 @@ public: void set_config_common(phy_cfg_common_t *common); void set_config_tdd(LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd); void set_config_64qam_en(bool enable); - + void set_config_mbsfn_sib2(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2); + void set_config_mbsfn_sib13(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13); + void set_config_mbsfn_mcch(LIBLTE_RRC_MCCH_MSG_STRUCT *mcch); + + /*Set MAC->PHY MCH period stopping point*/ + void set_mch_period_stop(uint32_t stop); + float get_phr(); float get_pathloss_db(); diff --git a/srsue/hdr/ue.h b/srsue/hdr/ue.h index 5a1ba31e7..7d33749b1 100644 --- a/srsue/hdr/ue.h +++ b/srsue/hdr/ue.h @@ -73,6 +73,8 @@ public: bool deattach(); bool is_attached(); void start_plot(); + void print_mbms(); + bool mbms_service_start(uint32_t serv, uint32_t port); void print_pool(); diff --git a/srsue/hdr/ue_base.h b/srsue/hdr/ue_base.h index 41abd15e0..4c71ba38f 100644 --- a/srsue/hdr/ue_base.h +++ b/srsue/hdr/ue_base.h @@ -116,6 +116,7 @@ typedef struct { bool print_buffer_state; bool metrics_csv_enable; std::string metrics_csv_filename; + int mbms_service; }expert_args_t; typedef struct { @@ -167,6 +168,9 @@ public: virtual void print_pool() = 0; virtual void radio_overflow() = 0; + + virtual void print_mbms() = 0; + virtual bool mbms_service_start(uint32_t serv, uint32_t port) = 0; void handle_rf_msg(srslte_rf_error_t error); diff --git a/srsue/hdr/upper/gw.h b/srsue/hdr/upper/gw.h index 3b17eb75b..46059b517 100644 --- a/srsue/hdr/upper/gw.h +++ b/srsue/hdr/upper/gw.h @@ -42,6 +42,7 @@ namespace srsue { class gw :public gw_interface_pdcp ,public gw_interface_nas + ,public gw_interface_rrc ,public thread { public: @@ -50,14 +51,17 @@ public: void stop(); void get_metrics(gw_metrics_t &m); + void set_netmask(std::string netmask); // PDCP interface void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu); + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu); // NAS interface srslte::error_t setup_if_addr(uint32_t ip_addr, char *err_str); - void set_netmask(std::string netmask); + // RRC interface + void add_mch_port(uint32_t lcid, uint32_t port); private: @@ -70,9 +74,10 @@ private: nas_interface_gw *nas; srslte::byte_buffer_pool *pool; - srslte::log *gw_log; + srslte::srslte_gw_config_t cfg; + bool running; bool run_enable; int32 tun_fd; @@ -88,6 +93,12 @@ private: void run_thread(); srslte::error_t init_if(char *err_str); + + // MBSFN + int mbsfn_sock_fd; // Sink UDP socket file descriptor + struct sockaddr_in mbsfn_sock_addr; // Target address + uint32_t mbsfn_ports[SRSLTE_N_MCH_LCIDS]; // Target ports for MBSFN data + }; } // namespace srsue diff --git a/srsue/hdr/upper/rrc.h b/srsue/hdr/upper/rrc.h index ee1c9e058..33acf8b7c 100644 --- a/srsue/hdr/upper/rrc.h +++ b/srsue/hdr/upper/rrc.h @@ -181,6 +181,9 @@ class cell_t LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3ptr() { return &sib3; } + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13ptr() { + return &sib13; + } uint32_t get_cell_id() { return sib1.cell_id; @@ -232,20 +235,23 @@ class cell_t } phy_interface_rrc::phy_cell_t phy_cell; - bool in_sync; + bool in_sync; + bool has_mcch; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT sib1; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT sib3; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT sib13; + LIBLTE_RRC_MCCH_MSG_STRUCT mcch; - private: +private: float rsrp; + struct timeval last_update; bool has_valid_sib1; bool has_valid_sib2; bool has_valid_sib3; bool has_valid_sib13; - LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT sib1; - LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2; - LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT sib3; - LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT sib13; }; class rrc @@ -267,6 +273,7 @@ public: pdcp_interface_rrc *pdcp_, nas_interface_rrc *nas_, usim_interface_rrc *usim_, + gw_interface_rrc *gw_, srslte::mac_interface_timers *mac_timers_, srslte::log *rrc_log_); @@ -278,6 +285,9 @@ public: // Timeout callback interface void timer_expired(uint32_t timeout_id); void liblte_rrc_log(char *str); + + void print_mbms(); + bool mbms_service_start(uint32_t serv, uint32_t port); // NAS interface void write_sdu(uint32_t lcid, byte_buffer_t *sdu); @@ -310,7 +320,7 @@ public: void write_pdu_bcch_bch(byte_buffer_t *pdu); void write_pdu_bcch_dlsch(byte_buffer_t *pdu); void write_pdu_pcch(byte_buffer_t *pdu); - + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu); private: @@ -336,7 +346,8 @@ private: pdcp_interface_rrc *pdcp; nas_interface_rrc *nas; usim_interface_rrc *usim; - + gw_interface_rrc *gw; + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; LIBLTE_RRC_UL_CCCH_MSG_STRUCT ul_ccch_msg; LIBLTE_RRC_DL_CCCH_MSG_STRUCT dl_ccch_msg; @@ -436,7 +447,7 @@ private: uint32_t sib_start_tti(uint32_t tti, uint32_t period, uint32_t offset, uint32_t sf); const static int SIB_SEARCH_TIMEOUT_MS = 1000; - const static uint32_t NOF_REQUIRED_SIBS = 3; // SIB1, SIB2 and SIB3 + const static uint32_t NOF_REQUIRED_SIBS = 13; // SIB1, SIB2 and SIB3 bool initiated; bool ho_start; @@ -619,12 +630,14 @@ private: void handle_sib13(); void apply_sib2_configs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2); + void apply_sib13_configs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13); void handle_con_setup(LIBLTE_RRC_CONNECTION_SETUP_STRUCT *setup); void handle_con_reest(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *setup); void handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig); void add_srb(LIBLTE_RRC_SRB_TO_ADD_MOD_STRUCT *srb_cnfg); void add_drb(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb_cnfg); void release_drb(uint8_t lcid); + void add_mrb(uint32_t lcid, uint32_t port); bool apply_rr_config_dedicated(LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT *cnfg); void apply_phy_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg, bool apply_defaults); void apply_mac_config_dedicated(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_cfg, bool apply_defaults); diff --git a/srsue/src/mac/demux.cc b/srsue/src/mac/demux.cc index dd481c76c..28e17d5b0 100644 --- a/srsue/src/mac/demux.cc +++ b/srsue/src/mac/demux.cc @@ -36,7 +36,7 @@ namespace srsue { -demux::demux() : mac_msg(20), pending_mac_msg(20), rlc(NULL) +demux::demux() : mac_msg(20), mch_mac_msg(20), pending_mac_msg(20), rlc(NULL) { } @@ -47,6 +47,7 @@ void demux::init(phy_interface_mac_common* phy_h_, rlc_interface_mac *rlc_, srsl rlc = rlc_; time_alignment_timer = time_alignment_timer_; pdus.init(this, log_h); + bzero(&mch_lcids, SRSLTE_N_MCH_LCIDS); } void demux::set_uecrid_callback(bool (*callback)(void*,uint64_t), void *arg) { @@ -129,7 +130,10 @@ void demux::push_pdu_bcch(uint8_t *buff, uint32_t nof_bytes, uint32_t tstamp) { } void demux::push_pdu_mch(uint8_t *buff, uint32_t nof_bytes, uint32_t tstamp) { - pdus.push(buff, nof_bytes, srslte::pdu_queue::MCH, tstamp); + uint8_t *mch_buffer_ptr = request_buffer(nof_bytes); + memcpy(mch_buffer_ptr, buff, nof_bytes); + pdus.push(mch_buffer_ptr, nof_bytes, srslte::pdu_queue::MCH, tstamp); + mch_buffer_ptr = NULL; } bool demux::process_pdus() @@ -145,16 +149,17 @@ void demux::process_pdu(uint8_t *mac_pdu, uint32_t nof_bytes, srslte::pdu_queue: // Unpack DLSCH MAC PDU mac_msg.init_rx(nof_bytes); mac_msg.parse_packet(mac_pdu); - process_sch_pdu(&mac_msg); - //srslte_vec_fprint_byte(stdout, mac_pdu, nof_bytes); - pdus.deallocate(mac_pdu); break; case srslte::pdu_queue::BCH: rlc->write_pdu_bcch_dlsch(mac_pdu, nof_bytes); break; case srslte::pdu_queue::MCH: + mch_mac_msg.init_rx(nof_bytes); + mch_mac_msg.parse_packet(mac_pdu); + deallocate(mac_pdu); + process_mch_pdu(&mch_mac_msg); // Process downlink MCH break; } @@ -168,7 +173,7 @@ void demux::process_sch_pdu(srslte::sch_pdu *pdu_msg) if (pdu_msg->get()->get_sdu_lcid() == 0) { uint8_t *x = pdu_msg->get()->get_sdu_ptr(); uint32_t sum = 0; - for (int i=0;iget()->get_payload_size();i++) { + for (uint32_t i=0;iget()->get_payload_size();i++) { sum += x[i]; } if (sum == 0) { @@ -196,6 +201,41 @@ void demux::process_sch_pdu(srslte::sch_pdu *pdu_msg) } } } +void demux::process_mch_pdu(srslte::mch_pdu *mch_msg){ + //disgarding headers that have already been processed + while(mch_msg->next()){ + + if(srslte::mch_subh::MCH_SCHED_INFO == mch_msg->get()->ce_type()){ + uint16_t stop; + uint8_t lcid; + if(mch_msg->get()->get_next_mch_sched_info(&lcid, &stop)) { + Info("MCH Sched Info: LCID: %d, Stop: %d, tti is %d \n", lcid, stop, phy_h->get_current_tti()); + } + } + if(mch_msg->get()->is_sdu()) { + uint32_t lcid = mch_msg->get()->get_sdu_lcid(); + + if(lcid >= SRSLTE_N_MCH_LCIDS) { + Error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_MCH_LCIDS, lcid); + return; + } + Debug("Wrote MCH LCID=%d to RLC\n", lcid); + if(1 == mch_lcids[lcid]) { + rlc->write_pdu_mch(lcid, mch_msg->get()->get_sdu_ptr(), mch_msg->get()->get_payload_size()); + } + } + } +} + +void demux::mch_start_rx(uint32_t lcid) +{ + if(lcid < 32) { + Info("MCH Channel Setup: LCID=%d\n", lcid); + mch_lcids[lcid] = 1; + } else { + Error("MCH Channel Setup: invalid LCID=%d\n", lcid); + } +} bool demux::process_ce(srslte::sch_subh *subh) { switch(subh->ce_type()) { diff --git a/srsue/src/mac/mac.cc b/srsue/src/mac/mac.cc index 03b57bc8f..ffa0ca1a3 100644 --- a/srsue/src/mac/mac.cc +++ b/srsue/src/mac/mac.cc @@ -43,7 +43,8 @@ namespace srsue { mac::mac() : timers(64), mux_unit(MAC_NOF_HARQ_PROC), - pdu_process_thread(&demux_unit) + pdu_process_thread(&demux_unit), + mch_msg(10) { pcap = NULL; bzero(&metrics, sizeof(mac_metrics_t)); @@ -58,6 +59,7 @@ bool mac::init(phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac tti = 0; srslte_softbuffer_rx_init(&pch_softbuffer, 100); + srslte_softbuffer_rx_init(&mch_softbuffer, 100); timer_alignment = timers.get_unique_id(); contention_resolution_timer = timers.get_unique_id(); @@ -220,6 +222,34 @@ void mac::pch_decoded_ok(uint32_t len) } } +void mac::mch_decoded_ok(uint32_t len) +{ + // Parse MAC header + mch_msg.init_rx(len); + + mch_msg.parse_packet(mch_payload_buffer); + while(mch_msg.next()) { + for(uint32_t i = 0; i < phy_mbsfn_cfg.nof_mbsfn_services;i++) { + if(srslte::mch_subh::MCH_SCHED_INFO == mch_msg.get()->ce_type()) { + uint16_t stop; + uint8_t lcid; + if(mch_msg.get()->get_next_mch_sched_info(&lcid, &stop)) { + phy_h->set_mch_period_stop(stop); + Info("MCH Sched Info: LCID: %d, Stop: %d, tti is %d \n", lcid, stop, phy_h->get_current_tti()); + } + } + } + } + + demux_unit.push_pdu_mch(mch_payload_buffer, len, 0); + pdu_process_thread.notify(); + if (pcap) { + pcap->write_dl_mch(mch_payload_buffer, len, true, phy_h->get_current_tti()); + } + + metrics.rx_brate += len*8; +} + void mac::tb_decoded(bool ack, uint32_t tb_idx, srslte_rnti_type_t rnti_type, uint32_t harq_pid) { if (rnti_type == SRSLTE_RNTI_RAR) { @@ -303,6 +333,16 @@ void mac::new_grant_ul_ack(mac_interface_phy::mac_grant_t grant, bool ack, mac_i } } +void mac::new_mch_dl(srslte_ra_dl_grant_t phy_grant, tb_action_dl_t *action) +{ + memcpy(&action->phy_grant, &phy_grant, sizeof(srslte_phy_grant_t)); + action->generate_ack = false; + action->decode_enabled[0] = true; + srslte_softbuffer_rx_reset_cb(&mch_softbuffer, 1); + action->payload_ptr[0] = mch_payload_buffer; + action->softbuffers[0] = &mch_softbuffer; +} + void mac::harq_recv(uint32_t tti, bool ack, mac_interface_phy::tb_action_ul_t* action) { int tbs = ul_harq.get_current_tbs(tti); @@ -383,6 +423,13 @@ void mac::get_config(mac_cfg_t* mac_cfg) memcpy(mac_cfg, &config, sizeof(mac_cfg_t)); } +void mac::set_mbsfn_config(uint32_t nof_mbsfn_services) +{ + //cfg->nof_mbsfn_services = config.mbsfn.mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size; + phy_mbsfn_cfg.nof_mbsfn_services = nof_mbsfn_services; +} + + void mac::set_config(mac_cfg_t* mac_cfg) { memcpy(&config, mac_cfg, sizeof(mac_cfg_t)); @@ -415,6 +462,11 @@ void mac::setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority, int PBR_x_t bsr_procedure.set_priority(lcid, priority); } +void mac::mch_start_rx(uint32_t lcid) +{ + demux_unit.mch_start_rx(lcid); +} + void mac::get_metrics(mac_metrics_t &m) { Info("DL retx: %.2f \%%, perpkt: %.2f, UL retx: %.2f \%% perpkt: %.2f\n", diff --git a/srsue/src/mac/mux.cc b/srsue/src/mac/mux.cc index 6d1edbebf..361c0bea4 100644 --- a/srsue/src/mac/mux.cc +++ b/srsue/src/mac/mux.cc @@ -188,7 +188,7 @@ uint8_t* mux::pdu_get(uint8_t *payload, uint32_t pdu_sz, uint32_t tx_tti, uint32 bsr_proc::bsr_t bsr; bool regular_bsr = bsr_procedure->need_to_send_bsr_on_ul_grant(pdu_msg.rem_size(), &bsr); - bool bsr_is_inserted = false; + bool bsr_is_inserted = false; // MAC control element for BSR, with exception of BSR included for padding; if (regular_bsr) { diff --git a/srsue/src/mac/proc_ra.cc b/srsue/src/mac/proc_ra.cc index d5efbea4f..5b5b56ee7 100644 --- a/srsue/src/mac/proc_ra.cc +++ b/srsue/src/mac/proc_ra.cc @@ -271,8 +271,9 @@ void ra_proc::step_preamble_transmission() { } void ra_proc::step_pdcch_setup() { + int ra_tti = phy_h->prach_tx_tti(); - if (ra_tti > 0) { + if (ra_tti > 0) { ra_rnti = 1+ra_tti%10; rInfo("seq=%d, ra-rnti=0x%x, ra-tti=%d\n", sel_preamble, ra_rnti, ra_tti); log_h->console("Random Access Transmission: seq=%d, ra-rnti=0x%x\n", sel_preamble, ra_rnti); @@ -387,7 +388,7 @@ void ra_proc::step_response_reception() { if (ra_tti >= 0 && !rar_received) { uint32_t interval = srslte_tti_interval(phy_h->get_current_tti(), ra_tti+3+responseWindowSize); if (interval > 1 && interval < 100) { - rDebug("RA response not received within the response window\n"); + Error("RA response not received within the response window\n"); state = RESPONSE_ERROR; } } diff --git a/srsue/src/main.cc b/srsue/src/main.cc index 85aacc330..a4651a029 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -139,7 +139,11 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { ("expert.ip_netmask", bpo::value(&args->expert.ip_netmask)->default_value("255.255.255.0"), "Netmask of the tun_srsue device") - + + ("expert.mbms_service", + bpo::value(&args->expert.mbms_service)->default_value(-1), + "automatically starts an mbms service of the number given") + ("expert.phy.worker_cpu_mask", bpo::value(&args->expert.phy.worker_cpu_mask)->default_value(-1), "cpu bit mask (eg 255 = 1111 1111)") @@ -420,6 +424,9 @@ static int sigcnt = 0; static bool running = true; static bool do_metrics = false; metrics_stdout metrics_screen; +static bool show_mbms = false; +static bool mbms_service_start = false; +uint32_t serv, port; void sig_int_handler(int signo) { sigcnt++; @@ -431,14 +438,14 @@ void sig_int_handler(int signo) { } void *input_loop(void *m) { - char key; + string key; while (running) { - cin >> key; + getline(cin, key); if (cin.eof() || cin.bad()) { cout << "Closing stdin thread." << endl; break; } else { - if ('t' == key) { + if (0 == key.compare("t")) { do_metrics = !do_metrics; if (do_metrics) { cout << "Enter t to stop trace." << endl; @@ -447,10 +454,31 @@ void *input_loop(void *m) { } metrics_screen.toggle_print(do_metrics); } else - if ('q' == key) { + if (0 == key.compare("q")) { running = false; + } + else if (0 == key.compare("mbms")) { + show_mbms = true; + } else if (key.find("mbms_service_start") != string::npos) { + + char *dup = strdup(key.c_str()); + strtok(dup, " "); + char *s = strtok(NULL, " "); + if(NULL == s) { + cout << "Usage: mbms_service_start " << endl; + continue; + } + serv = atoi(s); + char* p = strtok(NULL, " "); + if(NULL == p) { + cout << "Usage: mbms_service_start " << endl; + continue; } + port = atoi(p); + mbms_service_start = true; + free(dup); } + } } return NULL; } @@ -502,17 +530,36 @@ int main(int argc, char *argv[]) if (args.gui.enable) { ue->start_plot(); } + if(args.expert.mbms_service > -1){ + //ue->mbms_service_start(args.expert.mbms_service, 4321); + serv = args.expert.mbms_service; + port = 4321; + mbms_service_start = true; + } } int cnt=0; while (running) { + if(mbms_service_start) { + if(ue->mbms_service_start(serv, port)){ + mbms_service_start = false; + } + } + if(show_mbms) { + show_mbms = false; + ue->print_mbms(); + } + sleep(1); if (args.expert.print_buffer_state) { cnt++; if (cnt==10) { cnt=0; ue->print_pool(); } + } else { + while (!ue->attach() && running) { + sleep(1); + } } - sleep(1); } pthread_cancel(input); metricshub.stop(); diff --git a/srsue/src/phy/phch_common.cc b/srsue/src/phy/phch_common.cc index 647d0e0c7..d09338c4c 100644 --- a/srsue/src/phy/phch_common.cc +++ b/srsue/src/phy/phch_common.cc @@ -26,6 +26,7 @@ #include #include +#include #include "srslte/srslte.h" #include "srsue/hdr/phy/phch_common.h" @@ -50,7 +51,8 @@ phch_common::phch_common(uint32_t max_mutex_) : tx_mutex(max_mutex_) rx_gain_offset = 0; last_ri = 0; last_pmi = 0; - + //have_mtch_stop = false; + bzero(&dl_metrics, sizeof(dl_metrics_t)); dl_metrics_read = true; dl_metrics_count = 0; @@ -71,6 +73,8 @@ phch_common::phch_common(uint32_t max_mutex_) : tx_mutex(max_mutex_) reset(); + sib13_configured = false; + mcch_configured = false; } void phch_common::init(phy_interface_rrc::phy_cfg_t *_config, phy_args_t *_args, srslte::log *_log, srslte::radio *_radio, rrc_interface_phy *_rrc, mac_interface_phy *_mac) @@ -367,4 +371,172 @@ void phch_common::reset_ul() */ } +/* Convert 6-bit maps to 10-element subframe tables + bitmap = |0|0|0|0|0|0| + subframe index = |1|2|3|6|7|8| +*/ +void phch_common::build_mch_table() +{ + // First reset tables + bzero(&mch_table[0], sizeof(uint8_t)*40); + + // 40 element table represents 4 frames (40 subframes) + generate_mch_table(&mch_table[0], config->mbsfn.mbsfn_subfr_cnfg.subfr_alloc,(LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE == config->mbsfn.mbsfn_subfr_cnfg.subfr_alloc_num_frames)?1:4); + // Debug + + + std::stringstream ss; + ss << "|"; + for(uint32_t j=0; j<40; j++) { + ss << (int) mch_table[j] << "|"; + } + Info("MCH table: %s\n", ss.str().c_str()); +} + +void phch_common::build_mcch_table() +{ + // First reset tables + bzero(&mcch_table[0], sizeof(uint8_t)*10); + generate_mcch_table(&mcch_table[0], config->mbsfn.mbsfn_area_info.sf_alloc_info_r9); + // Debug + std::stringstream ss; + ss << "|"; + for(uint32_t j=0; j<10; j++) { + ss << (int) mcch_table[j] << "|"; + } + Info("MCCH table: %s\n", ss.str().c_str()); + sib13_configured = true; +} + +void phch_common::set_mcch() +{ + mcch_configured = true; +} + +void phch_common::set_mch_period_stop(uint32_t stop) +{ + pthread_mutex_lock(&mtch_mutex); + have_mtch_stop = true; + mch_period_stop = stop; + pthread_cond_signal(&mtch_cvar); + pthread_mutex_unlock(&mtch_mutex); + +} + +bool phch_common::is_mch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti) +{ + uint32_t sfn; // System Frame Number + uint8_t sf; // Subframe + uint8_t offset; + uint8_t period; + + sfn = phy_tti/10; + sf = phy_tti%10; + + // Set some defaults + cfg->mbsfn_area_id = 0; + cfg->non_mbsfn_region_length = 1; + cfg->mbsfn_mcs = 2; + cfg->mbsfn_decode = false; + cfg->is_mcch = false; + // Check for MCCH + if(is_mcch_subframe(cfg, phy_tti)) { + cfg->is_mcch = true; + return true; + } + + // Not MCCH, check for MCH + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT *subfr_cnfg = &config->mbsfn.mbsfn_subfr_cnfg; + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT *area_info = &config->mbsfn.mbsfn_area_info; + offset = subfr_cnfg->radio_fr_alloc_offset; + period = liblte_rrc_radio_frame_allocation_period_num[subfr_cnfg->radio_fr_alloc_period]; + + if(LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE == subfr_cnfg->subfr_alloc_num_frames) { + if((sfn%period == offset) && (mch_table[sf] > 0)) { + if(sib13_configured) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + if(mcch_configured) { + // Iterate through PMCH configs to see which one applies in the current frame + LIBLTE_RRC_MCCH_MSG_STRUCT *mcch = &config->mbsfn.mcch; + uint32_t mbsfn_per_frame = mcch->pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9/liblte_rrc_mch_scheduling_period_r9_num[mcch->pmch_infolist_r9[0].pmch_config_r9.mch_schedulingperiod_r9]; + uint32_t frame_alloc_idx = sfn%liblte_rrc_mbsfn_common_sf_alloc_period_r9_num[mcch->commonsf_allocperiod_r9]; + uint32_t sf_alloc_idx = frame_alloc_idx*mbsfn_per_frame + ((sf<4)?sf-1:sf-3); + pthread_mutex_lock(&mtch_mutex); + while(!have_mtch_stop) { + pthread_cond_wait(&mtch_cvar, &mtch_mutex); + } + pthread_mutex_unlock(&mtch_mutex); + + for(uint32_t i=0; ipmch_infolist_r9_size; i++) { + if(sf_alloc_idx <= mch_period_stop) { + //trigger conditional variable, has ot be untriggered by mtch stop location + cfg->mbsfn_mcs = mcch->pmch_infolist_r9[i].pmch_config_r9.datamcs_r9; + cfg->mbsfn_decode = true; + } else { + //have_mtch_stop = false; + } + } + Debug("MCH subframe TTI:%d\n", phy_tti); + } + } + return true; + } + }else if(LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_FOUR == subfr_cnfg->subfr_alloc_num_frames) { + uint8_t idx = sfn%period; + if((idx >= offset) && (idx < offset+4)) { + if(mch_table[(idx*10)+sf] > 0){ + if(sib13_configured) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + // TODO: check for MCCH configuration, set MCS and decode + + } + return true; + } + } + } + + return false; +} + +bool phch_common::is_mcch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti) +{ + uint32_t sfn; // System Frame Number + uint8_t sf; // Subframe + uint8_t offset; + uint8_t period; + + sfn = phy_tti/10; + sf = phy_tti%10; + + if(sib13_configured) { + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT *subfr_cnfg = &config->mbsfn.mbsfn_subfr_cnfg; + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT *area_info = &config->mbsfn.mbsfn_area_info; + + offset = area_info->mcch_offset_r9; + period = liblte_rrc_mcch_repetition_period_r9_num[area_info->mcch_repetition_period_r9]; + + if((sfn%period == offset) && mcch_table[sf] > 0) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + cfg->mbsfn_mcs = liblte_rrc_mcch_signalling_mcs_r9_num[area_info->signalling_mcs_r9]; + cfg->mbsfn_decode = true; + have_mtch_stop = false; + Debug("MCCH subframe TTI:%d\n", phy_tti); + return true; + } + } + return false; +} + +void phch_common::get_sf_config(subframe_cfg_t *cfg, uint32_t phy_tti) +{ + if(is_mch_subframe(cfg, phy_tti)) { + cfg->sf_type = SUBFRAME_TYPE_MBSFN; + }else{ + cfg->sf_type = SUBFRAME_TYPE_REGULAR; + } +} + } diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 499309d1e..ddeccd183 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -392,6 +392,7 @@ void phch_recv::run_thread() phy_state.state_exit(); break; case sync_state::SFN_SYNC: + /* SFN synchronization using MIB. run_subframe() receives and processes 1 subframe * and returns */ @@ -473,6 +474,7 @@ void phch_recv::run_thread() is_end_of_burst = true; + // Start worker workers_pool->start_worker(worker); @@ -500,6 +502,7 @@ void phch_recv::run_thread() } break; case sync_state::IDLE: + if (radio_h->is_init()) { uint32_t nsamples = 1920; if (current_srate > 0) { diff --git a/srsue/src/phy/phch_worker.cc b/srsue/src/phy/phch_worker.cc index a18fbb598..edc956bf5 100644 --- a/srsue/src/phy/phch_worker.cc +++ b/srsue/src/phy/phch_worker.cc @@ -133,6 +133,7 @@ bool phch_worker::init(uint32_t max_prb, srslte::log *log_h, srslte::log *log_ph return false; } + srslte_chest_dl_set_rsrp_neighbour(&ue_dl.chest, true); srslte_chest_dl_average_subframe(&ue_dl.chest, phy->args->average_subframe_enabled); srslte_chest_dl_cfo_estimate_enable(&ue_dl.chest, phy->args->cfo_ref_mask!=0, phy->args->cfo_ref_mask); @@ -157,7 +158,11 @@ bool phch_worker::set_cell(srslte_cell_t cell_) Error("Initiating UE DL\n"); goto unlock; } - + + if(srslte_ue_dl_set_mbsfn_area_id(&ue_dl, 1)){ + Error("Setting mbsfn id\n"); + } + if (srslte_ue_ul_set_cell(&ue_ul, cell)) { Error("Initiating UE UL\n"); goto unlock; @@ -247,6 +252,10 @@ void phch_worker::work_imp() reset_uci(); + subframe_cfg_t sf_cfg; + phy->get_sf_config(&sf_cfg, tti); + Debug("TTI: %d, Subframe type: %s\n", tti, subframe_type_text[sf_cfg.sf_type]); + bool dl_grant_available = false; bool ul_grant_available = false; bool dl_ack[SRSLTE_MAX_CODEWORDS] = {false}; @@ -270,47 +279,100 @@ void phch_worker::work_imp() phy->avg_rssi_dbm = SRSLTE_VEC_EMA(rssi_dbm, phy->avg_rssi_dbm, phy->args->snr_ema_coeff); } - /* Do FFT and extract PDCCH LLR, or quit if no actions are required in this subframe */ - bool chest_ok = extract_fft_and_pdcch_llr(); + bool mch_decoded = false; + srslte_ra_dl_grant_t mch_grant; + // Call feedback loop for chest if (chest_loop && ((1<<(tti%10)) & phy->args->cfo_ref_mask)) { chest_loop->set_cfo(srslte_chest_dl_get_cfo(&ue_dl.chest)); } + bool chest_ok = false; + bool snr_th_ok = false; - if (chest_ok) { + /***** Downlink Processing *******/ + + + if(SUBFRAME_TYPE_REGULAR == sf_cfg.sf_type) { + /* Do FFT and extract PDCCH LLR, or quit if no actions are required in this subframe */ + chest_ok = extract_fft_and_pdcch_llr(sf_cfg); - /***** Downlink Processing *******/ + snr_th_ok = 10*log10(srslte_chest_dl_get_snr(&ue_dl.chest))>1.0; - /* PDCCH DL + PDSCH */ - dl_grant_available = decode_pdcch_dl(&dl_mac_grant); - if(dl_grant_available) { - /* Send grant to MAC and get action for this TB */ + if (chest_ok && snr_th_ok) { + + /***** Downlink Processing *******/ + + /* PDCCH DL + PDSCH */ + dl_grant_available = decode_pdcch_dl(&dl_mac_grant); + if(dl_grant_available) { + /* Send grant to MAC and get action for this TB */ + phy->mac->new_grant_dl(dl_mac_grant, &dl_action); + + /* Set DL ACKs to default */ + for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { + dl_ack[tb] = dl_action.default_ack[tb]; + } + + /* Decode PDSCH if instructed to do so */ + if (dl_action.decode_enabled[0] || dl_action.decode_enabled[1]) { + decode_pdsch(&dl_action.phy_grant.dl, dl_action.payload_ptr, + dl_action.softbuffers, dl_action.rv, dl_action.rnti, + dl_mac_grant.pid, dl_ack); + } + if (dl_action.generate_ack_callback) { + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (dl_action.decode_enabled[tb]) { + phy->mac->tb_decoded(dl_ack[tb], tb, dl_mac_grant.rnti_type, dl_mac_grant.pid); + dl_ack[tb] = dl_action.generate_ack_callback(dl_action.generate_ack_callback_arg); + Debug("Calling generate ACK callback for TB %d returned=%d\n", tb, dl_ack[tb]); + } + } + } + Debug("dl_ack={%d, %d}, generate_ack=%d\n", dl_ack[0], dl_ack[1], dl_action.generate_ack); + if (dl_action.generate_ack) { + set_uci_ack(dl_ack, dl_mac_grant.tb_en); + } + } + } + + } else if(SUBFRAME_TYPE_MBSFN == sf_cfg.sf_type) { + srslte_ue_dl_set_non_mbsfn_region(&ue_dl, sf_cfg.non_mbsfn_region_length); + + /* Do FFT and extract PDCCH LLR, or quit if no actions are required in this subframe */ + if (extract_fft_and_pdcch_llr(sf_cfg)) { + + dl_grant_available = decode_pdcch_dl(&dl_mac_grant); phy->mac->new_grant_dl(dl_mac_grant, &dl_action); /* Set DL ACKs to default */ for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { dl_ack[tb] = dl_action.default_ack[tb]; - } - - /* Decode PDSCH if instructed to do so */ - if (dl_action.decode_enabled[0] || dl_action.decode_enabled[1]) { - decode_pdsch(&dl_action.phy_grant.dl, dl_action.payload_ptr, - dl_action.softbuffers, dl_action.rv, dl_action.rnti, - dl_mac_grant.pid, dl_ack); - } - if (dl_action.generate_ack_callback) { - for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { - if (dl_action.decode_enabled[tb]) { - phy->mac->tb_decoded(dl_ack[tb], tb, dl_mac_grant.rnti_type, dl_mac_grant.pid); - dl_ack[tb] = dl_action.generate_ack_callback(dl_action.generate_ack_callback_arg); - Debug("Calling generate ACK callback for TB %d returned=%d\n", tb, dl_ack[tb]); + } + if(sf_cfg.mbsfn_decode) { + + mch_grant.sf_type = SRSLTE_SF_MBSFN; + mch_grant.mcs[0].idx = sf_cfg.mbsfn_mcs; + mch_grant.tb_en[0] = true; + for(uint32_t i=1;imac->new_mch_dl(mch_grant, &dl_action); + srslte_softbuffer_rx_reset_tbs(dl_action.softbuffers[0], mch_grant.mcs[0].tbs); + Debug("TBS=%d, Softbuffer max_cb=%d\n", mch_grant.mcs[0].tbs, dl_action.softbuffers[0]->max_cb); + if(dl_action.decode_enabled[0]) { + mch_decoded = decode_pmch(&mch_grant, dl_action.payload_ptr[0], dl_action.softbuffers[0], sf_cfg.mbsfn_area_id); + } } } } @@ -394,21 +456,31 @@ void phch_worker::work_imp() phy->worker_end(tx_tti, signal_ready, &signal_ptr[-next_offset], SRSLTE_SF_LEN_PRB(cell.nof_prb)+next_offset, tx_time); } - if (!dl_action.generate_ack_callback) { - if (dl_mac_grant.rnti_type == SRSLTE_RNTI_PCH && dl_action.decode_enabled[0]) { - if (dl_ack[0]) { - phy->mac->pch_decoded_ok(dl_mac_grant.n_bytes[0]); - } - } else if (!rar_delivered) { - for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { - if (dl_action.decode_enabled[tb]) { - phy->mac->tb_decoded(dl_ack[tb], tb, dl_mac_grant.rnti_type, dl_mac_grant.pid); + if(SUBFRAME_TYPE_REGULAR == sf_cfg.sf_type) { + if (!dl_action.generate_ack_callback) { + if (dl_mac_grant.rnti_type == SRSLTE_RNTI_PCH && dl_action.decode_enabled[0]) { + if (dl_ack[0]) { + phy->mac->pch_decoded_ok(dl_mac_grant.n_bytes[0]); + } + } else if (!rar_delivered) { + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (dl_action.decode_enabled[tb]) { + phy->mac->tb_decoded(dl_ack[tb], tb, dl_mac_grant.rnti_type, dl_mac_grant.pid); + } } } } + } else if (SUBFRAME_TYPE_MBSFN == sf_cfg.sf_type && sf_cfg.mbsfn_decode) { + if(mch_decoded) { + phy->mac->mch_decoded_ok(mch_grant.mcs[0].tbs/8); + } else if(sf_cfg.is_mcch) { + //release lock in phch_common + phy->set_mch_period_stop(0); + } + } + if(SUBFRAME_TYPE_REGULAR == sf_cfg.sf_type){ + update_measurements(); } - - update_measurements(); if (chest_ok) { if (phy->avg_rsrp_dbm > -130.0 && phy->avg_snr_db_cqi > -6.0) { @@ -456,7 +528,9 @@ void phch_worker::compute_ri(uint8_t *ri, uint8_t *pmi, float *sinr) { } } -bool phch_worker::extract_fft_and_pdcch_llr() { + + +bool phch_worker::extract_fft_and_pdcch_llr(subframe_cfg_t sf_cfg) { bool decode_pdcch = true; // Do always channel estimation to keep track of out-of-sync and send measurements to RRC @@ -475,18 +549,21 @@ bool phch_worker::extract_fft_and_pdcch_llr() { srslte_chest_dl_set_noise_alg(&ue_dl.chest, SRSLTE_NOISE_ALG_PSS); } - if (srslte_ue_dl_decode_fft_estimate(&ue_dl, tti%10, &cfi) < 0) { - Error("Getting PDCCH FFT estimate\n"); - return false; - } - chest_done = true; - if (srslte_ue_dl_decode_fft_estimate(&ue_dl, tti%10, &cfi) < 0) { - Error("Getting PDCCH FFT estimate\n"); - return false; - } + int decode_fft = 0; + if(SUBFRAME_TYPE_MBSFN == sf_cfg.sf_type) { + srslte_ue_dl_set_non_mbsfn_region(&ue_dl, sf_cfg.non_mbsfn_region_length); + decode_fft = srslte_ue_dl_decode_fft_estimate_mbsfn(&ue_dl, tti%10, &cfi, SRSLTE_SF_MBSFN); + }else{ + decode_fft = srslte_ue_dl_decode_fft_estimate(&ue_dl, tti%10, &cfi); + } + if (decode_fft < 0) { + Error("Getting PDCCH FFT estimate\n"); + return false; + } + + chest_done = true; - chest_done = true; if (chest_done && decode_pdcch) { /* and not in DRX mode */ @@ -752,6 +829,73 @@ int phch_worker::decode_pdsch(srslte_ra_dl_grant_t *grant, uint8_t *payload[SRSL return ret; } +bool phch_worker::decode_pmch(srslte_ra_dl_grant_t *grant, uint8_t *payload, + srslte_softbuffer_rx_t* softbuffer, uint16_t mbsfn_area_id) +{ + char timestr[64]; + timestr[0]='\0'; + + Debug("DL Buffer TTI %d: Decoding PMCH\n", tti); + /* Setup PMCH configuration */ + srslte_ue_dl_set_mbsfn_area_id(&ue_dl, mbsfn_area_id); + + if (!srslte_ue_dl_cfg_grant(&ue_dl, grant, cfi, tti%10, SRSLTE_PMCH_RV, SRSLTE_MIMO_TYPE_SINGLE_ANTENNA)) { + if (ue_dl.pmch_cfg.grant.mcs[0].mod > 0 && ue_dl.pmch_cfg.grant.mcs[0].tbs >= 0) { + + Debug("Decoding PMCH SF: %d, MBSFN area ID: 0x%x, Mod %s, TBS: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d, C_prb=%d, cfi=%d\n", + ue_dl.pmch_cfg.sf_idx, mbsfn_area_id, srslte_mod_string(ue_dl.pmch_cfg.grant.mcs[0].mod), ue_dl.pmch_cfg.grant.mcs[0].tbs, ue_dl.pmch_cfg.nbits[0].nof_re, + ue_dl.pmch_cfg.nbits[0].nof_bits, 0, ue_dl.pmch_cfg.grant.nof_prb, ue_dl.pmch_cfg.nbits[0].lstart-1); + + float noise_estimate = srslte_chest_dl_get_noise_estimate(&ue_dl.chest); + + if (!phy->args->equalizer_mode.compare("zf")) { + noise_estimate = 0; + } + + /* Set decoder iterations */ + // TODO: Add separate arg for pmch_max_its + if (phy->args->pdsch_max_its > 0) { + srslte_sch_set_max_noi(&ue_dl.pmch.dl_sch, phy->args->pdsch_max_its); + } + +#ifdef LOG_EXECTIME + struct timeval t[3]; + gettimeofday(&t[1], NULL); +#endif + + bool ack = srslte_pmch_decode_multi(&ue_dl.pmch, &ue_dl.pmch_cfg, softbuffer, ue_dl.sf_symbols_m, + ue_dl.ce_m, noise_estimate, mbsfn_area_id, payload) == 0; + +#ifdef LOG_EXECTIME + gettimeofday(&t[2], NULL); + get_time_interval(t); + snprintf(timestr, 64, ", dec_time=%4d us", (int) t[0].tv_usec); +#endif + + Info("PMCH: l_crb=%2d, tbs=%d, mcs=%d, crc=%s, snr=%.1f dB, n_iter=%d%s\n", + grant->nof_prb, + grant->mcs[0].tbs/8, grant->mcs[0].idx, + ack?"OK":"KO", + 10*log10(srslte_chest_dl_get_snr(&ue_dl.chest)), + srslte_pmch_last_noi(&ue_dl.pmch), + timestr); + + //printf("tti=%d, cfo=%f\n", tti, cfo*15000); + //srslte_vec_save_file("pdsch", signal_buffer, sizeof(cf_t)*SRSLTE_SF_LEN_PRB(cell.nof_prb)); + + // Store metrics + dl_metrics.mcs = grant->mcs[0].idx; + + return ack; + } else { + Warning("Received grant for TBS=0\n"); + } + } else { + Error("Error configuring DL grant\n"); + } + return true; +} + bool phch_worker::decode_phich(bool *ack) { uint32_t I_lowest, n_dmrs; diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index bf8ccffaa..20f40f4b6 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -354,6 +354,7 @@ uint32_t phy::get_current_tti() return sf_recv.get_current_tti(); } + void phy::sr_send() { workers_common.sr_enabled = true; @@ -437,4 +438,40 @@ void phy::set_config_tdd(LIBLTE_RRC_TDD_CONFIG_STRUCT* tdd) memcpy(&config.common.tdd_cnfg, tdd, sizeof(LIBLTE_RRC_TDD_CONFIG_STRUCT)); } +void phy::set_config_mbsfn_sib2(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2) +{ + if(sib2->mbsfn_subfr_cnfg_list_size > 1) { + Warning("SIB2 has %d MBSFN subframe configs - only 1 supported\n", sib2->mbsfn_subfr_cnfg_list_size); + } + if(sib2->mbsfn_subfr_cnfg_list_size > 0) { + memcpy(&config.mbsfn.mbsfn_subfr_cnfg, &sib2->mbsfn_subfr_cnfg_list[0], sizeof(LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT)); + workers_common.build_mch_table(); + } +} + +void phy::set_config_mbsfn_sib13(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13) +{ + memcpy(&config.mbsfn.mbsfn_notification_cnfg, &sib13->mbsfn_notification_config, sizeof(LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT)); + if(sib13->mbsfn_area_info_list_r9_size > 1) { + Warning("SIB13 has %d MBSFN area info elements - only 1 supported\n", sib13->mbsfn_area_info_list_r9_size); + } + if(sib13->mbsfn_area_info_list_r9_size > 0) { + memcpy(&config.mbsfn.mbsfn_area_info, &sib13->mbsfn_area_info_list_r9[0], sizeof(LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT)); + workers_common.build_mcch_table(); + } +} + +void phy::set_config_mbsfn_mcch(LIBLTE_RRC_MCCH_MSG_STRUCT *mcch) +{ + memcpy(&config.mbsfn.mcch, mcch, sizeof(LIBLTE_RRC_MCCH_MSG_STRUCT)); + mac->set_mbsfn_config(config.mbsfn.mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size); + workers_common.set_mch_period_stop(config.mbsfn.mcch.pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9); + workers_common.set_mcch(); +} + +void phy::set_mch_period_stop(uint32_t stop) +{ + workers_common.set_mch_period_stop(stop); +} + } diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index 53dcf0528..68236722d 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -208,11 +208,9 @@ bool ue::init(all_args_t *args_) { srslte_nas_config_t nas_cfg(1, args->apn_name, args->apn_user, args->apn_pass); /* RB_ID_SRB1 */ nas.init(usim, &rrc, &gw, &nas_log, nas_cfg); gw.init(&pdcp, &nas, &gw_log, 3 /* RB_ID_DRB1 */); - gw.set_netmask(args->expert.ip_netmask); - - rrc.init(&phy, &mac, &rlc, &pdcp, &nas, usim, &mac, &rrc_log); - + rrc.init(&phy, &mac, &rlc, &pdcp, &nas, usim, &gw, &mac, &rrc_log); + // Get current band from provided EARFCN args->rrc.supported_bands[0] = srslte_band_get_band(args->rf.dl_earfcn); args->rrc.nof_supported_bands = 1; @@ -323,9 +321,19 @@ bool ue::get_metrics(ue_metrics_t &m) return false; } + void ue::radio_overflow() { phy.radio_overflow(); } +void ue::print_mbms() +{ + rrc.print_mbms(); +} + +bool ue::mbms_service_start(uint32_t serv, uint32_t port) +{ + return rrc.mbms_service_start(serv, port); +} void ue::rf_msg(srslte_rf_error_t error) { diff --git a/srsue/src/upper/gw.cc b/srsue/src/upper/gw.cc index 4fd439f77..3d91791b2 100644 --- a/srsue/src/upper/gw.cc +++ b/srsue/src/upper/gw.cc @@ -59,6 +59,19 @@ void gw::init(pdcp_interface_gw *pdcp_, nas_interface_gw *nas_, srslte::log *gw_ gettimeofday(&metrics_time[1], NULL); dl_tput_bytes = 0; ul_tput_bytes = 0; + // MBSFN + mbsfn_sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (mbsfn_sock_fd < 0) { + gw_log->error("Failed to create MBSFN sink socket\n"); + } + if (fcntl(mbsfn_sock_fd, F_SETFL, O_NONBLOCK)) { + gw_log->error("Failed to set non-blocking MBSFN sink socket\n"); + } + + mbsfn_sock_addr.sin_family = AF_INET; + mbsfn_sock_addr.sin_addr.s_addr =inet_addr("127.0.0.1"); + + bzero(mbsfn_ports, SRSLTE_N_MCH_LCIDS*sizeof(uint32_t)); } void gw::stop() @@ -83,14 +96,15 @@ void gw::stop() current_ip_addr = 0; } - // TODO: tear down TUN device? } + if (mbsfn_sock_fd) { + close(mbsfn_sock_fd); + } } void gw::get_metrics(gw_metrics_t &m) { - gettimeofday(&metrics_time[2], NULL); get_time_interval(metrics_time); double secs = (double) metrics_time[0].tv_sec+metrics_time[0].tv_usec*1e-6; @@ -105,7 +119,8 @@ void gw::get_metrics(gw_metrics_t &m) ul_tput_bytes = 0; } -void gw::set_netmask(std::string netmask) { +void gw::set_netmask(std::string netmask) +{ default_netmask = false; this->netmask = netmask; } @@ -116,8 +131,7 @@ void gw::set_netmask(std::string netmask) { *******************************************************************************/ void gw::write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu) { - gw_log->info_hex(pdu->msg, pdu->N_bytes, "RX PDU"); - gw_log->info("RX PDU. Stack latency: %ld us\n", pdu->get_latency_us()); + gw_log->info_hex(pdu->msg, pdu->N_bytes, "RX PDU. Stack latency: %ld us\n", pdu->get_latency_us()); dl_tput_bytes += pdu->N_bytes; if(!if_up) { @@ -132,6 +146,46 @@ void gw::write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu) pool->deallocate(pdu); } +void gw::write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu) +{ + if(pdu->N_bytes>2) + { + gw_log->info_hex(pdu->msg, pdu->N_bytes, "RX MCH PDU (%d B). Stack latency: %ld us\n", pdu->N_bytes, pdu->get_latency_us()); + dl_tput_bytes += pdu->N_bytes; + + //Hack to drop initial 2 bytes + pdu->msg +=2; + pdu->N_bytes-=2; + struct in_addr dst_addr; + memcpy(&dst_addr.s_addr, &pdu->msg[16],4); + + if(!if_up) + { + gw_log->warning("TUN/TAP not up - dropping gw RX message\n"); + }else{ + int n = write(tun_fd, pdu->msg, pdu->N_bytes); + if(n > 0 && (pdu->N_bytes != (uint32_t)n)) + { + gw_log->warning("DL TUN/TAP write failure\n"); + } + } + /* + // Strip IP/UDP header + pdu->msg += 28; + pdu->N_bytes -= 28; + + if(mbsfn_sock_fd) { + if(lcid > 0 && lcid < SRSLTE_N_MCH_LCIDS) { + mbsfn_sock_addr.sin_port = htons(mbsfn_ports[lcid]); + if(sendto(mbsfn_sock_fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&mbsfn_sock_addr, sizeof(struct sockaddr_in))<0) { + gw_log->error("Failed to send MCH PDU to port %d\n", mbsfn_ports[lcid]); + } + } + }*/ + } + pool->deallocate(pdu); +} + /******************************************************************************* NAS interface *******************************************************************************/ @@ -234,6 +288,19 @@ srslte::error_t gw::init_if(char *err_str) return(srslte::ERROR_NONE); } + +/******************************************************************************* + RRC interface +*******************************************************************************/ +void gw::add_mch_port(uint32_t lcid, uint32_t port) +{ + if(lcid > 0 && lcid < SRSLTE_N_MCH_LCIDS) { + mbsfn_ports[lcid] = port; + } +} + + + /********************/ /* GW Receive */ /********************/ diff --git a/srsue/src/upper/rrc.cc b/srsue/src/upper/rrc.cc index 65b3a3372..f94e0d59d 100644 --- a/srsue/src/upper/rrc.cc +++ b/srsue/src/upper/rrc.cc @@ -80,6 +80,67 @@ void rrc::liblte_rrc_log(char *str) { printf("[ASN]: %s\n", str); } } +void rrc::print_mbms() +{ + if(rrc_log) { + if(serving_cell->has_mcch) { + LIBLTE_RRC_MCCH_MSG_STRUCT msg; + memcpy(&msg, &serving_cell->mcch, sizeof(LIBLTE_RRC_MCCH_MSG_STRUCT)); + std::stringstream ss; + for(uint32_t i=0;imbms_sessioninfolist_r9_size; j++) { + LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *sess = &pmch->mbms_sessioninfolist_r9[j]; + ss << " Service ID: " << sess->tmgi_r9.serviceid_r9; + if(sess->sessionid_r9_present) { + ss << ", Session ID: " << (uint32_t)sess->sessionid_r9; + } + if(sess->tmgi_r9.plmn_id_explicit) { + std::string tmp; + if(mcc_to_string(sess->tmgi_r9.plmn_id_r9.mcc, &tmp)) { + ss << ", MCC: " << tmp; + } + if(mnc_to_string(sess->tmgi_r9.plmn_id_r9.mnc, &tmp)) { + ss << ", MNC: " << tmp; + } + } else { + ss << ", PLMN index: " << (uint32_t)sess->tmgi_r9.plmn_index_r9; + } + ss << ", LCID: " << (uint32_t)sess->logicalchannelid_r9; + ss << std::endl; + } + } + //rrc_log->console(ss.str()); + std::cout << ss.str(); + } else { + rrc_log->console("MCCH not available for current cell\n"); + } + } +} + +bool rrc::mbms_service_start(uint32_t serv, uint32_t port) +{ + bool ret = false; + + if(serving_cell->has_mcch) { + LIBLTE_RRC_MCCH_MSG_STRUCT msg; + memcpy(&msg, &serving_cell->mcch, sizeof(LIBLTE_RRC_MCCH_MSG_STRUCT)); + for(uint32_t i=0;imbms_sessioninfolist_r9_size; j++) { + LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *sess = &pmch->mbms_sessioninfolist_r9[j]; + if(serv == sess->tmgi_r9.serviceid_r9) { + rrc_log->console("MBMS service started. Service id:%d, port: %d\n", serv, port); + ret = true; + add_mrb(sess->logicalchannelid_r9, port); + } + } + } + } + return ret; +} + void rrc::init(phy_interface_rrc *phy_, mac_interface_rrc *mac_, @@ -87,6 +148,7 @@ void rrc::init(phy_interface_rrc *phy_, pdcp_interface_rrc *pdcp_, nas_interface_rrc *nas_, usim_interface_rrc *usim_, + gw_interface_rrc *gw_, mac_interface_timers *mac_timers_, srslte::log *rrc_log_) { pool = byte_buffer_pool::get_instance(); @@ -96,6 +158,7 @@ void rrc::init(phy_interface_rrc *phy_, pdcp = pdcp_; nas = nas_; usim = usim_; + gw = gw_; rrc_log = rrc_log_; // Use MAC timers @@ -474,6 +537,7 @@ bool rrc::configure_serving_cell() { rrc_log->error("Trying to configure Cell while not camping on it\n"); return false; } + serving_cell->has_mcch = false; // Apply configurations if already retrieved SIB2 if (serving_cell->has_sib2()) { apply_sib2_configs(serving_cell->sib2ptr()); @@ -490,6 +554,9 @@ bool rrc::configure_serving_cell() { } } else { rrc_log->info("Cell has SIB%d\n", i+1); + if(i+1 == 13){ + apply_sib13_configs(serving_cell->sib13ptr()); + } } } return true; @@ -1759,7 +1826,23 @@ void rrc::process_pcch(byte_buffer_t *pdu) { } +void rrc::write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu) +{ + if (pdu->N_bytes > 0 && pdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BITS) { + rrc_log->info_hex(pdu->msg, pdu->N_bytes, "MCH message received %d bytes on lcid:%d\n", pdu->N_bytes, lcid); + rrc_log->info("MCH message Stack latency: %ld us\n", pdu->get_latency_us()); + //TODO: handle MCCH notifications and update MCCH + if(0 == lcid && !serving_cell->has_mcch) { + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes * 8); + bit_buf.N_bits = pdu->N_bytes * 8; + liblte_rrc_unpack_mcch_msg((LIBLTE_BIT_MSG_STRUCT *) &bit_buf, &serving_cell->mcch); + serving_cell->has_mcch = true; + phy->set_config_mbsfn_mcch(&serving_cell->mcch); + } + pool->deallocate(pdu); + } +} @@ -2134,6 +2217,9 @@ void rrc::apply_sib2_configs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2) { // for(uint8_t i=0;imbsfn_subfr_cnfg_list_size;i++) { // memcpy(&cfg.mbsfn_subfr_cnfg_list[i], &sib2->mbsfn_subfr_cnfg_list[i], sizeof(LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT)); // } + + // Set MBSFN configs + phy->set_config_mbsfn_sib2(sib2); mac->set_config(&cfg); @@ -2198,6 +2284,12 @@ void rrc::apply_sib2_configs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2) { } +void rrc::apply_sib13_configs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13) +{ + phy->set_config_mbsfn_sib13(&serving_cell->sib13); + add_mrb(0, 0); // Add MRB0 +} + // Go through all information elements and apply defaults (9.2.4) if not defined void rrc::apply_phy_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg, bool apply_defaults) { // Get current configuration @@ -2585,6 +2677,14 @@ void rrc::release_drb(uint8_t lcid) { // TODO } +void rrc::add_mrb(uint32_t lcid, uint32_t port) +{ + gw->add_mch_port(lcid, port); + rlc->add_bearer_mrb(lcid); + mac->mch_start_rx(lcid); + rrc_log->info("Added MRB bearer for lcid:%d\n", lcid); +} + // PHY CONFIG DEDICATED Defaults (3GPP 36.331 v10 9.2.4) void rrc::set_phy_default_pucch_srs() { diff --git a/srsue/test/mac/mac_test.cc b/srsue/test/mac/mac_test.cc index e3d457872..633bb3b6d 100644 --- a/srsue/test/mac/mac_test.cc +++ b/srsue/test/mac/mac_test.cc @@ -418,6 +418,7 @@ public: } void write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes) {} + void write_pdu_mch(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) {} private: LIBLTE_BIT_MSG_STRUCT bit_msg; diff --git a/srsue/test/upper/nas_test.cc b/srsue/test/upper/nas_test.cc index 1034624f2..38a7f6ca3 100644 --- a/srsue/test/upper/nas_test.cc +++ b/srsue/test/upper/nas_test.cc @@ -77,6 +77,7 @@ public: void write_pdu_bcch_bch(byte_buffer_t *pdu) {} void write_pdu_bcch_dlsch(byte_buffer_t *pdu) {} void write_pdu_pcch(byte_buffer_t *pdu) {} + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) {} std::string get_rb_name(uint32_t lcid) { return std::string("lcid"); } }; @@ -126,6 +127,7 @@ class gw_dummy : public gw_interface_nas, public gw_interface_pdcp { error_t setup_if_addr(uint32_t ip_addr, char *err_str) { return ERROR_NONE; } void write_pdu(uint32_t lcid, byte_buffer_t *pdu) {} + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) {} }; } diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index 746df2192..07c78d9ee 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -201,6 +201,7 @@ enable = false ##################################################################### [expert] #ip_netmask = 255.255.255.0 +#mbms_service = -1 #rssi_sensor_enabled = false #rx_gain_offset = 72 #prach_gain = 30