Add UHID GET_REPORT functionality
GET_REPORT wasn't supported previously. >=4.10 hid-sony driver
requires this feature to get the calibration data for DualShock4.
To be backwards compatible with kernel 3.10, UHID_FEATURE and
UHID_FEATURE_ANSWER are used instead of UHID_GET_REPORT and
UHID_GET_REPORT_REPLY. Also, UHID_SET_REPORT functionality cannot
be supported since it's not implemented in kernel 3.10.
Test: Load >=4.10 hid-sony driver, connect a DualShock4, and check
that the DualShock4 LED turns blue, which confirms both
get feature report and output data report working.
Bug: 38511270
Change-Id: Ic28760f423cc09754fd32a107a499be6677ca747
diff --git a/bta/hh/bta_hh_act.cc b/bta/hh/bta_hh_act.cc
index 4d85437..05cf171 100644
--- a/bta/hh/bta_hh_act.cc
+++ b/bta/hh/bta_hh_act.cc
@@ -632,7 +632,7 @@
*
* Function bta_hh_handsk_act
*
- * Description HID Host process a handshake acknoledgement.
+ * Description HID Host process a handshake acknowledgement.
*
*
* Returns void
@@ -659,7 +659,9 @@
bta_hh.hs_data.status = bta_hh_get_trans_status(p_data->hid_cback.data);
if (bta_hh.hs_data.status == BTA_HH_OK)
bta_hh.hs_data.status = BTA_HH_HS_TRANS_NOT_SPT;
-
+ if (p_cb->w4_evt == BTA_HH_GET_RPT_EVT)
+ bta_hh_co_get_rpt_rsp(bta_hh.dev_status.handle, bta_hh.hs_data.status,
+ NULL, 0);
(*bta_hh_cb.p_cback)(p_cb->w4_evt, &bta_hh);
p_cb->w4_evt = 0;
break;
@@ -671,6 +673,9 @@
bta_hh.dev_status.handle = p_cb->hid_handle;
bta_hh.dev_status.status =
bta_hh_get_trans_status(p_data->hid_cback.data);
+ if (p_cb->w4_evt == BTA_HH_SET_RPT_EVT)
+ bta_hh_co_set_rpt_rsp(bta_hh.dev_status.handle,
+ bta_hh.dev_status.status);
(*bta_hh_cb.p_cback)(p_cb->w4_evt, &bta_hh);
p_cb->w4_evt = 0;
break;
@@ -726,6 +731,8 @@
break;
case BTA_HH_GET_RPT_EVT:
hs_data.rsp_data.p_rpt_data = pdata;
+ bta_hh_co_get_rpt_rsp(hs_data.handle, hs_data.status, pdata->data,
+ pdata->len);
break;
case BTA_HH_GET_PROTO_EVT:
/* match up BTE/BTA report/boot mode def*/
diff --git a/bta/include/bta_hh_co.h b/bta/include/bta_hh_co.h
index da884bc..44e5a9f 100644
--- a/bta/include/bta_hh_co.h
+++ b/bta/include/bta_hh_co.h
@@ -77,6 +77,31 @@
******************************************************************************/
extern void bta_hh_co_close(uint8_t dev_handle, uint8_t app_id);
+/*******************************************************************************
+ *
+ * Function bta_hh_co_set_rpt_rsp
+ *
+ * Description This callout function is executed by HH when Set Report
+ * Response is received on Control Channel.
+ *
+ * Returns void.
+ *
+ ******************************************************************************/
+extern void bta_hh_co_set_rpt_rsp(uint8_t dev_handle, uint8_t status);
+
+/*******************************************************************************
+ *
+ * Function bta_hh_co_get_rpt_rsp
+ *
+ * Description This callout function is executed by HH when Get Report
+ * Response is received on Control Channel.
+ *
+ * Returns void.
+ *
+ ******************************************************************************/
+extern void bta_hh_co_get_rpt_rsp(uint8_t dev_handle, uint8_t status,
+ uint8_t* p_rpt, uint16_t len);
+
#if (BTA_HH_LE_INCLUDED == TRUE)
/*******************************************************************************
*
diff --git a/btif/co/bta_hh_co.cc b/btif/co/bta_hh_co.cc
index 9d3e119..267b8ba 100644
--- a/btif/co/bta_hh_co.cc
+++ b/btif/co/bta_hh_co.cc
@@ -42,6 +42,7 @@
#define BTA_HH_NV_LOAD_MAX 16
static tBTA_HH_RPT_CACHE_ENTRY sReportCache[BTA_HH_NV_LOAD_MAX];
#endif
+#define GET_RPT_RSP_OFFSET 9
void uhid_set_non_blocking(int fd) {
int opts = fcntl(fd, F_GETFL);
@@ -128,8 +129,8 @@
btif_hh_setreport(p_dev, BTHH_OUTPUT_REPORT, ev.u.output.size,
ev.u.output.data);
else
- btif_hh_setreport(p_dev, BTHH_INPUT_REPORT, ev.u.output.size,
- ev.u.output.data);
+ APPL_TRACE_ERROR("%s: UHID_OUTPUT: Invalid report type = %d", __func__,
+ ev.u.output.rtype);
break;
case UHID_OUTPUT_EV:
if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.output_ev))) {
@@ -141,10 +142,24 @@
APPL_TRACE_DEBUG("UHID_OUTPUT_EV from uhid-dev\n");
break;
case UHID_FEATURE:
- APPL_TRACE_DEBUG("UHID_FEATURE from uhid-dev\n");
- break;
- case UHID_FEATURE_ANSWER:
- APPL_TRACE_DEBUG("UHID_FEATURE_ANSWER from uhid-dev\n");
+ if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.feature))) {
+ APPL_TRACE_ERROR(
+ "%s: UHID_FEATURE: Invalid size read from uhid-dev: %zd < %zu",
+ __func__, ret, sizeof(ev.type) + sizeof(ev.u.feature));
+ return -EFAULT;
+ }
+ APPL_TRACE_DEBUG("UHID_FEATURE: Report type = %d", ev.u.feature.rtype);
+ p_dev->get_rpt_snt++;
+ if (p_dev->get_rpt_id_queue) {
+ uint32_t* get_rpt_id = (uint32_t*)osi_malloc(sizeof(uint32_t));
+ *get_rpt_id = ev.u.feature.id;
+ fixed_queue_enqueue(p_dev->get_rpt_id_queue, (void*)get_rpt_id);
+ }
+ if (ev.u.feature.rtype == UHID_FEATURE_REPORT)
+ btif_hh_getreport(p_dev, BTHH_FEATURE_REPORT, ev.u.feature.rnum, 0);
+ else
+ APPL_TRACE_ERROR("%s: UHID_FEATURE: Invalid report type = %d", __func__,
+ ev.u.feature.rtype);
break;
default:
@@ -337,6 +352,9 @@
}
p_dev->dev_status = BTHH_CONN_STATE_CONNECTED;
+ p_dev->get_rpt_id_queue = fixed_queue_new(SIZE_MAX);
+ CHECK(p_dev->get_rpt_id_queue);
+
APPL_TRACE_DEBUG("%s: Return device status %d", __func__, p_dev->dev_status);
}
@@ -366,6 +384,9 @@
for (i = 0; i < BTIF_HH_MAX_HID; i++) {
p_dev = &btif_hh_cb.devices[i];
+ fixed_queue_flush(p_dev->get_rpt_id_queue, osi_free);
+ fixed_queue_free(p_dev->get_rpt_id_queue, NULL);
+ p_dev->get_rpt_id_queue = NULL;
if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN &&
p_dev->dev_handle == dev_handle) {
APPL_TRACE_WARNING(
@@ -493,6 +514,71 @@
}
}
+/*******************************************************************************
+ *
+ * Function bta_hh_co_set_rpt_rsp
+ *
+ * Description This callout function is executed by HH when Set Report
+ * Response is received on Control Channel.
+ *
+ * Returns void.
+ *
+ ******************************************************************************/
+void bta_hh_co_set_rpt_rsp(uint8_t dev_handle, uint8_t status) {
+ APPL_TRACE_ERROR("%s: Error: UHID_SET_REPORT_REPLY not supported", __func__);
+}
+
+/*******************************************************************************
+ *
+ * Function bta_hh_co_get_rpt_rsp
+ *
+ * Description This callout function is executed by HH when Get Report
+ * Response is received on Control Channel.
+ *
+ * Returns void.
+ *
+ ******************************************************************************/
+void bta_hh_co_get_rpt_rsp(uint8_t dev_handle, uint8_t status, uint8_t* p_rpt,
+ uint16_t len) {
+ struct uhid_event ev;
+ btif_hh_device_t* p_dev;
+
+ APPL_TRACE_VERBOSE("%s: dev_handle = %d", __func__, dev_handle);
+
+ p_dev = btif_hh_find_connected_dev_by_handle(dev_handle);
+ if (p_dev == NULL) {
+ APPL_TRACE_WARNING("%s: Error: unknown HID device handle %d", __func__,
+ dev_handle);
+ return;
+ }
+
+ if (!p_dev->get_rpt_id_queue) {
+ APPL_TRACE_WARNING("%s: Error: missing UHID_GET_REPORT id queue", __func__);
+ return;
+ }
+
+ // Send the HID report to the kernel.
+ if (p_dev->fd >= 0 && p_dev->get_rpt_snt--) {
+ uint32_t* get_rpt_id =
+ (uint32_t*)fixed_queue_dequeue(p_dev->get_rpt_id_queue);
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_FEATURE_ANSWER;
+ ev.u.feature_answer.id = *get_rpt_id;
+ ev.u.feature_answer.err = status;
+ ev.u.feature_answer.size = len;
+ osi_free(get_rpt_id);
+ if (len > 0) {
+ if (len > UHID_DATA_MAX) {
+ APPL_TRACE_WARNING("%s: Report size greater than allowed size",
+ __func__);
+ return;
+ }
+ memcpy(ev.u.feature_answer.data, p_rpt + GET_RPT_RSP_OFFSET, len);
+ uhid_write(p_dev->fd, &ev);
+ }
+ }
+}
+
#if (BTA_HH_LE_INCLUDED == TRUE)
/*******************************************************************************
*
diff --git a/btif/include/btif_hh.h b/btif/include/btif_hh.h
index 2364544..b71d347 100644
--- a/btif/include/btif_hh.h
+++ b/btif/include/btif_hh.h
@@ -25,6 +25,7 @@
#include <stdint.h>
#include "bta_hh_api.h"
#include "btu.h"
+#include "osi/include/fixed_queue.h"
/*******************************************************************************
* Constants & Macros
@@ -67,6 +68,8 @@
pthread_t hh_poll_thread_id;
uint8_t hh_keep_polling;
alarm_t* vup_timer;
+ fixed_queue_t* get_rpt_id_queue;
+ uint8_t get_rpt_snt;
bool local_vup; // Indicated locally initiated VUP
} btif_hh_device_t;
@@ -106,6 +109,9 @@
extern void btif_hh_setreport(btif_hh_device_t* p_dev,
bthh_report_type_t r_type, uint16_t size,
uint8_t* report);
+extern void btif_hh_getreport(btif_hh_device_t* p_dev,
+ bthh_report_type_t r_type, uint8_t reportId,
+ uint16_t bufferSize);
extern void btif_hh_service_registration(bool enable);
#endif
diff --git a/btif/src/btif_hh.cc b/btif/src/btif_hh.cc
index c5a9021..b2c1c6f 100644
--- a/btif/src/btif_hh.cc
+++ b/btif/src/btif_hh.cc
@@ -682,6 +682,21 @@
}
}
+/*******************************************************************************
+ *
+ *
+ * Function btif_hh_getreport
+ *
+ * Description getreport initiated from the BTIF thread context
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void btif_hh_getreport(btif_hh_device_t* p_dev, bthh_report_type_t r_type,
+ uint8_t reportId, uint16_t bufferSize) {
+ BTA_HhGetReport(p_dev->dev_handle, r_type, reportId, bufferSize);
+}
+
/*****************************************************************************
* Section name (Group of functions)
****************************************************************************/