diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c index 981cb33b0..085804a2f 100644 --- a/spa/plugins/bluez5/backend-native.c +++ b/spa/plugins/bluez5/backend-native.c @@ -131,6 +131,7 @@ enum hfp_hf_state { hfp_hf_chld, hfp_hf_clip, hfp_hf_ccwa, + hfp_hf_cmee, hfp_hf_slc1, hfp_hf_slc2, hfp_hf_vgs, @@ -183,6 +184,7 @@ struct rfcomm { unsigned int clip_notify:1; unsigned int hfp_hf_3way:1; unsigned int hfp_hf_clcc:1; + unsigned int hfp_hf_cme:1; unsigned int hfp_hf_in_progress:1; unsigned int chld_supported:1; enum hfp_hf_state hf_state; @@ -1313,7 +1315,8 @@ static bool hfp_hf_wait_for_reply(struct rfcomm *rfcomm, char *buf, size_t len) continue; spa_log_debug(backend->log, "RFCOMM event: %s", token); - if (spa_strstartswith(token, "OK") || spa_strstartswith(token, "ERROR")) { + 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); @@ -1328,12 +1331,23 @@ static bool hfp_hf_wait_for_reply(struct rfcomm *rfcomm, char *buf, size_t len) return reply_found; } -static void hfp_hf_answer(void *data, enum spa_bt_telephony_error *err) +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) { 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; @@ -1341,21 +1355,26 @@ static void hfp_hf_answer(void *data, enum spa_bt_telephony_error *err) } rfcomm_send_cmd(rfcomm, "ATA"); - if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + 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"); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; return; } *err = BT_TELEPHONY_ERROR_NONE; } -static void hfp_hf_hangup(void *data, enum spa_bt_telephony_error *err) +static void hfp_hf_hangup(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) { struct rfcomm_call_data *call_data = data; struct rfcomm *rfcomm = call_data->rfcomm; struct impl *backend = rfcomm->backend; char reply[20]; + bool res; switch (call_data->call->state) { case CALL_STATE_ACTIVE: @@ -1373,9 +1392,13 @@ static void hfp_hf_hangup(void *data, enum spa_bt_telephony_error *err) return; } - if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + 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"); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; return; } @@ -1409,25 +1432,30 @@ 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) +static void hfp_hf_dial(void *data, const char *number, enum spa_bt_telephony_error *err, uint8_t *cme_error) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; char reply[20]; + bool res; spa_log_info(backend->log, "Dialing: \"%s\"", number); rfcomm_send_cmd(rfcomm, "ATD%s;", number); - if (hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) && spa_strstartswith(reply, "OK")) { + res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); + if (res && spa_strstartswith(reply, "OK")) { struct spa_bt_telephony_call *call; call = hfp_hf_add_call(rfcomm, rfcomm->telephony_ag, CALL_STATE_DIALING, number); *err = call ? BT_TELEPHONY_ERROR_NONE : BT_TELEPHONY_ERROR_FAILED; } else { spa_log_info(backend->log, "Failed to dial: \"%s\"", number); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; } } -static void hfp_hf_swap_calls(void *data, enum spa_bt_telephony_error *err) +static void hfp_hf_swap_calls(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1435,6 +1463,7 @@ static void hfp_hf_swap_calls(void *data, enum spa_bt_telephony_error *err) bool found_active = false; bool found_held = false; char reply[20]; + bool res; if (!rfcomm->chld_supported) { *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; @@ -1462,9 +1491,13 @@ static void hfp_hf_swap_calls(void *data, enum spa_bt_telephony_error *err) } rfcomm_send_cmd(rfcomm, "AT+CHLD=2"); - if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + 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"); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; return; } @@ -1472,7 +1505,7 @@ static void hfp_hf_swap_calls(void *data, enum spa_bt_telephony_error *err) *err = BT_TELEPHONY_ERROR_NONE; } -static void hfp_hf_release_and_answer(void *data, enum spa_bt_telephony_error *err) +static void hfp_hf_release_and_answer(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1480,6 +1513,7 @@ static void hfp_hf_release_and_answer(void *data, enum spa_bt_telephony_error *e bool found_active = false; bool found_waiting = false; char reply[20]; + bool res; if (!rfcomm->chld_supported) { *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; @@ -1503,9 +1537,13 @@ static void hfp_hf_release_and_answer(void *data, enum spa_bt_telephony_error *e } rfcomm_send_cmd(rfcomm, "AT+CHLD=1"); - if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + 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"); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; return; } @@ -1513,7 +1551,7 @@ static void hfp_hf_release_and_answer(void *data, enum spa_bt_telephony_error *e *err = BT_TELEPHONY_ERROR_NONE; } -static void hfp_hf_release_and_swap(void *data, enum spa_bt_telephony_error *err) +static void hfp_hf_release_and_swap(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1521,6 +1559,7 @@ static void hfp_hf_release_and_swap(void *data, enum spa_bt_telephony_error *err bool found_active = false; bool found_held = false; char reply[20]; + bool res; if (!rfcomm->chld_supported) { *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; @@ -1548,9 +1587,13 @@ static void hfp_hf_release_and_swap(void *data, enum spa_bt_telephony_error *err } rfcomm_send_cmd(rfcomm, "AT+CHLD=1"); - if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + 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"); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; return; } @@ -1558,7 +1601,7 @@ static void hfp_hf_release_and_swap(void *data, enum spa_bt_telephony_error *err *err = BT_TELEPHONY_ERROR_NONE; } -static void hfp_hf_hold_and_answer(void *data, enum spa_bt_telephony_error *err) +static void hfp_hf_hold_and_answer(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1566,6 +1609,7 @@ static void hfp_hf_hold_and_answer(void *data, enum spa_bt_telephony_error *err) bool found_active = false; bool found_waiting = false; char reply[20]; + bool res; if (!rfcomm->chld_supported) { *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; @@ -1589,9 +1633,13 @@ static void hfp_hf_hold_and_answer(void *data, enum spa_bt_telephony_error *err) } rfcomm_send_cmd(rfcomm, "AT+CHLD=2"); - if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + 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"); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; return; } @@ -1599,7 +1647,7 @@ static void hfp_hf_hold_and_answer(void *data, enum spa_bt_telephony_error *err) *err = BT_TELEPHONY_ERROR_NONE; } -static void hfp_hf_hangup_all(void *data, enum spa_bt_telephony_error *err) +static void hfp_hf_hangup_all(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1607,6 +1655,7 @@ static void hfp_hf_hangup_all(void *data, enum spa_bt_telephony_error *err) 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) { @@ -1630,23 +1679,31 @@ static void hfp_hf_hangup_all(void *data, enum spa_bt_telephony_error *err) /* Hangup held calls */ if (found_held) { rfcomm_send_cmd(rfcomm, "AT+CHLD=0"); - if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + 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"); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; } } /* Hangup active calls */ if (found_active) { rfcomm_send_cmd(rfcomm, "AT+CHUP"); - if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + 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"); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; } } } -static void hfp_hf_create_multiparty(void *data, enum spa_bt_telephony_error *err) +static void hfp_hf_create_multiparty(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1654,6 +1711,7 @@ static void hfp_hf_create_multiparty(void *data, enum spa_bt_telephony_error *er bool found_active = false; bool found_held = false; char reply[20]; + bool res; if (!rfcomm->chld_supported) { *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; @@ -1681,9 +1739,13 @@ static void hfp_hf_create_multiparty(void *data, enum spa_bt_telephony_error *er } rfcomm_send_cmd(rfcomm, "AT+CHLD=3"); - if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + 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"); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; return; } @@ -1691,13 +1753,14 @@ static void hfp_hf_create_multiparty(void *data, enum spa_bt_telephony_error *er *err = BT_TELEPHONY_ERROR_NONE; } -static void hfp_hf_send_tones(void *data, const char *tones, enum spa_bt_telephony_error *err) +static void hfp_hf_send_tones(void *data, const char *tones, enum spa_bt_telephony_error *err, uint8_t *cme_error) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; 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) { @@ -1713,20 +1776,25 @@ static void hfp_hf_send_tones(void *data, const char *tones, enum spa_bt_telepho } rfcomm_send_cmd(rfcomm, "AT+VTS=%s", tones); - if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + 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); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; return; } *err = BT_TELEPHONY_ERROR_NONE; } -static void hfp_hf_transport_activate(void *data, enum spa_bt_telephony_error *err) +static void hfp_hf_transport_activate(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; char reply[20]; + bool res; if (spa_list_is_empty(&rfcomm->telephony_ag->call_list)) { spa_log_debug(backend->log, "no ongoing call"); @@ -1740,9 +1808,13 @@ static void hfp_hf_transport_activate(void *data, enum spa_bt_telephony_error *e } rfcomm_send_cmd(rfcomm, "AT+BCC"); - if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + 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"); - *err = BT_TELEPHONY_ERROR_FAILED; + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; return; } @@ -1774,6 +1846,7 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) rfcomm->codec_negotiation_supported = true; rfcomm->hfp_hf_3way = (features & SPA_BT_HFP_AG_FEATURE_3WAY) != 0; rfcomm->hfp_hf_clcc = (features & SPA_BT_HFP_AG_FEATURE_ENHANCED_CALL_STATUS) != 0; + rfcomm->hfp_hf_cme = (features & SPA_BT_HFP_AG_FEATURE_EXTENDED_RES_CODE) != 0; } else if (sscanf(token, "+BCS:%u", &selected_codec) == 1 && rfcomm->codec_negotiation_supported) { if (selected_codec != HFP_AUDIO_CODEC_CVSD && selected_codec != HFP_AUDIO_CODEC_MSBC && selected_codec != HFP_AUDIO_CODEC_LC3_SWB) { @@ -2175,6 +2248,13 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) } SPA_FALLTHROUGH; case hfp_hf_ccwa: + if (rfcomm->hfp_hf_cme) { + rfcomm_send_cmd(rfcomm, "AT+CMEE=1"); + rfcomm->hf_state = hfp_hf_cmee; + break; + } + SPA_FALLTHROUGH; + case hfp_hf_cmee: rfcomm->hf_state = hfp_hf_slc1; rfcomm->slc_configured = true; diff --git a/spa/plugins/bluez5/telephony.c b/spa/plugins/bluez5/telephony.c index ce43987c7..c2f9d6741 100644 --- a/spa/plugins/bluez5/telephony.c +++ b/spa/plugins/bluez5/telephony.c @@ -225,20 +225,20 @@ 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) ag_emit(s,dial,0,n,e) -#define ag_emit_swap_calls(s,e) ag_emit(s,swap_calls,0,e) -#define ag_emit_release_and_answer(s,e) ag_emit(s,release_and_answer,0,e) -#define ag_emit_release_and_swap(s,e) ag_emit(s,release_and_swap,0,e) -#define ag_emit_hold_and_answer(s,e) ag_emit(s,hold_and_answer,0,e) -#define ag_emit_hangup_all(s,e) ag_emit(s,hangup_all,0,e) -#define ag_emit_create_multiparty(s,e) ag_emit(s,create_multiparty,0,e) -#define ag_emit_send_tones(s,t,e) ag_emit(s,send_tones,0,t,e) -#define ag_emit_transport_activate(s,e) ag_emit(s,transport_activate,0,e) +#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 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) call_emit(s,answer,0,e) -#define call_emit_hangup(s,e) call_emit(s,hangup,0,e) +#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) 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); @@ -248,6 +248,7 @@ static void dbus_iter_append_call_properties(DBusMessageIter *i, struct spa_bt_t #define PW_TELEPHONY_ERROR_INVALID_FORMAT "org.pipewire.Telephony.Error.InvalidFormat" #define PW_TELEPHONY_ERROR_INVALID_STATE "org.pipewire.Telephony.Error.InvalidState" #define PW_TELEPHONY_ERROR_IN_PROGRESS "org.pipewire.Telephony.Error.InProgress" +#define PW_TELEPHONY_ERROR_CME "org.pipewire.Telephony.Error.CME" static const char *telephony_error_to_dbus (enum spa_bt_telephony_error err) { @@ -262,12 +263,14 @@ static const char *telephony_error_to_dbus (enum spa_bt_telephony_error err) return PW_TELEPHONY_ERROR_INVALID_STATE; case BT_TELEPHONY_ERROR_IN_PROGRESS: return PW_TELEPHONY_ERROR_IN_PROGRESS; + case BT_TELEPHONY_ERROR_CME: + return PW_TELEPHONY_ERROR_CME; default: return ""; } } -static const char *telephony_error_to_description (enum spa_bt_telephony_error err) +static const char *telephony_error_to_description (enum spa_bt_telephony_error err, uint8_t cme_error) { switch (err) { case BT_TELEPHONY_ERROR_FAILED: @@ -280,6 +283,33 @@ static const char *telephony_error_to_description (enum spa_bt_telephony_error e return "The current state does not allow this method call"; case BT_TELEPHONY_ERROR_IN_PROGRESS: return "Command already in progress"; + case BT_TELEPHONY_ERROR_CME: + switch (cme_error) { + case 0: return "AG failure"; + case 1: return "no connection to phone"; + case 3: return "operation not allowed"; + case 4: return "operation not supported"; + case 5: return "PH-SIM PIN required"; + case 10: return "SIM not inserted"; + case 11: return "SIM PIN required"; + case 12: return "SIM PUK required"; + case 13: return "SIM failure"; + case 14: return "SIM busy"; + case 16: return "incorrect password"; + case 17: return "SIM PIN2 required"; + case 18: return "SIM PUK2 required"; + case 20: return "memory full"; + case 21: return "invalid index"; + case 23: return "memory failure"; + case 24: return "text string too long"; + case 25: return "invalid characters in text string"; + case 26: return "dial string too long"; + case 27: return "invalid characters in dial string"; + case 30: return "no network service"; + case 31: return "network Timeout"; + case 32: return "network not allowed - Emergency calls only"; + default: return "Unknown CME error"; + } default: return ""; } @@ -816,6 +846,7 @@ 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, @@ -829,7 +860,7 @@ static DBusMessage *ag_dial(struct agimpl *agimpl, DBusMessage *m) } agimpl->dial_in_progress = true; - if (!ag_emit_dial(agimpl, number, &err)) { + if (!ag_emit_dial(agimpl, number, &err, &cme_error)) { agimpl->dial_in_progress = false; goto failed; } @@ -853,79 +884,86 @@ static DBusMessage *ag_dial(struct agimpl *agimpl, DBusMessage *m) failed: return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err)); + telephony_error_to_description (err, cme_error)); } 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) && err == BT_TELEPHONY_ERROR_NONE) + 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)); + telephony_error_to_description (err, cme_error)); } 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) && err == BT_TELEPHONY_ERROR_NONE) + 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)); + telephony_error_to_description (err, cme_error)); } 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) && err == BT_TELEPHONY_ERROR_NONE) + 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)); + telephony_error_to_description (err, cme_error)); } 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) && err == BT_TELEPHONY_ERROR_NONE) + 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)); + telephony_error_to_description (err, cme_error)); } 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) && err == BT_TELEPHONY_ERROR_NONE) + 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)); + telephony_error_to_description (err, cme_error)); } 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) && err == BT_TELEPHONY_ERROR_NONE) + 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)); + telephony_error_to_description (err, cme_error)); } 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, @@ -937,23 +975,24 @@ static DBusMessage *ag_send_tones(struct agimpl *agimpl, DBusMessage *m) goto failed; } - if (ag_emit_send_tones(agimpl, tones, &err) && err == BT_TELEPHONY_ERROR_NONE) + if (ag_emit_send_tones(agimpl, tones, &err, &cme_error) && err == BT_TELEPHONY_ERROR_NONE) return dbus_message_new_method_return(m); failed: return dbus_message_new_error(m, telephony_error_to_dbus (err), - telephony_error_to_description (err)); + telephony_error_to_description (err, cme_error)); } 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) && err == BT_TELEPHONY_ERROR_NONE) + 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)); + telephony_error_to_description (err, cme_error)); } static DBusHandlerResult ag_handler(DBusConnection *c, DBusMessage *m, void *userdata) @@ -1488,23 +1527,25 @@ 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) && err == BT_TELEPHONY_ERROR_NONE) + 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)); + telephony_error_to_description (err, cme_error)); } 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) && err == BT_TELEPHONY_ERROR_NONE) + 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)); + telephony_error_to_description (err, cme_error)); } static DBusHandlerResult call_handler(DBusConnection *c, DBusMessage *m, void *userdata) diff --git a/spa/plugins/bluez5/telephony.h b/spa/plugins/bluez5/telephony.h index d03ea8346..8ba85ec47 100644 --- a/spa/plugins/bluez5/telephony.h +++ b/spa/plugins/bluez5/telephony.h @@ -14,6 +14,7 @@ enum spa_bt_telephony_error { BT_TELEPHONY_ERROR_INVALID_FORMAT, BT_TELEPHONY_ERROR_INVALID_STATE, BT_TELEPHONY_ERROR_IN_PROGRESS, + BT_TELEPHONY_ERROR_CME, }; enum spa_bt_telephony_call_state { @@ -65,24 +66,24 @@ 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); - void (*swap_calls)(void *data, enum spa_bt_telephony_error *err); - void (*release_and_answer)(void *data, enum spa_bt_telephony_error *err); - void (*release_and_swap)(void *data, enum spa_bt_telephony_error *err); - void (*hold_and_answer)(void *data, enum spa_bt_telephony_error *err); - void (*hangup_all)(void *data, enum spa_bt_telephony_error *err); - void (*create_multiparty)(void *data, enum spa_bt_telephony_error *err); - void (*send_tones)(void *data, const char *tones, enum spa_bt_telephony_error *err); + 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 (*transport_activate)(void *data, enum spa_bt_telephony_error *err); + void (*transport_activate)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error); }; 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); - void (*hangup)(void *data, enum spa_bt_telephony_error *err); + 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); }; struct spa_bt_telephony *telephony_new(struct spa_log *log, struct spa_dbus *dbus,