bluez5: refcount transport acquire and release, let it manage fd

Backends don't necessarily allow for opening the same device multiple
times, and it shouldn't be necessary.

Since source and sink are not necessarily both running at the same time,
refcount the transport acquire/release so that it knows to close the fd
only when no source/sink is running.

Let the transport manage the fd lifecycle, also closing it once it is
not needed.

Don't return the fd from acquire(), since each transport is associated
with a single socket fd.
This commit is contained in:
Pauli Virtanen 2020-12-22 20:36:04 +02:00 committed by Wim Taymans
parent 8942f6b402
commit 036c10717d
7 changed files with 85 additions and 51 deletions

View file

@ -233,6 +233,7 @@ static int sco_acquire_cb(void *data, bool optional)
if (sock < 0)
goto fail;
t->fd = sock;
t->read_mtu = 48;
t->write_mtu = 48;
@ -250,7 +251,9 @@ static int sco_acquire_cb(void *data, bool optional)
t->write_mtu = sco_opt.mtu;
}
}
return sock;
return 0;
fail:
return -1;
}
@ -259,8 +262,14 @@ static int sco_release_cb(void *data)
{
struct spa_bt_transport *t = data;
struct spa_bt_backend *backend = t->backend;
spa_log_info(backend->log, NAME": Transport %s released", t->path);
/* device will close the SCO socket for us */
spa_log_info(backend->log, "Transport %s released", t->path);
/* Shutdown and close the socket */
shutdown(t->fd, SHUT_RDWR);
close(t->fd);
t->fd = -1;
return 0;
}

View file

@ -921,6 +921,9 @@ static int hsphfpd_audio_acquire(void *data, bool optional)
DBusPendingCall *call;
DBusError err;
spa_log_debug(backend->log, NAME": transport %p: Acquire %s",
transport, transport->path);
if (backend->acquire_in_progress)
return -EINPROGRESS;
@ -950,7 +953,7 @@ static int hsphfpd_audio_acquire(void *data, bool optional)
while (backend->acquire_in_progress && dbus_connection_read_write_dispatch(backend->conn, -1))
; // empty loop body
return transport->fd;
return 0;
}
static int hsphfpd_audio_release(void *data)
@ -959,11 +962,6 @@ static int hsphfpd_audio_release(void *data)
struct spa_bt_backend *backend = transport->backend;
struct hsphfpd_transport_data *transport_data = transport->user_data;
if (transport->fd < 0) {
spa_log_info(backend->log, NAME": transport %s already released", transport->path);
return 0;
}
spa_log_debug(backend->log, NAME": transport %p: Release %s",
transport, transport->path);

View file

@ -232,7 +232,7 @@ static int ofono_audio_acquire(void *data, bool optional)
transport->path, transport->fd, transport->codec);
ofono_transport_get_mtu(backend, transport);
ret = transport->fd;
ret = 0;
finish:
return ret;
@ -243,11 +243,6 @@ static int ofono_audio_release(void *data)
struct spa_bt_transport *transport = data;
struct spa_bt_backend *backend = transport->backend;
if (transport->fd < 0) {
spa_log_info(backend->log, NAME": transport %s already released", transport->path);
return 0;
}
spa_log_debug(backend->log, NAME": transport %p: Release %s",
transport, transport->path);

View file

@ -686,6 +686,7 @@ struct spa_bt_transport *spa_bt_transport_create(struct spa_bt_monitor *monitor,
if (t == NULL)
return NULL;
t->acquire_refcount = 0;
t->monitor = monitor;
t->path = path;
t->fd = -1;
@ -721,6 +722,12 @@ void spa_bt_transport_free(struct spa_bt_transport *transport)
spa_bt_transport_destroy(transport);
if (transport->fd >= 0) {
shutdown(transport->fd, SHUT_RDWR);
close(transport->fd);
transport->fd = -1;
}
spa_list_remove(&transport->link);
if (transport->device) {
transport->device->connected_profiles &= ~transport->profile;
@ -730,6 +737,50 @@ void spa_bt_transport_free(struct spa_bt_transport *transport)
free(transport);
}
int spa_bt_transport_acquire(struct spa_bt_transport *transport, bool optional)
{
struct spa_bt_monitor *monitor = transport->monitor;
int res;
if (transport->acquire_refcount > 0) {
spa_log_debug(monitor->log, "transport %p: incref %s", transport, transport->path);
transport->acquire_refcount += 1;
return 0;
}
spa_assert(transport->acquire_refcount == 0);
res = spa_bt_transport_impl(transport, acquire, 0, optional);
if (res >= 0)
transport->acquire_refcount = 1;
return res;
}
int spa_bt_transport_release(struct spa_bt_transport *transport)
{
struct spa_bt_monitor *monitor = transport->monitor;
int res;
if (transport->acquire_refcount > 1) {
spa_log_debug(monitor->log, "transport %p: decref %s", transport, transport->path);
transport->acquire_refcount -= 1;
return 0;
}
else if (transport->acquire_refcount == 0) {
spa_log_info(monitor->log, "transport %s already released", transport->path);
return 0;
}
spa_assert(transport->acquire_refcount == 1);
res = spa_bt_transport_impl(transport, release, 0);
if (res >= 0)
transport->acquire_refcount = 0;
return res;
}
static int transport_update_props(struct spa_bt_transport *transport,
DBusMessageIter *props_iter,
DBusMessageIter *invalidated_iter)
@ -840,9 +891,6 @@ static int transport_acquire(void *data, bool optional)
int ret = 0;
const char *method = optional ? "TryAcquire" : "Acquire";
if (transport->fd >= 0)
return 0;
m = dbus_message_new_method_call(BLUEZ_SERVICE,
transport->path,
BLUEZ_MEDIA_TRANSPORT_INTERFACE,
@ -900,10 +948,7 @@ static int transport_release(void *data)
DBusMessage *m, *r;
DBusError err;
if (transport->fd < 0)
return 0;
spa_log_debug(monitor->log, "transport %p: Release %s",
spa_log_debug(monitor->log, NAME": transport %p: Release %s",
transport, transport->path);
close(transport->fd);

View file

@ -284,7 +284,7 @@ struct spa_bt_transport {
void *configuration;
int configuration_len;
bool acquired;
int acquire_refcount;
int fd;
uint16_t read_mtu;
uint16_t write_mtu;
@ -302,6 +302,9 @@ struct spa_bt_transport *spa_bt_transport_find_full(struct spa_bt_monitor *monit
bool (*callback) (struct spa_bt_transport *t, const void *data),
const void *data);
int spa_bt_transport_acquire(struct spa_bt_transport *t, bool optional);
int spa_bt_transport_release(struct spa_bt_transport *t);
#define spa_bt_transport_emit(t,m,v,...) spa_hook_list_call(&(t)->listener_list, \
struct spa_bt_transport_events, \
m, v, ##__VA_ARGS__)
@ -323,8 +326,6 @@ struct spa_bt_transport *spa_bt_transport_find_full(struct spa_bt_monitor *monit
res; \
})
#define spa_bt_transport_acquire(t,o) spa_bt_transport_impl(t, acquire, 0, o)
#define spa_bt_transport_release(t) spa_bt_transport_impl(t, release, 0)
#define spa_bt_transport_destroy(t) spa_bt_transport_impl(t, destroy, 0)
static inline enum spa_bt_transport_state spa_bt_transport_state_from_string(const char *value)

View file

@ -110,7 +110,6 @@ struct impl {
/* Transport */
struct spa_bt_transport *transport;
struct spa_hook transport_listener;
int sock_fd;
/* Port */
struct port port;
@ -381,7 +380,7 @@ static void flush_data(struct impl *this)
}
next_write:
written = write(this->sock_fd, packet, this->transport->write_mtu);
written = write(this->transport->fd, packet, this->transport->write_mtu);
if (written <= 0) {
spa_log_debug(this->log, "failed to write data");
goto stop;
@ -472,6 +471,7 @@ static int lcm(int a, int b) {
static int do_start(struct impl *this)
{
bool do_accept;
int res;
/* Dont do anything if the node has already started */
if (this->started)
@ -484,9 +484,8 @@ static int do_start(struct impl *this)
do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY;
/* acquire the socked fd (false -> connect | true -> accept) */
this->sock_fd = spa_bt_transport_acquire(this->transport, do_accept);
if (this->sock_fd < 0)
return -1;
if ((res = spa_bt_transport_acquire(this->transport, do_accept)) < 0)
return res;
/* Init mSBC if needed */
if (this->transport->codec == HFP_AUDIO_CODEC_MSBC) {
@ -557,13 +556,8 @@ static int do_stop(struct impl *this)
}
if (this->transport) {
/* Release the transport */
/* Release the transport; it is responsible for closing the fd */
res = spa_bt_transport_release(this->transport);
/* Shutdown and close the socket */
shutdown(this->sock_fd, SHUT_RDWR);
close(this->sock_fd);
this->sock_fd = -1;
}
return res;
@ -1141,7 +1135,6 @@ impl_init(const struct spa_handle_factory *factory,
}
spa_bt_transport_add_listener(this->transport,
&this->transport_listener, &transport_events, this);
this->sock_fd = -1;
this->timerfd = spa_system_timerfd_create(this->data_system,
CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);

View file

@ -107,7 +107,6 @@ struct impl {
struct spa_bt_transport *transport;
struct spa_hook transport_listener;
int sock_fd;
struct port port;
@ -287,7 +286,7 @@ static int read_data(struct impl *this, uint8_t *data, uint32_t data_size)
int res = 0;
again:
res = read(this->sock_fd, data, data_size);
res = read(this->transport->fd, data, data_size);
if (res <= 0) {
/* retry if interrupted */
if (errno == EINTR)
@ -513,6 +512,7 @@ stop:
static int do_start(struct impl *this)
{
bool do_accept;
int res;
/* Dont do anything if the node has already started */
if (this->started)
@ -525,9 +525,8 @@ static int do_start(struct impl *this)
do_accept = this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY;
/* acquire the socked fd (false -> connect | true -> accept) */
this->sock_fd = spa_bt_transport_acquire(this->transport, do_accept);
if (this->sock_fd < 0)
return -1;
if ((res = spa_bt_transport_acquire(this->transport, do_accept)) < 0)
return res;
/* Reset the buffers and sample count */
reset_buffers(&this->port);
@ -543,7 +542,7 @@ static int do_start(struct impl *this)
/* Add the ready read callback */
this->source.data = this;
this->source.fd = this->sock_fd;
this->source.fd = this->transport->fd;
this->source.func = sco_on_ready_read;
this->source.mask = SPA_IO_IN;
this->source.rmask = 0;
@ -584,13 +583,8 @@ static int do_stop(struct impl *this)
this->started = false;
if (this->transport) {
/* Release the transport */
/* Release the transport; it is responsible for closing the fd */
res = spa_bt_transport_release(this->transport);
/* Shutdown and close the socket */
shutdown(this->sock_fd, SHUT_RDWR);
close(this->sock_fd);
this->sock_fd = -1;
}
return res;
@ -1197,7 +1191,6 @@ impl_init(const struct spa_handle_factory *factory,
}
spa_bt_transport_add_listener(this->transport,
&this->transport_listener, &transport_events, this);
this->sock_fd = -1;
return 0;
}