You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
334 lines
9.2 KiB
334 lines
9.2 KiB
3 years ago
|
/*
|
||
|
* upstream_hid.c
|
||
|
*
|
||
|
* Created on: Jan 16, 2016
|
||
|
* Author: Robert Fisk
|
||
|
*
|
||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
#include "upstream_hid.h"
|
||
|
#include "upstream_hid_botdetect.h"
|
||
|
#include "upstream_interface_def.h"
|
||
|
#include "build_config.h"
|
||
|
|
||
|
|
||
|
#if defined (CONFIG_KEYBOARD_ENABLED) || defined (CONFIG_MOUSE_ENABLED)
|
||
|
|
||
|
|
||
|
UpstreamPacketTypeDef* UpstreamHidPacket = NULL;
|
||
|
UpstreamHidGetReportCallback GetReportCallback = NULL;
|
||
|
|
||
|
#ifdef CONFIG_KEYBOARD_ENABLED
|
||
|
KeyboardOutStateTypeDef KeyboardOutDataState = KEYBOARD_OUT_STATE_IDLE;
|
||
|
uint8_t KeyboardOutData[HID_KEYBOARD_OUTPUT_DATA_LEN];
|
||
|
#endif
|
||
|
|
||
|
uint8_t GetReportLoopIsRunning = 0;
|
||
|
|
||
|
|
||
|
static void Upstream_HID_ReceiveInterruptReport(void);
|
||
|
static void Upstream_HID_ReceiveInterruptReportCallback(UpstreamPacketTypeDef* receivedPacket);
|
||
|
static void Upstream_HID_SendControlReport(void);
|
||
|
static void Upstream_HID_SendControlReportCallback(UpstreamPacketTypeDef* receivedPacket);
|
||
|
|
||
|
|
||
|
|
||
|
void Upstream_HID_DeInit(void)
|
||
|
{
|
||
|
if (UpstreamHidPacket != NULL)
|
||
|
{
|
||
|
Upstream_ReleasePacket(UpstreamHidPacket);
|
||
|
UpstreamHidPacket = NULL;
|
||
|
}
|
||
|
GetReportCallback = NULL;
|
||
|
GetReportLoopIsRunning = 0;
|
||
|
|
||
|
#ifdef CONFIG_KEYBOARD_ENABLED
|
||
|
KeyboardOutDataState = KEYBOARD_OUT_STATE_IDLE;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//Called by usbd_hid to request our next interrupt report
|
||
|
void Upstream_HID_GetInterruptReport(UpstreamHidGetReportCallback callback)
|
||
|
{
|
||
|
GetReportCallback = callback;
|
||
|
|
||
|
if (UpstreamHidPacket != NULL)
|
||
|
{
|
||
|
Upstream_ReleasePacket(UpstreamHidPacket);
|
||
|
UpstreamHidPacket = NULL;
|
||
|
}
|
||
|
|
||
|
//Wakeup interrupts are apparently broken.
|
||
|
//So we check for resume when the host sends us something here.
|
||
|
Upstream_StateMachine_CheckResume();
|
||
|
|
||
|
//Start our internal loop?
|
||
|
if (!GetReportLoopIsRunning)
|
||
|
{
|
||
|
GetReportLoopIsRunning = 1;
|
||
|
Upstream_HID_ReceiveInterruptReport();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//Our internal report request loop
|
||
|
static void Upstream_HID_ReceiveInterruptReport(void)
|
||
|
{
|
||
|
UpstreamPacketTypeDef* freePacket;
|
||
|
InterfaceCommandClassTypeDef activeClass;
|
||
|
|
||
|
activeClass = Upstream_StateMachine_CheckActiveClass();
|
||
|
if ((activeClass != COMMAND_CLASS_HID_MOUSE) &&
|
||
|
(activeClass != COMMAND_CLASS_HID_KEYBOARD)) //add classes here
|
||
|
{
|
||
|
UPSTREAM_STATEMACHINE_FREAKOUT;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
freePacket = Upstream_GetFreePacketImmediately();
|
||
|
if (freePacket == NULL)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16;
|
||
|
freePacket->CommandClass = activeClass;
|
||
|
freePacket->Command = COMMAND_HID_GET_REPORT;
|
||
|
|
||
|
if (Upstream_TransmitPacket(freePacket) == HAL_OK)
|
||
|
{
|
||
|
Upstream_ReceivePacket(Upstream_HID_ReceiveInterruptReportCallback);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Upstream_ReleasePacket(freePacket);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void Upstream_HID_ReceiveInterruptReportCallback(UpstreamPacketTypeDef* receivedPacket)
|
||
|
{
|
||
|
UpstreamHidGetReportCallback tempReportCallback;
|
||
|
InterfaceCommandClassTypeDef activeClass;
|
||
|
uint32_t i;
|
||
|
uint8_t dataLength;
|
||
|
|
||
|
|
||
|
activeClass = Upstream_StateMachine_CheckActiveClass();
|
||
|
if ((activeClass != COMMAND_CLASS_HID_MOUSE) &&
|
||
|
(activeClass != COMMAND_CLASS_HID_KEYBOARD)) //add classes here
|
||
|
{
|
||
|
UPSTREAM_STATEMACHINE_FREAKOUT;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (receivedPacket == NULL) //Error receiving packet
|
||
|
{
|
||
|
return; //Just give up...
|
||
|
}
|
||
|
|
||
|
if (receivedPacket->Length16 == UPSTREAM_PACKET_HEADER_LEN_16)
|
||
|
{
|
||
|
//Zero-length reply indicates no data from downstream device
|
||
|
Upstream_ReleasePacket(receivedPacket);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#ifdef CONFIG_MOUSE_ENABLED
|
||
|
if (activeClass == COMMAND_CLASS_HID_MOUSE)
|
||
|
{
|
||
|
if (receivedPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + ((HID_MOUSE_INPUT_DATA_LEN + 1) / 2)))
|
||
|
{
|
||
|
UPSTREAM_STATEMACHINE_FREAKOUT;
|
||
|
return;
|
||
|
}
|
||
|
dataLength = HID_MOUSE_INPUT_DATA_LEN;
|
||
|
if ((receivedPacket->Data[0] & ~((1 << HID_MOUSE_MAX_BUTTONS) - 1)) != 0) //Check number of buttons received
|
||
|
{
|
||
|
UPSTREAM_STATEMACHINE_FREAKOUT;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Mouse wakeup is triggered by button clicks only
|
||
|
if (receivedPacket->Data[0] != 0)
|
||
|
{
|
||
|
if (Upstream_StateMachine_GetSuspendState())
|
||
|
{
|
||
|
Upstream_StateMachine_Wakeup(); //Send wakeup signal to host
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_MOUSE_BOT_DETECT_ENABLED
|
||
|
Upstream_HID_BotDetectMouse(receivedPacket->Data);
|
||
|
#endif
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
#ifdef CONFIG_KEYBOARD_ENABLED
|
||
|
if (activeClass == COMMAND_CLASS_HID_KEYBOARD)
|
||
|
{
|
||
|
if (receivedPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + ((HID_KEYBOARD_INPUT_DATA_LEN + 1) / 2)))
|
||
|
{
|
||
|
UPSTREAM_STATEMACHINE_FREAKOUT;
|
||
|
return;
|
||
|
}
|
||
|
dataLength = HID_KEYBOARD_INPUT_DATA_LEN;
|
||
|
|
||
|
if (receivedPacket->Data[1] != 0)
|
||
|
{
|
||
|
UPSTREAM_STATEMACHINE_FREAKOUT;
|
||
|
return;
|
||
|
}
|
||
|
for (i = 2; i < HID_KEYBOARD_INPUT_DATA_LEN; i++)
|
||
|
{
|
||
|
if (receivedPacket->Data[i] > HID_KEYBOARD_MAX_KEY)
|
||
|
{
|
||
|
UPSTREAM_STATEMACHINE_FREAKOUT;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Upstream_StateMachine_GetSuspendState())
|
||
|
{
|
||
|
Upstream_StateMachine_Wakeup(); //Send wakeup signal to host
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_KEYBOARD_BOT_DETECT_ENABLED
|
||
|
Upstream_HID_BotDetectKeyboard(receivedPacket->Data);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//Other HID classes go here...
|
||
|
else
|
||
|
#endif
|
||
|
|
||
|
{
|
||
|
UPSTREAM_STATEMACHINE_FREAKOUT;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((GetReportCallback == NULL) ||
|
||
|
(UpstreamHidPacket != NULL) ||
|
||
|
(Upstream_StateMachine_GetSuspendState()))
|
||
|
{
|
||
|
Upstream_ReleasePacket(receivedPacket);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//Send resulting data to host
|
||
|
UpstreamHidPacket = receivedPacket; //Save packet so we can free it when upstream USB transaction is done
|
||
|
tempReportCallback = GetReportCallback;
|
||
|
GetReportCallback = NULL;
|
||
|
tempReportCallback(receivedPacket->Data, dataLength);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (GetReportLoopIsRunning)
|
||
|
{
|
||
|
#ifdef CONFIG_KEYBOARD_ENABLED
|
||
|
//Check if we need to send OUT data to the keyboard before requesting next Interrupt IN data
|
||
|
if (KeyboardOutDataState == KEYBOARD_OUT_STATE_DATA_READY)
|
||
|
{
|
||
|
Upstream_HID_SendControlReport();
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
Upstream_HID_ReceiveInterruptReport(); //Otherwise poll downstream again
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef CONFIG_KEYBOARD_ENABLED
|
||
|
void Upstream_HID_RequestSendControlReport(UpstreamPacketTypeDef* packetToSend, uint8_t dataLength)
|
||
|
{
|
||
|
InterfaceCommandClassTypeDef activeClass;
|
||
|
uint32_t i;
|
||
|
|
||
|
activeClass = Upstream_StateMachine_CheckActiveClass();
|
||
|
if ((packetToSend == NULL) ||
|
||
|
(activeClass != COMMAND_CLASS_HID_KEYBOARD) ||
|
||
|
(dataLength != HID_KEYBOARD_OUTPUT_DATA_LEN))
|
||
|
{
|
||
|
UPSTREAM_STATEMACHINE_FREAKOUT;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Save data until after the next interrupt data is received from Downstream
|
||
|
KeyboardOutDataState = KEYBOARD_OUT_STATE_DATA_READY;
|
||
|
for (i = 0; i < HID_KEYBOARD_OUTPUT_DATA_LEN; i++)
|
||
|
{
|
||
|
KeyboardOutData[i] = packetToSend->Data[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void Upstream_HID_SendControlReport(void)
|
||
|
{
|
||
|
UpstreamPacketTypeDef* freePacket;
|
||
|
uint32_t i;
|
||
|
|
||
|
KeyboardOutDataState = KEYBOARD_OUT_STATE_BUSY;
|
||
|
|
||
|
freePacket = Upstream_GetFreePacketImmediately();
|
||
|
if (freePacket == NULL) return;
|
||
|
|
||
|
freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16 + ((HID_KEYBOARD_OUTPUT_DATA_LEN + 1) / 2);
|
||
|
freePacket->CommandClass = COMMAND_CLASS_HID_KEYBOARD;
|
||
|
freePacket->Command = COMMAND_HID_SET_REPORT;
|
||
|
|
||
|
for (i = 0; i < HID_KEYBOARD_OUTPUT_DATA_LEN; i++)
|
||
|
{
|
||
|
freePacket->Data[i] = KeyboardOutData[i];
|
||
|
}
|
||
|
freePacket->Data[0] &= ((1 << HID_KEYBOARD_MAX_LED) - 1);
|
||
|
|
||
|
if (Upstream_TransmitPacket(freePacket) == HAL_OK)
|
||
|
{
|
||
|
Upstream_ReceivePacket(Upstream_HID_SendControlReportCallback);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Upstream_ReleasePacket(freePacket);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void Upstream_HID_SendControlReportCallback(UpstreamPacketTypeDef* receivedPacket)
|
||
|
{
|
||
|
InterfaceCommandClassTypeDef activeClass;
|
||
|
|
||
|
activeClass = Upstream_StateMachine_CheckActiveClass();
|
||
|
if (activeClass != COMMAND_CLASS_HID_KEYBOARD) //add classes here
|
||
|
{
|
||
|
UPSTREAM_STATEMACHINE_FREAKOUT;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (receivedPacket == NULL)
|
||
|
{
|
||
|
return; //Just give up...
|
||
|
}
|
||
|
|
||
|
Upstream_ReleasePacket(receivedPacket);
|
||
|
KeyboardOutDataState = KEYBOARD_OUT_STATE_IDLE;
|
||
|
Upstream_HID_ReceiveInterruptReport();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#endif //#if defined (CONFIG_KEYBOARD_ENABLED) || defined (CONFIG_MOUSE_ENABLED)
|
||
|
|