diff --git a/src/host/layer23/include/osmocom/bb/common/settings.h b/src/host/layer23/include/osmocom/bb/common/settings.h index 3b9473f96..468e8cd10 100644 --- a/src/host/layer23/include/osmocom/bb/common/settings.h +++ b/src/host/layer23/include/osmocom/bb/common/settings.h @@ -1,6 +1,8 @@ #ifndef _settings_h #define _settings_h +#include + #include #include #include @@ -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 { diff --git a/src/host/layer23/include/osmocom/bb/common/support.h b/src/host/layer23/include/osmocom/bb/common/support.h index 2fae57ec4..b0c71f5ab 100644 --- a/src/host/layer23/include/osmocom/bb/common/support.h +++ b/src/host/layer23/include/osmocom/bb/common/support.h @@ -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; diff --git a/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h b/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h index 4006688bb..05c539b0f 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h +++ b/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h @@ -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); diff --git a/src/host/layer23/src/common/settings.c b/src/host/layer23/src/common/settings.c index e79a6a148..5bae325ba 100644 --- a/src/host/layer23/src/common/settings.c +++ b/src/host/layer23/src/common/settings.c @@ -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; } diff --git a/src/host/layer23/src/common/support.c b/src/host/layer23/src/common/support.c index 2f93016cd..ed5146515 100644 --- a/src/host/layer23/src/common/support.c +++ b/src/host/layer23/src/common/support.c @@ -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); } diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c index b8980907d..a9dfec761 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 d23fbb70b..84271917f 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 aaf01bdae..e79a5a147 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);