From: Luiz Augusto von Dentz <[email protected]>
This adds avrcp_send function which can be used to response to
outstanding requests.
---
android/avrcp-lib.c | 27 +++++++++++++++++++++++++++
android/avrcp-lib.h | 4 +++-
2 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index 34eea66..6fed825 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
@@ -253,6 +253,33 @@ int avrcp_init_uinput(struct avrcp *session, const char *name,
return avctp_init_uinput(session->conn, name, address);
}
+int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
+ uint8_t subunit, uint8_t pdu_id,
+ uint8_t *params, size_t params_len)
+{
+ struct avrcp_header *pdu = (void *) session->tx_buf;
+ size_t len = sizeof(*pdu);
+
+ memset(pdu, 0, len);
+
+ hton24(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = pdu_id;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+
+ if (params_len > 0) {
+ len += params_len;
+
+ if (len > session->tx_mtu)
+ return -ENOBUFS;
+
+ memcpy(pdu->params, params, params_len);
+ pdu->params_len = htons(params_len);
+ }
+
+ return avctp_send_vendordep(session->conn, transaction, code, subunit,
+ session->tx_buf, len);
+}
+
static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit,
uint8_t pdu_id, uint8_t *params,
size_t params_len, avctp_rsp_cb func,
diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
index 0407cb4..a33bdfe 100644
--- a/android/avrcp-lib.h
+++ b/android/avrcp-lib.h
@@ -102,7 +102,9 @@ void avrcp_set_passthrough_handlers(struct avrcp *session,
void *user_data);
int avrcp_init_uinput(struct avrcp *session, const char *name,
const char *address);
-
+int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
+ uint8_t subunit, uint8_t pdu_id,
+ uint8_t *params, size_t params_len);
int avrcp_get_capabilities(struct avrcp *session, uint8_t param,
avctp_rsp_cb func, void *user_data);
int avrcp_list_player_attributes(struct avrcp *session, avctp_rsp_cb func,
--
1.8.5.3
Hi,
On Sun, Mar 2, 2014 at 8:48 PM, Luiz Augusto von Dentz
<[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> This adds avrcp_send function which can be used to response to
> outstanding requests.
> ---
> android/avrcp-lib.c | 27 +++++++++++++++++++++++++++
> android/avrcp-lib.h | 4 +++-
> 2 files changed, 30 insertions(+), 1 deletion(-)
>
> diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
> index 34eea66..6fed825 100644
> --- a/android/avrcp-lib.c
> +++ b/android/avrcp-lib.c
> @@ -253,6 +253,33 @@ int avrcp_init_uinput(struct avrcp *session, const char *name,
> return avctp_init_uinput(session->conn, name, address);
> }
>
> +int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
> + uint8_t subunit, uint8_t pdu_id,
> + uint8_t *params, size_t params_len)
> +{
> + struct avrcp_header *pdu = (void *) session->tx_buf;
> + size_t len = sizeof(*pdu);
> +
> + memset(pdu, 0, len);
> +
> + hton24(pdu->company_id, IEEEID_BTSIG);
> + pdu->pdu_id = pdu_id;
> + pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
> +
> + if (params_len > 0) {
> + len += params_len;
> +
> + if (len > session->tx_mtu)
> + return -ENOBUFS;
> +
> + memcpy(pdu->params, params, params_len);
> + pdu->params_len = htons(params_len);
> + }
> +
> + return avctp_send_vendordep(session->conn, transaction, code, subunit,
> + session->tx_buf, len);
> +}
> +
> static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit,
> uint8_t pdu_id, uint8_t *params,
> size_t params_len, avctp_rsp_cb func,
> diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
> index 0407cb4..a33bdfe 100644
> --- a/android/avrcp-lib.h
> +++ b/android/avrcp-lib.h
> @@ -102,7 +102,9 @@ void avrcp_set_passthrough_handlers(struct avrcp *session,
> void *user_data);
> int avrcp_init_uinput(struct avrcp *session, const char *name,
> const char *address);
> -
> +int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
> + uint8_t subunit, uint8_t pdu_id,
> + uint8_t *params, size_t params_len);
> int avrcp_get_capabilities(struct avrcp *session, uint8_t param,
> avctp_rsp_cb func, void *user_data);
> int avrcp_list_player_attributes(struct avrcp *session, avctp_rsp_cb func,
> --
> 1.8.5.3
I went ahead and pushed this sets with a few fixes suggested.
--
Luiz Augusto von Dentz
Hi Luiz,
On Mon, Mar 03, 2014 at 02:41:19PM +0200, Luiz Augusto von Dentz wrote:
> Hi Andrei,
>
> On Mon, Mar 3, 2014 at 11:52 AM, Andrei Emeltchenko
> <[email protected]> wrote:
> > Hi Luiz,
> >
> > On Sun, Mar 02, 2014 at 08:48:18PM +0200, Luiz Augusto von Dentz wrote:
> >> From: Luiz Augusto von Dentz <[email protected]>
> >>
> >> This rework handler callback parameters to make it able to return
> >> proper error such as -EAGAIN to implement asynchronous responses.
> >> ---
> >> android/avrcp-lib.c | 12 +++++++++---
> >> android/avrcp-lib.h | 5 +++--
> >> unit/test-avrcp.c | 42 +++++++++++++++++++++---------------------
> >> 3 files changed, 33 insertions(+), 26 deletions(-)
> >>
> >> diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
> >> index cdad6ac..339be16 100644
> >> --- a/android/avrcp-lib.c
> >> +++ b/android/avrcp-lib.c
> >> @@ -125,6 +125,7 @@ static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
> >> struct avrcp_header *pdu = (void *) operands;
> >> uint32_t company_id = ntoh24(pdu->company_id);
> >> uint16_t params_len = ntohs(pdu->params_len);
> >> + ssize_t ret;
> >>
> >> if (company_id != IEEEID_BTSIG) {
> >> *code = AVC_CTYPE_NOT_IMPLEMENTED;
> >> @@ -159,12 +160,17 @@ static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
> >> goto reject;
> >> }
> >>
> >> - *code = handler->func(session, transaction, ¶ms_len,
> >> + ret = handler->func(session, transaction, code, params_len,
> >> pdu->params, session->control_data);
> >> + if (ret < 0) {
> >> + if (ret == -EAGAIN)
> >> + return ret;
> >> + goto reject;
> >> + }
> >>
> >> - pdu->params_len = htons(params_len);
> >> + pdu->params_len = htons(ret);
> >>
> >> - return AVRCP_HEADER_LENGTH + params_len;
> >> + return AVRCP_HEADER_LENGTH + ret;
> >>
> >> reject:
> >> pdu->params_len = htons(1);
> >> diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
> >> index a33bdfe..d7a805b 100644
> >> --- a/android/avrcp-lib.h
> >> +++ b/android/avrcp-lib.h
> >> @@ -79,8 +79,9 @@ struct avrcp;
> >> struct avrcp_control_handler {
> >> uint8_t id;
> >> uint8_t code;
> >> - uint8_t (*func) (struct avrcp *session, uint8_t transaction,
> >> - uint16_t *params_len, uint8_t *params, void *user_data);
> >> + ssize_t (*func) (struct avrcp *session, uint8_t transaction,
> >> + uint8_t *code, uint16_t params_len, uint8_t *params,
> >> + void *user_data);
> >> };
> >>
> >> struct avrcp_passthrough_handler {
> >> diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
> >> index 302c331..2d5711b 100644
> >> --- a/unit/test-avrcp.c
> >> +++ b/unit/test-avrcp.c
> >> @@ -292,54 +292,54 @@ static const struct avrcp_passthrough_handler passthrough_handlers[] = {
> >> { },
> >> };
> >>
> >> -static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
> >> - uint8_t transaction, uint16_t *params_len,
> >> - uint8_t *params, void *user_data)
> >> +static ssize_t avrcp_handle_get_capabilities(struct avrcp *session,
> >> + uint8_t transaction, uint8_t *code,
> >> + uint16_t params_len, uint8_t *params,
> >> + void *user_data)
> >> {
> >> uint32_t id = 0x001958;
> >>
> >> - DBG("params[0] %d params_len %d", params[0], *params_len);
> >> -
> >> - if (*params_len != 1)
> >> + if (params_len != 1)
> >> goto fail;
> >>
> >> switch (params[0]) {
> >> case CAP_COMPANY_ID:
> >> - *params_len = 5;
> >> params[1] = 1;
> >> hton24(¶ms[2], id);
> >> - return AVC_CTYPE_STABLE;
> >> + *code = AVC_CTYPE_STABLE;
> >> + return 5;
> >
> > What is 5? Shall we define constants or use formulas?
>
> This is the param length, ideally we should define structs for all the
> PDUs.
I do agree here, currently the pointer magic generates warnings from
static analyzers so they are not useful much.
Best regards
Andrei Emeltchenko
Hi Andrei,
On Mon, Mar 3, 2014 at 11:52 AM, Andrei Emeltchenko
<[email protected]> wrote:
> Hi Luiz,
>
> On Sun, Mar 02, 2014 at 08:48:18PM +0200, Luiz Augusto von Dentz wrote:
>> From: Luiz Augusto von Dentz <[email protected]>
>>
>> This rework handler callback parameters to make it able to return
>> proper error such as -EAGAIN to implement asynchronous responses.
>> ---
>> android/avrcp-lib.c | 12 +++++++++---
>> android/avrcp-lib.h | 5 +++--
>> unit/test-avrcp.c | 42 +++++++++++++++++++++---------------------
>> 3 files changed, 33 insertions(+), 26 deletions(-)
>>
>> diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
>> index cdad6ac..339be16 100644
>> --- a/android/avrcp-lib.c
>> +++ b/android/avrcp-lib.c
>> @@ -125,6 +125,7 @@ static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
>> struct avrcp_header *pdu = (void *) operands;
>> uint32_t company_id = ntoh24(pdu->company_id);
>> uint16_t params_len = ntohs(pdu->params_len);
>> + ssize_t ret;
>>
>> if (company_id != IEEEID_BTSIG) {
>> *code = AVC_CTYPE_NOT_IMPLEMENTED;
>> @@ -159,12 +160,17 @@ static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
>> goto reject;
>> }
>>
>> - *code = handler->func(session, transaction, ¶ms_len,
>> + ret = handler->func(session, transaction, code, params_len,
>> pdu->params, session->control_data);
>> + if (ret < 0) {
>> + if (ret == -EAGAIN)
>> + return ret;
>> + goto reject;
>> + }
>>
>> - pdu->params_len = htons(params_len);
>> + pdu->params_len = htons(ret);
>>
>> - return AVRCP_HEADER_LENGTH + params_len;
>> + return AVRCP_HEADER_LENGTH + ret;
>>
>> reject:
>> pdu->params_len = htons(1);
>> diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
>> index a33bdfe..d7a805b 100644
>> --- a/android/avrcp-lib.h
>> +++ b/android/avrcp-lib.h
>> @@ -79,8 +79,9 @@ struct avrcp;
>> struct avrcp_control_handler {
>> uint8_t id;
>> uint8_t code;
>> - uint8_t (*func) (struct avrcp *session, uint8_t transaction,
>> - uint16_t *params_len, uint8_t *params, void *user_data);
>> + ssize_t (*func) (struct avrcp *session, uint8_t transaction,
>> + uint8_t *code, uint16_t params_len, uint8_t *params,
>> + void *user_data);
>> };
>>
>> struct avrcp_passthrough_handler {
>> diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
>> index 302c331..2d5711b 100644
>> --- a/unit/test-avrcp.c
>> +++ b/unit/test-avrcp.c
>> @@ -292,54 +292,54 @@ static const struct avrcp_passthrough_handler passthrough_handlers[] = {
>> { },
>> };
>>
>> -static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
>> - uint8_t transaction, uint16_t *params_len,
>> - uint8_t *params, void *user_data)
>> +static ssize_t avrcp_handle_get_capabilities(struct avrcp *session,
>> + uint8_t transaction, uint8_t *code,
>> + uint16_t params_len, uint8_t *params,
>> + void *user_data)
>> {
>> uint32_t id = 0x001958;
>>
>> - DBG("params[0] %d params_len %d", params[0], *params_len);
>> -
>> - if (*params_len != 1)
>> + if (params_len != 1)
>> goto fail;
>>
>> switch (params[0]) {
>> case CAP_COMPANY_ID:
>> - *params_len = 5;
>> params[1] = 1;
>> hton24(¶ms[2], id);
>> - return AVC_CTYPE_STABLE;
>> + *code = AVC_CTYPE_STABLE;
>> + return 5;
>
> What is 5? Shall we define constants or use formulas?
This is the param length, ideally we should define structs for all the PDUs.
--
Luiz Augusto von Dentz
Hi Luiz,
On Sun, Mar 02, 2014 at 08:48:25PM +0200, Luiz Augusto von Dentz wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> ---
> android/avrcp-lib.h | 1 +
> android/avrcp.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 43 insertions(+)
>
> diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
> index 8625425..dda1d28 100644
> --- a/android/avrcp-lib.h
> +++ b/android/avrcp-lib.h
> @@ -51,6 +51,7 @@
> #define AVRCP_EVENT_TRACK_CHANGED 0x02
> #define AVRCP_EVENT_TRACK_REACHED_END 0x03
> #define AVRCP_EVENT_TRACK_REACHED_START 0x04
> +#define AVRCP_EVENT_PLAYBACK_POS_CHANGED 0x05
> #define AVRCP_EVENT_SETTINGS_CHANGED 0x08
> #define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED 0x0a
> #define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED 0x0b
> diff --git a/android/avrcp.c b/android/avrcp.c
> index 9d86803..bcaa66c 100644
> --- a/android/avrcp.c
> +++ b/android/avrcp.c
> @@ -26,6 +26,7 @@
> #endif
>
> #include <stdbool.h>
> +#include <errno.h>
> #include <glib.h>
>
> #include "btio/btio.h"
> @@ -339,6 +340,46 @@ static const struct avrcp_passthrough_handler passthrough_handlers[] = {
> { },
> };
>
> +static ssize_t handle_get_capabilities(struct avrcp *session,
> + uint8_t transaction,
> + uint16_t params_len,
> + uint8_t *params,
> + void *user_data)
> +{
> + uint32_t id = 0x001958;
Don't we have it defined?
Best regards
Andrei Emeltchenko
> +
> + if (params_len != 1)
> + return -EINVAL;
> +
> + switch (params[0]) {
> + case CAP_COMPANY_ID:
> + params[params_len++] = 1;
> + hton24(¶ms[params_len], id);
> + return params_len + 3;
> + case CAP_EVENTS_SUPPORTED:
> + /* Android do not provide this info via HAL so the list most
> + * be hardcoded according to what RegisterNotification can
> + * actually handle */
> + params[params_len++] = 6;
> + params[params_len++] = AVRCP_EVENT_STATUS_CHANGED;
> + params[params_len++] = AVRCP_EVENT_TRACK_CHANGED;
> + params[params_len++] = AVRCP_EVENT_TRACK_REACHED_END;
> + params[params_len++] = AVRCP_EVENT_TRACK_REACHED_START;
> + params[params_len++] = AVRCP_EVENT_PLAYBACK_POS_CHANGED;
> + params[params_len++] = AVRCP_EVENT_SETTINGS_CHANGED;
> + return params_len;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const struct avrcp_control_handler control_handlers[] = {
> + { AVRCP_GET_CAPABILITIES,
> + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
> + handle_get_capabilities },
> + { },
> +};
> +
> static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
> {
> struct avrcp_device *dev;
> @@ -390,6 +431,7 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
> avrcp_set_destroy_cb(dev->session, disconnect_cb, dev);
> avrcp_set_passthrough_handlers(dev->session, passthrough_handlers,
> dev);
> + avrcp_set_control_handlers(dev->session, control_handlers, dev);
>
> /* FIXME: get the real name of the device */
> avrcp_init_uinput(dev->session, "bluetooth", address);
> --
> 1.8.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Luiz,
On Sun, Mar 02, 2014 at 08:48:18PM +0200, Luiz Augusto von Dentz wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> This rework handler callback parameters to make it able to return
> proper error such as -EAGAIN to implement asynchronous responses.
> ---
> android/avrcp-lib.c | 12 +++++++++---
> android/avrcp-lib.h | 5 +++--
> unit/test-avrcp.c | 42 +++++++++++++++++++++---------------------
> 3 files changed, 33 insertions(+), 26 deletions(-)
>
> diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
> index cdad6ac..339be16 100644
> --- a/android/avrcp-lib.c
> +++ b/android/avrcp-lib.c
> @@ -125,6 +125,7 @@ static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
> struct avrcp_header *pdu = (void *) operands;
> uint32_t company_id = ntoh24(pdu->company_id);
> uint16_t params_len = ntohs(pdu->params_len);
> + ssize_t ret;
>
> if (company_id != IEEEID_BTSIG) {
> *code = AVC_CTYPE_NOT_IMPLEMENTED;
> @@ -159,12 +160,17 @@ static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
> goto reject;
> }
>
> - *code = handler->func(session, transaction, ¶ms_len,
> + ret = handler->func(session, transaction, code, params_len,
> pdu->params, session->control_data);
> + if (ret < 0) {
> + if (ret == -EAGAIN)
> + return ret;
> + goto reject;
> + }
>
> - pdu->params_len = htons(params_len);
> + pdu->params_len = htons(ret);
>
> - return AVRCP_HEADER_LENGTH + params_len;
> + return AVRCP_HEADER_LENGTH + ret;
>
> reject:
> pdu->params_len = htons(1);
> diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
> index a33bdfe..d7a805b 100644
> --- a/android/avrcp-lib.h
> +++ b/android/avrcp-lib.h
> @@ -79,8 +79,9 @@ struct avrcp;
> struct avrcp_control_handler {
> uint8_t id;
> uint8_t code;
> - uint8_t (*func) (struct avrcp *session, uint8_t transaction,
> - uint16_t *params_len, uint8_t *params, void *user_data);
> + ssize_t (*func) (struct avrcp *session, uint8_t transaction,
> + uint8_t *code, uint16_t params_len, uint8_t *params,
> + void *user_data);
> };
>
> struct avrcp_passthrough_handler {
> diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
> index 302c331..2d5711b 100644
> --- a/unit/test-avrcp.c
> +++ b/unit/test-avrcp.c
> @@ -292,54 +292,54 @@ static const struct avrcp_passthrough_handler passthrough_handlers[] = {
> { },
> };
>
> -static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
> - uint8_t transaction, uint16_t *params_len,
> - uint8_t *params, void *user_data)
> +static ssize_t avrcp_handle_get_capabilities(struct avrcp *session,
> + uint8_t transaction, uint8_t *code,
> + uint16_t params_len, uint8_t *params,
> + void *user_data)
> {
> uint32_t id = 0x001958;
>
> - DBG("params[0] %d params_len %d", params[0], *params_len);
> -
> - if (*params_len != 1)
> + if (params_len != 1)
> goto fail;
>
> switch (params[0]) {
> case CAP_COMPANY_ID:
> - *params_len = 5;
> params[1] = 1;
> hton24(¶ms[2], id);
> - return AVC_CTYPE_STABLE;
> + *code = AVC_CTYPE_STABLE;
> + return 5;
What is 5? Shall we define constants or use formulas?
Best regards
Andrei Emeltchenko
From: Luiz Augusto von Dentz <[email protected]>
---
android/avrcp.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 93 insertions(+), 1 deletion(-)
diff --git a/android/avrcp.c b/android/avrcp.c
index bcaa66c..3ac8223 100644
--- a/android/avrcp.c
+++ b/android/avrcp.c
@@ -37,6 +37,7 @@
#include "avctp.h"
#include "avrcp-lib.h"
#include "hal-msg.h"
+#include "ipc-common.h"
#include "ipc.h"
#include "bluetooth.h"
#include "avrcp.h"
@@ -54,18 +55,76 @@ static GSList *devices = NULL;
static GIOChannel *server = NULL;
static struct ipc *hal_ipc = NULL;
+struct avrcp_request {
+ struct avrcp_device *dev;
+ uint8_t pdu_id;
+ uint8_t transaction;
+};
+
struct avrcp_device {
bdaddr_t dst;
struct avrcp *session;
GIOChannel *io;
+ GQueue *queue;
};
+static struct avrcp_request *pop_request(uint8_t pdu_id)
+{
+ GSList *l;
+
+ for (l = devices; l; l = g_slist_next(l)) {
+ struct avrcp_device *dev = l->data;
+ GList *reqs = g_queue_peek_head_link(dev->queue);
+ int i;
+
+ for (i = 0; reqs; reqs = g_list_next(reqs), i++) {
+ struct avrcp_request *req = reqs->data;
+
+ if (req->pdu_id == pdu_id) {
+ g_queue_pop_nth(dev->queue, i);
+ return req;
+ }
+ }
+ }
+
+ return NULL;
+}
+
static void handle_get_play_status(const void *buf, uint16_t len)
{
+ const struct hal_cmd_avrcp_get_play_status *cmd = buf;
+ uint8_t status;
+ struct avrcp_request *req;
+ uint8_t pdu[9];
+ int ret;
+
DBG("");
+ req = pop_request(AVRCP_GET_PLAY_STATUS);
+ if (!req) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ bt_put_be32(cmd->position, &pdu[0]);
+ bt_put_be32(cmd->duration, &pdu[4]);
+ pdu[8] = cmd->status;
+
+ ret = avrcp_send(req->dev->session, req->transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, req->pdu_id,
+ pdu, sizeof(pdu));
+ if (ret < 0) {
+ status = HAL_STATUS_FAILED;
+ g_free(req);
+ goto done;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ g_free(req);
+
+done:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
- HAL_OP_AVRCP_GET_PLAY_STATUS, HAL_STATUS_FAILED);
+ HAL_OP_AVRCP_GET_PLAY_STATUS, status);
}
static void handle_list_player_attrs(const void *buf, uint16_t len)
@@ -244,6 +303,9 @@ static void avrcp_device_free(void *data)
{
struct avrcp_device *dev = data;
+ g_queue_foreach(dev->queue, (GFunc) g_free, NULL);
+ g_queue_free(dev->queue);
+
if (dev->session)
avrcp_shutdown(dev->session);
@@ -373,10 +435,38 @@ static ssize_t handle_get_capabilities(struct avrcp *session,
return -EINVAL;
}
+static ssize_t handle_get_play_status_cmd(struct avrcp *session,
+ uint8_t transaction,
+ uint16_t params_len,
+ uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_device *dev = user_data;
+ struct avrcp_request *req;
+
+ if (params_len != 0)
+ return -EINVAL;
+
+ req = g_new0(struct avrcp_request, 1);
+ req->dev = dev;
+ req->pdu_id = AVRCP_GET_CAPABILITIES;
+ req->transaction = transaction;
+
+ g_queue_push_tail(dev->queue, req);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_GET_PLAY_STATUS, 0, NULL);
+
+ return -EAGAIN;
+}
+
static const struct avrcp_control_handler control_handlers[] = {
{ AVRCP_GET_CAPABILITIES,
AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
handle_get_capabilities },
+ { AVRCP_GET_PLAY_STATUS,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+ handle_get_play_status_cmd },
{ },
};
@@ -433,6 +523,8 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
dev);
avrcp_set_control_handlers(dev->session, control_handlers, dev);
+ dev->queue = g_queue_new();
+
/* FIXME: get the real name of the device */
avrcp_init_uinput(dev->session, "bluetooth", address);
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
This makes the handlers code simpler as they don't need to care about
the response or reject opcode.
---
android/avrcp-lib.c | 16 ++++++++++++----
android/avrcp-lib.h | 4 ++--
unit/test-avrcp.c | 41 +++++++++++++++++++++--------------------
3 files changed, 35 insertions(+), 26 deletions(-)
diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index 339be16..7b89ee2 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
@@ -160,14 +160,22 @@ static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
goto reject;
}
- ret = handler->func(session, transaction, code, params_len,
- pdu->params, session->control_data);
+ ret = handler->func(session, transaction, params_len, pdu->params,
+ session->control_data);
if (ret < 0) {
- if (ret == -EAGAIN)
+ switch (ret) {
+ case -EAGAIN:
return ret;
- goto reject;
+ case -EINVAL:
+ pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+ goto reject;
+ default:
+ pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR;
+ goto reject;
+ }
}
+ *code = handler->rsp;
pdu->params_len = htons(ret);
return AVRCP_HEADER_LENGTH + ret;
diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
index d7a805b..df9f1c6 100644
--- a/android/avrcp-lib.h
+++ b/android/avrcp-lib.h
@@ -79,9 +79,9 @@ struct avrcp;
struct avrcp_control_handler {
uint8_t id;
uint8_t code;
+ uint8_t rsp;
ssize_t (*func) (struct avrcp *session, uint8_t transaction,
- uint8_t *code, uint16_t params_len, uint8_t *params,
- void *user_data);
+ uint16_t params_len, uint8_t *params, void *user_data);
};
struct avrcp_passthrough_handler {
diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
index 2d5711b..d8484cd 100644
--- a/unit/test-avrcp.c
+++ b/unit/test-avrcp.c
@@ -31,6 +31,7 @@
#include <inttypes.h>
#include <string.h>
#include <fcntl.h>
+#include <errno.h>
#include <sys/socket.h>
#include <glib.h>
@@ -293,61 +294,61 @@ static const struct avrcp_passthrough_handler passthrough_handlers[] = {
};
static ssize_t avrcp_handle_get_capabilities(struct avrcp *session,
- uint8_t transaction, uint8_t *code,
- uint16_t params_len, uint8_t *params,
- void *user_data)
+ uint8_t transaction,
+ uint16_t params_len,
+ uint8_t *params,
+ void *user_data)
{
uint32_t id = 0x001958;
if (params_len != 1)
- goto fail;
+ return -EINVAL;
switch (params[0]) {
case CAP_COMPANY_ID:
params[1] = 1;
hton24(¶ms[2], id);
- *code = AVC_CTYPE_STABLE;
return 5;
}
-fail:
- params[0] = AVRCP_STATUS_INVALID_PARAM;
- *code = AVC_CTYPE_REJECTED;
- return 1;
+ return -EINVAL;
}
static ssize_t avrcp_handle_list_attributes(struct avrcp *session,
- uint8_t transaction, uint8_t *code,
- uint16_t params_len, uint8_t *params,
- void *user_data)
+ uint8_t transaction,
+ uint16_t params_len,
+ uint8_t *params,
+ void *user_data)
{
DBG("");
params[0] = 0;
- *code = AVC_CTYPE_STABLE;
return 1;
}
static ssize_t avrcp_handle_get_player_attr_text(struct avrcp *session,
- uint8_t transaction, uint8_t *code,
- uint16_t params_len, uint8_t *params,
- void *user_data)
+ uint8_t transaction,
+ uint16_t params_len,
+ uint8_t *params,
+ void *user_data)
{
DBG("");
params[0] = 0;
- *code = AVC_CTYPE_STABLE;
return 1;
}
static const struct avrcp_control_handler control_handlers[] = {
- { AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
+ { AVRCP_GET_CAPABILITIES,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
avrcp_handle_get_capabilities },
- { AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS,
+ { AVRCP_LIST_PLAYER_ATTRIBUTES,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
avrcp_handle_list_attributes },
- { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, AVC_CTYPE_STATUS,
+ { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
avrcp_handle_get_player_attr_text },
{ },
};
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
---
android/avrcp-lib.c | 2 +-
android/avrcp-lib.h | 2 +-
unit/test-avrcp.c | 8 ++++----
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index 7b89ee2..8854263 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
@@ -209,7 +209,7 @@ static bool handle_passthrough_pdu(struct avctp *conn, uint8_t op,
if (!pressed)
return true;
- return handler->func(session);
+ return handler->func(session, session->passthrough_data);
}
struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
index df9f1c6..430f545 100644
--- a/android/avrcp-lib.h
+++ b/android/avrcp-lib.h
@@ -86,7 +86,7 @@ struct avrcp_control_handler {
struct avrcp_passthrough_handler {
uint8_t op;
- bool (*func) (struct avrcp *session);
+ bool (*func) (struct avrcp *session, void *user_data);
};
typedef void (*avrcp_destroy_cb_t) (void *user_data);
diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
index d8484cd..ac267bb 100644
--- a/unit/test-avrcp.c
+++ b/unit/test-avrcp.c
@@ -257,28 +257,28 @@ static void execute_context(struct context *context)
destroy_context(context);
}
-static bool handle_play(struct avrcp *session)
+static bool handle_play(struct avrcp *session, void *user_data)
{
DBG("");
return true;
}
-static bool handle_volume_up(struct avrcp *session)
+static bool handle_volume_up(struct avrcp *session, void *user_data)
{
DBG("");
return true;
}
-static bool handle_channel_up(struct avrcp *session)
+static bool handle_channel_up(struct avrcp *session, void *user_data)
{
DBG("");
return true;
}
-static bool handle_select(struct avrcp *session)
+static bool handle_select(struct avrcp *session, void *user_data)
{
DBG("");
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
---
android/avrcp.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 112 insertions(+), 1 deletion(-)
diff --git a/android/avrcp.c b/android/avrcp.c
index 3ac8223..6ef8d0a 100644
--- a/android/avrcp.c
+++ b/android/avrcp.c
@@ -167,12 +167,79 @@ static void handle_get_player_values_text(const void *buf, uint16_t len)
HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, HAL_STATUS_FAILED);
}
+static void write_element_text(uint8_t id, uint8_t text_len, uint8_t *text,
+ uint8_t *pdu, size_t *len)
+{
+ uint16_t charset = 106;
+
+ bt_put_be32(id, pdu);
+ pdu += 4;
+ *len += 4;
+
+ bt_put_be16(charset, pdu);
+ pdu += 2;
+ *len += 2;
+
+ bt_put_be16(text_len, pdu);
+ pdu += 2;
+ *len += 2;
+
+ memcpy(pdu, text, text_len);
+ *len += text_len;
+}
+
+static void write_element_attrs(uint8_t *ptr, uint8_t number, uint8_t *pdu,
+ size_t *len)
+{
+ int i;
+
+ for (i = 0; i < number; i++) {
+ struct hal_avrcp_player_setting_text *text = (void *) ptr;
+
+ write_element_text(text->id, text->len, text->text, pdu, len);
+
+ ptr += sizeof(*text) + text->len;
+ pdu += *len;
+ }
+}
+
static void handle_get_element_attrs_text(const void *buf, uint16_t len)
{
+ struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf;
+ uint8_t status;
+ struct avrcp_request *req;
+ uint8_t pdu[IPC_MTU];
+ uint8_t *ptr;
+ size_t pdu_len;
+ int ret;
+
DBG("");
+ req = pop_request(AVRCP_GET_ELEMENT_ATTRIBUTES);
+ if (!req) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ ptr = (uint8_t *) &cmd->values[0];
+ pdu_len = 0;
+ write_element_attrs(ptr, cmd->number, pdu, &pdu_len);
+
+ ret = avrcp_send(req->dev->session, req->transaction, AVC_CTYPE_STABLE,
+ AVC_SUBUNIT_PANEL, req->pdu_id,
+ pdu, pdu_len);
+ if (ret < 0) {
+ status = HAL_STATUS_FAILED;
+ g_free(req);
+ goto done;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ g_free(req);
+
+done:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
- HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, HAL_STATUS_FAILED);
+ HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, status);
}
static void handle_set_player_attrs_value(const void *buf, uint16_t len)
@@ -460,6 +527,47 @@ static ssize_t handle_get_play_status_cmd(struct avrcp *session,
return -EAGAIN;
}
+static ssize_t handle_get_element_attrs_cmd(struct avrcp *session,
+ uint8_t transaction,
+ uint16_t params_len,
+ uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_device *dev = user_data;
+ struct avrcp_request *req;
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_avrcp_get_element_attrs *ev = (void *) buf;
+ int i;
+
+ if (params_len < 9)
+ return -EINVAL;
+
+ ev->number = params[8];
+
+ if (params_len < ev->number * sizeof(uint32_t) + 1)
+ return -EINVAL;
+
+ params += 9;
+ for (i = 0; i < ev->number; i++) {
+ ev->attrs[i] = bt_get_be32(params);
+ params += 4;
+ }
+
+ req = g_new0(struct avrcp_request, 1);
+ req->dev = dev;
+ req->pdu_id = AVRCP_GET_ELEMENT_ATTRIBUTES;
+ req->transaction = transaction;
+
+ g_queue_push_tail(dev->queue, req);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_GET_ELEMENT_ATTRS,
+ sizeof(*ev) + ev->number, ev);
+
+ return -EAGAIN;
+
+}
+
static const struct avrcp_control_handler control_handlers[] = {
{ AVRCP_GET_CAPABILITIES,
AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
@@ -467,6 +575,9 @@ static const struct avrcp_control_handler control_handlers[] = {
{ AVRCP_GET_PLAY_STATUS,
AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
handle_get_play_status_cmd },
+ { AVRCP_GET_ELEMENT_ATTRIBUTES,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+ handle_get_element_attrs_cmd },
{ },
};
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
This adds pressed status to passthrough handler callback which is
required by Android HAL for certain keys such as fast forward and rewind.
---
android/avrcp-lib.c | 6 +-----
android/avrcp-lib.h | 2 +-
unit/test-avrcp.c | 10 ++++++----
3 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index 8854263..40ff7b3 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
@@ -205,11 +205,7 @@ static bool handle_passthrough_pdu(struct avctp *conn, uint8_t op,
if (handler->func == NULL)
return false;
- /* Do not trigger handler on release */
- if (!pressed)
- return true;
-
- return handler->func(session, session->passthrough_data);
+ return handler->func(session, pressed, session->passthrough_data);
}
struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
index 430f545..3df5897 100644
--- a/android/avrcp-lib.h
+++ b/android/avrcp-lib.h
@@ -86,7 +86,7 @@ struct avrcp_control_handler {
struct avrcp_passthrough_handler {
uint8_t op;
- bool (*func) (struct avrcp *session, void *user_data);
+ bool (*func) (struct avrcp *session, bool pressed, void *user_data);
};
typedef void (*avrcp_destroy_cb_t) (void *user_data);
diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
index ac267bb..0294973 100644
--- a/unit/test-avrcp.c
+++ b/unit/test-avrcp.c
@@ -257,28 +257,30 @@ static void execute_context(struct context *context)
destroy_context(context);
}
-static bool handle_play(struct avrcp *session, void *user_data)
+static bool handle_play(struct avrcp *session, bool pressed, void *user_data)
{
DBG("");
return true;
}
-static bool handle_volume_up(struct avrcp *session, void *user_data)
+static bool handle_volume_up(struct avrcp *session, bool pressed,
+ void *user_data)
{
DBG("");
return true;
}
-static bool handle_channel_up(struct avrcp *session, void *user_data)
+static bool handle_channel_up(struct avrcp *session, bool pressed,
+ void *user_data)
{
DBG("");
return true;
}
-static bool handle_select(struct avrcp *session, void *user_data)
+static bool handle_select(struct avrcp *session, bool pressed, void *user_data)
{
DBG("");
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
Fast forward should be send via HAL not handled directly by uinput
otherwise Android media player might not interpret it correctly.
---
android/avrcp.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/android/avrcp.c b/android/avrcp.c
index 678c321..3d56ccf 100644
--- a/android/avrcp.c
+++ b/android/avrcp.c
@@ -301,6 +301,27 @@ static void disconnect_cb(void *data)
avrcp_device_remove(dev);
}
+static bool handle_fast_forward(struct avrcp *session, bool pressed,
+ void *user_data)
+{
+ struct hal_ev_avrcp_passthrough_cmd ev;
+
+ DBG("pressed %s", pressed ? "true" : "false");
+
+ ev.id = AVC_FAST_FORWARD;
+ ev.state = pressed;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev);
+
+ return true;
+}
+
+static const struct avrcp_passthrough_handler passthrough_handlers[] = {
+ { AVC_FAST_FORWARD, handle_fast_forward },
+ { },
+};
+
static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
{
struct avrcp_device *dev;
@@ -350,6 +371,8 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
}
avrcp_set_destroy_cb(dev->session, disconnect_cb, dev);
+ avrcp_set_passthrough_handlers(dev->session, passthrough_handlers,
+ dev);
/* FIXME: get the real name of the device */
avrcp_init_uinput(dev->session, "bluetooth", address);
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
Rewind should be send via HAL not handled directly by uinput otherwise
Android media player might not interpret it correctly.
---
android/avrcp.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/android/avrcp.c b/android/avrcp.c
index 3d56ccf..9d86803 100644
--- a/android/avrcp.c
+++ b/android/avrcp.c
@@ -317,8 +317,25 @@ static bool handle_fast_forward(struct avrcp *session, bool pressed,
return true;
}
+static bool handle_rewind(struct avrcp *session, bool pressed,
+ void *user_data)
+{
+ struct hal_ev_avrcp_passthrough_cmd ev;
+
+ DBG("pressed %s", pressed ? "true" : "false");
+
+ ev.id = AVC_REWIND;
+ ev.state = pressed;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev);
+
+ return true;
+}
+
static const struct avrcp_passthrough_handler passthrough_handlers[] = {
{ AVC_FAST_FORWARD, handle_fast_forward },
+ { AVC_REWIND, handle_rewind },
{ },
};
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
---
android/avrcp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/android/avrcp.c b/android/avrcp.c
index 875c6e2..268c1b1 100644
--- a/android/avrcp.c
+++ b/android/avrcp.c
@@ -355,7 +355,7 @@ static sdp_record_t *avrcp_record(void)
sdp_record_t *record;
sdp_data_t *psm, *version, *features;
uint16_t lp = L2CAP_PSM_AVCTP;
- uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103;
+ uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
This avoids duplicating those function everytime.
---
android/avrcp-lib.c | 12 ------------
android/avrcp-lib.h | 12 ++++++++++++
unit/test-avrcp.c | 7 -------
3 files changed, 12 insertions(+), 19 deletions(-)
diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index 40ff7b3..60c8ba3 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
@@ -73,18 +73,6 @@ struct avrcp_header {
#error "Unknown byte order"
#endif
-static inline uint32_t ntoh24(const uint8_t src[3])
-{
- return src[0] << 16 | src[1] << 8 | src[2];
-}
-
-static inline void hton24(uint8_t dst[3], uint32_t src)
-{
- dst[0] = (src & 0xff0000) >> 16;
- dst[1] = (src & 0x00ff00) >> 8;
- dst[2] = (src & 0x0000ff);
-}
-
struct avrcp {
struct avctp *conn;
diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
index 3df5897..8625425 100644
--- a/android/avrcp-lib.h
+++ b/android/avrcp-lib.h
@@ -91,6 +91,18 @@ struct avrcp_passthrough_handler {
typedef void (*avrcp_destroy_cb_t) (void *user_data);
+static inline uint32_t ntoh24(const uint8_t src[3])
+{
+ return src[0] << 16 | src[1] << 8 | src[2];
+}
+
+static inline void hton24(uint8_t dst[3], uint32_t src)
+{
+ dst[0] = (src & 0xff0000) >> 16;
+ dst[1] = (src & 0x00ff00) >> 8;
+ dst[2] = (src & 0x0000ff);
+}
+
struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
void avrcp_shutdown(struct avrcp *session);
void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb,
diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
index 0294973..695f62c 100644
--- a/unit/test-avrcp.c
+++ b/unit/test-avrcp.c
@@ -94,13 +94,6 @@ struct context {
g_test_add_data_func(name, &data, function); \
} while (0)
-static inline void hton24(uint8_t dst[3], uint32_t src)
-{
- dst[0] = (src & 0xff0000) >> 16;
- dst[1] = (src & 0x00ff00) >> 8;
- dst[2] = (src & 0x0000ff);
-}
-
static void test_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
---
android/avrcp-lib.h | 1 +
android/avrcp.c | 42 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+)
diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
index 8625425..dda1d28 100644
--- a/android/avrcp-lib.h
+++ b/android/avrcp-lib.h
@@ -51,6 +51,7 @@
#define AVRCP_EVENT_TRACK_CHANGED 0x02
#define AVRCP_EVENT_TRACK_REACHED_END 0x03
#define AVRCP_EVENT_TRACK_REACHED_START 0x04
+#define AVRCP_EVENT_PLAYBACK_POS_CHANGED 0x05
#define AVRCP_EVENT_SETTINGS_CHANGED 0x08
#define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED 0x0a
#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED 0x0b
diff --git a/android/avrcp.c b/android/avrcp.c
index 9d86803..bcaa66c 100644
--- a/android/avrcp.c
+++ b/android/avrcp.c
@@ -26,6 +26,7 @@
#endif
#include <stdbool.h>
+#include <errno.h>
#include <glib.h>
#include "btio/btio.h"
@@ -339,6 +340,46 @@ static const struct avrcp_passthrough_handler passthrough_handlers[] = {
{ },
};
+static ssize_t handle_get_capabilities(struct avrcp *session,
+ uint8_t transaction,
+ uint16_t params_len,
+ uint8_t *params,
+ void *user_data)
+{
+ uint32_t id = 0x001958;
+
+ if (params_len != 1)
+ return -EINVAL;
+
+ switch (params[0]) {
+ case CAP_COMPANY_ID:
+ params[params_len++] = 1;
+ hton24(¶ms[params_len], id);
+ return params_len + 3;
+ case CAP_EVENTS_SUPPORTED:
+ /* Android do not provide this info via HAL so the list most
+ * be hardcoded according to what RegisterNotification can
+ * actually handle */
+ params[params_len++] = 6;
+ params[params_len++] = AVRCP_EVENT_STATUS_CHANGED;
+ params[params_len++] = AVRCP_EVENT_TRACK_CHANGED;
+ params[params_len++] = AVRCP_EVENT_TRACK_REACHED_END;
+ params[params_len++] = AVRCP_EVENT_TRACK_REACHED_START;
+ params[params_len++] = AVRCP_EVENT_PLAYBACK_POS_CHANGED;
+ params[params_len++] = AVRCP_EVENT_SETTINGS_CHANGED;
+ return params_len;
+ }
+
+ return -EINVAL;
+}
+
+static const struct avrcp_control_handler control_handlers[] = {
+ { AVRCP_GET_CAPABILITIES,
+ AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+ handle_get_capabilities },
+ { },
+};
+
static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
{
struct avrcp_device *dev;
@@ -390,6 +431,7 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
avrcp_set_destroy_cb(dev->session, disconnect_cb, dev);
avrcp_set_passthrough_handlers(dev->session, passthrough_handlers,
dev);
+ avrcp_set_control_handlers(dev->session, control_handlers, dev);
/* FIXME: get the real name of the device */
avrcp_init_uinput(dev->session, "bluetooth", address);
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
---
android/avrcp.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 83 insertions(+), 1 deletion(-)
diff --git a/android/avrcp.c b/android/avrcp.c
index 6ef8d0a..875c6e2 100644
--- a/android/avrcp.c
+++ b/android/avrcp.c
@@ -252,10 +252,58 @@ static void handle_set_player_attrs_value(const void *buf, uint16_t len)
static void handle_register_notification(const void *buf, uint16_t len)
{
+ struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+ uint8_t status;
+ struct avrcp_request *req;
+ uint8_t pdu[IPC_MTU];
+ size_t pdu_len = 1;
+ int ret;
+
DBG("");
+ req = pop_request(AVRCP_REGISTER_NOTIFICATION);
+ if (!req) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ pdu[0] = cmd->event;
+
+ switch (cmd->event) {
+ case AVRCP_EVENT_STATUS_CHANGED:
+ case AVRCP_EVENT_TRACK_CHANGED:
+ case AVRCP_EVENT_TRACK_REACHED_END:
+ case AVRCP_EVENT_TRACK_REACHED_START:
+ case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+ if (cmd->len > 0) {
+ memcpy(&pdu[1], cmd->data, cmd->len);
+ pdu_len += cmd->len;
+ }
+ break;
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ pdu[1] = cmd->len / sizeof(struct hal_avrcp_player_attr_value);
+ memcpy(&pdu[2], cmd->data, cmd->len);
+ break;
+ default:
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ ret = avrcp_send(req->dev->session, req->transaction, cmd->type,
+ AVC_SUBUNIT_PANEL, req->pdu_id,
+ pdu, pdu_len);
+ if (ret < 0) {
+ status = HAL_STATUS_FAILED;
+ g_free(req);
+ goto done;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ g_free(req);
+
+done:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
- HAL_OP_AVRCP_REGISTER_NOTIFICATION, HAL_STATUS_FAILED);
+ HAL_OP_AVRCP_REGISTER_NOTIFICATION, status);
}
static void handle_set_volume(const void *buf, uint16_t len)
@@ -568,6 +616,37 @@ static ssize_t handle_get_element_attrs_cmd(struct avrcp *session,
}
+static ssize_t handle_register_notification_cmd(struct avrcp *session,
+ uint8_t transaction,
+ uint16_t params_len,
+ uint8_t *params,
+ void *user_data)
+{
+ struct avrcp_device *dev = user_data;
+ struct avrcp_request *req;
+ struct hal_ev_avrcp_register_notification ev;
+
+ if (params_len != 5)
+ return -EINVAL;
+
+ req = g_new0(struct avrcp_request, 1);
+ req->dev = dev;
+ req->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+ req->transaction = transaction;
+
+ g_queue_push_tail(dev->queue, req);
+
+ ev.event = params[0];
+ ev.param = bt_get_be32(¶ms[1]);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+ HAL_EV_AVRCP_REGISTER_NOTIFICATION,
+ sizeof(ev), &ev);
+
+ return -EAGAIN;
+
+}
+
static const struct avrcp_control_handler control_handlers[] = {
{ AVRCP_GET_CAPABILITIES,
AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
@@ -578,6 +657,9 @@ static const struct avrcp_control_handler control_handlers[] = {
{ AVRCP_GET_ELEMENT_ATTRIBUTES,
AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
handle_get_element_attrs_cmd },
+ { AVRCP_REGISTER_NOTIFICATION,
+ AVC_CTYPE_NOTIFY, AVC_CTYPE_INTERIM,
+ handle_register_notification_cmd },
{ },
};
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
This rework handler callback parameters to make it able to return
proper error such as -EAGAIN to implement asynchronous responses.
---
android/avrcp-lib.c | 12 +++++++++---
android/avrcp-lib.h | 5 +++--
unit/test-avrcp.c | 42 +++++++++++++++++++++---------------------
3 files changed, 33 insertions(+), 26 deletions(-)
diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index cdad6ac..339be16 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
@@ -125,6 +125,7 @@ static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
struct avrcp_header *pdu = (void *) operands;
uint32_t company_id = ntoh24(pdu->company_id);
uint16_t params_len = ntohs(pdu->params_len);
+ ssize_t ret;
if (company_id != IEEEID_BTSIG) {
*code = AVC_CTYPE_NOT_IMPLEMENTED;
@@ -159,12 +160,17 @@ static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
goto reject;
}
- *code = handler->func(session, transaction, ¶ms_len,
+ ret = handler->func(session, transaction, code, params_len,
pdu->params, session->control_data);
+ if (ret < 0) {
+ if (ret == -EAGAIN)
+ return ret;
+ goto reject;
+ }
- pdu->params_len = htons(params_len);
+ pdu->params_len = htons(ret);
- return AVRCP_HEADER_LENGTH + params_len;
+ return AVRCP_HEADER_LENGTH + ret;
reject:
pdu->params_len = htons(1);
diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
index a33bdfe..d7a805b 100644
--- a/android/avrcp-lib.h
+++ b/android/avrcp-lib.h
@@ -79,8 +79,9 @@ struct avrcp;
struct avrcp_control_handler {
uint8_t id;
uint8_t code;
- uint8_t (*func) (struct avrcp *session, uint8_t transaction,
- uint16_t *params_len, uint8_t *params, void *user_data);
+ ssize_t (*func) (struct avrcp *session, uint8_t transaction,
+ uint8_t *code, uint16_t params_len, uint8_t *params,
+ void *user_data);
};
struct avrcp_passthrough_handler {
diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
index 302c331..2d5711b 100644
--- a/unit/test-avrcp.c
+++ b/unit/test-avrcp.c
@@ -292,54 +292,54 @@ static const struct avrcp_passthrough_handler passthrough_handlers[] = {
{ },
};
-static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
- uint8_t transaction, uint16_t *params_len,
- uint8_t *params, void *user_data)
+static ssize_t avrcp_handle_get_capabilities(struct avrcp *session,
+ uint8_t transaction, uint8_t *code,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
{
uint32_t id = 0x001958;
- DBG("params[0] %d params_len %d", params[0], *params_len);
-
- if (*params_len != 1)
+ if (params_len != 1)
goto fail;
switch (params[0]) {
case CAP_COMPANY_ID:
- *params_len = 5;
params[1] = 1;
hton24(¶ms[2], id);
- return AVC_CTYPE_STABLE;
+ *code = AVC_CTYPE_STABLE;
+ return 5;
}
fail:
- *params_len = 1;
params[0] = AVRCP_STATUS_INVALID_PARAM;
-
- return AVC_CTYPE_REJECTED;
+ *code = AVC_CTYPE_REJECTED;
+ return 1;
}
-static uint8_t avrcp_handle_list_attributes(struct avrcp *session,
- uint8_t transaction, uint16_t *params_len,
- uint8_t *params, void *user_data)
+static ssize_t avrcp_handle_list_attributes(struct avrcp *session,
+ uint8_t transaction, uint8_t *code,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
{
DBG("");
- *params_len = 1;
params[0] = 0;
+ *code = AVC_CTYPE_STABLE;
- return AVC_CTYPE_STABLE;
+ return 1;
}
-static uint8_t avrcp_handle_get_player_attr_text(struct avrcp *session,
- uint8_t transaction, uint16_t *params_len,
- uint8_t *params, void *user_data)
+static ssize_t avrcp_handle_get_player_attr_text(struct avrcp *session,
+ uint8_t transaction, uint8_t *code,
+ uint16_t params_len, uint8_t *params,
+ void *user_data)
{
DBG("");
- *params_len = 1;
params[0] = 0;
+ *code = AVC_CTYPE_STABLE;
- return AVC_CTYPE_STABLE;
+ return 1;
}
static const struct avrcp_control_handler control_handlers[] = {
--
1.8.5.3
From: Luiz Augusto von Dentz <[email protected]>
This makes possible to return errors such as -EAGAIN to indicate the
request would block and a response will be sent asynchronously.
---
android/avctp.c | 17 ++++++++++++-----
android/avctp.h | 2 +-
android/avrcp-lib.c | 2 +-
unit/test-avctp.c | 2 +-
4 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/android/avctp.c b/android/avctp.c
index bc1bd80..aa5abc9 100644
--- a/android/avctp.c
+++ b/android/avctp.c
@@ -311,7 +311,7 @@ static gboolean auto_release(gpointer user_data)
return FALSE;
}
-static size_t handle_panel_passthrough(struct avctp *session,
+static ssize_t handle_panel_passthrough(struct avctp *session,
uint8_t transaction, uint8_t *code,
uint8_t *subunit, uint8_t *operands,
size_t operand_count, void *user_data)
@@ -402,7 +402,7 @@ done:
return operand_count;
}
-static size_t handle_unit_info(struct avctp *session,
+static ssize_t handle_unit_info(struct avctp *session,
uint8_t transaction, uint8_t *code,
uint8_t *subunit, uint8_t *operands,
size_t operand_count, void *user_data)
@@ -428,7 +428,7 @@ static size_t handle_unit_info(struct avctp *session,
return operand_count;
}
-static size_t handle_subunit_info(struct avctp *session,
+static ssize_t handle_subunit_info(struct avctp *session,
uint8_t transaction, uint8_t *code,
uint8_t *subunit, uint8_t *operands,
size_t operand_count, void *user_data)
@@ -849,8 +849,9 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
uint8_t *operands, code, subunit;
struct avctp_header *avctp;
struct avc_header *avc;
- int ret, packet_size, operand_count, sock;
+ int packet_size, operand_count, sock;
struct avctp_pdu_handler *handler;
+ ssize_t ret;
if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
goto failed;
@@ -910,10 +911,16 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
code = avc->code;
subunit = avc->subunit_type;
- packet_size += handler->cb(session, avctp->transaction, &code,
+ ret = handler->cb(session, avctp->transaction, &code,
&subunit, operands, operand_count,
handler->user_data);
+ if (ret < 0) {
+ if (ret == -EAGAIN)
+ return TRUE;
+ goto failed;
+ }
+ packet_size += ret;
avc->code = code;
avc->subunit_type = subunit;
diff --git a/android/avctp.h b/android/avctp.h
index dfa0ca6..2f419a2 100644
--- a/android/avctp.h
+++ b/android/avctp.h
@@ -113,7 +113,7 @@ struct avctp;
typedef bool (*avctp_passthrough_cb) (struct avctp *session,
uint8_t op, bool pressed,
void *user_data);
-typedef size_t (*avctp_control_pdu_cb) (struct avctp *session,
+typedef ssize_t (*avctp_control_pdu_cb) (struct avctp *session,
uint8_t transaction, uint8_t *code,
uint8_t *subunit, uint8_t *operands,
size_t operand_count, void *user_data);
diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index 6fed825..cdad6ac 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
@@ -115,7 +115,7 @@ void avrcp_shutdown(struct avrcp *session)
g_free(session);
}
-static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
+static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
uint8_t *code, uint8_t *subunit,
uint8_t *operands, size_t operand_count,
void *user_data)
diff --git a/unit/test-avctp.c b/unit/test-avctp.c
index be1dfd7..0759731 100644
--- a/unit/test-avctp.c
+++ b/unit/test-avctp.c
@@ -227,7 +227,7 @@ static void execute_context(struct context *context)
destroy_context(context);
}
-static size_t handler(struct avctp *session,
+static ssize_t handler(struct avctp *session,
uint8_t transaction, uint8_t *code,
uint8_t *subunit, uint8_t *operands,
size_t operand_count, void *user_data)
--
1.8.5.3