for (l = s->cb; l != NULL; l = l->next) {
struct a2dp_setup_cb *cb = l->data;
- if (cb->resume_cb) {
+ if (cb && cb->resume_cb) {
cb->resume_cb(s->session, s->err, cb->user_data);
cb->resume_cb = NULL;
setup_unref(s);
guint dc_timer;
+ /* Attempt stream setup instead of disconnecting */
+ gboolean stream_setup;
+
DBusPendingCall *pending_auth;
};
static gboolean disconnect_timeout(gpointer user_data)
{
struct avdtp *session = user_data;
+ struct audio_device *dev;
+ gboolean stream_setup;
+ int i = 0;
assert(session->ref == 1);
session->dc_timer = 0;
+ stream_setup = session->stream_setup;
+ session->stream_setup = FALSE;
+ dev = manager_find_device(&session->dst, AUDIO_CONTROL_INTERFACE, FALSE);
- connection_lost(session, -ETIMEDOUT);
-
+ if (dev && dev->sink && stream_setup)
+ sink_setup_stream(dev->sink, session);
+ else
+ connection_lost(session, -ETIMEDOUT);
return FALSE;
}
{
g_source_remove(session->dc_timer);
session->dc_timer = 0;
+ session->stream_setup = FALSE;
}
static void set_disconnect_timer(struct avdtp *session)
dev = manager_find_device(&session->dst, AUDIO_CONTROL_INTERFACE,
FALSE);
- if (dev)
+ if (dev && dev->control) {
+ device_remove_control_timer(dev);
avrcp_disconnect(dev);
+ }
if (session->state == AVDTP_SESSION_STATE_CONNECTED) {
char address[18];
session->buf = g_malloc0(session->mtu);
+ session->stream_setup = TRUE;
set_disconnect_timer(session);
session->state = AVDTP_SESSION_STATE_CONNECTED;
dev = manager_find_device(&session->dst, AUDIO_CONTROL_INTERFACE,
FALSE);
- if (dev)
- avrcp_connect(dev);
+ if (dev && dev->control)
+ device_set_control_timer(dev);
g_source_remove(session->io);
struct l2cap_options l2o;
struct avctp *session;
GIOCondition flags = G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ struct audio_device *dev;
char address[18];
if (err < 0) {
goto drop;
}
+ dev = manager_find_device(&session->dst, AUDIO_CONTROL_INTERFACE, FALSE);
+
+ if (!dev) {
+ error("Unable to get audio device object for %s", address);
+ goto drop;
+ }
+
+ if (!dev->control)
+ dev->control = control_init(dev);
+
+ device_remove_control_timer(dev);
+
session->state = AVCTP_STATE_CONNECTING;
session->sock = g_io_channel_unix_get_fd(chan);
return FALSE;
}
+ device_remove_control_timer(dev);
+
session->dev = dev;
session->state = AVCTP_STATE_CONNECTING;
#include "headset.h"
#include "sink.h"
+#define CONTROL_CONNECT_TIMEOUT 2000
+
static DBusMessage *device_get_address(DBusConnection *conn,
DBusMessage *msg, void *data)
{
if (dev->conn)
dbus_connection_unref(dev->conn);
+ if (dev->control_timer)
+ g_source_remove(dev->control_timer);
+
g_free(dev->adapter_path);
g_free(dev->path);
g_free(dev->name);
device_free(device);
}
+static gboolean control_connect_timeout(gpointer user_data)
+{
+ struct audio_device *dev = user_data;
+
+ dev->control_timer = 0;
+
+ if (dev->control)
+ avrcp_connect(dev);
+
+ return FALSE;
+}
+
+gboolean device_set_control_timer(struct audio_device *dev)
+{
+ if (!dev->control)
+ return FALSE;
+
+ if (dev->control_timer)
+ return FALSE;
+
+ dev->control_timer = g_timeout_add_seconds(CONTROL_CONNECT_TIMEOUT,
+ control_connect_timeout,
+ dev);
+
+ return TRUE;
+}
+
+void device_remove_control_timer(struct audio_device *dev)
+{
+ if (dev->control_timer)
+ g_source_remove(dev->control_timer);
+ dev->control_timer = 0;
+}
+
struct audio_device *device_register(DBusConnection *conn,
const char *path, const bdaddr_t *bda)
{
struct source *source;
struct control *control;
struct target *target;
+
+ guint control_timer;
};
+gboolean device_set_control_timer(struct audio_device *dev);
+void device_remove_control_timer(struct audio_device *dev);
+
struct audio_device *device_register(DBusConnection *conn,
const char *path, const bdaddr_t *bda);
#define ENABLE_DEBUG
/* #define ENABLE_VERBOSE */
+/* #define ENABLE_TIMING */
#define BUFFER_SIZE 2048
/* Number of packets to buffer in the stream socket */
#define PACKET_BUFFER_COUNT 10
+/* timeout in milliseconds to prevent poll() from hanging indefinitely */
+#define POLL_TIMEOUT 1000
+
struct bluetooth_data {
int link_mtu; /* MTU for selected transport channel */
struct pollfd stream; /* Audio stream filedescriptor */
int channels;
/* used for pacing our writes to the output socket */
- struct timeval next_write;
+ uint64_t next_write;
};
+static uint64_t get_microseconds()
+{
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return (now.tv_sec * 1000000UL + now.tv_usec);
+}
+
+#ifdef ENABLE_TIMING
+static void print_time(const char* message, uint64_t then, uint64_t now)
+{
+ DBG("%s: %lld us", message, now - then);
+}
+#endif
static int audioservice_send(struct bluetooth_data *data, const bt_audio_msg_header_t *msg);
static int audioservice_expect(struct bluetooth_data *data, bt_audio_msg_header_t *outmsg,
static void bluetooth_close(struct bluetooth_data *data)
{
- LOGD("bluetooth_close");
+ DBG("bluetooth_close");
if (data->server.fd >= 0) {
bt_audio_service_close(data->server.fd);
data->server.fd = -1;
data->nsamples = 0;
data->seq_num = 0;
data->frame_count = 0;
- data->next_write.tv_sec = 0;
- data->next_write.tv_usec = 0;
+ data->next_write = 0;
return 0;
}
data->sbc.bitpool = active_capabilities.max_bitpool;
data->codesize = sbc_get_codesize(&data->sbc);
data->frame_duration = sbc_get_frame_duration(&data->sbc);
+ DBG("frame_duration: %d us", data->frame_duration);
}
static int bluetooth_a2dp_hw_params(struct bluetooth_data *data)
}
data->link_mtu = setconf_rsp->link_mtu;
+ DBG("MTU: %d", data->link_mtu);
/* Setup SBC encoder now we agree on parameters */
bluetooth_a2dp_setup(data);
int ret = 0;
struct rtp_header *header;
struct rtp_payload *payload;
- struct timeval now;
+ uint64_t now;
long duration = data->frame_duration * data->frame_count;
+#ifdef ENABLE_TIMING
+ uint64_t begin, end, begin2, end2;
+ begin = get_microseconds();
+#endif
header = (struct rtp_header *)data->buffer;
payload = (struct rtp_payload *)(data->buffer + sizeof(*header));
header->ssrc = htonl(1);
data->stream.revents = 0;
- ret = poll(&data->stream, 1, -1);
+#ifdef ENABLE_TIMING
+ begin2 = get_microseconds();
+#endif
+ ret = poll(&data->stream, 1, POLL_TIMEOUT);
+#ifdef ENABLE_TIMING
+ end2 = get_microseconds();
+ print_time("poll", begin2, end2);
+#endif
if (ret == 1 && data->stream.revents == POLLOUT) {
long ahead = 0;
- gettimeofday(&now, NULL);
+ now = get_microseconds();
- if (data->next_write.tv_sec || data->next_write.tv_usec) {
- ahead = (data->next_write.tv_sec - now.tv_sec) * 1000000
- + (data->next_write.tv_usec - now.tv_usec);
+ if (data->next_write) {
+ ahead = data->next_write - now;
+#ifdef ENABLE_TIMING
+ DBG("duration: %ld, ahead: %ld", duration, ahead);
+#endif
if (ahead > 0) {
/* too fast, need to throttle */
usleep(ahead);
} else {
data->next_write = now;
}
- if (ahead < -duration * PACKET_BUFFER_COUNT) {
+ if (ahead < 0) {
/* fallen too far behind, don't try to catch up */
- data->next_write.tv_sec = 0;
- data->next_write.tv_usec = 0;
+ VDBG("ahead < 0, resetting next_write");
+ data->next_write = 0;
} else {
/* advance next_write by duration */
- data->next_write.tv_usec += duration;
- data->next_write.tv_sec +=
- data->next_write.tv_usec / 1000000;
- data->next_write.tv_usec %= 1000000;
+ data->next_write += duration;
}
+#ifdef ENABLE_TIMING
+ begin2 = get_microseconds();
+#endif
ret = send(data->stream.fd, data->buffer, data->count, MSG_NOSIGNAL);
+#ifdef ENABLE_TIMING
+ end2 = get_microseconds();
+ print_time("send", begin2, end2);
+#endif
if (ret < 0) {
ERR("send returned %d errno %s.", ret, strerror(errno));
ret = -errno;
}
} else {
ret = -errno;
+ ERR("poll failed: %d", ret);
+ }
+
+ if (ret < 0) {
+ close(data->stream.fd);
+ data->stream.fd = -1;
}
/* Reset buffer of data to send */
data->samples = 0;
data->seq_num++;
+#ifdef ENABLE_TIMING
+ end = get_microseconds();
+ print_time("avdtp_write", begin, end);
+#endif
return ret;
}
return err;
}
-static int audioservice_recv(int sk, bt_audio_msg_header_t *inmsg)
+static int audioservice_recv(struct bluetooth_data *data,
+ bt_audio_msg_header_t *inmsg)
{
int err;
const char *type;
VDBG("trying to receive msg from audio service...");
- if (recv(sk, inmsg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) {
+ data->server.revents = 0;
+ err = poll(&data->server, 1, POLL_TIMEOUT);
+ VDBG("poll returned %d", ret);
+ if (err == 1)
+ err = recv(data->server.fd, inmsg, BT_AUDIO_IPC_PACKET_SIZE, 0);
+ if (err > 0) {
type = bt_audio_strmsg(inmsg->msg_type);
if (type) {
VDBG("Received %s", type);
err = -errno;
ERR("Error receiving data from audio service: %s(%d)",
strerror(errno), errno);
+ close(data->server.fd);
+ data->server.fd = -1;
}
return err;
static int audioservice_expect(struct bluetooth_data *data,
bt_audio_msg_header_t *rsp_hdr, int expected_type)
{
- int err = audioservice_recv(data->server.fd, rsp_hdr);
+ int err = audioservice_recv(data, rsp_hdr);
if (err == 0) {
if (rsp_hdr->msg_type != expected_type) {
err = -EINVAL;
struct bt_getcapabilities_req *getcaps_req = (void*) buf;
struct bt_getcapabilities_rsp *getcaps_rsp = (void*) buf;
- LOGD("bluetooth_init");
+ DBG("bluetooth_init");
sk = bt_audio_service_open();
if (sk <= 0) {
long frames_left = count;
int encoded, written;
const char *buff;
+#ifdef ENABLE_TIMING
+ uint64_t begin, end;
+ DBG("********** a2dp_write **********");
+ begin = get_microseconds();
+#endif
if (data->server.fd == -1) {
err = bluetooth_init(data);
ERR("%ld bytes left at end of a2dp_write\n", frames_left);
done:
- VDBG("returning %ld", ret);
+#ifdef ENABLE_TIMING
+ end = get_microseconds();
+ print_time("a2dp_write total", begin, end);
+#endif
return ret;
}
struct pending_request *pending = sink->connect;
if (sink->state >= AVDTP_STATE_OPEN) {
- DBusMessage *reply;
debug("Stream successfully created, after XCASE connect:connect");
- reply = dbus_message_new_method_return(pending->msg);
- dbus_connection_send(pending->conn, reply, NULL);
- dbus_message_unref(reply);
+ if (pending->msg) {
+ DBusMessage *reply;
+ reply = dbus_message_new_method_return(pending->msg);
+ dbus_connection_send(pending->conn, reply, NULL);
+ dbus_message_unref(reply);
+ }
} else {
debug("Stream setup failed, after XCASE connect:connect");
- error_failed(pending->conn, pending->msg, "Stream setup failed");
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
}
sink->connect = NULL;
pending = sink->connect;
if (stream) {
- DBusMessage *reply;
+ debug("Stream successfully created");
+
+ if (pending->msg) {
+ DBusMessage *reply;
+ reply = dbus_message_new_method_return(pending->msg);
+ dbus_connection_send(pending->conn, reply, NULL);
+ dbus_message_unref(reply);
+ }
sink->connect = NULL;
- reply = dbus_message_new_method_return(pending->msg);
- dbus_connection_send(pending->conn, reply, NULL);
- dbus_message_unref(reply);
pending_request_free(pending);
- debug("Stream successfully created");
+ return;
+ }
+
+ avdtp_unref(sink->session);
+ sink->session = NULL;
+ if (avdtp_error_type(err) == AVDTP_ERROR_ERRNO
+ && avdtp_error_posix_errno(err) != EHOSTDOWN) {
+ debug("connect:connect XCASE detected");
+ g_timeout_add(STREAM_SETUP_RETRY_TIMER,
+ stream_setup_retry, sink);
} else {
- avdtp_unref(sink->session);
- sink->session = NULL;
- if (avdtp_error_type(err) == AVDTP_ERROR_ERRNO
- && avdtp_error_posix_errno(err) != EHOSTDOWN) {
- debug("connect:connect XCASE detected");
- g_timeout_add(STREAM_SETUP_RETRY_TIMER,
- stream_setup_retry, sink);
- } else {
- sink->connect = NULL;
+ if (pending->msg)
error_failed(pending->conn, pending->msg, "Stream setup failed");
- pending_request_free(pending);
- debug("Stream setup failed : %s", avdtp_strerror(err));
- }
+ sink->connect = NULL;
+ pending_request_free(pending);
+ debug("Stream setup failed : %s", avdtp_strerror(err));
}
}
return;
failed:
+ if (pending->msg)
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
pending_request_free(pending);
sink->connect = NULL;
avdtp_unref(sink->session);
sink->session = NULL;
- error_failed(pending->conn, pending->msg, "Stream setup failed");
+}
+
+gboolean sink_setup_stream(struct sink *sink, struct avdtp *session)
+{
+ if (sink->connect || sink->disconnect)
+ return FALSE;
+
+ if (session && !sink->session)
+ sink->session = avdtp_ref(session);
+
+ if (avdtp_discover(sink->session, discovery_complete, sink) < 0)
+ return FALSE;
+
+ sink->connect = g_new0(struct pending_request, 1);
+
+ return TRUE;
}
static DBusMessage *sink_connect(DBusConnection *conn,
".AlreadyConnected",
"Device Already Connected");
- pending = g_new0(struct pending_request, 1);
+ if (!sink_setup_stream(sink, NULL))
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".FAILED",
+ "Failed to create a stream");
+
+ pending = sink->connect;
pending->conn = dbus_connection_ref(conn);
pending->msg = dbus_message_ref(msg);
- sink->connect = pending;
-
- avdtp_discover(sink->session, discovery_complete, sink);
debug("stream creation in progress");
avdtp_state_t sink_get_state(struct audio_device *dev);
gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session,
struct avdtp_stream *stream);
+gboolean sink_setup_stream(struct sink *sink, struct avdtp *session);