summaryrefslogtreecommitdiffstats
path: root/src/host/layer23/src/mobile/gsm48_cc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/host/layer23/src/mobile/gsm48_cc.c')
-rw-r--r--src/host/layer23/src/mobile/gsm48_cc.c169
1 files changed, 125 insertions, 44 deletions
diff --git a/src/host/layer23/src/mobile/gsm48_cc.c b/src/host/layer23/src/mobile/gsm48_cc.c
index f1e81098..94b81cb0 100644
--- a/src/host/layer23/src/mobile/gsm48_cc.c
+++ b/src/host/layer23/src/mobile/gsm48_cc.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -29,13 +25,16 @@
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/signal.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/mobile/mncc.h>
#include <osmocom/bb/mobile/transaction.h>
#include <osmocom/bb/mobile/gsm48_cc.h>
-#include <osmocom/bb/mobile/voice.h>
+#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h>
+#include <osmocom/bb/mobile/tch.h>
#include <l1ctl_proto.h>
static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg);
@@ -44,6 +43,8 @@ int mncc_release_ind(struct osmocom_ms *ms, struct gsm_trans *trans,
uint32_t callref, int location, int value);
static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg);
static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg);
+static void gsm48_cc_trans_bcap_update(struct gsm_trans *trans,
+ const struct gsm_mncc *mncc);
/*
* init
@@ -55,7 +56,7 @@ int gsm48_cc_init(struct osmocom_ms *ms)
cc->ms = ms;
- if (!cc->mncc_upqueue.next == 0)
+ if (cc->mncc_upqueue.next != NULL)
return 0;
LOGP(DCC, LOGL_INFO, "init Call Control\n");
@@ -183,11 +184,13 @@ static int gsm48_cc_to_mm(struct msgb *msg, struct gsm_trans *trans,
/* enqueue message to application (MNCC-SAP) */
static int mncc_recvmsg(struct osmocom_ms *ms, struct gsm_trans *trans,
- int msg_type, struct gsm_mncc *mncc)
+ uint32_t msg_type, struct gsm_mncc *mncc)
{
struct gsm48_cclayer *cc = &ms->cclayer;
struct msgb *msg;
+ gsm48_cc_trans_bcap_update(trans, mncc);
+
if (trans)
LOGP(DCC, LOGL_INFO, "(ms %s ti %x) Sending '%s' to MNCC.\n",
ms->name, trans->transaction_id,
@@ -241,6 +244,8 @@ static void new_cc_state(struct gsm_trans *trans, int state)
gsm48_cc_state_name(state));
trans->cc.state = state;
+
+ osmo_signal_dispatch(SS_L23_TRANS, S_L23_CC_TRANS_STATE_CHG, trans);
}
/*
@@ -369,6 +374,9 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans)
{
gsm48_stop_cc_timer(trans);
+ talloc_free(trans->cc.bcap);
+ trans->cc.bcap = NULL;
+
/* disable audio distribution */
if (trans->ms->mncc_entity.ref == trans->callref)
trans->ms->mncc_entity.ref = 0;
@@ -384,6 +392,16 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans)
new_cc_state(trans, GSM_CSTATE_NULL);
}
+static void gsm48_cc_trans_bcap_update(struct gsm_trans *trans,
+ const struct gsm_mncc *mncc)
+{
+ if (~mncc->fields & MNCC_F_BEARER_CAP)
+ return;
+ if (trans->cc.bcap == NULL)
+ trans->cc.bcap = talloc(trans, struct gsm_mncc_bearer_cap);
+ memcpy(trans->cc.bcap, &mncc->bearer_cap, sizeof(mncc->bearer_cap));
+}
+
/* release MM connection, go NULL state, free transaction */
static int gsm48_rel_null_free(struct gsm_trans *trans)
{
@@ -625,10 +643,14 @@ static int gsm48_cc_rx_progress(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received PROGRESS\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_PROGR_IND, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
memset(&progress, 0, sizeof(struct gsm_mncc));
progress.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
- GSM48_IE_PROGR_IND, 0);
/* progress */
if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
progress.fields |= MNCC_F_PROGRESS;
@@ -656,13 +678,17 @@ static int gsm48_cc_rx_call_proceeding(struct gsm_trans *trans,
struct tlv_parsed tp;
struct gsm_mncc call_proc;
- LOGP(DCC, LOGL_INFO, "sending CALL PROCEEDING\n");
+ LOGP(DCC, LOGL_INFO, "received CALL PROCEEDING\n");
+
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
gsm48_stop_cc_timer(trans);
memset(&call_proc, 0, sizeof(struct gsm_mncc));
call_proc.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
#if 0
/* repeat */
if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR))
@@ -714,12 +740,16 @@ static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received ALERTING\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
/* no T301 in MS call control */
memset(&alerting, 0, sizeof(struct gsm_mncc));
alerting.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* facility */
if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
alerting.fields |= MNCC_F_FACILITY;
@@ -755,11 +785,15 @@ static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received CONNECT\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
memset(&connect, 0, sizeof(struct gsm_mncc));
connect.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* facility */
if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
connect.fields |= MNCC_F_FACILITY;
@@ -825,10 +859,13 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received SETUP\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
memset(&setup, 0, sizeof(struct gsm_mncc));
setup.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
-
/* bearer capability */
if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
setup.fields |= MNCC_F_BEARER_CAP;
@@ -1078,9 +1115,13 @@ static int gsm48_cc_rx_start_dtmf_ack(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received START DTMF ACKNOWLEDGE\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
memset(&dtmf, 0, sizeof(struct gsm_mncc));
dtmf.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* keypad facility */
if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) {
dtmf.fields |= MNCC_F_KEYPAD;
@@ -1141,9 +1182,13 @@ static int gsm48_cc_rx_stop_dtmf_ack(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received STOP DTMF ACKNOWLEDGE\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
memset(&dtmf, 0, sizeof(struct gsm_mncc));
dtmf.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
return mncc_recvmsg(trans->ms, trans, MNCC_STOP_DTMF_RSP, &dtmf);
}
@@ -1337,15 +1382,18 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received USERINFO\n");
- memset(&user, 0, sizeof(struct gsm_mncc));
- user.callref = trans->callref;
if (payload_len < 1) {
- LOGP(DCC, LOGL_NOTICE, "Short read of userinfo message "
- "error.\n");
+ LOGP(DCC, LOGL_NOTICE, "Short read of USERINFO message\n");
return -EINVAL;
}
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
- GSM48_IE_USER_USER, 0);
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_USER_USER, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(&user, 0, sizeof(struct gsm_mncc));
+ user.callref = trans->callref;
/* user-user */
gsm48_decode_useruser(&user.useruser,
TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
@@ -1419,17 +1467,20 @@ static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received MODIFY REJECT\n");
+ if (payload_len < 1) {
+ LOGP(DCC, LOGL_NOTICE, "Short read of MODIFY REJECT message\n");
+ return -EINVAL;
+ }
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
memset(&modify, 0, sizeof(struct gsm_mncc));
modify.callref = trans->callref;
- if (payload_len < 1) {
- LOGP(DCC, LOGL_NOTICE, "Short read of modify reject message "
- "error.\n");
- return -EINVAL;
- }
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
- GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE);
/* bearer capability */
if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
modify.fields |= MNCC_F_BEARER_CAP;
@@ -1677,14 +1728,18 @@ static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received DISCONNECT\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
+ GSM48_IE_CAUSE, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND);
memset(&disc, 0, sizeof(struct gsm_mncc));
disc.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len,
- GSM48_IE_CAUSE, 0);
/* cause */
if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
disc.fields |= MNCC_F_CAUSE;
@@ -1726,11 +1781,15 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received RELEASE\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
memset(&rel, 0, sizeof(struct gsm_mncc));
rel.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* cause */
if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
rel.fields |= MNCC_F_CAUSE;
@@ -1750,7 +1809,7 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
}
- /* in case we receive a relase, when we are already in NULL state */
+ /* in case we receive a release, when we are already in NULL state */
if (trans->cc.state == GSM_CSTATE_NULL) {
LOGP(DCC, LOGL_INFO, "ignoring RELEASE in NULL state\n");
/* release MM conn, free trans */
@@ -1795,11 +1854,15 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
LOGP(DCC, LOGL_INFO, "received RELEASE COMPLETE\n");
+ if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) {
+ LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__);
+ return -EINVAL;
+ }
+
gsm48_stop_cc_timer(trans);
memset(&rel, 0, sizeof(struct gsm_mncc));
rel.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
/* cause */
if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
rel.fields |= MNCC_F_CAUSE;
@@ -1937,10 +2000,16 @@ int mncc_tx_to_cc(void *inst, int msg_type, void *arg)
return -EBUSY;
}
+ /* ASCI call does not allow other transactions */
+ if (trans_find_ongoing_gcc_bcc(ms)) {
+ LOGP(DCC, LOGL_NOTICE, "Phone is busy doing ASCI call\n");
+ return -EBUSY;
+ }
+
data->msg_type = msg_type;
/* Find callref */
- trans = trans_find_by_callref(ms, data->callref);
+ trans = trans_find_by_callref(ms, GSM48_PDISC_CC, data->callref);
if (!trans) {
/* check for SETUP message */
@@ -1968,9 +2037,15 @@ int mncc_tx_to_cc(void *inst, int msg_type, void *arg)
}
}
+ gsm48_cc_trans_bcap_update(trans, data);
+
switch (msg_type) {
case GSM_TCHF_FRAME:
- return gsm_send_voice(ms, arg);
+ case GSM_TCHF_FRAME_EFR:
+ case GSM_TCHH_FRAME:
+ case GSM_TCH_FRAME_AMR:
+ case GSM_BAD_FRAME:
+ return tch_send_mncc_frame(ms, arg);
case MNCC_LCHAN_MODIFY:
return 0;
case MNCC_FRAME_RECV:
@@ -2095,15 +2170,21 @@ static struct datastate {
static int gsm48_cc_data_ind(struct gsm_trans *trans, struct msgb *msg)
{
struct osmocom_ms *ms = trans->ms;
- struct gsm48_hdr *gh = msgb_l3(msg);
- int msg_type = gh->msg_type & 0xbf;
- uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4;
- /* flip */
+ const struct gsm48_hdr *gh = msgb_l3(msg);
int msg_supported = 0; /* determine, if message is supported at all */
+ uint8_t msg_type;
int i, rc;
- /* set transaction ID, if not already */
- trans->transaction_id = transaction_id;
+ if (msgb_l3len(msg) < sizeof(*gh)) {
+ LOGP(DCC, LOGL_INFO, "%s(): short read of msgb: %s\n",
+ __func__, msgb_hexdump(msg));
+ return -EINVAL;
+ }
+
+ msg_type = gh->msg_type & 0xbf;
+
+ /* set transaction ID (flip), if not already */
+ trans->transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4;
/* pull the MMCC header */
msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr));
@@ -2146,7 +2227,7 @@ int gsm48_rcv_cc(struct osmocom_ms *ms, struct msgb *msg)
struct gsm_trans *trans;
int rc = 0;
- trans = trans_find_by_callref(ms, mmh->ref);
+ trans = trans_find_by_callref(ms, GSM48_PDISC_CC, mmh->ref);
if (!trans) {
trans = trans_alloc(ms, GSM48_PDISC_CC, mmh->transaction_id,
mmh->ref);