diff options
Diffstat (limited to 'src/host/layer23/src/mobile/mnccms.c')
-rw-r--r-- | src/host/layer23/src/mobile/mnccms.c | 116 |
1 files changed, 107 insertions, 9 deletions
diff --git a/src/host/layer23/src/mobile/mnccms.c b/src/host/layer23/src/mobile/mnccms.c index d23fbb70..fb09778c 100644 --- a/src/host/layer23/src/mobile/mnccms.c +++ b/src/host/layer23/src/mobile/mnccms.c @@ -134,9 +134,9 @@ static int8_t mncc_get_bearer(const struct gsm_settings *set, uint8_t speech_ver return speech_ver; } -static void mncc_set_bearer(struct gsm_mncc *mncc, - const struct gsm_settings *set, - int8_t speech_ver) +static void mncc_set_bcap_speech(struct gsm_mncc *mncc, + const struct gsm_settings *set, + int speech_ver) { int i = 0; @@ -192,6 +192,44 @@ static void mncc_set_bearer(struct gsm_mncc *mncc, mncc->bearer_cap.mode = GSM48_BCAP_TMOD_CIRCUIT; } +static void mncc_set_bcap_data(struct gsm_mncc *mncc, + const struct gsm_settings *set) +{ + struct gsm_mncc_bearer_cap *bcap = &mncc->bearer_cap; + + mncc->fields |= MNCC_F_BEARER_CAP; + + *bcap = (struct gsm_mncc_bearer_cap) { + .transfer = GSM_MNCC_BCAP_UNR_DIG, + .mode = GSM48_BCAP_TMOD_CIRCUIT, + .coding = GSM48_BCAP_CODING_GSM_STD, + /* .radio is set below */ + .data = { + /* TODO: make these fields configurable via *set */ + .rate_adaption = GSM48_BCAP_RA_V110_X30, + .sig_access = GSM48_BCAP_SA_I440_I450, + .async = 1, + .transp = GSM48_BCAP_TR_TRANSP, + .nr_data_bits = 8, + .parity = GSM48_BCAP_PAR_NONE, + .nr_stop_bits = 1, + .user_rate = GSM48_BCAP_UR_9600, + .interm_rate = GSM48_BCAP_IR_16k, + }, + }; + + if (set->ch_cap == GSM_CAP_SDCCH_TCHF_TCHH) { + if (set->half_prefer) + bcap->radio = GSM48_BCAP_RRQ_DUAL_HR; + else + bcap->radio = GSM48_BCAP_RRQ_DUAL_FR; + LOGP(DMNCC, LOGL_INFO, " support TCH/H also\n"); + } else { + bcap->radio = GSM48_BCAP_RRQ_FR_ONLY; + LOGP(DMNCC, LOGL_INFO, " support TCH/F only\n"); + } +} + /* Check the given Bearer Capability, select first supported speech codec version. * The choice between half-rate and full-rate is made based on current settings. * Return a selected codec or -1 if no speech codec was selected. */ @@ -249,6 +287,59 @@ static int mncc_handle_bcap_speech(const struct gsm_mncc_bearer_cap *bcap, return speech_ver; } +/* Check the given Bearer Capability for a data call (CSD). + * Return 0 if the bearer is accepted, otherwise return -1. */ +static int mncc_handle_bcap_data(const struct gsm_mncc_bearer_cap *bcap, + const struct gsm_settings *set) +{ + if (bcap->data.rate_adaption != GSM48_BCAP_RA_V110_X30) { + LOGP(DMNCC, LOGL_ERROR, + "%s(): Rate adaption (octet 5) 0x%02x is not supported\n", + __func__, bcap->data.rate_adaption); + return -ENOTSUP; + } + if (bcap->data.sig_access != GSM48_BCAP_SA_I440_I450) { + LOGP(DMNCC, LOGL_ERROR, + "%s(): Signalling access protocol (octet 5) 0x%02x is not supported\n", + __func__, bcap->data.sig_access); + return -ENOTSUP; + } + +#define BCAP_RATE(interm_rate, user_rate) \ + ((interm_rate << 8) | (user_rate << 0)) + + switch (BCAP_RATE(bcap->data.interm_rate, bcap->data.user_rate)) { + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_300): + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_1200): + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_2400): + if (bcap->data.transp != GSM48_BCAP_TR_TRANSP) { + LOGP(DMNCC, LOGL_ERROR, + "%s(): wrong user-rate 0x%02x for a non-transparent call\n", + __func__, bcap->data.user_rate); + return -EINVAL; + } + /* fall-through */ + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_4800): + case BCAP_RATE(GSM48_BCAP_IR_16k, GSM48_BCAP_UR_9600): + if (bcap->data.transp != GSM48_BCAP_TR_TRANSP) { + LOGP(DMNCC, LOGL_ERROR, + "%s(): only transparent calls are supported so far\n", + __func__); + return -ENOTSUP; + } + break; + default: + LOGP(DMNCC, LOGL_ERROR, + "%s(): User rate 0x%02x (octets 6a) is not supported (IR=0x%02x)\n", + __func__, bcap->data.user_rate, bcap->data.interm_rate); + return -ENOTSUP; + } + +#undef BCAP_RATE + + return 0; +} + static int mncc_handle_bcap(struct gsm_mncc *mncc_out, /* CC Call Confirmed */ const struct gsm_mncc *mncc_in, /* CC Setup */ const struct gsm_settings *set) @@ -262,7 +353,7 @@ static int mncc_handle_bcap(struct gsm_mncc *mncc_out, /* CC Call Confirmed */ /* if the Bearer Capability 1 IE is not present */ if (~mncc_in->fields & MNCC_F_BEARER_CAP) { /* ... include our own Bearer Capability, assuming a speech call */ - mncc_set_bearer(mncc_out, set, -1); + mncc_set_bcap_speech(mncc_out, set, -1); return 0; } @@ -289,12 +380,15 @@ static int mncc_handle_bcap(struct gsm_mncc *mncc_out, /* CC Call Confirmed */ * or if given codec is unimplemented */ if (speech_ver < 0) - mncc_set_bearer(mncc_out, set, -1); + mncc_set_bcap_speech(mncc_out, set, -1); else if (bcap->speech_ver[1] >= 0 || speech_ver != 0) - mncc_set_bearer(mncc_out, set, speech_ver); + mncc_set_bcap_speech(mncc_out, set, speech_ver); break; } case GSM48_BCAP_ITCAP_UNR_DIG_INF: + if (mncc_handle_bcap_data(bcap, set) != 0) + return -ENOTSUP; + break; default: LOGP(DMNCC, LOGL_ERROR, "%s(): Information transfer capability 0x%02x is not supported\n", @@ -596,7 +690,7 @@ int mncc_recv_internal(struct osmocom_ms *ms, int msg_type, void *arg) return 0; } -int mncc_call(struct osmocom_ms *ms, const char *number) +int mncc_call(struct osmocom_ms *ms, const char *number, bool data) { struct gsm_call *call; struct gsm_mncc setup; @@ -627,7 +721,8 @@ int mncc_call(struct osmocom_ms *ms, const char *number) /* emergency */ setup.emergency = 1; } else { - LOGP(DMNCC, LOGL_INFO, "Make call to %s\n", number); + LOGP(DMNCC, LOGL_INFO, "Make %s call to %s\n", + data ? "data" : "voice", number); /* called number */ setup.fields |= MNCC_F_CALLED; if (number[0] == '+') { @@ -640,7 +735,10 @@ int mncc_call(struct osmocom_ms *ms, const char *number) OSMO_STRLCPY_ARRAY(setup.called.number, number); /* bearer capability (mandatory) */ - mncc_set_bearer(&setup, &ms->settings, -1); + if (data) + mncc_set_bcap_data(&setup, &ms->settings); + else + mncc_set_bcap_speech(&setup, &ms->settings, -1); /* CLIR */ if (ms->settings.clir) |