Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6fbe4548 authored by Hemant Gupta's avatar Hemant Gupta Committed by Andre Eisenbach
Browse files

HID: Fix scroll issue with Apple Magic Mouse

Usecase
1) Pair and connect with Apple Magic Mouse
2) Scroll over HID mouse and see if pointer moves up or down
   on DUT.

Observation:
Scroll functionality does not work on DUT and mouse pointer
stays still.

Root Cause:
From kernel 3.18 onwards, UHID flags are updated and following new
flags are added. Support for handling same is missing in user
space hid driver (bta_hh_co.cc)
UHID_GET_REPORT
UHID_GET_REPORT_REPLY
UHID_SET_REPORT
UHID_SET_REPORT_REPLY

Fix:
Add support for set report and get report in HID user
space because of changes in uhid kernel driver with kernel changes
for kernel 3.18. Apple Magic Mouse scroll functionality does not
work without this fix, as Apple Mouse requires set report from
HID host for it to start working properly wrt Scroll functionality.

Bug: 35306202
Change-Id: I90230b76d68e9275dcacd9f46f03e312ddf2fa30
parent dd001c64
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -620,7 +620,7 @@ void bta_hh_data_act(tBTA_HH_DEV_CB* p_cb, tBTA_HH_DATA* p_data) {
 *
 * Function         bta_hh_handsk_act
 *
 * Description      HID Host process a handshake acknoledgement.
 * Description      HID Host process a handshake acknowledgement.
 *
 *
 * Returns          void
@@ -650,7 +650,8 @@ void bta_hh_handsk_act(tBTA_HH_DEV_CB* p_cb, tBTA_HH_DATA* p_data) {
      /* if handshake gives an OK code for these transaction, fill in UNSUPT */
      hs_data.status = bta_hh_get_trans_status(p_data->hid_cback.data);
      if (hs_data.status == BTA_HH_OK) 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(cback_data.handle, hs_data.status, NULL, 0);
      (*bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH*)&hs_data);
      p_cb->w4_evt = 0;
      break;
@@ -661,6 +662,8 @@ void bta_hh_handsk_act(tBTA_HH_DEV_CB* p_cb, tBTA_HH_DATA* p_data) {
    case BTA_HH_SET_IDLE_EVT:
      cback_data.handle = p_cb->hid_handle;
      cback_data.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(cback_data.handle, cback_data.status);
      (*bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH*)&cback_data);
      p_cb->w4_evt = 0;
      break;
@@ -715,6 +718,7 @@ void bta_hh_ctrl_dat_act(tBTA_HH_DEV_CB* p_cb, tBTA_HH_DATA* p_data) {
      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*/
+24 −0
Original line number Diff line number Diff line
@@ -77,6 +77,30 @@ extern void bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class,
 ******************************************************************************/
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)
/*******************************************************************************
 *
+127 −5
Original line number Diff line number Diff line
@@ -141,11 +141,49 @@ static int uhid_read_event(btif_hh_device_t* p_dev) {
      }
      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");
    case UHID_SET_REPORT: {
      if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.set_report))) {
        APPL_TRACE_ERROR(
            "%s: UHID_SET_REPORT: Invalid size read from "
            "uhid-dev: %zd < %zu",
            __func__, ret, sizeof(ev.type) + sizeof(ev.u.set_report));
        return -EFAULT;
      }
      APPL_TRACE_DEBUG(
          "UHID_SET_REPORT: Report type = %d, report_size = %d"
          " report id = %d",
          ev.u.set_report.rtype, ev.u.set_report.size, ev.u.set_report.id);
      if (p_dev->set_rpt_id_queue) {
        void* set_rpt_id = (void*)&ev.u.set_report.id;
        fixed_queue_enqueue(p_dev->set_rpt_id_queue, set_rpt_id);
      }
      if (ev.u.set_report.rtype == UHID_FEATURE_REPORT)
        btif_hh_setreport(p_dev, BTHH_FEATURE_REPORT, ev.u.set_report.size,
                          ev.u.set_report.data);
      else if (ev.u.set_report.rtype == UHID_OUTPUT_REPORT)
        btif_hh_setreport(p_dev, BTHH_OUTPUT_REPORT, ev.u.set_report.size,
                          ev.u.set_report.data);
      else
        btif_hh_setreport(p_dev, BTHH_INPUT_REPORT, ev.u.set_report.size,
                          ev.u.set_report.data);
    } break;
    case UHID_GET_REPORT:
      if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.get_report))) {
        APPL_TRACE_ERROR(
            "%s: UHID_GET_REPORT: Invalid size read from "
            "uhid-dev: %zd < %zu",
            __func__, ret, sizeof(ev.type) + sizeof(ev.u.get_report));
        return -EFAULT;
      }
      APPL_TRACE_DEBUG("UHID_GET_REPORT: Report type = %d",
                       ev.u.get_report.rtype);
      p_dev->get_rpt_snt++;
      if (ev.u.get_report.rtype == UHID_FEATURE_REPORT)
        btif_hh_getreport(p_dev, BTHH_FEATURE_REPORT, ev.u.get_report.rnum, 0);
      else if (ev.u.get_report.rtype == UHID_OUTPUT_REPORT)
        btif_hh_getreport(p_dev, BTHH_OUTPUT_REPORT, ev.u.get_report.rnum, 0);
      else
        btif_hh_getreport(p_dev, BTHH_INPUT_REPORT, ev.u.get_report.rnum, 0);
      break;

    default:
@@ -344,6 +382,9 @@ void bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class,
  }

  p_dev->dev_status = BTHH_CONN_STATE_CONNECTED;
  p_dev->set_rpt_id_queue = fixed_queue_new(SIZE_MAX);
  CHECK(p_dev->set_rpt_id_queue);

  APPL_TRACE_DEBUG("%s: Return device status %d", __func__, p_dev->dev_status);
}

@@ -373,6 +414,8 @@ void bta_hh_co_close(uint8_t dev_handle, uint8_t app_id) {

  for (i = 0; i < BTIF_HH_MAX_HID; i++) {
    p_dev = &btif_hh_cb.devices[i];
    fixed_queue_free(p_dev->set_rpt_id_queue, NULL);
    p_dev->set_rpt_id_queue = NULL;
    if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN &&
        p_dev->dev_handle == dev_handle) {
      APPL_TRACE_WARNING(
@@ -503,6 +546,85 @@ void bta_hh_co_send_hid_info(btif_hh_device_t* p_dev, const char* dev_name,
  }
}

/*******************************************************************************
 *
 * 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) {
  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;
  }
  // Send the HID report to the kernel.
  if (!p_dev->set_rpt_id_queue)
      return;

  struct uhid_event ev;
  uint32_t* set_rpt_id =
      (uint32_t*)fixed_queue_dequeue(p_dev->set_rpt_id_queue);
  memset(&ev, 0, sizeof(ev));
  ev.type = UHID_SET_REPORT_REPLY;
  /* get the report id from queue_start pointer */
  ev.u.set_report_reply.id = *set_rpt_id;
  APPL_TRACE_VERBOSE("%s: set_report_reply_id = %d", __func__,
                     ev.u.set_report_reply.id);
  ev.u.set_report_reply.err = status;
  uhid_write(p_dev->fd, &ev);
}

/*******************************************************************************
 *
 * 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;
  }
  // Send the HID report to the kernel.
  if (p_dev->fd >= 0 && p_dev->get_rpt_snt--) {
    memset(&ev, 0, sizeof(ev));
    ev.type = UHID_GET_REPORT_REPLY;
    ev.u.get_report_reply.err = status;
    ev.u.get_report_reply.size = len;
    if (len > 0) {
      if (len > UHID_DATA_MAX) {
        APPL_TRACE_WARNING("%s: Report size greater than allowed size",
                           __func__);
        return;
      }
      memcpy(ev.u.get_report_reply.data, p_rpt, len);
      uhid_write(p_dev->fd, &ev);
    }
  }
}

#if (BTA_HH_LE_INCLUDED == TRUE)
/*******************************************************************************
 *
+7 −1
Original line number Diff line number Diff line
@@ -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 @@ typedef struct {
  pthread_t hh_poll_thread_id;
  uint8_t hh_keep_polling;
  alarm_t* vup_timer;
  fixed_queue_t* set_rpt_id_queue;
  uint8_t get_rpt_snt;
  bool local_vup;  // Indicated locally initiated VUP
} btif_hh_device_t;

@@ -101,10 +104,13 @@ extern void btif_hh_remove_device(bt_bdaddr_t bd_addr);
bool btif_hh_add_added_dev(bt_bdaddr_t bda, tBTA_HH_ATTR_MASK attr_mask);
extern bt_status_t btif_hh_virtual_unplug(bt_bdaddr_t* bd_addr);
extern void btif_hh_disconnect(bt_bdaddr_t* bd_addr);
extern void btif_hh_service_registration(bool enable);
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_service_registration(bool enable);
extern void btif_hh_getreport(btif_hh_device_t* p_dev,
                              bthh_report_type_t r_type, uint8_t reportId,
                              uint16_t bufferSize);

bool btif_hh_add_added_dev(bt_bdaddr_t bd_addr, tBTA_HH_ATTR_MASK attr_mask);

+15 −0
Original line number Diff line number Diff line
@@ -700,6 +700,21 @@ void btif_hh_service_registration(bool enable) {
  }
}

/*******************************************************************************
 *
 *
 * 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)
 ****************************************************************************/