From 306a3a6f19546b7b9b6b8df973d75710ce21bc3c Mon Sep 17 00:00:00 2001 From: Joey Hewitt Date: Mon, 4 Sep 2017 23:51:07 -0700 Subject: [PATCH] qmimodem: implement DTMF The TLVs are documented in GobiAPI. I pass 0xff for the call ID, as the stock RIL appears to always do. I would guess it means "current foreground call." The call ID is returned in TLV 0x10, but I didn't implement parsing of that. Acked-by: Alexey Andreyev diff --git a/drivers/qmimodem/voice_generated.c b/drivers/qmimodem/voice_generated.c index aef79c4..b579043 100644 --- a/drivers/qmimodem/voice_generated.c +++ b/drivers/qmimodem/voice_generated.c @@ -216,3 +216,72 @@ enum parse_error qmi_voice_call_status( return err; } + +int qmi_voice_start_cont_dtmf( + struct qmi_voice_start_cont_dtmf_arg *arg, + struct qmi_service *service, + qmi_result_func_t func, + void *user_data, + qmi_destroy_func_t destroy) +{ + struct qmi_param *param = NULL; + uint8_t param_body[2]; + + param = qmi_param_new(); + if (!param) + goto error; + + param_body[0] = arg->call_id; + param_body[1] = arg->dtmf_char; + + if (!qmi_param_append( + param, + 0x1, + sizeof(param_body), + param_body)) + goto error; + + if (qmi_service_send(service, + 0x29, + param, + func, + user_data, + destroy) > 0) + return 0; + +error: + g_free(param); + return 1; +} + +int qmi_voice_stop_cont_dtmf( + struct qmi_voice_stop_cont_dtmf_arg *arg, + struct qmi_service *service, + qmi_result_func_t func, + void *user_data, + qmi_destroy_func_t destroy) +{ + struct qmi_param *param = NULL; + + param = qmi_param_new(); + if (!param) + goto error; + + if (!qmi_param_append_uint8( + param, + 0x1, + arg->call_id)) + goto error; + + if (qmi_service_send(service, + 0x2a, + param, + func, + user_data, + destroy) > 0) + return 0; + +error: + g_free(param); + return 1; +} \ No newline at end of file diff --git a/drivers/qmimodem/voice_generated.h b/drivers/qmimodem/voice_generated.h index dc238ef..c627fe1 100644 --- a/drivers/qmimodem/voice_generated.h +++ b/drivers/qmimodem/voice_generated.h @@ -110,4 +110,27 @@ enum parse_error qmi_voice_call_status( struct qmi_result *qmi_result, struct qmi_voice_all_call_status_ind *result); +struct qmi_voice_start_cont_dtmf_arg { + uint8_t call_id; + uint8_t dtmf_char; +}; + +int qmi_voice_start_cont_dtmf( + struct qmi_voice_start_cont_dtmf_arg *arg, + struct qmi_service *service, + qmi_result_func_t func, + void *user_data, + qmi_destroy_func_t destroy); + +struct qmi_voice_stop_cont_dtmf_arg { + uint8_t call_id; +}; + +int qmi_voice_stop_cont_dtmf( + struct qmi_voice_stop_cont_dtmf_arg *arg, + struct qmi_service *service, + qmi_result_func_t func, + void *user_data, + qmi_destroy_func_t destroy); + #endif /* __OFONO_QMI_VOICE_GENERATED_H */ diff --git a/drivers/qmimodem/voicecall.c b/drivers/qmimodem/voicecall.c index bc8ac2b..5d8d35f 100644 --- a/drivers/qmimodem/voicecall.c +++ b/drivers/qmimodem/voicecall.c @@ -413,6 +413,110 @@ static void hangup_active(struct ofono_voicecall *vc, 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; + + if (qmi_result_set_error(result, &error)) { + DBG("QMI Error %d", error); + CALLBACK_WITH_FAILURE(cb, cbd->data); + return; + } + + CALLBACK_WITH_SUCCESS(cb, cbd->data); +} + +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); + struct qmi_voice_stop_cont_dtmf_arg arg; + uint16_t error; + + if (qmi_result_set_error(result, &error)) { + DBG("QMI Error %d", error); + CALLBACK_WITH_FAILURE(cb, cbd->data); + return; + } + + arg.call_id = 0xff; + + if (!qmi_voice_stop_cont_dtmf(&arg, + vd->voice, + stop_cont_dtmf_cb, + cbd, + g_free)) + return; + + CALLBACK_WITH_FAILURE(cb, cbd->data); +} + +static void send_one_dtmf(struct ofono_voicecall *vc, const char dtmf, + ofono_voicecall_cb_t cb, void *data) { + struct qmi_voice_start_cont_dtmf_arg arg; + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + + arg.call_id = 0xff; + arg.dtmf_char = (uint8_t) dtmf; + + struct cb_data *cbd = cb_data_new(cb, data); + cbd->user = vc; + + if (!qmi_voice_start_cont_dtmf(&arg, + vd->voice, + start_cont_dtmf_cb, + cbd, + NULL)) + return; + + CALLBACK_WITH_FAILURE(cb, data); + g_free(cbd); +} + +struct send_one_dtmf_cb_data { + const char *full_dtmf; + const char *next_dtmf; + struct ofono_voicecall *vc; +}; + +static void send_one_dtmf_cb(const struct ofono_error *error, void *data) { + struct cb_data *cbd = data; + ofono_voicecall_cb_t cb = cbd->cb; + struct send_one_dtmf_cb_data *send_one_dtmf_cb_data = cbd->user; + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR || *send_one_dtmf_cb_data->next_dtmf == 0) { + if (error->type == OFONO_ERROR_TYPE_NO_ERROR) { + CALLBACK_WITH_SUCCESS(cb, cbd->data); + } else { + CALLBACK_WITH_FAILURE(cb, cbd->data); + } + g_free((gpointer)send_one_dtmf_cb_data->full_dtmf); + g_free(send_one_dtmf_cb_data); + g_free(cbd); + } else { + send_one_dtmf(send_one_dtmf_cb_data->vc, *(send_one_dtmf_cb_data->next_dtmf++), send_one_dtmf_cb, data); + } +} + +static void send_dtmf(struct ofono_voicecall *vc, const char *dtmf, + ofono_voicecall_cb_t cb, void *data) +{ + struct cb_data *cbd = cb_data_new(cb, data); + struct send_one_dtmf_cb_data *send_one_dtmf_cb_data = g_new(struct send_one_dtmf_cb_data, 1); + + send_one_dtmf_cb_data->full_dtmf = g_strdup(dtmf); + send_one_dtmf_cb_data->next_dtmf = &send_one_dtmf_cb_data->full_dtmf[1]; + send_one_dtmf_cb_data->vc = vc; + cbd->user = send_one_dtmf_cb_data; + + send_one_dtmf(vc, *dtmf, send_one_dtmf_cb, cbd); +} + static const struct ofono_voicecall_driver driver = { .name = "qmimodem", .probe = qmi_voicecall_probe, @@ -421,6 +525,7 @@ static const struct ofono_voicecall_driver driver = { .answer = answer, .hangup_active = hangup_active, .release_specific = release_specific, + .send_tones = send_dtmf, }; void qmi_voicecall_init(void)