diff options
| -rw-r--r-- | src/modules/bluetooth/bluetooth-util.c | 127 | ||||
| -rw-r--r-- | src/modules/bluetooth/bluetooth-util.h | 39 | ||||
| -rw-r--r-- | src/modules/bluetooth/ipc.c | 1 | ||||
| -rw-r--r-- | src/modules/bluetooth/ipc.h | 73 | ||||
| -rw-r--r-- | src/modules/bluetooth/module-bluetooth-device.c | 99 | ||||
| -rw-r--r-- | src/modules/bluetooth/module-bluetooth-discover.c | 14 | ||||
| -rw-r--r-- | src/modules/bluetooth/sbc.c | 41 | ||||
| -rw-r--r-- | src/modules/bluetooth/sbc.h | 12 | 
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);  | 
