2014-01-15 18:03:46

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 00/11] Audio HAL implementation

Hi,

changes v1 -> v2
- fixed comments from Luiz
- removed "codec_" prefix from function related to SBC codec
- additional patch to implement set_parameters for audio device
(A2dpSuspend is initialized this way)
- changes dummy buffer size to 20*512 which is inline with Bluedroid
(AudioFlinger does crash when closing output stream when set to 512)


Andrzej Kaczmarek (10):
android/hal-audio: Add support to register audio endpoints
android/hal-audio: Add support to unregister audio endpoints
android/hal-audio: Add support to open output stream
android/hal-audio: Add support to close output stream
android/hal-audio: Add support to resume output stream
android/hal-audio: Add support to suspend output stream
android/hal-audio: Handle audio preset from stream
android/hal-audio: Fix module loading
android/hal-audio: Fix AudioFlinger crash
android/hal-audio: Implement set_parameters for device

Lukasz Rymanowski (1):
android/hal-audio: Add audio_ipc_cmd

android/Makefile.am | 1 +
android/hal-audio.c | 729 +++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 696 insertions(+), 34 deletions(-)

--
1.8.5.2



2014-01-16 14:13:18

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH v2 00/11] Audio HAL implementation

Hi Andrzej,

On Wed, Jan 15, 2014 at 8:03 PM, Andrzej Kaczmarek
<[email protected]> wrote:
> Hi,
>
> changes v1 -> v2
> - fixed comments from Luiz
> - removed "codec_" prefix from function related to SBC codec
> - additional patch to implement set_parameters for audio device
> (A2dpSuspend is initialized this way)
> - changes dummy buffer size to 20*512 which is inline with Bluedroid
> (AudioFlinger does crash when closing output stream when set to 512)
>
>
> Andrzej Kaczmarek (10):
> android/hal-audio: Add support to register audio endpoints
> android/hal-audio: Add support to unregister audio endpoints
> android/hal-audio: Add support to open output stream
> android/hal-audio: Add support to close output stream
> android/hal-audio: Add support to resume output stream
> android/hal-audio: Add support to suspend output stream
> android/hal-audio: Handle audio preset from stream
> android/hal-audio: Fix module loading
> android/hal-audio: Fix AudioFlinger crash
> android/hal-audio: Implement set_parameters for device
>
> Lukasz Rymanowski (1):
> android/hal-audio: Add audio_ipc_cmd
>
> android/Makefile.am | 1 +
> android/hal-audio.c | 729 +++++++++++++++++++++++++++++++++++++++++++++++++---
> 2 files changed, 696 insertions(+), 34 deletions(-)
>
> --
> 1.8.5.2

Pushed, thanks.

--
Luiz Augusto von Dentz

2014-01-15 18:03:57

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 11/11] android/hal-audio: Implement set_parameters for device

---
android/hal-audio.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 58f48bb..a435aec 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -943,8 +943,16 @@ static void audio_close_output_stream(struct audio_hw_device *dev,
static int audio_set_parameters(struct audio_hw_device *dev,
const char *kvpairs)
{
+ struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+ struct a2dp_stream_out *out = a2dp_dev->out;
+
DBG("");
- return -ENOSYS;
+
+ if (!out)
+ return 0;
+
+ return out->stream.common.set_parameters((struct audio_stream *) out,
+ kvpairs);
}

static char *audio_get_parameters(const struct audio_hw_device *dev,
--
1.8.5.2


2014-01-15 18:03:56

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 10/11] android/hal-audio: Fix AudioFlinger crash

We need to return some valid values for buffer size and latency so
AudioFlinger does not crash. For now just use some dummy values until
codec implementation is in place.
---
android/hal-audio.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 3c2dcb2..58f48bb 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -622,7 +622,7 @@ static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
static size_t out_get_buffer_size(const struct audio_stream *stream)
{
DBG("");
- return -ENOSYS;
+ return 20 * 512;
}

static uint32_t out_get_channels(const struct audio_stream *stream)
@@ -729,7 +729,7 @@ static char *out_get_parameters(const struct audio_stream *stream,
static uint32_t out_get_latency(const struct audio_stream_out *stream)
{
DBG("");
- return -ENOSYS;
+ return 0;
}

static int out_set_volume(struct audio_stream_out *stream, float left,
--
1.8.5.2


2014-01-15 18:03:53

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 07/11] android/hal-audio: Add support to suspend output stream

This patch adds support to suspend output stream via Audio IPC.
>From HAL perspective stream can be either in standby or suspended -
the former is default one and can be auto-resumed on write while the
latter cannot be resumed only after explicitly going into standby
on audio code request.
---
android/hal-audio.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 70 insertions(+), 3 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 1de8830..93db6eb 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -450,6 +450,21 @@ static int ipc_resume_stream_cmd(uint8_t endpoint_id)
return result;
}

+static int ipc_suspend_stream_cmd(uint8_t endpoint_id)
+{
+ struct audio_cmd_suspend_stream cmd;
+ int result;
+
+ DBG("");
+
+ cmd.id = endpoint_id;
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ return result;
+}
+
static int register_endpoints(void)
{
struct audio_endpoint *ep = &audio_endpoints[0];
@@ -548,8 +563,17 @@ static int out_set_format(struct audio_stream *stream, audio_format_t format)

static int out_standby(struct audio_stream *stream)
{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
DBG("");
- return -ENOSYS;
+
+ if (out->audio_state == AUDIO_A2DP_STATE_STARTED) {
+ if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+ return -1;
+ out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+ }
+
+ return 0;
}

static int out_dump(const struct audio_stream *stream, int fd)
@@ -560,8 +584,51 @@ static int out_dump(const struct audio_stream *stream, int fd)

static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
- DBG("");
- return -ENOSYS;
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+ char *kvpair;
+ char *str;
+ char *saveptr;
+ bool enter_suspend = false;
+ bool exit_suspend = false;
+
+ DBG("%s", kvpairs);
+
+ str = strdup(kvpairs);
+ kvpair = strtok_r(str, ";", &saveptr);
+
+ for (; kvpair && *kvpair; kvpair = strtok_r(NULL, ";", &saveptr)) {
+ char *keyval;
+
+ keyval = strchr(kvpair, '=');
+ if (!keyval)
+ continue;
+
+ *keyval = '\0';
+ keyval++;
+
+ if (!strcmp(kvpair, "closing")) {
+ if (!strcmp(keyval, "true"))
+ out->audio_state = AUDIO_A2DP_STATE_NONE;
+ } else if (!strcmp(kvpair, "A2dpSuspended")) {
+ if (!strcmp(keyval, "true"))
+ enter_suspend = true;
+ else
+ exit_suspend = true;
+ }
+ }
+
+ free(str);
+
+ if (enter_suspend && out->audio_state == AUDIO_A2DP_STATE_STARTED) {
+ if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+ return -1;
+ out->audio_state = AUDIO_A2DP_STATE_SUSPENDED;
+ }
+
+ if (exit_suspend && out->audio_state == AUDIO_A2DP_STATE_SUSPENDED)
+ out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+
+ return 0;
}

static char *out_get_parameters(const struct audio_stream *stream,
--
1.8.5.2


2014-01-15 18:03:50

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 04/11] android/hal-audio: Add support to open output stream

This patch adds support to open output stream via Audio IPC.
Since only SBC is supported, we always try to open stream for first
endpoint only which is enough.
---
android/hal-audio.c | 109 ++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 88 insertions(+), 21 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 13ae056..2abd92a 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -87,9 +87,23 @@ struct audio_endpoint {

static struct audio_endpoint audio_endpoints[MAX_AUDIO_ENDPOINTS];

+enum a2dp_state_t {
+ AUDIO_A2DP_STATE_NONE,
+ AUDIO_A2DP_STATE_STANDBY,
+ AUDIO_A2DP_STATE_SUSPENDED,
+ AUDIO_A2DP_STATE_STARTED
+};
+
+struct a2dp_stream_out {
+ struct audio_stream_out stream;
+
+ struct audio_endpoint *ep;
+ enum a2dp_state_t audio_state;
+};
+
struct a2dp_audio_dev {
struct audio_hw_device dev;
- struct audio_stream_out *out;
+ struct a2dp_stream_out *out;
};

static const a2dp_sbc_t sbc_presets[] = {
@@ -374,6 +388,38 @@ static int ipc_close_cmd(uint8_t endpoint_id)
return result;
}

+static int ipc_open_stream_cmd(uint8_t endpoint_id,
+ struct audio_preset **caps)
+{
+ char buf[BLUEZ_AUDIO_MTU];
+ struct audio_cmd_open_stream cmd;
+ struct audio_rsp_open_stream *rsp =
+ (struct audio_rsp_open_stream *) &buf;
+ size_t rsp_len = sizeof(buf);
+ int result;
+
+ DBG("");
+
+ if (!caps)
+ return AUDIO_STATUS_FAILED;
+
+ cmd.id = endpoint_id;
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+ sizeof(cmd), &cmd, &rsp_len, rsp, NULL);
+
+ if (result == AUDIO_STATUS_SUCCESS) {
+ size_t buf_len = sizeof(struct audio_preset) +
+ rsp->preset[0].len;
+ *caps = malloc(buf_len);
+ memcpy(*caps, &rsp->preset, buf_len);
+ } else {
+ *caps = NULL;
+ }
+
+ return result;
+}
+
static int register_endpoints(void)
{
struct audio_endpoint *ep = &audio_endpoints[0];
@@ -615,35 +661,56 @@ static int audio_open_output_stream(struct audio_hw_device *dev,

{
struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
- struct audio_stream_out *out;
+ struct a2dp_stream_out *out;
+ struct audio_preset *preset;

- out = calloc(1, sizeof(struct audio_stream_out));
+ out = calloc(1, sizeof(struct a2dp_stream_out));
if (!out)
return -ENOMEM;

DBG("");

- out->common.get_sample_rate = out_get_sample_rate;
- out->common.set_sample_rate = out_set_sample_rate;
- out->common.get_buffer_size = out_get_buffer_size;
- out->common.get_channels = out_get_channels;
- out->common.get_format = out_get_format;
- out->common.set_format = out_set_format;
- out->common.standby = out_standby;
- out->common.dump = out_dump;
- out->common.set_parameters = out_set_parameters;
- out->common.get_parameters = out_get_parameters;
- out->common.add_audio_effect = out_add_audio_effect;
- out->common.remove_audio_effect = out_remove_audio_effect;
- out->get_latency = out_get_latency;
- out->set_volume = out_set_volume;
- out->write = out_write;
- out->get_render_position = out_get_render_position;
-
- *stream_out = out;
+ out->stream.common.get_sample_rate = out_get_sample_rate;
+ out->stream.common.set_sample_rate = out_set_sample_rate;
+ out->stream.common.get_buffer_size = out_get_buffer_size;
+ out->stream.common.get_channels = out_get_channels;
+ out->stream.common.get_format = out_get_format;
+ out->stream.common.set_format = out_set_format;
+ out->stream.common.standby = out_standby;
+ out->stream.common.dump = out_dump;
+ out->stream.common.set_parameters = out_set_parameters;
+ out->stream.common.get_parameters = out_get_parameters;
+ out->stream.common.add_audio_effect = out_add_audio_effect;
+ out->stream.common.remove_audio_effect = out_remove_audio_effect;
+ out->stream.get_latency = out_get_latency;
+ out->stream.set_volume = out_set_volume;
+ out->stream.write = out_write;
+ out->stream.get_render_position = out_get_render_position;
+
+ /* TODO: for now we always use endpoint 0 */
+ out->ep = &audio_endpoints[0];
+
+ if (ipc_open_stream_cmd(out->ep->id, &preset) != AUDIO_STATUS_SUCCESS)
+ goto fail;
+
+ if (!preset)
+ goto fail;
+
+ /* TODO: initialize codec using received audio_preset */
+
+ free(preset);
+
+ *stream_out = &out->stream;
a2dp_dev->out = out;

+ out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+
return 0;
+
+fail:
+ free(out);
+ *stream_out = NULL;
+ return -EIO;
}

static void audio_close_output_stream(struct audio_hw_device *dev,
--
1.8.5.2


2014-01-15 18:03:55

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 09/11] android/hal-audio: Fix module loading

---
android/hal-audio.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 946531f..3c2dcb2 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -957,7 +957,7 @@ static char *audio_get_parameters(const struct audio_hw_device *dev,
static int audio_init_check(const struct audio_hw_device *dev)
{
DBG("");
- return -ENOSYS;
+ return 0;
}

static int audio_set_voice_volume(struct audio_hw_device *dev, float volume)
@@ -1194,6 +1194,7 @@ static int audio_open(const hw_module_t *module, const char *name,
if (!a2dp_dev)
return -ENOMEM;

+ a2dp_dev->dev.common.tag = HARDWARE_DEVICE_TAG;
a2dp_dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
a2dp_dev->dev.common.module = (struct hw_module_t *) module;
a2dp_dev->dev.common.close = audio_close;
--
1.8.5.2


2014-01-15 18:03:52

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 06/11] android/hal-audio: Add support to resume output stream

This patch adds support to resume output stream via Audio IPC.
Stream is automatically resumed on first write when stream is in
standby state.
---
android/hal-audio.c | 37 +++++++++++++++++++++++++++++++++++--
1 file changed, 35 insertions(+), 2 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 2a6b6c4..1de8830 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -435,6 +435,21 @@ static int ipc_close_stream_cmd(uint8_t endpoint_id)
return result;
}

+static int ipc_resume_stream_cmd(uint8_t endpoint_id)
+{
+ struct audio_cmd_resume_stream cmd;
+ int result;
+
+ DBG("");
+
+ cmd.id = endpoint_id;
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ return result;
+}
+
static int register_endpoints(void)
{
struct audio_endpoint *ep = &audio_endpoints[0];
@@ -473,8 +488,26 @@ static void unregister_endpoints(void)
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
size_t bytes)
{
- DBG("");
- return -ENOSYS;
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+ /* We can auto-start only from standby */
+ if (out->audio_state == AUDIO_A2DP_STATE_STANDBY) {
+ DBG("stream in standby, auto-start");
+
+ if (ipc_resume_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+ return -1;
+
+ out->audio_state = AUDIO_A2DP_STATE_STARTED;
+ }
+
+ if (out->audio_state != AUDIO_A2DP_STATE_STARTED) {
+ DBG("stream not started");
+ return -1;
+ }
+
+ /* TODO: encode data using codec */
+
+ return bytes;
}

static uint32_t out_get_sample_rate(const struct audio_stream *stream)
--
1.8.5.2


2014-01-15 18:03:49

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 03/11] android/hal-audio: Add support to unregister audio endpoints

---
android/hal-audio.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 6aad002..13ae056 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -359,6 +359,21 @@ static int ipc_open_cmd(const struct audio_codec *codec)
return rsp.id;
}

+static int ipc_close_cmd(uint8_t endpoint_id)
+{
+ struct audio_cmd_close cmd;
+ int result;
+
+ DBG("");
+
+ cmd.id = endpoint_id;
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ return result;
+}
+
static int register_endpoints(void)
{
struct audio_endpoint *ep = &audio_endpoints[0];
@@ -380,6 +395,20 @@ static int register_endpoints(void)
return AUDIO_STATUS_SUCCESS;
}

+static void unregister_endpoints(void)
+{
+ size_t i;
+
+ for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++) {
+ struct audio_endpoint *ep = &audio_endpoints[i];
+
+ if (ep->id) {
+ ipc_close_cmd(ep->id);
+ memset(ep, 0, sizeof(*ep));
+ }
+ }
+}
+
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
size_t bytes)
{
@@ -775,6 +804,8 @@ static void *ipc_handler(void *data)
if (register_endpoints() != AUDIO_STATUS_SUCCESS) {
error("audio: Failed to register endpoints");

+ unregister_endpoints();
+
shutdown(audio_sk, SHUT_RDWR);
continue;
}
@@ -798,6 +829,8 @@ static void *ipc_handler(void *data)
pthread_mutex_unlock(&close_mutex);
}

+ unregister_endpoints();
+
info("Closing Audio IPC thread");
return NULL;
}
--
1.8.5.2


2014-01-15 18:03:54

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 08/11] android/hal-audio: Handle audio preset from stream

This patch adds handling of audio preset received after stream is
opened. Preset is used to initialize codec and then to set input
configuration so audio subsystem can write data in a format that
codec can handle later.
---
android/hal-audio.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 102 insertions(+), 6 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 93db6eb..946531f 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -51,7 +51,15 @@ struct audio_input_config {
audio_format_t format;
};

+struct sbc_data {
+ a2dp_sbc_t sbc;
+};
+
static int sbc_get_presets(struct audio_preset *preset, size_t *len);
+static int sbc_init(struct audio_preset *preset, void **codec_data);
+static int sbc_cleanup(void *codec_data);
+static int sbc_get_config(void *codec_data,
+ struct audio_input_config *config);

struct audio_codec {
uint8_t type;
@@ -71,6 +79,10 @@ static const struct audio_codec audio_codecs[] = {
.type = A2DP_CODEC_SBC,

.get_presets = sbc_get_presets,
+
+ .init = sbc_init,
+ .cleanup = sbc_cleanup,
+ .get_config = sbc_get_config,
}
};

@@ -99,6 +111,7 @@ struct a2dp_stream_out {

struct audio_endpoint *ep;
enum a2dp_state_t audio_state;
+ struct audio_input_config cfg;
};

struct a2dp_audio_dev {
@@ -191,6 +204,64 @@ static int sbc_get_presets(struct audio_preset *preset, size_t *len)
return i;
}

+static int sbc_init(struct audio_preset *preset, void **codec_data)
+{
+ struct sbc_data *sbc_data;
+
+ DBG("");
+
+ if (preset->len != sizeof(a2dp_sbc_t)) {
+ DBG("preset size mismatch");
+ return AUDIO_STATUS_FAILED;
+ }
+
+ sbc_data = calloc(sizeof(struct sbc_data), 1);
+
+ memcpy(&sbc_data->sbc, preset->data, preset->len);
+
+ *codec_data = sbc_data;
+
+ return AUDIO_STATUS_SUCCESS;
+}
+
+static int sbc_cleanup(void *codec_data)
+{
+ DBG("");
+
+ free(codec_data);
+
+ return AUDIO_STATUS_SUCCESS;
+}
+
+static int sbc_get_config(void *codec_data,
+ struct audio_input_config *config)
+{
+ struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+ switch (sbc_data->sbc.frequency) {
+ case SBC_SAMPLING_FREQ_16000:
+ config->rate = 16000;
+ break;
+ case SBC_SAMPLING_FREQ_32000:
+ config->rate = 32000;
+ break;
+ case SBC_SAMPLING_FREQ_44100:
+ config->rate = 44100;
+ break;
+ case SBC_SAMPLING_FREQ_48000:
+ config->rate = 48000;
+ break;
+ default:
+ return AUDIO_STATUS_FAILED;
+ }
+ config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ?
+ AUDIO_CHANNEL_OUT_MONO :
+ AUDIO_CHANNEL_OUT_STEREO;
+ config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+ return AUDIO_STATUS_SUCCESS;
+}
+
static void audio_ipc_cleanup(void)
{
if (audio_sk >= 0) {
@@ -527,14 +598,25 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,

static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
DBG("");
- return -ENOSYS;
+
+ return out->cfg.rate;
}

static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
DBG("");
- return -ENOSYS;
+
+ if (rate != out->cfg.rate) {
+ DBG("cannot set sample rate to %d", rate);
+ return -1;
+ }
+
+ return 0;
}

static size_t out_get_buffer_size(const struct audio_stream *stream)
@@ -545,14 +627,20 @@ static size_t out_get_buffer_size(const struct audio_stream *stream)

static uint32_t out_get_channels(const struct audio_stream *stream)
{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
DBG("");
- return -ENOSYS;
+
+ return out->cfg.channels;
}

static audio_format_t out_get_format(const struct audio_stream *stream)
{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
DBG("");
- return -ENOSYS;
+
+ return out->cfg.format;
}

static int out_set_format(struct audio_stream *stream, audio_format_t format)
@@ -778,6 +866,7 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
struct a2dp_stream_out *out;
struct audio_preset *preset;
+ const struct audio_codec *codec;

out = calloc(1, sizeof(struct a2dp_stream_out));
if (!out)
@@ -811,7 +900,13 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
if (!preset)
goto fail;

- /* TODO: initialize codec using received audio_preset */
+ codec = out->ep->codec;
+
+ codec->init(preset, &out->ep->codec_data);
+ codec->get_config(out->ep->codec_data, &out->cfg);
+
+ DBG("rate=%d channels=%d format=%d", out->cfg.rate,
+ out->cfg.channels, out->cfg.format);

free(preset);

@@ -838,7 +933,8 @@ static void audio_close_output_stream(struct audio_hw_device *dev,

ipc_close_stream_cmd(ep->id);

- /* TODO: cleanup codec */
+ ep->codec->cleanup(ep->codec_data);
+ ep->codec_data = NULL;

free(stream);
a2dp_dev->out = NULL;
--
1.8.5.2


2014-01-15 18:03:48

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 02/11] android/hal-audio: Add support to register audio endpoints

This patch adds support to register audio enpoints via Audio IPC.
Endpoints are registered based on predefined codecs table and for
each defined codec one endpoint is registered. By default, only
SBC will be supported.
---
android/hal-audio.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 187 insertions(+)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 354c3cf..6aad002 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -31,6 +31,11 @@
#include "audio-msg.h"
#include "hal-log.h"
#include "hal-msg.h"
+#include "../profiles/audio/a2dp-codecs.h"
+
+static const uint8_t a2dp_src_uuid[] = {
+ 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };

static int listen_sk = -1;
static int audio_sk = -1;
@@ -40,11 +45,138 @@ static pthread_t ipc_th = 0;
static pthread_mutex_t close_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;

+struct audio_input_config {
+ uint32_t rate;
+ uint32_t channels;
+ audio_format_t format;
+};
+
+static int sbc_get_presets(struct audio_preset *preset, size_t *len);
+
+struct audio_codec {
+ uint8_t type;
+
+ int (*get_presets) (struct audio_preset *preset, size_t *len);
+
+ int (*init) (struct audio_preset *preset, void **codec_data);
+ int (*cleanup) (void *codec_data);
+ int (*get_config) (void *codec_data,
+ struct audio_input_config *config);
+ ssize_t (*write_data) (void *codec_data, const void *buffer,
+ size_t bytes);
+};
+
+static const struct audio_codec audio_codecs[] = {
+ {
+ .type = A2DP_CODEC_SBC,
+
+ .get_presets = sbc_get_presets,
+ }
+};
+
+#define NUM_CODECS (sizeof(audio_codecs) / sizeof(audio_codecs[0]))
+
+#define MAX_AUDIO_ENDPOINTS NUM_CODECS
+
+struct audio_endpoint {
+ uint8_t id;
+ const struct audio_codec *codec;
+ void *codec_data;
+ int fd;
+};
+
+static struct audio_endpoint audio_endpoints[MAX_AUDIO_ENDPOINTS];
+
struct a2dp_audio_dev {
struct audio_hw_device dev;
struct audio_stream_out *out;
};

+static const a2dp_sbc_t sbc_presets[] = {
+ {
+ .frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,
+ .channel_mode = SBC_CHANNEL_MODE_MONO |
+ SBC_CHANNEL_MODE_DUAL_CHANNEL |
+ SBC_CHANNEL_MODE_STEREO |
+ SBC_CHANNEL_MODE_JOINT_STEREO,
+ .subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,
+ .allocation_method = SBC_ALLOCATION_SNR |
+ SBC_ALLOCATION_LOUDNESS,
+ .block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
+ SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
+ .min_bitpool = MIN_BITPOOL,
+ .max_bitpool = MAX_BITPOOL
+ },
+ /* middle quality */
+ {
+ .frequency = SBC_SAMPLING_FREQ_44100,
+ .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+ .subbands = SBC_SUBBANDS_8,
+ .allocation_method = SBC_ALLOCATION_LOUDNESS,
+ .block_length = SBC_BLOCK_LENGTH_16,
+ .min_bitpool = MIN_BITPOOL,
+ .max_bitpool = 35
+ },
+ {
+ .frequency = SBC_SAMPLING_FREQ_48000,
+ .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+ .subbands = SBC_SUBBANDS_8,
+ .allocation_method = SBC_ALLOCATION_LOUDNESS,
+ .block_length = SBC_BLOCK_LENGTH_16,
+ .min_bitpool = MIN_BITPOOL,
+ .max_bitpool = 33
+ },
+ /* high quality */
+ {
+ .frequency = SBC_SAMPLING_FREQ_44100,
+ .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+ .subbands = SBC_SUBBANDS_8,
+ .allocation_method = SBC_ALLOCATION_LOUDNESS,
+ .block_length = SBC_BLOCK_LENGTH_16,
+ .min_bitpool = MIN_BITPOOL,
+ .max_bitpool = 53
+ },
+ {
+ .frequency = SBC_SAMPLING_FREQ_48000,
+ .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+ .subbands = SBC_SUBBANDS_8,
+ .allocation_method = SBC_ALLOCATION_LOUDNESS,
+ .block_length = SBC_BLOCK_LENGTH_16,
+ .min_bitpool = MIN_BITPOOL,
+ .max_bitpool = 51
+ },
+};
+
+static int sbc_get_presets(struct audio_preset *preset, size_t *len)
+{
+ int i;
+ int count;
+ size_t new_len = 0;
+ uint8_t *ptr = (uint8_t *) preset;
+ size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t);
+
+ DBG("");
+
+ count = sizeof(sbc_presets) / sizeof(sbc_presets[0]);
+
+ for (i = 0; i < count; i++) {
+ preset = (struct audio_preset *) ptr;
+
+ if (new_len + preset_size > *len)
+ break;
+
+ preset->len = sizeof(a2dp_sbc_t);
+ memcpy(preset->data, &sbc_presets[i], preset->len);
+
+ new_len += preset_size;
+ ptr += preset_size;
+ }
+
+ *len = new_len;
+
+ return i;
+}
+
static void audio_ipc_cleanup(void)
{
if (audio_sk >= 0) {
@@ -200,6 +332,54 @@ failed:
return AUDIO_STATUS_FAILED;
}

+static int ipc_open_cmd(const struct audio_codec *codec)
+{
+ uint8_t buf[BLUEZ_AUDIO_MTU];
+ struct audio_cmd_open *cmd = (struct audio_cmd_open *) buf;
+ struct audio_rsp_open rsp;
+ size_t cmd_len = sizeof(buf) - sizeof(*cmd);
+ size_t rsp_len = sizeof(rsp);
+ int result;
+
+ DBG("");
+
+ memcpy(cmd->uuid, a2dp_src_uuid, sizeof(a2dp_src_uuid));
+
+ cmd->codec = codec->type;
+ cmd->presets = codec->get_presets(cmd->preset, &cmd_len);
+
+ cmd_len += sizeof(*cmd);
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN, cmd_len, cmd,
+ &rsp_len, &rsp, NULL);
+
+ if (result != AUDIO_STATUS_SUCCESS)
+ return 0;
+
+ return rsp.id;
+}
+
+static int register_endpoints(void)
+{
+ struct audio_endpoint *ep = &audio_endpoints[0];
+ size_t i;
+
+ for (i = 0; i < NUM_CODECS; i++, ep++) {
+ const struct audio_codec *codec = &audio_codecs[i];
+
+ ep->id = ipc_open_cmd(codec);
+
+ if (!ep->id)
+ return AUDIO_STATUS_FAILED;
+
+ ep->codec = codec;
+ ep->codec_data = NULL;
+ ep->fd = -1;
+ }
+
+ return AUDIO_STATUS_SUCCESS;
+}
+
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
size_t bytes)
{
@@ -592,6 +772,13 @@ static void *ipc_handler(void *data)

DBG("Audio IPC: Connected");

+ if (register_endpoints() != AUDIO_STATUS_SUCCESS) {
+ error("audio: Failed to register endpoints");
+
+ shutdown(audio_sk, SHUT_RDWR);
+ continue;
+ }
+
memset(&pfd, 0, sizeof(pfd));
pfd.fd = audio_sk;
pfd.events = POLLHUP | POLLERR | POLLNVAL;
--
1.8.5.2


2014-01-15 18:03:51

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 05/11] android/hal-audio: Add support to close output stream

---
android/hal-audio.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 2abd92a..2a6b6c4 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -420,6 +420,21 @@ static int ipc_open_stream_cmd(uint8_t endpoint_id,
return result;
}

+static int ipc_close_stream_cmd(uint8_t endpoint_id)
+{
+ struct audio_cmd_close_stream cmd;
+ int result;
+
+ DBG("");
+
+ cmd.id = endpoint_id;
+
+ result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+ sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+ return result;
+}
+
static int register_endpoints(void)
{
struct audio_endpoint *ep = &audio_endpoints[0];
@@ -717,9 +732,14 @@ static void audio_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream)
{
struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+ struct audio_endpoint *ep = a2dp_dev->out->ep;

DBG("");

+ ipc_close_stream_cmd(ep->id);
+
+ /* TODO: cleanup codec */
+
free(stream);
a2dp_dev->out = NULL;
}
--
1.8.5.2


2014-01-15 18:03:47

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [PATCH v2 01/11] android/hal-audio: Add audio_ipc_cmd

From: Lukasz Rymanowski <[email protected]>

Add function to handle send/receive on audio_sk.
---
android/Makefile.am | 1 +
android/hal-audio.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 150 insertions(+)

diff --git a/android/Makefile.am b/android/Makefile.am
index 7806f79..e09f967 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -120,6 +120,7 @@ android_android_tester_LDFLAGS = -pthread -ldl
plugin_LTLIBRARIES += android/audio.a2dp.default.la

android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
+ android/hal-msg.h \
android/hal-audio.c \
android/hardware/audio.h \
android/hardware/audio_effect.h \
diff --git a/android/hal-audio.c b/android/hal-audio.c
index c51b065..354c3cf 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -30,6 +30,7 @@

#include "audio-msg.h"
#include "hal-log.h"
+#include "hal-msg.h"

static int listen_sk = -1;
static int audio_sk = -1;
@@ -37,6 +38,7 @@ static bool close_thread = false;

static pthread_t ipc_th = 0;
static pthread_mutex_t close_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;

struct a2dp_audio_dev {
struct audio_hw_device dev;
@@ -51,6 +53,153 @@ static void audio_ipc_cleanup(void)
}
}

+static int audio_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
+ void *param, size_t *rsp_len, void *rsp, int *fd)
+{
+ ssize_t ret;
+ struct msghdr msg;
+ struct iovec iv[2];
+ struct hal_hdr cmd;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ struct hal_status s;
+ size_t s_len = sizeof(s);
+
+ if (audio_sk < 0) {
+ error("audio: Invalid cmd socket passed to audio_ipc_cmd");
+ goto failed;
+ }
+
+ if (!rsp || !rsp_len) {
+ memset(&s, 0, s_len);
+ rsp_len = &s_len;
+ rsp = &s;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.service_id = service_id;
+ cmd.opcode = opcode;
+ cmd.len = len;
+
+ iv[0].iov_base = &cmd;
+ iv[0].iov_len = sizeof(cmd);
+
+ iv[1].iov_base = param;
+ iv[1].iov_len = len;
+
+ msg.msg_iov = iv;
+ msg.msg_iovlen = 2;
+
+ pthread_mutex_lock(&sk_mutex);
+
+ ret = sendmsg(audio_sk, &msg, 0);
+ if (ret < 0) {
+ error("audio: Sending command failed:%s", strerror(errno));
+ pthread_mutex_unlock(&sk_mutex);
+ goto failed;
+ }
+
+ /* socket was shutdown */
+ if (ret == 0) {
+ error("audio: Command socket closed");
+ goto failed;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&cmd, 0, sizeof(cmd));
+
+ iv[0].iov_base = &cmd;
+ iv[0].iov_len = sizeof(cmd);
+
+ iv[1].iov_base = rsp;
+ iv[1].iov_len = *rsp_len;
+
+ msg.msg_iov = iv;
+ msg.msg_iovlen = 2;
+
+ if (fd) {
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof(cmsgbuf);
+ }
+
+ ret = recvmsg(audio_sk, &msg, 0);
+ if (ret < 0) {
+ error("audio: Receiving command response failed:%s",
+ strerror(errno));
+ pthread_mutex_unlock(&sk_mutex);
+ goto failed;
+ }
+
+ pthread_mutex_unlock(&sk_mutex);
+
+ if (ret < (ssize_t) sizeof(cmd)) {
+ error("audio: Too small response received(%zd bytes)", ret);
+ goto failed;
+ }
+
+ if (cmd.service_id != service_id) {
+ error("audio: Invalid service id (%u vs %u)", cmd.service_id,
+ service_id);
+ goto failed;
+ }
+
+ if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+ error("audio: Malformed response received(%zd bytes)", ret);
+ goto failed;
+ }
+
+ if (cmd.opcode != opcode && cmd.opcode != AUDIO_OP_STATUS) {
+ error("audio: Invalid opcode received (%u vs %u)",
+ cmd.opcode, opcode);
+ goto failed;
+ }
+
+ if (cmd.opcode == AUDIO_OP_STATUS) {
+ struct hal_status *s = rsp;
+
+ if (sizeof(*s) != cmd.len) {
+ error("audio: Invalid status length");
+ goto failed;
+ }
+
+ if (s->code == AUDIO_STATUS_SUCCESS) {
+ error("audio: Invalid success status response");
+ goto failed;
+ }
+
+ return s->code;
+ }
+
+ /* Receive auxiliary data in msg */
+ if (fd) {
+ struct cmsghdr *cmsg;
+
+ *fd = -1;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_RIGHTS) {
+ memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+ break;
+ }
+ }
+ }
+
+ if (rsp_len)
+ *rsp_len = cmd.len;
+
+ return AUDIO_STATUS_SUCCESS;
+
+failed:
+ /* Some serious issue happen on IPC - recover */
+ shutdown(audio_sk, SHUT_RDWR);
+ audio_sk = -1;
+ return AUDIO_STATUS_FAILED;
+}
+
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
size_t bytes)
{
--
1.8.5.2