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

Commit 916a8e56 authored by Himanshu Rawat's avatar Himanshu Rawat Committed by Gerrit Code Review
Browse files

Merge "Support Get Report and Set Report procedures"

parents 75e0b4cd 163d75ba
Loading
Loading
Loading
Loading
+0 −8
Original line number Diff line number Diff line
@@ -659,9 +659,6 @@ void bta_hh_handsk_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {
      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;
@@ -673,9 +670,6 @@ void bta_hh_handsk_act(tBTA_HH_DEV_CB* p_cb, const tBTA_HH_DATA* p_data) {
      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;
@@ -732,8 +726,6 @@ void bta_hh_ctrl_dat_act(tBTA_HH_DEV_CB* p_cb, const 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*/
+25 −29
Original line number Diff line number Diff line
@@ -1726,56 +1726,53 @@ void bta_hh_le_api_disc_act(tBTA_HH_DEV_CB* p_cb) {
static void read_report_cb(uint16_t conn_id, tGATT_STATUS status,
                           uint16_t handle, uint16_t len, uint8_t* value,
                           void* data) {
  tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data;
  if (p_dev_cb->w4_evt != BTA_HH_GET_RPT_EVT) {
    LOG_WARN("Unexpected Read response, w4_evt = %d", p_dev_cb->w4_evt);
    return;
  }

  const gatt::Characteristic* p_char =
      BTA_GATTC_GetCharacteristic(conn_id, handle);

  if (p_char == NULL) return;

  uint16_t char_uuid = p_char->uuid.As16Bit();

  if (char_uuid != GATT_UUID_HID_REPORT &&
      char_uuid != GATT_UUID_HID_BT_KB_INPUT &&
      char_uuid != GATT_UUID_HID_BT_KB_OUTPUT &&
      char_uuid != GATT_UUID_HID_BT_MOUSE_INPUT &&
      char_uuid != GATT_UUID_BATTERY_LEVEL) {
    APPL_TRACE_ERROR("%s: Unexpected Read UUID: 0x%04x", __func__, char_uuid);
  if (p_char == nullptr) {
    LOG_ERROR("Unknown handle");
    return;
  }

  tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data;
  if (p_dev_cb->w4_evt != BTA_HH_GET_RPT_EVT) {
    APPL_TRACE_ERROR("Unexpected READ cmpl, w4_evt = %d", p_dev_cb->w4_evt);
  uint16_t char_uuid = p_char->uuid.As16Bit();
  switch (char_uuid) {
    case GATT_UUID_HID_REPORT:
    case GATT_UUID_HID_BT_KB_INPUT:
    case GATT_UUID_HID_BT_KB_OUTPUT:
    case GATT_UUID_HID_BT_MOUSE_INPUT:
    case GATT_UUID_BATTERY_LEVEL:
      break;
    default:
      LOG_ERROR("Unexpected Read UUID: 0x%04x", char_uuid);
      return;
  }

  /* GET_REPORT */
  BT_HDR* p_buf = NULL;
  tBTA_HH_LE_RPT* p_rpt;
  tBTA_HH_HSDATA hs_data;
  uint8_t* pp;

  memset(&hs_data, 0, sizeof(hs_data));
  tBTA_HH_HSDATA hs_data = {};
  hs_data.status = BTA_HH_ERR;
  hs_data.handle = p_dev_cb->hid_handle;

  if (status == GATT_SUCCESS) {
    tBTA_HH_LE_RPT* p_rpt;
    const gatt::Service* p_svc =
        BTA_GATTC_GetOwningService(conn_id, p_char->value_handle);

    p_rpt = bta_hh_le_find_report_entry(p_dev_cb, p_svc->handle, char_uuid,
                                        p_char->value_handle);

    if (p_rpt != NULL && len) {
      p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR) + len + 1);
    if (p_rpt != nullptr && len) {
      BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR) + len + 1);
      /* pack data send to app */
      hs_data.status = BTA_HH_OK;
      p_buf->len = len + 1;
      p_buf->layer_specific = 0;
      p_buf->offset = 0;

      uint8_t* pp = (uint8_t*)(p_buf + 1);
      /* attach report ID as the first byte of the report before sending it to
       * USB HID driver */
      pp = (uint8_t*)(p_buf + 1);
      UINT8_TO_STREAM(pp, p_rpt->rpt_id);
      memcpy(pp, value, len);

@@ -1785,8 +1782,7 @@ static void read_report_cb(uint16_t conn_id, tGATT_STATUS status,

  p_dev_cb->w4_evt = 0;
  (*bta_hh_cb.p_cback)(BTA_HH_GET_RPT_EVT, (tBTA_HH*)&hs_data);

  osi_free_and_reset((void**)&p_buf);
  osi_free(hs_data.rsp_data.p_rpt_data);
}

/*******************************************************************************
+1 −1
Original line number Diff line number Diff line
@@ -103,7 +103,7 @@ extern void bta_hh_co_set_rpt_rsp(uint8_t dev_handle, uint8_t status);
 *
 ******************************************************************************/
extern void bta_hh_co_get_rpt_rsp(uint8_t dev_handle, uint8_t status,
                                  uint8_t* p_rpt, uint16_t len);
                                  const uint8_t* p_rpt, uint16_t len);

/*******************************************************************************
 *
+1 −2
Original line number Diff line number Diff line
@@ -104,6 +104,5 @@ TEST_F(BtaHhTest, bta_hh_ctrl_dat_act__BTA_HH_GET_RPT_EVT) {
  };

  bta_hh_ctrl_dat_act(&cb, &data);

  ASSERT_EQ(get_func_call_count("bta_hh_co_get_rpt_rsp"), 1);
  ASSERT_EQ(cb.w4_evt, 0);
}
+136 −100
Original line number Diff line number Diff line
@@ -45,11 +45,13 @@ const char* dev_path = "/dev/uhid";
#include "btif_config.h"
#define BTA_HH_NV_LOAD_MAX 16
static tBTA_HH_RPT_CACHE_ENTRY sReportCache[BTA_HH_NV_LOAD_MAX];
#define GET_RPT_RSP_OFFSET 9
#define BTA_HH_CACHE_REPORT_VERSION 1
#define THREAD_NORMAL_PRIORITY 0
#define BT_HH_THREAD "bt_hh_thread"

static const bthh_report_type_t map_rtype_uhid_hh[] = {
    BTHH_FEATURE_REPORT, BTHH_OUTPUT_REPORT, BTHH_INPUT_REPORT};

void uhid_set_non_blocking(int fd) {
  int opts = fcntl(fd, F_GETFL);
  if (opts < 0)
@@ -63,6 +65,62 @@ void uhid_set_non_blocking(int fd) {
                     strerror(errno));
}

static bool uhid_feature_req_handler(btif_hh_device_t* p_dev,
                                     struct uhid_feature_req& req) {
  LOG_DEBUG("Report type = %d, id = %d", req.rtype, req.rnum);

  if (req.rtype > UHID_INPUT_REPORT) {
    LOG_ERROR("Invalid report type %d", req.rtype);
    return false;
  }

  if (p_dev->get_rpt_id_queue == nullptr) {
    LOG_ERROR("Queue is not initialized");
    return false;
  }

  uint32_t* context = (uint32_t*)osi_malloc(sizeof(uint32_t));
  *context = req.id;

  if (!fixed_queue_try_enqueue(p_dev->get_rpt_id_queue, (void*)context)) {
    osi_free(context);
    LOG_ERROR("Queue is full, dropping event %d", req.id);
    return false;
  }

  btif_hh_getreport(p_dev, map_rtype_uhid_hh[req.rtype], req.rnum, 0);
  return true;
}

#if ENABLE_UHID_SET_REPORT
static bool uhid_set_report_req_handler(btif_hh_device_t* p_dev,
                                        struct uhid_set_report_req& req) {
  LOG_DEBUG("Report type = %d, id = %d", req.rtype, req.rnum);

  if (req.rtype > UHID_INPUT_REPORT) {
    LOG_ERROR("Invalid report type %d", req.rtype);
    return false;
  }

  if (p_dev->set_rpt_id_queue == nullptr) {
    LOG_ERROR("Queue is not initialized");
    return false;
  }

  uint32_t* context = (uint32_t*)osi_malloc(sizeof(uint32_t));
  *context = req.id;

  if (!fixed_queue_try_enqueue(p_dev->set_rpt_id_queue, (void*)context)) {
    osi_free(context);
    LOG_ERROR("Queue is full, dropping event %d", req.id);
    return false;
  }

  btif_hh_setreport(p_dev, map_rtype_uhid_hh[req.rtype], req.size, req.data);
  return true;
}
#endif  // ENABLE_UHID_SET_REPORT

/*Internal function to perform UHID write and error checking*/
static int uhid_write(int fd, const struct uhid_event* ev) {
  ssize_t ret;
@@ -146,74 +204,37 @@ 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:

    case UHID_FEATURE:  // UHID_GET_REPORT
      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));
        LOG_ERROR("UHID_GET_REPORT: Invalid size read from uhid-dev: %zd < %zu",
                  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;
        auto ok = fixed_queue_try_enqueue(p_dev->get_rpt_id_queue, (void*)get_rpt_id);
        if (!ok) {
            LOG_ERROR("get_rpt_id_queue is full, dropping event %d", *get_rpt_id);
            osi_free(get_rpt_id);

      if (!uhid_feature_req_handler(p_dev, ev.u.feature)) {
        return -EFAULT;
      }
      }
      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;

#if ENABLE_UHID_SET_REPORT
    case UHID_SET_REPORT: {
      bool sent = true;

      if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.set_report))) {
        LOG_ERROR("Invalid size read from uhid-dev: %zd < %zu", ret,
                  sizeof(ev.type) + sizeof(ev.u.set_report));
        LOG_ERROR("UHID_SET_REPORT: Invalid size read from uhid-dev: %zd < %zu",
                  ret, sizeof(ev.type) + sizeof(ev.u.set_report));
        return -EFAULT;
      }

      LOG_DEBUG("UHID_SET_REPORT: Report type = %d, report_size = %d",
                ev.u.set_report.rtype, ev.u.set_report.size);

      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 if (ev.u.set_report.rtype == UHID_INPUT_REPORT) {
        btif_hh_setreport(p_dev, BTHH_INPUT_REPORT, ev.u.set_report.size,
                          ev.u.set_report.data);
      } else {
        LOG_ERROR("UHID_SET_REPORT: Invalid Report type = %d",
                  ev.u.set_report.rtype);
        sent = false;
      }

      if (sent && p_dev->set_rpt_id_queue) {
        uint32_t* set_rpt_id = (uint32_t*)osi_malloc(sizeof(uint32_t));
        *set_rpt_id = ev.u.set_report.id;
        auto ok = fixed_queue_try_enqueue(p_dev->set_rpt_id_queue, (void*)set_rpt_id);
        if (!ok) {
            LOG_ERROR("set_rpt_id_queue is full, dropping event %d", *set_rpt_id);
            osi_free(set_rpt_id);
      if (!uhid_set_report_req_handler(p_dev, ev.u.set_report)) {
        return -EFAULT;
      }
      }
      break;
    }
#endif  // ENABLE_UHID_SET_REPORT

    default:
      APPL_TRACE_DEBUG("Invalid event from uhid-dev: %u\n", ev.type);
      LOG_ERROR("Invalid event from uhid-dev: %u\n", ev.type);
  }

  return 0;
@@ -612,33 +633,42 @@ void bta_hh_co_set_rpt_rsp(uint8_t dev_handle, uint8_t status) {

  btif_hh_device_t* p_dev = btif_hh_find_connected_dev_by_handle(dev_handle);
  if (p_dev == nullptr) {
    LOG_WARN("Error: unknown HID device handle %d", dev_handle);
    LOG_WARN("Unknown HID device handle %d", dev_handle);
    return;
  }

  if (!p_dev->set_rpt_id_queue) {
    LOG_WARN("Error: missing UHID_SET_REPORT id queue");
    LOG_WARN("Missing UHID_SET_REPORT id queue");
    return;
  }

  // Send the HID set report reply to the kernel.
  if (p_dev->fd >= 0) {
    uint32_t* set_rpt_id =
        (uint32_t*)fixed_queue_try_dequeue(p_dev->set_rpt_id_queue);
    if (set_rpt_id) {
      struct uhid_event ev = {};

      ev.type = UHID_SET_REPORT_REPLY;
      ev.u.set_report_reply.id = *set_rpt_id;
      ev.u.set_report_reply.err = status;
      osi_free(set_rpt_id);
      uhid_write(p_dev->fd, &ev);
    } else {
      LOG_VERBOSE("No pending UHID_SET_REPORT");
  if (p_dev->fd < 0) {
    LOG_ERROR("Unexpected Set Report response");
    return;
  }

  uint32_t* context = (uint32_t*)fixed_queue_try_dequeue(p_dev->set_rpt_id_queue);

  if (context == nullptr) {
    LOG_WARN("No pending UHID_SET_REPORT");
    return;
  }

  struct uhid_event ev = {
      .type = UHID_SET_REPORT_REPLY,
      .u = {
          .set_report_reply = {
              .id = *context,
              .err = status,
          },
      },
  };
  uhid_write(p_dev->fd, &ev);
  osi_free(context);

#else
  LOG_ERROR("Error: UHID_SET_REPORT_REPLY not supported");
  LOG_ERROR("UHID_SET_REPORT_REPLY not supported");
#endif  // ENABLE_UHID_SET_REPORT
}

@@ -652,49 +682,55 @@ void bta_hh_co_set_rpt_rsp(uint8_t dev_handle, uint8_t status) {
 * 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;
void bta_hh_co_get_rpt_rsp(uint8_t dev_handle, uint8_t status,
                           const uint8_t* p_rpt, uint16_t len) {
  btif_hh_device_t* p_dev;

  APPL_TRACE_VERBOSE("%s: dev_handle = %d", __func__, dev_handle);
  LOG_VERBOSE("dev_handle = %d, status = %d", dev_handle, status);

  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);
  if (p_dev == nullptr) {
    LOG_WARN("Unknown HID device handle %d", dev_handle);
    return;
  }

  if (!p_dev->get_rpt_id_queue) {
    APPL_TRACE_WARNING("%s: Error: missing UHID_GET_REPORT id queue", __func__);
    LOG_WARN("Missing UHID_GET_REPORT id queue");
    return;
  }

  // Send the HID report to the kernel.
  if (p_dev->fd >= 0 && p_dev->get_rpt_snt > 0 && p_dev->get_rpt_snt--) {
    uint32_t* get_rpt_id =
        (uint32_t*)fixed_queue_try_dequeue(p_dev->get_rpt_id_queue);
    if (get_rpt_id == nullptr) {
      APPL_TRACE_WARNING("%s: Error: UHID_GET_REPORT queue is empty", __func__);
  if (p_dev->fd < 0) {
    LOG_WARN("Unexpected Get Report response");
    return;
  }
    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__);

  uint32_t* context = (uint32_t*)fixed_queue_try_dequeue(p_dev->get_rpt_id_queue);

  if (context == nullptr) {
    LOG_WARN("No pending UHID_GET_REPORT");
    return;
  }
      memcpy(ev.u.feature_answer.data, p_rpt + GET_RPT_RSP_OFFSET, len);
      uhid_write(p_dev->fd, &ev);
    }

  if (len == 0 || len > UHID_DATA_MAX) {
    LOG_WARN("Invalid report size = %d", len);
    return;
  }

  struct uhid_event ev = {
      .type = UHID_FEATURE_ANSWER,
      .u = {
          .feature_answer = {
              .id = *context,
              .err = status,
              .size = len,
          },
      },
  };
  memcpy(ev.u.feature_answer.data, p_rpt, len);

  uhid_write(p_dev->fd, &ev);
  osi_free(context);
}

/*******************************************************************************
Loading