#include "hid.h" #include "cccd.h" #include "epicardium.h" #include "modules/log.h" #include "dm_api.h" #include "att_api.h" #include "app_api.h" #include "hid/hid_api.h" #include "svc_hid.h" #include "FreeRTOS.h" #include "queue.h" #include <stdint.h> #include <stdio.h> #include <string.h> /* HidApp TX path flags */ #define HIDAPP_TX_FLAGS_READY 0x01 /*! application control block */ /* clang-format off */ struct { uint8_t txFlags; /* transmit flags */ uint8_t protocolMode; /* current protocol mode */ uint8_t hostSuspended; /* TRUE if host suspended */ } hidAppCb; /* clang-format on */ struct report { uint8_t report_id; uint8_t data[8]; uint8_t len; }; #define QUEUE_SIZE 10 static QueueHandle_t queue; static uint8_t buffer[sizeof(struct report) * QUEUE_SIZE]; static StaticQueue_t queue_data; static int hid_queue_data(uint8_t report_id, uint8_t *data, uint8_t len) { struct report report; if (report_id < 1 || report_id > 3) { return -EINVAL; } report.report_id = report_id; if (len > sizeof(report.data)) { return -EINVAL; } memcpy(report.data, data, len); report.len = len; if (xQueueSend(queue, &report, 0) != pdTRUE) { /* Likely full */ return -EAGAIN; } return 0; } static bool hid_dequeue_data(dmConnId_t connId) { uint8_t cccHandle; struct report report; //lock(); if (!(hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY)) { //unlock(); return false; } // Loop until a CCC is enabled or the queue is empty while (true) { if (xQueueReceive(queue, &report, 0) != pdTRUE) { break; } if (HidGetProtocolMode() == HID_PROTOCOL_MODE_BOOT) { if (report.report_id == HIDAPP_KEYBOARD_REPORT_ID) { report.report_id = HID_KEYBOARD_BOOT_ID; cccHandle = HIDAPP_KBI_CCC_HDL; } else if (report.report_id == HIDAPP_MOUSE_REPORT_ID) { report.report_id = HID_MOUSE_BOOT_ID; cccHandle = HIDAPP_MBI_CCC_HDL; } else { break; } } else { if (report.report_id == HIDAPP_KEYBOARD_REPORT_ID) { cccHandle = HIDAPP_IN_KEYBOARD_CCC_HDL; } else if (report.report_id == HIDAPP_MOUSE_REPORT_ID) { cccHandle = HIDAPP_IN_MOUSE_CCC_HDL; } else if (report.report_id == HIDAPP_CONSUMER_REPORT_ID) { cccHandle = HIDAPP_IN_CONSUMER_CCC_HDL; } else { break; }; } if (AttsCccEnabled(connId, cccHandle) || 1) { hidAppCb.txFlags &= ~(HIDAPP_TX_FLAGS_READY); /* Send the message */ HidSendInputReport( connId, report.report_id, report.len, report.data ); break; } } //unlock(); return true; } /*************************************************************************************************/ /*! * \brief Callback to handle an output report from the host. * * \param connId The connection identifier. * \param id The ID of the report. * \param len The length of the report data in pReport. * \param pReport A buffer containing the report. * * \return None. */ /*************************************************************************************************/ void hidAppOutputCback( dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport ) { /* TODO: process output reports */ } /*************************************************************************************************/ /*! * \brief Callback to handle a feature report from the host. * * \param connId The connection identifier. * \param id The ID of the report. * \param len The length of the report data in pReport. * \param pReport A buffer containing the report. * * \return None. */ /*************************************************************************************************/ void hidAppFeatureCback( dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport ) { /* TODO: process feature reports */ } /*************************************************************************************************/ /*! * \brief Callback to handle a change in protocol mode or control point from the host. * * \param connId The connection identifier. * \param mode The type of information (HID_INFO_CONTROL_POINT or HID_INFO_PROTOCOL_MODE) * \param value The value of the information * * \return None. */ /*************************************************************************************************/ void hidAppInfoCback(dmConnId_t connId, uint8_t type, uint8_t value) { if (type == HID_INFO_PROTOCOL_MODE) { LOG_INFO("hid", "protocol mode: %u\n", value); hidAppCb.protocolMode = value; } else if (type == HID_INFO_CONTROL_POINT) { LOG_INFO("hid", "host suspended: %u\n", value); hidAppCb.hostSuspended = (value == HID_CONTROL_POINT_SUSPEND) ? TRUE : FALSE; } } void HidProcMsg(wsfMsgHdr_t *pMsg) { if (!queue) { /* Not initialized (yet). */ return; } if (pMsg->event == ATTS_HANDLE_VALUE_CNF) { if (pMsg->status == ATT_SUCCESS) { hidAppCb.txFlags |= HIDAPP_TX_FLAGS_READY; hid_dequeue_data((dmConnId_t)pMsg->param); } } if (pMsg->event == DM_CONN_OPEN_IND) { hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY; struct report report; while (xQueueReceive(queue, &report, 0) == pdTRUE) ; /* Todo: At this point the CCC descriptors are not set up yet * and things which get sent until then are discarded. */ } } int epic_ble_hid_send_report(uint8_t report_id, uint8_t *data, uint8_t len) { dmConnId_t connId = AppConnIsOpen(); if (connId == DM_CONN_ID_NONE) { return -EIO; } int ret; ret = hid_queue_data(report_id, data, len); if (ret < 0) { return ret; } if (hid_dequeue_data(connId)) { return 0; } else { return 1; } } void hid_work_init(void) { queue = xQueueCreateStatic( QUEUE_SIZE, sizeof(struct report), buffer, &queue_data ); hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY; }