summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/modules/bluetooth/bluetooth-util.c127
-rw-r--r--src/modules/bluetooth/bluetooth-util.h39
-rw-r--r--src/modules/bluetooth/ipc.c1
-rw-r--r--src/modules/bluetooth/ipc.h73
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c99
-rw-r--r--src/modules/bluetooth/module-bluetooth-discover.c14
-rw-r--r--src/modules/bluetooth/sbc.c41
-rw-r--r--src/modules/bluetooth/sbc.h12
8 files changed, 275 insertions, 131 deletions
diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 1cdade99..94c1d315 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -39,6 +39,25 @@ struct pa_bluetooth_discovery {
pa_hook hook;
};
+static void get_properties_reply(DBusPendingCall *pending, void *userdata);
+static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessage *m, DBusPendingCallNotifyFunction func);
+
+static enum pa_bt_audio_state pa_bt_audio_state_from_string(const char* value) {
+ pa_assert(value);
+
+ if (pa_streq(value, "disconnected")) {
+ return PA_BT_AUDIO_STATE_DISCONNECTED;
+ } else if (pa_streq(value, "connecting")) {
+ return PA_BT_AUDIO_STATE_CONNECTING;
+ } else if (pa_streq(value, "connected")) {
+ return PA_BT_AUDIO_STATE_CONNECTED;
+ } else if (pa_streq(value, "playing")) {
+ return PA_BT_AUDIO_STATE_PLAYING;
+ }
+
+ return PA_BT_AUDIO_STATE_INVALID;
+}
+
static pa_bluetooth_uuid *uuid_new(const char *uuid) {
pa_bluetooth_uuid *u;
@@ -63,7 +82,7 @@ static pa_bluetooth_device* device_new(const char *path) {
d->dead = FALSE;
- d->device_info_valid = d->audio_sink_info_valid = d->headset_info_valid = 0;
+ d->device_info_valid = 0;
d->name = NULL;
d->path = pa_xstrdup(path);
@@ -75,9 +94,9 @@ static pa_bluetooth_device* device_new(const char *path) {
d->class = -1;
d->trusted = -1;
- d->audio_sink_connected = -1;
-
- d->headset_connected = -1;
+ d->audio_state = PA_BT_AUDIO_STATE_INVALID;
+ d->audio_sink_state = PA_BT_AUDIO_STATE_INVALID;
+ d->headset_state = PA_BT_AUDIO_STATE_INVALID;
return d;
}
@@ -99,25 +118,14 @@ static void device_free(pa_bluetooth_device *d) {
pa_xfree(d);
}
-static pa_bool_t device_is_loaded(pa_bluetooth_device *d) {
- pa_assert(d);
-
- return
- d->device_info_valid &&
- d->audio_sink_info_valid &&
- d->headset_info_valid;
-}
-
static pa_bool_t device_is_audio(pa_bluetooth_device *d) {
pa_assert(d);
- pa_assert(d->device_info_valid);
- pa_assert(d->audio_sink_info_valid);
- pa_assert(d->headset_info_valid);
-
return
- d->device_info_valid > 0 &&
- (d->audio_sink_info_valid > 0 || d->headset_info_valid > 0);
+ d->device_info_valid &&
+ (d->audio_state != PA_BT_AUDIO_STATE_INVALID ||
+ d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
+ d->headset_state != PA_BT_AUDIO_STATE_INVALID);
}
static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) {
@@ -213,11 +221,25 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
pa_bluetooth_uuid *node;
const char *value;
+ DBusMessage *m;
dbus_message_iter_get_basic(&ai, &value);
node = uuid_new(value);
PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
+ /* this might eventually be racy if .Audio is not there yet, but the State change will come anyway later, so this call is for cold-detection mostly */
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+
+ /* Vudentz said the interfaces are here when the UUIDs are announced */
+ if (strcasecmp(HSP_HS_UUID, value) == 0 || strcasecmp(HFP_HS_UUID, value) == 0) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Headset", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+ } else if (strcasecmp(A2DP_SINK_UUID, value) == 0) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSink", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+ }
+
if (!dbus_message_iter_next(&ai))
break;
}
@@ -230,12 +252,12 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
return 0;
}
-static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusMessageIter *i) {
+static int parse_audio_property(pa_bluetooth_discovery *u, int *state, DBusMessageIter *i) {
const char *key;
DBusMessageIter variant_i;
pa_assert(u);
- pa_assert(connected);
+ pa_assert(state);
pa_assert(i);
if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
@@ -257,17 +279,27 @@ static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusM
dbus_message_iter_recurse(i, &variant_i);
-/* pa_log_debug("Parsing property org.bluez.{AudioSink|Headset}.%s", key); */
+/* pa_log_debug("Parsing property org.bluez.{Audio|AudioSink|Headset}.%s", key); */
switch (dbus_message_iter_get_arg_type(&variant_i)) {
+ case DBUS_TYPE_STRING: {
+
+ const char *value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "State"))
+ *state = pa_bt_audio_state_from_string(value);
+/* pa_log_debug("Value %s", value); */
+ }
+
case DBUS_TYPE_BOOLEAN: {
dbus_bool_t value;
dbus_message_iter_get_basic(&variant_i, &value);
- if (pa_streq(key, "Connected"))
- *connected = !!value;
+ /* if (pa_streq(key, "Connected")) */
+ /* *connected = !!value; */
/* pa_log_debug("Value %s", pa_yes_no(value)); */
@@ -282,9 +314,6 @@ static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_b
pa_assert(y);
pa_assert(d);
- if (!device_is_loaded(d))
- return;
-
if (!device_is_audio(d))
return;
@@ -314,10 +343,6 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
d->device_info_valid = valid;
- else if (dbus_message_is_method_call(p->message, "org.bluez.Headset", "GetProperties"))
- d->headset_info_valid = valid;
- else if (dbus_message_is_method_call(p->message, "org.bluez.AudioSink", "GetProperties"))
- d->audio_sink_info_valid = valid;
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
@@ -349,12 +374,16 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
if (parse_device_property(y, d, &dict_i) < 0)
goto finish;
+ } else if (dbus_message_has_interface(p->message, "org.bluez.Audio")) {
+ if (parse_audio_property(y, &d->audio_state, &dict_i) < 0)
+ goto finish;
+
} else if (dbus_message_has_interface(p->message, "org.bluez.Headset")) {
- if (parse_audio_property(y, &d->headset_connected, &dict_i) < 0)
+ if (parse_audio_property(y, &d->headset_state, &dict_i) < 0)
goto finish;
} else if (dbus_message_has_interface(p->message, "org.bluez.AudioSink")) {
- if (parse_audio_property(y, &d->audio_sink_connected, &dict_i) < 0)
+ if (parse_audio_property(y, &d->audio_sink_state, &dict_i) < 0)
goto finish;
}
}
@@ -401,12 +430,6 @@ static void found_device(pa_bluetooth_discovery *y, const char* path) {
pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties"));
send_and_add_to_pending(y, d, m, get_properties_reply);
-
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Headset", "GetProperties"));
- send_and_add_to_pending(y, d, m, get_properties_reply);
-
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.AudioSink", "GetProperties"));
- send_and_add_to_pending(y, d, m, get_properties_reply);
}
static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
@@ -566,7 +589,8 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
found_adapter(y, path);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- } else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") ||
+ } else if (dbus_message_is_signal(m, "org.bluez.Audio", "PropertyChanged") ||
+ dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") ||
dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged") ||
dbus_message_is_signal(m, "org.bluez.Device", "PropertyChanged")) {
@@ -584,12 +608,16 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
if (parse_device_property(y, d, &arg_i) < 0)
goto fail;
+ } else if (dbus_message_has_interface(m, "org.bluez.Audio")) {
+ if (parse_audio_property(y, &d->audio_state, &arg_i) < 0)
+ goto fail;
+
} else if (dbus_message_has_interface(m, "org.bluez.Headset")) {
- if (parse_audio_property(y, &d->headset_connected, &arg_i) < 0)
+ if (parse_audio_property(y, &d->headset_state, &arg_i) < 0)
goto fail;
} else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) {
- if (parse_audio_property(y, &d->audio_sink_connected, &arg_i) < 0)
+ if (parse_audio_property(y, &d->audio_sink_state, &arg_i) < 0)
goto fail;
}
@@ -684,6 +712,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
"type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL) < 0) {
pa_log("Failed to add D-Bus matches: %s", err.message);
@@ -740,6 +769,7 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
"type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL);
@@ -827,3 +857,16 @@ char *pa_bluetooth_cleanup_name(const char *name) {
return t;
}
+
+pa_bool_t pa_bluetooth_uuid_has(pa_bluetooth_uuid *uuids, const char *uuid) {
+ pa_assert(uuid);
+
+ while (uuids) {
+ if (strcasecmp(uuids->uuid, uuid) == 0)
+ return TRUE;
+
+ uuids = uuids->next;
+ }
+
+ return FALSE;
+}
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 1d05e63c..54114738 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -28,6 +28,20 @@
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
+/* UUID copied from bluez/audio/device.h */
+#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805F9B34FB"
+
+#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB"
+#define HSP_AG_UUID "00001112-0000-1000-8000-00805F9B34FB"
+
+#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB"
+#define HFP_AG_UUID "0000111F-0000-1000-8000-00805F9B34FB"
+
+#define ADVANCED_AUDIO_UUID "0000110D-0000-1000-8000-00805F9B34FB"
+
+#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB"
+#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB"
+
typedef struct pa_bluetooth_uuid pa_bluetooth_uuid;
typedef struct pa_bluetooth_device pa_bluetooth_device;
typedef struct pa_bluetooth_discovery pa_bluetooth_discovery;
@@ -39,12 +53,20 @@ struct pa_bluetooth_uuid {
PA_LLIST_FIELDS(pa_bluetooth_uuid);
};
+/* This enum is shared among Audio, Headset, and AudioSink, although not all values are acceptable in all profiles */
+enum pa_bt_audio_state {
+ PA_BT_AUDIO_STATE_INVALID = -1,
+ PA_BT_AUDIO_STATE_DISCONNECTED,
+ PA_BT_AUDIO_STATE_CONNECTING,
+ PA_BT_AUDIO_STATE_CONNECTED,
+ PA_BT_AUDIO_STATE_PLAYING,
+ PA_BT_AUDIO_STATE_LAST
+};
+
struct pa_bluetooth_device {
pa_bool_t dead;
int device_info_valid; /* 0: no results yet; 1: good results; -1: bad results ... */
- int audio_sink_info_valid; /* ... same here ... */
- int headset_info_valid; /* ... and here */
/* Device information */
char *name;
@@ -57,11 +79,14 @@ struct pa_bluetooth_device {
int class;
int trusted;
- /* AudioSink information */
- int audio_sink_connected;
+ /* Audio state */
+ int audio_state;
- /* Headset information */
- int headset_connected;
+ /* AudioSink state */
+ int audio_sink_state;
+
+ /* Headset state */
+ int headset_state;
};
pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core);
@@ -79,4 +104,6 @@ const char* pa_bluetooth_get_form_factor(uint32_t class);
char *pa_bluetooth_cleanup_name(const char *name);
+pa_bool_t pa_bluetooth_uuid_has(pa_bluetooth_uuid *uuids, const char *uuid);
+
#endif
diff --git a/src/modules/bluetooth/ipc.c b/src/modules/bluetooth/ipc.c
index f14c92c4..dcecad8a 100644
--- a/src/modules/bluetooth/ipc.c
+++ b/src/modules/bluetooth/ipc.c
@@ -35,6 +35,7 @@ static const char *strtypes[] = {
/* This table contains the string representation for messages names */
static const char *strnames[] = {
"BT_GET_CAPABILITIES",
+ "BT_OPEN",
"BT_SET_CONFIGURATION",
"BT_NEW_STREAM",
"BT_START_STREAM",
diff --git a/src/modules/bluetooth/ipc.h b/src/modules/bluetooth/ipc.h
index f030acfa..2e170f50 100644
--- a/src/modules/bluetooth/ipc.h
+++ b/src/modules/bluetooth/ipc.h
@@ -71,7 +71,7 @@ extern "C" {
#include <sys/un.h>
#include <errno.h>
-#define BT_SUGGESTED_BUFFER_SIZE 128
+#define BT_SUGGESTED_BUFFER_SIZE 512
#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio"
/* Generic message header definition, except for RESPONSE messages */
@@ -94,10 +94,12 @@ typedef struct {
/* Messages names */
#define BT_GET_CAPABILITIES 0
-#define BT_SET_CONFIGURATION 1
-#define BT_NEW_STREAM 2
-#define BT_START_STREAM 3
-#define BT_STOP_STREAM 4
+#define BT_OPEN 1
+#define BT_SET_CONFIGURATION 2
+#define BT_NEW_STREAM 3
+#define BT_START_STREAM 4
+#define BT_STOP_STREAM 5
+#define BT_CLOSE 6
#define BT_CONTROL 7
#define BT_CAPABILITIES_TRANSPORT_A2DP 0
@@ -112,19 +114,31 @@ typedef struct {
struct bt_get_capabilities_req {
bt_audio_msg_header_t h;
- char device[18]; /* Address of the remote Device */
+ char source[18]; /* Address of the local Device */
+ char destination[18];/* Address of the remote Device */
+ char object[128]; /* DBus object path */
uint8_t transport; /* Requested transport */
uint8_t flags; /* Requested flags */
+ uint8_t seid; /* Requested capability configuration */
} __attribute__ ((packed));
/**
- * SBC Codec parameters as per A2DP profile 1.0 § 4.3
+ * SBC Codec parameters as per A2DP profile 1.0 § 4.3
*/
-#define BT_A2DP_CODEC_SBC 0x00
-#define BT_A2DP_CODEC_MPEG12 0x01
-#define BT_A2DP_CODEC_MPEG24 0x02
-#define BT_A2DP_CODEC_ATRAC 0x03
+/* A2DP seid are 6 bytes long so HSP/HFP are assigned to 7-8 bits */
+#define BT_A2DP_SEID_RANGE (1 << 6) - 1
+
+#define BT_A2DP_SBC_SOURCE 0x00
+#define BT_A2DP_SBC_SINK 0x01
+#define BT_A2DP_MPEG12_SOURCE 0x02
+#define BT_A2DP_MPEG12_SINK 0x03
+#define BT_A2DP_MPEG24_SOURCE 0x04
+#define BT_A2DP_MPEG24_SINK 0x05
+#define BT_A2DP_ATRAC_SOURCE 0x06
+#define BT_A2DP_ATRAC_SINK 0x07
+#define BT_A2DP_UNKNOWN_SOURCE 0x08
+#define BT_A2DP_UNKNOWN_SINK 0x09
#define BT_SBC_SAMPLING_FREQ_16000 (1 << 3)
#define BT_SBC_SAMPLING_FREQ_32000 (1 << 2)
@@ -163,10 +177,16 @@ struct bt_get_capabilities_req {
#define BT_PCM_FLAG_NREC 0x01
#define BT_PCM_FLAG_PCM_ROUTING 0x02
+#define BT_WRITE_LOCK (1 << 1)
+#define BT_READ_LOCK 1
+
typedef struct {
+ uint8_t seid;
uint8_t transport;
uint8_t type;
uint8_t length;
+ uint8_t configured;
+ uint8_t lock;
uint8_t data[0];
} __attribute__ ((packed)) codec_capabilities_t;
@@ -199,20 +219,35 @@ typedef struct {
struct bt_get_capabilities_rsp {
bt_audio_msg_header_t h;
+ char source[18]; /* Address of the local Device */
+ char destination[18];/* Address of the remote Device */
+ char object[128]; /* DBus object path */
uint8_t data[0]; /* First codec_capabilities_t */
} __attribute__ ((packed));
+struct bt_open_req {
+ bt_audio_msg_header_t h;
+ char source[18]; /* Address of the local Device */
+ char destination[18];/* Address of the remote Device */
+ char object[128]; /* DBus object path */
+ uint8_t seid; /* Requested capability configuration to lock */
+ uint8_t lock; /* Requested lock */
+} __attribute__ ((packed));
+
+struct bt_open_rsp {
+ bt_audio_msg_header_t h;
+ char source[18]; /* Address of the local Device */
+ char destination[18];/* Address of the remote Device */
+ char object[128]; /* DBus object path */
+} __attribute__ ((packed));
+
struct bt_set_configuration_req {
bt_audio_msg_header_t h;
- char device[18]; /* Address of the remote Device */
- uint8_t access_mode; /* Requested access mode */
codec_capabilities_t codec; /* Requested codec */
} __attribute__ ((packed));
struct bt_set_configuration_rsp {
bt_audio_msg_header_t h;
- uint8_t transport; /* Granted transport */
- uint8_t access_mode; /* Granted access mode */
uint16_t link_mtu; /* Max length that transport supports */
} __attribute__ ((packed));
@@ -241,6 +276,14 @@ struct bt_stop_stream_rsp {
bt_audio_msg_header_t h;
} __attribute__ ((packed));
+struct bt_close_req {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+struct bt_close_rsp {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
struct bt_suspend_stream_ind {
bt_audio_msg_header_t h;
} __attribute__ ((packed));
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 2ff64ba9..f9800ac0 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -132,6 +132,7 @@ struct userdata {
char *address;
char *path;
+ const pa_bluetooth_device* device;
pa_dbus_connection *connection;
@@ -274,7 +275,7 @@ static ssize_t service_expect(struct userdata*u, bt_audio_msg_header_t *rsp, siz
return 0;
}
-static int parse_caps(struct userdata *u, const struct bt_get_capabilities_rsp *rsp) {
+static int parse_caps(struct userdata *u, uint8_t seid, const struct bt_get_capabilities_rsp *rsp) {
uint16_t bytes_left;
const codec_capabilities_t *codec;
@@ -305,12 +306,15 @@ static int parse_caps(struct userdata *u, const struct bt_get_capabilities_rsp *
pa_assert(codec->type == BT_HFP_CODEC_PCM);
+ if (codec->configured && seid == 0)
+ return codec->seid;
+
memcpy(&u->hsp.pcm_capabilities, codec, sizeof(u->hsp.pcm_capabilities));
} else if (u->profile == PROFILE_A2DP) {
while (bytes_left > 0) {
- if (codec->type == BT_A2DP_CODEC_SBC)
+ if ((codec->type == BT_A2DP_SBC_SINK) && !codec->lock)
break;
bytes_left -= codec->length;
@@ -320,7 +324,10 @@ static int parse_caps(struct userdata *u, const struct bt_get_capabilities_rsp *
if (bytes_left <= 0 || codec->length != sizeof(u->a2dp.sbc_capabilities))
return -1;
- pa_assert(codec->type == BT_A2DP_CODEC_SBC);
+ pa_assert(codec->type == BT_A2DP_SBC_SINK);
+
+ if (codec->configured && seid == 0)
+ return codec->seid;
memcpy(&u->a2dp.sbc_capabilities, codec, sizeof(u->a2dp.sbc_capabilities));
}
@@ -328,13 +335,14 @@ static int parse_caps(struct userdata *u, const struct bt_get_capabilities_rsp *
return 0;
}
-static int get_caps(struct userdata *u) {
+static int get_caps(struct userdata *u, uint8_t seid) {
union {
struct bt_get_capabilities_req getcaps_req;
struct bt_get_capabilities_rsp getcaps_rsp;
bt_audio_error_t error;
uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
+ int ret;
pa_assert(u);
@@ -342,8 +350,9 @@ static int get_caps(struct userdata *u) {
msg.getcaps_req.h.type = BT_REQUEST;
msg.getcaps_req.h.name = BT_GET_CAPABILITIES;
msg.getcaps_req.h.length = sizeof(msg.getcaps_req);
+ msg.getcaps_req.seid = seid;
- pa_strlcpy(msg.getcaps_req.device, u->address, sizeof(msg.getcaps_req.device));
+ pa_strlcpy(msg.getcaps_req.object, u->path, sizeof(msg.getcaps_req.object));
if (u->profile == PROFILE_A2DP)
msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
else {
@@ -358,7 +367,11 @@ static int get_caps(struct userdata *u) {
if (service_expect(u, &msg.getcaps_rsp.h, sizeof(msg), BT_GET_CAPABILITIES, 0) < 0)
return -1;
- return parse_caps(u, &msg.getcaps_rsp);
+ ret = parse_caps(u, seid, &msg.getcaps_rsp);
+ if (ret <= 0)
+ return ret;
+
+ return get_caps(u, ret);
}
static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
@@ -434,8 +447,8 @@ static int setup_a2dp(struct userdata *u) {
break;
}
- if ((unsigned) i >= PA_ELEMENTSOF(freq_table)) {
- for (; i >= 0; i--) {
+ if ((unsigned) i == PA_ELEMENTSOF(freq_table)) {
+ for (--i; i >= 0; i--) {
if (cap->frequency & freq_table[i].cap) {
u->sample_spec.rate = freq_table[i].rate;
cap->frequency = freq_table[i].cap;
@@ -449,6 +462,11 @@ static int setup_a2dp(struct userdata *u) {
}
}
+ pa_assert(i < PA_ELEMENTSOF(freq_table));
+
+ if (cap->capability.configured)
+ return 0;
+
if (u->sample_spec.channels <= 1) {
if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
@@ -601,12 +619,29 @@ static void setup_sbc(struct a2dp_info *a2dp) {
static int set_conf(struct userdata *u) {
union {
+ struct bt_open_req open_req;
+ struct bt_open_rsp open_rsp;
struct bt_set_configuration_req setconf_req;
struct bt_set_configuration_rsp setconf_rsp;
bt_audio_error_t error;
uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
+ memset(&msg, 0, sizeof(msg));
+ msg.open_req.h.type = BT_REQUEST;
+ msg.open_req.h.name = BT_OPEN;
+ msg.open_req.h.length = sizeof(msg.open_req);
+
+ pa_strlcpy(msg.open_req.object, u->path, sizeof(msg.open_req.object));
+ msg.open_req.seid = u->profile == PROFILE_A2DP ? u->a2dp.sbc_capabilities.capability.seid : BT_A2DP_SEID_RANGE + 1;
+ msg.open_req.lock = u->profile == PROFILE_A2DP ? BT_WRITE_LOCK : BT_READ_LOCK | BT_WRITE_LOCK;
+
+ if (service_send(u, &msg.open_req.h) < 0)
+ return -1;
+
+ if (service_expect(u, &msg.open_rsp.h, sizeof(msg), BT_OPEN, sizeof(msg.open_rsp)) < 0)
+ return -1;
+
if (u->profile == PROFILE_A2DP ) {
u->sample_spec.format = PA_SAMPLE_S16LE;
@@ -625,15 +660,14 @@ static int set_conf(struct userdata *u) {
msg.setconf_req.h.name = BT_SET_CONFIGURATION;
msg.setconf_req.h.length = sizeof(msg.setconf_req);
- pa_strlcpy(msg.setconf_req.device, u->address, sizeof(msg.setconf_req.device));
- msg.setconf_req.access_mode = u->profile == PROFILE_A2DP ? BT_CAPABILITIES_ACCESS_MODE_WRITE : BT_CAPABILITIES_ACCESS_MODE_READWRITE;
-
- msg.setconf_req.codec.transport = u->profile == PROFILE_A2DP ? BT_CAPABILITIES_TRANSPORT_A2DP : BT_CAPABILITIES_TRANSPORT_SCO;
-
if (u->profile == PROFILE_A2DP) {
memcpy(&msg.setconf_req.codec, &u->a2dp.sbc_capabilities, sizeof(u->a2dp.sbc_capabilities));
- msg.setconf_req.h.length += msg.setconf_req.codec.length - sizeof(msg.setconf_req.codec);
+ } else {
+ msg.setconf_req.codec.transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ msg.setconf_req.codec.seid = BT_A2DP_SEID_RANGE + 1;
+ msg.setconf_req.codec.length = sizeof(pcm_capabilities_t);
}
+ msg.setconf_req.h.length += msg.setconf_req.codec.length - sizeof(msg.setconf_req.codec);
if (service_send(u, &msg.setconf_req.h) < 0)
return -1;
@@ -641,18 +675,6 @@ static int set_conf(struct userdata *u) {
if (service_expect(u, &msg.setconf_rsp.h, sizeof(msg), BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp)) < 0)
return -1;
- if ((u->profile == PROFILE_A2DP && msg.setconf_rsp.transport != BT_CAPABILITIES_TRANSPORT_A2DP) ||
- (u->profile == PROFILE_HSP && msg.setconf_rsp.transport != BT_CAPABILITIES_TRANSPORT_SCO)) {
- pa_log("Transport doesn't match what we requested.");
- return -1;
- }
-
- if ((u->profile == PROFILE_A2DP && msg.setconf_rsp.access_mode != BT_CAPABILITIES_ACCESS_MODE_WRITE) ||
- (u->profile == PROFILE_HSP && msg.setconf_rsp.access_mode != BT_CAPABILITIES_ACCESS_MODE_READWRITE)) {
- pa_log("Access mode doesn't match what we requested.");
- return -1;
- }
-
u->link_mtu = msg.setconf_rsp.link_mtu;
/* setup SBC encoder now we agree on parameters */
@@ -1570,7 +1592,7 @@ static int init_bt(struct userdata *u) {
static int setup_bt(struct userdata *u) {
pa_assert(u);
- if (get_caps(u) < 0)
+ if (get_caps(u, 0) < 0)
return -1;
pa_log_debug("Got device capabilities");
@@ -1713,6 +1735,15 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
d = PA_CARD_PROFILE_DATA(new_profile);
+ if (u->device->headset_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HSP) {
+ pa_log_warn("HSP is not connected, refused to switch profile");
+ return -1;
+ }
+ else if (u->device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP) {
+ pa_log_warn("A2DP is not connected, refused to switch profile");
+ return -1;
+ }
+
if (u->sink) {
inputs = pa_sink_move_all_start(u->sink);
#ifdef NOKIA
@@ -1789,7 +1820,11 @@ static int add_card(struct userdata *u, const char *default_profile, const pa_bl
data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- if (device->audio_sink_info_valid > 0) {
+ /* we base hsp/a2dp availability on UUIDs.
+ Ideally, it would be based on "Connected" state, but
+ we can't afford to wait for this information when
+ we are loaded with profile="hsp", for instance */
+ if (pa_bluetooth_uuid_has(device->uuids, A2DP_SINK_UUID)) {
p = pa_card_profile_new("a2dp", _("High Fidelity Playback (A2DP)"), sizeof(enum profile));
p->priority = 10;
p->n_sinks = 1;
@@ -1803,7 +1838,8 @@ static int add_card(struct userdata *u, const char *default_profile, const pa_bl
pa_hashmap_put(data.profiles, p->name, p);
}
- if (device->headset_info_valid > 0) {
+ if (pa_bluetooth_uuid_has(device->uuids, HSP_HS_UUID) ||
+ pa_bluetooth_uuid_has(device->uuids, HFP_HS_UUID)) {
p = pa_card_profile_new("hsp", _("Telephony Duplex (HSP/HFP)"), sizeof(enum profile));
p->priority = 20;
p->n_sinks = 1;
@@ -1906,7 +1942,6 @@ int pa__init(pa_module* m) {
uint32_t channels;
struct userdata *u;
const char *address, *path;
- const pa_bluetooth_device *d;
pa_bluetooth_discovery *y = NULL;
DBusError err;
char *mike, *speaker;
@@ -1967,11 +2002,11 @@ int pa__init(pa_module* m) {
if (!(y = pa_bluetooth_discovery_get(m->core)))
goto fail;
- if (!(d = find_device(u, y, address, path)))
+ if (!(u->device = find_device(u, y, address, path))) /* should discovery ref be kept? */
goto fail;
/* Add the card structure. This will also initialize the default profile */
- if (add_card(u, pa_modargs_get_value(ma, "profile", NULL), d) < 0)
+ if (add_card(u, pa_modargs_get_value(ma, "profile", NULL), u->device) < 0)
goto fail;
pa_bluetooth_discovery_unref(y);
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 46f8b723..788fee00 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -84,8 +84,7 @@ static pa_hook_result_t load_module_for_device(pa_bluetooth_discovery *y, const
mi = pa_hashmap_get(u->hashmap, d->path);
if (!d->dead &&
- d->device_connected > 0 &&
- (d->audio_sink_connected > 0 || d->headset_connected > 0)) {
+ d->device_connected > 0 && d->audio_state >= PA_BT_AUDIO_STATE_CONNECTED) {
if (!mi) {
pa_module *m = NULL;
@@ -93,7 +92,16 @@ static pa_hook_result_t load_module_for_device(pa_bluetooth_discovery *y, const
/* Oh, awesome, a new device has shown up and been connected! */
- args = pa_sprintf_malloc("address=\"%s\" path=\"%s\" profile=\"%s\"", d->address, d->path, d->headset_connected ? "hsp" : "a2dp");
+ args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path);
+#if 0
+ /* This is in case we have to use hsp immediately, without waiting for .Audio.State = Connected */
+ if (d->headset_state >= PA_BT_AUDIO_STATE_CONNECTED && somecondition) {
+ char *tmp;
+ tmp = pa_sprintf_malloc("%s profile=\"hsp\"", args);
+ pa_xfree(args);
+ args = tmp;
+ }
+#endif
#ifdef NOKIA
if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) &&
diff --git a/src/modules/bluetooth/sbc.c b/src/modules/bluetooth/sbc.c
index 42bae91a..779be4bd 100644
--- a/src/modules/bluetooth/sbc.c
+++ b/src/modules/bluetooth/sbc.c
@@ -978,10 +978,8 @@ ssize_t sbc_parse(sbc_t *sbc, const void *input, size_t input_len)
return sbc_decode(sbc, input, input_len, NULL, 0, NULL);
}
-ssize_t sbc_decode(sbc_t *sbc,
- const void *input, size_t input_len,
- void *output, size_t output_len,
- size_t *written)
+ssize_t sbc_decode(sbc_t *sbc, const void *input, size_t input_len,
+ void *output, size_t output_len, size_t *written)
{
struct sbc_priv *priv;
char *ptr;
@@ -1006,7 +1004,7 @@ ssize_t sbc_decode(sbc_t *sbc,
sbc->bitpool = priv->frame.bitpool;
priv->frame.codesize = sbc_get_codesize(sbc);
- priv->frame.length = sbc_get_frame_length(sbc);
+ priv->frame.length = framelen;
}
if (!output)
@@ -1046,10 +1044,8 @@ ssize_t sbc_decode(sbc_t *sbc,
return framelen;
}
-ssize_t sbc_encode(sbc_t *sbc,
- const void *input, size_t input_len,
- void *output, size_t output_len,
- size_t *written)
+ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len,
+ void *output, size_t output_len, size_t *written)
{
struct sbc_priv *priv;
int framelen, samples;
@@ -1140,30 +1136,25 @@ void sbc_finish(sbc_t *sbc)
size_t sbc_get_frame_length(sbc_t *sbc)
{
size_t ret;
- uint8_t subbands, channels, blocks, joint;
+ uint8_t subbands, channels, blocks, joint, bitpool;
struct sbc_priv *priv;
priv = sbc->priv;
- if (!priv->init) {
- subbands = sbc->subbands ? 8 : 4;
- blocks = 4 + (sbc->blocks * 4);
- channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
- joint = sbc->mode == SBC_MODE_JOINT_STEREO ? 1 : 0;
- } else {
- subbands = priv->frame.subbands;
- blocks = priv->frame.blocks;
- channels = priv->frame.channels;
- joint = priv->frame.joint;
- }
+ if (priv->init)
+ return priv->frame.length;
- ret = 4 + (4 * subbands * channels) / 8;
+ subbands = sbc->subbands ? 8 : 4;
+ blocks = 4 + (sbc->blocks * 4);
+ channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+ joint = sbc->mode == SBC_MODE_JOINT_STEREO ? 1 : 0;
+ bitpool = sbc->bitpool;
+ ret = 4 + (4 * subbands * channels) / 8;
/* This term is not always evenly divide so we round it up */
if (channels == 1)
- ret += ((blocks * channels * sbc->bitpool) + 7) / 8;
+ ret += ((blocks * channels * bitpool) + 7) / 8;
else
- ret += (((joint ? subbands : 0) + blocks * sbc->bitpool) + 7)
- / 8;
+ ret += (((joint ? subbands : 0) + blocks * bitpool) + 7) / 8;
return ret;
}
diff --git a/src/modules/bluetooth/sbc.h b/src/modules/bluetooth/sbc.h
index 9a7b4cec..65435884 100644
--- a/src/modules/bluetooth/sbc.h
+++ b/src/modules/bluetooth/sbc.h
@@ -85,16 +85,12 @@ int sbc_reinit(sbc_t *sbc, unsigned long flags);
ssize_t sbc_parse(sbc_t *sbc, const void *input, size_t input_len);
-ssize_t sbc_decode(sbc_t *sbc,
- const void *input, size_t input_len,
- void *output, size_t output_len,
- size_t *written);
+ssize_t sbc_decode(sbc_t *sbc, const void *input, size_t input_len,
+ void *output, size_t output_len, size_t *written);
/* Encodes ONE input block into ONE output block */
-ssize_t sbc_encode(sbc_t *sbc,
- const void *input, size_t input_len,
- void *output, size_t output_len,
- size_t *written);
+ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len,
+ void *output, size_t output_len, size_t *written);
/* Returns the output block size in bytes */
size_t sbc_get_frame_length(sbc_t *sbc);