mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-01 22:58:50 -04:00
bluez5: backend-native: Support of ATA and AT+CHUP
Allow to answer, reject or terminate a call. Answering or rejecting a call can only be done on an incoming call. Terminating a call can only be done on active, dialing or alerting call.
This commit is contained in:
parent
e9b82252f7
commit
4a89a13bda
3 changed files with 238 additions and 0 deletions
|
|
@ -799,6 +799,7 @@ static bool rfcomm_hfp_ag(struct rfcomm *rfcomm, char* buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send reply to HF with the features supported by Audio Gateway (=computer) */
|
/* send reply to HF with the features supported by Audio Gateway (=computer) */
|
||||||
|
ag_features |= mm_supported_features();
|
||||||
ag_features |= SPA_BT_HFP_AG_FEATURE_HF_INDICATORS;
|
ag_features |= SPA_BT_HFP_AG_FEATURE_HF_INDICATORS;
|
||||||
rfcomm_send_reply(rfcomm, "+BRSF: %u", ag_features);
|
rfcomm_send_reply(rfcomm, "+BRSF: %u", ag_features);
|
||||||
rfcomm_send_reply(rfcomm, "OK");
|
rfcomm_send_reply(rfcomm, "OK");
|
||||||
|
|
@ -1024,6 +1025,40 @@ next_indicator:
|
||||||
} else if (spa_strstartswith(buf, "AT+APLSIRI?")) {
|
} else if (spa_strstartswith(buf, "AT+APLSIRI?")) {
|
||||||
// This command is sent when we activate Apple extensions
|
// This command is sent when we activate Apple extensions
|
||||||
rfcomm_send_reply(rfcomm, "OK");
|
rfcomm_send_reply(rfcomm, "OK");
|
||||||
|
} else if (!mm_is_available(backend->modemmanager)) {
|
||||||
|
spa_log_warn(backend->log, "RFCOMM receive command but modem not available: %s", buf);
|
||||||
|
rfcomm_send_error(rfcomm, CMEE_NO_CONNECTION_TO_PHONE);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* *****
|
||||||
|
* Following commands requires a Service Level Connection
|
||||||
|
* and acces to a modem
|
||||||
|
* ***** */
|
||||||
|
|
||||||
|
} else if (!backend->modem.network_has_service) {
|
||||||
|
spa_log_warn(backend->log, "RFCOMM receive command but network not available: %s", buf);
|
||||||
|
rfcomm_send_error(rfcomm, CMEE_NO_NETWORK_SERVICE);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* *****
|
||||||
|
* Following commands requires a Service Level Connection,
|
||||||
|
* acces to a modem and to the network
|
||||||
|
* ***** */
|
||||||
|
|
||||||
|
} else if (spa_strstartswith(buf, "ATA")) {
|
||||||
|
enum cmee_error error;
|
||||||
|
|
||||||
|
if (!mm_answer_call(backend->modemmanager, rfcomm, &error)) {
|
||||||
|
rfcomm_send_error(rfcomm, error);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (spa_strstartswith(buf, "AT+CHUP")) {
|
||||||
|
enum cmee_error error;
|
||||||
|
|
||||||
|
if (!mm_hangup_call(backend->modemmanager, rfcomm, &error)) {
|
||||||
|
rfcomm_send_error(rfcomm, error);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -2450,6 +2485,18 @@ static void set_modem_signal_strength(unsigned int strength, void *user_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void send_cmd_result(bool success, enum cmee_error error, void *user_data)
|
||||||
|
{
|
||||||
|
struct rfcomm *rfcomm = user_data;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
rfcomm_send_reply(rfcomm, "OK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfcomm_send_error(rfcomm, error);
|
||||||
|
}
|
||||||
|
|
||||||
static int backend_native_free(void *data)
|
static int backend_native_free(void *data)
|
||||||
{
|
{
|
||||||
struct impl *backend = data;
|
struct impl *backend = data;
|
||||||
|
|
@ -2516,6 +2563,7 @@ static const struct spa_bt_backend_implementation backend_impl = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mm_ops mm_ops = {
|
static const struct mm_ops mm_ops = {
|
||||||
|
.send_cmd_result = send_cmd_result,
|
||||||
.set_modem_service = set_modem_service,
|
.set_modem_service = set_modem_service,
|
||||||
.set_modem_signal_strength = set_modem_signal_strength,
|
.set_modem_signal_strength = set_modem_signal_strength,
|
||||||
.set_modem_operator_name = set_modem_operator_name,
|
.set_modem_operator_name = set_modem_operator_name,
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,12 @@ struct impl {
|
||||||
struct spa_list call_list;
|
struct spa_list call_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dbus_cmd_data {
|
||||||
|
struct impl *this;
|
||||||
|
struct call *call;
|
||||||
|
void *user_data;
|
||||||
|
};
|
||||||
|
|
||||||
static bool mm_dbus_connection_send_with_reply(struct impl *this, DBusMessage *m, DBusPendingCall **pending_return,
|
static bool mm_dbus_connection_send_with_reply(struct impl *this, DBusMessage *m, DBusPendingCall **pending_return,
|
||||||
DBusPendingCallNotifyFunction function, void *user_data)
|
DBusPendingCallNotifyFunction function, void *user_data)
|
||||||
{
|
{
|
||||||
|
|
@ -738,6 +744,161 @@ finish:
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mm_is_available(void *modemmanager)
|
||||||
|
{
|
||||||
|
struct impl *this = modemmanager;
|
||||||
|
|
||||||
|
if (this == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return this->modem.path != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int mm_supported_features()
|
||||||
|
{
|
||||||
|
return SPA_BT_HFP_AG_FEATURE_REJECT_CALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mm_get_call_simple_reply(DBusPendingCall *pending, void *data)
|
||||||
|
{
|
||||||
|
struct dbus_cmd_data *dbus_cmd_data = data;
|
||||||
|
struct impl *this = dbus_cmd_data->this;
|
||||||
|
struct call *call = dbus_cmd_data->call;
|
||||||
|
void *user_data = dbus_cmd_data->user_data;
|
||||||
|
DBusMessage *r;
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
|
||||||
|
spa_assert(call->pending == pending);
|
||||||
|
dbus_pending_call_unref(pending);
|
||||||
|
call->pending = NULL;
|
||||||
|
|
||||||
|
r = dbus_pending_call_steal_reply(pending);
|
||||||
|
if (r == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
|
||||||
|
spa_log_warn(this->log, "ModemManager D-Bus method not available");
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
|
||||||
|
spa_log_error(this->log, "ModemManager method failed: %s", dbus_message_get_error_name(r));
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->ops->send_cmd_result(true, 0, user_data);
|
||||||
|
return;
|
||||||
|
|
||||||
|
finish:
|
||||||
|
this->ops->send_cmd_result(false, CMEE_AG_FAILURE, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mm_answer_call(void *modemmanager, void *user_data, enum cmee_error *error)
|
||||||
|
{
|
||||||
|
struct impl *this = modemmanager;
|
||||||
|
struct call *call_object, *call_tmp;
|
||||||
|
struct dbus_cmd_data *data;
|
||||||
|
DBusMessage *m;
|
||||||
|
|
||||||
|
call_object = NULL;
|
||||||
|
spa_list_for_each(call_tmp, &this->call_list, link) {
|
||||||
|
if (call_tmp->state == MM_CALL_STATE_RINGING_IN) {
|
||||||
|
call_object = call_tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!call_object) {
|
||||||
|
spa_log_debug(this->log, "No ringing in call");
|
||||||
|
if (error)
|
||||||
|
*error = CMEE_OPERATION_NOT_ALLOWED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = malloc(sizeof(struct dbus_cmd_data));
|
||||||
|
if (!data) {
|
||||||
|
if (error)
|
||||||
|
*error = CMEE_AG_FAILURE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
data->this = this;
|
||||||
|
data->call = call_object;
|
||||||
|
data->user_data = user_data;
|
||||||
|
|
||||||
|
m = dbus_message_new_method_call(MM_DBUS_SERVICE, call_object->path, MM_DBUS_INTERFACE_CALL, MM_CALL_METHOD_ACCEPT);
|
||||||
|
if (m == NULL) {
|
||||||
|
if (error)
|
||||||
|
*error = CMEE_AG_FAILURE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!mm_dbus_connection_send_with_reply(this, m, &call_object->pending, mm_get_call_simple_reply, data)) {
|
||||||
|
spa_log_error(this->log, "dbus call failure");
|
||||||
|
dbus_message_unref(m);
|
||||||
|
if (error)
|
||||||
|
*error = CMEE_AG_FAILURE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mm_hangup_call(void *modemmanager, void *user_data, enum cmee_error *error)
|
||||||
|
{
|
||||||
|
struct impl *this = modemmanager;
|
||||||
|
struct call *call_object, *call_tmp;
|
||||||
|
struct dbus_cmd_data *data;
|
||||||
|
DBusMessage *m;
|
||||||
|
|
||||||
|
call_object = NULL;
|
||||||
|
spa_list_for_each(call_tmp, &this->call_list, link) {
|
||||||
|
if (call_tmp->state == MM_CALL_STATE_ACTIVE) {
|
||||||
|
call_object = call_tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!call_object) {
|
||||||
|
spa_list_for_each(call_tmp, &this->call_list, link) {
|
||||||
|
if (call_tmp->state == MM_CALL_STATE_RINGING_OUT ||
|
||||||
|
call_tmp->state == MM_CALL_STATE_RINGING_IN ||
|
||||||
|
call_tmp->state == MM_CALL_STATE_DIALING) {
|
||||||
|
call_object = call_tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!call_object) {
|
||||||
|
spa_log_debug(this->log, "No call to reject or hang up");
|
||||||
|
if (error)
|
||||||
|
*error = CMEE_OPERATION_NOT_ALLOWED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = malloc(sizeof(struct dbus_cmd_data));
|
||||||
|
if (!data) {
|
||||||
|
if (error)
|
||||||
|
*error = CMEE_AG_FAILURE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
data->this = this;
|
||||||
|
data->call = call_object;
|
||||||
|
data->user_data = user_data;
|
||||||
|
|
||||||
|
m = dbus_message_new_method_call(MM_DBUS_SERVICE, call_object->path, MM_DBUS_INTERFACE_CALL, MM_CALL_METHOD_HANGUP);
|
||||||
|
if (m == NULL) {
|
||||||
|
if (error)
|
||||||
|
*error = CMEE_AG_FAILURE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!mm_dbus_connection_send_with_reply(this, m, &call_object->pending, mm_get_call_simple_reply, data)) {
|
||||||
|
spa_log_error(this->log, "dbus call failure");
|
||||||
|
dbus_message_unref(m);
|
||||||
|
if (error)
|
||||||
|
*error = CMEE_AG_FAILURE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void *mm_register(struct spa_log *log, void *dbus_connection, const struct mm_ops *ops, void *user_data)
|
void *mm_register(struct spa_log *log, void *dbus_connection, const struct mm_ops *ops, void *user_data)
|
||||||
{
|
{
|
||||||
struct impl *this;
|
struct impl *this;
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ enum call_setup {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mm_ops {
|
struct mm_ops {
|
||||||
|
void (*send_cmd_result)(bool success, enum cmee_error error, void *user_data);
|
||||||
void (*set_modem_service)(bool available, void *user_data);
|
void (*set_modem_service)(bool available, void *user_data);
|
||||||
void (*set_modem_signal_strength)(unsigned int strength, void *user_data);
|
void (*set_modem_signal_strength)(unsigned int strength, void *user_data);
|
||||||
void (*set_modem_operator_name)(const char *name, void *user_data);
|
void (*set_modem_operator_name)(const char *name, void *user_data);
|
||||||
|
|
@ -56,6 +57,10 @@ struct mm_ops {
|
||||||
#ifdef HAVE_BLUEZ_5_BACKEND_NATIVE_MM
|
#ifdef HAVE_BLUEZ_5_BACKEND_NATIVE_MM
|
||||||
void *mm_register(struct spa_log *log, void *dbus_connection, const struct mm_ops *ops, void *user_data);
|
void *mm_register(struct spa_log *log, void *dbus_connection, const struct mm_ops *ops, void *user_data);
|
||||||
void mm_unregister(void *data);
|
void mm_unregister(void *data);
|
||||||
|
bool mm_is_available(void *modemmanager);
|
||||||
|
unsigned int mm_supported_features();
|
||||||
|
bool mm_answer_call(void *modemmanager, void *user_data, enum cmee_error *error);
|
||||||
|
bool mm_hangup_call(void *modemmanager, void *user_data, enum cmee_error *error);
|
||||||
#else
|
#else
|
||||||
void *mm_register(struct spa_log *log, void *dbus_connection, const struct mm_ops *ops, void *user_data)
|
void *mm_register(struct spa_log *log, void *dbus_connection, const struct mm_ops *ops, void *user_data)
|
||||||
{
|
{
|
||||||
|
|
@ -65,6 +70,30 @@ void *mm_register(struct spa_log *log, void *dbus_connection, const struct mm_op
|
||||||
void mm_unregister(void *data)
|
void mm_unregister(void *data)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool mm_is_available(void *modemmanager)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int mm_supported_features()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mm_answer_call(void *modemmanager, void *user_data, enum cmee_error *error)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
*error = CMEE_OPERATION_NOT_SUPPORTED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mm_hangup_call(void *modemmanager, void *user_data, enum cmee_error *error)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
*error = CMEE_OPERATION_NOT_SUPPORTED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue