aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2024-03-08 17:02:05 -0500
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2024-03-11 15:54:17 -0400
commit92ed637ab2bc44b812fd8c7bff5b5f41fcc48255 (patch)
treebaea588ebbe8ec20b4f416c970bec70457215853
parentc9dddb642663b8bafe8998fef6132a2bb36d609d (diff)
shared/uhid: Add dedicated functions for each UHID opcode
This adds bt_uhid_create which uses UHID_CREATE2 and tracks progress of when the device is ready to receive events and in the meantime queues them while waiting for UHID_START and other dedicated functions for each UHID opcode so users don't need to build each command manually.
-rw-r--r--src/shared/uhid.c212
-rw-r--r--src/shared/uhid.h13
2 files changed, 221 insertions, 4 deletions
diff --git a/src/shared/uhid.c b/src/shared/uhid.c
index 1f15443cd6..46edb3bfa3 100644
--- a/src/shared/uhid.c
+++ b/src/shared/uhid.c
@@ -26,11 +26,18 @@
#define UHID_DEVICE_FILE "/dev/uhid"
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
struct bt_uhid {
int ref_count;
struct io *io;
unsigned int notify_id;
struct queue *notify_list;
+ struct queue *input;
+ bool created;
+ bool started;
};
struct uhid_notify {
@@ -48,6 +55,9 @@ static void uhid_free(struct bt_uhid *uhid)
if (uhid->notify_list)
queue_destroy(uhid->notify_list, free);
+ if (uhid->input)
+ queue_destroy(uhid->input, free);
+
free(uhid);
}
@@ -215,14 +225,11 @@ bool bt_uhid_unregister_all(struct bt_uhid *uhid)
return true;
}
-int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev)
+static int uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev)
{
ssize_t len;
struct iovec iov;
- if (!uhid->io)
- return -ENOTCONN;
-
iov.iov_base = (void *) ev;
iov.iov_len = sizeof(*ev);
@@ -233,3 +240,200 @@ int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev)
/* uHID kernel driver does not handle partial writes */
return len != sizeof(*ev) ? -EIO : 0;
}
+
+int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev)
+{
+ if (!uhid || !ev)
+ return -EINVAL;
+
+ if (!uhid->io)
+ return -ENOTCONN;
+
+ return uhid_send(uhid, ev);
+}
+
+static bool input_dequeue(const void *data, const void *match_data)
+{
+ struct uhid_event *ev = (void *)data;
+ struct bt_uhid *uhid = (void *)match_data;
+
+ return bt_uhid_send(uhid, ev) == 0;
+}
+
+static void uhid_start(struct uhid_event *ev, void *user_data)
+{
+ struct bt_uhid *uhid = user_data;
+
+ uhid->started = true;
+
+ /* dequeue input events send while UHID_CREATE2 was in progress */
+ queue_remove_all(uhid->input, input_dequeue, uhid, free);
+}
+
+int bt_uhid_create(struct bt_uhid *uhid, const char *name, bdaddr_t *src,
+ bdaddr_t *dst, uint32_t vendor, uint32_t product,
+ uint32_t version, uint32_t country, void *rd_data,
+ size_t rd_size)
+{
+ struct uhid_event ev;
+ int err;
+
+ if (!uhid || !name || rd_size > sizeof(ev.u.create2.rd_data))
+ return -EINVAL;
+
+ if (uhid->created)
+ return 0;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_CREATE2;
+ strncpy((char *) ev.u.create2.name, name,
+ sizeof(ev.u.create2.name) - 1);
+ if (src)
+ sprintf((char *)ev.u.create2.phys,
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+ src->b[5], src->b[4], src->b[3], src->b[2], src->b[1],
+ src->b[0]);
+ if (dst)
+ sprintf((char *)ev.u.create2.uniq,
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+ dst->b[5], dst->b[4], dst->b[3], dst->b[2], dst->b[1],
+ dst->b[0]);
+ ev.u.create2.vendor = vendor;
+ ev.u.create2.product = product;
+ ev.u.create2.version = version;
+ ev.u.create2.country = country;
+ ev.u.create2.bus = BUS_BLUETOOTH;
+ if (rd_size)
+ memcpy(ev.u.create2.rd_data, rd_data, rd_size);
+ ev.u.create2.rd_size = rd_size;
+
+ err = bt_uhid_send(uhid, &ev);
+ if (err)
+ return err;
+
+ bt_uhid_register(uhid, UHID_START, uhid_start, uhid);
+
+ uhid->created = true;
+ uhid->started = false;
+
+ return 0;
+}
+
+bool bt_uhid_created(struct bt_uhid *uhid)
+{
+ if (!uhid)
+ return false;
+
+ return uhid->created;
+}
+
+bool bt_uhid_started(struct bt_uhid *uhid)
+{
+ if (!uhid)
+ return false;
+
+ return uhid->started;
+}
+
+int bt_uhid_input(struct bt_uhid *uhid, uint8_t number, const void *data,
+ size_t size)
+{
+ struct uhid_event ev;
+ struct uhid_input2_req *req = &ev.u.input2;
+ size_t len = 0;
+
+ if (!uhid)
+ return -EINVAL;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_INPUT2;
+
+ if (number) {
+ req->data[len++] = number;
+ req->size = 1 + MIN(size, sizeof(req->data) - 1);
+ } else
+ req->size = MIN(size, sizeof(req->data));
+
+ if (data && size)
+ memcpy(&req->data[len], data, req->size - len);
+
+ /* Queue events if UHID_START has not been received yet */
+ if (!uhid->started) {
+ if (!uhid->input)
+ uhid->input = queue_new();
+
+ queue_push_tail(uhid->input, util_memdup(&ev, sizeof(ev)));
+ return 0;
+ }
+
+ return bt_uhid_send(uhid, &ev);
+}
+
+int bt_uhid_set_report_reply(struct bt_uhid *uhid, uint8_t id, uint8_t status)
+{
+ struct uhid_event ev;
+ struct uhid_set_report_reply_req *rsp = &ev.u.set_report_reply;
+
+ if (!uhid)
+ return false;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_SET_REPORT_REPLY;
+ rsp->id = id;
+ rsp->err = status;
+
+ return bt_uhid_send(uhid, &ev);
+}
+
+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)
+{
+ struct uhid_event ev;
+ struct uhid_get_report_reply_req *rsp = &ev.u.get_report_reply;
+ size_t len = 0;
+
+ if (!uhid)
+ return false;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_GET_REPORT_REPLY;
+ rsp->id = id;
+ rsp->err = status;
+
+ if (!data || !size)
+ goto done;
+
+ if (number) {
+ rsp->data[len++] = number;
+ rsp->size += MIN(size, sizeof(rsp->data) - 1);
+ } else
+ rsp->size = MIN(size, sizeof(ev.u.input.data));
+
+ memcpy(&rsp->data[len], data, rsp->size - len);
+
+done:
+ return bt_uhid_send(uhid, &ev);
+}
+
+int bt_uhid_destroy(struct bt_uhid *uhid)
+{
+ struct uhid_event ev;
+ int err;
+
+ if (!uhid)
+ return -EINVAL;
+
+ if (!uhid->created)
+ return 0;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_DESTROY;
+
+ err = bt_uhid_send(uhid, &ev);
+ if (err < 0)
+ return err;
+
+ uhid->created = false;
+
+ return err;
+}
diff --git a/src/shared/uhid.h b/src/shared/uhid.h
index 55ae839f30..d705338827 100644
--- a/src/shared/uhid.h
+++ b/src/shared/uhid.h
@@ -11,6 +11,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <linux/uhid.h>
+#include <bluetooth/bluetooth.h>
struct bt_uhid;
@@ -29,3 +30,15 @@ bool bt_uhid_unregister(struct bt_uhid *uhid, unsigned int id);
bool bt_uhid_unregister_all(struct bt_uhid *uhid);
int bt_uhid_send(struct bt_uhid *uhid, const struct uhid_event *ev);
+int bt_uhid_create(struct bt_uhid *uhid, const char *name, bdaddr_t *src,
+ bdaddr_t *dst, uint32_t vendor, uint32_t product,
+ uint32_t version, uint32_t country, void *rd_data,
+ size_t rd_size);
+bool bt_uhid_created(struct bt_uhid *uhid);
+bool bt_uhid_started(struct bt_uhid *uhid);
+int bt_uhid_input(struct bt_uhid *uhid, uint8_t number, const void *data,
+ size_t size);
+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);