diff options
author | Adam Pigg <adam@piggz.co.uk> | 2024-04-26 22:22:03 +0100 |
---|---|---|
committer | Denis Kenzior <denkenz@gmail.com> | 2024-04-29 15:00:46 -0500 |
commit | 2da0527477383561ca476d738096145a5dab74d1 (patch) | |
tree | 4d73adc32de147f3b7c9fb6c01695e446e6adb2f | |
parent | 8254e8ff15536e5e6f23601cde80dd0b574dd245 (diff) | |
download | ofono-2da0527477383561ca476d738096145a5dab74d1.tar.gz |
qmimodem: voicecall: Implement DTMF tones
The send_dtmf function sets up a call to send_one_dtmf, which will call
the QMI_VOICE_START_CONTINUOUS_DTMF service function. The parameters to
this call are a hard coded call-id and the DTMF character to send.
start_cont_dtmf_cb will then be called which will set up a call to
QMI_VOICE_STOP_CONTINUOUS_DTMF to stop the tone. Finally,
stop_cont_dtmf_cb will check the final status.
-rw-r--r-- | drivers/qmimodem/voice.h | 6 | ||||
-rw-r--r-- | drivers/qmimodem/voicecall.c | 127 |
2 files changed, 133 insertions, 0 deletions
diff --git a/drivers/qmimodem/voice.h b/drivers/qmimodem/voice.h index caedb079b..92186b72b 100644 --- a/drivers/qmimodem/voice.h +++ b/drivers/qmimodem/voice.h @@ -53,6 +53,8 @@ enum voice_commands { QMI_VOICE_GET_ALL_CALL_INFO = 0x2f, QMI_VOICE_END_CALL = 0x21, QMI_VOICE_ANSWER_CALL = 0x22, + QMI_VOICE_START_CONTINUOUS_DTMF = 0x29, + QMI_VOICE_STOP_CONTINUOUS_DTMF = 0x2A, QMI_VOICE_SUPS_NOTIFICATION_IND = 0x32, QMI_VOICE_SET_SUPS_SERVICE = 0x33, QMI_VOICE_GET_CALL_WAITING = 0x34, @@ -85,6 +87,10 @@ enum qmi_voice_call_state { QMI_VOICE_CALL_STATE_SETUP }; +enum qmi_voice_call_dtmf_param { + QMI_VOICE_DTMF_DATA = 0x01, +}; + struct qmi_ussd_data { uint8_t dcs; uint8_t length; diff --git a/drivers/qmimodem/voicecall.c b/drivers/qmimodem/voicecall.c index 7c9326fef..0df652263 100644 --- a/drivers/qmimodem/voicecall.c +++ b/drivers/qmimodem/voicecall.c @@ -42,6 +42,10 @@ struct voicecall_data { uint16_t minor; struct l_queue *call_list; struct ofono_phone_number dialed; + char *full_dtmf; + const char *next_dtmf; + ofono_voicecall_cb_t send_dtmf_cb; + struct cb_data *send_dtmf_data; }; struct qmi_voice_call_information_instance { @@ -599,6 +603,127 @@ static void hangup_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, release_specific(vc, call->id, cb, data); } +static void stop_cont_dtmf_cb(struct qmi_result *result, void *user_data) +{ + struct cb_data *cbd = user_data; + ofono_voicecall_cb_t cb = cbd->cb; + + uint16_t error; + + DBG(""); + + if (qmi_result_set_error(result, &error)) { + DBG("QMI Error %d", error); + CALLBACK_WITH_FAILURE(cb, cbd); + return; + } + + CALLBACK_WITH_SUCCESS(cb, cbd); +} + +static void start_cont_dtmf_cb(struct qmi_result *result, void *user_data) +{ + struct cb_data *cbd = user_data; + ofono_voicecall_cb_t cb = cbd->cb; + struct ofono_voicecall *vc = cbd->user; + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + uint16_t error; + struct qmi_param *param; + + DBG(""); + + if (qmi_result_set_error(result, &error)) { + DBG("QMI Error %d", error); + CALLBACK_WITH_FAILURE(cb, cbd); + return; + } + + param = qmi_param_new(); + + if (!qmi_param_append_uint8(param, QMI_VOICE_DTMF_DATA, 0xff)) + goto error; + + if (qmi_service_send(vd->voice, QMI_VOICE_STOP_CONTINUOUS_DTMF, param, + stop_cont_dtmf_cb, cbd, cb_data_unref) > 0) { + cb_data_ref(cbd); + return; + } + +error: + CALLBACK_WITH_FAILURE(cb, cbd->data); + l_free(param); +} + +static void send_one_dtmf(struct ofono_voicecall *vc, const char dtmf, + ofono_voicecall_cb_t cb, void *data) +{ + struct voicecall_data *vd = data; + struct qmi_param *param; + uint8_t param_body[2]; + struct cb_data *cbd = cb_data_new(cb, data); + + DBG(""); + + cbd->user = vc; + + param = qmi_param_new(); + + param_body[0] = 0xff; + param_body[1] = (uint8_t)dtmf; + + if (!qmi_param_append(param, QMI_VOICE_DTMF_DATA, sizeof(param_body), + param_body)) + goto error; + + if (qmi_service_send(vd->voice, QMI_VOICE_START_CONTINUOUS_DTMF, param, + start_cont_dtmf_cb, cbd, cb_data_unref) > 0) + return; + +error: + CALLBACK_WITH_FAILURE(cb, data); + l_free(param); +} + +static void send_one_dtmf_cb(const struct ofono_error *error, void *data) +{ + struct cb_data *cbd = data; + struct ofono_voicecall *vc = cbd->user; + struct voicecall_data *vd = cbd->data; + + DBG(""); + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR || + *vd->next_dtmf == 0) { + if (error->type == OFONO_ERROR_TYPE_NO_ERROR) + CALLBACK_WITH_SUCCESS(vd->send_dtmf_cb, vd->send_dtmf_data); + else + CALLBACK_WITH_FAILURE(vd->send_dtmf_cb, vd->send_dtmf_data); + + l_free(vd->full_dtmf); + vd->full_dtmf = NULL; + } else { + send_one_dtmf(vc, + *(vd->next_dtmf++), + send_one_dtmf_cb, vd); + } +} + +static void send_dtmf(struct ofono_voicecall *vc, const char *dtmf, + ofono_voicecall_cb_t cb, void *data) +{ + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + + DBG(""); + + l_free(vd->full_dtmf); + vd->full_dtmf = l_strdup(dtmf); + vd->next_dtmf = &vd->full_dtmf[1]; + vd->send_dtmf_data = data; + vd->send_dtmf_cb = cb; + + send_one_dtmf(vc, *dtmf, send_one_dtmf_cb, vd); +} + static void create_voice_cb(struct qmi_service *service, void *user_data) { struct ofono_voicecall *vc = user_data; @@ -656,6 +781,7 @@ static void qmi_voicecall_remove(struct ofono_voicecall *vc) qmi_service_free(data->voice); l_queue_destroy(data->call_list, l_free); + l_free(data->full_dtmf); l_free(data); } @@ -666,6 +792,7 @@ static const struct ofono_voicecall_driver driver = { .answer = answer, .hangup_active = hangup_active, .release_specific = release_specific, + .send_tones = send_dtmf, }; OFONO_ATOM_DRIVER_BUILTIN(voicecall, qmimodem, &driver) |