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
This commit is contained in:
Vadim Yanitskiy 2023-10-11 05:14:19 +07:00
parent 011308cbcc
commit e73a604de0
8 changed files with 516 additions and 14 deletions

View File

@ -1,6 +1,8 @@
#ifndef _settings_h
#define _settings_h
#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
@ -82,6 +84,35 @@ struct test_sim_settings {
} locigprs;
};
enum data_call_type {
DATA_CALL_TYPE_ISDN,
DATA_CALL_TYPE_ANALOG,
};
enum data_call_rate {
DATA_CALL_RATE_V110_300,
DATA_CALL_RATE_V110_1200,
DATA_CALL_RATE_V110_2400,
DATA_CALL_RATE_V110_4800,
DATA_CALL_RATE_V110_9600,
DATA_CALL_RATE_V110_14400,
};
/* Connection Element (transparency) */
enum data_call_ce {
DATA_CALL_CE_TRANSP,
DATA_CALL_CE_TRANSP_PREF,
DATA_CALL_CE_NON_TRANSP,
DATA_CALL_CE_NON_TRANSP_PREF,
};
/* Data (CSD) call parameters */
struct data_call_params {
enum data_call_type type;
enum data_call_rate rate;
enum data_call_ce ce;
};
struct gsm_settings {
char layer2_socket_path[128];
char sap_socket_path[128];
@ -161,6 +192,14 @@ struct gsm_settings {
uint8_t ch_cap; /* channel capability */
int8_t min_rxlev_dbm; /* min dBm to access */
/* CSD modes */
bool csd_tch_f144;
bool csd_tch_f96;
bool csd_tch_f48;
bool csd_tch_h48;
bool csd_tch_f24;
bool csd_tch_h24;
/* support for ASCI */
bool vgcs; /* support of VGCS */
bool vbs; /* support of VBS */
@ -192,6 +231,11 @@ struct gsm_settings {
/* ASCI settings */
bool uplink_release_local;
bool asci_allow_any;
/* call parameters */
struct {
struct data_call_params data;
} call_params;
};
struct gsm_settings_abbrev {

View File

@ -91,6 +91,14 @@ struct gsm_support {
uint8_t half_v1;
uint8_t half_v3;
/* CSD modes */
uint8_t csd_tch_f144;
uint8_t csd_tch_f96;
uint8_t csd_tch_f48;
uint8_t csd_tch_h48;
uint8_t csd_tch_f24;
uint8_t csd_tch_h24;
/* EDGE / UMTS / CDMA */
uint8_t edge_ms_sup;
uint8_t edge_psk_sup;

View File

@ -7,12 +7,20 @@
struct osmocom_ms;
enum gsm_call_type {
GSM_CALL_T_UNKNOWN = 0,
GSM_CALL_T_VOICE,
GSM_CALL_T_DATA, /* UDI or 3.1 kHz audio */
GSM_CALL_T_DATA_FAX,
};
struct gsm_call {
struct llist_head entry;
struct osmocom_ms *ms;
uint32_t callref;
enum gsm_call_type type;
bool init; /* call initiated, no response yet */
bool hold; /* call on hold */
@ -24,7 +32,8 @@ struct gsm_call {
char dtmf[32]; /* dtmf sequence */
};
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);
int mncc_hangup(struct osmocom_ms *ms);
int mncc_answer(struct osmocom_ms *ms);
int mncc_hold(struct osmocom_ms *ms);

View File

@ -100,6 +100,14 @@ int gsm_settings_init(struct osmocom_ms *ms)
set->ch_cap = sup->ch_cap;
set->min_rxlev_dbm = sup->min_rxlev_dbm;
set->dsc_max = sup->dsc_max;
set->csd_tch_f144 = sup->csd_tch_f144;
set->csd_tch_f96 = sup->csd_tch_f96;
set->csd_tch_f48 = sup->csd_tch_f48;
set->csd_tch_h48 = sup->csd_tch_h48;
set->csd_tch_f24 = sup->csd_tch_f24;
set->csd_tch_h24 = sup->csd_tch_h24;
set->vgcs = sup->vgcs;
set->vbs = sup->vbs;
@ -118,6 +126,12 @@ int gsm_settings_init(struct osmocom_ms *ms)
set->uplink_release_local = true;
set->call_params.data = (struct data_call_params) {
.type = DATA_CALL_TYPE_ISDN,
.rate = DATA_CALL_RATE_V110_9600,
.ce = DATA_CALL_CE_TRANSP,
};
return 0;
}

View File

@ -97,6 +97,14 @@ void gsm_support_init(struct osmocom_ms *ms)
sup->full_v3 = 0;
sup->half_v1 = 1;
sup->half_v3 = 0;
/* CSD modes */
sup->csd_tch_f144 = 1;
sup->csd_tch_f96 = 1;
sup->csd_tch_f48 = 1;
sup->csd_tch_h48 = 1;
sup->csd_tch_f24 = 1;
sup->csd_tch_h24 = 1;
}
/* (3.2.1) maximum channels to scan within each band */
@ -173,6 +181,14 @@ void gsm_support_dump(struct osmocom_ms *ms,
print(priv, " Full-Rate V3 : %s\n", SUP_SET(full_v3));
print(priv, " Half-Rate V1 : %s\n", SUP_SET(half_v1));
print(priv, " Half-Rate V3 : %s\n", SUP_SET(half_v3));
print(priv, " CSD TCH/F14.4: %s\n", SUP_SET(csd_tch_f144));
print(priv, " CSD TCH/F9.6 : %s\n", SUP_SET(csd_tch_f96));
print(priv, " CSD TCH/F4.8 : %s\n", SUP_SET(csd_tch_f48));
print(priv, " CSD TCH/H4.8 : %s\n", SUP_SET(csd_tch_h48));
print(priv, " CSD TCH/F2.4 : %s\n", SUP_SET(csd_tch_f24));
print(priv, " CSD TCH/H2.4 : %s\n", SUP_SET(csd_tch_h24));
print(priv, " Min RXLEV : %d\n", set->min_rxlev_dbm);
}

View File

@ -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;

View File

@ -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)

View File

@ -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);