bluez: backend-ofono: advertise transport state for incoming connections

This commit is contained in:
George Kiagiadakis 2021-03-18 22:46:56 +02:00 committed by Wim Taymans
parent f9b39191c4
commit 343be7503a

View file

@ -54,6 +54,10 @@ struct spa_bt_backend {
unsigned int msbc_supported:1; unsigned int msbc_supported:1;
}; };
struct transport_data {
struct spa_source sco;
};
#define OFONO_HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager" #define OFONO_HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager"
#define OFONO_HF_AUDIO_CARD_INTERFACE OFONO_SERVICE ".HandsfreeAudioCard" #define OFONO_HF_AUDIO_CARD_INTERFACE OFONO_SERVICE ".HandsfreeAudioCard"
#define OFONO_HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent" #define OFONO_HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent"
@ -80,6 +84,7 @@ struct spa_bt_backend {
"</node>" "</node>"
#define OFONO_ERROR_INVALID_ARGUMENTS "org.ofono.Error.InvalidArguments" #define OFONO_ERROR_INVALID_ARGUMENTS "org.ofono.Error.InvalidArguments"
#define OFONO_ERROR_NOT_IMPLEMENTED "org.ofono.Error.NotImplemented"
#define OFONO_ERROR_IN_USE "org.ofono.Error.InUse" #define OFONO_ERROR_IN_USE "org.ofono.Error.InUse"
static void ofono_transport_get_mtu(struct spa_bt_backend *backend, struct spa_bt_transport *t) static void ofono_transport_get_mtu(struct spa_bt_backend *backend, struct spa_bt_transport *t)
@ -113,7 +118,7 @@ static struct spa_bt_transport *_transport_create(struct spa_bt_backend *backend
struct spa_bt_transport *t = NULL; struct spa_bt_transport *t = NULL;
char *t_path = strdup(path); char *t_path = strdup(path);
t = spa_bt_transport_create(backend->monitor, t_path, 0); t = spa_bt_transport_create(backend->monitor, t_path, sizeof(struct transport_data));
if (t == NULL) { if (t == NULL) {
spa_log_warn(backend->log, NAME": can't create transport: %m"); spa_log_warn(backend->log, NAME": can't create transport: %m");
free(t_path); free(t_path);
@ -186,6 +191,9 @@ static int ofono_audio_acquire(void *data, bool optional)
uint8_t codec; uint8_t codec;
int ret = 0; int ret = 0;
if (transport->fd)
goto finish;
ret = _audio_acquire(backend, transport->path, &codec); ret = _audio_acquire(backend, transport->path, &codec);
if (ret < 0) if (ret < 0)
goto finish; goto finish;
@ -283,8 +291,7 @@ static DBusHandlerResult ofono_audio_card_found(struct spa_bt_backend *backend,
struct spa_bt_device *d; struct spa_bt_device *d;
struct spa_bt_transport *t; struct spa_bt_transport *t;
enum spa_bt_profile profile = SPA_BT_PROFILE_HFP_AG; enum spa_bt_profile profile = SPA_BT_PROFILE_HFP_AG;
int fd; uint8_t codec = HFP_AUDIO_CODEC_CVSD;
uint8_t codec;
spa_assert(backend); spa_assert(backend);
spa_assert(path); spa_assert(path);
@ -324,14 +331,23 @@ static DBusHandlerResult ofono_audio_card_found(struct spa_bt_backend *backend,
dbus_message_iter_next(props_i); dbus_message_iter_next(props_i);
} }
fd = _audio_acquire(backend, path, &codec); /*
if (fd < 0) { * Acquire and close immediately to figure out the codec.
spa_log_error(backend->log, NAME": Failed to retrieve codec for %s", path); * This is necessary if we are in HF mode, because we need to emit
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; * nodes and the advertised sample rate of the node depends on the codec.
* For AG mode, we delay the emission of the nodes, so it is not necessary
* to know the codec in advance
*/
if (profile == SPA_BT_PROFILE_HFP_HF) {
int fd = _audio_acquire(backend, path, &codec);
if (fd < 0) {
spa_log_error(backend->log, NAME": Failed to retrieve codec for %s", path);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
/* shutdown to make sure connection is dropped immediately */
shutdown(fd, SHUT_RDWR);
close(fd);
} }
/* shutdown to make sure connection is dropped immediately */
shutdown(fd, SHUT_RDWR);
close(fd);
d = spa_bt_device_find_by_address(backend->monitor, remote_address, local_address); d = spa_bt_device_find_by_address(backend->monitor, remote_address, local_address);
if (!d) { if (!d) {
@ -366,6 +382,24 @@ static DBusHandlerResult ofono_release(DBusConnection *conn, DBusMessage *m, voi
return DBUS_HANDLER_RESULT_HANDLED; return DBUS_HANDLER_RESULT_HANDLED;
} }
static void sco_event(struct spa_source *source)
{
struct spa_bt_transport *t = source->data;
struct spa_bt_backend *backend = t->backend;
if (source->rmask & (SPA_IO_HUP | SPA_IO_ERR)) {
spa_log_debug(backend->log, NAME": transport %p: error on SCO socket: %s", t, strerror(errno));
if (t->fd >= 0) {
if (source->loop)
spa_loop_remove_source(source->loop, source);
shutdown(t->fd, SHUT_RDWR);
close (t->fd);
t->fd = -1;
spa_bt_transport_set_state(t, SPA_BT_TRANSPORT_STATE_IDLE);
}
}
}
static DBusHandlerResult ofono_new_audio_connection(DBusConnection *conn, DBusMessage *m, void *userdata) static DBusHandlerResult ofono_new_audio_connection(DBusConnection *conn, DBusMessage *m, void *userdata)
{ {
struct spa_bt_backend *backend = userdata; struct spa_bt_backend *backend = userdata;
@ -373,6 +407,7 @@ static DBusHandlerResult ofono_new_audio_connection(DBusConnection *conn, DBusMe
int fd; int fd;
uint8_t codec; uint8_t codec;
struct spa_bt_transport *t; struct spa_bt_transport *t;
struct transport_data *td;
DBusMessage *r = NULL; DBusMessage *r = NULL;
if (dbus_message_get_args(m, NULL, if (dbus_message_get_args(m, NULL,
@ -385,34 +420,30 @@ static DBusHandlerResult ofono_new_audio_connection(DBusConnection *conn, DBusMe
} }
t = spa_bt_transport_find(backend->monitor, path); t = spa_bt_transport_find(backend->monitor, path);
if (t && (t->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY)) {
t->fd = fd;
t->codec = codec;
if (!t || t->codec != codec || t->fd >= 0) { spa_log_debug(backend->log, NAME": transport %p: NewConnection %s, fd %d codec %d",
spa_log_warn(backend->log, NAME": New audio connection invalid " t, t->path, t->fd, t->codec);
"arguments (path=%s fd=%d, codec=%d)", path, fd, codec);
r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call");
shutdown(fd, SHUT_RDWR);
close(fd);
if (t->codec != codec) { td = t->user_data;
struct spa_bt_transport *transport = NULL; td->sco.func = sco_event;
td->sco.data = t;
td->sco.fd = fd;
td->sco.mask = SPA_IO_HUP | SPA_IO_ERR;
td->sco.rmask = 0;
spa_loop_add_source(backend->main_loop, &td->sco);
spa_log_warn(backend->log, NAME": Acquired codec (%d) differs from transport one (%d)", ofono_transport_get_mtu(backend, t);
codec, t->codec); spa_bt_transport_set_state (t, SPA_BT_TRANSPORT_STATE_PENDING);
}
/* Create a new transport which differs only for codec */ else if (fd) {
transport = _transport_create(backend, t->path, t->device, t->profile, codec, &t->impl); spa_log_debug(backend->log, NAME": ignoring NewConnection");
spa_bt_transport_free(t); r = dbus_message_new_error(m, OFONO_ERROR_NOT_IMPLEMENTED, "Method not implemented");
spa_bt_device_connect_profile(transport->device, transport->profile); shutdown(fd, SHUT_RDWR);
} close(fd);
goto fail;
} }
t->fd = fd;
ofono_transport_get_mtu(backend, t);
spa_log_debug(backend->log, NAME": transport %p: NewConnection %s, fd %d codec %d", t, t->path, t->fd, t->codec);
/* TODO: pass fd to SCO nodes */
fail: fail:
if (r) { if (r) {