/* * downstream_hid.c * * Created on: Apr 10, 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 "downstream_hid.h" #include "downstream_statemachine.h" #include "usbh_hid.h" #include "stm32f4xx_hal.h" #define HID_MAX_REPORT_LEN 8 #define HID_MOUSE_DATA_LEN 4 #define HID_KEYBOARD_DATA_LEN 0 #define HID_MOUSE_MAX_BUTTONS 4 DownstreamPacketTypeDef* SavedPacket; extern USBH_HandleTypeDef hUsbHostFS; //Hard-link ourselves to usb_host.c extern InterfaceCommandClassTypeDef ConfiguredDeviceClass; //Do a cheap hard-link to downstream_statemachine.c, rather than keep a duplicate here //Information required to extract the data we need from incoming device reports. uint8_t ReportButtonBitOffset; uint8_t ReportButtonBitLength; uint8_t ReportXBitOffset; uint8_t ReportXBitLength; uint8_t ReportYBitOffset; uint8_t ReportYBitLength; uint8_t ReportWheelBitOffset; uint8_t ReportWheelBitLength; //Stuff used while parsing HID report uint8_t* ReportDataPointer; uint8_t ItemHeader; uint8_t ItemData; static HAL_StatusTypeDef Downstream_HID_Mouse_ParseReportDescriptor(void); static HAL_StatusTypeDef Downstream_HID_GetNextReportItem(void); static void Downstream_HID_Mouse_ExtractDataFromReport(uint8_t* reportBuffer); InterfaceCommandClassTypeDef Downstream_HID_ApproveConnectedDevice(void) { HID_HandleTypeDef* HID_Handle = (HID_HandleTypeDef*)hUsbHostFS.pActiveClass->pData; if (HID_Handle->Protocol != HID_MOUSE_BOOT_CODE) { return COMMAND_CLASS_INTERFACE; //fail } if (Downstream_HID_Mouse_ParseReportDescriptor() != HAL_OK) { return COMMAND_CLASS_INTERFACE; //fail } return COMMAND_CLASS_HID_MOUSE; //success! } HAL_StatusTypeDef Downstream_HID_Mouse_ParseReportDescriptor(void) { uint32_t currentReportBitIndex = 0; uint8_t currentUsagePage = 0; uint8_t currentUsageIndex = 0; uint8_t xUsageIndex = 0xFF; uint8_t yUsageIndex = 0xFF; uint8_t wheelUsageIndex = 0xFF; uint8_t currentReportSize = 0; uint8_t currentReportCount = 0; ReportDataPointer = hUsbHostFS.device.Data; ReportButtonBitLength = 0; ReportXBitLength = 0; ReportYBitLength = 0; ReportWheelBitLength = 0; while ((Downstream_HID_GetNextReportItem() == HAL_OK) && ((ReportButtonBitLength == 0) || (ReportXBitLength == 0) || (ReportYBitLength == 0) || (ReportWheelBitLength == 0))) { switch (ItemHeader) { case HID_ITEM_USAGE_PAGE: currentUsagePage = ItemData; currentUsageIndex = 0; xUsageIndex = 0xFF; yUsageIndex = 0xFF; wheelUsageIndex = 0xFF; break; case HID_ITEM_COLLECTION: //Physical collections also clear the usage index... if (ItemData == HID_ITEM_COLLECTION_PHYS) { currentUsageIndex = 0; xUsageIndex = 0xFF; yUsageIndex = 0xFF; wheelUsageIndex = 0xFF; } break; case HID_ITEM_END_COLLECTION: //...and so do collection ends currentUsageIndex = 0; xUsageIndex = 0xFF; yUsageIndex = 0xFF; wheelUsageIndex = 0xFF; break; case HID_ITEM_USAGE: switch (ItemData) { case HID_ITEM_USAGE_X: xUsageIndex = currentUsageIndex; break; case HID_ITEM_USAGE_Y: yUsageIndex = currentUsageIndex; break; case HID_ITEM_USAGE_WHEEL: wheelUsageIndex = currentUsageIndex; break; } currentUsageIndex++; break; case HID_ITEM_REPORT_SIZE: currentReportSize = ItemData; break; case HID_ITEM_REPORT_COUNT: currentReportCount = ItemData; break; case HID_ITEM_INPUT: switch (currentUsagePage) { case HID_ITEM_USAGE_PAGE_BUTTON: if (ItemData == HID_ITEM_INPUT_ABS) { //Buttons found! if (currentReportSize != 1) { return HAL_ERROR; } ReportButtonBitOffset = currentReportBitIndex; ReportButtonBitLength = currentReportCount; if (ReportButtonBitLength > HID_MOUSE_MAX_BUTTONS) { ReportButtonBitLength = HID_MOUSE_MAX_BUTTONS; } } break; case HID_ITEM_USAGE_PAGE_DESKTOP: if (ItemData == HID_ITEM_INPUT_REL) { //Movement data found! if ((currentReportSize < 8) || (currentReportSize > 16)) { return HAL_ERROR; } if (xUsageIndex != 0xFF) { ReportXBitOffset = currentReportBitIndex + (currentReportSize * xUsageIndex); ReportXBitLength = currentReportSize; } if (yUsageIndex != 0xFF) { ReportYBitOffset = currentReportBitIndex + (currentReportSize * yUsageIndex); ReportYBitLength = currentReportSize; } if (wheelUsageIndex != 0xFF) { ReportWheelBitOffset = currentReportBitIndex + (currentReportSize * wheelUsageIndex); ReportWheelBitLength = currentReportSize; } } break; } currentReportBitIndex += (currentReportSize * currentReportCount); if (currentReportBitIndex >= (HID_MAX_REPORT_LEN * 8)) { return HAL_ERROR; } break; } } //We don't mind if we didn't find a scrollwheel item. if ((ReportButtonBitLength == 0) || (ReportXBitLength == 0) || (ReportYBitLength == 0)) { return HAL_ERROR; } return HAL_OK; } //Retrieves the next item in the HID report, and at most one of its associated data bytes. //Then it updates ReportDataPointer based on the actual length of the retrieved item. HAL_StatusTypeDef Downstream_HID_GetNextReportItem(void) { HID_HandleTypeDef* HID_Handle = (HID_HandleTypeDef*)hUsbHostFS.pActiveClass->pData; uint32_t itemLength; if ((ReportDataPointer >= &hUsbHostFS.device.Data[USBH_MAX_DATA_BUFFER]) || ((ReportDataPointer - &hUsbHostFS.device.Data[0]) >= HID_Handle->HID_Desc.wItemLength)) { return HAL_ERROR; } ItemHeader = *ReportDataPointer++; itemLength = ItemHeader & HID_ITEM_LENGTH_MASK; if (ItemHeader == HID_ITEM_LONG) { itemLength += *ReportDataPointer; //Long items have another length byte } else { if (itemLength > 0) { ItemData = *ReportDataPointer; //If it is a short item, grab its first data byte } } ReportDataPointer += itemLength; return HAL_OK; } void Downstream_HID_PacketProcessor(DownstreamPacketTypeDef* receivedPacket) { if (receivedPacket->Command != COMMAND_HID_REPORT) { Downstream_PacketProcessor_FreakOut(); return; } SavedPacket = receivedPacket; if (USBH_HID_GetInterruptReport(&hUsbHostFS, Downstream_HID_InterruptReportCallback) != HAL_OK) { Downstream_PacketProcessor_FreakOut(); } } void Downstream_HID_InterruptReportCallback(uint8_t* reportBuffer) { if (ConfiguredDeviceClass == COMMAND_CLASS_HID_MOUSE) { Downstream_HID_Mouse_ExtractDataFromReport(reportBuffer); SavedPacket->Length16 = ((HID_MOUSE_DATA_LEN + 1) / 2) + DOWNSTREAM_PACKET_HEADER_LEN_16; } //else if... else { Downstream_PacketProcessor_FreakOut(); return; } Downstream_PacketProcessor_ClassReply(SavedPacket); } void Downstream_HID_Mouse_ExtractDataFromReport(uint8_t* reportBuffer) { uint16_t readData; //Grab the buttons readData = *(uint16_t*)&(reportBuffer[(ReportButtonBitOffset / 8)]); readData >>= (ReportButtonBitOffset % 8); readData &= ((1 << ReportButtonBitLength) - 1); SavedPacket->Data[0] = readData; //Grab X readData = *(uint16_t*)&(reportBuffer[(ReportXBitOffset / 8)]); readData >>= (ReportXBitOffset % 8); SavedPacket->Data[1] = (uint8_t)readData; //Grab Y readData = *(uint16_t*)&(reportBuffer[(ReportYBitOffset / 8)]); readData >>= (ReportYBitOffset % 8); SavedPacket->Data[2] = (uint8_t)readData; //Grab wheel readData = *(uint16_t*)&(reportBuffer[(ReportWheelBitOffset / 8)]); readData >>= (ReportWheelBitOffset % 8); SavedPacket->Data[3] = (uint8_t)readData; }