summaryrefslogtreecommitdiff
path: root/profiles/input
diff options
context:
space:
mode:
authorArchie Pusaka <apusaka@chromium.org>2020-08-03 14:52:23 +0800
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2020-08-03 12:58:47 -0700
commit41400986618e1534880ed0e4f1ede858e1519fe6 (patch)
treeb852442c9836c3ba556e2f5c1e83147056e931c1 /profiles/input
parent646db71713b05d50b2b0c421da34e80ed4c54fbd (diff)
downloadbluez-41400986618e1534880ed0e4f1ede858e1519fe6.tar.gz
input/device: Implement handle for UHID_SET_REPORT
This patch listens to UHID_SET_REPORT event and forwards this message to the hid device. Upon reply, we also send a report back to the kernel as UHID_SET_REPORT_REPLY. hidp_send_set_report no longer listen UHID_OUTPUT events, that is handled by hidp_send_output instead.
Diffstat (limited to 'profiles/input')
-rw-r--r--profiles/input/device.c100
1 files changed, 76 insertions, 24 deletions
diff --git a/profiles/input/device.c b/profiles/input/device.c
index ac4487f63..757290080 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -258,6 +258,29 @@ static bool uhid_send_get_report_reply(struct input_device *idev,
return true;
}
+static bool uhid_send_set_report_reply(struct input_device *idev,
+ uint32_t id, uint16_t err)
+{
+ struct uhid_event ev;
+ int ret;
+
+ if (!idev->uhid_created)
+ return false;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_SET_REPORT_REPLY;
+ ev.u.set_report_reply.id = id;
+ ev.u.set_report_reply.err = err;
+
+ ret = bt_uhid_send(idev->uhid, &ev);
+ if (ret < 0) {
+ error("bt_uhid_send: %s (%d)", strerror(-ret), -ret);
+ return false;
+ }
+
+ return true;
+}
+
static bool uhid_send_input_report(struct input_device *idev,
const uint8_t *data, size_t size)
{
@@ -404,6 +427,8 @@ static void hidp_recv_ctrl_handshake(struct input_device *idev, uint8_t param)
pending_req_complete = true;
} else if (pending_req_type == HIDP_TRANS_SET_REPORT) {
DBG("SET_REPORT failed (%u)", param);
+ uhid_send_set_report_reply(idev, idev->report_rsp_id,
+ EIO);
pending_req_complete = true;
} else
DBG("Spurious HIDP_HSHK_ERR");
@@ -446,7 +471,8 @@ static void hidp_recv_ctrl_data(struct input_device *idev, uint8_t param,
DBG("");
pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK;
- if (pending_req_type != HIDP_TRANS_GET_REPORT) {
+ if (pending_req_type != HIDP_TRANS_GET_REPORT &&
+ pending_req_type != HIDP_TRANS_SET_REPORT) {
DBG("Spurious DATA on control channel");
return;
}
@@ -461,8 +487,12 @@ static void hidp_recv_ctrl_data(struct input_device *idev, uint8_t param,
case HIDP_DATA_RTYPE_FEATURE:
case HIDP_DATA_RTYPE_INPUT:
case HIDP_DATA_RTYPE_OUTPUT:
- uhid_send_get_report_reply(idev, data + 1, size - 1,
+ if (pending_req_type == HIDP_TRANS_GET_REPORT)
+ uhid_send_get_report_reply(idev, data + 1, size - 1,
idev->report_rsp_id, 0);
+ else
+ uhid_send_set_report_reply(idev, idev->report_rsp_id,
+ 0);
break;
case HIDP_DATA_RTYPE_OTHER:
@@ -579,9 +609,13 @@ static gboolean hidp_report_req_timeout(gpointer data)
switch (pending_req_type) {
case HIDP_TRANS_GET_REPORT:
req_type_str = "GET_REPORT";
+ uhid_send_get_report_reply(idev, NULL, 0, idev->report_rsp_id,
+ ETIMEDOUT);
break;
case HIDP_TRANS_SET_REPORT:
req_type_str = "SET_REPORT";
+ uhid_send_set_report_reply(idev, idev->report_rsp_id,
+ ETIMEDOUT);
break;
default:
/* Should never happen */
@@ -598,6 +632,17 @@ static gboolean hidp_report_req_timeout(gpointer data)
return FALSE;
}
+static void hidp_send_output(struct uhid_event *ev, void *user_data)
+{
+ struct input_device *idev = user_data;
+ uint8_t hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUTPUT;
+
+ DBG("");
+
+ hidp_send_intr_message(idev, hdr, ev->u.output.data,
+ ev->u.output.size);
+}
+
static void hidp_send_set_report(struct uhid_event *ev, void *user_data)
{
struct input_device *idev = user_data;
@@ -606,34 +651,37 @@ static void hidp_send_set_report(struct uhid_event *ev, void *user_data)
DBG("");
- switch (ev->u.output.rtype) {
+ switch (ev->u.set_report.rtype) {
case UHID_FEATURE_REPORT:
- /* Send SET_REPORT on control channel */
- if (idev->report_req_pending) {
- DBG("Old GET_REPORT or SET_REPORT still pending");
- return;
- }
-
hdr = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
- sent = hidp_send_ctrl_message(idev, hdr, ev->u.output.data,
- ev->u.output.size);
- if (sent) {
- idev->report_req_pending = hdr;
- idev->report_req_timer =
- g_timeout_add_seconds(REPORT_REQ_TIMEOUT,
- hidp_report_req_timeout, idev);
- }
+ break;
+ case UHID_INPUT_REPORT:
+ hdr = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_INPUT;
break;
case UHID_OUTPUT_REPORT:
- /* Send DATA on interrupt channel */
- hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUTPUT;
- hidp_send_intr_message(idev, hdr, ev->u.output.data,
- ev->u.output.size);
+ hdr = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUTPUT;
break;
default:
- DBG("Unsupported HID report type %u", ev->u.output.rtype);
+ DBG("Unsupported HID report type %u", ev->u.set_report.rtype);
+ return;
+ }
+
+ if (idev->report_req_pending) {
+ DBG("Old GET_REPORT or SET_REPORT still pending");
+ uhid_send_set_report_reply(idev, ev->u.set_report.id, EBUSY);
return;
}
+
+ sent = hidp_send_ctrl_message(idev, hdr, ev->u.set_report.data,
+ ev->u.set_report.size);
+ if (sent) {
+ idev->report_req_pending = hdr;
+ idev->report_req_timer =
+ g_timeout_add_seconds(REPORT_REQ_TIMEOUT,
+ hidp_report_req_timeout, idev);
+ idev->report_rsp_id = ev->u.set_report.id;
+ } else
+ uhid_send_set_report_reply(idev, ev->u.set_report.id, EIO);
}
static void hidp_send_get_report(struct uhid_event *ev, void *user_data)
@@ -675,7 +723,9 @@ static void hidp_send_get_report(struct uhid_event *ev, void *user_data)
g_timeout_add_seconds(REPORT_REQ_TIMEOUT,
hidp_report_req_timeout, idev);
idev->report_rsp_id = ev->u.get_report.id;
- }
+ } else
+ uhid_send_get_report_reply(idev, NULL, 0, ev->u.get_report.id,
+ EIO);
}
static void epox_endian_quirk(unsigned char *data, int size)
@@ -908,9 +958,11 @@ static int uhid_connadd(struct input_device *idev, struct hidp_connadd_req *req)
return err;
}
- bt_uhid_register(idev->uhid, UHID_OUTPUT, hidp_send_set_report, idev);
+ bt_uhid_register(idev->uhid, UHID_OUTPUT, hidp_send_output, idev);
bt_uhid_register(idev->uhid, UHID_GET_REPORT, hidp_send_get_report,
idev);
+ bt_uhid_register(idev->uhid, UHID_SET_REPORT, hidp_send_set_report,
+ idev);
idev->uhid_created = true;