diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c index 0d6819ded..e799b3234 100644 --- a/spa/plugins/bluez5/backend-native.c +++ b/spa/plugins/bluez5/backend-native.c @@ -1119,6 +1119,21 @@ next_indicator: rfcomm_send_error(rfcomm, error); return true; } + } else if (spa_strstartswith(buf, "AT+VTS=")) { + char *dtmf; + enum cmee_error error; + + dtmf = calloc(1, 2); + if (sscanf(buf, "AT+VTS=%1s", dtmf) != 1) { + spa_log_debug(backend->log, "Failed to parse AT+VTS: \"%s\"", buf); + rfcomm_send_error(rfcomm, CMEE_AG_FAILURE); + return true; + } + + if (!mm_send_dtmf(backend->modemmanager, dtmf, rfcomm, &error)) { + rfcomm_send_error(rfcomm, error); + return true; + } } else { return false; } diff --git a/spa/plugins/bluez5/modemmanager.c b/spa/plugins/bluez5/modemmanager.c index ea3203a1b..443534b4f 100644 --- a/spa/plugins/bluez5/modemmanager.c +++ b/spa/plugins/bluez5/modemmanager.c @@ -1040,6 +1040,66 @@ bool mm_do_call(void *modemmanager, const char* number, void *user_data, enum cm return true; } +bool mm_send_dtmf(void *modemmanager, const char *dtmf, void *user_data, enum cmee_error *error) +{ + struct impl *this = modemmanager; + struct call *call_object, *call_tmp; + struct dbus_cmd_data *data; + DBusMessage *m; + + call_object = NULL; + spa_list_for_each(call_tmp, &this->call_list, link) { + if (call_tmp->state == CLCC_ACTIVE) { + call_object = call_tmp; + break; + } + } + if (!call_object) { + spa_log_debug(this->log, "No active call"); + if (error) + *error = CMEE_OPERATION_NOT_ALLOWED; + return false; + } + + /* Allowed dtmf characters: 0-9, *, #, A-D */ + if (!((dtmf[0] >= '0' && dtmf[0] <= '9') + || (dtmf[0] == '*') + || (dtmf[0] == '#') + || (dtmf[0] >= 'A' && dtmf[0] <= 'D'))) { + spa_log_debug(this->log, "Invalid DTMF character: %s", dtmf); + if (error) + *error = CMEE_INVALID_CHARACTERS_TEXT_STRING; + return false; + } + + data = malloc(sizeof(struct dbus_cmd_data)); + if (!data) { + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + data->this = this; + data->call = call_object; + data->user_data = user_data; + + m = dbus_message_new_method_call(MM_DBUS_SERVICE, call_object->path, MM_DBUS_INTERFACE_CALL, MM_CALL_METHOD_SENDDTMF); + if (m == NULL) { + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + dbus_message_append_args(m, DBUS_TYPE_STRING, &dtmf, DBUS_TYPE_INVALID); + if (!mm_dbus_connection_send_with_reply(this, m, &call_object->pending, mm_get_call_simple_reply, data)) { + spa_log_error(this->log, "dbus call failure"); + dbus_message_unref(m); + if (error) + *error = CMEE_AG_FAILURE; + return false; + } + + return true; +} + const char *mm_get_incoming_call_number(void *modemmanager) { struct impl *this = modemmanager; diff --git a/spa/plugins/bluez5/modemmanager.h b/spa/plugins/bluez5/modemmanager.h index bdc11092e..27c590f08 100644 --- a/spa/plugins/bluez5/modemmanager.h +++ b/spa/plugins/bluez5/modemmanager.h @@ -94,6 +94,7 @@ unsigned int mm_supported_features(); bool mm_answer_call(void *modemmanager, void *user_data, enum cmee_error *error); bool mm_hangup_call(void *modemmanager, void *user_data, enum cmee_error *error); bool mm_do_call(void *modemmanager, const char* number, void *user_data, enum cmee_error *error); +bool mm_send_dtmf(void *modemmanager, const char *dtmf, void *user_data, enum cmee_error *error); const char *mm_get_incoming_call_number(void *modemmanager); struct spa_list *mm_get_calls(void *modemmanager); #else @@ -137,6 +138,13 @@ bool mm_do_call(void *modemmanager, const char* number, void *user_data, enum cm return false; } +bool mm_send_dtmf(void *modemmanager, const char *dtmf, void *user_data, enum cmee_error *error) +{ + if (error) + *error = CMEE_OPERATION_NOT_SUPPORTED; + return false; +} + const char *mm_get_incoming_call_number(void *modemmanager) { return NULL;