Skip to content

Commit

Permalink
shared/uhid: Add dedicated functions for each UHID opcode
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Vudentz committed Mar 11, 2024
1 parent c9dddb6 commit 92ed637
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 4 deletions.
212 changes: 208 additions & 4 deletions src/shared/uhid.c
Expand Up @@ -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 {
Expand All @@ -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);
}

Expand Down Expand Up @@ -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);

Expand All @@ -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;
}
13 changes: 13 additions & 0 deletions src/shared/uhid.h
Expand Up @@ -11,6 +11,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <linux/uhid.h>
#include <bluetooth/bluetooth.h>

struct bt_uhid;

Expand All @@ -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);

0 comments on commit 92ed637

Please sign in to comment.