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,