summaryrefslogtreecommitdiffstats
path: root/src/host/layer23/src/mobile
diff options
context:
space:
mode:
authorVadim Yanitskiy <vyanitskiy@sysmocom.de>2023-10-11 05:14:19 +0700
committerVadim Yanitskiy <vyanitskiy@sysmocom.de>2023-11-02 14:41:23 +0700
commite73a604de0ed5af7b7ef4c61ad3a92ec7a062ee8 (patch)
tree8e5951f5acaad9cad660fd934ec5095c22482257 /src/host/layer23/src/mobile
parent011308cbcc1cb4f0b93bf6b9c04513f59c3b2d93 (diff)
mobile: add support for Circuit Switched Data calls
This patch implements the signalling part for mobile originating and mobile terminating CSD calls. The user plane interface is to be implemented in follow-up patches. Change-Id: I1995fa0a7a68d9b980852b664d472d4633777ac6 Related: OS#4396
Diffstat (limited to 'src/host/layer23/src/mobile')
-rw-r--r--src/host/layer23/src/mobile/gsm48_rr.c50
-rw-r--r--src/host/layer23/src/mobile/mnccms.c213
-rw-r--r--src/host/layer23/src/mobile/vty_interface.c174
3 files changed, 424 insertions, 13 deletions
diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c
index b8980907..a9dfec76 100644
--- a/src/host/layer23/src/mobile/gsm48_rr.c
+++ b/src/host/layer23/src/mobile/gsm48_rr.c
@@ -374,6 +374,56 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr,
LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V3\n");
}
break;
+ case GSM48_CMODE_DATA_14k5:
+ if (ch_type != RSL_CHAN_Bm_ACCHs) {
+ LOGP(DRR, LOGL_ERROR, "TCH/F is expected for mode %s\n",
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ } else if (!set->csd_tch_f144) {
+ LOGP(DRR, LOGL_ERROR, "Not supporting TCH/F14.4 data (%s)\n",
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ }
+ LOGP(DRR, LOGL_INFO, "Mode: TCH/F14.4 data (%s)\n",
+ gsm48_chan_mode_name(mode));
+ break;
+ case GSM48_CMODE_DATA_12k0:
+ if (ch_type != RSL_CHAN_Bm_ACCHs) {
+ LOGP(DRR, LOGL_ERROR, "TCH/F is expected for mode %s\n",
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ } else if (!set->csd_tch_f96) {
+ LOGP(DRR, LOGL_ERROR, "Not supporting TCH/F9.6 data (%s)\n",
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ }
+ LOGP(DRR, LOGL_INFO, "Mode: TCH/F9.6 data (%s)\n",
+ gsm48_chan_mode_name(mode));
+ break;
+ case GSM48_CMODE_DATA_6k0:
+ if ((ch_type == RSL_CHAN_Bm_ACCHs && !set->csd_tch_f48)
+ || (ch_type == RSL_CHAN_Lm_ACCHs && !set->csd_tch_h48)) {
+ LOGP(DRR, LOGL_ERROR, "Not supporting TCH/%c4.8 data (%s)\n",
+ ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ }
+ LOGP(DRR, LOGL_INFO, "Mode: TCH/%c4.8 data (%s)\n",
+ ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
+ gsm48_chan_mode_name(mode));
+ break;
+ case GSM48_CMODE_DATA_3k6:
+ if ((ch_type == RSL_CHAN_Bm_ACCHs && !set->csd_tch_f24)
+ || (ch_type == RSL_CHAN_Lm_ACCHs && !set->csd_tch_h24)) {
+ LOGP(DRR, LOGL_ERROR, "Not supporting TCH/%c2.4 data (%s)\n",
+ ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
+ gsm48_chan_mode_name(mode));
+ return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
+ }
+ LOGP(DRR, LOGL_INFO, "Mode: TCH/%c2.4 data (%s)\n",
+ ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H',
+ gsm48_chan_mode_name(mode));
+ break;
default:
LOGP(DRR, LOGL_ERROR, "Mode 0x%02x not supported!\n", mode);
return GSM48_RR_CAUSE_CHAN_MODE_UNACCT;
diff --git a/src/host/layer23/src/mobile/mnccms.c b/src/host/layer23/src/mobile/mnccms.c
index d23fbb70..84271917 100644
--- a/src/host/layer23/src/mobile/mnccms.c
+++ b/src/host/layer23/src/mobile/mnccms.c
@@ -35,6 +35,13 @@
static uint32_t new_callref = 1;
static LLIST_HEAD(call_list);
+static const char * const gsm_call_type_str[] = {
+ [GSM_CALL_T_UNKNOWN] = "unknown",
+ [GSM_CALL_T_VOICE] = "voice",
+ [GSM_CALL_T_DATA] = "data",
+ [GSM_CALL_T_DATA_FAX] = "fax",
+};
+
static int dtmf_statemachine(struct gsm_call *call,
const struct gsm_mncc *mncc);
static void timeout_dtmf(void *arg);
@@ -134,9 +141,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 +199,116 @@ 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,
+ enum gsm_call_type call_type)
+{
+ const struct data_call_params *cp = &set->call_params.data;
+ struct gsm_mncc_bearer_cap *bcap = &mncc->bearer_cap;
+
+ mncc->fields |= MNCC_F_BEARER_CAP;
+
+ *bcap = (struct gsm_mncc_bearer_cap) {
+ /* .transfer is set below */
+ .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 is set below */
+ .nr_data_bits = 8,
+ .parity = GSM48_BCAP_PAR_NONE,
+ .nr_stop_bits = 1,
+ /* .user_rate is set below */
+ /* .interm_rate is set below */
+ },
+ };
+
+ /* Radio channel requirement (octet 3) */
+ 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");
+ }
+
+ /* Information transfer capability (octet 3) */
+ switch (call_type) {
+ case GSM_CALL_T_DATA:
+ if (cp->type == DATA_CALL_TYPE_ISDN)
+ bcap->transfer = GSM_MNCC_BCAP_UNR_DIG;
+ else /* == DATA_CALL_TYPE_ANALOG */
+ bcap->transfer = GSM_MNCC_BCAP_AUDIO;
+ break;
+ case GSM_CALL_T_DATA_FAX:
+ bcap->transfer = GSM_MNCC_BCAP_FAX_G3;
+ break;
+ case GSM_CALL_T_VOICE:
+ default: /* shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ /* User rate (octet 6a) */
+ switch (cp->rate) {
+ case DATA_CALL_RATE_V110_300:
+ bcap->data.user_rate = GSM48_BCAP_UR_300;
+ bcap->data.interm_rate = GSM48_BCAP_IR_8k;
+ break;
+ case DATA_CALL_RATE_V110_1200:
+ bcap->data.user_rate = GSM48_BCAP_UR_1200;
+ bcap->data.interm_rate = GSM48_BCAP_IR_8k;
+ break;
+ case DATA_CALL_RATE_V110_2400:
+ bcap->data.user_rate = GSM48_BCAP_UR_2400;
+ bcap->data.interm_rate = GSM48_BCAP_IR_8k;
+ break;
+ case DATA_CALL_RATE_V110_4800:
+ bcap->data.user_rate = GSM48_BCAP_UR_4800;
+ bcap->data.interm_rate = GSM48_BCAP_IR_8k;
+ break;
+ case DATA_CALL_RATE_V110_9600:
+ bcap->data.user_rate = GSM48_BCAP_UR_9600;
+ bcap->data.interm_rate = GSM48_BCAP_IR_16k;
+ break;
+ case DATA_CALL_RATE_V110_14400: /* TODO: the bcap encoder does not support 14400 bps */
+ LOGP(DMNCC, LOGL_INFO, " support for 14400 bps is incomplete\n");
+ bcap->data.user_rate = GSM48_BCAP_UR_9600;
+ bcap->data.interm_rate = GSM48_BCAP_IR_16k;
+ break;
+ }
+
+ /* Connection element (octet 6c) */
+ switch (cp->ce) {
+ case DATA_CALL_CE_TRANSP:
+ bcap->data.transp = GSM48_BCAP_TR_TRANSP;
+ break;
+ case DATA_CALL_CE_TRANSP_PREF:
+ bcap->data.transp = GSM48_BCAP_TR_TR_PREF;
+ break;
+ case DATA_CALL_CE_NON_TRANSP:
+ bcap->data.transp = GSM48_BCAP_TR_RLP;
+ break;
+ case DATA_CALL_CE_NON_TRANSP_PREF:
+ bcap->data.transp = GSM48_BCAP_TR_RLP_PREF;
+ break;
+ }
+
+ /* FAX calls are special (see 3GPP TS 24.008, Annex D.3) */
+ if (call_type == GSM_CALL_T_DATA_FAX) {
+ bcap->data.rate_adaption = GSM48_BCAP_RA_NONE;
+ bcap->data.async = 0; /* shall be sync */
+ bcap->data.transp = GSM48_BCAP_TR_TRANSP;
+ bcap->data.modem_type = GSM48_BCAP_MT_NONE;
+ }
+}
+
/* 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 +366,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 +432,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 +459,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:
+ case GSM48_BCAP_ITCAP_3k1_AUDIO:
+ case GSM48_BCAP_ITCAP_FAX_G3:
+ return mncc_handle_bcap_data(bcap, set);
default:
LOGP(DMNCC, LOGL_ERROR,
"%s(): Information transfer capability 0x%02x is not supported\n",
@@ -402,6 +575,7 @@ int mncc_recv_internal(struct osmocom_ms *ms, int msg_type, void *arg)
return -ENOMEM;
call->ms = ms;
call->callref = data->callref;
+ call->type = GSM_CALL_T_UNKNOWN;
llist_add_tail(&call->entry, &call_list);
}
@@ -530,6 +704,21 @@ int mncc_recv_internal(struct osmocom_ms *ms, int msg_type, void *arg)
cause = GSM48_CC_CAUSE_INCOMPAT_DEST;
goto release;
}
+ switch (mncc.bearer_cap.transfer) {
+ case GSM48_BCAP_ITCAP_SPEECH:
+ call->type = GSM_CALL_T_VOICE;
+ break;
+ case GSM48_BCAP_ITCAP_UNR_DIG_INF:
+ case GSM48_BCAP_ITCAP_3k1_AUDIO:
+ call->type = GSM_CALL_T_DATA;
+ break;
+ case GSM48_BCAP_ITCAP_FAX_G3:
+ call->type = GSM_CALL_T_DATA_FAX;
+ break;
+ default:
+ call->type = GSM_CALL_T_UNKNOWN;
+ break;
+ }
/* CC capabilities (optional) */
if (ms->settings.cc_dtmf) {
mncc.fields |= MNCC_F_CCCAP;
@@ -596,7 +785,8 @@ 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,
+ enum gsm_call_type call_type)
{
struct gsm_call *call;
struct gsm_mncc setup;
@@ -616,6 +806,7 @@ int mncc_call(struct osmocom_ms *ms, const char *number)
return -ENOMEM;
call->ms = ms;
call->callref = new_callref++;
+ call->type = call_type;
call->init = true;
llist_add_tail(&call->entry, &call_list);
@@ -627,7 +818,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",
+ gsm_call_type_str[call_type], number);
/* called number */
setup.fields |= MNCC_F_CALLED;
if (number[0] == '+') {
@@ -640,7 +832,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 (call_type == GSM_CALL_T_VOICE)
+ mncc_set_bcap_speech(&setup, &ms->settings, -1);
+ else
+ mncc_set_bcap_data(&setup, &ms->settings, call_type);
/* CLIR */
if (ms->settings.clir)
diff --git a/src/host/layer23/src/mobile/vty_interface.c b/src/host/layer23/src/mobile/vty_interface.c
index aaf01bda..e79a5a14 100644
--- a/src/host/layer23/src/mobile/vty_interface.c
+++ b/src/host/layer23/src/mobile/vty_interface.c
@@ -494,14 +494,18 @@ DEFUN(network_select, network_select_cmd,
"Name of MS (see \"show ms\")\n"
DEFUN(call_num, call_num_cmd,
- CALL_CMD " NUMBER",
+ CALL_CMD " NUMBER [(voice|data|fax)]",
CALL_CMD_DESC
"Phone number to call "
- "(Use digits '0123456789*#abc', and '+' to dial international)\n")
+ "(Use digits '0123456789*#abc', and '+' to dial international)\n"
+ "Initiate a regular voice call (default)\n"
+ "Initiate a data call (UDI or 3.1 kHz audio)\n"
+ "Initiate a data call (Facsimile group 3)\n")
{
struct osmocom_ms *ms;
struct gsm_settings *set;
struct gsm_settings_abbrev *abbrev;
+ enum gsm_call_type call_type;
const char *number;
ms = l23_vty_get_ms(argv[0], vty);
@@ -527,7 +531,17 @@ DEFUN(call_num, call_num_cmd,
if (vty_check_number(vty, number))
return CMD_WARNING;
- mncc_call(ms, number);
+
+ if (argc < 3 || !strcmp(argv[2], "voice"))
+ call_type = GSM_CALL_T_VOICE; /* implicit default */
+ else if (!strcmp(argv[2], "data"))
+ call_type = GSM_CALL_T_DATA;
+ else if (!strcmp(argv[2], "fax"))
+ call_type = GSM_CALL_T_DATA_FAX;
+ else
+ return CMD_WARNING;
+
+ mncc_call(ms, number, call_type);
return CMD_SUCCESS;
}
@@ -557,7 +571,7 @@ DEFUN(call, call_cmd,
number = argv[1];
if (!strcmp(number, "emergency"))
- mncc_call(ms, number);
+ mncc_call(ms, number, GSM_CALL_T_VOICE);
else if (!strcmp(number, "answer"))
mncc_answer(ms);
else if (!strcmp(number, "hangup"))
@@ -612,6 +626,130 @@ DEFUN(call_dtmf, call_dtmf_cmd,
return CMD_SUCCESS;
}
+#define CALL_PARAMS_CMD \
+ CALL_CMD " params"
+#define CALL_PARAMS_CMD_DESC \
+ CALL_CMD_DESC \
+ "Call related parameters\n"
+
+#define CALL_PARAMS_DATA_CMD \
+ CALL_PARAMS_CMD " data"
+#define CALL_PARAMS_DATA_CMD_DESC \
+ CALL_PARAMS_CMD_DESC \
+ "Parameters for data calls\n"
+
+DEFUN(call_params_data_type,
+ call_params_data_type_cmd,
+ CALL_PARAMS_DATA_CMD " type (isdn|analog-modem)",
+ CALL_PARAMS_DATA_CMD_DESC
+ "Data call type (does not apply to FAX calls)\n"
+ "ISDN (Unrestricted Digital Information)\n"
+ "Analog modem (3.1 kHz audio, ex PLMN)\n")
+{
+ struct osmocom_ms *ms;
+ struct gsm_settings *set;
+ struct data_call_params *cp;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+ set = &ms->settings;
+ cp = &set->call_params.data;
+
+ if (!strcmp(argv[1], "isdn"))
+ cp->type = DATA_CALL_TYPE_ISDN;
+ else if (!strcmp(argv[1], "analog-modem"))
+ cp->type = DATA_CALL_TYPE_ANALOG;
+ else /* should not happen */
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(call_params_data_rate,
+ call_params_data_rate_cmd,
+ CALL_PARAMS_DATA_CMD " rate (65|66|68|70|71|75)",
+ CALL_PARAMS_DATA_CMD_DESC
+ "Data rate (values like in AT+CBST)\n"
+ "300 bps (V.110)\n"
+ "1200 bps (V.110)\n"
+ "2400 bps (V.110 or X.31 flag stuffing)\n"
+ "4800 bps (V.110 or X.31 flag stuffing)\n"
+ "9600 bps (V.110 or X.31 flag stuffing)\n"
+ "14400 bps (V.110 or X.31 flag stuffing)\n")
+{
+ struct osmocom_ms *ms;
+ struct gsm_settings *set;
+ struct data_call_params *cp;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+ set = &ms->settings;
+ cp = &set->call_params.data;
+
+ switch (atoi(argv[1])) {
+ case 65:
+ cp->rate = DATA_CALL_RATE_V110_300;
+ break;
+ case 66:
+ cp->rate = DATA_CALL_RATE_V110_1200;
+ break;
+ case 68:
+ cp->rate = DATA_CALL_RATE_V110_2400;
+ break;
+ case 70:
+ cp->rate = DATA_CALL_RATE_V110_4800;
+ break;
+ case 71:
+ cp->rate = DATA_CALL_RATE_V110_9600;
+ break;
+ case 75:
+ cp->rate = DATA_CALL_RATE_V110_14400;
+ break;
+ default: /* should not happen */
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(call_params_data_ce,
+ call_params_data_ce_cmd,
+ CALL_PARAMS_DATA_CMD " ce (transparent|non-transparent) [prefer]",
+ CALL_PARAMS_DATA_CMD_DESC
+ "Connection element (does not apply to FAX calls)\n"
+ "Transparent connection\n"
+ "Non-transparent connection (RLP)\n"
+ "Prefer the selected mode, but also accept other(s)\n")
+{
+ struct osmocom_ms *ms;
+ struct gsm_settings *set;
+ struct data_call_params *cp;
+
+ ms = l23_vty_get_ms(argv[0], vty);
+ if (!ms)
+ return CMD_WARNING;
+ set = &ms->settings;
+ cp = &set->call_params.data;
+
+ if (!strcmp(argv[1], "transparent")) {
+ if (argc > 2)
+ cp->ce = DATA_CALL_CE_TRANSP_PREF;
+ else
+ cp->ce = DATA_CALL_CE_TRANSP;
+ } else if (!strcmp(argv[1], "non-transparent")) {
+ if (argc > 2)
+ cp->ce = DATA_CALL_CE_NON_TRANSP_PREF;
+ else
+ cp->ce = DATA_CALL_CE_NON_TRANSP;
+ } else { /* should not happen */
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN(sms, sms_cmd, "sms MS_NAME NUMBER .LINE",
"Send an SMS\nName of MS (see \"show ms\")\nPhone number to send SMS "
"(Use digits '0123456789*#abc', and '+' to dial international)\n"
@@ -1311,6 +1449,12 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms)
SUP_WRITE(full_v3, "full-speech-v3");
SUP_WRITE(half_v1, "half-speech-v1");
SUP_WRITE(half_v3, "half-speech-v3");
+ SUP_WRITE(csd_tch_f144, "full-data-14400");
+ SUP_WRITE(csd_tch_f96, "full-data-9600");
+ SUP_WRITE(csd_tch_f48, "full-data-4800");
+ SUP_WRITE(csd_tch_h48, "half-data-4800");
+ SUP_WRITE(csd_tch_f24, "full-data-2400");
+ SUP_WRITE(csd_tch_h24, "half-data-2400");
if (!l23_vty_hide_default || sup->min_rxlev_dbm != set->min_rxlev_dbm)
vty_out(vty, " min-rxlev %d%s", set->min_rxlev_dbm,
VTY_NEWLINE);
@@ -2273,6 +2417,13 @@ SUP_EN_DI(full_v3, "full-speech-v3", "Full rate speech V3 (AMR)", 0);
SUP_EN_DI(half_v1, "half-speech-v1", "Half rate speech V1", 0);
SUP_EN_DI(half_v3, "half-speech-v3", "Half rate speech V3 (AMR)", 0);
+SUP_EN_DI(csd_tch_f144, "full-data-14400", "CSD TCH/F14.4", 0);
+SUP_EN_DI(csd_tch_f96, "full-data-9600", "CSD TCH/F9.6", 0);
+SUP_EN_DI(csd_tch_f48, "full-data-4800", "CSD TCH/F4.8", 0);
+SUP_EN_DI(csd_tch_h48, "half-data-4800", "CSD TCH/H4.8", 0);
+SUP_EN_DI(csd_tch_f24, "full-data-2400", "CSD TCH/F2.4", 0);
+SUP_EN_DI(csd_tch_h24, "half-data-2400", "CSD TCH/H2.4", 0);
+
DEFUN(cfg_ms_sup_min_rxlev, cfg_ms_sup_min_rxlev_cmd, "min-rxlev <-110--47>",
"Set the minimum receive level to select a cell\n"
"Minimum receive level from -110 dBm to -47 dBm")
@@ -2541,6 +2692,9 @@ int ms_vty_init(void)
install_element(ENABLE_NODE, &call_cmd);
install_element(ENABLE_NODE, &call_retr_cmd);
install_element(ENABLE_NODE, &call_dtmf_cmd);
+ install_element(ENABLE_NODE, &call_params_data_type_cmd);
+ install_element(ENABLE_NODE, &call_params_data_rate_cmd);
+ install_element(ENABLE_NODE, &call_params_data_ce_cmd);
install_element(ENABLE_NODE, &sms_cmd);
install_element(ENABLE_NODE, &service_cmd);
install_element(ENABLE_NODE, &vgcs_enter_cmd);
@@ -2665,6 +2819,18 @@ int ms_vty_init(void)
install_element(SUPPORT_NODE, &cfg_ms_sup_di_half_v1_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_en_half_v3_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_di_half_v3_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f144_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f144_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f96_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f96_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f48_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f48_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_h48_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_h48_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f24_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f24_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_h24_cmd);
+ install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_h24_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_min_rxlev_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_dsc_max_cmd);
install_element(SUPPORT_NODE, &cfg_ms_sup_skip_max_per_band_cmd);