2013-05-10 11:02:00

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 1/8] AVCTP: Add support to register pass-through handler

From: Luiz Augusto von Dentz <[email protected]>

Pass-through handler can be used to capture AV/C Panel commands before
they are sent to uinput.
---
profiles/audio/avctp.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++
profiles/audio/avctp.h | 8 ++++++
2 files changed, 80 insertions(+)

diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index 21aeb6f..705bf71 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -192,10 +192,18 @@ struct avctp {
struct avctp_channel *control;
struct avctp_channel *browsing;

+ struct avctp_passthrough_handler *handler;
+
uint8_t key_quirks[256];
struct key_pressed *key;
};

+struct avctp_passthrough_handler {
+ avctp_passthrough_cb cb;
+ void *user_data;
+ unsigned int id;
+};
+
struct avctp_pdu_handler {
uint8_t opcode;
avctp_control_pdu_cb cb;
@@ -280,6 +288,8 @@ static size_t handle_panel_passthrough(struct avctp *session,
uint8_t *subunit, uint8_t *operands,
size_t operand_count, void *user_data)
{
+ struct key_pressed *key = session->key;
+ struct avctp_passthrough_handler *handler = session->handler;
const char *status;
int pressed, i;

@@ -299,6 +309,12 @@ static size_t handle_panel_passthrough(struct avctp *session,
pressed = 1;
}

+ if (key == NULL && handler != NULL) {
+ if (handler->cb(session, operands[0] & 0x7F,
+ pressed, handler->user_data))
+ goto done;
+ }
+
for (i = 0; key_map[i].name != NULL; i++) {
uint8_t key_quirks;

@@ -321,6 +337,16 @@ static size_t handle_panel_passthrough(struct avctp *session,
break;
}

+ if (pressed) {
+ if (key == NULL)
+ key = g_new0(struct key_pressed, 1);
+ key->op = key_map[i].avc;
+ session->key = key;
+ } else if (key && key->op == key_map[i].avc) {
+ g_free(key);
+ session->key = NULL;
+ }
+
send_key(session->uinput, key_map[i].uinput, pressed);
break;
}
@@ -1692,6 +1718,52 @@ gboolean avctp_remove_state_cb(unsigned int id)
return FALSE;
}

+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+ avctp_passthrough_cb cb,
+ void *user_data)
+{
+ struct avctp_channel *control = session->control;
+ struct avctp_passthrough_handler *handler;
+ static unsigned int id = 0;
+
+ if (control == NULL || session->handler != NULL)
+ return 0;
+
+ handler = g_new(struct avctp_passthrough_handler, 1);
+ handler->cb = cb;
+ handler->user_data = user_data;
+ handler->id = ++id;
+
+ session->handler = handler;
+
+ return handler->id;
+}
+
+bool avctp_unregister_passthrough_handler(unsigned int id)
+{
+ GSList *l;
+
+ for (l = servers; l; l = l->next) {
+ struct avctp_server *server = l->data;
+ GSList *s;
+
+ for (s = server->sessions; s; s = s->next) {
+ struct avctp *session = s->data;
+
+ if (session->handler == NULL)
+ continue;
+
+ if (session->handler->id == id) {
+ g_free(session->handler);
+ session->handler = NULL;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
avctp_control_pdu_cb cb,
void *user_data)
diff --git a/profiles/audio/avctp.h b/profiles/audio/avctp.h
index 648e982..0a414af 100644
--- a/profiles/audio/avctp.h
+++ b/profiles/audio/avctp.h
@@ -94,6 +94,9 @@ typedef void (*avctp_state_cb) (struct audio_device *dev,
avctp_state_t old_state,
avctp_state_t new_state);

+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,
uint8_t transaction, uint8_t *code,
uint8_t *subunit, uint8_t *operands,
@@ -120,6 +123,11 @@ struct avctp *avctp_get(struct audio_device *device);
int avctp_connect_browsing(struct avctp *session);
void avctp_disconnect(struct avctp *session);

+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+ avctp_passthrough_cb cb,
+ void *user_data);
+bool avctp_unregister_passthrough_handler(unsigned int id);
+
unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
avctp_control_pdu_cb cb,
void *user_data);
--
1.8.1.4



2013-05-14 03:07:40

by Lucas De Marchi

[permalink] [raw]
Subject: Re: [PATCH BlueZ 7/8] media: Ignore signess for D-Bus int types

On Fri, May 10, 2013 at 8:02 AM, Luiz Augusto von Dentz
<[email protected]> wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> Some players such as Spotify implements some properties that are
> defined in MPRIS as int64 as uint64 which makes it fail to register.

If it's defined in spec as int64, wouldn't it be the case of fixing
Spotify instead?


Lucas De Marchi

2013-05-10 11:27:11

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH BlueZ 1/8] AVCTP: Add support to register pass-through handler

Hi Luiz,

On Fri, May 10, 2013, Luiz Augusto von Dentz wrote:
> Pass-through handler can be used to capture AV/C Panel commands before
> they are sent to uinput.
> ---
> profiles/audio/avctp.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++
> profiles/audio/avctp.h | 8 ++++++
> 2 files changed, 80 insertions(+)

All patches in this set have been applied. Thanks.

Johan

2013-05-10 11:02:07

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 8/8] media: Check MPRIS Can* properties before calling the respective method

From: Luiz Augusto von Dentz <[email protected]>

With MPRIS the player can disable support for certain control using
properties such as CanPlay, CanPause..., in this case these methods
should not be called and the code should fallback to uinput method.
---
profiles/audio/media.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)

diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index ce674d0..eb5ea81 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -102,6 +102,11 @@ struct media_player {
uint32_t duration;
uint8_t volume;
GTimer *timer;
+ bool play;
+ bool pause;
+ bool next;
+ bool previous;
+ bool control;
};

static GSList *adapters = NULL;
@@ -1151,6 +1156,9 @@ static bool play(void *user_data)

DBG("");

+ if (!mp->play || !mp->control)
+ return false;
+
return media_player_send(mp, "Play");
}

@@ -1160,6 +1168,9 @@ static bool stop(void *user_data)

DBG("");

+ if (!mp->control)
+ return false;
+
return media_player_send(mp, "Stop");
}

@@ -1169,6 +1180,9 @@ static bool pause(void *user_data)

DBG("");

+ if (!mp->pause || !mp->control)
+ return false;
+
return media_player_send(mp, "Pause");
}

@@ -1178,6 +1192,9 @@ static bool next(void *user_data)

DBG("");

+ if (!mp->next || !mp->control)
+ return false;
+
return media_player_send(mp, "Next");
}

@@ -1187,6 +1204,9 @@ static bool previous(void *user_data)

DBG("");

+ if (!mp->previous || !mp->control)
+ return false;
+
return media_player_send(mp, "Previous");
}

@@ -1512,6 +1532,21 @@ static gboolean set_repeat(struct media_player *mp, DBusMessageIter *iter)
return set_property(mp, "Repeat", value);
}

+static gboolean set_flag(struct media_player *mp, DBusMessageIter *iter,
+ bool *var)
+{
+ dbus_bool_t value;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN)
+ return FALSE;
+
+ dbus_message_iter_get_basic(iter, &value);
+
+ *var = value;
+
+ return TRUE;
+}
+
static gboolean set_player_property(struct media_player *mp, const char *key,
DBusMessageIter *entry)
{
@@ -1537,6 +1572,21 @@ static gboolean set_player_property(struct media_player *mp, const char *key,
if (strcasecmp(key, "LoopStatus") == 0)
return set_repeat(mp, &var);

+ if (strcasecmp(key, "CanPlay") == 0)
+ return set_flag(mp, &var, &mp->play);
+
+ if (strcasecmp(key, "CanPause") == 0)
+ return set_flag(mp, &var, &mp->pause);
+
+ if (strcasecmp(key, "CanGoNext") == 0)
+ return set_flag(mp, &var, &mp->next);
+
+ if (strcasecmp(key, "CanGoPrevious") == 0)
+ return set_flag(mp, &var, &mp->previous);
+
+ if (strcasecmp(key, "CanControl") == 0)
+ return set_flag(mp, &var, &mp->control);
+
DBG("%s not supported, ignoring", key);

return TRUE;
--
1.8.1.4


2013-05-10 11:02:06

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 7/8] media: Ignore signess for D-Bus int types

From: Luiz Augusto von Dentz <[email protected]>

Some players such as Spotify implements some properties that are
defined in MPRIS as int64 as uint64 which makes it fail to register.
---
profiles/audio/media.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 33847e5..ce674d0 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -1322,8 +1322,12 @@ static gboolean parse_int64_metadata(struct media_player *mp, const char *key,
{
uint64_t value;
char valstr[20];
+ int type;

- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INT64)
+ type = dbus_message_iter_get_arg_type(iter);
+ if (type == DBUS_TYPE_UINT64)
+ warn("expected DBUS_TYPE_INT64 got DBUS_TYPE_UINT64");
+ else if (type != DBUS_TYPE_INT64)
return FALSE;

dbus_message_iter_get_basic(iter, &value);
@@ -1345,8 +1349,12 @@ static gboolean parse_int32_metadata(struct media_player *mp, const char *key,
{
uint32_t value;
char valstr[20];
+ int type;

- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INT32)
+ type = dbus_message_iter_get_arg_type(iter);
+ if (type == DBUS_TYPE_UINT32)
+ warn("expected DBUS_TYPE_INT32 got DBUS_TYPE_UINT32");
+ else if (type != DBUS_TYPE_INT32)
return FALSE;

dbus_message_iter_get_basic(iter, &value);
--
1.8.1.4


2013-05-10 11:02:05

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 6/8] media: Fix not handling "Playlist" as possible value to LoopStatus

From: Luiz Augusto von Dentz <[email protected]>

---
profiles/audio/media.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 327b58e..33847e5 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -1482,7 +1482,7 @@ static const char *loop_status_to_repeat(const char *value)
return "off";
else if (strcasecmp(value, "Track") == 0)
return "singletrack";
- else if (strcasecmp(value, "Track") == 0)
+ else if (strcasecmp(value, "Playlist") == 0)
return "alltracks";

return NULL;
--
1.8.1.4


2013-05-10 11:02:04

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 5/8] media: Implement new callbacks for pass-through operations

From: Luiz Augusto von Dentz <[email protected]>

This implements newly introduced callbacks for pass-through operation so
they are directly sent to the active player.
---
profiles/audio/media.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 67 insertions(+), 1 deletion(-)

diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index cab79b5..327b58e 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -1129,6 +1129,67 @@ static void set_volume(uint8_t volume, struct audio_device *dev, void *user_data
}
}

+static bool media_player_send(struct media_player *mp, const char *name)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(mp->sender, mp->path,
+ MEDIA_PLAYER_INTERFACE, name);
+ if (msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return false;
+ }
+
+ g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+ return true;
+}
+
+static bool play(void *user_data)
+{
+ struct media_player *mp = user_data;
+
+ DBG("");
+
+ return media_player_send(mp, "Play");
+}
+
+static bool stop(void *user_data)
+{
+ struct media_player *mp = user_data;
+
+ DBG("");
+
+ return media_player_send(mp, "Stop");
+}
+
+static bool pause(void *user_data)
+{
+ struct media_player *mp = user_data;
+
+ DBG("");
+
+ return media_player_send(mp, "Pause");
+}
+
+static bool next(void *user_data)
+{
+ struct media_player *mp = user_data;
+
+ DBG("");
+
+ return media_player_send(mp, "Next");
+}
+
+static bool previous(void *user_data)
+{
+ struct media_player *mp = user_data;
+
+ DBG("");
+
+ return media_player_send(mp, "Previous");
+}
+
static struct avrcp_player_cb player_cb = {
.list_settings = list_settings,
.get_setting = get_setting,
@@ -1139,7 +1200,12 @@ static struct avrcp_player_cb player_cb = {
.get_position = get_position,
.get_duration = get_duration,
.get_status = get_status,
- .set_volume = set_volume
+ .set_volume = set_volume,
+ .play = play,
+ .stop = stop,
+ .pause = pause,
+ .next = next,
+ .previous = previous,
};

static void media_player_exit(DBusConnection *connection, void *user_data)
--
1.8.1.4


2013-05-10 11:02:03

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 4/8] AVRCP: Register pass-through handler for CT

From: Luiz Augusto von Dentz <[email protected]>

Register pass-through handler for CT role if version >= 1.3 and creates
callbacks to direct this commands to the active player.
---
profiles/audio/avrcp.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++
profiles/audio/avrcp.h | 5 +++
2 files changed, 101 insertions(+)

diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index b7de051..4acf396 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -203,8 +203,10 @@ struct avrcp {
void (*init_browsing) (struct avrcp *session);
void (*destroy) (struct avrcp *sesion);

+ const struct passthrough_handler *passthrough_handlers;
const struct control_pdu_handler *control_handlers;

+ unsigned int passthrough_id;
unsigned int control_id;
unsigned int browsing_id;
uint16_t supported_events;
@@ -214,6 +216,11 @@ struct avrcp {
struct pending_pdu *pending_pdu;
};

+struct passthrough_handler {
+ uint8_t op;
+ bool (*func) (struct avrcp *session);
+};
+
struct control_pdu_handler {
uint8_t pdu_id;
uint8_t code;
@@ -1252,6 +1259,87 @@ static GList *player_list_settings(struct avrcp_player *player)
return player->cb->list_settings(player->user_data);
}

+static bool avrcp_handle_play(struct avrcp *session)
+{
+ struct avrcp_player *player = session->player;
+
+ if (session->player == NULL)
+ return false;
+
+ return player->cb->play(player->user_data);
+}
+
+static bool avrcp_handle_stop(struct avrcp *session)
+{
+ struct avrcp_player *player = session->player;
+
+ if (session->player == NULL)
+ return false;
+
+ return player->cb->stop(player->user_data);
+}
+
+static bool avrcp_handle_pause(struct avrcp *session)
+{
+ struct avrcp_player *player = session->player;
+
+ if (session->player == NULL)
+ return false;
+
+ return player->cb->pause(player->user_data);
+}
+
+static bool avrcp_handle_next(struct avrcp *session)
+{
+ struct avrcp_player *player = session->player;
+
+ if (session->player == NULL)
+ return false;
+
+ return player->cb->next(player->user_data);
+}
+
+static bool avrcp_handle_previous(struct avrcp *session)
+{
+ struct avrcp_player *player = session->player;
+
+ if (session->player == NULL)
+ return false;
+
+ return player->cb->previous(player->user_data);
+}
+
+static const struct passthrough_handler tg_passthrough_handlers[] = {
+ { AVC_PLAY, avrcp_handle_play },
+ { AVC_STOP, avrcp_handle_stop },
+ { AVC_PAUSE, avrcp_handle_pause },
+ { AVC_FORWARD, avrcp_handle_next },
+ { AVC_BACKWARD, avrcp_handle_previous },
+ { },
+};
+
+static bool handle_passthrough(struct avctp *conn, uint8_t op, bool pressed,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ const struct passthrough_handler *handler;
+
+ for (handler = session->passthrough_handlers; handler->func;
+ handler++) {
+ if (handler->op == op)
+ break;
+ }
+
+ if (handler->func == NULL)
+ return false;
+
+ /* Do not trigger handler on release */
+ if (!pressed)
+ return true;
+
+ return handler->func(session);
+}
+
static uint8_t avrcp_handle_register_notification(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -2653,6 +2741,11 @@ static void session_tg_init_control(struct avrcp *session)
player->sessions = g_slist_prepend(player->sessions, session);
}

+ session->passthrough_id = avctp_register_passthrough_handler(
+ session->conn,
+ handle_passthrough,
+ session);
+ session->passthrough_handlers = tg_passthrough_handlers;
session->control_id = avctp_register_pdu_handler(session->conn,
AVC_OP_VENDORDEP,
handle_vendordep_pdu,
@@ -2712,6 +2805,9 @@ static void session_destroy(struct avrcp *session)

server->sessions = g_slist_remove(server->sessions, session);

+ if (session->passthrough_id > 0)
+ avctp_unregister_passthrough_handler(session->passthrough_id);
+
if (session->control_id > 0)
avctp_unregister_pdu_handler(session->control_id);

diff --git a/profiles/audio/avrcp.h b/profiles/audio/avrcp.h
index 7b6fe92..3b1963f 100644
--- a/profiles/audio/avrcp.h
+++ b/profiles/audio/avrcp.h
@@ -93,6 +93,11 @@ struct avrcp_player_cb {
uint32_t (*get_duration) (void *user_data);
void (*set_volume) (uint8_t volume, struct audio_device *dev,
void *user_data);
+ bool (*play) (void *user_data);
+ bool (*stop) (void *user_data);
+ bool (*pause) (void *user_data);
+ bool (*next) (void *user_data);
+ bool (*previous) (void *user_data);
};

int avrcp_target_register(struct btd_adapter *adapter, GKeyFile *config);
--
1.8.1.4


2013-05-10 11:02:02

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 3/8] AVCTP: Add timeout for pass-through commands with value pressed

From: Luiz Augusto von Dentz <[email protected]>

>From AV/C spec 1.23, page 76:

"A command with the pressed value is valid for two seconds from the time
when a target sends back a response of the command. The controller shall
continue sending pressed value with identical operation id value in the
operation_id field while the command is wished to stay valid. Either if
the target has not received the pressed command within two seconds or the
target receives the pressed command with another operation id, then the
target regards that the released command was sent but missed to receive."
---
profiles/audio/avctp.c | 39 ++++++++++++++++++++++++++++++++++++---
1 file changed, 36 insertions(+), 3 deletions(-)

diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index 4224a9c..6372709 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -58,6 +58,11 @@
#include "avctp.h"
#include "avrcp.h"

+/* AV/C Panel 1.23, page 76:
+ * command with the pressed value is valid for two seconds
+ */
+#define AVC_PRESS_TIMEOUT 2
+
#define QUIRK_NO_RELEASE 1 << 0

/* Message types */
@@ -283,6 +288,19 @@ static void send_key(int fd, uint16_t key, int pressed)
send_event(fd, EV_SYN, SYN_REPORT, 0);
}

+static gboolean auto_release(gpointer user_data)
+{
+ struct avctp *session = user_data;
+
+ session->key.timer = 0;
+
+ DBG("AV/C: key press timeout");
+
+ send_key(session->uinput, session->key.op, 0);
+
+ return FALSE;
+}
+
static size_t handle_panel_passthrough(struct avctp *session,
uint8_t transaction, uint8_t *code,
uint8_t *subunit, uint8_t *operands,
@@ -336,8 +354,21 @@ static size_t handle_panel_passthrough(struct avctp *session,
break;
}

- if (pressed)
- session->key.op = key_map[i].avc;
+ if (pressed) {
+ if (session->key.timer > 0) {
+ g_source_remove(session->key.timer);
+ send_key(session->uinput, session->key.op, 0);
+ }
+
+ session->key.op = key_map[i].uinput;
+ session->key.timer = g_timeout_add_seconds(
+ AVC_PRESS_TIMEOUT,
+ auto_release,
+ session);
+ } else if (session->key.timer > 0) {
+ g_source_remove(session->key.timer);
+ session->key.timer = 0;
+ }

send_key(session->uinput, key_map[i].uinput, pressed);
break;
@@ -1615,7 +1646,9 @@ static bool set_pressed(struct avctp *session, uint8_t op)
return FALSE;

session->key.op = op;
- session->key.timer = g_timeout_add_seconds(2, repeat_timeout, session);
+ session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT,
+ repeat_timeout,
+ session);

return TRUE;
}
--
1.8.1.4


2013-05-10 11:02:01

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ 2/8] AVCTP: Don't allocate memory for every key pressed

From: Luiz Augusto von Dentz <[email protected]>

The timer field can be used to detect if the key is currectly active
instead of relying on a pointer that needs to be allocated for every
single key press.
---
profiles/audio/avctp.c | 54 ++++++++++++++++----------------------------------
1 file changed, 17 insertions(+), 37 deletions(-)

diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index 705bf71..4224a9c 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -195,7 +195,7 @@ struct avctp {
struct avctp_passthrough_handler *handler;

uint8_t key_quirks[256];
- struct key_pressed *key;
+ struct key_pressed key;
};

struct avctp_passthrough_handler {
@@ -288,7 +288,6 @@ static size_t handle_panel_passthrough(struct avctp *session,
uint8_t *subunit, uint8_t *operands,
size_t operand_count, void *user_data)
{
- struct key_pressed *key = session->key;
struct avctp_passthrough_handler *handler = session->handler;
const char *status;
int pressed, i;
@@ -309,7 +308,7 @@ static size_t handle_panel_passthrough(struct avctp *session,
pressed = 1;
}

- if (key == NULL && handler != NULL) {
+ if (session->key.timer == 0 && handler != NULL) {
if (handler->cb(session, operands[0] & 0x7F,
pressed, handler->user_data))
goto done;
@@ -337,15 +336,8 @@ static size_t handle_panel_passthrough(struct avctp *session,
break;
}

- if (pressed) {
- if (key == NULL)
- key = g_new0(struct key_pressed, 1);
- key->op = key_map[i].avc;
- session->key = key;
- } else if (key && key->op == key_map[i].avc) {
- g_free(key);
- session->key = NULL;
- }
+ if (pressed)
+ session->key.op = key_map[i].avc;

send_key(session->uinput, key_map[i].uinput, pressed);
break;
@@ -482,11 +474,8 @@ static void avctp_disconnected(struct avctp *session)
session->auth_id = 0;
}

- if (session->key != NULL) {
- if (session->key->timer > 0)
- g_source_remove(session->key->timer);
- g_free(session->key);
- }
+ if (session->key.timer > 0)
+ g_source_remove(session->key.timer);

if (session->uinput >= 0) {
char address[18];
@@ -1597,33 +1586,27 @@ static int avctp_passthrough_release(struct avctp *session, uint8_t op)
static gboolean repeat_timeout(gpointer user_data)
{
struct avctp *session = user_data;
- struct key_pressed *key = session->key;

- avctp_passthrough_release(session, key->op);
- avctp_passthrough_press(session, key->op);
+ avctp_passthrough_release(session, session->key.op);
+ avctp_passthrough_press(session, session->key.op);

return TRUE;
}

static void release_pressed(struct avctp *session)
{
- struct key_pressed *key = session->key;
-
- avctp_passthrough_release(session, key->op);
+ avctp_passthrough_release(session, session->key.op);

- if (key->timer > 0)
- g_source_remove(key->timer);
+ if (session->key.timer > 0)
+ g_source_remove(session->key.timer);

- g_free(key);
- session->key = NULL;
+ session->key.timer = 0;
}

static bool set_pressed(struct avctp *session, uint8_t op)
{
- struct key_pressed *key;
-
- if (session->key != NULL) {
- if (session->key->op == op)
+ if (session->key.timer > 0) {
+ if (session->key.op == op)
return TRUE;
release_pressed(session);
}
@@ -1631,11 +1614,8 @@ static bool set_pressed(struct avctp *session, uint8_t op)
if (op != AVC_FAST_FORWARD && op != AVC_REWIND)
return FALSE;

- key = g_new0(struct key_pressed, 1);
- key->op = op;
- key->timer = g_timeout_add_seconds(2, repeat_timeout, session);
-
- session->key = key;
+ session->key.op = op;
+ session->key.timer = g_timeout_add_seconds(2, repeat_timeout, session);

return TRUE;
}
@@ -1658,7 +1638,7 @@ static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
int avctp_send_passthrough(struct avctp *session, uint8_t op)
{
/* Auto release if key pressed */
- if (session->key != NULL)
+ if (session->key.timer > 0)
release_pressed(session);

return avctp_passthrough_press(session, op);
--
1.8.1.4