From ba4bc50bac8e94982503af42c853295939bd76b5 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 26 Mar 2013 08:20:43 +0100 Subject: Add full AMR multirate IE support with VTY config for MS and BTS side --- include/osmocom/bsc/bsc_api.h | 2 +- include/osmocom/bsc/gsm_04_08.h | 3 + include/osmocom/bsc/gsm_data_shared.h | 13 +- src/libbsc/abis_rsl.c | 13 +- src/libbsc/bsc_api.c | 28 +- src/libbsc/bsc_vty.c | 605 +++++++++++++++++++++++++++++++++- src/libbsc/chan_alloc.c | 3 +- src/libbsc/gsm_04_08_utils.c | 82 +++-- 8 files changed, 708 insertions(+), 41 deletions(-) diff --git a/include/osmocom/bsc/bsc_api.h b/include/osmocom/bsc/bsc_api.h index f52984f39..b28d1980e 100644 --- a/include/osmocom/bsc/bsc_api.h +++ b/include/osmocom/bsc/bsc_api.h @@ -40,7 +40,7 @@ struct bsc_api { * not implemented AMR5.9 will be used. */ void (*mr_config)(struct gsm_subscriber_connection *conn, - struct gsm48_multi_rate_conf *conf); + uint8_t *mr_ms_lv, uint8_t *mr_bts_lv); }; int bsc_api_init(struct gsm_network *network, struct bsc_api *api); diff --git a/include/osmocom/bsc/gsm_04_08.h b/include/osmocom/bsc/gsm_04_08.h index 068672059..d071ad595 100644 --- a/include/osmocom/bsc/gsm_04_08.h +++ b/include/osmocom/bsc/gsm_04_08.h @@ -14,6 +14,7 @@ struct gsm_subscriber; struct gsm_network; struct gsm_trans; struct gsm_subscriber_connection; +struct amr_multirate_conf; #define GSM48_ALLOC_SIZE 2048 #define GSM48_ALLOC_HEADROOM 256 @@ -75,4 +76,6 @@ void allocate_security_operation(struct gsm_subscriber_connection *conn); int tch_frame_down(struct gsm_network *net, uint32_t callref, struct gsm_data_frame *data); +int gsm48_multirate_config(uint8_t *lv, struct amr_multirate_conf *mr, int ms); + #endif diff --git a/include/osmocom/bsc/gsm_data_shared.h b/include/osmocom/bsc/gsm_data_shared.h index ded00307a..07fa9823d 100644 --- a/include/osmocom/bsc/gsm_data_shared.h +++ b/include/osmocom/bsc/gsm_data_shared.h @@ -151,8 +151,10 @@ struct bts_codec_conf { struct amr_mode { uint8_t mode; - uint8_t threshold; - uint8_t hysteresis; + uint8_t threshold_ms; + uint8_t hysteresis_ms; + uint8_t threshold_bts; + uint8_t hysteresis_bts; }; struct amr_multirate_conf { uint8_t gsm48_ie[2]; @@ -207,7 +209,8 @@ struct gsm_lchan { } encr; /* AMR bits */ - struct gsm48_multi_rate_conf mr_conf; + uint8_t mr_ms_lv[7]; + uint8_t mr_bts_lv[7]; /* Established data link layer services */ uint8_t sapis[8]; @@ -713,6 +716,10 @@ struct gsm_bts { /* supported codecs beside FR */ struct bts_codec_conf codec; + + /* full and half rate multirate config */ + struct amr_multirate_conf mr_full; + struct amr_multirate_conf mr_half; #endif /* ROLE_BSC */ void *role; }; diff --git a/src/libbsc/abis_rsl.c b/src/libbsc/abis_rsl.c index 41bfcdc92..fbea70f80 100644 --- a/src/libbsc/abis_rsl.c +++ b/src/libbsc/abis_rsl.c @@ -538,8 +538,8 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) - msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), - (uint8_t *) &lchan->mr_conf); + msgb_tlv_put(msg, RSL_IE_MR_CONFIG, lchan->mr_bts_lv[0], + lchan->mr_bts_lv + 1); msg->dst = lchan->ts->trx->rsl_link; @@ -575,10 +575,11 @@ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan) msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); } - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { - msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), - (uint8_t *) &lchan->mr_conf); - } + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) +{ + msgb_tlv_put(msg, RSL_IE_MR_CONFIG, lchan->mr_bts_lv[0], + lchan->mr_bts_lv + 1); +} msg->dst = lchan->ts->trx->rsl_link; diff --git a/src/libbsc/bsc_api.c b/src/libbsc/bsc_api.c index bde13b929..045068f24 100644 --- a/src/libbsc/bsc_api.c +++ b/src/libbsc/bsc_api.c @@ -154,17 +154,31 @@ static void assignment_t10_timeout(void *_conn) * Handle the multirate config */ static void handle_mr_config(struct gsm_subscriber_connection *conn, - struct gsm_lchan *lchan) + struct gsm_lchan *lchan, int full_rate) { struct bsc_api *api; api = conn->bts->network->bsc_api; + struct amr_multirate_conf *mr; + struct gsm48_multi_rate_conf *mr_conf; if (api->mr_config) - return api->mr_config(conn, &lchan->mr_conf); + return api->mr_config(conn, lchan->mr_ms_lv, lchan->mr_bts_lv); - lchan->mr_conf.ver = 1; - lchan->mr_conf.icmi = 1; - lchan->mr_conf.m5_90 = 1; + if (full_rate) + mr = &lchan->ts->trx->bts->mr_full; + else + mr = &lchan->ts->trx->bts->mr_half; + + mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + mr_conf->ver = 1; + + /* default, if no AMR codec defined */ + if (!mr->gsm48_ie[1]) { + mr_conf->icmi = 1; + mr_conf->m5_90 = 1; + } + gsm48_multirate_config(lchan->mr_ms_lv, mr, 1); + gsm48_multirate_config(lchan->mr_bts_lv, mr, 0); } /* @@ -207,7 +221,7 @@ static int handle_new_assignment(struct gsm_subscriber_connection *conn, int cha /* handle AMR correctly */ if (chan_mode == GSM48_CMODE_SPEECH_AMR) - handle_mr_config(conn, new_lchan); + handle_mr_config(conn, new_lchan, full_rate); if (rsl_chan_activate_lchan(new_lchan, 0x1, 0, 0) < 0) { LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); @@ -380,7 +394,7 @@ int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, in LOGP(DMSC, LOGL_NOTICE, "Sending ChanModify for speech %d %d\n", chan_mode, full_rate); if (chan_mode == GSM48_CMODE_SPEECH_AMR) - handle_mr_config(conn, conn->lchan); + handle_mr_config(conn, conn->lchan, full_rate); gsm48_lchan_modify(conn->lchan, chan_mode); } diff --git a/src/libbsc/bsc_vty.c b/src/libbsc/bsc_vty.c index ad1911bd6..e41d4de4c 100644 --- a/src/libbsc/bsc_vty.c +++ b/src/libbsc/bsc_vty.c @@ -459,7 +459,9 @@ static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts) static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) { struct gsm_bts_trx *trx; - int i; + struct amr_multirate_conf *mr; + struct gsm48_multi_rate_conf *mr_conf; + int i, num; vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE); vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE); @@ -607,6 +609,108 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) vty_out(vty, " ahs"); vty_out(vty, "%s", VTY_NEWLINE); + mr = &bts->mr_full; + mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + if (mr->gsm48_ie[1]) { + num = 0; + vty_out(vty, " amr tch-f modes"); + for (i = 0; i < 8; i++) { + if ((mr->gsm48_ie[1] & (1 << i))) { + vty_out(vty, " %d", i); + num++; + } + } + vty_out(vty, "%s", VTY_NEWLINE); + if (num > 3) + num = 3; + if (num > 1) { + vty_out(vty, " amr tch-f threshold ms"); + for (i = 0; i < num - 1; i++) { + vty_out(vty, " %d", mr->mode[i].threshold_ms); + } + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, " amr tch-f hysteresis ms"); + for (i = 0; i < num - 1; i++) { + vty_out(vty, " %d", mr->mode[i].hysteresis_ms); + } + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, " amr tch-f threshold bts"); + for (i = 0; i < num - 1; i++) { + vty_out(vty, " %d", mr->mode[i].threshold_bts); + } + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, " amr tch-f hysteresis bts"); + for (i = 0; i < num - 1; i++) { + vty_out(vty, " %d", mr->mode[i].hysteresis_bts); + } + vty_out(vty, "%s", VTY_NEWLINE); + } + vty_out(vty, " amr tch-f start-mode "); + if (mr_conf->icmi) { + num = 0; + for (i = 0; i < 8 && num < 4; i++) { + if ((mr->gsm48_ie[1] & (1 << i))) + num++; + if (mr_conf->smod == num - 1) { + vty_out(vty, "%d%s", num, VTY_NEWLINE); + break; + } + } + } else + vty_out(vty, "auto%s", VTY_NEWLINE); + } + + mr = &bts->mr_half; + mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + if (mr->gsm48_ie[1]) { + num = 0; + vty_out(vty, " amr tch-h modes"); + for (i = 0; i < 6; i++) { + if ((mr->gsm48_ie[1] & (1 << i))) { + vty_out(vty, " %d", i); + num++; + } + } + vty_out(vty, "%s", VTY_NEWLINE); + if (num > 3) + num = 3; + if (num > 1) { + vty_out(vty, " amr tch-h threshold ms"); + for (i = 0; i < num - 1; i++) { + vty_out(vty, " %d", mr->mode[i].threshold_ms); + } + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, " amr tch-h hysteresis ms"); + for (i = 0; i < num - 1; i++) { + vty_out(vty, " %d", mr->mode[i].hysteresis_ms); + } + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, " amr tch-h threshold bts"); + for (i = 0; i < num - 1; i++) { + vty_out(vty, " %d", mr->mode[i].threshold_bts); + } + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, " amr tch-h hysteresis bts"); + for (i = 0; i < num - 1; i++) { + vty_out(vty, " %d", mr->mode[i].hysteresis_bts); + } + vty_out(vty, "%s", VTY_NEWLINE); + } + vty_out(vty, " amr tch-h start-mode "); + if (mr_conf->icmi) { + num = 0; + for (i = 0; i < 6 && num < 4; i++) { + if ((mr->gsm48_ie[1] & (1 << i))) + num++; + if (mr_conf->smod == num - 1) { + vty_out(vty, "%d%s", num, VTY_NEWLINE); + break; + } + } + } else + vty_out(vty, "auto%s", VTY_NEWLINE); + } + config_write_bts_gprs(vty, bts); if (bts->excl_from_rf_lock) @@ -2692,6 +2796,483 @@ DEFUN(cfg_bts_codec5, cfg_bts_codec5_cmd, return CMD_SUCCESS; } +#define AMR_TEXT "Adaptive Multi Rate settings\n" + +DEFUN(cfg_bts_amr_fr_modes1, cfg_bts_amr_fr_modes1_cmd, + "amr tch-f modes (0|1|2|3|4|5|6|7)", + AMR_TEXT "Full Rate\n" + "Codec modes to use with AMR codec\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n10,2k\n12,2k\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_full; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + mr->gsm48_ie[1] = 0; + mr->gsm48_ie[1] |= 1 << atoi(argv[0]); + mr_conf->icmi = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_fr_modes2, cfg_bts_amr_fr_modes2_cmd, + "amr tch-f modes (0|1|2|3|4|5|6|7) (0|1|2|3|4|5|6|7)", + AMR_TEXT "Full Rate\n" + "Codec modes to use with AMR codec\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n10,2k\n12,2k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n10,2k\n12,2k\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_full; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + + mr->gsm48_ie[1] = 0; + mr->gsm48_ie[1] |= 1 << atoi(argv[0]); + mr->gsm48_ie[1] |= 1 << atoi(argv[1]); + mr_conf->icmi = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_fr_modes3, cfg_bts_amr_fr_modes3_cmd, + "amr tch-f modes (0|1|2|3|4|5|6|7) (0|1|2|3|4|5|6|7) (0|1|2|3|4|5|6|7)", + AMR_TEXT "Full Rate\n" + "Codec modes to use with AMR codec\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n10,2k\n12,2k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n10,2k\n12,2k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n10,2k\n12,2k\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_full; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + + mr->gsm48_ie[1] = 0; + mr->gsm48_ie[1] |= 1 << atoi(argv[0]); + mr->gsm48_ie[1] |= 1 << atoi(argv[1]); + mr->gsm48_ie[1] |= 1 << atoi(argv[2]); + mr_conf->icmi = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_fr_modes4, cfg_bts_amr_fr_modes4_cmd, + "amr tch-f modes (0|1|2|3|4|5|6|7) (0|1|2|3|4|5|6|7) (0|1|2|3|4|5|6|7) " + "(0|1|2|3|4|5|6|7)", + AMR_TEXT "Full Rate\n" + "Codec modes to use with AMR codec\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n10,2k\n12,2k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n10,2k\n12,2k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n10,2k\n12,2k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n10,2k\n12,2k\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_full; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + + mr->gsm48_ie[1] = 0; + mr->gsm48_ie[1] |= 1 << atoi(argv[0]); + mr->gsm48_ie[1] |= 1 << atoi(argv[1]); + mr->gsm48_ie[1] |= 1 << atoi(argv[2]); + mr->gsm48_ie[1] |= 1 << atoi(argv[3]); + mr_conf->icmi = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_fr_start_mode, cfg_bts_amr_fr_start_mode_cmd, + "amr tch-f start-mode (auto|1|2|3|4)", + AMR_TEXT "Full Rate\n" + "Initial codec to use with AMR\n" + "Automatically\nFirst codec\nSecond codec\nThird codec\nFourth codec\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_full; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + + if (argv[0][0] == 'a') + mr_conf->icmi = 0; + else { + mr_conf->icmi = 1; + mr_conf->smod = atoi(argv[0]) - 1; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_fr_thres1, cfg_bts_amr_fr_thres1_cmd, + "amr tch-f threshold (ms|bts) <0-63>", + AMR_TEXT "Full Rate\n" + "AMR threshold between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_full; + + if (argv[0][0]=='m') { + mr->mode[0].threshold_ms = atoi(argv[0]); + } else { + mr->mode[0].threshold_bts = atoi(argv[0]); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_fr_thres2, cfg_bts_amr_fr_thres2_cmd, + "amr tch-f threshold (ms|bts) <0-63> <0-63>", + AMR_TEXT "Full Rate\n" + "AMR threshold between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n" + "Hysteresis between codec 2 and 3\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_full; + + if (argv[0][0]=='m') { + mr->mode[0].threshold_ms = atoi(argv[0]); + mr->mode[1].threshold_ms = atoi(argv[1]); + } else { + mr->mode[0].threshold_bts = atoi(argv[0]); + mr->mode[1].threshold_bts = atoi(argv[1]); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_fr_thres3, cfg_bts_amr_fr_thres3_cmd, + "amr tch-f threshold (ms|bts) <0-63> <0-63> <0-63>", + AMR_TEXT "Full Rate\n" + "AMR threshold between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n" + "Hysteresis between codec 2 and 3\n" + "Hysteresis between codec 3 and 4\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_full; + + if (argv[0][0]=='m') { + mr->mode[0].threshold_ms = atoi(argv[0]); + mr->mode[1].threshold_ms = atoi(argv[1]); + mr->mode[2].threshold_ms = atoi(argv[2]); + } else { + mr->mode[0].threshold_bts = atoi(argv[0]); + mr->mode[1].threshold_bts = atoi(argv[1]); + mr->mode[2].threshold_bts = atoi(argv[2]); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_fr_hyst1, cfg_bts_amr_fr_hyst1_cmd, + "amr tch-f hysteresis (ms|bts) <0-15>", + AMR_TEXT "Full Rate\n" + "AMR hysteresis between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_full; + + if (argv[0][0]=='m') { + mr->mode[0].hysteresis_ms = atoi(argv[0]); + } else { + mr->mode[0].hysteresis_bts = atoi(argv[0]); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_fr_hyst2, cfg_bts_amr_fr_hyst2_cmd, + "amr tch-f hysteresis (ms|bts) <0-15> <0-15>", + AMR_TEXT "Full Rate\n" + "AMR hysteresis between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n" + "Hysteresis between codec 2 and 3\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_full; + + if (argv[0][0]=='m') { + mr->mode[0].hysteresis_ms = atoi(argv[0]); + mr->mode[1].hysteresis_ms = atoi(argv[1]); + } else { + mr->mode[0].hysteresis_bts = atoi(argv[0]); + mr->mode[1].hysteresis_bts = atoi(argv[1]); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_fr_hyst3, cfg_bts_amr_fr_hyst3_cmd, + "amr tch-f hysteresis (ms|bts) <0-15> <0-15> <0-15>", + AMR_TEXT "Full Rate\n" + "AMR hysteresis between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n" + "Hysteresis between codec 2 and 3\n" + "Hysteresis between codec 3 and 4\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_full; + + if (argv[0][0]=='m') { + mr->mode[0].hysteresis_ms = atoi(argv[0]); + mr->mode[1].hysteresis_ms = atoi(argv[1]); + mr->mode[2].hysteresis_ms = atoi(argv[2]); + } else { + mr->mode[0].hysteresis_bts = atoi(argv[0]); + mr->mode[1].hysteresis_bts = atoi(argv[1]); + mr->mode[2].hysteresis_bts = atoi(argv[2]); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_hr_modes1, cfg_bts_amr_hr_modes1_cmd, + "amr tch-h modes (0|1|2|3|4|5)", + AMR_TEXT "Half Rate\n" + "Codec modes to use with AMR codec\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_half; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + + mr->gsm48_ie[1] = 0; + mr->gsm48_ie[1] |= 1 << atoi(argv[0]); + mr_conf->icmi = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_hr_modes2, cfg_bts_amr_hr_modes2_cmd, + "amr tch-h modes (0|1|2|3|4|5) (0|1|2|3|4|5)", + AMR_TEXT "Half Rate\n" + "Codec modes to use with AMR codec\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_half; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + + mr->gsm48_ie[1] = 0; + mr->gsm48_ie[1] |= 1 << atoi(argv[0]); + mr->gsm48_ie[1] |= 1 << atoi(argv[1]); + mr_conf->icmi = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_hr_modes3, cfg_bts_amr_hr_modes3_cmd, + "amr tch-h modes (0|1|2|3|4|5) (0|1|2|3|4|5) (0|1|2|3|4|5)", + AMR_TEXT "Half Rate\n" + "Codec modes to use with AMR codec\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_half; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + + mr->gsm48_ie[1] = 0; + mr->gsm48_ie[1] |= 1 << atoi(argv[0]); + mr->gsm48_ie[1] |= 1 << atoi(argv[1]); + mr->gsm48_ie[1] |= 1 << atoi(argv[2]); + mr_conf->icmi = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_hr_modes4, cfg_bts_amr_hr_modes4_cmd, + "amr tch-h modes (0|1|2|3|4|5) (0|1|2|3|4|5) (0|1|2|3|4|5) " + "(0|1|2|3|4|5)", + AMR_TEXT "Half Rate\n" + "Codec modes to use with AMR codec\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" + "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_half; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + + mr->gsm48_ie[1] = 0; + mr->gsm48_ie[1] |= 1 << atoi(argv[0]); + mr->gsm48_ie[1] |= 1 << atoi(argv[1]); + mr->gsm48_ie[1] |= 1 << atoi(argv[2]); + mr->gsm48_ie[1] |= 1 << atoi(argv[3]); + mr_conf->icmi = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_hr_start_mode, cfg_bts_amr_hr_start_mode_cmd, + "amr tch-h start-mode (auto|1|2|3|4)", + AMR_TEXT "Half Rate\n" + "Initial codec to use with AMR\n" + "Automatically\nFirst codec\nSecond codec\nThird codec\nFourth codec\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_half; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) mr->gsm48_ie; + + if (argv[0][0] == 'a') + mr_conf->icmi = 0; + else { + mr_conf->icmi = 1; + mr_conf->smod = atoi(argv[0]) - 1; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_hr_thres1, cfg_bts_amr_hr_thres1_cmd, + "amr tch-h threshold (ms|bts) <0-63>", + AMR_TEXT "Half Rate\n" + "AMR threshold between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_half; + + if (argv[0][0]=='m') { + mr->mode[0].threshold_ms = atoi(argv[0]); + } else { + mr->mode[0].threshold_bts = atoi(argv[0]); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_hr_thres2, cfg_bts_amr_hr_thres2_cmd, + "amr tch-h threshold (ms|bts) <0-63> <0-63>", + AMR_TEXT "Half Rate\n" + "AMR threshold between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n" + "Hysteresis between codec 2 and 3\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_half; + + if (argv[0][0]=='m') { + mr->mode[0].threshold_ms = atoi(argv[0]); + mr->mode[1].threshold_ms = atoi(argv[1]); + } else { + mr->mode[0].threshold_bts = atoi(argv[0]); + mr->mode[1].threshold_bts = atoi(argv[1]); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_hr_thres3, cfg_bts_amr_hr_thres3_cmd, + "amr tch-h threshold (ms|bts) <0-63> <0-63> <0-63>", + AMR_TEXT "Half Rate\n" + "AMR threshold between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n" + "Hysteresis between codec 2 and 3\n" + "Hysteresis between codec 3 and 4\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_half; + + if (argv[0][0]=='m') { + mr->mode[0].threshold_ms = atoi(argv[0]); + mr->mode[1].threshold_ms = atoi(argv[1]); + mr->mode[2].threshold_ms = atoi(argv[2]); + } else { + mr->mode[0].threshold_bts = atoi(argv[0]); + mr->mode[1].threshold_bts = atoi(argv[1]); + mr->mode[2].threshold_bts = atoi(argv[2]); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_hr_hyst1, cfg_bts_amr_hr_hyst1_cmd, + "amr tch-h hysteresis (ms|bts) <0-15>", + AMR_TEXT "Half Rate\n" + "AMR hysteresis between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_half; + + if (argv[0][0]=='m') { + mr->mode[0].hysteresis_ms = atoi(argv[0]); + } else { + mr->mode[0].hysteresis_bts = atoi(argv[0]); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_hr_hyst2, cfg_bts_amr_hr_hyst2_cmd, + "amr tch-h hysteresis (ms|bts) <0-15> <0-15>", + AMR_TEXT "Half Rate\n" + "AMR hysteresis between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n" + "Hysteresis between codec 2 and 3\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_half; + + if (argv[0][0]=='m') { + mr->mode[0].hysteresis_ms = atoi(argv[0]); + mr->mode[1].hysteresis_ms = atoi(argv[1]); + } else { + mr->mode[0].hysteresis_bts = atoi(argv[0]); + mr->mode[1].hysteresis_bts = atoi(argv[1]); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_amr_hr_hyst3, cfg_bts_amr_hr_hyst3_cmd, + "amr tch-h hysteresis (ms|bts) <0-15> <0-15> <0-15>", + AMR_TEXT "Half Rate\n" + "AMR hysteresis between codecs\n" + "MS side\nBTS side\n" + "Hysteresis between codec 1 and 2\n" + "Hysteresis between codec 2 and 3\n" + "Hysteresis between codec 3 and 4\n") +{ + struct gsm_bts *bts = vty->index; + struct amr_multirate_conf *mr = &bts->mr_half; + + if (argv[0][0]=='m') { + mr->mode[0].hysteresis_ms = atoi(argv[0]); + mr->mode[1].hysteresis_ms = atoi(argv[1]); + mr->mode[2].hysteresis_ms = atoi(argv[2]); + } else { + mr->mode[0].hysteresis_bts = atoi(argv[0]); + mr->mode[1].hysteresis_bts = atoi(argv[1]); + mr->mode[2].hysteresis_bts = atoi(argv[2]); + } + + return CMD_SUCCESS; +} + #define TRX_TEXT "Radio Transceiver\n" /* per TRX configuration */ @@ -3295,6 +3876,28 @@ int bsc_vty_init(const struct log_info *cat) install_element(BTS_NODE, &cfg_bts_codec3_cmd); install_element(BTS_NODE, &cfg_bts_codec4_cmd); install_element(BTS_NODE, &cfg_bts_codec5_cmd); + install_element(BTS_NODE, &cfg_bts_amr_fr_modes1_cmd); + install_element(BTS_NODE, &cfg_bts_amr_fr_modes2_cmd); + install_element(BTS_NODE, &cfg_bts_amr_fr_modes3_cmd); + install_element(BTS_NODE, &cfg_bts_amr_fr_modes4_cmd); + install_element(BTS_NODE, &cfg_bts_amr_fr_thres1_cmd); + install_element(BTS_NODE, &cfg_bts_amr_fr_thres2_cmd); + install_element(BTS_NODE, &cfg_bts_amr_fr_thres3_cmd); + install_element(BTS_NODE, &cfg_bts_amr_fr_hyst1_cmd); + install_element(BTS_NODE, &cfg_bts_amr_fr_hyst2_cmd); + install_element(BTS_NODE, &cfg_bts_amr_fr_hyst3_cmd); + install_element(BTS_NODE, &cfg_bts_amr_fr_start_mode_cmd); + install_element(BTS_NODE, &cfg_bts_amr_hr_modes1_cmd); + install_element(BTS_NODE, &cfg_bts_amr_hr_modes2_cmd); + install_element(BTS_NODE, &cfg_bts_amr_hr_modes3_cmd); + install_element(BTS_NODE, &cfg_bts_amr_hr_modes4_cmd); + install_element(BTS_NODE, &cfg_bts_amr_hr_thres1_cmd); + install_element(BTS_NODE, &cfg_bts_amr_hr_thres2_cmd); + install_element(BTS_NODE, &cfg_bts_amr_hr_thres3_cmd); + install_element(BTS_NODE, &cfg_bts_amr_hr_hyst1_cmd); + install_element(BTS_NODE, &cfg_bts_amr_hr_hyst2_cmd); + install_element(BTS_NODE, &cfg_bts_amr_hr_hyst3_cmd); + install_element(BTS_NODE, &cfg_bts_amr_hr_start_mode_cmd); install_element(BTS_NODE, &cfg_trx_cmd); install_node(&trx_node, dummy_config_write); diff --git a/src/libbsc/chan_alloc.c b/src/libbsc/chan_alloc.c index 9b74329f4..3feecc59c 100644 --- a/src/libbsc/chan_alloc.c +++ b/src/libbsc/chan_alloc.c @@ -280,7 +280,8 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis)); /* clear multi rate config */ - memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf)); + memset(&lchan->mr_ms_lv, 0, sizeof(lchan->mr_ms_lv)); + memset(&lchan->mr_bts_lv, 0, sizeof(lchan->mr_bts_lv)); } else { struct challoc_signal_data sig; sig.bts = bts; diff --git a/src/libbsc/gsm_04_08_utils.c b/src/libbsc/gsm_04_08_utils.c index 8ccefd740..eef3ae1c6 100644 --- a/src/libbsc/gsm_04_08_utils.c +++ b/src/libbsc/gsm_04_08_utils.c @@ -356,6 +356,60 @@ void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, } } +int gsm48_multirate_config(uint8_t *lv, struct amr_multirate_conf *mr, int ms) +{ + int num = 0, i; + + for (i = 0; i < 8; i++) { + if (((mr->gsm48_ie[1] >> i) & 1)) + num++; + } + if (num > 4) { + LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with too " + "many modes in config.\n"); + num = 4; + } + if (num < 1) { + LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with no " + "mode in config.\n"); + num = 1; + } + + lv[0] = (num == 1) ? 2 : (num + 2); + memcpy(lv + 1, mr->gsm48_ie, 2); + if (num == 1) + return 0; + if (ms) { + lv[3] = mr->mode[0].threshold_ms & 0x3f; + lv[4] = mr->mode[0].hysteresis_ms << 4; + if (num == 2) + return 0; + lv[4] |= (mr->mode[1].threshold_ms & 0x3f) >> 2; + lv[5] = mr->mode[1].threshold_ms << 6; + lv[5] |= (mr->mode[1].hysteresis_ms & 0x0f) << 2; + if (num == 3) + return 0; + lv[5] |= (mr->mode[2].threshold_ms & 0x3f) >> 4; + lv[6] = mr->mode[2].threshold_ms << 4; + lv[6] |= mr->mode[2].hysteresis_ms & 0x0f; + } else { + lv[3] = mr->mode[0].threshold_bts & 0x3f; + lv[4] = mr->mode[0].hysteresis_bts << 4; + if (num == 2) + return 0; + lv[4] |= (mr->mode[1].threshold_bts & 0x3f) >> 2; + lv[5] = mr->mode[1].threshold_bts << 6; + lv[5] |= (mr->mode[1].hysteresis_bts & 0x0f) << 2; + if (num == 3) + return 0; + lv[5] |= (mr->mode[2].threshold_bts & 0x3f) >> 4; + lv[6] = mr->mode[2].threshold_bts << 4; + lv[6] |= mr->mode[2].hysteresis_bts & 0x0f; + } + + return 0; +} + #define GSM48_HOCMD_CCHDESC_LEN 16 /* Chapter 9.1.15: Handover Command */ @@ -434,17 +488,9 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, } /* in case of multi rate we need to attach a config */ - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { - if (lchan->mr_conf.ver == 0) { - LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " - "without multirate config.\n"); - } else { - uint8_t *data = msgb_put(msg, 4); - data[0] = GSM48_IE_MUL_RATE_CFG; - data[1] = 0x2; - memcpy(&data[2], &lchan->mr_conf, 2); - } - } + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) + msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0], + lchan->mr_ms_lv + 1); return gsm48_sendmsg(msg); } @@ -470,17 +516,9 @@ int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, uint8_t mode) cmm->mode = mode; /* in case of multi rate we need to attach a config */ - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { - if (lchan->mr_conf.ver == 0) { - LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " - "without multirate config.\n"); - } else { - uint8_t *data = msgb_put(msg, 4); - data[0] = GSM48_IE_MUL_RATE_CFG; - data[1] = 0x2; - memcpy(&data[2], &lchan->mr_conf, 2); - } - } + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) + msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0], + lchan->mr_ms_lv + 1); return gsm48_sendmsg(msg); } -- cgit v1.2.3