From: Luiz Augusto von Dentz <[email protected]>
---
android/a2dp.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 158 insertions(+), 1 deletion(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index b59c53d..28b7406 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -52,9 +52,23 @@
static GIOChannel *server = NULL;
static GSList *devices = NULL;
+static GSList *endpoints = NULL;
static bdaddr_t adapter_addr;
static uint32_t record_id = 0;
+struct a2dp_preset {
+ void *data;
+ int8_t len;
+};
+
+struct a2dp_endpoint {
+ uint8_t id;
+ uint8_t codec;
+ struct avdtp_local_sep *sep;
+ struct a2dp_preset *caps;
+ GSList *presets;
+};
+
struct a2dp_device {
bdaddr_t dst;
uint8_t state;
@@ -70,6 +84,29 @@ static int device_cmp(gconstpointer s, gconstpointer user_data)
return bacmp(&dev->dst, dst);
}
+static void preset_free(void *data)
+{
+ struct a2dp_preset *preset = data;
+
+ g_free(preset->data);
+ g_free(preset);
+}
+
+static void unregister_endpoint(void *data)
+{
+ struct a2dp_endpoint *endpoint = data;
+
+ if (endpoint->sep)
+ avdtp_unregister_sep(endpoint->sep);
+
+ if (endpoint->caps)
+ preset_free(endpoint->caps);
+
+ g_slist_free_full(endpoint->presets, preset_free);
+
+ g_free(endpoint);
+}
+
static void a2dp_device_free(struct a2dp_device *dev)
{
if (dev->session)
@@ -354,10 +391,127 @@ static sdp_record_t *a2dp_record(void)
return record;
}
+static gboolean sep_getcap_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ GSList **caps, uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_preset *cap = endpoint->presets->data;
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct avdtp_media_codec_capability *codec_caps;
+
+ *caps = NULL;
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ *caps = g_slist_append(*caps, media_transport);
+
+ codec_caps = g_malloc0(sizeof(*codec_caps) + sizeof(cap));
+ codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ codec_caps->media_codec_type = endpoint->codec;
+ memcpy(codec_caps->data, cap->data, cap->len);
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
+ sizeof(*codec_caps) + sizeof(cap));
+
+ *caps = g_slist_append(*caps, media_codec);
+ g_free(codec_caps);
+
+ return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind = {
+ .get_capability = sep_getcap_ind,
+};
+
+static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
+ GSList *presets)
+{
+ struct a2dp_endpoint *endpoint;
+
+ /* FIXME: Add proper check for uuid */
+
+ endpoint = g_new0(struct a2dp_endpoint, 1);
+ endpoint->id = g_slist_length(endpoints) + 1;
+ endpoint->codec = codec;
+ endpoint->sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE,
+ AVDTP_MEDIA_TYPE_AUDIO,
+ codec, FALSE, &sep_ind, NULL,
+ endpoint);
+ endpoint->caps = g_slist_nth_data(presets->data, 0);
+ endpoint->presets = g_slist_copy(g_slist_nth(presets, 1));
+
+ endpoints = g_slist_append(endpoints, endpoint);
+
+ return endpoint->id;
+}
+
+static GSList *parse_presets(const struct audio_preset *p, uint8_t count,
+ uint16_t len)
+{
+ GSList *l = NULL;
+ uint8_t i;
+
+ for (i = 0; count > i; i++) {
+ struct a2dp_preset *preset;
+
+ if (len < sizeof(struct audio_preset)) {
+ DBG("Invalid preset index %u", i);
+ break;
+ }
+
+ len -= sizeof(struct audio_preset);
+ if (len == 0 || len < p->len) {
+ DBG("Invalid preset size of %u for index %u", len, i);
+ break;
+ }
+
+ preset = g_new0(struct a2dp_preset, 1);
+ preset->len = p->len;
+ preset->data = g_memdup(p->data, preset->len);
+ l = g_slist_append(l, preset);
+
+ len -= preset->len;
+ p += sizeof(struct audio_preset) + preset->len;
+ }
+
+ return l;
+}
+
static void bt_audio_open(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_open *cmd = buf;
+ struct audio_rsp_open rsp;
+ GSList *presets;
+
+ DBG("");
+ if (cmd->presets == 0) {
+ error("No audio presets found");
+ goto failed;
+ }
+
+ presets = parse_presets(cmd->preset, cmd->presets, len - sizeof(*cmd));
+ if (!presets) {
+ error("No audio presets found");
+ goto failed;
+ }
+
+ rsp.id = register_endpoint(cmd->uuid, cmd->codec, presets);
+ g_slist_free(presets);
+
+ if (rsp.id == 0) {
+ error("Unable to register endpoint");
+ goto failed;
+ }
+
+ audio_ipc_send_rsp_full(AUDIO_OP_OPEN, sizeof(rsp), &rsp, -1);
+
+ return;
+
+failed:
audio_ipc_send_rsp(AUDIO_OP_OPEN, AUDIO_STATUS_FAILED);
}
@@ -471,6 +625,9 @@ void bt_a2dp_unregister(void)
{
DBG("");
+ g_slist_free_full(endpoints, unregister_endpoint);
+ endpoints = NULL;
+
g_slist_foreach(devices, a2dp_device_disconnected, NULL);
devices = NULL;
--
1.8.4.2
Hi Luiz,
On Mon, Jan 13, 2014 at 8:55 AM, Luiz Augusto von Dentz
<[email protected]> wrote:
> Hi Lukasz,
>
> On Mon, Jan 13, 2014 at 2:10 AM, Lukasz Rymanowski
> <[email protected]> wrote:
>> Hi Luiz,
>>
>> On Sat, Jan 11, 2014 at 11:13 AM, Luiz Augusto von Dentz
>> <[email protected]> wrote:
>>> From: Luiz Augusto von Dentz <[email protected]>
>>>
>>> ---
>>> android/a2dp.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>>> 1 file changed, 197 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/android/a2dp.c b/android/a2dp.c
>>> index 8649cf3..479cb71 100644
>>> --- a/android/a2dp.c
>>> +++ b/android/a2dp.c
>>> @@ -37,6 +37,7 @@
>>> #include "lib/bluetooth.h"
>>> #include "lib/sdp.h"
>>> #include "lib/sdp_lib.h"
>>> +#include "profiles/audio/a2dp-codecs.h"
>>
>> We need to discuss how we gonna use structs from this file in
>> hal-audio (eg. a2dp_sbc_t). Maybe we should have some special file
>> with audio structs/defines for IPC only? Then you could copy data to
>> structs you need.
>
> Not sure what you mean about this header, its LGPL already so I see no
> problem of including it in hal-audio.
You are right, it is LGPL so no problem.
\Lukasz
Hi Lukasz,
On Mon, Jan 13, 2014 at 2:10 AM, Lukasz Rymanowski
<[email protected]> wrote:
> Hi Luiz,
>
> On Sat, Jan 11, 2014 at 11:13 AM, Luiz Augusto von Dentz
> <[email protected]> wrote:
>> From: Luiz Augusto von Dentz <[email protected]>
>>
>> ---
>> android/a2dp.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>> 1 file changed, 197 insertions(+), 2 deletions(-)
>>
>> diff --git a/android/a2dp.c b/android/a2dp.c
>> index 8649cf3..479cb71 100644
>> --- a/android/a2dp.c
>> +++ b/android/a2dp.c
>> @@ -37,6 +37,7 @@
>> #include "lib/bluetooth.h"
>> #include "lib/sdp.h"
>> #include "lib/sdp_lib.h"
>> +#include "profiles/audio/a2dp-codecs.h"
>
> We need to discuss how we gonna use structs from this file in
> hal-audio (eg. a2dp_sbc_t). Maybe we should have some special file
> with audio structs/defines for IPC only? Then you could copy data to
> structs you need.
Not sure what you mean about this header, its LGPL already so I see no
problem of including it in hal-audio.
Hi Luiz,
On Sat, Jan 11, 2014 at 11:13 AM, Luiz Augusto von Dentz
<[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> ---
> android/a2dp.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 197 insertions(+), 2 deletions(-)
>
> diff --git a/android/a2dp.c b/android/a2dp.c
> index 8649cf3..479cb71 100644
> --- a/android/a2dp.c
> +++ b/android/a2dp.c
> @@ -37,6 +37,7 @@
> #include "lib/bluetooth.h"
> #include "lib/sdp.h"
> #include "lib/sdp_lib.h"
> +#include "profiles/audio/a2dp-codecs.h"
We need to discuss how we gonna use structs from this file in
hal-audio (eg. a2dp_sbc_t). Maybe we should have some special file
with audio structs/defines for IPC only? Then you could copy data to
structs you need.
> #include "log.h"
> #include "a2dp.h"
> #include "hal-msg.h"
> @@ -53,6 +54,7 @@
> static GIOChannel *server = NULL;
> static GSList *devices = NULL;
> static GSList *endpoints = NULL;
> +static GSList *setups = NULL;
> static bdaddr_t adapter_addr;
> static uint32_t record_id = 0;
>
> @@ -67,6 +69,7 @@ struct a2dp_endpoint {
> struct avdtp_local_sep *sep;
> struct a2dp_preset *caps;
> GSList *presets;
> + struct a2dp_config *config;
> };
>
> struct a2dp_device {
> @@ -76,6 +79,13 @@ struct a2dp_device {
> struct avdtp *session;
> };
>
> +struct a2dp_setup {
> + struct a2dp_device *dev;
> + struct a2dp_endpoint *endpoint;
> + struct a2dp_preset *preset;
> + struct avdtp_stream *stream;
> +};
> +
> static int device_cmp(gconstpointer s, gconstpointer user_data)
> {
> const struct a2dp_device *dev = s;
> @@ -422,8 +432,160 @@ static gboolean sep_getcap_ind(struct avdtp *session,
> return TRUE;
> }
>
> +static int sbc_check_config(struct a2dp_endpoint *endpoint,
> + struct a2dp_preset *conf)
> +{
> + a2dp_sbc_t *caps, *config;
> +
> + if (conf->len != sizeof(a2dp_sbc_t)) {
> + error("SBC: Invalid configuration size (%u)", conf->len);
> + return -EINVAL;
> + }
> +
> + caps = endpoint->caps->data;
> + config = conf->data;
> +
> + if (!(caps->frequency & config->frequency)) {
> + error("SBC: Unsupported frequency (%u) by endpoint",
> + config->frequency);
> + return -EINVAL;
> + }
> +
> + if (!(caps->channel_mode & config->channel_mode)) {
> + error("SBC: Unsupported channel mode (%u) by endpoint",
> + config->channel_mode);
> + return -EINVAL;
> + }
> +
> + if (!(caps->block_length & config->block_length)) {
> + error("SBC: Unsupported block length (%u) by endpoint",
> + config->block_length);
> + return -EINVAL;
> + }
> +
> + if (!(caps->allocation_method & config->allocation_method)) {
> + error("SBC: Unsupported allocation method (%u) by endpoint",
> + config->block_length);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int check_config(struct a2dp_endpoint *endpoint,
> + struct a2dp_preset *config)
> +{
> + GSList *l;
> +
> + for (l = endpoint->presets; l; l = g_slist_next(l)) {
> + struct a2dp_preset *preset = l->data;
> +
> + if (preset->len != config->len)
> + continue;
> +
> + if (memcmp(preset->data, config->data, preset->len) == 0)
> + return 0;
> + }
> +
> + /* Codec specific */
> + switch (endpoint->codec) {
> + case A2DP_CODEC_SBC:
> + return sbc_check_config(endpoint, config);
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static struct a2dp_device *find_device_by_session(struct avdtp *session)
> +{
> + GSList *l;
> +
> + for (l = devices; l; l = g_slist_next(l)) {
> + struct a2dp_device *dev = l->data;
> +
> + if (dev->session == session)
> + return dev;
> + }
> +
> + return NULL;
> +}
> +
> +static void setup_free(void *data)
> +{
> + struct a2dp_setup *setup = data;
> +
> + preset_free(setup->preset);
> + g_free(setup);
> +}
> +
> +static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint,
> + struct a2dp_preset *preset, struct avdtp_stream *stream)
> +{
> + struct a2dp_setup *setup;
> +
> + setup = g_new0(struct a2dp_setup, 1);
> + setup->dev = dev;
> + setup->endpoint = endpoint;
> + setup->preset = preset;
> + setup->stream = stream;
> + setups = g_slist_append(setups, setup);
> +}
> +
> +static gboolean sep_setconf_ind(struct avdtp *session,
> + struct avdtp_local_sep *sep,
> + struct avdtp_stream *stream,
> + GSList *caps,
> + avdtp_set_configuration_cb cb,
> + void *user_data)
> +{
> + struct a2dp_endpoint *endpoint = user_data;
> + struct a2dp_device *dev;
> + struct a2dp_preset *preset = NULL;
> +
> + DBG("");
> +
> + dev = find_device_by_session(session);
> + if (!dev) {
> + error("Unable to find device for session %p", session);
> + return FALSE;
> + }
> +
> + for (; caps != NULL; caps = g_slist_next(caps)) {
> + struct avdtp_service_capability *cap = caps->data;
> + struct avdtp_media_codec_capability *codec;
> +
> + if (cap->category == AVDTP_DELAY_REPORTING)
> + return FALSE;
> +
> + if (cap->category != AVDTP_MEDIA_CODEC)
> + continue;
> +
> + codec = (struct avdtp_media_codec_capability *) cap->data;
> +
> + if (codec->media_codec_type != endpoint->codec)
> + return FALSE;
> +
> + preset = g_new0(struct a2dp_preset, 1);
> + preset->len = cap->length - sizeof(*codec);
> + preset->data = g_memdup(codec->data, preset->len);
> +
> + if (check_config(endpoint, preset) < 0) {
> + preset_free(preset);
> + return FALSE;
> + }
> + }
> +
> + if (!preset)
> + return FALSE;
> +
> + setup_add(dev, endpoint, preset, stream);
> +
> + return TRUE;
> +}
> +
> static struct avdtp_sep_ind sep_ind = {
> .get_capability = sep_getcap_ind,
> + .set_configuration = sep_setconf_ind,
> };
>
> static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
> @@ -548,11 +710,41 @@ static void bt_audio_close(const void *buf, uint16_t len)
> audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_SUCCESS);
> }
>
> +static struct a2dp_setup *find_setup(uint8_t id)
> +{
> + GSList *l;
> +
> + for (l = setups; l; l = g_slist_next(l)) {
> + struct a2dp_setup *setup = l->data;
> +
> + if (setup->endpoint->id == id)
> + return setup;
> + }
> +
> + return NULL;
> +}
> +
> static void bt_stream_open(const void *buf, uint16_t len)
> {
> - DBG("Not Implemented");
> + const struct audio_cmd_open_stream *cmd = buf;
> + struct audio_rsp_open_stream *rsp;
> + struct a2dp_setup *setup;
> +
> + DBG("");
>
> - audio_ipc_send_rsp(AUDIO_OP_OPEN_STREAM, AUDIO_STATUS_FAILED);
> + setup = find_setup(cmd->id);
> + if (!setup) {
> + error("Unable to find stream for endpoint %u", cmd->id);
> + audio_ipc_send_rsp(AUDIO_OP_OPEN_STREAM, HAL_STATUS_FAILED);
Use AUDIO_STATUS_FAILED
> + return;
> + }
> +
> + len = sizeof(*rsp) + setup->preset->len;
> + rsp = g_malloc0(sizeof(*rsp) + setup->preset->len);
> + rsp->preset->len = setup->preset->len;
> + memcpy(rsp->preset->data, setup->preset->data, setup->preset->len);
> +
> + audio_ipc_send_rsp_full(AUDIO_OP_OPEN_STREAM, len, rsp, -1);
> }
>
> static void bt_stream_close(const void *buf, uint16_t len)
> @@ -651,6 +843,9 @@ void bt_a2dp_unregister(void)
> {
> DBG("");
>
> + g_slist_free_full(setups, setup_free);
> + setups = NULL;
> +
> g_slist_free_full(endpoints, unregister_endpoint);
> endpoints = NULL;
>
> --
> 1.8.4.2
>
BR
Lukasz
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Luiz,
On 11 January 2014 11:13, Luiz Augusto von Dentz <[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> ---
> android/a2dp.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 197 insertions(+), 2 deletions(-)
>
> diff --git a/android/a2dp.c b/android/a2dp.c
> index 8649cf3..479cb71 100644
> --- a/android/a2dp.c
> +++ b/android/a2dp.c
> @@ -37,6 +37,7 @@
> #include "lib/bluetooth.h"
> #include "lib/sdp.h"
> #include "lib/sdp_lib.h"
> +#include "profiles/audio/a2dp-codecs.h"
> #include "log.h"
> #include "a2dp.h"
> #include "hal-msg.h"
> @@ -53,6 +54,7 @@
> static GIOChannel *server = NULL;
> static GSList *devices = NULL;
> static GSList *endpoints = NULL;
> +static GSList *setups = NULL;
> static bdaddr_t adapter_addr;
> static uint32_t record_id = 0;
>
> @@ -67,6 +69,7 @@ struct a2dp_endpoint {
> struct avdtp_local_sep *sep;
> struct a2dp_preset *caps;
> GSList *presets;
> + struct a2dp_config *config;
> };
>
> struct a2dp_device {
> @@ -76,6 +79,13 @@ struct a2dp_device {
> struct avdtp *session;
> };
>
> +struct a2dp_setup {
> + struct a2dp_device *dev;
> + struct a2dp_endpoint *endpoint;
> + struct a2dp_preset *preset;
> + struct avdtp_stream *stream;
> +};
> +
> static int device_cmp(gconstpointer s, gconstpointer user_data)
> {
> const struct a2dp_device *dev = s;
> @@ -422,8 +432,160 @@ static gboolean sep_getcap_ind(struct avdtp *session,
> return TRUE;
> }
>
> +static int sbc_check_config(struct a2dp_endpoint *endpoint,
> + struct a2dp_preset *conf)
> +{
> + a2dp_sbc_t *caps, *config;
> +
> + if (conf->len != sizeof(a2dp_sbc_t)) {
> + error("SBC: Invalid configuration size (%u)", conf->len);
> + return -EINVAL;
> + }
> +
> + caps = endpoint->caps->data;
> + config = conf->data;
> +
> + if (!(caps->frequency & config->frequency)) {
> + error("SBC: Unsupported frequency (%u) by endpoint",
> + config->frequency);
> + return -EINVAL;
> + }
> +
> + if (!(caps->channel_mode & config->channel_mode)) {
> + error("SBC: Unsupported channel mode (%u) by endpoint",
> + config->channel_mode);
> + return -EINVAL;
> + }
> +
> + if (!(caps->block_length & config->block_length)) {
> + error("SBC: Unsupported block length (%u) by endpoint",
> + config->block_length);
> + return -EINVAL;
> + }
> +
> + if (!(caps->allocation_method & config->allocation_method)) {
> + error("SBC: Unsupported allocation method (%u) by endpoint",
> + config->block_length);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int check_config(struct a2dp_endpoint *endpoint,
> + struct a2dp_preset *config)
> +{
> + GSList *l;
> +
> + for (l = endpoint->presets; l; l = g_slist_next(l)) {
> + struct a2dp_preset *preset = l->data;
> +
> + if (preset->len != config->len)
> + continue;
> +
> + if (memcmp(preset->data, config->data, preset->len) == 0)
> + return 0;
> + }
> +
> + /* Codec specific */
> + switch (endpoint->codec) {
> + case A2DP_CODEC_SBC:
> + return sbc_check_config(endpoint, config);
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static struct a2dp_device *find_device_by_session(struct avdtp *session)
> +{
> + GSList *l;
> +
> + for (l = devices; l; l = g_slist_next(l)) {
> + struct a2dp_device *dev = l->data;
> +
> + if (dev->session == session)
> + return dev;
> + }
> +
> + return NULL;
> +}
> +
> +static void setup_free(void *data)
> +{
> + struct a2dp_setup *setup = data;
> +
> + preset_free(setup->preset);
> + g_free(setup);
> +}
> +
> +static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint,
> + struct a2dp_preset *preset, struct avdtp_stream *stream)
> +{
> + struct a2dp_setup *setup;
> +
> + setup = g_new0(struct a2dp_setup, 1);
> + setup->dev = dev;
> + setup->endpoint = endpoint;
> + setup->preset = preset;
> + setup->stream = stream;
> + setups = g_slist_append(setups, setup);
> +}
> +
> +static gboolean sep_setconf_ind(struct avdtp *session,
> + struct avdtp_local_sep *sep,
> + struct avdtp_stream *stream,
> + GSList *caps,
> + avdtp_set_configuration_cb cb,
> + void *user_data)
> +{
> + struct a2dp_endpoint *endpoint = user_data;
> + struct a2dp_device *dev;
> + struct a2dp_preset *preset = NULL;
> +
> + DBG("");
> +
> + dev = find_device_by_session(session);
> + if (!dev) {
> + error("Unable to find device for session %p", session);
> + return FALSE;
> + }
> +
> + for (; caps != NULL; caps = g_slist_next(caps)) {
> + struct avdtp_service_capability *cap = caps->data;
> + struct avdtp_media_codec_capability *codec;
> +
> + if (cap->category == AVDTP_DELAY_REPORTING)
> + return FALSE;
> +
> + if (cap->category != AVDTP_MEDIA_CODEC)
> + continue;
> +
> + codec = (struct avdtp_media_codec_capability *) cap->data;
> +
> + if (codec->media_codec_type != endpoint->codec)
> + return FALSE;
> +
> + preset = g_new0(struct a2dp_preset, 1);
> + preset->len = cap->length - sizeof(*codec);
> + preset->data = g_memdup(codec->data, preset->len);
> +
> + if (check_config(endpoint, preset) < 0) {
> + preset_free(preset);
> + return FALSE;
> + }
> + }
> +
> + if (!preset)
> + return FALSE;
> +
> + setup_add(dev, endpoint, preset, stream);
> +
> + return TRUE;
I guess there should be call to avdtp_set_configuration_cb somewhere
in this function?
BR,
Andrzej
Hi Luiz,
On 11 January 2014 11:13, Luiz Augusto von Dentz <[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> ---
> android/a2dp.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 158 insertions(+), 1 deletion(-)
>
> diff --git a/android/a2dp.c b/android/a2dp.c
> index b59c53d..28b7406 100644
> --- a/android/a2dp.c
> +++ b/android/a2dp.c
> @@ -52,9 +52,23 @@
>
> static GIOChannel *server = NULL;
> static GSList *devices = NULL;
> +static GSList *endpoints = NULL;
> static bdaddr_t adapter_addr;
> static uint32_t record_id = 0;
>
> +struct a2dp_preset {
> + void *data;
> + int8_t len;
> +};
> +
> +struct a2dp_endpoint {
> + uint8_t id;
> + uint8_t codec;
> + struct avdtp_local_sep *sep;
> + struct a2dp_preset *caps;
> + GSList *presets;
> +};
> +
> struct a2dp_device {
> bdaddr_t dst;
> uint8_t state;
> @@ -70,6 +84,29 @@ static int device_cmp(gconstpointer s, gconstpointer user_data)
> return bacmp(&dev->dst, dst);
> }
>
> +static void preset_free(void *data)
> +{
> + struct a2dp_preset *preset = data;
> +
> + g_free(preset->data);
> + g_free(preset);
> +}
> +
> +static void unregister_endpoint(void *data)
> +{
> + struct a2dp_endpoint *endpoint = data;
> +
> + if (endpoint->sep)
> + avdtp_unregister_sep(endpoint->sep);
> +
> + if (endpoint->caps)
> + preset_free(endpoint->caps);
> +
> + g_slist_free_full(endpoint->presets, preset_free);
> +
> + g_free(endpoint);
> +}
> +
> static void a2dp_device_free(struct a2dp_device *dev)
> {
> if (dev->session)
> @@ -354,10 +391,127 @@ static sdp_record_t *a2dp_record(void)
> return record;
> }
>
> +static gboolean sep_getcap_ind(struct avdtp *session,
> + struct avdtp_local_sep *sep,
> + GSList **caps, uint8_t *err,
> + void *user_data)
> +{
> + struct a2dp_endpoint *endpoint = user_data;
> + struct a2dp_preset *cap = endpoint->presets->data;
endpoint->caps
> + struct avdtp_service_capability *media_transport, *media_codec;
> + struct avdtp_media_codec_capability *codec_caps;
> +
> + *caps = NULL;
> +
> + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
> + NULL, 0);
> +
> + *caps = g_slist_append(*caps, media_transport);
> +
> + codec_caps = g_malloc0(sizeof(*codec_caps) + sizeof(cap));
> + codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
> + codec_caps->media_codec_type = endpoint->codec;
> + memcpy(codec_caps->data, cap->data, cap->len);
> +
> + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
> + sizeof(*codec_caps) + sizeof(cap));
> +
> + *caps = g_slist_append(*caps, media_codec);
> + g_free(codec_caps);
> +
> + return TRUE;
> +}
> +
> +static struct avdtp_sep_ind sep_ind = {
> + .get_capability = sep_getcap_ind,
> +};
> +
> +static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
> + GSList *presets)
> +{
> + struct a2dp_endpoint *endpoint;
> +
> + /* FIXME: Add proper check for uuid */
> +
> + endpoint = g_new0(struct a2dp_endpoint, 1);
> + endpoint->id = g_slist_length(endpoints) + 1;
> + endpoint->codec = codec;
> + endpoint->sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE,
> + AVDTP_MEDIA_TYPE_AUDIO,
> + codec, FALSE, &sep_ind, NULL,
> + endpoint);
> + endpoint->caps = g_slist_nth_data(presets->data, 0);
g_slist_nth_data(presets, 0)
BR,
Andrzej
Hi Luiz,
On Sat, Jan 11, 2014 at 11:13 AM, Luiz Augusto von Dentz
<[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> ---
> android/a2dp.c | 30 ++++++++++++++++++++++++++++--
> 1 file changed, 28 insertions(+), 2 deletions(-)
>
> diff --git a/android/a2dp.c b/android/a2dp.c
> index 28b7406..8649cf3 100644
> --- a/android/a2dp.c
> +++ b/android/a2dp.c
> @@ -515,11 +515,37 @@ failed:
> audio_ipc_send_rsp(AUDIO_OP_OPEN, AUDIO_STATUS_FAILED);
> }
>
> +static struct a2dp_endpoint *find_endpoint(uint8_t id)
> +{
> + GSList *l;
> +
> + for (l = endpoints; l; l = g_slist_next(l)) {
> + struct a2dp_endpoint *endpoint = l->data;
> +
> + if (endpoint->id == id)
> + return endpoint;
> + }
> +
> + return NULL;
> +}
> +
> static void bt_audio_close(const void *buf, uint16_t len)
> {
> - DBG("Not Implemented");
> + const struct audio_cmd_close *cmd = buf;
> + struct a2dp_endpoint *endpoint;
> +
> + DBG("");
> +
> + endpoint = find_endpoint(cmd->id);
> + if (!endpoint) {
> + error("Unable to find endpoint %u", cmd->id);
> + audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_FAILED);
I think we should use AUDIO_STATUS_FAILED instead of HAL_STATUS_FAILED
> + return;
> + }
> +
> + unregister_endpoint(endpoint);
>
> - audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_FAILED);
> + audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_SUCCESS);
Similar here.
> }
>
> static void bt_stream_open(const void *buf, uint16_t len)
> --
> 1.8.4.2
>
> --
\Lukasz
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Luiz,
On 11 January 2014 11:13, Luiz Augusto von Dentz <[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> ---
> android/a2dp.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 158 insertions(+), 1 deletion(-)
>
> diff --git a/android/a2dp.c b/android/a2dp.c
> index b59c53d..28b7406 100644
> --- a/android/a2dp.c
> +++ b/android/a2dp.c
> @@ -52,9 +52,23 @@
>
> static GIOChannel *server = NULL;
> static GSList *devices = NULL;
> +static GSList *endpoints = NULL;
> static bdaddr_t adapter_addr;
> static uint32_t record_id = 0;
>
> +struct a2dp_preset {
> + void *data;
> + int8_t len;
> +};
> +
> +struct a2dp_endpoint {
> + uint8_t id;
> + uint8_t codec;
> + struct avdtp_local_sep *sep;
> + struct a2dp_preset *caps;
> + GSList *presets;
> +};
> +
> struct a2dp_device {
> bdaddr_t dst;
> uint8_t state;
> @@ -70,6 +84,29 @@ static int device_cmp(gconstpointer s, gconstpointer user_data)
> return bacmp(&dev->dst, dst);
> }
>
> +static void preset_free(void *data)
> +{
> + struct a2dp_preset *preset = data;
> +
> + g_free(preset->data);
> + g_free(preset);
> +}
> +
> +static void unregister_endpoint(void *data)
> +{
> + struct a2dp_endpoint *endpoint = data;
> +
> + if (endpoint->sep)
> + avdtp_unregister_sep(endpoint->sep);
> +
> + if (endpoint->caps)
> + preset_free(endpoint->caps);
> +
> + g_slist_free_full(endpoint->presets, preset_free);
> +
> + g_free(endpoint);
> +}
> +
> static void a2dp_device_free(struct a2dp_device *dev)
> {
> if (dev->session)
> @@ -354,10 +391,127 @@ static sdp_record_t *a2dp_record(void)
> return record;
> }
>
> +static gboolean sep_getcap_ind(struct avdtp *session,
> + struct avdtp_local_sep *sep,
> + GSList **caps, uint8_t *err,
> + void *user_data)
> +{
> + struct a2dp_endpoint *endpoint = user_data;
> + struct a2dp_preset *cap = endpoint->presets->data;
> + struct avdtp_service_capability *media_transport, *media_codec;
> + struct avdtp_media_codec_capability *codec_caps;
> +
> + *caps = NULL;
> +
> + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
> + NULL, 0);
> +
> + *caps = g_slist_append(*caps, media_transport);
> +
> + codec_caps = g_malloc0(sizeof(*codec_caps) + sizeof(cap));
> + codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
> + codec_caps->media_codec_type = endpoint->codec;
> + memcpy(codec_caps->data, cap->data, cap->len);
> +
> + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
> + sizeof(*codec_caps) + sizeof(cap));
> +
> + *caps = g_slist_append(*caps, media_codec);
> + g_free(codec_caps);
> +
> + return TRUE;
> +}
> +
> +static struct avdtp_sep_ind sep_ind = {
> + .get_capability = sep_getcap_ind,
> +};
> +
> +static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
> + GSList *presets)
> +{
> + struct a2dp_endpoint *endpoint;
> +
> + /* FIXME: Add proper check for uuid */
> +
> + endpoint = g_new0(struct a2dp_endpoint, 1);
> + endpoint->id = g_slist_length(endpoints) + 1;
> + endpoint->codec = codec;
> + endpoint->sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE,
> + AVDTP_MEDIA_TYPE_AUDIO,
> + codec, FALSE, &sep_ind, NULL,
> + endpoint);
> + endpoint->caps = g_slist_nth_data(presets->data, 0);
> + endpoint->presets = g_slist_copy(g_slist_nth(presets, 1));
> +
> + endpoints = g_slist_append(endpoints, endpoint);
> +
> + return endpoint->id;
> +}
> +
> +static GSList *parse_presets(const struct audio_preset *p, uint8_t count,
> + uint16_t len)
> +{
> + GSList *l = NULL;
> + uint8_t i;
> +
> + for (i = 0; count > i; i++) {
> + struct a2dp_preset *preset;
> +
> + if (len < sizeof(struct audio_preset)) {
> + DBG("Invalid preset index %u", i);
> + break;
> + }
> +
> + len -= sizeof(struct audio_preset);
> + if (len == 0 || len < p->len) {
> + DBG("Invalid preset size of %u for index %u", len, i);
> + break;
> + }
> +
> + preset = g_new0(struct a2dp_preset, 1);
> + preset->len = p->len;
> + preset->data = g_memdup(p->data, preset->len);
> + l = g_slist_append(l, preset);
> +
> + len -= preset->len;
> + p += sizeof(struct audio_preset) + preset->len;
It would be better to explicitly use uint8_t pointer for presets
buffer since this will work only as long as sizeof(struct
audio_preset) is 1.
Andrzej
Hi Luiz,
On Saturday 11 January 2014 12:13:31 Luiz Augusto von Dentz wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> ---
> android/a2dp.c | 159
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed,
> 158 insertions(+), 1 deletion(-)
>
> diff --git a/android/a2dp.c b/android/a2dp.c
> index b59c53d..28b7406 100644
> --- a/android/a2dp.c
> +++ b/android/a2dp.c
> @@ -52,9 +52,23 @@
>
> static GIOChannel *server = NULL;
> static GSList *devices = NULL;
> +static GSList *endpoints = NULL;
> static bdaddr_t adapter_addr;
> static uint32_t record_id = 0;
>
> +struct a2dp_preset {
> + void *data;
> + int8_t len;
> +};
> +
> +struct a2dp_endpoint {
> + uint8_t id;
> + uint8_t codec;
> + struct avdtp_local_sep *sep;
> + struct a2dp_preset *caps;
> + GSList *presets;
> +};
> +
> struct a2dp_device {
> bdaddr_t dst;
> uint8_t state;
> @@ -70,6 +84,29 @@ static int device_cmp(gconstpointer s, gconstpointer
> user_data) return bacmp(&dev->dst, dst);
> }
>
> +static void preset_free(void *data)
> +{
> + struct a2dp_preset *preset = data;
> +
> + g_free(preset->data);
> + g_free(preset);
> +}
> +
> +static void unregister_endpoint(void *data)
> +{
> + struct a2dp_endpoint *endpoint = data;
> +
> + if (endpoint->sep)
> + avdtp_unregister_sep(endpoint->sep);
> +
> + if (endpoint->caps)
> + preset_free(endpoint->caps);
> +
> + g_slist_free_full(endpoint->presets, preset_free);
> +
> + g_free(endpoint);
> +}
> +
> static void a2dp_device_free(struct a2dp_device *dev)
> {
> if (dev->session)
> @@ -354,10 +391,127 @@ static sdp_record_t *a2dp_record(void)
> return record;
> }
>
> +static gboolean sep_getcap_ind(struct avdtp *session,
> + struct avdtp_local_sep *sep,
> + GSList **caps, uint8_t *err,
> + void *user_data)
> +{
> + struct a2dp_endpoint *endpoint = user_data;
> + struct a2dp_preset *cap = endpoint->presets->data;
> + struct avdtp_service_capability *media_transport, *media_codec;
> + struct avdtp_media_codec_capability *codec_caps;
> +
> + *caps = NULL;
> +
> + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
> + NULL, 0);
> +
> + *caps = g_slist_append(*caps, media_transport);
> +
> + codec_caps = g_malloc0(sizeof(*codec_caps) + sizeof(cap));
Shouldn't this be cap->len instead of sizeof(cap) ?
> + codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
> + codec_caps->media_codec_type = endpoint->codec;
> + memcpy(codec_caps->data, cap->data, cap->len);
> +
> + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
> + sizeof(*codec_caps) + sizeof(cap));
> +
> + *caps = g_slist_append(*caps, media_codec);
> + g_free(codec_caps);
> +
> + return TRUE;
> +}
> +
> +static struct avdtp_sep_ind sep_ind = {
> + .get_capability = sep_getcap_ind,
> +};
> +
> +static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
> + GSList *presets)
> +{
> + struct a2dp_endpoint *endpoint;
> +
> + /* FIXME: Add proper check for uuid */
> +
> + endpoint = g_new0(struct a2dp_endpoint, 1);
> + endpoint->id = g_slist_length(endpoints) + 1;
> + endpoint->codec = codec;
> + endpoint->sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE,
> + AVDTP_MEDIA_TYPE_AUDIO,
> + codec, FALSE, &sep_ind, NULL,
> + endpoint);
> + endpoint->caps = g_slist_nth_data(presets->data, 0);
> + endpoint->presets = g_slist_copy(g_slist_nth(presets, 1));
> +
> + endpoints = g_slist_append(endpoints, endpoint);
> +
> + return endpoint->id;
> +}
> +
> +static GSList *parse_presets(const struct audio_preset *p, uint8_t count,
> + uint16_t len)
> +{
> + GSList *l = NULL;
> + uint8_t i;
> +
> + for (i = 0; count > i; i++) {
> + struct a2dp_preset *preset;
> +
> + if (len < sizeof(struct audio_preset)) {
If Audio IPC command is malformed we should discard it, free list and return
NULL.
(I'm still not sure how daemon should behave if there is Audio IPC error,
possibly closing IPC socket and reset a2dp service state?)
> + DBG("Invalid preset index %u", i);
> + break;
> + }
> +
> + len -= sizeof(struct audio_preset);
> + if (len == 0 || len < p->len) {
> + DBG("Invalid preset size of %u for index %u", len, i);
> + break;
> + }
> +
> + preset = g_new0(struct a2dp_preset, 1);
> + preset->len = p->len;
> + preset->data = g_memdup(p->data, preset->len);
> + l = g_slist_append(l, preset);
> +
> + len -= preset->len;
> + p += sizeof(struct audio_preset) + preset->len;
> + }
> +
> + return l;
> +}
> +
> static void bt_audio_open(const void *buf, uint16_t len)
> {
> - DBG("Not Implemented");
> + const struct audio_cmd_open *cmd = buf;
> + struct audio_rsp_open rsp;
> + GSList *presets;
> +
> + DBG("");
>
> + if (cmd->presets == 0) {
> + error("No audio presets found");
> + goto failed;
> + }
> +
> + presets = parse_presets(cmd->preset, cmd->presets, len - sizeof(*cmd));
> + if (!presets) {
> + error("No audio presets found");
> + goto failed;
> + }
> +
> + rsp.id = register_endpoint(cmd->uuid, cmd->codec, presets);
> + g_slist_free(presets);
> +
> + if (rsp.id == 0) {
> + error("Unable to register endpoint");
> + goto failed;
> + }
> +
> + audio_ipc_send_rsp_full(AUDIO_OP_OPEN, sizeof(rsp), &rsp, -1);
> +
> + return;
> +
> +failed:
> audio_ipc_send_rsp(AUDIO_OP_OPEN, AUDIO_STATUS_FAILED);
> }
>
> @@ -471,6 +625,9 @@ void bt_a2dp_unregister(void)
> {
> DBG("");
>
> + g_slist_free_full(endpoints, unregister_endpoint);
> + endpoints = NULL;
> +
> g_slist_foreach(devices, a2dp_device_disconnected, NULL);
> devices = NULL;
--
Szymon K. Janc
[email protected]
From: Luiz Augusto von Dentz <[email protected]>
---
android/a2dp.c | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 28b7406..8649cf3 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -515,11 +515,37 @@ failed:
audio_ipc_send_rsp(AUDIO_OP_OPEN, AUDIO_STATUS_FAILED);
}
+static struct a2dp_endpoint *find_endpoint(uint8_t id)
+{
+ GSList *l;
+
+ for (l = endpoints; l; l = g_slist_next(l)) {
+ struct a2dp_endpoint *endpoint = l->data;
+
+ if (endpoint->id == id)
+ return endpoint;
+ }
+
+ return NULL;
+}
+
static void bt_audio_close(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_close *cmd = buf;
+ struct a2dp_endpoint *endpoint;
+
+ DBG("");
+
+ endpoint = find_endpoint(cmd->id);
+ if (!endpoint) {
+ error("Unable to find endpoint %u", cmd->id);
+ audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_FAILED);
+ return;
+ }
+
+ unregister_endpoint(endpoint);
- audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_FAILED);
+ audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_SUCCESS);
}
static void bt_stream_open(const void *buf, uint16_t len)
--
1.8.4.2
From: Luiz Augusto von Dentz <[email protected]>
---
android/a2dp.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 4e4f43f..2d886e9 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -777,8 +777,29 @@ failed:
static void bt_stream_resume(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_resume_stream *cmd = buf;
+ struct a2dp_setup *setup;
+ int err;
+
+ DBG("");
+ setup = find_setup(cmd->id);
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ goto failed;
+ }
+
+ err = avdtp_start(setup->dev->session, setup->stream);
+ if (err < 0) {
+ error("avdtp_start: %s", strerror(-err));
+ goto failed;
+ }
+
+ audio_ipc_send_rsp(AUDIO_OP_RESUME_STREAM, AUDIO_STATUS_SUCCESS);
+
+ return;
+
+failed:
audio_ipc_send_rsp(AUDIO_OP_RESUME_STREAM, AUDIO_STATUS_FAILED);
}
--
1.8.4.2
From: Luiz Augusto von Dentz <[email protected]>
---
android/a2dp.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 2d886e9..8d7d554 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -805,8 +805,29 @@ failed:
static void bt_stream_suspend(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_suspend_stream *cmd = buf;
+ struct a2dp_setup *setup;
+ int err;
+
+ DBG("");
+
+ setup = find_setup(cmd->id);
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ goto failed;
+ }
+
+ err = avdtp_suspend(setup->dev->session, setup->stream);
+ if (err < 0) {
+ error("avdtp_suspend: %s", strerror(-err));
+ goto failed;
+ }
+ audio_ipc_send_rsp(AUDIO_OP_SUSPEND_STREAM, AUDIO_STATUS_SUCCESS);
+
+ return;
+
+failed:
audio_ipc_send_rsp(AUDIO_OP_SUSPEND_STREAM, AUDIO_STATUS_FAILED);
}
--
1.8.4.2
From: Luiz Augusto von Dentz <[email protected]>
---
android/a2dp.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 479cb71..4e4f43f 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -749,8 +749,29 @@ static void bt_stream_open(const void *buf, uint16_t len)
static void bt_stream_close(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_close_stream *cmd = buf;
+ struct a2dp_setup *setup;
+ int err;
+ DBG("");
+
+ setup = find_setup(cmd->id);
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ goto failed;
+ }
+
+ err = avdtp_close(setup->dev->session, setup->stream, FALSE);
+ if (err < 0) {
+ error("avdtp_close: %s", strerror(-err));
+ goto failed;
+ }
+
+ audio_ipc_send_rsp(AUDIO_OP_CLOSE_STREAM, AUDIO_STATUS_SUCCESS);
+
+ return;
+
+failed:
audio_ipc_send_rsp(AUDIO_OP_CLOSE_STREAM, AUDIO_STATUS_FAILED);
}
--
1.8.4.2
From: Luiz Augusto von Dentz <[email protected]>
---
android/a2dp.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 197 insertions(+), 2 deletions(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 8649cf3..479cb71 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -37,6 +37,7 @@
#include "lib/bluetooth.h"
#include "lib/sdp.h"
#include "lib/sdp_lib.h"
+#include "profiles/audio/a2dp-codecs.h"
#include "log.h"
#include "a2dp.h"
#include "hal-msg.h"
@@ -53,6 +54,7 @@
static GIOChannel *server = NULL;
static GSList *devices = NULL;
static GSList *endpoints = NULL;
+static GSList *setups = NULL;
static bdaddr_t adapter_addr;
static uint32_t record_id = 0;
@@ -67,6 +69,7 @@ struct a2dp_endpoint {
struct avdtp_local_sep *sep;
struct a2dp_preset *caps;
GSList *presets;
+ struct a2dp_config *config;
};
struct a2dp_device {
@@ -76,6 +79,13 @@ struct a2dp_device {
struct avdtp *session;
};
+struct a2dp_setup {
+ struct a2dp_device *dev;
+ struct a2dp_endpoint *endpoint;
+ struct a2dp_preset *preset;
+ struct avdtp_stream *stream;
+};
+
static int device_cmp(gconstpointer s, gconstpointer user_data)
{
const struct a2dp_device *dev = s;
@@ -422,8 +432,160 @@ static gboolean sep_getcap_ind(struct avdtp *session,
return TRUE;
}
+static int sbc_check_config(struct a2dp_endpoint *endpoint,
+ struct a2dp_preset *conf)
+{
+ a2dp_sbc_t *caps, *config;
+
+ if (conf->len != sizeof(a2dp_sbc_t)) {
+ error("SBC: Invalid configuration size (%u)", conf->len);
+ return -EINVAL;
+ }
+
+ caps = endpoint->caps->data;
+ config = conf->data;
+
+ if (!(caps->frequency & config->frequency)) {
+ error("SBC: Unsupported frequency (%u) by endpoint",
+ config->frequency);
+ return -EINVAL;
+ }
+
+ if (!(caps->channel_mode & config->channel_mode)) {
+ error("SBC: Unsupported channel mode (%u) by endpoint",
+ config->channel_mode);
+ return -EINVAL;
+ }
+
+ if (!(caps->block_length & config->block_length)) {
+ error("SBC: Unsupported block length (%u) by endpoint",
+ config->block_length);
+ return -EINVAL;
+ }
+
+ if (!(caps->allocation_method & config->allocation_method)) {
+ error("SBC: Unsupported allocation method (%u) by endpoint",
+ config->block_length);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int check_config(struct a2dp_endpoint *endpoint,
+ struct a2dp_preset *config)
+{
+ GSList *l;
+
+ for (l = endpoint->presets; l; l = g_slist_next(l)) {
+ struct a2dp_preset *preset = l->data;
+
+ if (preset->len != config->len)
+ continue;
+
+ if (memcmp(preset->data, config->data, preset->len) == 0)
+ return 0;
+ }
+
+ /* Codec specific */
+ switch (endpoint->codec) {
+ case A2DP_CODEC_SBC:
+ return sbc_check_config(endpoint, config);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct a2dp_device *find_device_by_session(struct avdtp *session)
+{
+ GSList *l;
+
+ for (l = devices; l; l = g_slist_next(l)) {
+ struct a2dp_device *dev = l->data;
+
+ if (dev->session == session)
+ return dev;
+ }
+
+ return NULL;
+}
+
+static void setup_free(void *data)
+{
+ struct a2dp_setup *setup = data;
+
+ preset_free(setup->preset);
+ g_free(setup);
+}
+
+static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint,
+ struct a2dp_preset *preset, struct avdtp_stream *stream)
+{
+ struct a2dp_setup *setup;
+
+ setup = g_new0(struct a2dp_setup, 1);
+ setup->dev = dev;
+ setup->endpoint = endpoint;
+ setup->preset = preset;
+ setup->stream = stream;
+ setups = g_slist_append(setups, setup);
+}
+
+static gboolean sep_setconf_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ GSList *caps,
+ avdtp_set_configuration_cb cb,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_device *dev;
+ struct a2dp_preset *preset = NULL;
+
+ DBG("");
+
+ dev = find_device_by_session(session);
+ if (!dev) {
+ error("Unable to find device for session %p", session);
+ return FALSE;
+ }
+
+ for (; caps != NULL; caps = g_slist_next(caps)) {
+ struct avdtp_service_capability *cap = caps->data;
+ struct avdtp_media_codec_capability *codec;
+
+ if (cap->category == AVDTP_DELAY_REPORTING)
+ return FALSE;
+
+ if (cap->category != AVDTP_MEDIA_CODEC)
+ continue;
+
+ codec = (struct avdtp_media_codec_capability *) cap->data;
+
+ if (codec->media_codec_type != endpoint->codec)
+ return FALSE;
+
+ preset = g_new0(struct a2dp_preset, 1);
+ preset->len = cap->length - sizeof(*codec);
+ preset->data = g_memdup(codec->data, preset->len);
+
+ if (check_config(endpoint, preset) < 0) {
+ preset_free(preset);
+ return FALSE;
+ }
+ }
+
+ if (!preset)
+ return FALSE;
+
+ setup_add(dev, endpoint, preset, stream);
+
+ return TRUE;
+}
+
static struct avdtp_sep_ind sep_ind = {
.get_capability = sep_getcap_ind,
+ .set_configuration = sep_setconf_ind,
};
static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
@@ -548,11 +710,41 @@ static void bt_audio_close(const void *buf, uint16_t len)
audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_SUCCESS);
}
+static struct a2dp_setup *find_setup(uint8_t id)
+{
+ GSList *l;
+
+ for (l = setups; l; l = g_slist_next(l)) {
+ struct a2dp_setup *setup = l->data;
+
+ if (setup->endpoint->id == id)
+ return setup;
+ }
+
+ return NULL;
+}
+
static void bt_stream_open(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_open_stream *cmd = buf;
+ struct audio_rsp_open_stream *rsp;
+ struct a2dp_setup *setup;
+
+ DBG("");
- audio_ipc_send_rsp(AUDIO_OP_OPEN_STREAM, AUDIO_STATUS_FAILED);
+ setup = find_setup(cmd->id);
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ audio_ipc_send_rsp(AUDIO_OP_OPEN_STREAM, HAL_STATUS_FAILED);
+ return;
+ }
+
+ len = sizeof(*rsp) + setup->preset->len;
+ rsp = g_malloc0(sizeof(*rsp) + setup->preset->len);
+ rsp->preset->len = setup->preset->len;
+ memcpy(rsp->preset->data, setup->preset->data, setup->preset->len);
+
+ audio_ipc_send_rsp_full(AUDIO_OP_OPEN_STREAM, len, rsp, -1);
}
static void bt_stream_close(const void *buf, uint16_t len)
@@ -651,6 +843,9 @@ void bt_a2dp_unregister(void)
{
DBG("");
+ g_slist_free_full(setups, setup_free);
+ setups = NULL;
+
g_slist_free_full(endpoints, unregister_endpoint);
endpoints = NULL;
--
1.8.4.2