From: Andrei Emeltchenko <[email protected]>
Fixes static analyzers NULL dereference warnings. The similar check is
done in src/device.c in line 2959.
---
android/handsfree.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/android/handsfree.c b/android/handsfree.c
index 02b6dad..c8026a0 100644
--- a/android/handsfree.c
+++ b/android/handsfree.c
@@ -1405,6 +1405,9 @@ static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data)
goto fail;
}
+ if (!classes)
+ goto fail;
+
if (sdp_get_access_protos(recs->data, &protos) < 0) {
error("handsfree: unable to get access protocols from record");
sdp_list_free(classes, free);
@@ -1495,6 +1498,9 @@ static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
goto fail;
}
+ if (!classes)
+ goto fail;
+
if (sdp_get_access_protos(recs->data, &protos) < 0) {
error("handsfree: unable to get access protocols from record");
sdp_list_free(classes, free);
--
1.8.3.2
Ok, thanks
-----Original Message-----
From: Luiz Augusto von Dentz [mailto:[email protected]]
Sent: Wednesday, April 23, 2014 2:41 PM
To: Jesse Koegler
Cc: Andrei Emeltchenko; [email protected]
Subject: Re: [PATCH 5/7] android/hal-audio: Add SCO audio API
Hi Jesse,
On Wed, Apr 23, 2014 at 5:06 PM, Jesse Koegler
<[email protected]> wrote:
> All,
>
> I am currently using bluez 4.98 (time to update, I know). Within 4.98 I
have
> been unable to find a way to control the responses to SCO link requests.
> Will this or any other patches since 4.98 allow controlling of the
responses
> to SCO link requests? To my understanding this is done on the chip level
and
> is outside the scope of the stack, has that changed at all?
Please don't hijack threads with unrelated questions, create a new
thread... Regarding your question, have a look at the SCO socket API
> Best,
>
> Jesse Koegler
>
> -----Original Message-----
> From: [email protected]
> [mailto:[email protected]] On Behalf Of Andrei
> Emeltchenko
> Sent: Thursday, April 17, 2014 8:24 AM
> To: [email protected]
> Subject: [PATCH 5/7] android/hal-audio: Add SCO audio API
>
> From: Andrei Emeltchenko <[email protected]>
>
> ---
> android/audio-msg.h | 6 +-
> android/hal-audio-hsp.c | 314
> +++++++++++++++++++++++++++++++++++++++++++++++-
> android/handsfree.c | 42 +++++++
> 3 files changed, 358 insertions(+), 4 deletions(-)
>
> diff --git a/android/audio-msg.h b/android/audio-msg.h
> index 5981355..d247c80 100644
> --- a/android/audio-msg.h
> +++ b/android/audio-msg.h
> @@ -24,9 +24,11 @@
> #define BLUEZ_AUDIO_MTU 1024
>
> static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
> +static const char BLUEZ_AUDIO_SCO_SK_PATH[] = "\0bluez_audio_sco_socket";
>
> #define AUDIO_SERVICE_ID 0
> -#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_ID
> +#define AUDIO_SERVICE_SCO_ID 1
> +#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_SCO_ID
>
> #define AUDIO_STATUS_SUCCESS IPC_STATUS_SUCCESS
> #define AUDIO_STATUS_FAILED 0x01
> @@ -79,3 +81,5 @@ struct audio_cmd_resume_stream {
> struct audio_cmd_suspend_stream {
> uint8_t id;
> } __attribute__((packed));
> +
> +#define AUDIO_OP_SCO_GET_FD 0x01
> diff --git a/android/hal-audio-hsp.c b/android/hal-audio-hsp.c
> index 992066c..cac6c8d 100644
> --- a/android/hal-audio-hsp.c
> +++ b/android/hal-audio-hsp.c
> @@ -16,13 +16,20 @@
> */
>
> #include <errno.h>
> +#include <pthread.h>
> +#include <poll.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> +#include <sys/socket.h>
> +#include <sys/un.h>
> +#include <unistd.h>
>
> #include <hardware/audio.h>
> #include <hardware/hardware.h>
>
> +#include "audio-msg.h"
> +#include "ipc-common.h"
> #include "hal-log.h"
>
> #define AUDIO_STREAM_DEFAULT_RATE 44100
> @@ -30,6 +37,12 @@
>
> #define OUT_BUFFER_SIZE 2560
>
> +static int listen_sk = -1;
> +static int audio_sk = -1;
> +
> +static pthread_t ipc_th = 0;
> +static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
> +
> struct hsp_audio_config {
> uint32_t rate;
> uint32_t channels;
> @@ -38,7 +51,9 @@ struct hsp_audio_config {
>
> struct hsp_stream_out {
> struct audio_stream_out stream;
> +
> struct hsp_audio_config cfg;
> + int fd;
> };
>
> struct hsp_audio_dev {
> @@ -46,13 +61,178 @@ struct hsp_audio_dev {
> struct hsp_stream_out *out;
> };
>
> +/* Audio IPC functions */
> +
> +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 ipc_hdr cmd;
> + char cmsgbuf[CMSG_SPACE(sizeof(int))];
> + struct ipc_status s;
> + size_t s_len = sizeof(s);
> +
> + pthread_mutex_lock(&sk_mutex);
> +
> + 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;
> +
> + ret = sendmsg(audio_sk, &msg, 0);
> + if (ret < 0) {
> + error("audio: Sending command failed:%s",
strerror(errno));
> + 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));
> + goto failed;
> + }
> +
> + 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 ipc_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;
> + }
> +
> + pthread_mutex_unlock(&sk_mutex);
> +
> + return s->code;
> + }
> +
> + pthread_mutex_unlock(&sk_mutex);
> +
> + /* 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 (*fd < 0)
> + goto failed;
> + }
> +
> + if (rsp_len)
> + *rsp_len = cmd.len;
> +
> + return AUDIO_STATUS_SUCCESS;
> +
> +failed:
> + /* Some serious issue happen on IPC - recover */
> + shutdown(audio_sk, SHUT_RDWR);
> + pthread_mutex_unlock(&sk_mutex);
> +
> + return AUDIO_STATUS_FAILED;
> +}
> +
> +static int ipc_get_sco_fd(int *fd)
> +{
> + DBG("");
> +
> + return audio_ipc_cmd(AUDIO_SERVICE_SCO_ID, AUDIO_OP_SCO_GET_FD, 0,
> + NULL, NULL, NULL,
> fd);
> +}
> +
> /* Audio stream functions */
>
> static ssize_t out_write(struct audio_stream_out *stream, const void
> *buffer,
> size_t
> bytes)
> {
> + struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
> +
> /* write data */
>
> + DBG("write to fd %d bytes %zu", out->fd, bytes);
> +
> return bytes;
> }
>
> @@ -60,7 +240,7 @@ static uint32_t out_get_sample_rate(const struct
> audio_stream *stream)
> {
> struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
>
> - DBG("");
> + DBG("rate %u", out->cfg.rate);
>
> return out->cfg.rate;
> }
> @@ -74,7 +254,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("");
> + DBG("buf size %u", OUT_BUFFER_SIZE);
>
> return OUT_BUFFER_SIZE;
> }
> @@ -182,9 +362,17 @@ static int audio_open_output_stream(struct
> audio_hw_device *dev,
> {
> struct hsp_audio_dev *adev = (struct hsp_audio_dev *) dev;
> struct hsp_stream_out *out;
> + int fd = -1;
>
> DBG("");
>
> + if (!ipc_get_sco_fd(&fd)) {
> + error("audio: cannot get fd");
> + return -EIO;
> + }
> +
> + DBG("got sco fd %d", fd);
> +
> out = calloc(1, sizeof(struct hsp_stream_out));
> if (!out)
> return -ENOMEM;
> @@ -212,6 +400,7 @@ static int audio_open_output_stream(struct
> audio_hw_device *dev,
>
> *stream_out = &out->stream;
> adev->out = out;
> + out->fd = fd;
>
> return 0;
> }
> @@ -219,9 +408,17 @@ static int audio_open_output_stream(struct
> audio_hw_device *dev,
> static void audio_close_output_stream(struct audio_hw_device *dev,
> struct audio_stream_out
*stream_out)
> {
> - DBG("");
> + struct hsp_audio_dev *hsp_dev = (struct hsp_audio_dev *) dev;
> +
> + DBG("dev %p stream %p fd %d", dev, stream_out, hsp_dev->out->fd);
> +
> + if (hsp_dev->out && hsp_dev->out->fd) {
> + close(hsp_dev->out->fd);
> + hsp_dev->out->fd = -1;
> + }
>
> free(stream_out);
> + hsp_dev->out = NULL;
> }
>
> static int audio_set_parameters(struct audio_hw_device *dev,
> @@ -325,10 +522,117 @@ static int audio_close(hw_device_t *device)
> return 0;
> }
>
> +static void *ipc_handler(void *data)
> +{
> + bool done = false;
> + struct pollfd pfd;
> + int sk;
> +
> + DBG("");
> +
> + while (!done) {
> + DBG("Waiting for connection ...");
> +
> + sk = accept(listen_sk, NULL, NULL);
> + if (sk < 0) {
> + int err = errno;
> +
> + if (err == EINTR)
> + continue;
> +
> + if (err != ECONNABORTED && err != EINVAL)
> + error("audio: Failed to accept socket: %d
> (%s)",
> + err,
strerror(err));
> +
> + break;
> + }
> +
> + pthread_mutex_lock(&sk_mutex);
> + audio_sk = sk;
> + pthread_mutex_unlock(&sk_mutex);
> +
> + DBG("Audio IPC: Connected");
> +
> + memset(&pfd, 0, sizeof(pfd));
> + pfd.fd = audio_sk;
> + pfd.events = POLLHUP | POLLERR | POLLNVAL;
> +
> + /* Check if socket is still alive. Empty while loop.*/
> + while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
> +
> + if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
> + info("Audio HAL: Socket closed");
> +
> + pthread_mutex_lock(&sk_mutex);
> + close(audio_sk);
> + audio_sk = -1;
> + pthread_mutex_unlock(&sk_mutex);
> + }
> + }
> +
> + info("Closing Audio IPC thread");
> + return NULL;
> +}
> +
> +static int audio_ipc_init(void)
> +{
> + struct sockaddr_un addr;
> + int err;
> + int sk;
> +
> + DBG("");
> +
> + sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
> + if (sk < 0) {
> + err = -errno;
> + error("audio: Failed to create socket: %d (%s)", -err,
> +
> strerror(-err));
> + return err;
> + }
> +
> + memset(&addr, 0, sizeof(addr));
> + addr.sun_family = AF_UNIX;
> +
> + memcpy(addr.sun_path, BLUEZ_AUDIO_SCO_SK_PATH,
> + sizeof(BLUEZ_AUDIO_SCO_SK_PATH));
> +
> + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
> + err = -errno;
> + error("audio: Failed to bind socket: %d (%s)", -err,
> +
> strerror(-err));
> + goto failed;
> + }
> +
> + if (listen(sk, 1) < 0) {
> + err = -errno;
> + error("audio: Failed to listen on the socket: %d (%s)",
> -err,
> +
> strerror(-err));
> + goto failed;
> + }
> +
> + listen_sk = sk;
> +
> + err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
> + if (err) {
> + err = -err;
> + ipc_th = 0;
> + error("audio: Failed to start Audio IPC thread: %d (%s)",
> + -err,
> strerror(-err));
> + goto failed;
> + }
> +
> + return 0;
> +
> +failed:
> + close(sk);
> + return err;
> +}
> +
> static int audio_open(const hw_module_t *module, const char *name,
> hw_device_t
> **device)
> {
> struct hsp_audio_dev *adev;
> + int err;
>
> DBG("");
>
> @@ -338,6 +642,10 @@ static int audio_open(const hw_module_t *module,
const
> char *name,
> return -EINVAL;
> }
>
> + err = audio_ipc_init();
> + if (err < 0)
> + return err;
> +
> adev = calloc(1, sizeof(struct hsp_audio_dev));
> if (!adev)
> return -ENOMEM;
> diff --git a/android/handsfree.c b/android/handsfree.c
> index c8026a0..55e151a 100644
> --- a/android/handsfree.c
> +++ b/android/handsfree.c
> @@ -45,6 +45,7 @@
> #include "bluetooth.h"
> #include "src/log.h"
> #include "utils.h"
> +#include "audio-msg.h"
>
> #define HSP_AG_CHANNEL 12
> #define HFP_AG_CHANNEL 13
> @@ -156,7 +157,9 @@ static struct {
> static uint32_t hfp_ag_features = 0;
>
> static bdaddr_t adapter_addr;
> +
> static struct ipc *hal_ipc = NULL;
> +static struct ipc *audio_ipc = NULL;
>
> static uint32_t hfp_record_id = 0;
> static GIOChannel *hfp_server = NULL;
> @@ -2563,6 +2566,42 @@ static void disable_sco_server(void)
> }
> }
>
> +static void bt_audio_sco_get_fd(const void *buf, uint16_t len)
> +{
> + int fd = -1;
> +
> + connect_audio();
> +
> + if (device.sco)
> + fd = g_io_channel_unix_get_fd(device.sco);
> +
> + DBG("sco fd %d", fd);
> +
> + ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_SCO_ID,
> AUDIO_OP_SCO_GET_FD,
> + 0, NULL,
> fd);
> +}
> +
> +static const struct ipc_handler audio_handlers[] = {
> + /* AUDIO_OP_SCO_GET_FD */
> + { bt_audio_sco_get_fd, false, 0 }
> +};
> +
> +static bool bt_audio_register(ipc_disconnect_cb disconnect)
> +{
> + DBG("");
> +
> + audio_ipc = ipc_init(BLUEZ_AUDIO_SCO_SK_PATH,
> + sizeof(BLUEZ_AUDIO_SCO_SK_PATH),
> + AUDIO_SERVICE_ID_MAX, false, disconnect,
> NULL);
> + if (!audio_ipc)
> + return false;
> +
> + ipc_register(audio_ipc, AUDIO_SERVICE_SCO_ID, audio_handlers,
> +
> G_N_ELEMENTS(audio_handlers));
> +
> + return true;
> +}
> +
> bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t
> mode)
> {
> DBG("mode 0x%x", mode);
> @@ -2597,6 +2636,9 @@ done:
> hal_ipc = ipc;
> ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
>
G_N_ELEMENTS(cmd_handlers));
> +
> + bt_audio_register(NULL);
> +
> return true;
> }
>
> --
> 1.8.3.2
>
> --
> 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
>
> --
> 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
--
Luiz Augusto von Dentz
Hi Jesse,
On Wed, Apr 23, 2014 at 5:06 PM, Jesse Koegler
<[email protected]> wrote:
> All,
>
> I am currently using bluez 4.98 (time to update, I know). Within 4.98 I have
> been unable to find a way to control the responses to SCO link requests.
> Will this or any other patches since 4.98 allow controlling of the responses
> to SCO link requests? To my understanding this is done on the chip level and
> is outside the scope of the stack, has that changed at all?
Please don't hijack threads with unrelated questions, create a new
thread... Regarding your question, have a look at the SCO socket API
> Best,
>
> Jesse Koegler
>
> -----Original Message-----
> From: [email protected]
> [mailto:[email protected]] On Behalf Of Andrei
> Emeltchenko
> Sent: Thursday, April 17, 2014 8:24 AM
> To: [email protected]
> Subject: [PATCH 5/7] android/hal-audio: Add SCO audio API
>
> From: Andrei Emeltchenko <[email protected]>
>
> ---
> android/audio-msg.h | 6 +-
> android/hal-audio-hsp.c | 314
> +++++++++++++++++++++++++++++++++++++++++++++++-
> android/handsfree.c | 42 +++++++
> 3 files changed, 358 insertions(+), 4 deletions(-)
>
> diff --git a/android/audio-msg.h b/android/audio-msg.h
> index 5981355..d247c80 100644
> --- a/android/audio-msg.h
> +++ b/android/audio-msg.h
> @@ -24,9 +24,11 @@
> #define BLUEZ_AUDIO_MTU 1024
>
> static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
> +static const char BLUEZ_AUDIO_SCO_SK_PATH[] = "\0bluez_audio_sco_socket";
>
> #define AUDIO_SERVICE_ID 0
> -#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_ID
> +#define AUDIO_SERVICE_SCO_ID 1
> +#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_SCO_ID
>
> #define AUDIO_STATUS_SUCCESS IPC_STATUS_SUCCESS
> #define AUDIO_STATUS_FAILED 0x01
> @@ -79,3 +81,5 @@ struct audio_cmd_resume_stream {
> struct audio_cmd_suspend_stream {
> uint8_t id;
> } __attribute__((packed));
> +
> +#define AUDIO_OP_SCO_GET_FD 0x01
> diff --git a/android/hal-audio-hsp.c b/android/hal-audio-hsp.c
> index 992066c..cac6c8d 100644
> --- a/android/hal-audio-hsp.c
> +++ b/android/hal-audio-hsp.c
> @@ -16,13 +16,20 @@
> */
>
> #include <errno.h>
> +#include <pthread.h>
> +#include <poll.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> +#include <sys/socket.h>
> +#include <sys/un.h>
> +#include <unistd.h>
>
> #include <hardware/audio.h>
> #include <hardware/hardware.h>
>
> +#include "audio-msg.h"
> +#include "ipc-common.h"
> #include "hal-log.h"
>
> #define AUDIO_STREAM_DEFAULT_RATE 44100
> @@ -30,6 +37,12 @@
>
> #define OUT_BUFFER_SIZE 2560
>
> +static int listen_sk = -1;
> +static int audio_sk = -1;
> +
> +static pthread_t ipc_th = 0;
> +static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
> +
> struct hsp_audio_config {
> uint32_t rate;
> uint32_t channels;
> @@ -38,7 +51,9 @@ struct hsp_audio_config {
>
> struct hsp_stream_out {
> struct audio_stream_out stream;
> +
> struct hsp_audio_config cfg;
> + int fd;
> };
>
> struct hsp_audio_dev {
> @@ -46,13 +61,178 @@ struct hsp_audio_dev {
> struct hsp_stream_out *out;
> };
>
> +/* Audio IPC functions */
> +
> +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 ipc_hdr cmd;
> + char cmsgbuf[CMSG_SPACE(sizeof(int))];
> + struct ipc_status s;
> + size_t s_len = sizeof(s);
> +
> + pthread_mutex_lock(&sk_mutex);
> +
> + 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;
> +
> + ret = sendmsg(audio_sk, &msg, 0);
> + if (ret < 0) {
> + error("audio: Sending command failed:%s", strerror(errno));
> + 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));
> + goto failed;
> + }
> +
> + 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 ipc_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;
> + }
> +
> + pthread_mutex_unlock(&sk_mutex);
> +
> + return s->code;
> + }
> +
> + pthread_mutex_unlock(&sk_mutex);
> +
> + /* 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 (*fd < 0)
> + goto failed;
> + }
> +
> + if (rsp_len)
> + *rsp_len = cmd.len;
> +
> + return AUDIO_STATUS_SUCCESS;
> +
> +failed:
> + /* Some serious issue happen on IPC - recover */
> + shutdown(audio_sk, SHUT_RDWR);
> + pthread_mutex_unlock(&sk_mutex);
> +
> + return AUDIO_STATUS_FAILED;
> +}
> +
> +static int ipc_get_sco_fd(int *fd)
> +{
> + DBG("");
> +
> + return audio_ipc_cmd(AUDIO_SERVICE_SCO_ID, AUDIO_OP_SCO_GET_FD, 0,
> + NULL, NULL, NULL,
> fd);
> +}
> +
> /* Audio stream functions */
>
> static ssize_t out_write(struct audio_stream_out *stream, const void
> *buffer,
> size_t
> bytes)
> {
> + struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
> +
> /* write data */
>
> + DBG("write to fd %d bytes %zu", out->fd, bytes);
> +
> return bytes;
> }
>
> @@ -60,7 +240,7 @@ static uint32_t out_get_sample_rate(const struct
> audio_stream *stream)
> {
> struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
>
> - DBG("");
> + DBG("rate %u", out->cfg.rate);
>
> return out->cfg.rate;
> }
> @@ -74,7 +254,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("");
> + DBG("buf size %u", OUT_BUFFER_SIZE);
>
> return OUT_BUFFER_SIZE;
> }
> @@ -182,9 +362,17 @@ static int audio_open_output_stream(struct
> audio_hw_device *dev,
> {
> struct hsp_audio_dev *adev = (struct hsp_audio_dev *) dev;
> struct hsp_stream_out *out;
> + int fd = -1;
>
> DBG("");
>
> + if (!ipc_get_sco_fd(&fd)) {
> + error("audio: cannot get fd");
> + return -EIO;
> + }
> +
> + DBG("got sco fd %d", fd);
> +
> out = calloc(1, sizeof(struct hsp_stream_out));
> if (!out)
> return -ENOMEM;
> @@ -212,6 +400,7 @@ static int audio_open_output_stream(struct
> audio_hw_device *dev,
>
> *stream_out = &out->stream;
> adev->out = out;
> + out->fd = fd;
>
> return 0;
> }
> @@ -219,9 +408,17 @@ static int audio_open_output_stream(struct
> audio_hw_device *dev,
> static void audio_close_output_stream(struct audio_hw_device *dev,
> struct audio_stream_out *stream_out)
> {
> - DBG("");
> + struct hsp_audio_dev *hsp_dev = (struct hsp_audio_dev *) dev;
> +
> + DBG("dev %p stream %p fd %d", dev, stream_out, hsp_dev->out->fd);
> +
> + if (hsp_dev->out && hsp_dev->out->fd) {
> + close(hsp_dev->out->fd);
> + hsp_dev->out->fd = -1;
> + }
>
> free(stream_out);
> + hsp_dev->out = NULL;
> }
>
> static int audio_set_parameters(struct audio_hw_device *dev,
> @@ -325,10 +522,117 @@ static int audio_close(hw_device_t *device)
> return 0;
> }
>
> +static void *ipc_handler(void *data)
> +{
> + bool done = false;
> + struct pollfd pfd;
> + int sk;
> +
> + DBG("");
> +
> + while (!done) {
> + DBG("Waiting for connection ...");
> +
> + sk = accept(listen_sk, NULL, NULL);
> + if (sk < 0) {
> + int err = errno;
> +
> + if (err == EINTR)
> + continue;
> +
> + if (err != ECONNABORTED && err != EINVAL)
> + error("audio: Failed to accept socket: %d
> (%s)",
> + err, strerror(err));
> +
> + break;
> + }
> +
> + pthread_mutex_lock(&sk_mutex);
> + audio_sk = sk;
> + pthread_mutex_unlock(&sk_mutex);
> +
> + DBG("Audio IPC: Connected");
> +
> + memset(&pfd, 0, sizeof(pfd));
> + pfd.fd = audio_sk;
> + pfd.events = POLLHUP | POLLERR | POLLNVAL;
> +
> + /* Check if socket is still alive. Empty while loop.*/
> + while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
> +
> + if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
> + info("Audio HAL: Socket closed");
> +
> + pthread_mutex_lock(&sk_mutex);
> + close(audio_sk);
> + audio_sk = -1;
> + pthread_mutex_unlock(&sk_mutex);
> + }
> + }
> +
> + info("Closing Audio IPC thread");
> + return NULL;
> +}
> +
> +static int audio_ipc_init(void)
> +{
> + struct sockaddr_un addr;
> + int err;
> + int sk;
> +
> + DBG("");
> +
> + sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
> + if (sk < 0) {
> + err = -errno;
> + error("audio: Failed to create socket: %d (%s)", -err,
> +
> strerror(-err));
> + return err;
> + }
> +
> + memset(&addr, 0, sizeof(addr));
> + addr.sun_family = AF_UNIX;
> +
> + memcpy(addr.sun_path, BLUEZ_AUDIO_SCO_SK_PATH,
> + sizeof(BLUEZ_AUDIO_SCO_SK_PATH));
> +
> + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
> + err = -errno;
> + error("audio: Failed to bind socket: %d (%s)", -err,
> +
> strerror(-err));
> + goto failed;
> + }
> +
> + if (listen(sk, 1) < 0) {
> + err = -errno;
> + error("audio: Failed to listen on the socket: %d (%s)",
> -err,
> +
> strerror(-err));
> + goto failed;
> + }
> +
> + listen_sk = sk;
> +
> + err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
> + if (err) {
> + err = -err;
> + ipc_th = 0;
> + error("audio: Failed to start Audio IPC thread: %d (%s)",
> + -err,
> strerror(-err));
> + goto failed;
> + }
> +
> + return 0;
> +
> +failed:
> + close(sk);
> + return err;
> +}
> +
> static int audio_open(const hw_module_t *module, const char *name,
> hw_device_t
> **device)
> {
> struct hsp_audio_dev *adev;
> + int err;
>
> DBG("");
>
> @@ -338,6 +642,10 @@ static int audio_open(const hw_module_t *module, const
> char *name,
> return -EINVAL;
> }
>
> + err = audio_ipc_init();
> + if (err < 0)
> + return err;
> +
> adev = calloc(1, sizeof(struct hsp_audio_dev));
> if (!adev)
> return -ENOMEM;
> diff --git a/android/handsfree.c b/android/handsfree.c
> index c8026a0..55e151a 100644
> --- a/android/handsfree.c
> +++ b/android/handsfree.c
> @@ -45,6 +45,7 @@
> #include "bluetooth.h"
> #include "src/log.h"
> #include "utils.h"
> +#include "audio-msg.h"
>
> #define HSP_AG_CHANNEL 12
> #define HFP_AG_CHANNEL 13
> @@ -156,7 +157,9 @@ static struct {
> static uint32_t hfp_ag_features = 0;
>
> static bdaddr_t adapter_addr;
> +
> static struct ipc *hal_ipc = NULL;
> +static struct ipc *audio_ipc = NULL;
>
> static uint32_t hfp_record_id = 0;
> static GIOChannel *hfp_server = NULL;
> @@ -2563,6 +2566,42 @@ static void disable_sco_server(void)
> }
> }
>
> +static void bt_audio_sco_get_fd(const void *buf, uint16_t len)
> +{
> + int fd = -1;
> +
> + connect_audio();
> +
> + if (device.sco)
> + fd = g_io_channel_unix_get_fd(device.sco);
> +
> + DBG("sco fd %d", fd);
> +
> + ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_SCO_ID,
> AUDIO_OP_SCO_GET_FD,
> + 0, NULL,
> fd);
> +}
> +
> +static const struct ipc_handler audio_handlers[] = {
> + /* AUDIO_OP_SCO_GET_FD */
> + { bt_audio_sco_get_fd, false, 0 }
> +};
> +
> +static bool bt_audio_register(ipc_disconnect_cb disconnect)
> +{
> + DBG("");
> +
> + audio_ipc = ipc_init(BLUEZ_AUDIO_SCO_SK_PATH,
> + sizeof(BLUEZ_AUDIO_SCO_SK_PATH),
> + AUDIO_SERVICE_ID_MAX, false, disconnect,
> NULL);
> + if (!audio_ipc)
> + return false;
> +
> + ipc_register(audio_ipc, AUDIO_SERVICE_SCO_ID, audio_handlers,
> +
> G_N_ELEMENTS(audio_handlers));
> +
> + return true;
> +}
> +
> bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t
> mode)
> {
> DBG("mode 0x%x", mode);
> @@ -2597,6 +2636,9 @@ done:
> hal_ipc = ipc;
> ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
> G_N_ELEMENTS(cmd_handlers));
> +
> + bt_audio_register(NULL);
> +
> return true;
> }
>
> --
> 1.8.3.2
>
> --
> 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
>
> --
> 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
--
Luiz Augusto von Dentz
All,
I am currently using bluez 4.98 (time to update, I know). Within 4.98 I have
been unable to find a way to control the responses to SCO link requests.
Will this or any other patches since 4.98 allow controlling of the responses
to SCO link requests? To my understanding this is done on the chip level and
is outside the scope of the stack, has that changed at all?
Best,
Jesse Koegler
-----Original Message-----
From: [email protected]
[mailto:[email protected]] On Behalf Of Andrei
Emeltchenko
Sent: Thursday, April 17, 2014 8:24 AM
To: [email protected]
Subject: [PATCH 5/7] android/hal-audio: Add SCO audio API
From: Andrei Emeltchenko <[email protected]>
---
android/audio-msg.h | 6 +-
android/hal-audio-hsp.c | 314
+++++++++++++++++++++++++++++++++++++++++++++++-
android/handsfree.c | 42 +++++++
3 files changed, 358 insertions(+), 4 deletions(-)
diff --git a/android/audio-msg.h b/android/audio-msg.h
index 5981355..d247c80 100644
--- a/android/audio-msg.h
+++ b/android/audio-msg.h
@@ -24,9 +24,11 @@
#define BLUEZ_AUDIO_MTU 1024
static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
+static const char BLUEZ_AUDIO_SCO_SK_PATH[] = "\0bluez_audio_sco_socket";
#define AUDIO_SERVICE_ID 0
-#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_ID
+#define AUDIO_SERVICE_SCO_ID 1
+#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_SCO_ID
#define AUDIO_STATUS_SUCCESS IPC_STATUS_SUCCESS
#define AUDIO_STATUS_FAILED 0x01
@@ -79,3 +81,5 @@ struct audio_cmd_resume_stream {
struct audio_cmd_suspend_stream {
uint8_t id;
} __attribute__((packed));
+
+#define AUDIO_OP_SCO_GET_FD 0x01
diff --git a/android/hal-audio-hsp.c b/android/hal-audio-hsp.c
index 992066c..cac6c8d 100644
--- a/android/hal-audio-hsp.c
+++ b/android/hal-audio-hsp.c
@@ -16,13 +16,20 @@
*/
#include <errno.h>
+#include <pthread.h>
+#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
#include <hardware/audio.h>
#include <hardware/hardware.h>
+#include "audio-msg.h"
+#include "ipc-common.h"
#include "hal-log.h"
#define AUDIO_STREAM_DEFAULT_RATE 44100
@@ -30,6 +37,12 @@
#define OUT_BUFFER_SIZE 2560
+static int listen_sk = -1;
+static int audio_sk = -1;
+
+static pthread_t ipc_th = 0;
+static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
struct hsp_audio_config {
uint32_t rate;
uint32_t channels;
@@ -38,7 +51,9 @@ struct hsp_audio_config {
struct hsp_stream_out {
struct audio_stream_out stream;
+
struct hsp_audio_config cfg;
+ int fd;
};
struct hsp_audio_dev {
@@ -46,13 +61,178 @@ struct hsp_audio_dev {
struct hsp_stream_out *out;
};
+/* Audio IPC functions */
+
+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 ipc_hdr cmd;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ struct ipc_status s;
+ size_t s_len = sizeof(s);
+
+ pthread_mutex_lock(&sk_mutex);
+
+ 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;
+
+ ret = sendmsg(audio_sk, &msg, 0);
+ if (ret < 0) {
+ error("audio: Sending command failed:%s", strerror(errno));
+ 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));
+ goto failed;
+ }
+
+ 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 ipc_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;
+ }
+
+ pthread_mutex_unlock(&sk_mutex);
+
+ return s->code;
+ }
+
+ pthread_mutex_unlock(&sk_mutex);
+
+ /* 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 (*fd < 0)
+ goto failed;
+ }
+
+ if (rsp_len)
+ *rsp_len = cmd.len;
+
+ return AUDIO_STATUS_SUCCESS;
+
+failed:
+ /* Some serious issue happen on IPC - recover */
+ shutdown(audio_sk, SHUT_RDWR);
+ pthread_mutex_unlock(&sk_mutex);
+
+ return AUDIO_STATUS_FAILED;
+}
+
+static int ipc_get_sco_fd(int *fd)
+{
+ DBG("");
+
+ return audio_ipc_cmd(AUDIO_SERVICE_SCO_ID, AUDIO_OP_SCO_GET_FD, 0,
+ NULL, NULL, NULL,
fd);
+}
+
/* Audio stream functions */
static ssize_t out_write(struct audio_stream_out *stream, const void
*buffer,
size_t
bytes)
{
+ struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
+
/* write data */
+ DBG("write to fd %d bytes %zu", out->fd, bytes);
+
return bytes;
}
@@ -60,7 +240,7 @@ static uint32_t out_get_sample_rate(const struct
audio_stream *stream)
{
struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
- DBG("");
+ DBG("rate %u", out->cfg.rate);
return out->cfg.rate;
}
@@ -74,7 +254,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("");
+ DBG("buf size %u", OUT_BUFFER_SIZE);
return OUT_BUFFER_SIZE;
}
@@ -182,9 +362,17 @@ static int audio_open_output_stream(struct
audio_hw_device *dev,
{
struct hsp_audio_dev *adev = (struct hsp_audio_dev *) dev;
struct hsp_stream_out *out;
+ int fd = -1;
DBG("");
+ if (!ipc_get_sco_fd(&fd)) {
+ error("audio: cannot get fd");
+ return -EIO;
+ }
+
+ DBG("got sco fd %d", fd);
+
out = calloc(1, sizeof(struct hsp_stream_out));
if (!out)
return -ENOMEM;
@@ -212,6 +400,7 @@ static int audio_open_output_stream(struct
audio_hw_device *dev,
*stream_out = &out->stream;
adev->out = out;
+ out->fd = fd;
return 0;
}
@@ -219,9 +408,17 @@ static int audio_open_output_stream(struct
audio_hw_device *dev,
static void audio_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream_out)
{
- DBG("");
+ struct hsp_audio_dev *hsp_dev = (struct hsp_audio_dev *) dev;
+
+ DBG("dev %p stream %p fd %d", dev, stream_out, hsp_dev->out->fd);
+
+ if (hsp_dev->out && hsp_dev->out->fd) {
+ close(hsp_dev->out->fd);
+ hsp_dev->out->fd = -1;
+ }
free(stream_out);
+ hsp_dev->out = NULL;
}
static int audio_set_parameters(struct audio_hw_device *dev,
@@ -325,10 +522,117 @@ static int audio_close(hw_device_t *device)
return 0;
}
+static void *ipc_handler(void *data)
+{
+ bool done = false;
+ struct pollfd pfd;
+ int sk;
+
+ DBG("");
+
+ while (!done) {
+ DBG("Waiting for connection ...");
+
+ sk = accept(listen_sk, NULL, NULL);
+ if (sk < 0) {
+ int err = errno;
+
+ if (err == EINTR)
+ continue;
+
+ if (err != ECONNABORTED && err != EINVAL)
+ error("audio: Failed to accept socket: %d
(%s)",
+ err, strerror(err));
+
+ break;
+ }
+
+ pthread_mutex_lock(&sk_mutex);
+ audio_sk = sk;
+ pthread_mutex_unlock(&sk_mutex);
+
+ DBG("Audio IPC: Connected");
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = audio_sk;
+ pfd.events = POLLHUP | POLLERR | POLLNVAL;
+
+ /* Check if socket is still alive. Empty while loop.*/
+ while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
+
+ if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
+ info("Audio HAL: Socket closed");
+
+ pthread_mutex_lock(&sk_mutex);
+ close(audio_sk);
+ audio_sk = -1;
+ pthread_mutex_unlock(&sk_mutex);
+ }
+ }
+
+ info("Closing Audio IPC thread");
+ return NULL;
+}
+
+static int audio_ipc_init(void)
+{
+ struct sockaddr_un addr;
+ int err;
+ int sk;
+
+ DBG("");
+
+ sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ if (sk < 0) {
+ err = -errno;
+ error("audio: Failed to create socket: %d (%s)", -err,
+
strerror(-err));
+ return err;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ memcpy(addr.sun_path, BLUEZ_AUDIO_SCO_SK_PATH,
+ sizeof(BLUEZ_AUDIO_SCO_SK_PATH));
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = -errno;
+ error("audio: Failed to bind socket: %d (%s)", -err,
+
strerror(-err));
+ goto failed;
+ }
+
+ if (listen(sk, 1) < 0) {
+ err = -errno;
+ error("audio: Failed to listen on the socket: %d (%s)",
-err,
+
strerror(-err));
+ goto failed;
+ }
+
+ listen_sk = sk;
+
+ err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
+ if (err) {
+ err = -err;
+ ipc_th = 0;
+ error("audio: Failed to start Audio IPC thread: %d (%s)",
+ -err,
strerror(-err));
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ close(sk);
+ return err;
+}
+
static int audio_open(const hw_module_t *module, const char *name,
hw_device_t
**device)
{
struct hsp_audio_dev *adev;
+ int err;
DBG("");
@@ -338,6 +642,10 @@ static int audio_open(const hw_module_t *module, const
char *name,
return -EINVAL;
}
+ err = audio_ipc_init();
+ if (err < 0)
+ return err;
+
adev = calloc(1, sizeof(struct hsp_audio_dev));
if (!adev)
return -ENOMEM;
diff --git a/android/handsfree.c b/android/handsfree.c
index c8026a0..55e151a 100644
--- a/android/handsfree.c
+++ b/android/handsfree.c
@@ -45,6 +45,7 @@
#include "bluetooth.h"
#include "src/log.h"
#include "utils.h"
+#include "audio-msg.h"
#define HSP_AG_CHANNEL 12
#define HFP_AG_CHANNEL 13
@@ -156,7 +157,9 @@ static struct {
static uint32_t hfp_ag_features = 0;
static bdaddr_t adapter_addr;
+
static struct ipc *hal_ipc = NULL;
+static struct ipc *audio_ipc = NULL;
static uint32_t hfp_record_id = 0;
static GIOChannel *hfp_server = NULL;
@@ -2563,6 +2566,42 @@ static void disable_sco_server(void)
}
}
+static void bt_audio_sco_get_fd(const void *buf, uint16_t len)
+{
+ int fd = -1;
+
+ connect_audio();
+
+ if (device.sco)
+ fd = g_io_channel_unix_get_fd(device.sco);
+
+ DBG("sco fd %d", fd);
+
+ ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_SCO_ID,
AUDIO_OP_SCO_GET_FD,
+ 0, NULL,
fd);
+}
+
+static const struct ipc_handler audio_handlers[] = {
+ /* AUDIO_OP_SCO_GET_FD */
+ { bt_audio_sco_get_fd, false, 0 }
+};
+
+static bool bt_audio_register(ipc_disconnect_cb disconnect)
+{
+ DBG("");
+
+ audio_ipc = ipc_init(BLUEZ_AUDIO_SCO_SK_PATH,
+ sizeof(BLUEZ_AUDIO_SCO_SK_PATH),
+ AUDIO_SERVICE_ID_MAX, false, disconnect,
NULL);
+ if (!audio_ipc)
+ return false;
+
+ ipc_register(audio_ipc, AUDIO_SERVICE_SCO_ID, audio_handlers,
+
G_N_ELEMENTS(audio_handlers));
+
+ return true;
+}
+
bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t
mode)
{
DBG("mode 0x%x", mode);
@@ -2597,6 +2636,9 @@ done:
hal_ipc = ipc;
ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
G_N_ELEMENTS(cmd_handlers));
+
+ bt_audio_register(NULL);
+
return true;
}
--
1.8.3.2
Hi Andrei,
On Thu, Apr 17, 2014 at 4:20 PM, Szymon Janc <[email protected]> wrote:
> Hi Andrei,
>
> On Thursday 17 of April 2014 15:23:49 Andrei Emeltchenko wrote:
>> From: Andrei Emeltchenko <[email protected]>
>>
>> ---
>> android/audio-msg.h | 6 +-
>> android/hal-audio-hsp.c | 314 +++++++++++++++++++++++++++++++++++++++++++++++-
>> android/handsfree.c | 42 +++++++
>> 3 files changed, 358 insertions(+), 4 deletions(-)
>>
>> diff --git a/android/audio-msg.h b/android/audio-msg.h
>> index 5981355..d247c80 100644
>> --- a/android/audio-msg.h
>> +++ b/android/audio-msg.h
>> @@ -24,9 +24,11 @@
>> #define BLUEZ_AUDIO_MTU 1024
>>
>> static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
>> +static const char BLUEZ_AUDIO_SCO_SK_PATH[] = "\0bluez_audio_sco_socket";
>>
>> #define AUDIO_SERVICE_ID 0
>> -#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_ID
>> +#define AUDIO_SERVICE_SCO_ID 1
>> +#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_SCO_ID
>>
>> #define AUDIO_STATUS_SUCCESS IPC_STATUS_SUCCESS
>> #define AUDIO_STATUS_FAILED 0x01
>> @@ -79,3 +81,5 @@ struct audio_cmd_resume_stream {
>> struct audio_cmd_suspend_stream {
>> uint8_t id;
>> } __attribute__((packed));
>> +
>> +#define AUDIO_OP_SCO_GET_FD 0x01
Is this supposed to work with HFP/WBS or just basic HSP? Btw, we
probably can create a loopback for testing in via hal-tester, so
whatever you say on the mic should return to the headset speakers.
>> diff --git a/android/hal-audio-hsp.c b/android/hal-audio-hsp.c
>> index 992066c..cac6c8d 100644
>> --- a/android/hal-audio-hsp.c
>> +++ b/android/hal-audio-hsp.c
>> @@ -16,13 +16,20 @@
>> */
>>
>> #include <errno.h>
>> +#include <pthread.h>
>> +#include <poll.h>
>> #include <stdio.h>
>> #include <stdlib.h>
>> #include <string.h>
>> +#include <sys/socket.h>
>> +#include <sys/un.h>
>> +#include <unistd.h>
>>
>> #include <hardware/audio.h>
>> #include <hardware/hardware.h>
>>
>> +#include "audio-msg.h"
>> +#include "ipc-common.h"
>> #include "hal-log.h"
>>
>> #define AUDIO_STREAM_DEFAULT_RATE 44100
>> @@ -30,6 +37,12 @@
>>
>> #define OUT_BUFFER_SIZE 2560
>>
>> +static int listen_sk = -1;
>> +static int audio_sk = -1;
>> +
>> +static pthread_t ipc_th = 0;
>> +static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
>> +
>> struct hsp_audio_config {
>> uint32_t rate;
>> uint32_t channels;
>> @@ -38,7 +51,9 @@ struct hsp_audio_config {
>>
>> struct hsp_stream_out {
>> struct audio_stream_out stream;
>> +
>> struct hsp_audio_config cfg;
>> + int fd;
>> };
>>
>> struct hsp_audio_dev {
>> @@ -46,13 +61,178 @@ struct hsp_audio_dev {
>> struct hsp_stream_out *out;
>> };
>>
>> +/* Audio IPC functions */
>> +
>> +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)
>> +{
>
> This is going to be third copy of HAL side command handling. Maybe it is time
> to re-factor code out and make it reusable? Same as was done for daemon part.
Maybe we should create a file to accommodate this is a single place,
how about audio-ipc or just audio{c,h}.
>> + ssize_t ret;
>> + struct msghdr msg;
>> + struct iovec iv[2];
>> + struct ipc_hdr cmd;
>> + char cmsgbuf[CMSG_SPACE(sizeof(int))];
>> + struct ipc_status s;
>> + size_t s_len = sizeof(s);
>> +
>> + pthread_mutex_lock(&sk_mutex);
>> +
>> + 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;
>> +
>> + ret = sendmsg(audio_sk, &msg, 0);
>> + if (ret < 0) {
>> + error("audio: Sending command failed:%s", strerror(errno));
>> + 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));
>> + goto failed;
>> + }
>> +
>> + 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 ipc_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;
>> + }
>> +
>> + pthread_mutex_unlock(&sk_mutex);
>> +
>> + return s->code;
>> + }
>> +
>> + pthread_mutex_unlock(&sk_mutex);
>> +
>> + /* 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 (*fd < 0)
>> + goto failed;
>> + }
>> +
>> + if (rsp_len)
>> + *rsp_len = cmd.len;
>> +
>> + return AUDIO_STATUS_SUCCESS;
>> +
>> +failed:
>> + /* Some serious issue happen on IPC - recover */
>> + shutdown(audio_sk, SHUT_RDWR);
>> + pthread_mutex_unlock(&sk_mutex);
>> +
>> + return AUDIO_STATUS_FAILED;
>> +}
>> +
>> +static int ipc_get_sco_fd(int *fd)
>> +{
>> + DBG("");
>> +
>> + return audio_ipc_cmd(AUDIO_SERVICE_SCO_ID, AUDIO_OP_SCO_GET_FD, 0,
>> + NULL, NULL, NULL, fd);
>> +}
>> +
>> /* Audio stream functions */
>>
>> static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
>> size_t bytes)
>> {
>> + struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
>> +
>> /* write data */
>>
>> + DBG("write to fd %d bytes %zu", out->fd, bytes);
>> +
>> return bytes;
>> }
>>
>> @@ -60,7 +240,7 @@ static uint32_t out_get_sample_rate(const struct audio_stream *stream)
>> {
>> struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
>>
>> - DBG("");
>> + DBG("rate %u", out->cfg.rate);
>>
>> return out->cfg.rate;
>> }
>> @@ -74,7 +254,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("");
>> + DBG("buf size %u", OUT_BUFFER_SIZE);
>>
>> return OUT_BUFFER_SIZE;
>> }
>> @@ -182,9 +362,17 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
>> {
>> struct hsp_audio_dev *adev = (struct hsp_audio_dev *) dev;
>> struct hsp_stream_out *out;
>> + int fd = -1;
>>
>> DBG("");
>>
>> + if (!ipc_get_sco_fd(&fd)) {
>> + error("audio: cannot get fd");
>> + return -EIO;
>> + }
>> +
>> + DBG("got sco fd %d", fd);
>> +
>> out = calloc(1, sizeof(struct hsp_stream_out));
>> if (!out)
>> return -ENOMEM;
>> @@ -212,6 +400,7 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
>>
>> *stream_out = &out->stream;
>> adev->out = out;
>> + out->fd = fd;
>>
>> return 0;
>> }
>> @@ -219,9 +408,17 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
>> static void audio_close_output_stream(struct audio_hw_device *dev,
>> struct audio_stream_out *stream_out)
>> {
>> - DBG("");
>> + struct hsp_audio_dev *hsp_dev = (struct hsp_audio_dev *) dev;
>> +
>> + DBG("dev %p stream %p fd %d", dev, stream_out, hsp_dev->out->fd);
>> +
>> + if (hsp_dev->out && hsp_dev->out->fd) {
>> + close(hsp_dev->out->fd);
>> + hsp_dev->out->fd = -1;
>> + }
It is probably a good idea to shutdown before close since the daemon
also have a reference to it.
>> free(stream_out);
>> + hsp_dev->out = NULL;
>> }
>>
>> static int audio_set_parameters(struct audio_hw_device *dev,
>> @@ -325,10 +522,117 @@ static int audio_close(hw_device_t *device)
>> return 0;
>> }
>>
>> +static void *ipc_handler(void *data)
>> +{
>> + bool done = false;
>> + struct pollfd pfd;
>> + int sk;
>> +
>> + DBG("");
>> +
>> + while (!done) {
>> + DBG("Waiting for connection ...");
>> +
>> + sk = accept(listen_sk, NULL, NULL);
>> + if (sk < 0) {
>> + int err = errno;
>> +
>> + if (err == EINTR)
>> + continue;
>> +
>> + if (err != ECONNABORTED && err != EINVAL)
>> + error("audio: Failed to accept socket: %d (%s)",
>> + err, strerror(err));
>> +
>> + break;
>> + }
>> +
>> + pthread_mutex_lock(&sk_mutex);
>> + audio_sk = sk;
>> + pthread_mutex_unlock(&sk_mutex);
>> +
>> + DBG("Audio IPC: Connected");
>> +
>> + memset(&pfd, 0, sizeof(pfd));
>> + pfd.fd = audio_sk;
>> + pfd.events = POLLHUP | POLLERR | POLLNVAL;
>> +
>> + /* Check if socket is still alive. Empty while loop.*/
>> + while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
>> +
>> + if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
>> + info("Audio HAL: Socket closed");
>> +
>> + pthread_mutex_lock(&sk_mutex);
>> + close(audio_sk);
>> + audio_sk = -1;
>> + pthread_mutex_unlock(&sk_mutex);
>> + }
>> + }
>> +
>> + info("Closing Audio IPC thread");
>> + return NULL;
>> +}
>> +
>> +static int audio_ipc_init(void)
>> +{
>> + struct sockaddr_un addr;
>> + int err;
>> + int sk;
>> +
>> + DBG("");
>> +
>> + sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
>> + if (sk < 0) {
>> + err = -errno;
>> + error("audio: Failed to create socket: %d (%s)", -err,
>> + strerror(-err));
>> + return err;
>> + }
>> +
>> + memset(&addr, 0, sizeof(addr));
>> + addr.sun_family = AF_UNIX;
>> +
>> + memcpy(addr.sun_path, BLUEZ_AUDIO_SCO_SK_PATH,
>> + sizeof(BLUEZ_AUDIO_SCO_SK_PATH));
>> +
>> + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
>> + err = -errno;
>> + error("audio: Failed to bind socket: %d (%s)", -err,
>> + strerror(-err));
>> + goto failed;
>> + }
>> +
>> + if (listen(sk, 1) < 0) {
>> + err = -errno;
>> + error("audio: Failed to listen on the socket: %d (%s)", -err,
>> + strerror(-err));
>> + goto failed;
>> + }
>> +
>> + listen_sk = sk;
>> +
>> + err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
>> + if (err) {
>> + err = -err;
>> + ipc_th = 0;
>> + error("audio: Failed to start Audio IPC thread: %d (%s)",
>> + -err, strerror(-err));
>> + goto failed;
>> + }
>> +
>> + return 0;
>> +
>> +failed:
>> + close(sk);
>> + return err;
>> +}
>> +
>> static int audio_open(const hw_module_t *module, const char *name,
>> hw_device_t **device)
>> {
>> struct hsp_audio_dev *adev;
>> + int err;
>>
>> DBG("");
>>
>> @@ -338,6 +642,10 @@ static int audio_open(const hw_module_t *module, const char *name,
>> return -EINVAL;
>> }
>>
>> + err = audio_ipc_init();
>> + if (err < 0)
>> + return err;
>> +
>> adev = calloc(1, sizeof(struct hsp_audio_dev));
>> if (!adev)
>> return -ENOMEM;
>> diff --git a/android/handsfree.c b/android/handsfree.c
>> index c8026a0..55e151a 100644
>> --- a/android/handsfree.c
>> +++ b/android/handsfree.c
>> @@ -45,6 +45,7 @@
>> #include "bluetooth.h"
>> #include "src/log.h"
>> #include "utils.h"
>> +#include "audio-msg.h"
>>
>> #define HSP_AG_CHANNEL 12
>> #define HFP_AG_CHANNEL 13
>> @@ -156,7 +157,9 @@ static struct {
>> static uint32_t hfp_ag_features = 0;
>>
>> static bdaddr_t adapter_addr;
>> +
>> static struct ipc *hal_ipc = NULL;
>> +static struct ipc *audio_ipc = NULL;
>>
>> static uint32_t hfp_record_id = 0;
>> static GIOChannel *hfp_server = NULL;
>> @@ -2563,6 +2566,42 @@ static void disable_sco_server(void)
>> }
>> }
>>
>> +static void bt_audio_sco_get_fd(const void *buf, uint16_t len)
>> +{
>> + int fd = -1;
>> +
>> + connect_audio();
>> +
>> + if (device.sco)
>> + fd = g_io_channel_unix_get_fd(device.sco);
>> +
>> + DBG("sco fd %d", fd);
>> +
>> + ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_SCO_ID, AUDIO_OP_SCO_GET_FD,
>> + 0, NULL, fd);
>> +}
>> +
>> +static const struct ipc_handler audio_handlers[] = {
>> + /* AUDIO_OP_SCO_GET_FD */
>> + { bt_audio_sco_get_fd, false, 0 }
>> +};
>> +
>> +static bool bt_audio_register(ipc_disconnect_cb disconnect)
>> +{
>> + DBG("");
>> +
>> + audio_ipc = ipc_init(BLUEZ_AUDIO_SCO_SK_PATH,
>> + sizeof(BLUEZ_AUDIO_SCO_SK_PATH),
>> + AUDIO_SERVICE_ID_MAX, false, disconnect, NULL);
>> + if (!audio_ipc)
>> + return false;
>> +
>> + ipc_register(audio_ipc, AUDIO_SERVICE_SCO_ID, audio_handlers,
>> + G_N_ELEMENTS(audio_handlers));
>> +
>> + return true;
>> +}
>> +
>> bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
>> {
>> DBG("mode 0x%x", mode);
>> @@ -2597,6 +2636,9 @@ done:
>> hal_ipc = ipc;
>> ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
>> G_N_ELEMENTS(cmd_handlers));
>> +
>> + bt_audio_register(NULL);
>> +
>
> You are missing cleanup tasks for this.
>
> Also, do we want to always load it? Maybe it should be configurable?
>
>> return true;
>> }
>>
>>
>
> --
> Best regards,
> Szymon Janc
> --
> 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
--
Luiz Augusto von Dentz
Hi Andrei,
On Thursday 17 of April 2014 15:23:51 Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> ---
> android/handsfree.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/android/handsfree.c b/android/handsfree.c
> index 55e151a..51dddb3 100644
> --- a/android/handsfree.c
> +++ b/android/handsfree.c
> @@ -2204,10 +2204,10 @@ static const struct ipc_handler cmd_handlers[] = {
> /* HAL_OP_HANDSFREE_DISCONNECT */
> { handle_disconnect, false,
> sizeof(struct hal_cmd_handsfree_disconnect) },
> - /*HAL_OP_HANDSFREE_CONNECT_AUDIO*/
> + /* HAL_OP_HANDSFREE_CONNECT_AUDIO */
> { handle_connect_audio, false,
> sizeof(struct hal_cmd_handsfree_connect_audio) },
> - /*HAL_OP_HANDSFREE_DISCONNECT_AUDIO*/
> + /* HAL_OP_HANDSFREE_DISCONNECT_AUDIO */
> { handle_disconnect_audio, false,
> sizeof(struct hal_cmd_handsfree_disconnect_audio) },
> /* define HAL_OP_HANDSFREE_START_VR */
>
This one is now pushed, thanks.
--
Best regards,
Szymon Janc
Hi Andrei,
On Thursday 17 of April 2014 15:23:50 Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> This helps to identify problem since DBG is not printed by default.
> ---
> android/ipc.c | 12 ++++++------
> 1 file changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/android/ipc.c b/android/ipc.c
> index 8cd34ea..89fff8d 100644
> --- a/android/ipc.c
> +++ b/android/ipc.c
> @@ -96,31 +96,31 @@ static int ipc_handle_msg(struct service_handler *handlers, size_t max_index,
> const struct ipc_handler *handler;
>
> if (len < (ssize_t) sizeof(*msg)) {
> - DBG("message too small (%zd bytes)", len);
> + error("message too small (%zd bytes)", len);
> return -EBADMSG;
> }
>
> if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
> - DBG("message malformed (%zd bytes)", len);
> + error("message malformed (%zd bytes)", len);
> return -EBADMSG;
> }
>
> /* if service is valid */
> if (msg->service_id > max_index) {
> - DBG("unknown service (0x%x)", msg->service_id);
> + error("unknown service (0x%x)", msg->service_id);
> return -EOPNOTSUPP;
> }
>
> /* if service is registered */
> if (!handlers[msg->service_id].handler) {
> - DBG("service not registered (0x%x)", msg->service_id);
> + error("service not registered (0x%x)", msg->service_id);
> return -EOPNOTSUPP;
> }
>
> /* if opcode is valid */
> if (msg->opcode == IPC_OP_STATUS ||
> msg->opcode > handlers[msg->service_id].size) {
> - DBG("invalid opcode 0x%x for service 0x%x", msg->opcode,
> + error("invalid opcode 0x%x for service 0x%x", msg->opcode,
> msg->service_id);
> return -EOPNOTSUPP;
> }
> @@ -131,7 +131,7 @@ static int ipc_handle_msg(struct service_handler *handlers, size_t max_index,
> /* if payload size is valid */
> if ((handler->var_len && handler->data_len > msg->len) ||
> (!handler->var_len && handler->data_len != msg->len)) {
> - DBG("invalid size for opcode 0x%x service 0x%x",
> + error("invalid size for opcode 0x%x service 0x%x",
> msg->opcode, msg->service_id);
> return -EMSGSIZE;
> }
>
I'm fine this change (although we already print error later on based on returned code),
but please prefix those with "IPC: " for clarity.
--
Best regards,
Szymon Janc
Hi Andrei,
On Thursday 17 of April 2014 15:23:49 Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> ---
> android/audio-msg.h | 6 +-
> android/hal-audio-hsp.c | 314 +++++++++++++++++++++++++++++++++++++++++++++++-
> android/handsfree.c | 42 +++++++
> 3 files changed, 358 insertions(+), 4 deletions(-)
>
> diff --git a/android/audio-msg.h b/android/audio-msg.h
> index 5981355..d247c80 100644
> --- a/android/audio-msg.h
> +++ b/android/audio-msg.h
> @@ -24,9 +24,11 @@
> #define BLUEZ_AUDIO_MTU 1024
>
> static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
> +static const char BLUEZ_AUDIO_SCO_SK_PATH[] = "\0bluez_audio_sco_socket";
>
> #define AUDIO_SERVICE_ID 0
> -#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_ID
> +#define AUDIO_SERVICE_SCO_ID 1
> +#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_SCO_ID
>
> #define AUDIO_STATUS_SUCCESS IPC_STATUS_SUCCESS
> #define AUDIO_STATUS_FAILED 0x01
> @@ -79,3 +81,5 @@ struct audio_cmd_resume_stream {
> struct audio_cmd_suspend_stream {
> uint8_t id;
> } __attribute__((packed));
> +
> +#define AUDIO_OP_SCO_GET_FD 0x01
> diff --git a/android/hal-audio-hsp.c b/android/hal-audio-hsp.c
> index 992066c..cac6c8d 100644
> --- a/android/hal-audio-hsp.c
> +++ b/android/hal-audio-hsp.c
> @@ -16,13 +16,20 @@
> */
>
> #include <errno.h>
> +#include <pthread.h>
> +#include <poll.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> +#include <sys/socket.h>
> +#include <sys/un.h>
> +#include <unistd.h>
>
> #include <hardware/audio.h>
> #include <hardware/hardware.h>
>
> +#include "audio-msg.h"
> +#include "ipc-common.h"
> #include "hal-log.h"
>
> #define AUDIO_STREAM_DEFAULT_RATE 44100
> @@ -30,6 +37,12 @@
>
> #define OUT_BUFFER_SIZE 2560
>
> +static int listen_sk = -1;
> +static int audio_sk = -1;
> +
> +static pthread_t ipc_th = 0;
> +static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
> +
> struct hsp_audio_config {
> uint32_t rate;
> uint32_t channels;
> @@ -38,7 +51,9 @@ struct hsp_audio_config {
>
> struct hsp_stream_out {
> struct audio_stream_out stream;
> +
> struct hsp_audio_config cfg;
> + int fd;
> };
>
> struct hsp_audio_dev {
> @@ -46,13 +61,178 @@ struct hsp_audio_dev {
> struct hsp_stream_out *out;
> };
>
> +/* Audio IPC functions */
> +
> +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)
> +{
This is going to be third copy of HAL side command handling. Maybe it is time
to re-factor code out and make it reusable? Same as was done for daemon part.
> + ssize_t ret;
> + struct msghdr msg;
> + struct iovec iv[2];
> + struct ipc_hdr cmd;
> + char cmsgbuf[CMSG_SPACE(sizeof(int))];
> + struct ipc_status s;
> + size_t s_len = sizeof(s);
> +
> + pthread_mutex_lock(&sk_mutex);
> +
> + 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;
> +
> + ret = sendmsg(audio_sk, &msg, 0);
> + if (ret < 0) {
> + error("audio: Sending command failed:%s", strerror(errno));
> + 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));
> + goto failed;
> + }
> +
> + 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 ipc_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;
> + }
> +
> + pthread_mutex_unlock(&sk_mutex);
> +
> + return s->code;
> + }
> +
> + pthread_mutex_unlock(&sk_mutex);
> +
> + /* 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 (*fd < 0)
> + goto failed;
> + }
> +
> + if (rsp_len)
> + *rsp_len = cmd.len;
> +
> + return AUDIO_STATUS_SUCCESS;
> +
> +failed:
> + /* Some serious issue happen on IPC - recover */
> + shutdown(audio_sk, SHUT_RDWR);
> + pthread_mutex_unlock(&sk_mutex);
> +
> + return AUDIO_STATUS_FAILED;
> +}
> +
> +static int ipc_get_sco_fd(int *fd)
> +{
> + DBG("");
> +
> + return audio_ipc_cmd(AUDIO_SERVICE_SCO_ID, AUDIO_OP_SCO_GET_FD, 0,
> + NULL, NULL, NULL, fd);
> +}
> +
> /* Audio stream functions */
>
> static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
> size_t bytes)
> {
> + struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
> +
> /* write data */
>
> + DBG("write to fd %d bytes %zu", out->fd, bytes);
> +
> return bytes;
> }
>
> @@ -60,7 +240,7 @@ static uint32_t out_get_sample_rate(const struct audio_stream *stream)
> {
> struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
>
> - DBG("");
> + DBG("rate %u", out->cfg.rate);
>
> return out->cfg.rate;
> }
> @@ -74,7 +254,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("");
> + DBG("buf size %u", OUT_BUFFER_SIZE);
>
> return OUT_BUFFER_SIZE;
> }
> @@ -182,9 +362,17 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
> {
> struct hsp_audio_dev *adev = (struct hsp_audio_dev *) dev;
> struct hsp_stream_out *out;
> + int fd = -1;
>
> DBG("");
>
> + if (!ipc_get_sco_fd(&fd)) {
> + error("audio: cannot get fd");
> + return -EIO;
> + }
> +
> + DBG("got sco fd %d", fd);
> +
> out = calloc(1, sizeof(struct hsp_stream_out));
> if (!out)
> return -ENOMEM;
> @@ -212,6 +400,7 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
>
> *stream_out = &out->stream;
> adev->out = out;
> + out->fd = fd;
>
> return 0;
> }
> @@ -219,9 +408,17 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
> static void audio_close_output_stream(struct audio_hw_device *dev,
> struct audio_stream_out *stream_out)
> {
> - DBG("");
> + struct hsp_audio_dev *hsp_dev = (struct hsp_audio_dev *) dev;
> +
> + DBG("dev %p stream %p fd %d", dev, stream_out, hsp_dev->out->fd);
> +
> + if (hsp_dev->out && hsp_dev->out->fd) {
> + close(hsp_dev->out->fd);
> + hsp_dev->out->fd = -1;
> + }
>
> free(stream_out);
> + hsp_dev->out = NULL;
> }
>
> static int audio_set_parameters(struct audio_hw_device *dev,
> @@ -325,10 +522,117 @@ static int audio_close(hw_device_t *device)
> return 0;
> }
>
> +static void *ipc_handler(void *data)
> +{
> + bool done = false;
> + struct pollfd pfd;
> + int sk;
> +
> + DBG("");
> +
> + while (!done) {
> + DBG("Waiting for connection ...");
> +
> + sk = accept(listen_sk, NULL, NULL);
> + if (sk < 0) {
> + int err = errno;
> +
> + if (err == EINTR)
> + continue;
> +
> + if (err != ECONNABORTED && err != EINVAL)
> + error("audio: Failed to accept socket: %d (%s)",
> + err, strerror(err));
> +
> + break;
> + }
> +
> + pthread_mutex_lock(&sk_mutex);
> + audio_sk = sk;
> + pthread_mutex_unlock(&sk_mutex);
> +
> + DBG("Audio IPC: Connected");
> +
> + memset(&pfd, 0, sizeof(pfd));
> + pfd.fd = audio_sk;
> + pfd.events = POLLHUP | POLLERR | POLLNVAL;
> +
> + /* Check if socket is still alive. Empty while loop.*/
> + while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
> +
> + if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
> + info("Audio HAL: Socket closed");
> +
> + pthread_mutex_lock(&sk_mutex);
> + close(audio_sk);
> + audio_sk = -1;
> + pthread_mutex_unlock(&sk_mutex);
> + }
> + }
> +
> + info("Closing Audio IPC thread");
> + return NULL;
> +}
> +
> +static int audio_ipc_init(void)
> +{
> + struct sockaddr_un addr;
> + int err;
> + int sk;
> +
> + DBG("");
> +
> + sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
> + if (sk < 0) {
> + err = -errno;
> + error("audio: Failed to create socket: %d (%s)", -err,
> + strerror(-err));
> + return err;
> + }
> +
> + memset(&addr, 0, sizeof(addr));
> + addr.sun_family = AF_UNIX;
> +
> + memcpy(addr.sun_path, BLUEZ_AUDIO_SCO_SK_PATH,
> + sizeof(BLUEZ_AUDIO_SCO_SK_PATH));
> +
> + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
> + err = -errno;
> + error("audio: Failed to bind socket: %d (%s)", -err,
> + strerror(-err));
> + goto failed;
> + }
> +
> + if (listen(sk, 1) < 0) {
> + err = -errno;
> + error("audio: Failed to listen on the socket: %d (%s)", -err,
> + strerror(-err));
> + goto failed;
> + }
> +
> + listen_sk = sk;
> +
> + err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
> + if (err) {
> + err = -err;
> + ipc_th = 0;
> + error("audio: Failed to start Audio IPC thread: %d (%s)",
> + -err, strerror(-err));
> + goto failed;
> + }
> +
> + return 0;
> +
> +failed:
> + close(sk);
> + return err;
> +}
> +
> static int audio_open(const hw_module_t *module, const char *name,
> hw_device_t **device)
> {
> struct hsp_audio_dev *adev;
> + int err;
>
> DBG("");
>
> @@ -338,6 +642,10 @@ static int audio_open(const hw_module_t *module, const char *name,
> return -EINVAL;
> }
>
> + err = audio_ipc_init();
> + if (err < 0)
> + return err;
> +
> adev = calloc(1, sizeof(struct hsp_audio_dev));
> if (!adev)
> return -ENOMEM;
> diff --git a/android/handsfree.c b/android/handsfree.c
> index c8026a0..55e151a 100644
> --- a/android/handsfree.c
> +++ b/android/handsfree.c
> @@ -45,6 +45,7 @@
> #include "bluetooth.h"
> #include "src/log.h"
> #include "utils.h"
> +#include "audio-msg.h"
>
> #define HSP_AG_CHANNEL 12
> #define HFP_AG_CHANNEL 13
> @@ -156,7 +157,9 @@ static struct {
> static uint32_t hfp_ag_features = 0;
>
> static bdaddr_t adapter_addr;
> +
> static struct ipc *hal_ipc = NULL;
> +static struct ipc *audio_ipc = NULL;
>
> static uint32_t hfp_record_id = 0;
> static GIOChannel *hfp_server = NULL;
> @@ -2563,6 +2566,42 @@ static void disable_sco_server(void)
> }
> }
>
> +static void bt_audio_sco_get_fd(const void *buf, uint16_t len)
> +{
> + int fd = -1;
> +
> + connect_audio();
> +
> + if (device.sco)
> + fd = g_io_channel_unix_get_fd(device.sco);
> +
> + DBG("sco fd %d", fd);
> +
> + ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_SCO_ID, AUDIO_OP_SCO_GET_FD,
> + 0, NULL, fd);
> +}
> +
> +static const struct ipc_handler audio_handlers[] = {
> + /* AUDIO_OP_SCO_GET_FD */
> + { bt_audio_sco_get_fd, false, 0 }
> +};
> +
> +static bool bt_audio_register(ipc_disconnect_cb disconnect)
> +{
> + DBG("");
> +
> + audio_ipc = ipc_init(BLUEZ_AUDIO_SCO_SK_PATH,
> + sizeof(BLUEZ_AUDIO_SCO_SK_PATH),
> + AUDIO_SERVICE_ID_MAX, false, disconnect, NULL);
> + if (!audio_ipc)
> + return false;
> +
> + ipc_register(audio_ipc, AUDIO_SERVICE_SCO_ID, audio_handlers,
> + G_N_ELEMENTS(audio_handlers));
> +
> + return true;
> +}
> +
> bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
> {
> DBG("mode 0x%x", mode);
> @@ -2597,6 +2636,9 @@ done:
> hal_ipc = ipc;
> ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
> G_N_ELEMENTS(cmd_handlers));
> +
> + bt_audio_register(NULL);
> +
You are missing cleanup tasks for this.
Also, do we want to always load it? Maybe it should be configurable?
> return true;
> }
>
>
--
Best regards,
Szymon Janc
Hi Andrei,
On Thursday 17 of April 2014 15:23:47 Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> ---
> android/hal-audio.c | 14 +++++++-------
> 1 file changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/android/hal-audio.c b/android/hal-audio.c
> index c4bd4fe..76d1efb 100644
> --- a/android/hal-audio.c
> +++ b/android/hal-audio.c
> @@ -1876,12 +1876,12 @@ static struct hw_module_methods_t hal_module_methods = {
>
> struct audio_module HAL_MODULE_INFO_SYM = {
> .common = {
> - .tag = HARDWARE_MODULE_TAG,
> - .version_major = 1,
> - .version_minor = 0,
> - .id = AUDIO_HARDWARE_MODULE_ID,
> - .name = "A2DP Bluez HW HAL",
> - .author = "Intel Corporation",
> - .methods = &hal_module_methods,
> + .tag = HARDWARE_MODULE_TAG,
> + .version_major = 1,
> + .version_minor = 0,
> + .id = AUDIO_HARDWARE_MODULE_ID,
> + .name = "A2DP Bluez HW HAL",
> + .author = "Intel Corporation",
> + .methods = &hal_module_methods,
> },
> };
>
This patch is now applied, thanks.
--
Best regards,
Szymon Janc
Hi Andrei,
On Thursday 17 of April 2014 15:23:46 Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> This adds audio HAL for handling SCO. Following needs to be added to
> audio_policy.conf:
>
> hsp {
> outputs {
> hsp {
> ...
> devices AUDIO_DEVICE_OUT_ALL_SCO
> ...
> }
> }
> ---
> android/Android.mk | 23 ++++++
> android/Makefile.am | 15 ++++
> android/hal-audio-hsp.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 249 insertions(+)
> create mode 100644 android/hal-audio-hsp.c
I think we should avoid calling this HSP. This should be profile agnostic since it
might also be usable with HFP. Maybe name it simply hal-audio-sco?
>
> diff --git a/android/Android.mk b/android/Android.mk
> index 5f4e70c..a6aa55d 100644
> --- a/android/Android.mk
> +++ b/android/Android.mk
> @@ -269,6 +269,29 @@ LOCAL_MODULE := audio.a2dp.default
> include $(BUILD_SHARED_LIBRARY)
>
> #
> +# HSP audio
> +#
> +
> +include $(CLEAR_VARS)
> +
> +LOCAL_SRC_FILES := bluez/android/hal-audio-hsp.c
> +
> +LOCAL_C_INCLUDES = \
> + $(call include-path-for, system-core) \
> + $(call include-path-for, libhardware) \
> +
> +LOCAL_SHARED_LIBRARIES := \
> + libcutils \
> +
> +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
> +
> +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
> +LOCAL_MODULE_TAGS := optional
> +LOCAL_MODULE := audio.hsp.default
> +
> +include $(BUILD_SHARED_LIBRARY)
> +
> +#
> # l2cap-test
> #
>
> diff --git a/android/Makefile.am b/android/Makefile.am
> index c51cce2..caeb9cc 100644
> --- a/android/Makefile.am
> +++ b/android/Makefile.am
> @@ -167,6 +167,21 @@ android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
> android/hardware/hardware.h \
> android/system/audio.h
>
> +android_audio_hsp_default_la_SOURCES = android/audio-msg.h \
> + android/hal-msg.h \
> + android/hal-audio-hsp.c \
> + android/hardware/audio.h \
> + android/hardware/audio_effect.h \
> + android/hardware/hardware.h \
> + android/system/audio.h
> +
> +android_audio_hsp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
> +
> +android_audio_hsp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
> + -no-undefined -lrt
> +
> +plugin_LTLIBRARIES += android/audio.hsp.default.la
> +
> unit_tests += android/test-ipc
>
> android_test_ipc_SOURCES = android/test-ipc.c \
> diff --git a/android/hal-audio-hsp.c b/android/hal-audio-hsp.c
> new file mode 100644
> index 0000000..c7ba504
> --- /dev/null
> +++ b/android/hal-audio-hsp.c
> @@ -0,0 +1,211 @@
> +/*
> + * Copyright (C) 2013 Intel Corporation
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + *
> + */
> +
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <hardware/audio.h>
> +#include <hardware/hardware.h>
> +
> +#include "hal-log.h"
> +
> +struct hsp_audio_dev {
> + struct audio_hw_device dev;
> + struct a2dp_stream_out *out;
> +};
> +
> +static int audio_open_output_stream(struct audio_hw_device *dev,
> + audio_io_handle_t handle,
> + audio_devices_t devices,
> + audio_output_flags_t flags,
> + struct audio_config *config,
> + struct audio_stream_out **stream_out)
> +
> +{
> + DBG("");
> +
> + return -EINVAL;
> +}
> +
> +static void audio_close_output_stream(struct audio_hw_device *dev,
> + struct audio_stream_out *stream_out)
> +{
> + DBG("");
> +
> + free(stream_out);
> +}
> +
> +static int audio_set_parameters(struct audio_hw_device *dev,
> + const char *kvpairs)
> +{
> + DBG("%s", kvpairs);
> +
> + return 0;
> +}
> +
> +static char *audio_get_parameters(const struct audio_hw_device *dev,
> + const char *keys)
> +{
> + DBG("");
> +
> + return strdup("");
> +}
> +
> +static int audio_init_check(const struct audio_hw_device *dev)
> +{
> + DBG("");
> +
> + return 0;
> +}
> +
> +static int audio_set_voice_volume(struct audio_hw_device *dev, float volume)
> +{
> + DBG("%f", volume);
> +
> + return 0;
> +}
> +
> +static int audio_set_master_volume(struct audio_hw_device *dev, float volume)
> +{
> + DBG("%f", volume);
> +
> + return 0;
> +}
> +
> +static int audio_set_mode(struct audio_hw_device *dev, int mode)
> +{
> + DBG("");
> +
> + return -ENOSYS;
> +}
> +
> +static int audio_set_mic_mute(struct audio_hw_device *dev, bool state)
> +{
> + DBG("");
> +
> + return -ENOSYS;
> +}
> +
> +static int audio_get_mic_mute(const struct audio_hw_device *dev, bool *state)
> +{
> + DBG("");
> +
> + return -ENOSYS;
> +}
> +
> +static size_t audio_get_input_buffer_size(const struct audio_hw_device *dev,
> + const struct audio_config *config)
> +{
> + DBG("");
> +
> + return -ENOSYS;
> +}
> +
> +static int audio_open_input_stream(struct audio_hw_device *dev,
> + audio_io_handle_t handle,
> + audio_devices_t devices,
> + struct audio_config *config,
> + struct audio_stream_in **stream_in)
> +{
> + DBG("");
> +
> + return 0;
> +}
> +
> +static void audio_close_input_stream(struct audio_hw_device *dev,
> + struct audio_stream_in *stream_in)
> +{
> + DBG("");
> +
> + free(stream_in);
> +}
> +
> +static int audio_dump(const audio_hw_device_t *device, int fd)
> +{
> + DBG("");
> +
> + return 0;
> +}
> +
> +static int audio_close(hw_device_t *device)
> +{
> + DBG("");
> +
> + free(device);
> +
> + return 0;
> +}
> +
> +static int audio_open(const hw_module_t *module, const char *name,
> + hw_device_t **device)
> +{
> + struct hsp_audio_dev *adev;
> +
> + DBG("");
> +
> + if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
> + error("audio: interface %s not matching [%s]", name,
> + AUDIO_HARDWARE_INTERFACE);
> + return -EINVAL;
> + }
> +
> + adev = calloc(1, sizeof(struct hsp_audio_dev));
> + if (!adev)
> + return -ENOMEM;
> +
> + adev->dev.common.tag = HARDWARE_DEVICE_TAG;
> + adev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
> + adev->dev.common.module = (struct hw_module_t *) module;
> + adev->dev.common.close = audio_close;
> +
> + adev->dev.init_check = audio_init_check;
> + adev->dev.set_voice_volume = audio_set_voice_volume;
> + adev->dev.set_master_volume = audio_set_master_volume;
> + adev->dev.set_mode = audio_set_mode;
> + adev->dev.set_mic_mute = audio_set_mic_mute;
> + adev->dev.get_mic_mute = audio_get_mic_mute;
> + adev->dev.set_parameters = audio_set_parameters;
> + adev->dev.get_parameters = audio_get_parameters;
> + adev->dev.get_input_buffer_size = audio_get_input_buffer_size;
> + adev->dev.open_output_stream = audio_open_output_stream;
> + adev->dev.close_output_stream = audio_close_output_stream;
> + adev->dev.open_input_stream = audio_open_input_stream;
> + adev->dev.close_input_stream = audio_close_input_stream;
> + adev->dev.dump = audio_dump;
> +
> + *device = &adev->dev.common;
> +
> + return 0;
> +}
> +
> +static struct hw_module_methods_t hal_module_methods = {
> + .open = audio_open,
> +};
> +
> +struct audio_module HAL_MODULE_INFO_SYM = {
> + .common = {
> + .tag = HARDWARE_MODULE_TAG,
> + .version_major = 1,
> + .version_minor = 0,
> + .id = AUDIO_HARDWARE_MODULE_ID,
> + .name = "HSP Audio HW HAL",
> + .author = "Intel Corporation",
> + .methods = &hal_module_methods,
> + },
> +};
>
--
Best regards,
Szymon Janc
Hi Andrei,
On Thursday 17 of April 2014 15:23:45 Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> Fixes static analyzers NULL dereference warnings. The similar check is
> done in src/device.c in line 2959.
> ---
> android/handsfree.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/android/handsfree.c b/android/handsfree.c
> index 02b6dad..c8026a0 100644
> --- a/android/handsfree.c
> +++ b/android/handsfree.c
> @@ -1405,6 +1405,9 @@ static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data)
> goto fail;
> }
>
> + if (!classes)
> + goto fail;
> +
This is already checked :
if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) {
error("handsfree: unable to get service classes from record");
goto fail;
}
What kind of warning do you get from static analyzer?
> if (sdp_get_access_protos(recs->data, &protos) < 0) {
> error("handsfree: unable to get access protocols from record");
> sdp_list_free(classes, free);
> @@ -1495,6 +1498,9 @@ static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
> goto fail;
> }
>
> + if (!classes)
> + goto fail;
> +
Ditto.
> if (sdp_get_access_protos(recs->data, &protos) < 0) {
> error("handsfree: unable to get access protocols from record");
> sdp_list_free(classes, free);
>
--
Best regards,
Szymon Janc
From: Andrei Emeltchenko <[email protected]>
Function adds audio_open_output_stream() and sets dummy callbacks.
---
android/hal-audio-hsp.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 176 insertions(+), 3 deletions(-)
diff --git a/android/hal-audio-hsp.c b/android/hal-audio-hsp.c
index c7ba504..992066c 100644
--- a/android/hal-audio-hsp.c
+++ b/android/hal-audio-hsp.c
@@ -25,22 +25,195 @@
#include "hal-log.h"
+#define AUDIO_STREAM_DEFAULT_RATE 44100
+#define AUDIO_STREAM_DEFAULT_FORMAT AUDIO_FORMAT_PCM_16_BIT
+
+#define OUT_BUFFER_SIZE 2560
+
+struct hsp_audio_config {
+ uint32_t rate;
+ uint32_t channels;
+ audio_format_t format;
+};
+
+struct hsp_stream_out {
+ struct audio_stream_out stream;
+ struct hsp_audio_config cfg;
+};
+
struct hsp_audio_dev {
struct audio_hw_device dev;
- struct a2dp_stream_out *out;
+ struct hsp_stream_out *out;
};
+/* Audio stream functions */
+
+static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
+ size_t bytes)
+{
+ /* write data */
+
+ return bytes;
+}
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+ struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
+
+ DBG("");
+
+ return out->cfg.rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+ DBG("rate %u", rate);
+
+ return 0;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+ DBG("");
+
+ return OUT_BUFFER_SIZE;
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+ DBG("");
+
+ /* AudioFlinger can only provide stereo stream, so we return it here and
+ * later we'll downmix this to mono in case codec requires it
+ */
+ return AUDIO_CHANNEL_OUT_STEREO;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+ struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
+
+ DBG("");
+
+ return out->cfg.format;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+ DBG("");
+
+ return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ DBG("%s", kvpairs);
+
+ return 0;
+}
+
+static char *out_get_parameters(const struct audio_stream *stream,
+ const char *keys)
+{
+ DBG("");
+
+ return strdup("");
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+ DBG("");
+
+ return 0;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+ float right)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+ uint32_t *dsp_frames)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream,
+ effect_handle_t effect)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
static int audio_open_output_stream(struct audio_hw_device *dev,
audio_io_handle_t handle,
audio_devices_t devices,
audio_output_flags_t flags,
struct audio_config *config,
struct audio_stream_out **stream_out)
-
{
+ struct hsp_audio_dev *adev = (struct hsp_audio_dev *) dev;
+ struct hsp_stream_out *out;
+
DBG("");
- return -EINVAL;
+ out = calloc(1, sizeof(struct hsp_stream_out));
+ if (!out)
+ return -ENOMEM;
+
+ 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;
+
+ out->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
+ out->cfg.channels = AUDIO_CHANNEL_OUT_MONO;
+ out->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+
+ *stream_out = &out->stream;
+ adev->out = out;
+
+ return 0;
}
static void audio_close_output_stream(struct audio_hw_device *dev,
--
1.8.3.2
From: Andrei Emeltchenko <[email protected]>
---
android/hal-audio.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/android/hal-audio.c b/android/hal-audio.c
index c4bd4fe..76d1efb 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -1876,12 +1876,12 @@ static struct hw_module_methods_t hal_module_methods = {
struct audio_module HAL_MODULE_INFO_SYM = {
.common = {
- .tag = HARDWARE_MODULE_TAG,
- .version_major = 1,
- .version_minor = 0,
- .id = AUDIO_HARDWARE_MODULE_ID,
- .name = "A2DP Bluez HW HAL",
- .author = "Intel Corporation",
- .methods = &hal_module_methods,
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = AUDIO_HARDWARE_MODULE_ID,
+ .name = "A2DP Bluez HW HAL",
+ .author = "Intel Corporation",
+ .methods = &hal_module_methods,
},
};
--
1.8.3.2
From: Andrei Emeltchenko <[email protected]>
This adds audio HAL for handling SCO. Following needs to be added to
audio_policy.conf:
hsp {
outputs {
hsp {
...
devices AUDIO_DEVICE_OUT_ALL_SCO
...
}
}
---
android/Android.mk | 23 ++++++
android/Makefile.am | 15 ++++
android/hal-audio-hsp.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 249 insertions(+)
create mode 100644 android/hal-audio-hsp.c
diff --git a/android/Android.mk b/android/Android.mk
index 5f4e70c..a6aa55d 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -269,6 +269,29 @@ LOCAL_MODULE := audio.a2dp.default
include $(BUILD_SHARED_LIBRARY)
#
+# HSP audio
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := bluez/android/hal-audio-hsp.c
+
+LOCAL_C_INCLUDES = \
+ $(call include-path-for, system-core) \
+ $(call include-path-for, libhardware) \
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := audio.hsp.default
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
# l2cap-test
#
diff --git a/android/Makefile.am b/android/Makefile.am
index c51cce2..caeb9cc 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -167,6 +167,21 @@ android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
android/hardware/hardware.h \
android/system/audio.h
+android_audio_hsp_default_la_SOURCES = android/audio-msg.h \
+ android/hal-msg.h \
+ android/hal-audio-hsp.c \
+ android/hardware/audio.h \
+ android/hardware/audio_effect.h \
+ android/hardware/hardware.h \
+ android/system/audio.h
+
+android_audio_hsp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+
+android_audio_hsp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+ -no-undefined -lrt
+
+plugin_LTLIBRARIES += android/audio.hsp.default.la
+
unit_tests += android/test-ipc
android_test_ipc_SOURCES = android/test-ipc.c \
diff --git a/android/hal-audio-hsp.c b/android/hal-audio-hsp.c
new file mode 100644
index 0000000..c7ba504
--- /dev/null
+++ b/android/hal-audio-hsp.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hardware/audio.h>
+#include <hardware/hardware.h>
+
+#include "hal-log.h"
+
+struct hsp_audio_dev {
+ struct audio_hw_device dev;
+ struct a2dp_stream_out *out;
+};
+
+static int audio_open_output_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out)
+
+{
+ DBG("");
+
+ return -EINVAL;
+}
+
+static void audio_close_output_stream(struct audio_hw_device *dev,
+ struct audio_stream_out *stream_out)
+{
+ DBG("");
+
+ free(stream_out);
+}
+
+static int audio_set_parameters(struct audio_hw_device *dev,
+ const char *kvpairs)
+{
+ DBG("%s", kvpairs);
+
+ return 0;
+}
+
+static char *audio_get_parameters(const struct audio_hw_device *dev,
+ const char *keys)
+{
+ DBG("");
+
+ return strdup("");
+}
+
+static int audio_init_check(const struct audio_hw_device *dev)
+{
+ DBG("");
+
+ return 0;
+}
+
+static int audio_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+ DBG("%f", volume);
+
+ return 0;
+}
+
+static int audio_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+ DBG("%f", volume);
+
+ return 0;
+}
+
+static int audio_set_mode(struct audio_hw_device *dev, int mode)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int audio_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int audio_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static size_t audio_get_input_buffer_size(const struct audio_hw_device *dev,
+ const struct audio_config *config)
+{
+ DBG("");
+
+ return -ENOSYS;
+}
+
+static int audio_open_input_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ struct audio_stream_in **stream_in)
+{
+ DBG("");
+
+ return 0;
+}
+
+static void audio_close_input_stream(struct audio_hw_device *dev,
+ struct audio_stream_in *stream_in)
+{
+ DBG("");
+
+ free(stream_in);
+}
+
+static int audio_dump(const audio_hw_device_t *device, int fd)
+{
+ DBG("");
+
+ return 0;
+}
+
+static int audio_close(hw_device_t *device)
+{
+ DBG("");
+
+ free(device);
+
+ return 0;
+}
+
+static int audio_open(const hw_module_t *module, const char *name,
+ hw_device_t **device)
+{
+ struct hsp_audio_dev *adev;
+
+ DBG("");
+
+ if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+ error("audio: interface %s not matching [%s]", name,
+ AUDIO_HARDWARE_INTERFACE);
+ return -EINVAL;
+ }
+
+ adev = calloc(1, sizeof(struct hsp_audio_dev));
+ if (!adev)
+ return -ENOMEM;
+
+ adev->dev.common.tag = HARDWARE_DEVICE_TAG;
+ adev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
+ adev->dev.common.module = (struct hw_module_t *) module;
+ adev->dev.common.close = audio_close;
+
+ adev->dev.init_check = audio_init_check;
+ adev->dev.set_voice_volume = audio_set_voice_volume;
+ adev->dev.set_master_volume = audio_set_master_volume;
+ adev->dev.set_mode = audio_set_mode;
+ adev->dev.set_mic_mute = audio_set_mic_mute;
+ adev->dev.get_mic_mute = audio_get_mic_mute;
+ adev->dev.set_parameters = audio_set_parameters;
+ adev->dev.get_parameters = audio_get_parameters;
+ adev->dev.get_input_buffer_size = audio_get_input_buffer_size;
+ adev->dev.open_output_stream = audio_open_output_stream;
+ adev->dev.close_output_stream = audio_close_output_stream;
+ adev->dev.open_input_stream = audio_open_input_stream;
+ adev->dev.close_input_stream = audio_close_input_stream;
+ adev->dev.dump = audio_dump;
+
+ *device = &adev->dev.common;
+
+ return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+ .open = audio_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = AUDIO_HARDWARE_MODULE_ID,
+ .name = "HSP Audio HW HAL",
+ .author = "Intel Corporation",
+ .methods = &hal_module_methods,
+ },
+};
--
1.8.3.2
From: Andrei Emeltchenko <[email protected]>
---
android/handsfree.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/android/handsfree.c b/android/handsfree.c
index 55e151a..51dddb3 100644
--- a/android/handsfree.c
+++ b/android/handsfree.c
@@ -2204,10 +2204,10 @@ static const struct ipc_handler cmd_handlers[] = {
/* HAL_OP_HANDSFREE_DISCONNECT */
{ handle_disconnect, false,
sizeof(struct hal_cmd_handsfree_disconnect) },
- /*HAL_OP_HANDSFREE_CONNECT_AUDIO*/
+ /* HAL_OP_HANDSFREE_CONNECT_AUDIO */
{ handle_connect_audio, false,
sizeof(struct hal_cmd_handsfree_connect_audio) },
- /*HAL_OP_HANDSFREE_DISCONNECT_AUDIO*/
+ /* HAL_OP_HANDSFREE_DISCONNECT_AUDIO */
{ handle_disconnect_audio, false,
sizeof(struct hal_cmd_handsfree_disconnect_audio) },
/* define HAL_OP_HANDSFREE_START_VR */
--
1.8.3.2
From: Andrei Emeltchenko <[email protected]>
This helps to identify problem since DBG is not printed by default.
---
android/ipc.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/android/ipc.c b/android/ipc.c
index 8cd34ea..89fff8d 100644
--- a/android/ipc.c
+++ b/android/ipc.c
@@ -96,31 +96,31 @@ static int ipc_handle_msg(struct service_handler *handlers, size_t max_index,
const struct ipc_handler *handler;
if (len < (ssize_t) sizeof(*msg)) {
- DBG("message too small (%zd bytes)", len);
+ error("message too small (%zd bytes)", len);
return -EBADMSG;
}
if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
- DBG("message malformed (%zd bytes)", len);
+ error("message malformed (%zd bytes)", len);
return -EBADMSG;
}
/* if service is valid */
if (msg->service_id > max_index) {
- DBG("unknown service (0x%x)", msg->service_id);
+ error("unknown service (0x%x)", msg->service_id);
return -EOPNOTSUPP;
}
/* if service is registered */
if (!handlers[msg->service_id].handler) {
- DBG("service not registered (0x%x)", msg->service_id);
+ error("service not registered (0x%x)", msg->service_id);
return -EOPNOTSUPP;
}
/* if opcode is valid */
if (msg->opcode == IPC_OP_STATUS ||
msg->opcode > handlers[msg->service_id].size) {
- DBG("invalid opcode 0x%x for service 0x%x", msg->opcode,
+ error("invalid opcode 0x%x for service 0x%x", msg->opcode,
msg->service_id);
return -EOPNOTSUPP;
}
@@ -131,7 +131,7 @@ static int ipc_handle_msg(struct service_handler *handlers, size_t max_index,
/* if payload size is valid */
if ((handler->var_len && handler->data_len > msg->len) ||
(!handler->var_len && handler->data_len != msg->len)) {
- DBG("invalid size for opcode 0x%x service 0x%x",
+ error("invalid size for opcode 0x%x service 0x%x",
msg->opcode, msg->service_id);
return -EMSGSIZE;
}
--
1.8.3.2
From: Andrei Emeltchenko <[email protected]>
---
android/audio-msg.h | 6 +-
android/hal-audio-hsp.c | 314 +++++++++++++++++++++++++++++++++++++++++++++++-
android/handsfree.c | 42 +++++++
3 files changed, 358 insertions(+), 4 deletions(-)
diff --git a/android/audio-msg.h b/android/audio-msg.h
index 5981355..d247c80 100644
--- a/android/audio-msg.h
+++ b/android/audio-msg.h
@@ -24,9 +24,11 @@
#define BLUEZ_AUDIO_MTU 1024
static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
+static const char BLUEZ_AUDIO_SCO_SK_PATH[] = "\0bluez_audio_sco_socket";
#define AUDIO_SERVICE_ID 0
-#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_ID
+#define AUDIO_SERVICE_SCO_ID 1
+#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_SCO_ID
#define AUDIO_STATUS_SUCCESS IPC_STATUS_SUCCESS
#define AUDIO_STATUS_FAILED 0x01
@@ -79,3 +81,5 @@ struct audio_cmd_resume_stream {
struct audio_cmd_suspend_stream {
uint8_t id;
} __attribute__((packed));
+
+#define AUDIO_OP_SCO_GET_FD 0x01
diff --git a/android/hal-audio-hsp.c b/android/hal-audio-hsp.c
index 992066c..cac6c8d 100644
--- a/android/hal-audio-hsp.c
+++ b/android/hal-audio-hsp.c
@@ -16,13 +16,20 @@
*/
#include <errno.h>
+#include <pthread.h>
+#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
#include <hardware/audio.h>
#include <hardware/hardware.h>
+#include "audio-msg.h"
+#include "ipc-common.h"
#include "hal-log.h"
#define AUDIO_STREAM_DEFAULT_RATE 44100
@@ -30,6 +37,12 @@
#define OUT_BUFFER_SIZE 2560
+static int listen_sk = -1;
+static int audio_sk = -1;
+
+static pthread_t ipc_th = 0;
+static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
struct hsp_audio_config {
uint32_t rate;
uint32_t channels;
@@ -38,7 +51,9 @@ struct hsp_audio_config {
struct hsp_stream_out {
struct audio_stream_out stream;
+
struct hsp_audio_config cfg;
+ int fd;
};
struct hsp_audio_dev {
@@ -46,13 +61,178 @@ struct hsp_audio_dev {
struct hsp_stream_out *out;
};
+/* Audio IPC functions */
+
+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 ipc_hdr cmd;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ struct ipc_status s;
+ size_t s_len = sizeof(s);
+
+ pthread_mutex_lock(&sk_mutex);
+
+ 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;
+
+ ret = sendmsg(audio_sk, &msg, 0);
+ if (ret < 0) {
+ error("audio: Sending command failed:%s", strerror(errno));
+ 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));
+ goto failed;
+ }
+
+ 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 ipc_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;
+ }
+
+ pthread_mutex_unlock(&sk_mutex);
+
+ return s->code;
+ }
+
+ pthread_mutex_unlock(&sk_mutex);
+
+ /* 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 (*fd < 0)
+ goto failed;
+ }
+
+ if (rsp_len)
+ *rsp_len = cmd.len;
+
+ return AUDIO_STATUS_SUCCESS;
+
+failed:
+ /* Some serious issue happen on IPC - recover */
+ shutdown(audio_sk, SHUT_RDWR);
+ pthread_mutex_unlock(&sk_mutex);
+
+ return AUDIO_STATUS_FAILED;
+}
+
+static int ipc_get_sco_fd(int *fd)
+{
+ DBG("");
+
+ return audio_ipc_cmd(AUDIO_SERVICE_SCO_ID, AUDIO_OP_SCO_GET_FD, 0,
+ NULL, NULL, NULL, fd);
+}
+
/* Audio stream functions */
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
size_t bytes)
{
+ struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
+
/* write data */
+ DBG("write to fd %d bytes %zu", out->fd, bytes);
+
return bytes;
}
@@ -60,7 +240,7 @@ static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
struct hsp_stream_out *out = (struct hsp_stream_out *) stream;
- DBG("");
+ DBG("rate %u", out->cfg.rate);
return out->cfg.rate;
}
@@ -74,7 +254,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("");
+ DBG("buf size %u", OUT_BUFFER_SIZE);
return OUT_BUFFER_SIZE;
}
@@ -182,9 +362,17 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
{
struct hsp_audio_dev *adev = (struct hsp_audio_dev *) dev;
struct hsp_stream_out *out;
+ int fd = -1;
DBG("");
+ if (!ipc_get_sco_fd(&fd)) {
+ error("audio: cannot get fd");
+ return -EIO;
+ }
+
+ DBG("got sco fd %d", fd);
+
out = calloc(1, sizeof(struct hsp_stream_out));
if (!out)
return -ENOMEM;
@@ -212,6 +400,7 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
*stream_out = &out->stream;
adev->out = out;
+ out->fd = fd;
return 0;
}
@@ -219,9 +408,17 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
static void audio_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream_out)
{
- DBG("");
+ struct hsp_audio_dev *hsp_dev = (struct hsp_audio_dev *) dev;
+
+ DBG("dev %p stream %p fd %d", dev, stream_out, hsp_dev->out->fd);
+
+ if (hsp_dev->out && hsp_dev->out->fd) {
+ close(hsp_dev->out->fd);
+ hsp_dev->out->fd = -1;
+ }
free(stream_out);
+ hsp_dev->out = NULL;
}
static int audio_set_parameters(struct audio_hw_device *dev,
@@ -325,10 +522,117 @@ static int audio_close(hw_device_t *device)
return 0;
}
+static void *ipc_handler(void *data)
+{
+ bool done = false;
+ struct pollfd pfd;
+ int sk;
+
+ DBG("");
+
+ while (!done) {
+ DBG("Waiting for connection ...");
+
+ sk = accept(listen_sk, NULL, NULL);
+ if (sk < 0) {
+ int err = errno;
+
+ if (err == EINTR)
+ continue;
+
+ if (err != ECONNABORTED && err != EINVAL)
+ error("audio: Failed to accept socket: %d (%s)",
+ err, strerror(err));
+
+ break;
+ }
+
+ pthread_mutex_lock(&sk_mutex);
+ audio_sk = sk;
+ pthread_mutex_unlock(&sk_mutex);
+
+ DBG("Audio IPC: Connected");
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = audio_sk;
+ pfd.events = POLLHUP | POLLERR | POLLNVAL;
+
+ /* Check if socket is still alive. Empty while loop.*/
+ while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
+
+ if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
+ info("Audio HAL: Socket closed");
+
+ pthread_mutex_lock(&sk_mutex);
+ close(audio_sk);
+ audio_sk = -1;
+ pthread_mutex_unlock(&sk_mutex);
+ }
+ }
+
+ info("Closing Audio IPC thread");
+ return NULL;
+}
+
+static int audio_ipc_init(void)
+{
+ struct sockaddr_un addr;
+ int err;
+ int sk;
+
+ DBG("");
+
+ sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+ if (sk < 0) {
+ err = -errno;
+ error("audio: Failed to create socket: %d (%s)", -err,
+ strerror(-err));
+ return err;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+
+ memcpy(addr.sun_path, BLUEZ_AUDIO_SCO_SK_PATH,
+ sizeof(BLUEZ_AUDIO_SCO_SK_PATH));
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = -errno;
+ error("audio: Failed to bind socket: %d (%s)", -err,
+ strerror(-err));
+ goto failed;
+ }
+
+ if (listen(sk, 1) < 0) {
+ err = -errno;
+ error("audio: Failed to listen on the socket: %d (%s)", -err,
+ strerror(-err));
+ goto failed;
+ }
+
+ listen_sk = sk;
+
+ err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
+ if (err) {
+ err = -err;
+ ipc_th = 0;
+ error("audio: Failed to start Audio IPC thread: %d (%s)",
+ -err, strerror(-err));
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ close(sk);
+ return err;
+}
+
static int audio_open(const hw_module_t *module, const char *name,
hw_device_t **device)
{
struct hsp_audio_dev *adev;
+ int err;
DBG("");
@@ -338,6 +642,10 @@ static int audio_open(const hw_module_t *module, const char *name,
return -EINVAL;
}
+ err = audio_ipc_init();
+ if (err < 0)
+ return err;
+
adev = calloc(1, sizeof(struct hsp_audio_dev));
if (!adev)
return -ENOMEM;
diff --git a/android/handsfree.c b/android/handsfree.c
index c8026a0..55e151a 100644
--- a/android/handsfree.c
+++ b/android/handsfree.c
@@ -45,6 +45,7 @@
#include "bluetooth.h"
#include "src/log.h"
#include "utils.h"
+#include "audio-msg.h"
#define HSP_AG_CHANNEL 12
#define HFP_AG_CHANNEL 13
@@ -156,7 +157,9 @@ static struct {
static uint32_t hfp_ag_features = 0;
static bdaddr_t adapter_addr;
+
static struct ipc *hal_ipc = NULL;
+static struct ipc *audio_ipc = NULL;
static uint32_t hfp_record_id = 0;
static GIOChannel *hfp_server = NULL;
@@ -2563,6 +2566,42 @@ static void disable_sco_server(void)
}
}
+static void bt_audio_sco_get_fd(const void *buf, uint16_t len)
+{
+ int fd = -1;
+
+ connect_audio();
+
+ if (device.sco)
+ fd = g_io_channel_unix_get_fd(device.sco);
+
+ DBG("sco fd %d", fd);
+
+ ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_SCO_ID, AUDIO_OP_SCO_GET_FD,
+ 0, NULL, fd);
+}
+
+static const struct ipc_handler audio_handlers[] = {
+ /* AUDIO_OP_SCO_GET_FD */
+ { bt_audio_sco_get_fd, false, 0 }
+};
+
+static bool bt_audio_register(ipc_disconnect_cb disconnect)
+{
+ DBG("");
+
+ audio_ipc = ipc_init(BLUEZ_AUDIO_SCO_SK_PATH,
+ sizeof(BLUEZ_AUDIO_SCO_SK_PATH),
+ AUDIO_SERVICE_ID_MAX, false, disconnect, NULL);
+ if (!audio_ipc)
+ return false;
+
+ ipc_register(audio_ipc, AUDIO_SERVICE_SCO_ID, audio_handlers,
+ G_N_ELEMENTS(audio_handlers));
+
+ return true;
+}
+
bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
{
DBG("mode 0x%x", mode);
@@ -2597,6 +2636,9 @@ done:
hal_ipc = ipc;
ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
G_N_ELEMENTS(cmd_handlers));
+
+ bt_audio_register(NULL);
+
return true;
}
--
1.8.3.2