aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmocom/bsc/bsc_api.h2
-rw-r--r--include/osmocom/bsc/gsm_04_08.h3
-rw-r--r--include/osmocom/bsc/gsm_data_shared.h13
-rw-r--r--src/libbsc/abis_rsl.c13
-rw-r--r--src/libbsc/bsc_api.c28
-rw-r--r--src/libbsc/bsc_vty.c605
-rw-r--r--src/libbsc/chan_alloc.c3
-rw-r--r--src/libbsc/gsm_04_08_utils.c82
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);
}