From e4b0f68e0b83e0c783d098d9c9bb84c4b5775b4b Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Fri, 25 Jul 2025 22:15:17 +0300 Subject: [PATCH] bluez5: telephony: implement asynchronous D-Bus calls This removes the need to call poll() on the rfcomm socket in order to wait for replies from the AG. Use a queue to buffer all the commands that are to be sent to the AG and match them to replies when they are received. Optionally associate each command with a DBusMessage that is assumed to be a method call from the telephony interface, which is then replied to when the rfcomm command reply is received. Also associate each command with a state, so that it is always deterministic what gets executed after the reply is received. On the telephony module, pass on the DBusMessage on the callbacks and add a method to allow the receiver to send a reply. Only send FAILED directly when the callback is not handled. Also, remove the return value from the Dial() command (it was not advertised on the introspection anyway) to make things easier. --- spa/plugins/bluez5/README-Telephony.md | 12 +- spa/plugins/bluez5/backend-native.c | 743 +++++++++---------------- spa/plugins/bluez5/telephony.c | 211 +++---- spa/plugins/bluez5/telephony.h | 31 +- 4 files changed, 369 insertions(+), 628 deletions(-) diff --git a/spa/plugins/bluez5/README-Telephony.md b/spa/plugins/bluez5/README-Telephony.md index 93910b057..994480af0 100644 --- a/spa/plugins/bluez5/README-Telephony.md +++ b/spa/plugins/bluez5/README-Telephony.md @@ -102,10 +102,10 @@ NOTE: This method is implemented only on the `org.ofono.VoiceCallManager` interface, for compatibility. Call announcements are normally made available via the standard `org.freedesktop.DBus.ObjectManager` interface. -`object Dial(string number)` +`void Dial(string number)` -Initiates a new outgoing call. Returns the object path to the newly created -call. +Initiates a new outgoing call. If this succeeds, the new call is announced via +the signals. The number must be a string containing the following characters: `[0-9+*#,ABCD]{1,80}` In other words, it must be a non-empty string consisting @@ -176,12 +176,12 @@ Possible Errors: * org.pipewire.Telephony.Error.InvalidState * org.freedesktop.DBus.Error.Failed -`array{object} CreateMultiparty()` +`void CreateMultiparty()` Joins active and held calls together into a multi-party call. If one of the calls is already a multi-party call, then the other call is added to the -multiparty conversation. Returns the new list of calls participating in the -multiparty call. +multiparty conversation. Changes to the call objects are announced via the +signals. There can only be one subscriber controlled multi-party call according to the GSM specification. diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c index 707c40be6..0abfe7dea 100644 --- a/spa/plugins/bluez5/backend-native.c +++ b/spa/plugins/bluez5/backend-native.c @@ -61,6 +61,8 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.native"); #define MAX_HF_INDICATORS 16 +#define RFCOMM_MESSAGE_MAX_LENGTH 256 + enum { HFP_AG_INITIAL_CODEC_SETUP_NONE = 0, HFP_AG_INITIAL_CODEC_SETUP_SEND, @@ -133,6 +135,7 @@ struct transport_data { }; enum hfp_hf_state { + hfp_hf_idle, hfp_hf_brsf, hfp_hf_bac, hfp_hf_cind1, @@ -145,8 +148,8 @@ enum hfp_hf_state { hfp_hf_nrec, hfp_hf_clcc, hfp_hf_vgs, - hfp_hf_done, - hfp_hf_clcc_update + hfp_hf_clcc_update, + hfp_hf_chld1_hangup }; enum hsp_hs_state { @@ -168,7 +171,9 @@ struct rfcomm_call_data { struct rfcomm_cmd { struct spa_list link; - char* cmd; + int next_state; + DBusMessage *msg; + char cmd[RFCOMM_MESSAGE_MAX_LENGTH + 1]; }; struct codec_item { @@ -196,6 +201,15 @@ struct rfcomm { bool has_volume; struct rfcomm_volume volumes[SPA_BT_VOLUME_ID_TERM]; unsigned int broken_mic_hw_volume:1; + + unsigned int hfp_cmd_in_progress:1; + union { + enum hfp_hf_state hf_state; + enum hsp_hs_state hs_state; + int hf_or_hs_state; + }; + struct spa_list cmd_send_queue; // elements: struct rfcomm_cmd + #ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE struct spa_list available_codec_list; struct spa_list supported_codec_list; @@ -211,16 +225,12 @@ struct rfcomm { unsigned int hfp_hf_nrec:1; unsigned int hfp_hf_clcc:1; unsigned int hfp_hf_cme:1; - unsigned int hfp_hf_cmd_in_progress:1; unsigned int hfp_hf_in_progress:1; unsigned int chld_supported:1; - enum hfp_hf_state hf_state; - enum hsp_hs_state hs_state; unsigned int codec; uint32_t cind_enabled_indicators; char *hf_indicators[MAX_HF_INDICATORS]; struct spa_bt_telephony_ag *telephony_ag; - struct spa_list hfp_hf_commands; struct spa_list updated_call_list; char *dialing_number; #endif @@ -443,37 +453,57 @@ static void rfcomm_free(struct rfcomm *rfcomm) free(rfcomm); } -#define RFCOMM_MESSAGE_MAX_LENGTH 256 - -/* from HF/HS to AG */ -SPA_PRINTF_FUNC(2, 3) -static ssize_t rfcomm_send_cmd(struct rfcomm *rfcomm, const char *format, ...) +static void rfcomm_cmd_done(struct rfcomm *rfcomm, char *reply) { struct impl *backend = rfcomm->backend; - char message[RFCOMM_MESSAGE_MAX_LENGTH + 1]; - ssize_t len; - va_list args; - va_start(args, format); - len = vsnprintf(message, RFCOMM_MESSAGE_MAX_LENGTH + 1, format, args); - va_end(args); + if (SPA_LIKELY (!spa_list_is_empty(&rfcomm->cmd_send_queue))) { + struct rfcomm_cmd *cmd = NULL; + cmd = spa_list_first(&rfcomm->cmd_send_queue, struct rfcomm_cmd, link); - if (len < 0) - return -EINVAL; + spa_log_debug(backend->log, "%s -> %s", cmd->cmd, reply); - if (len > RFCOMM_MESSAGE_MAX_LENGTH) - return -E2BIG; + if (cmd->msg) { + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_NONE; + uint8_t cme_error = 0; - if (rfcomm->hfp_hf_cmd_in_progress) { - spa_log_debug(backend->log, "Command in progress, postponing: %s", message); - struct rfcomm_cmd *cmd = calloc(1, sizeof(struct rfcomm_cmd)); - cmd->cmd = strndup(message, len); - spa_list_append(&rfcomm->hfp_hf_commands, &cmd->link); - return 0; + if (spa_strstartswith(reply, "+CME ERROR:")) { + cme_error = atoi(reply + strlen("+CME ERROR:")); + err = BT_TELEPHONY_ERROR_CME; + } else if (!spa_strstartswith(reply, "OK")) { + err = BT_TELEPHONY_ERROR_FAILED; + } + + telephony_send_dbus_method_reply(backend->telephony, cmd->msg, err, cme_error); + spa_clear_ptr(cmd->msg, dbus_message_unref); + } + + spa_list_remove(&cmd->link); + free(cmd); + } else { + spa_log_warn(backend->log, "received response but no command was sent"); } - if (rfcomm->hf_state == hfp_hf_done && spa_streq(message, "AT+CLCC")) - rfcomm->hf_state = hfp_hf_clcc_update; + rfcomm->hfp_cmd_in_progress = false; +} + +static ssize_t rfcomm_send_next_cmd(struct rfcomm *rfcomm) +{ + struct impl *backend = rfcomm->backend; + struct rfcomm_cmd *cmd = NULL; + char message[RFCOMM_MESSAGE_MAX_LENGTH + 1]; + ssize_t len = 0; + + if (rfcomm->hfp_cmd_in_progress) + return -EINPROGRESS; + + if (spa_list_is_empty(&rfcomm->cmd_send_queue)) + return -ENODATA; + + cmd = spa_list_first(&rfcomm->cmd_send_queue, struct rfcomm_cmd, link); + + strncpy(message, cmd->cmd, RFCOMM_MESSAGE_MAX_LENGTH + 1); + len = strlen(message); spa_log_debug(backend->log, "RFCOMM >> %s", message); @@ -495,11 +525,43 @@ static ssize_t rfcomm_send_cmd(struct rfcomm *rfcomm, const char *format, ...) spa_log_error(backend->log, "RFCOMM write error: %s", strerror(errno)); } - rfcomm->hfp_hf_cmd_in_progress = true; + rfcomm->hfp_cmd_in_progress = true; + rfcomm->hf_or_hs_state = cmd->next_state; return len; } +/* from HF/HS to AG */ +SPA_PRINTF_FUNC(4, 5) +static ssize_t rfcomm_send_cmd(struct rfcomm *rfcomm, int next_state, DBusMessage *m, const char *format, ...) +{ + struct impl *backend = rfcomm->backend; + spa_autofree struct rfcomm_cmd *cmd = NULL; + ssize_t len; + va_list args; + + cmd = calloc(1, sizeof(struct rfcomm_cmd)); + + va_start(args, format); + len = vsnprintf(cmd->cmd, RFCOMM_MESSAGE_MAX_LENGTH + 1, format, args); + va_end(args); + + if (len < 0) + return -EINVAL; + + if (len > RFCOMM_MESSAGE_MAX_LENGTH) + return -E2BIG; + + spa_log_debug(backend->log, "Queueing command: %s", cmd->cmd); + + cmd->next_state = next_state; + cmd->msg = m ? dbus_message_ref(m) : NULL; + spa_list_append(&rfcomm->cmd_send_queue, &cmd->link); + cmd = NULL; + + return rfcomm_send_next_cmd(rfcomm); +} + /* from AG to HF/HS */ SPA_PRINTF_FUNC(2, 3) static ssize_t rfcomm_send_reply(const struct rfcomm *rfcomm, const char *format, ...) @@ -633,7 +695,7 @@ static bool rfcomm_hsp_ag(struct rfcomm *rfcomm, char* buf) return true; } -static void rfcomm_send_volume_cmd(struct rfcomm *rfcomm, int id) +static void rfcomm_send_volume_cmd(struct rfcomm *rfcomm, int next_state, DBusMessage *m, int id) { struct spa_bt_transport_volume *t_volume; const char *format; @@ -660,7 +722,7 @@ static void rfcomm_send_volume_cmd(struct rfcomm *rfcomm, int id) else spa_assert_not_reached(); - rfcomm_send_cmd(rfcomm, "%s=%d", format, hw_volume); + rfcomm_send_cmd(rfcomm, next_state, m, "%s=%d", format, hw_volume); } static bool rfcomm_hsp_hs(struct rfcomm *rfcomm, char* buf) @@ -687,14 +749,18 @@ static bool rfcomm_hsp_hs(struct rfcomm *rfcomm, char* buf) } else { spa_log_debug(backend->log, "RFCOMM receive unsupported VGM gain: %s", buf); } - } else if (spa_streq(buf, "OK")) { - if (rfcomm->hs_state == hsp_hs_init2) { - rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX); - rfcomm->hs_state = hsp_hs_vgs; - } else if (rfcomm->hs_state == hsp_hs_vgs) { - rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_TX); - rfcomm->hs_state = hsp_hs_vgm; + } else if (spa_streq(buf, "OK") || spa_streq(buf, "ERROR")) { + rfcomm_cmd_done(rfcomm, buf); + + if (spa_streq(buf, "OK")) { + if (rfcomm->hs_state == hsp_hs_init2) { + rfcomm_send_volume_cmd(rfcomm, hsp_hs_vgs, NULL, SPA_BT_VOLUME_ID_RX); + } else if (rfcomm->hs_state == hsp_hs_vgs) { + rfcomm_send_volume_cmd(rfcomm, hsp_hs_vgm, NULL, SPA_BT_VOLUME_ID_TX); + } } + + rfcomm_send_next_cmd(rfcomm); } return true; @@ -1385,136 +1451,28 @@ next_indicator: return true; } -static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token); - -static bool hfp_hf_wait_for_reply(struct rfcomm *rfcomm, char *buf, size_t len) -{ - struct impl *backend = rfcomm->backend; - struct pollfd fds[1]; - bool reply_found = false; - - fds[0].fd = rfcomm->source.fd; - fds[0].events = POLLIN; - while (!reply_found) { - int ret; - char tmp_buf[512]; - ssize_t tmp_len; - char *ptr, *token; - - ret = poll(fds, 1, 2000); - if (ret < 0) { - spa_log_error(backend->log, "RFCOMM poll error: %s", strerror(errno)); - goto done; - } else if (ret == 0) { - spa_log_error(backend->log, "RFCOMM poll timeout"); - goto done; - } - - if (fds[0].revents & (POLLHUP | POLLERR)) { - spa_log_info(backend->log, "lost RFCOMM connection."); - rfcomm_free(rfcomm); - return false; - } - - if (fds[0].revents & POLLIN) { - tmp_len = read(rfcomm->source.fd, tmp_buf, sizeof(tmp_buf) - 1); - if (tmp_len < 0) { - spa_log_error(backend->log, "RFCOMM read error: %s", strerror(errno)); - goto done; - } - tmp_buf[tmp_len] = '\0'; - - /* Relaxed parsing of \r\n\r\n */ - ptr = tmp_buf; - while ((token = strsep(&ptr, "\r"))) { - size_t ptr_len; - - /* Skip leading and trailing \n */ - while (*token == '\n') - ++token; - for (ptr_len = strlen(token); ptr_len > 0 && token[ptr_len - 1] == '\n'; --ptr_len) - token[ptr_len - 1] = '\0'; - - /* Skip empty */ - if (*token == '\0' /*&& buf == NULL*/) - continue; - - spa_log_debug(backend->log, "RFCOMM event: %s", token); - if (spa_strstartswith(token, "OK") || spa_strstartswith(token, "ERROR") || - spa_strstartswith(token, "+CME ERROR:")) { - spa_log_debug(backend->log, "RFCOMM reply found: %s", token); - reply_found = true; - strncpy(buf, token, len); - buf[len-1] = '\0'; - } else if (!rfcomm_hfp_hf(rfcomm, token)) { - spa_log_debug(backend->log, "RFCOMM received unsupported event: %s", token); - } - } - } - } - -done: - rfcomm->hfp_hf_cmd_in_progress = false; - if (!spa_list_is_empty(&rfcomm->hfp_hf_commands)) { - struct rfcomm_cmd *cmd; - cmd = spa_list_first(&rfcomm->hfp_hf_commands, struct rfcomm_cmd, link); - spa_list_remove(&cmd->link); - spa_log_debug(backend->log, "Sending postponed command: %s", cmd->cmd); - rfcomm_send_cmd(rfcomm, "%s", cmd->cmd); - free(cmd->cmd); - free(cmd); - } - - return reply_found; -} - -static void hfp_hf_get_error_from_reply(char *reply, enum spa_bt_telephony_error *err, uint8_t *cme_error) -{ - if (spa_strstartswith(reply, "+CME ERROR:")) { - *cme_error = atoi(reply + strlen("+CME ERROR:")); - *err = BT_TELEPHONY_ERROR_CME; - } else { - *err = BT_TELEPHONY_ERROR_FAILED; - } -} - -static void hfp_hf_answer(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_answer(void *data, DBusMessage *m) { struct rfcomm_call_data *call_data = data; struct rfcomm *rfcomm = call_data->rfcomm; struct impl *backend = rfcomm->backend; - char reply[20]; - bool res; if (call_data->call->state != CALL_STATE_INCOMING) { - *err = BT_TELEPHONY_ERROR_INVALID_STATE; + telephony_send_dbus_method_reply(backend->telephony, m, BT_TELEPHONY_ERROR_INVALID_STATE, 0); return; } - rfcomm_send_cmd(rfcomm, "ATA"); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to answer call"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - return; - } - - *err = BT_TELEPHONY_ERROR_NONE; + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "ATA"); } -static void hfp_hf_hangup(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_hangup(void *data, DBusMessage *m) { struct rfcomm_call_data *call_data = data; struct rfcomm *rfcomm = call_data->rfcomm; struct impl *backend = rfcomm->backend; - struct spa_bt_telephony_call *call, *tcall; + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_NONE; + struct spa_bt_telephony_call *call; bool found_held = false; - bool hfp_hf_in_progress = false; - char reply[20]; - bool res; spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { if (call->state == CALL_STATE_HELD) @@ -1528,64 +1486,37 @@ static void hfp_hf_hangup(void *data, enum spa_bt_telephony_error *err, uint8_t case CALL_STATE_INCOMING: if (found_held) { if (!rfcomm->chld_supported) { - *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; - return; + err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + goto error; } else if (rfcomm->hfp_hf_in_progress) { - *err = BT_TELEPHONY_ERROR_IN_PROGRESS; - return; + err = BT_TELEPHONY_ERROR_IN_PROGRESS; + goto error; } - rfcomm_send_cmd(rfcomm, "AT+CHLD=1"); - hfp_hf_in_progress = true; + rfcomm_send_cmd(rfcomm, hfp_hf_chld1_hangup, m, "AT+CHLD=1"); + rfcomm->hfp_hf_in_progress = true; } else { - rfcomm_send_cmd(rfcomm, "AT+CHUP"); + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "AT+CHUP"); } break; case CALL_STATE_HELD: case CALL_STATE_WAITING: if (rfcomm->hfp_hf_in_progress) { - *err = BT_TELEPHONY_ERROR_IN_PROGRESS; - return; + err = BT_TELEPHONY_ERROR_IN_PROGRESS; + goto error; } - rfcomm_send_cmd(rfcomm, "AT+CHLD=0"); - hfp_hf_in_progress = true; + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "AT+CHLD=0"); + rfcomm->hfp_hf_in_progress = true; break; default: - spa_log_info(backend->log, "Call invalid state: skip hangup"); - *err = BT_TELEPHONY_ERROR_INVALID_STATE; - return; + spa_log_warn(backend->log, "Call in invalid state: skip hangup"); + err = BT_TELEPHONY_ERROR_INVALID_STATE; + goto error; } - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to hangup call"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - return; - } - - if (hfp_hf_in_progress) { - if (!rfcomm->hfp_hf_clcc && call_data->call->state != CALL_STATE_HELD && - call_data->call->state != CALL_STATE_WAITING) { - spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) { - if (call->state == CALL_STATE_ACTIVE) { - call->state = CALL_STATE_DISCONNECTED; - telephony_call_notify_updated_props(call); - telephony_call_destroy(call); - } - } - spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { - if (call->state == CALL_STATE_HELD) { - call->state = CALL_STATE_ACTIVE; - telephony_call_notify_updated_props(call); - } - } - } - rfcomm->hfp_hf_in_progress = true; - } - *err = BT_TELEPHONY_ERROR_NONE; + return; +error: + telephony_send_dbus_method_reply(backend->telephony, m, err, 0); } static const struct spa_bt_telephony_call_callbacks telephony_call_callbacks = { @@ -1615,45 +1546,33 @@ static struct spa_bt_telephony_call *hfp_hf_add_call(struct rfcomm *rfcomm, stru return call; } -static void hfp_hf_dial(void *data, const char *number, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_dial(void *data, const char *number, DBusMessage *m) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; - char reply[20]; - bool res; + + /* store the number in case we need to create the Call object + via CIND notifications (if CLCC is not supported) */ + free(spa_exchange(rfcomm->dialing_number, strdup(number))); spa_log_info(backend->log, "Dialing: \"%s\"", number); - rfcomm_send_cmd(rfcomm, "ATD%s;", number); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (res && spa_strstartswith(reply, "OK")) { - if (rfcomm->dialing_number) - free(rfcomm->dialing_number); - rfcomm->dialing_number = strdup(number); - *err = BT_TELEPHONY_ERROR_NONE; - } else { - spa_log_info(backend->log, "Failed to dial: \"%s\"", number); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - } + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "ATD%s;", number); } -static void hfp_hf_swap_calls(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_swap_calls(void *data, DBusMessage *m) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_NONE; struct spa_bt_telephony_call *call; bool found_held = false; - char reply[20]; - bool res; if (!rfcomm->chld_supported) { - *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; - return; + err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + goto error; } else if (rfcomm->hfp_hf_in_progress) { - *err = BT_TELEPHONY_ERROR_IN_PROGRESS; - return; + err = BT_TELEPHONY_ERROR_IN_PROGRESS; + goto error; } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { @@ -1665,41 +1584,33 @@ static void hfp_hf_swap_calls(void *data, enum spa_bt_telephony_error *err, uint if (!found_held) { spa_log_debug(backend->log, "no held calls"); - *err = BT_TELEPHONY_ERROR_INVALID_STATE; - return; - } - - rfcomm_send_cmd(rfcomm, "AT+CHLD=2"); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to swap calls"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - return; + err = BT_TELEPHONY_ERROR_INVALID_STATE; + goto error; } + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "AT+CHLD=2"); rfcomm->hfp_hf_in_progress = true; - *err = BT_TELEPHONY_ERROR_NONE; + return; + +error: + telephony_send_dbus_method_reply(backend->telephony, m, err, 0); } -static void hfp_hf_release_and_answer(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_release_and_answer(void *data, DBusMessage *m) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_NONE; struct spa_bt_telephony_call *call; bool found_active = false; bool found_waiting = false; - char reply[20]; - bool res; if (!rfcomm->chld_supported) { - *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; - return; + err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + goto error; } else if (rfcomm->hfp_hf_in_progress) { - *err = BT_TELEPHONY_ERROR_IN_PROGRESS; - return; + err = BT_TELEPHONY_ERROR_IN_PROGRESS; + goto error; } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { @@ -1711,48 +1622,40 @@ static void hfp_hf_release_and_answer(void *data, enum spa_bt_telephony_error *e if (!found_active || !found_waiting) { spa_log_debug(backend->log, "no active and waiting calls"); - *err = BT_TELEPHONY_ERROR_INVALID_STATE; - return; - } - - rfcomm_send_cmd(rfcomm, "AT+CHLD=1"); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to release and answer calls"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - return; + err = BT_TELEPHONY_ERROR_INVALID_STATE; + goto error; } + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "AT+CHLD=1"); rfcomm->hfp_hf_in_progress = true; - *err = BT_TELEPHONY_ERROR_NONE; + return; + +error: + telephony_send_dbus_method_reply(backend->telephony, m, err, 0); } -static void hfp_hf_release_and_swap(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_release_and_swap(void *data, DBusMessage *m) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_NONE; struct spa_bt_telephony_call *call; bool found_active = false; bool found_held = false; - char reply[20]; - bool res; if (!rfcomm->chld_supported) { - *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; - return; + err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + goto error; } else if (rfcomm->hfp_hf_in_progress) { - *err = BT_TELEPHONY_ERROR_IN_PROGRESS; - return; + err = BT_TELEPHONY_ERROR_IN_PROGRESS; + goto error; } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { if (call->state == CALL_STATE_WAITING) { spa_log_debug(backend->log, "call waiting before release and swap"); - *err = BT_TELEPHONY_ERROR_INVALID_STATE; - return; + err = BT_TELEPHONY_ERROR_INVALID_STATE; + goto error; } else if (call->state == CALL_STATE_ACTIVE) found_active = true; else if (call->state == CALL_STATE_HELD) @@ -1761,41 +1664,33 @@ static void hfp_hf_release_and_swap(void *data, enum spa_bt_telephony_error *err if (!found_active || !found_held) { spa_log_debug(backend->log, "no active and held calls"); - *err = BT_TELEPHONY_ERROR_INVALID_STATE; - return; - } - - rfcomm_send_cmd(rfcomm, "AT+CHLD=1"); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to release and swap calls"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - return; + err = BT_TELEPHONY_ERROR_INVALID_STATE; + goto error; } + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "AT+CHLD=1"); rfcomm->hfp_hf_in_progress = true; - *err = BT_TELEPHONY_ERROR_NONE; + return; + +error: + telephony_send_dbus_method_reply(backend->telephony, m, err, 0); } -static void hfp_hf_hold_and_answer(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_hold_and_answer(void *data, DBusMessage *m) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_NONE; struct spa_bt_telephony_call *call; bool found_active = false; bool found_waiting = false; - char reply[20]; - bool res; if (!rfcomm->chld_supported) { - *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; - return; + err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + goto error; } else if (rfcomm->hfp_hf_in_progress) { - *err = BT_TELEPHONY_ERROR_IN_PROGRESS; - return; + err = BT_TELEPHONY_ERROR_IN_PROGRESS; + goto error; } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { @@ -1807,34 +1702,24 @@ static void hfp_hf_hold_and_answer(void *data, enum spa_bt_telephony_error *err, if (!found_active || !found_waiting) { spa_log_debug(backend->log, "no active and waiting calls"); - *err = BT_TELEPHONY_ERROR_INVALID_STATE; - return; - } - - rfcomm_send_cmd(rfcomm, "AT+CHLD=2"); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to hold and answer calls"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - return; + err = BT_TELEPHONY_ERROR_INVALID_STATE; + goto error; } + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "AT+CHLD=2"); rfcomm->hfp_hf_in_progress = true; - *err = BT_TELEPHONY_ERROR_NONE; + return; + +error: + telephony_send_dbus_method_reply(backend->telephony, m, err, 0); } -static void hfp_hf_hangup_all(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_hangup_all(void *data, DBusMessage *m) { struct rfcomm *rfcomm = data; - struct impl *backend = rfcomm->backend; struct spa_bt_telephony_call *call; bool found_active = false; bool found_held = false; - char reply[20]; - bool res; spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { switch (call->state) { @@ -1853,58 +1738,39 @@ static void hfp_hf_hangup_all(void *data, enum spa_bt_telephony_error *err, uint } } - *err = BT_TELEPHONY_ERROR_NONE; - /* Hangup held calls */ if (found_held) { - rfcomm_send_cmd(rfcomm, "AT+CHLD=0"); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to hangup held calls"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - } + rfcomm_send_cmd(rfcomm, hfp_hf_idle, found_active ? NULL : m, "AT+CHLD=0"); } /* Hangup active calls */ if (found_active) { - rfcomm_send_cmd(rfcomm, "AT+CHUP"); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to hangup active calls"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - } + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "AT+CHUP"); } } -static void hfp_hf_create_multiparty(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_create_multiparty(void *data, DBusMessage *m) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_NONE; struct spa_bt_telephony_call *call; bool found_active = false; bool found_held = false; - char reply[20]; - bool res; if (!rfcomm->chld_supported) { - *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; - return; + err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + goto error; } else if (rfcomm->hfp_hf_in_progress) { - *err = BT_TELEPHONY_ERROR_IN_PROGRESS; - return; + err = BT_TELEPHONY_ERROR_IN_PROGRESS; + goto error; } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { if (call->state == CALL_STATE_WAITING) { spa_log_debug(backend->log, "call waiting before creating multiparty"); - *err = BT_TELEPHONY_ERROR_INVALID_STATE; - return; + err = BT_TELEPHONY_ERROR_INVALID_STATE; + goto error; } else if (call->state == CALL_STATE_ACTIVE) found_active = true; else if (call->state == CALL_STATE_HELD) @@ -1913,33 +1779,25 @@ static void hfp_hf_create_multiparty(void *data, enum spa_bt_telephony_error *er if (!found_active || !found_held) { spa_log_debug(backend->log, "no active and held calls"); - *err = BT_TELEPHONY_ERROR_INVALID_STATE; - return; - } - - rfcomm_send_cmd(rfcomm, "AT+CHLD=3"); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to create multiparty"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - return; + err = BT_TELEPHONY_ERROR_INVALID_STATE; + goto error; } + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "AT+CHLD=3"); rfcomm->hfp_hf_in_progress = true; - *err = BT_TELEPHONY_ERROR_NONE; + return; + +error: + telephony_send_dbus_method_reply(backend->telephony, m, err, 0); } -static void hfp_hf_send_tones(void *data, const char *tones, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_send_tones(void *data, const char *tones, DBusMessage *m) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_NONE; struct spa_bt_telephony_call *call; bool found = false; - char reply[20]; - bool res; spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { if (call->state == CALL_STATE_ACTIVE) { @@ -1950,69 +1808,51 @@ static void hfp_hf_send_tones(void *data, const char *tones, enum spa_bt_telepho if (!found) { spa_log_debug(backend->log, "no active call"); - *err = BT_TELEPHONY_ERROR_INVALID_STATE; - return; + err = BT_TELEPHONY_ERROR_INVALID_STATE; + goto error; } - rfcomm_send_cmd(rfcomm, "AT+VTS=%s", tones); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to send tones: %s", tones); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - return; - } + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "AT+VTS=%s", tones); + return; - *err = BT_TELEPHONY_ERROR_NONE; +error: + telephony_send_dbus_method_reply(backend->telephony, m, err, 0); } static int sco_do_connect(struct spa_bt_transport *t); -static void hfp_hf_transport_activate(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_transport_activate(void *data, DBusMessage *m) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; - char reply[20]; - bool res; + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_NONE; if (rfcomm->transport && rfcomm->transport->fd > 0) { spa_log_debug(backend->log, "transport is already active; SCO socket exists"); - *err = BT_TELEPHONY_ERROR_INVALID_STATE; - return; + err = BT_TELEPHONY_ERROR_INVALID_STATE; + goto out; } if (rfcomm->codec_negotiation_supported) { - rfcomm_send_cmd(rfcomm, "AT+BCC"); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to send AT+BCC"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - return; - } + rfcomm_send_cmd(rfcomm, hfp_hf_idle, m, "AT+BCC"); + return; } else { if (!rfcomm->transport || rfcomm->transport->media_codec->id != SPA_BLUETOOTH_AUDIO_CODEC_CVSD) { - *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; - return; + err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + goto out; } sco_do_connect(rfcomm->transport); } - *err = BT_TELEPHONY_ERROR_NONE; +out: + telephony_send_dbus_method_reply(backend->telephony, m, err, 0); } -static void hfp_hf_set_speaker_volume(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_set_speaker_volume(void *data, uint8_t volume, DBusMessage *m) { struct rfcomm *rfcomm = data; - struct impl *backend = rfcomm->backend; struct spa_bt_transport_volume *t_volume; - char reply[20]; - bool res; rfcomm->volumes[SPA_BT_VOLUME_ID_RX].hw_volume = volume; if (rfcomm_hw_volume_enabled(rfcomm)) { @@ -2024,27 +1864,13 @@ static void hfp_hf_set_speaker_volume(void *data, uint8_t volume, enum spa_bt_te } } - rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to send AT+VGS"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - return; - } - - *err = BT_TELEPHONY_ERROR_NONE; + rfcomm_send_volume_cmd(rfcomm, hfp_hf_idle, m, SPA_BT_VOLUME_ID_RX); } -static void hfp_hf_set_microphone_volume(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error) +static void hfp_hf_set_microphone_volume(void *data, uint8_t volume, DBusMessage *m) { struct rfcomm *rfcomm = data; - struct impl *backend = rfcomm->backend; struct spa_bt_transport_volume *t_volume; - char reply[20]; - bool res; rfcomm->volumes[SPA_BT_VOLUME_ID_TX].hw_volume = volume; if (rfcomm_hw_volume_enabled(rfcomm)) { @@ -2056,18 +1882,7 @@ static void hfp_hf_set_microphone_volume(void *data, uint8_t volume, enum spa_bt } } - rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_TX); - res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); - if (!res || !spa_strstartswith(reply, "OK")) { - spa_log_info(backend->log, "Failed to send AT+VGM"); - if (res) - hfp_hf_get_error_from_reply(reply, err, cme_error); - else - *err = BT_TELEPHONY_ERROR_FAILED; - return; - } - - *err = BT_TELEPHONY_ERROR_NONE; + rfcomm_send_volume_cmd(rfcomm, hfp_hf_idle, m, SPA_BT_VOLUME_ID_TX); } static const struct spa_bt_telephony_ag_callbacks telephony_ag_callbacks = { @@ -2136,7 +1951,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) spa_log_debug(backend->log, "RFCOMM selected_codec = %i", selected_codec); /* send codec selection to AG */ - rfcomm_send_cmd(rfcomm, "AT+BCS=%u", selected_codec); + rfcomm_send_cmd(rfcomm, hfp_hf_idle, NULL, "AT+BCS=%u", selected_codec); if (!rfcomm->transport || (rfcomm->codec != selected_codec) ) { if (rfcomm_new_transport(rfcomm, selected_codec) < 0) { @@ -2219,7 +2034,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) spa_bt_device_report_battery_level(rfcomm->device, value * 100 / 5); } else if (spa_streq(rfcomm->hf_indicators[indicator], "callsetup")) { if (rfcomm->hfp_hf_clcc) { - rfcomm_send_cmd(rfcomm, "AT+CLCC"); + rfcomm_send_cmd(rfcomm, hfp_hf_clcc_update, NULL, "AT+CLCC"); return true; } @@ -2266,10 +2081,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) spa_log_info(backend->log, "Dialing call"); if (hfp_hf_add_call(rfcomm, rfcomm->telephony_ag, CALL_STATE_DIALING, rfcomm->dialing_number) == NULL) spa_log_warn(backend->log, "failed to create dialing call"); - if (rfcomm->dialing_number) { - free(rfcomm->dialing_number); - rfcomm->dialing_number = NULL; - } + spa_clear_ptr(rfcomm->dialing_number, free); } } else if (value == CIND_CALLSETUP_ALERTING) { struct spa_bt_telephony_call *call; @@ -2284,7 +2096,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) rfcomm->hfp_hf_in_progress = false; } else if (spa_streq(rfcomm->hf_indicators[indicator], "call")) { if (rfcomm->hfp_hf_clcc) { - rfcomm_send_cmd(rfcomm, "AT+CLCC"); + rfcomm_send_cmd(rfcomm, hfp_hf_clcc_update, NULL, "AT+CLCC"); return true; } @@ -2311,7 +2123,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) rfcomm->hfp_hf_in_progress = false; } else if (spa_streq(rfcomm->hf_indicators[indicator], "callheld")) { if (rfcomm->hfp_hf_clcc) { - rfcomm_send_cmd(rfcomm, "AT+CLCC"); + rfcomm_send_cmd(rfcomm, hfp_hf_clcc_update, NULL, "AT+CLCC"); return true; } @@ -2499,6 +2311,9 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) rfcomm->hfp_hf_in_progress = false; } else if (spa_strstartswith(token, "OK") || spa_strstartswith(token, "ERROR") || spa_strstartswith(token, "+CME ERROR:")) { + + rfcomm_cmd_done(rfcomm, token); + if (spa_strstartswith(token, "OK")) { switch(rfcomm->hf_state) { case hfp_hf_brsf: @@ -2512,29 +2327,23 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) spa_list_for_each(item, &rfcomm->available_codec_list, link) spa_strbuf_append(&str, ",%u", item->codec->codec_id); - rfcomm_send_cmd(rfcomm, "AT+BAC=%s", buf); - rfcomm->hf_state = hfp_hf_bac; + rfcomm_send_cmd(rfcomm, hfp_hf_bac, NULL, "AT+BAC=%s", buf); } else { - rfcomm_send_cmd(rfcomm, "AT+CIND=?"); - rfcomm->hf_state = hfp_hf_cind1; + rfcomm_send_cmd(rfcomm, hfp_hf_cind1, NULL, "AT+CIND=?"); } break; case hfp_hf_bac: - rfcomm_send_cmd(rfcomm, "AT+CIND=?"); - rfcomm->hf_state = hfp_hf_cind1; + rfcomm_send_cmd(rfcomm, hfp_hf_cind1, NULL, "AT+CIND=?"); break; case hfp_hf_cind1: - rfcomm_send_cmd(rfcomm, "AT+CIND?"); - rfcomm->hf_state = hfp_hf_cind2; + rfcomm_send_cmd(rfcomm, hfp_hf_cind2, NULL, "AT+CIND?"); break; case hfp_hf_cind2: - rfcomm_send_cmd(rfcomm, "AT+CMER=3,0,0,1"); - rfcomm->hf_state = hfp_hf_cmer; + rfcomm_send_cmd(rfcomm, hfp_hf_cmer, NULL, "AT+CMER=3,0,0,1"); break; case hfp_hf_cmer: if (rfcomm->hfp_hf_3way) { - rfcomm_send_cmd(rfcomm, "AT+CHLD=?"); - rfcomm->hf_state = hfp_hf_chld; + rfcomm_send_cmd(rfcomm, hfp_hf_chld, NULL, "AT+CHLD=?"); break; } SPA_FALLTHROUGH; @@ -2561,34 +2370,29 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) } telephony_ag_register(rfcomm->telephony_ag); - rfcomm_send_cmd(rfcomm, "AT+CLIP=1"); - rfcomm->hf_state = hfp_hf_clip; + rfcomm_send_cmd(rfcomm, hfp_hf_clip, NULL, "AT+CLIP=1"); break; case hfp_hf_clip: if (rfcomm->chld_supported) { - rfcomm_send_cmd(rfcomm, "AT+CCWA=1"); - rfcomm->hf_state = hfp_hf_ccwa; + rfcomm_send_cmd(rfcomm, hfp_hf_ccwa, NULL, "AT+CCWA=1"); break; } SPA_FALLTHROUGH; case hfp_hf_ccwa: if (rfcomm->hfp_hf_cme) { - rfcomm_send_cmd(rfcomm, "AT+CMEE=1"); - rfcomm->hf_state = hfp_hf_cmee; + rfcomm_send_cmd(rfcomm, hfp_hf_cmee, NULL, "AT+CMEE=1"); break; } SPA_FALLTHROUGH; case hfp_hf_cmee: if (backend->hfp_disable_nrec && rfcomm->hfp_hf_nrec) { - rfcomm_send_cmd(rfcomm, "AT+NREC=0"); - rfcomm->hf_state = hfp_hf_nrec; + rfcomm_send_cmd(rfcomm, hfp_hf_nrec, NULL, "AT+NREC=0"); break; } SPA_FALLTHROUGH; case hfp_hf_nrec: if (rfcomm->hfp_hf_clcc) { - rfcomm_send_cmd(rfcomm, "AT+CLCC"); - rfcomm->hf_state = hfp_hf_clcc; + rfcomm_send_cmd(rfcomm, hfp_hf_clcc, NULL, "AT+CLCC"); break; } else { // TODO: Create calls if CIND reports one during SLC setup @@ -2600,32 +2404,40 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) if (rfcomm->hf_state == hfp_hf_clcc) { hfp_hf_remove_disconnected_calls(rfcomm); } - rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX); - rfcomm->hf_state = hfp_hf_vgs; + rfcomm_send_volume_cmd(rfcomm, hfp_hf_vgs, NULL, SPA_BT_VOLUME_ID_RX); break; case hfp_hf_vgs: - rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_TX); - rfcomm->hf_state = hfp_hf_done; + rfcomm_send_volume_cmd(rfcomm, hfp_hf_idle, NULL, SPA_BT_VOLUME_ID_TX); break; case hfp_hf_clcc_update: hfp_hf_remove_disconnected_calls(rfcomm); - rfcomm->hf_state = hfp_hf_done; break; + case hfp_hf_chld1_hangup: + /* For HFP/HF/TWC/BV-03-C - see 0e92ab9307e05758b3f70b4c0648e29c1d1e50be */ + if (!rfcomm->hfp_hf_clcc) { + struct spa_bt_telephony_call *call, *tcall; + spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) { + if (call->state == CALL_STATE_ACTIVE) { + call->state = CALL_STATE_DISCONNECTED; + telephony_call_notify_updated_props(call); + telephony_call_destroy(call); + } + } + spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { + if (call->state == CALL_STATE_HELD) { + call->state = CALL_STATE_ACTIVE; + telephony_call_notify_updated_props(call); + } + } + } + break; + case hfp_hf_idle: default: break; } } - rfcomm->hfp_hf_cmd_in_progress = false; - if (!spa_list_is_empty(&rfcomm->hfp_hf_commands)) { - struct rfcomm_cmd *cmd; - cmd = spa_list_first(&rfcomm->hfp_hf_commands, struct rfcomm_cmd, link); - spa_list_remove(&cmd->link); - spa_log_debug(backend->log, "Sending postponed command: %s", cmd->cmd); - rfcomm_send_cmd(rfcomm, "%s", cmd->cmd); - free(cmd->cmd); - free(cmd); - } + rfcomm_send_next_cmd(rfcomm); } return true; @@ -2798,7 +2610,7 @@ static int sco_do_connect(struct spa_bt_transport *t) td->rfcomm->hfp_ag_switching_codec = true; rfcomm_send_reply(td->rfcomm, "+BCS: 1"); } else if (t->profile == SPA_BT_PROFILE_HFP_AG) { - rfcomm_send_cmd(td->rfcomm, "AT+BAC=1"); + rfcomm_send_cmd(td->rfcomm, hfp_hf_idle, NULL, "AT+BAC=1"); } } #endif @@ -3151,14 +2963,12 @@ static void sco_listen_event(struct spa_source *source) /* Report initial volume to remote */ if (t->profile == SPA_BT_PROFILE_HSP_AG) { - rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX); - rfcomm->hs_state = hsp_hs_vgs; + rfcomm_send_volume_cmd(rfcomm, hsp_hs_vgs, NULL, SPA_BT_VOLUME_ID_RX); } else if (t->profile == SPA_BT_PROFILE_HFP_AG && rfcomm->hf_state > hfp_hf_vgs) { /* Report volume only if SLC and setup sequence has been completed * else this could break the sequence. * The volumes will be reported at the end of the setup sequence. */ - rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX); - rfcomm->hf_state = hfp_hf_vgs; + rfcomm_send_volume_cmd(rfcomm, hfp_hf_vgs, NULL, SPA_BT_VOLUME_ID_RX); } spa_bt_transport_set_state(t, SPA_BT_TRANSPORT_STATE_PENDING); @@ -3567,7 +3377,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag rfcomm->source.fd = spa_steal_fd(fd); rfcomm->source.mask = SPA_IO_IN; rfcomm->source.rmask = 0; - spa_list_init(&rfcomm->hfp_hf_commands); + spa_list_init(&rfcomm->cmd_send_queue); spa_list_init(&rfcomm->updated_call_list); /* By default all indicators are enabled */ rfcomm->cind_enabled_indicators = 0xFFFFFFFF; @@ -3619,9 +3429,8 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag hf_features |= SPA_BT_HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL; /* send command to AG with the features supported by Hands-Free */ - rfcomm_send_cmd(rfcomm, "AT+BRSF=%u", hf_features); + rfcomm_send_cmd(rfcomm, hfp_hf_brsf, NULL, "AT+BRSF=%u", hf_features); - rfcomm->hf_state = hfp_hf_brsf; } else if (profile == SPA_BT_PROFILE_HFP_HF) { make_available_codec_list(backend, rfcomm->device, &rfcomm->available_codec_list); } diff --git a/spa/plugins/bluez5/telephony.c b/spa/plugins/bluez5/telephony.c index f5887053d..7695c0b81 100644 --- a/spa/plugins/bluez5/telephony.c +++ b/spa/plugins/bluez5/telephony.c @@ -203,9 +203,6 @@ struct agimpl { struct spa_callbacks callbacks; void *user_data; - bool dial_in_progress; - struct callimpl *dial_return; - struct { int volume[SPA_BT_VOLUME_ID_TERM]; struct spa_bt_telephony_ag_transport transport; @@ -228,22 +225,22 @@ struct callimpl { } prev; }; -#define ag_emit(ag,m,v,...) spa_callbacks_call(&ag->callbacks, struct spa_bt_telephony_ag_callbacks, m, v, ##__VA_ARGS__) -#define ag_emit_dial(s,n,e,cme) ag_emit(s,dial,0,n,e,cme) -#define ag_emit_swap_calls(s,e,cme) ag_emit(s,swap_calls,0,e,cme) -#define ag_emit_release_and_answer(s,e,cme) ag_emit(s,release_and_answer,0,e,cme) -#define ag_emit_release_and_swap(s,e,cme) ag_emit(s,release_and_swap,0,e,cme) -#define ag_emit_hold_and_answer(s,e,cme) ag_emit(s,hold_and_answer,0,e,cme) -#define ag_emit_hangup_all(s,e,cme) ag_emit(s,hangup_all,0,e,cme) -#define ag_emit_create_multiparty(s,e,cme) ag_emit(s,create_multiparty,0,e,cme) -#define ag_emit_send_tones(s,t,e,cme) ag_emit(s,send_tones,0,t,e,cme) -#define ag_emit_transport_activate(s,e,cme) ag_emit(s,transport_activate,0,e,cme) -#define ag_emit_set_speaker_volume(s,v,e,cme) ag_emit(s,set_speaker_volume,0,v,e,cme) -#define ag_emit_set_microphone_volume(s,v,e,cme) ag_emit(s,set_microphone_volume,0,v,e,cme) +#define ag_emit(ag,m,v,...) spa_callbacks_call(&ag->callbacks, struct spa_bt_telephony_ag_callbacks, m, v, ##__VA_ARGS__) +#define ag_emit_dial(s,n,m) ag_emit(s,dial,0,n,m) +#define ag_emit_swap_calls(s,m) ag_emit(s,swap_calls,0,m) +#define ag_emit_release_and_answer(s,m) ag_emit(s,release_and_answer,0,m) +#define ag_emit_release_and_swap(s,m) ag_emit(s,release_and_swap,0,m) +#define ag_emit_hold_and_answer(s,m) ag_emit(s,hold_and_answer,0,m) +#define ag_emit_hangup_all(s,m) ag_emit(s,hangup_all,0,m) +#define ag_emit_create_multiparty(s,m) ag_emit(s,create_multiparty,0,m) +#define ag_emit_send_tones(s,t,m) ag_emit(s,send_tones,0,t,m) +#define ag_emit_transport_activate(s,m) ag_emit(s,transport_activate,0,m) +#define ag_emit_set_speaker_volume(s,v,m) ag_emit(s,set_speaker_volume,0,v,m) +#define ag_emit_set_microphone_volume(s,v,m) ag_emit(s,set_microphone_volume,0,v,m) #define call_emit(c,m,v,...) spa_callbacks_call(&c->callbacks, struct spa_bt_telephony_call_callbacks, m, v, ##__VA_ARGS__) -#define call_emit_answer(s,e,cme) call_emit(s,answer,0,e,cme) -#define call_emit_hangup(s,e,cme) call_emit(s,hangup,0,e,cme) +#define call_emit_answer(s,m) call_emit(s,answer,0,m) +#define call_emit_hangup(s,m) call_emit(s,hangup,0,m) static void dbus_iter_append_ag_interfaces(DBusMessageIter *i, struct spa_bt_telephony_ag *ag); static void dbus_iter_append_call_properties(DBusMessageIter *i, struct spa_bt_telephony_call *call, bool all); @@ -521,6 +518,21 @@ void telephony_free(struct spa_bt_telephony *telephony) free(impl); } +void telephony_send_dbus_method_reply(struct spa_bt_telephony *telephony, DBusMessage *m, + enum spa_bt_telephony_error err, uint8_t cme_error) +{ + struct impl *impl = SPA_CONTAINER_OF(telephony, struct impl, this); + spa_autoptr(DBusMessage) reply = NULL; + + if (err == BT_TELEPHONY_ERROR_NONE) + reply = dbus_message_new_method_return(m); + else + reply = dbus_message_new_error(m, telephony_error_to_dbus (err), + telephony_error_to_description (err, cme_error)); + + dbus_connection_send(impl->conn, reply, NULL); +} + static void telephony_ag_commit_properties(struct spa_bt_telephony_ag *ag) { struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this); @@ -859,9 +871,6 @@ static DBusMessage *ag_properties_set(struct agimpl *agimpl, DBusMessage *m) return NULL; if (spa_streq(iface, PW_TELEPHONY_AG_IFACE)) { - enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; - if (spa_streq(name, "SpeakerVolume")) { dbus_message_iter_init(m, &i); dbus_message_iter_next(&i); /* skip iface */ @@ -869,12 +878,10 @@ static DBusMessage *ag_properties_set(struct agimpl *agimpl, DBusMessage *m) dbus_message_iter_recurse(&i, &variant); /* value */ dbus_message_iter_get_basic(&variant, &agimpl->this.volume[SPA_BT_VOLUME_ID_RX]); - if (ag_emit_set_speaker_volume(agimpl, agimpl->this.volume[SPA_BT_VOLUME_ID_RX], &err, &cme_error) && - err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); + return ag_emit_set_speaker_volume(agimpl, agimpl->this.volume[SPA_BT_VOLUME_ID_RX], m) ? NULL : + dbus_message_new_error(m, telephony_error_to_dbus (BT_TELEPHONY_ERROR_FAILED), + telephony_error_to_description (BT_TELEPHONY_ERROR_FAILED, 0)); - return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); } else if (spa_streq(name, "MicrophoneVolume")) { dbus_message_iter_init(m, &i); dbus_message_iter_next(&i); /* skip iface */ @@ -882,12 +889,9 @@ static DBusMessage *ag_properties_set(struct agimpl *agimpl, DBusMessage *m) dbus_message_iter_recurse(&i, &variant); /* value */ dbus_message_iter_get_basic(&variant, &agimpl->this.volume[SPA_BT_VOLUME_ID_TX]); - if (ag_emit_set_microphone_volume(agimpl, agimpl->this.volume[SPA_BT_VOLUME_ID_TX], &err, &cme_error) && - err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); - - return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + return ag_emit_set_microphone_volume(agimpl, agimpl->this.volume[SPA_BT_VOLUME_ID_TX], m) ? NULL : + dbus_message_new_error(m, telephony_error_to_dbus (BT_TELEPHONY_ERROR_FAILED), + telephony_error_to_description (BT_TELEPHONY_ERROR_FAILED, 0)); } } else if (spa_streq(iface, PW_TELEPHONY_AG_TRANSPORT_IFACE)) { if (spa_streq(name, "RejectSCO")) { @@ -939,8 +943,6 @@ static DBusMessage *ag_dial(struct agimpl *agimpl, DBusMessage *m) { const char *number = NULL; enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; - spa_autoptr(DBusMessage) r = NULL; if (!dbus_message_get_args(m, NULL, DBUS_TYPE_STRING, &number, @@ -952,111 +954,60 @@ static DBusMessage *ag_dial(struct agimpl *agimpl, DBusMessage *m) goto failed; } - agimpl->dial_in_progress = true; - if (!ag_emit_dial(agimpl, number, &err, &cme_error)) { - agimpl->dial_in_progress = false; - goto failed; - } - agimpl->dial_in_progress = false; - - if (!agimpl->dial_return || !agimpl->dial_return->path) - err = BT_TELEPHONY_ERROR_FAILED; - - if (err != BT_TELEPHONY_ERROR_NONE) - goto failed; - - if ((r = dbus_message_new_method_return(m)) == NULL) + if (ag_emit_dial(agimpl, number, m)) return NULL; - if (!dbus_message_append_args(r, DBUS_TYPE_OBJECT_PATH, - &agimpl->dial_return->path, DBUS_TYPE_INVALID)) - return NULL; - - agimpl->dial_return = NULL; - - return spa_steal_ptr(r); failed: return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + telephony_error_to_description (err, 0)); } static DBusMessage *ag_swap_calls(struct agimpl *agimpl, DBusMessage *m) { - enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; - - if (ag_emit_swap_calls(agimpl, &err, &cme_error) && err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); - - return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + return ag_emit_swap_calls(agimpl, m) ? NULL : + dbus_message_new_error(m, telephony_error_to_dbus (BT_TELEPHONY_ERROR_FAILED), + telephony_error_to_description (BT_TELEPHONY_ERROR_FAILED, 0)); } static DBusMessage *ag_release_and_answer(struct agimpl *agimpl, DBusMessage *m) { - enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; - - if (ag_emit_release_and_answer(agimpl, &err, &cme_error) && err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); - - return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + return ag_emit_release_and_answer(agimpl, m) ? NULL : + dbus_message_new_error(m, telephony_error_to_dbus (BT_TELEPHONY_ERROR_FAILED), + telephony_error_to_description (BT_TELEPHONY_ERROR_FAILED, 0)); } static DBusMessage *ag_release_and_swap(struct agimpl *agimpl, DBusMessage *m) { - enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; - - if (ag_emit_release_and_swap(agimpl, &err, &cme_error) && err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); - - return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + return ag_emit_release_and_swap(agimpl, m) ? NULL : + dbus_message_new_error(m, telephony_error_to_dbus (BT_TELEPHONY_ERROR_FAILED), + telephony_error_to_description (BT_TELEPHONY_ERROR_FAILED, 0)); } static DBusMessage *ag_hold_and_answer(struct agimpl *agimpl, DBusMessage *m) { - enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; - - if (ag_emit_hold_and_answer(agimpl, &err, &cme_error) && err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); - - return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + return ag_emit_hold_and_answer(agimpl, m) ? NULL : + dbus_message_new_error(m, telephony_error_to_dbus (BT_TELEPHONY_ERROR_FAILED), + telephony_error_to_description (BT_TELEPHONY_ERROR_FAILED, 0)); } static DBusMessage *ag_hangup_all(struct agimpl *agimpl, DBusMessage *m) { - enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; - - if (ag_emit_hangup_all(agimpl, &err, &cme_error) && err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); - - return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + return ag_emit_hangup_all(agimpl, m) ? NULL : + dbus_message_new_error(m, telephony_error_to_dbus (BT_TELEPHONY_ERROR_FAILED), + telephony_error_to_description (BT_TELEPHONY_ERROR_FAILED, 0)); } static DBusMessage *ag_create_multiparty(struct agimpl *agimpl, DBusMessage *m) { - enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; - - if (ag_emit_create_multiparty(agimpl, &err, &cme_error) && err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); - - return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + return ag_emit_create_multiparty(agimpl, m) ? NULL : + dbus_message_new_error(m, telephony_error_to_dbus (BT_TELEPHONY_ERROR_FAILED), + telephony_error_to_description (BT_TELEPHONY_ERROR_FAILED, 0)); } static DBusMessage *ag_send_tones(struct agimpl *agimpl, DBusMessage *m) { const char *tones = NULL; enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; if (!dbus_message_get_args(m, NULL, DBUS_TYPE_STRING, &tones, @@ -1068,24 +1019,19 @@ static DBusMessage *ag_send_tones(struct agimpl *agimpl, DBusMessage *m) goto failed; } - if (ag_emit_send_tones(agimpl, tones, &err, &cme_error) && err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); + if (ag_emit_send_tones(agimpl, tones, m)) + return NULL; failed: return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + telephony_error_to_description (err, 0)); } static DBusMessage *ag_transport_activate(struct agimpl *agimpl, DBusMessage *m) { - enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; - - if (ag_emit_transport_activate(agimpl, &err, &cme_error) && err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); - - return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + return ag_emit_transport_activate(agimpl, m) ? NULL : + dbus_message_new_error(m, telephony_error_to_dbus (BT_TELEPHONY_ERROR_FAILED), + telephony_error_to_description (BT_TELEPHONY_ERROR_FAILED, 0)); } static DBusHandlerResult ag_handler(DBusConnection *c, DBusMessage *m, void *userdata) @@ -1144,9 +1090,7 @@ static DBusHandlerResult ag_handler(DBusConnection *c, DBusMessage *m, void *use return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - if (r == NULL) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - if (!dbus_connection_send(impl->conn, r, NULL)) + if (r && !dbus_connection_send(impl->conn, r, NULL)) return DBUS_HANDLER_RESULT_NEED_MEMORY; return DBUS_HANDLER_RESULT_HANDLED; } @@ -1386,7 +1330,6 @@ void telephony_ag_set_callbacks(struct spa_bt_telephony_ag *ag, struct spa_bt_telephony_call * telephony_call_new(struct spa_bt_telephony_ag *ag, size_t user_data_size) { - struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this); struct callimpl *callimpl; spa_assert(user_data_size < SIZE_MAX - sizeof(*callimpl)); @@ -1403,10 +1346,6 @@ telephony_call_new(struct spa_bt_telephony_ag *ag, size_t user_data_size) if (user_data_size > 0) callimpl->user_data = SPA_PTROFF(callimpl, sizeof(struct callimpl), void); - /* mark this object as the return value of the Dial method */ - if (agimpl->dial_in_progress) - agimpl->dial_return = callimpl; - return &callimpl->this; } @@ -1650,26 +1589,16 @@ static DBusMessage *call_properties_set(struct callimpl *callimpl, DBusMessage * static DBusMessage *call_answer(struct callimpl *callimpl, DBusMessage *m) { - enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; - - if (call_emit_answer(callimpl, &err, &cme_error) && err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); - - return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + return call_emit_answer(callimpl, m) ? NULL : + dbus_message_new_error(m, telephony_error_to_dbus (BT_TELEPHONY_ERROR_FAILED), + telephony_error_to_description (BT_TELEPHONY_ERROR_FAILED, 0)); } static DBusMessage *call_hangup(struct callimpl *callimpl, DBusMessage *m) { - enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - uint8_t cme_error; - - if (call_emit_hangup(callimpl, &err, &cme_error) && err == BT_TELEPHONY_ERROR_NONE) - return dbus_message_new_method_return(m); - - return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err, cme_error)); + return call_emit_hangup(callimpl, m) ? NULL : + dbus_message_new_error(m, telephony_error_to_dbus (BT_TELEPHONY_ERROR_FAILED), + telephony_error_to_description (BT_TELEPHONY_ERROR_FAILED, 0)); } static DBusHandlerResult call_handler(DBusConnection *c, DBusMessage *m, void *userdata) @@ -1707,9 +1636,7 @@ static DBusHandlerResult call_handler(DBusConnection *c, DBusMessage *m, void *u return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - if (r == NULL) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - if (!dbus_connection_send(impl->conn, r, NULL)) + if (r && !dbus_connection_send(impl->conn, r, NULL)) return DBUS_HANDLER_RESULT_NEED_MEMORY; return DBUS_HANDLER_RESULT_HANDLED; } diff --git a/spa/plugins/bluez5/telephony.h b/spa/plugins/bluez5/telephony.h index 13961b7db..1f7f67362 100644 --- a/spa/plugins/bluez5/telephony.h +++ b/spa/plugins/bluez5/telephony.h @@ -67,33 +67,38 @@ struct spa_bt_telephony_ag_callbacks { #define SPA_VERSION_BT_TELEPHONY_AG_CALLBACKS 0 uint32_t version; - void (*dial)(void *data, const char *number, enum spa_bt_telephony_error *err, uint8_t *cme_error); - void (*swap_calls)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error); - void (*release_and_answer)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error); - void (*release_and_swap)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error); - void (*hold_and_answer)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error); - void (*hangup_all)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error); - void (*create_multiparty)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error); - void (*send_tones)(void *data, const char *tones, enum spa_bt_telephony_error *err, uint8_t *cme_error); + void (*dial)(void *data, const char *number, DBusMessage *m); + void (*swap_calls)(void *data, DBusMessage *m); + void (*release_and_answer)(void *data, DBusMessage *m); + void (*release_and_swap)(void *data, DBusMessage *m); + void (*hold_and_answer)(void *data, DBusMessage *m); + void (*hangup_all)(void *data, DBusMessage *m); + void (*create_multiparty)(void *data, DBusMessage *m); + void (*send_tones)(void *data, const char *tones, DBusMessage *m); - void (*transport_activate)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error); + void (*transport_activate)(void *data, DBusMessage *m); - void (*set_speaker_volume)(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error); - void (*set_microphone_volume)(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error); + void (*set_speaker_volume)(void *data, uint8_t volume, DBusMessage *m); + void (*set_microphone_volume)(void *data, uint8_t volume, DBusMessage *m); }; struct spa_bt_telephony_call_callbacks { #define SPA_VERSION_BT_TELEPHONY_CALL_CALLBACKS 0 uint32_t version; - void (*answer)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error); - void (*hangup)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error); + void (*answer)(void *data, DBusMessage *m); + void (*hangup)(void *data, DBusMessage *m); }; struct spa_bt_telephony *telephony_new(struct spa_log *log, struct spa_dbus *dbus, const struct spa_dict *info); void telephony_free(struct spa_bt_telephony *telephony); +/* send a reply to any of the methods (they all return void); + this is must be called from the callbacks either in sync or async */ +void telephony_send_dbus_method_reply(struct spa_bt_telephony *telephony, + DBusMessage *m, enum spa_bt_telephony_error err, uint8_t cme_error); + /* create/destroy the ag object */ struct spa_bt_telephony_ag * telephony_ag_new(struct spa_bt_telephony *telephony,