diff options
-rw-r--r-- | openbsc/include/openbsc/mncc.h | 4 | ||||
-rw-r--r-- | openbsc/src/libmsc/mncc_builtin.c | 107 |
2 files changed, 98 insertions, 13 deletions
diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h index 10192addd..fd1f2badd 100644 --- a/openbsc/include/openbsc/mncc.h +++ b/openbsc/include/openbsc/mncc.h @@ -44,6 +44,10 @@ struct gsm_call { uint32_t callref; /* the 'remote' transaction */ uint32_t remote_ref; + + /* the capabilities */ + uint8_t lchan_type; + struct gsm_mncc_bearer_cap bcap; }; #define MNCC_SETUP_REQ 0x0101 diff --git a/openbsc/src/libmsc/mncc_builtin.c b/openbsc/src/libmsc/mncc_builtin.c index 77df6fba3..1d27a5204 100644 --- a/openbsc/src/libmsc/mncc_builtin.c +++ b/openbsc/src/libmsc/mncc_builtin.c @@ -67,17 +67,60 @@ static struct gsm_call *get_call_ref(uint32_t callref) uint8_t mncc_codec_for_mode(int lchan_type) { - /* FIXME: check codec capabilities of the phone */ - if (lchan_type != GSM_LCHAN_TCH_H) return mncc_int.def_codec[0]; else return mncc_int.def_codec[1]; + } -static uint8_t determine_lchan_mode(struct gsm_mncc *setup) +static void store_bearer_cap(struct gsm_call *call, struct gsm_mncc *mncc) { - return mncc_codec_for_mode(setup->lchan_type); + call->lchan_type = mncc->lchan_type; + memcpy(&call->bcap, &mncc->bearer_cap, sizeof(struct gsm_mncc_bearer_cap)); +} + +#define is_speech_ver_tch_h(speech_ver) ((speech_ver & 1) == 1) + +#define speech_ver_to_lchan_mode(speech_ver) (((speech_ver & 0xe) << 4) | 1) + +static int determine_lchan_mode(struct gsm_call *calling, + struct gsm_call *called) +{ + int i, j; + + /* FIXME: dynamic channel configuration */ + if (calling->lchan_type != called->lchan_type) { + LOGP(DMNCC, LOGL_NOTICE, "Not equal lchan_types\n"); + return -ENOTSUP; + } + if (calling->lchan_type != GSM_LCHAN_TCH_F + && calling->lchan_type != GSM_LCHAN_TCH_H) { + LOGP(DMNCC, LOGL_NOTICE, "Not TCH lchan_types\n"); + return -ENOTSUP; + } + + /* select best codec, as prefered by the caller and supporte by both. */ + for (i = 0; calling->bcap.speech_ver[i] >= 0; i++) { + /* omit capability of different channel type + * FIXME: dynamic channel configuration */ + if (calling->lchan_type == GSM_LCHAN_TCH_F + && is_speech_ver_tch_h(calling->bcap.speech_ver[i])) + continue; + if (calling->lchan_type == GSM_LCHAN_TCH_H + && !is_speech_ver_tch_h(calling->bcap.speech_ver[i])) + continue; + for (j = 0; called->bcap.speech_ver[j] >= 0; j++) { + if (calling->bcap.speech_ver[i] + == called->bcap.speech_ver[j]) { + /* convert speech version to lchan mode */ + return speech_ver_to_lchan_mode( + calling->bcap.speech_ver[i]); + } + } + } + + return -ENOTSUP; } /* on incoming call, look up database and send setup to remote subscr. */ @@ -134,12 +177,8 @@ static int mncc_setup_ind(struct gsm_call *call, int msg_type, DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref); mncc_tx_to_cc(call->net, MNCC_CALL_PROC_REQ, &mncc); - /* modify mode */ - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - mncc.lchan_mode = determine_lchan_mode(setup); - DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref); - mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, &mncc); + /* store bearer capabilites of supported modes */ + store_bearer_cap(call, setup); /* send setup to remote */ // setup->fields |= MNCC_F_SIGNAL; @@ -154,6 +193,50 @@ out_reject: return 0; } +static int mncc_call_conf_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *conf) +{ + struct gsm_call *remote; + struct gsm_mncc mncc; + int mode; + + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + + /* send alerting to remote */ + if (!(remote = get_call_ref(call->remote_ref))) + return 0; + + /* store bearer capabilites of supported modes */ + store_bearer_cap(call, conf); + + mode = determine_lchan_mode(call, remote); + if (mode < 0) { + LOGP(DMNCC, LOGL_NOTICE, "(call %x,%x) There is no commonly " + "supported speech version\n", call->callref, + remote->callref); + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); + goto out_release; + } + + /* modify mode */ + mncc.lchan_mode = mode; + DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref); + mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, &mncc); + mncc.callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", remote->callref); + mncc_tx_to_cc(remote->net, MNCC_LCHAN_MODIFY, &mncc); + + return 0; + +out_release: + mncc_tx_to_cc(call->net, MNCC_REL_REQ, &mncc); + free_call(call); + mncc_tx_to_cc(remote->net, MNCC_REL_REQ, &mncc); + free_call(remote); + return 0; +} static int mncc_alert_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *alert) { @@ -362,9 +445,7 @@ int int_mncc_recv(struct gsm_network *net, struct msgb *msg) case MNCC_SETUP_COMPL_IND: break; case MNCC_CALL_CONF_IND: - /* we now need to MODIFY the channel */ - data->lchan_mode = determine_lchan_mode(data); - mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, data); + rc = mncc_call_conf_ind(call, msg_type, arg); break; case MNCC_ALERT_IND: rc = mncc_alert_ind(call, msg_type, arg); |