Skip to content
Snippets Groups Projects
hid_work.c 5.70 KiB
#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;
}