From 7335cee638ad561c73d9df6ddff9c27c9f58fae3 Mon Sep 17 00:00:00 2001 From: Edmunt Pienkowsky Date: Tue, 25 Jul 2023 08:49:10 +0200 Subject: [PATCH] Add dsci configuration option Some Quectel modules (EP06) do not have `ccinfo` notifications. You may switch back to (less efficient) `dsci` notifications by `dsci=on` configuration switch. --- README.md | 6 +++++- etc/quectel.conf | 2 ++ quectel.conf | 2 ++ src/at_command.c | 11 ++++++++-- src/at_command.h | 2 +- src/at_parse.c | 4 ---- src/at_parse.h | 2 -- src/at_response.c | 54 +++++++++++++++++++++++++---------------------- src/cli.c | 1 + src/dc_config.c | 44 ++++++++++++++++++++++++++------------ src/dc_config.h | 1 + uac/quectel.conf | 2 ++ 12 files changed, 83 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index cabe0e22..f37fa7b6 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,11 @@ Supported modules: * `UCS-2` encoding is mandatory now. * Hanging-up calls using `AT+QHUP` command with specific *release cause*. -* Handling calls is based on `ccinfo` notifications (see `AT+QINDCFG="ccinfo"` command) instead of `DSCI` (`AT^DSCI` command) call status notifications. +* Call handling is based on automatic call status contifications. + + * For *Quectel* modules `ccinfo` (`AT+QINDCFG="ccinfo"` command) notifications are used by default.\ + You can switch back to (less efficient) `dsci` (`AT^DSCI` command) notifications by `dcsi` (on/**off**) configuration switch. + * For *SimCOM* modules `clcc` (`AT+CLCC=1` command) notifications are used. * Improved/extended *AT* commands response handler. Changes required to properly handle `AT+CMGL` command response. diff --git a/etc/quectel.conf b/etc/quectel.conf index 8309ea95..4c0722b8 100644 --- a/etc/quectel.conf +++ b/etc/quectel.conf @@ -42,6 +42,8 @@ initstate=start ; specified initial state of device, must be one of 'stop' 's ;msg_storage=auto ; auto,sm,me,mt,sr ;msg_direct=none ; none,on,off +;dsci=off ; on,off + ; quectel required settings [quectel0] audio=/dev/ttyUSB1 ; tty port for Audio, set as ttyUSB4 for SimCOM if no other dev present diff --git a/quectel.conf b/quectel.conf index e03d97cc..3d5a5302 100644 --- a/quectel.conf +++ b/quectel.conf @@ -42,6 +42,8 @@ initstate=start ; specified initial state of device, must be one of 'stop' 's ;msg_storage=auto ; auto,sm,me,mt,sr ;msg_direct=none ; none,on,off +;dsci=off ; on,off + ; quectel required settings [quectel0] audio=/dev/ttyUSB1 ; tty port for Audio, set as ttyUSB4 for Simcom if no other dev present diff --git a/src/at_command.c b/src/at_command.c index 66d4d42c..e5dd47f9 100644 --- a/src/at_command.c +++ b/src/at_command.c @@ -278,11 +278,13 @@ int at_enqueue_initialization(struct cpvt *cpvt) return 0; } -int at_enqueue_initialization_quectel(struct cpvt *cpvt) +int at_enqueue_initialization_quectel(struct cpvt *cpvt, unsigned int dsci) { static const char cmd_qpcmv[] = "AT+QPCMV?\r"; static const char cmd_at_qindcfg_cc[] = "AT+QINDCFG=\"ccinfo\",1,0\r"; + static const char cmd_at_dsci[] = "AT^DSCI=1\r"; + static const char cmd_at_qindcfg_csq[] = "AT+QINDCFG=\"csq\",1,0\r"; static const char cmd_at_qindcfg_act[] = "AT+QINDCFG=\"act\",1,0\r"; static const char cmd_at_qindcfg_ring[] = "AT+QINDCFG=\"ring\",0,0\r"; @@ -292,6 +294,11 @@ int at_enqueue_initialization_quectel(struct cpvt *cpvt) static const char cmd_at_qccid[] = "AT+QCCID\r"; + static const at_queue_cmd_t ccinfo_cmds[] = { + ATQ_CMD_DECLARE_ST(CMD_AT_QINDCFG_CC, cmd_at_qindcfg_cc), + ATQ_CMD_DECLARE_ST(CMD_AT_DSCI, cmd_at_dsci) + }; + static const at_queue_cmd_t tonedet_cmds[] = { ATQ_CMD_DECLARE_STI(CMD_AT_QTONEDET_0, cmd_at_qtonedet_0), ATQ_CMD_DECLARE_STI(CMD_AT_QTONEDET_1, cmd_at_qtonedet_1), @@ -306,7 +313,7 @@ int at_enqueue_initialization_quectel(struct cpvt *cpvt) ATQ_CMD_DECLARE_STI(CMD_AT_QCCID, cmd_at_qccid), ATQ_CMD_DECLARE_STI(CMD_AT_QTONEDET_1, cmd_at_qtonedet_1), ATQ_CMD_DECLARE_ST(CMD_AT_CVOICE, cmd_qpcmv), /* read the current voice mode, and return sampling rate、data bit、frame period */ - ATQ_CMD_DECLARE_ST(CMD_AT_QINDCFG_CC, cmd_at_qindcfg_cc), + ccinfo_cmds[dsci? 1:0], ATQ_CMD_DECLARE_ST(CMD_AT_QINDCFG_CSQ, cmd_at_qindcfg_csq), ATQ_CMD_DECLARE_ST(CMD_AT_QINDCFG_ACT, cmd_at_qindcfg_act), ATQ_CMD_DECLARE_ST(CMD_AT_QINDCFG_RING, cmd_at_qindcfg_ring), diff --git a/src/at_command.h b/src/at_command.h index cba23f94..9609f21e 100644 --- a/src/at_command.h +++ b/src/at_command.h @@ -138,7 +138,7 @@ struct cpvt; const char *at_cmd2str(at_cmd_t cmd); int at_enqueue_at(struct cpvt* cpvt); int at_enqueue_initialization(struct cpvt *cpvt); -int at_enqueue_initialization_quectel(struct cpvt*); +int at_enqueue_initialization_quectel(struct cpvt*, unsigned int); int at_enqueue_initialization_simcom(struct cpvt*); int at_enqueue_initialization_other(struct cpvt*); int at_enqueue_ping(struct cpvt *cpvt); diff --git a/src/at_parse.c b/src/at_parse.c index 5a783eb1..a5831675 100644 --- a/src/at_parse.c +++ b/src/at_parse.c @@ -1052,8 +1052,6 @@ int at_parse_csca(char* str, char ** csca) return 0; } -#ifdef HANDLE_DSCI - #/* */ int at_parse_dsci(char* str, unsigned* call_idx, unsigned* dir, unsigned* state, unsigned* call_type, char** number, unsigned* toa) { @@ -1088,8 +1086,6 @@ int at_parse_dsci(char* str, unsigned* call_idx, unsigned* dir, unsigned* state, return -1; } -#endif - #/* */ int at_parse_clcc(char* str, unsigned* call_idx, unsigned* dir, unsigned* state, unsigned* mode, unsigned* mpty, char** number, unsigned* toa) { diff --git a/src/at_parse.h b/src/at_parse.h index be31e741..7b4f7383 100644 --- a/src/at_parse.h +++ b/src/at_parse.h @@ -45,9 +45,7 @@ int at_parse_qind_csq(const char* params, int* rssi); int at_parse_qind_act(char* params, int* act); int at_parse_qind_cc(char* params, unsigned* call_idx, unsigned* dir, unsigned* state, unsigned* mode, unsigned* mpty, char** number, unsigned* toa); int at_parse_csca(char* str, char ** csca); -#ifdef HANDLE_DSCI int at_parse_dsci(char* str, unsigned* call_idx, unsigned* dir, unsigned* state, unsigned* call_type, char** number, unsigned* toa); -#endif int at_parse_clcc(char* str, unsigned* call_idx, unsigned* dir, unsigned* state, unsigned* mode, unsigned* mpty, char** number, unsigned* toa); int at_parse_ccwa(char* str, unsigned * class); int at_parse_qtonedet(const char* str, int* dtmf); diff --git a/src/at_response.c b/src/at_response.c index 40642a57..ce4ed6de 100644 --- a/src/at_response.c +++ b/src/at_response.c @@ -878,7 +878,7 @@ static void handle_clcc(struct pvt* pvt, { struct cpvt* cpvt = pvt_find_cpvt(pvt, (int)call_idx); - if(cpvt) { + if (cpvt) { /* cpvt alive */ CPVT_SET_FLAGS(cpvt, CALL_FLAG_ALIVE); @@ -887,20 +887,23 @@ static void handle_clcc(struct pvt* pvt, return; } - if (CONF_SHARED(pvt, multiparty)) { - if(mpty) - CPVT_SET_FLAGS(cpvt, CALL_FLAG_MULTIPARTY); - else - CPVT_RESET_FLAGS(cpvt, CALL_FLAG_MULTIPARTY); - } - else { - if (!CPVT_TEST_FLAG(cpvt, CALL_FLAG_MULTIPARTY) && mpty) { - ast_log(LOG_ERROR, "[%s] Rejecting multiparty call - idx:%d\n", PVT_ID(pvt), call_idx); - at_enqueue_hangup(&pvt->sys_chan, call_idx, AST_CAUSE_CALL_REJECTED); + // mpty == 2u : ignore this flag + if (mpty != 2u) { + if (CONF_SHARED(pvt, multiparty)) { + if (mpty) + CPVT_SET_FLAGS(cpvt, CALL_FLAG_MULTIPARTY); + else + CPVT_RESET_FLAGS(cpvt, CALL_FLAG_MULTIPARTY); + } + else { + if (!CPVT_TEST_FLAG(cpvt, CALL_FLAG_MULTIPARTY) && mpty) { + ast_log(LOG_ERROR, "[%s] Rejecting multiparty call - idx:%d\n", PVT_ID(pvt), call_idx); + at_enqueue_hangup(&pvt->sys_chan, call_idx, AST_CAUSE_CALL_REJECTED); + } } } - if(state != cpvt->state) { + if (state != cpvt->state) { change_channel_state(cpvt, state, 0); } else { @@ -908,14 +911,16 @@ static void handle_clcc(struct pvt* pvt, } } else { - switch (state) - { + switch (state) { case CALL_STATE_DIALING: case CALL_STATE_ALERTING: cpvt = last_initialized_cpvt(pvt); - if (!CONF_SHARED(pvt, multiparty)) { - if (!CPVT_TEST_FLAG(cpvt, CALL_FLAG_MULTIPARTY) && mpty) { - cpvt = NULL; + // mpty == 2u : ignore this flag + if (mpty != 2u) { + if (!CONF_SHARED(pvt, multiparty)) { + if (!CPVT_TEST_FLAG(cpvt, CALL_FLAG_MULTIPARTY) && mpty) { + cpvt = NULL; + } } } @@ -1090,7 +1095,6 @@ static int at_response_clcc(struct pvt* pvt, char* str) return 0; } -#ifdef HANDLE_DSCI /*! * \brief Handle ^DSCI response * \param pvt -- pvt structure @@ -1125,13 +1129,17 @@ static int at_response_dsci(struct pvt* pvt, char* str) break; default: // request CLCC anyway - request_clcc(pvt); + if (CONF_SHARED(pvt, multiparty)) { + request_clcc(pvt); + } + else { + handle_clcc(pvt, call_index, call_dir, call_state, call_type, 2u, number, number_type); + } break; } return 0; } -#endif /*! * \brief Handle +QIND response. @@ -2050,7 +2058,7 @@ static int at_response_cgmi(struct pvt* pvt, const struct ast_str* const respons ast_verb(1, "[%s] Quectel module\n", PVT_ID(pvt)); pvt->is_simcom = 0; pvt->has_voice = 0; - return at_enqueue_initialization_quectel(&pvt->sys_chan); + return at_enqueue_initialization_quectel(&pvt->sys_chan, CONF_SHARED(pvt, dsci)); } else if (!strncasecmp(ast_str_buffer(response), MANUFACTURER_SIMCOM, STRLEN(MANUFACTURER_SIMCOM))) { ast_verb(1, "[%s] SimCOM module\n", PVT_ID(pvt)); @@ -2644,11 +2652,7 @@ int at_response(struct pvt* pvt, const struct ast_str* const result, at_res_t at return 0; case RES_DSCI: -#ifdef HANDLE_DSCI return at_response_dsci(pvt, str); -#else - return 0; -#endif case RES_CEND: #ifdef HANDLE_CEND diff --git a/src/cli.c b/src/cli.c index 26ac6dd0..385c01d9 100644 --- a/src/cli.c +++ b/src/cli.c @@ -157,6 +157,7 @@ static char* cli_show_device_settings (struct ast_cli_entry* e, int cmd, struct ast_cli (a->fd, " Group : %d\n", CONF_SHARED(pvt, group)); ast_cli (a->fd, " RX gain : %d\n", CONF_SHARED(pvt, rxgain)); ast_cli (a->fd, " TX gain : %d\n", CONF_SHARED(pvt, txgain)); + ast_cli (a->fd, " Use ^DSCI notifications : %s\n", AST_CLI_YESNO(CONF_SHARED(pvt, dsci))); ast_cli (a->fd, " Use CallingPres : %s\n", AST_CLI_YESNO(CONF_SHARED(pvt, usecallingpres))); ast_cli (a->fd, " Default CallingPres : %s\n", CONF_SHARED(pvt, callingpres) < 0 ? "" : ast_describe_caller_presentation (CONF_SHARED(pvt, callingpres))); ast_cli (a->fd, " Disable SMS : %s\n", AST_CLI_YESNO(CONF_SHARED(pvt, disablesms))); diff --git a/src/dc_config.c b/src/dc_config.c index e04fadaa..ebcff19a 100644 --- a/src/dc_config.c +++ b/src/dc_config.c @@ -105,6 +105,21 @@ const char* dc_3stbool2str_capitalized(int v) return enum2str_def(b, strs, ITEMS_OF(strs), "None"); } +static unsigned int parse_on_off(const char* const name, const char* const value, unsigned int defval) { + if (!value) { + return defval; + } + if (ast_true(value)) { + return 1u; + } + if (ast_false(value)) { + return 0u; + } + + ast_log(LOG_ERROR, "Invalid value '%s' for configuration option '%s'\n", value, name); + return defval; +} + static const char* const msgstor_strs[] = { "AUTO", "SM", @@ -166,14 +181,13 @@ static int dc_uconfig_fill(struct ast_config * cfg, const char * cat, struct dc_ } if (slin16_str) { - slin16 = ast_true(slin16_str); + slin16 = parse_on_off("slin16", slin16_str, 0u); } else { slin16 = 0; } - if(!data_tty && !imei && !imsi) - { + if (!data_tty && !imei && !imsi) { ast_log (LOG_ERROR, "Skipping device %s. Missing required data_tty setting\n", cat); return 1; } @@ -265,19 +279,20 @@ void dc_sconfig_fill(struct ast_config * cfg, const char * cat, struct dc_sconfi } } else if (!strcasecmp (v->name, "usecallingpres")) { - config->usecallingpres = ast_true (v->value); /* usecallingpres is set to 0 if invalid */ + config->usecallingpres = parse_on_off(v->name, v->value, 0u); /* usecallingpres is set to 0 if invalid */ } else if (!strcasecmp (v->name, "autodeletesms")) { - config->autodeletesms = ast_true (v->value); /* autodeletesms is set to 0 if invalid */ + config->autodeletesms = parse_on_off(v->name, v->value, 0u); /* autodeletesms is set to 0 if invalid */ } else if (!strcasecmp (v->name, "resetquectel")) { - config->resetquectel = ast_true (v->value); /* resetquectel is set to 0 if invalid */ + config->resetquectel = parse_on_off(v->name, v->value, 0u); /* resetquectel is set to 0 if invalid */ } else if (!strcasecmp (v->name, "disablesms")) { - config->disablesms = ast_true (v->value); /* disablesms is set to 0 if invalid */ + config->disablesms = parse_on_off(v->name, v->value, 0u); /* disablesms is set to 0 if invalid */ } else if (!strcasecmp (v->name, "disable")) { - config->initstate = ast_true (v->value) ? DEV_STATE_REMOVED : DEV_STATE_STARTED; + const unsigned int is = parse_on_off(v->name, v->value, 0u); + config->initstate = is ? DEV_STATE_REMOVED : DEV_STATE_STARTED; } else if (!strcasecmp (v->name, "initstate")) { const dev_state_t val = str2dev_state(v->value); @@ -288,13 +303,13 @@ void dc_sconfig_fill(struct ast_config * cfg, const char * cat, struct dc_sconfi } else if (!strcasecmp (v->name, "callwaiting")) { if(strcasecmp(v->value, "auto")) - config->callwaiting = ast_true (v->value); + config->callwaiting = parse_on_off(v->name, v->value, 0u); } else if (!strcasecmp (v->name, "multiparty")) { - config->multiparty = ast_true(v->value); + config->multiparty = parse_on_off(v->name, v->value, 0u); } else if (!strcasecmp (v->name, "dtmf")) { - config->dtmf = ast_true(v->value); + config->dtmf = parse_on_off(v->name, v->value, 0u); } else if (!strcasecmp (v->name, "dtmf_duration")) { config->dtmf_duration = strtol(v->value, (char**) NULL, 10); @@ -303,10 +318,13 @@ void dc_sconfig_fill(struct ast_config * cfg, const char * cat, struct dc_sconfi } } else if (!strcasecmp (v->name, "moh")) { - config->moh = ast_true(v->value); + config->moh = parse_on_off(v->name, v->value, 0u); } else if (!strcasecmp (v->name, "query_time")) { - config->query_time = ast_true(v->value); + config->query_time = parse_on_off(v->name, v->value, 0u); + } + else if (!strcasecmp (v->name, "dsci")) { + config->dsci = parse_on_off(v->name, v->value, 0u); } else if (!strcasecmp (v->name, "msg_direct")) { config->msg_direct = dc_str23stbool(v->value); diff --git a/src/dc_config.h b/src/dc_config.h index 7f43c9f0..87a8befd 100644 --- a/src/dc_config.h +++ b/src/dc_config.h @@ -93,6 +93,7 @@ typedef struct dc_sconfig unsigned int dtmf:1; /*! 0 */ unsigned int moh:1; /*! 0 */ unsigned int query_time:1; /*! 0 */ + unsigned int dsci:1; /*!< use ^DSCI call state notifications */ long dtmf_duration; /*! duration of DTMF in miliseconds */ dev_state_t initstate; /*! DEV_STATE_STARTED */ diff --git a/uac/quectel.conf b/uac/quectel.conf index 590b9ff9..0517b1af 100644 --- a/uac/quectel.conf +++ b/uac/quectel.conf @@ -42,6 +42,8 @@ initstate=start ; specified initial state of device, must be one of 'stop' 's ;msg_storage=auto ; auto,sm,me,mt,sr ;msg_direct=none ; none,on,off +;dsci=off ; on,off + ; quectel required settings [quectel0] data=/dev/ttyUSB2 ; tty port for AT commands; no default value