aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2024-04-08 17:24:59 -0400
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2024-04-10 09:56:37 -0400
commit991ec8e2e088dbfeb954b6fe003e4188d516ba5a (patch)
treed835e675835d303d6e5fa0531eba1c378e132ceb
parent7604a577c9d7bf15c9144b8154842ce277afec90 (diff)
shared/uhid: Add support for bt_uhid_replay
This adds support for bt_uhid_replay which enablind replaying GET/SET_REPORT messages stored during the first time a device is created.
-rw-r--r--src/shared/uhid.c123
-rw-r--r--src/shared/uhid.h1
2 files changed, 124 insertions, 0 deletions
diff --git a/src/shared/uhid.c b/src/shared/uhid.c
index 46edb3bfa3..690d58d7bd 100644
--- a/src/shared/uhid.c
+++ b/src/shared/uhid.c
@@ -30,6 +30,14 @@
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#endif
+struct uhid_replay {
+ bool active;
+ struct queue *out;
+ struct queue *in;
+ struct queue *rout;
+ struct queue *rin;
+};
+
struct bt_uhid {
int ref_count;
struct io *io;
@@ -38,6 +46,7 @@ struct bt_uhid {
struct queue *input;
bool created;
bool started;
+ struct uhid_replay *replay;
};
struct uhid_notify {
@@ -47,6 +56,18 @@ struct uhid_notify {
void *user_data;
};
+static void uhid_replay_free(struct uhid_replay *replay)
+{
+ if (!replay)
+ return;
+
+ queue_destroy(replay->rin, NULL);
+ queue_destroy(replay->in, free);
+ queue_destroy(replay->rout, NULL);
+ queue_destroy(replay->out, free);
+ free(replay);
+}
+
static void uhid_free(struct bt_uhid *uhid)
{
if (uhid->io)
@@ -58,6 +79,8 @@ static void uhid_free(struct bt_uhid *uhid)
if (uhid->input)
queue_destroy(uhid->input, free);
+ uhid_replay_free(uhid->replay);
+
free(uhid);
}
@@ -73,6 +96,42 @@ static void notify_handler(void *data, void *user_data)
notify->func(ev, notify->user_data);
}
+static struct uhid_replay *uhid_replay_new(void)
+{
+ struct uhid_replay *replay = new0(struct uhid_replay, 1);
+
+ replay->out = queue_new();
+ replay->in = queue_new();
+
+ return replay;
+}
+
+static int bt_uhid_record(struct bt_uhid *uhid, bool input,
+ struct uhid_event *ev)
+{
+ if (!uhid)
+ return -EINVAL;
+
+ /* Capture input events in replay mode and send the next replay event */
+ if (uhid->replay && uhid->replay->active && input) {
+ queue_pop_head(uhid->replay->rin);
+ bt_uhid_replay(uhid);
+ return -EALREADY;
+ }
+
+ if (!uhid->replay)
+ uhid->replay = uhid_replay_new();
+
+ if (input)
+ queue_push_tail(uhid->replay->in,
+ util_memdup(ev, sizeof(*ev)));
+ else
+ queue_push_tail(uhid->replay->out,
+ util_memdup(ev, sizeof(*ev)));
+
+ return 0;
+}
+
static bool uhid_read_handler(struct io *io, void *user_data)
{
struct bt_uhid *uhid = user_data;
@@ -93,6 +152,13 @@ static bool uhid_read_handler(struct io *io, void *user_data)
if ((size_t) len < sizeof(ev.type))
return false;
+ switch (ev.type) {
+ case UHID_GET_REPORT:
+ case UHID_SET_REPORT:
+ bt_uhid_record(uhid, false, &ev);
+ break;
+ }
+
queue_foreach(uhid->notify_list, notify_handler, &ev);
return true;
@@ -382,6 +448,9 @@ int bt_uhid_set_report_reply(struct bt_uhid *uhid, uint8_t id, uint8_t status)
rsp->id = id;
rsp->err = status;
+ if (bt_uhid_record(uhid, true, &ev) == -EALREADY)
+ return 0;
+
return bt_uhid_send(uhid, &ev);
}
@@ -412,6 +481,9 @@ int bt_uhid_get_report_reply(struct bt_uhid *uhid, uint8_t id, uint8_t number,
memcpy(&rsp->data[len], data, rsp->size - len);
done:
+ if (bt_uhid_record(uhid, true, &ev) == -EALREADY)
+ return 0;
+
return bt_uhid_send(uhid, &ev);
}
@@ -437,3 +509,54 @@ int bt_uhid_destroy(struct bt_uhid *uhid)
return err;
}
+
+static void queue_append(void *data, void *user_data)
+{
+ queue_push_tail(user_data, data);
+}
+
+static struct queue *queue_dup(struct queue *q)
+{
+ struct queue *dup;
+
+ if (!q || queue_isempty(q))
+ return NULL;
+
+ dup = queue_new();
+
+ queue_foreach(q, queue_append, dup);
+
+ return dup;
+}
+
+int bt_uhid_replay(struct bt_uhid *uhid)
+{
+ struct uhid_event *ev;
+
+ if (!uhid || !uhid->started)
+ return -EINVAL;
+
+ if (!uhid->replay)
+ return 0;
+
+ if (uhid->replay->active)
+ goto resend;
+
+ uhid->replay->active = true;
+ queue_destroy(uhid->replay->rin, NULL);
+ uhid->replay->rin = queue_dup(uhid->replay->in);
+
+ queue_destroy(uhid->replay->rout, NULL);
+ uhid->replay->rout = queue_dup(uhid->replay->out);
+
+resend:
+ ev = queue_pop_head(uhid->replay->rout);
+ if (!ev) {
+ uhid->replay->active = false;
+ return 0;
+ }
+
+ queue_foreach(uhid->notify_list, notify_handler, ev);
+
+ return 0;
+}
diff --git a/src/shared/uhid.h b/src/shared/uhid.h
index d705338827..4e288cb192 100644
--- a/src/shared/uhid.h
+++ b/src/shared/uhid.h
@@ -42,3 +42,4 @@ int bt_uhid_set_report_reply(struct bt_uhid *uhid, uint8_t id, uint8_t status);
int bt_uhid_get_report_reply(struct bt_uhid *uhid, uint8_t id, uint8_t number,
uint8_t status, const void *data, size_t size);
int bt_uhid_destroy(struct bt_uhid *uhid);
+int bt_uhid_replay(struct bt_uhid *uhid);