diff options
author | Harald Welte <laforge@gnumonks.org> | 2011-09-06 22:14:31 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2011-09-06 22:19:40 +0200 |
commit | 24713348c45b648f26f4ce65b591b2de9245ef58 (patch) | |
tree | ace0cee14eec1ef128c992e6c2566671b68a188e /src | |
parent | a16bcc2cc68e8e808e73e50f5149beb265385e09 (diff) |
More comprehensive AMR handling
* parse AMR multirate config form 04.08 IE into easier format
* CMR, CMC and CMI on the L1 side are an _index_ into the current
mode array
* Fix conversion of AMR SID frames from RTP -> L1
Diffstat (limited to 'src')
-rw-r--r-- | src/common/rsl.c | 92 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/tch.c | 94 |
2 files changed, 153 insertions, 33 deletions
diff --git a/src/common/rsl.c b/src/common/rsl.c index b9046f35..459ad2b3 100644 --- a/src/common/rsl.c +++ b/src/common/rsl.c @@ -121,6 +121,88 @@ static void lchan_tchmode_from_cmode(struct gsm_lchan *lchan, * support */ +static void log_mr_conf(int ss, int logl, const char *pfx, + struct amr_multirate_conf *amr_mrc) +{ + int i; + + LOGP(ss, logl, "%s AMR MR Conf: num_modes=%u", + pfx, amr_mrc->num_modes); + + for (i = 0; i < amr_mrc->num_modes; i++) + LOGPC(ss, logl, ", mode[%u] = %u/%u/%u", + i, amr_mrc->mode[i].mode, amr_mrc->mode[i].threshold, + amr_mrc->mode[i].hysteresis); + LOGPC(ss, logl, "\n"); +} + + +/* parse a GSM 04.08 MultiRate Config IE (10.5.2.21aa) in a more + * comfortable internal data structure */ +static int parse_mr_conf(struct amr_multirate_conf *amr_mrc, + const uint8_t *mr_conf, unsigned int len) +{ + uint8_t mr_version = mr_conf[0] >> 5; + uint8_t num_codecs = 0; + int i, j = 0; + + if (mr_version != 1) { + LOGP(DRSL, LOGL_ERROR, "AMR Multirate Version %u unknonw\n", + mr_version); + goto ret_einval; + } + + /* check number of active codecs */ + for (i = 0; i < 8; i++) { + if (mr_conf[1] & (1 << i)) + num_codecs++; + } + + /* check for minimum length */ + if (num_codecs == 0 || + (num_codecs == 1 && len < 2) || + (num_codecs == 2 && len < 4) || + (num_codecs == 3 && len < 5) || + (num_codecs == 4 && len < 6) || + (num_codecs > 4)) { + LOGP(DRSL, LOGL_ERROR, "AMR Multirate with %u modes len=%u " + "not possible\n", num_codecs, len); + goto ret_einval; + } + + /* copy the first two octets of the IE */ + amr_mrc->gsm48_ie[0] = mr_conf[0]; + amr_mrc->gsm48_ie[1] = mr_conf[1]; + + amr_mrc->num_modes = num_codecs; + + for (i = 0; i < 8; i++) { + if (mr_conf[1] & (1 << i)) { + amr_mrc->mode[j++].mode = i; + } + } + + if (num_codecs >= 2) { + amr_mrc->mode[0].threshold = mr_conf[1] & 0x3F; + amr_mrc->mode[0].hysteresis = mr_conf[2] >> 4; + } + if (num_codecs >= 3) { + amr_mrc->mode[1].threshold = + ((mr_conf[2] & 0xF) << 2) | (mr_conf[3] >> 6); + amr_mrc->mode[1].hysteresis = (mr_conf[3] >> 2) & 0x7; + } + if (num_codecs >= 4) { + amr_mrc->mode[3].threshold = + ((mr_conf[3] & 0x3) << 4) | (mr_conf[4] >> 4); + amr_mrc->mode[3].hysteresis = mr_conf[4] & 0xF; + } + + return num_codecs; + +ret_einval: + return -EINVAL; +} + #warning merge lchan_lookup with OpenBSC /* determine logical channel based on TRX and channel number IE */ @@ -683,6 +765,10 @@ static int rsl_rx_chan_activ(struct msgb *msg) } memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); + parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), + TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); + log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan), + &lchan->tch.amr_mr); } /* 9.3.53 MultiRate Control */ /* 9.3.54 Supported Codec Types */ @@ -917,7 +1003,11 @@ static int rsl_rx_mode_modif(struct msgb *msg) return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT); } memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), - TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); + TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); + parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), + TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); + log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan), + &lchan->tch.amr_mr); } /* 9.3.53 MultiRate Control */ /* 9.3.54 Supported Codec Types */ diff --git a/src/osmo-bts-sysmo/tch.c b/src/osmo-bts-sysmo/tch.c index dd2ae32d..68ad0ce4 100644 --- a/src/osmo-bts-sysmo/tch.c +++ b/src/osmo-bts-sysmo/tch.c @@ -65,9 +65,9 @@ void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in, /* shift the last nibble, in case there's an odd count */ i = num_whole_bytes; if (num_nibbles & 1) - out[i] = (in[i-1] & 0xF) << 4; - else out[i] = ((in[i-1] & 0xF) << 4) | (in[i] >> 4); + else + out[i] = (in[i-1] & 0xF) << 4; } @@ -201,29 +201,30 @@ static int rtppayload_to_l1_hr(uint8_t *l1_payload, uint8_t *rtp_payload, #define AMR_CMR_NONE 0xF static struct msgb *l1_to_rtppayload_amr(uint8_t *l1_payload, uint8_t payload_len, - uint8_t active_codec_set) + struct amr_multirate_conf *amr_mrc) { struct msgb *msg = msgb_alloc_headroom(1024, 128, "L1C-to-RTP"); uint8_t *cur; + u_int8_t cmr; uint8_t ft = l1_payload[2] & 0xF; - uint8_t cmr = l1_payload[1]; + uint8_t cmr_idx = l1_payload[1]; uint8_t amr_if2_len = payload_len - 2; -#warning Only use CMR when it actually appears according to TDMA time #if 0 - /* CMR can never be > 7 */ - if (cmr > 7) + /* CMR == Unset means CMR was not transmitted at this TDMA */ + if (cmr_idx >= GsmL1_AmrCodecMode_Unset) cmr = AMR_CMR_NONE; - else { + else if (cmr_idx >= amr_mrc->num_modes) { /* Make sure the CMR of the phone is in the active codec set */ - if (!(active_codec_set & (1 << cmr))) { - LOGP(DL1C, LOGL_NOTICE, "L1->RTP: overriding CMR %u\n", cmr); - cmr = AMR_CMR_NONE; - } + LOGP(DL1C, LOGL_NOTICE, "L1->RTP: overriding CMR IDX %u\n", cmr_idx); + cmr = AMR_CMR_NONE; + } else { + cmr = amr_mrc->mode[cmr_idx].mode; } #else cmr = AMR_CMR_NONE; #endif + /* RFC 3267 4.4.1 Payload Header */ msgb_put_u8(msg, (cmr << 4)); @@ -245,6 +246,16 @@ enum amr_frame_type { AMR_FT_SID_AMR = 8, }; +int get_amr_mode_idx(const struct amr_multirate_conf *amr_mrc, uint8_t cmi) +{ + unsigned int i; + for (i = 0; i < amr_mrc->num_modes; i++) { + if (amr_mrc->mode[i].mode == cmi) + return i; + } + return -EINVAL; +} + /*! \brief convert AMR from RTP payload to L1 format * \param[out] l1_payload payload part of L1 buffer * \param[in] rtp_payload pointer to RTP payload data @@ -252,47 +263,67 @@ enum amr_frame_type { * \returns number of \a l1_payload bytes filled */ static int rtppayload_to_l1_amr(uint8_t *l1_payload, uint8_t *rtp_payload, - uint8_t payload_len, const uint8_t active_codec_set) + uint8_t payload_len, + struct amr_multirate_conf *amr_mrc) { uint8_t ft = (rtp_payload[1] >> 3) & 0xf; uint8_t cmr = rtp_payload[0] >> 4; - uint8_t *l1_cmi = l1_payload; - uint8_t *l1_cmr = l1_payload+1; + uint8_t cmi, sti; + uint8_t *l1_cmi_idx = l1_payload; + uint8_t *l1_cmr_idx = l1_payload+1; uint8_t amr_if2_core_len = payload_len - 2; + int rc; /* step1: shift everything right one nibble; make space for FT */ - osmo_nibble_shift_right(l1_payload+2, rtp_payload+2, amr_if2_core_len*2 -1); + osmo_nibble_shift_right(l1_payload+2, rtp_payload+2, amr_if2_core_len*2); /* step2: reverse the bit-order within every byte of the IF2 * core frame contained in the RTP payload */ - osmo_revbytebits_buf(l1_payload+2, amr_if2_core_len); + osmo_revbytebits_buf(l1_payload+2, amr_if2_core_len+1); /* CMI in downlink tells the L1 encoder which encoding function * it will use, so we have to use the frame type */ switch (ft) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: - *l1_cmi = ft; + cmi = ft; + LOGP(DL1C, LOGL_ERROR, "SPEECH frame with CMI %u\n", cmi); break; case AMR_FT_SID_AMR: - /* extract the mode indiciation from last 3 bits of - * 39 bit SID frame */ - *l1_cmi = *(l1_payload+2+4) >> 4; + /* extract the mode indiciation from last bits of + * 39 bit SID frame (Table 6 / 26.101) */ + cmi = (rtp_payload[2+4] >> 1) & 0x7; + sti = rtp_payload[2+4] & 0x10; + LOGP(DL1C, LOGL_ERROR, "SID %s frame with CMI %u\n", + sti ? "UPDATE" : "FIRST", cmi); break; default: LOGP(DL1C, LOGL_ERROR, "unsupported AMR FT 0x%02x\n", ft); + return -EINVAL; break; } + rc = get_amr_mode_idx(amr_mrc, cmi); + if (rc < 0) { + LOGP(DL1C, LOGL_ERROR, "AMR CMI %u not part of AMR MR set\n", + cmi); + *l1_cmi_idx = 0; + } else + *l1_cmi_idx = rc; + /* Codec Mode Request is in upper 4 bits of RTP payload header, * and we simply copy the CMR into the CMC */ - if (cmr == 0xF) - *l1_cmr = 2; - else if (!(active_codec_set & (1 << cmr))) { + if (cmr == 0xF) { /* FIXME: we need some state about the last codec mode */ - LOGP(DL1C, LOGL_NOTICE, "RTP->L1: overriding CMR %u\n", cmr); - *l1_cmr = 2; - } else - *l1_cmr = cmr; + *l1_cmr_idx = 0; + } else { + rc = get_amr_mode_idx(amr_mrc, cmr); + if (rc < 0) { + /* FIXME: we need some state about the last codec mode */ + LOGP(DL1C, LOGL_NOTICE, "RTP->L1: overriding CMR %u\n", cmr); + *l1_cmr_idx = 0; + } else + *l1_cmr_idx = rc; + } #if 0 /* check for bad quality indication */ if (rtp_payload[1] & AMR_TOC_QBIT) { @@ -339,7 +370,6 @@ void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, uint8_t *rtp_pl, GsmL1_MsgUnitParam_t *msu_param = &data_req->msgUnitParam; uint8_t *payload_type = &msu_param->u8Buffer[0]; uint8_t *l1_payload = &msu_param->u8Buffer[1]; - uint8_t *mr_conf = &lchan->mr_conf; int rc; DEBUGP(DL1C, "%s RTP IN: %s\n", gsm_lchan_name(lchan), @@ -366,7 +396,7 @@ void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, uint8_t *rtp_pl, case GSM48_CMODE_SPEECH_AMR: *payload_type = GsmL1_TchPlType_Amr; rc = rtppayload_to_l1_amr(l1_payload, rtp_pl, - rtp_pl_len, mr_conf[1]); + rtp_pl_len, &lchan->tch.amr_mr); break; default: /* we don't support CSD modes */ @@ -416,7 +446,6 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg) GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd; uint8_t payload_type = data_ind->msgUnitParam.u8Buffer[0]; uint8_t *payload = data_ind->msgUnitParam.u8Buffer + 1; - uint8_t *mr_conf = &lchan->mr_conf; uint8_t payload_len; struct msgb *rmsg = NULL; @@ -467,7 +496,8 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg) break; #endif case GsmL1_TchPlType_Amr: - rmsg = l1_to_rtppayload_amr(payload, payload_len, mr_conf[1]); + rmsg = l1_to_rtppayload_amr(payload, payload_len, + &lchan->tch.amr_mr); break; } |