From fd14db8ccb3442ca9f93dcece8b201ba6c7d5bdf Mon Sep 17 00:00:00 2001 From: Robert Fisk Date: Thu, 28 Apr 2016 09:28:33 +1200 Subject: [PATCH] Implemented HID report parser. Untested! --- Downstream/Inc/downstream_hid.h | 22 +- Downstream/Inc/downstream_spi.h | 1 - Downstream/Inc/usbh_config.h | 2 +- .../Class/HID/Inc/usbh_hid.h | 10 +- .../Class/HID/Src/usbh_hid.c | 49 ++-- .../Core/Src/usbh_ctlreq.c | 11 + Downstream/Src/downstream_hid.c | 240 +++++++++++++++++- Downstream/Src/downstream_msc.c | 2 +- 8 files changed, 296 insertions(+), 41 deletions(-) diff --git a/Downstream/Inc/downstream_hid.h b/Downstream/Inc/downstream_hid.h index e3c3bbd..033392e 100644 --- a/Downstream/Inc/downstream_hid.h +++ b/Downstream/Inc/downstream_hid.h @@ -18,10 +18,30 @@ +#define HID_ITEM_LONG 0xFE +#define HID_ITEM_LENGTH_MASK 0x03 + +#define HID_ITEM_USAGE_PAGE 0x05 //'global' usage page +#define HID_ITEM_USAGE_PAGE_BUTTON 0x09 +#define HID_ITEM_USAGE_PAGE_DESKTOP 0x01 + +#define HID_ITEM_USAGE 0x09 //'local' usage +#define HID_ITEM_USAGE_X 0x30 +#define HID_ITEM_USAGE_Y 0x31 +#define HID_ITEM_USAGE_WHEEL 0x38 + +#define HID_ITEM_REPORT_SIZE 0x75 //'global' report size +#define HID_ITEM_REPORT_COUNT 0x95 //'global' report count + +#define HID_ITEM_INPUT 0x81 //'main' input +#define HID_ITEM_INPUT_ABS 0x02 +#define HID_ITEM_INPUT_REL 0x06 + + InterfaceCommandClassTypeDef Downstream_HID_ApproveConnectedDevice(void); void Downstream_HID_PacketProcessor(DownstreamPacketTypeDef* receivedPacket); -void Downstream_HID_InterruptReportCallback(DownstreamPacketTypeDef* packetToSend); +void Downstream_HID_InterruptReportCallback(uint8_t* reportBuffer); diff --git a/Downstream/Inc/downstream_spi.h b/Downstream/Inc/downstream_spi.h index 6f59a6f..f8941a1 100644 --- a/Downstream/Inc/downstream_spi.h +++ b/Downstream/Inc/downstream_spi.h @@ -71,7 +71,6 @@ typedef void (*SpiPacketReceivedCallbackTypeDef)(DownstreamPacketTypeDef* receiv void Downstream_InitSPI(void); HAL_StatusTypeDef Downstream_GetFreePacket(FreePacketCallbackTypeDef callback); -DownstreamPacketTypeDef* Downstream_GetFreePacketImmediately(void); void Downstream_ReleasePacket(DownstreamPacketTypeDef* packetToRelease); HAL_StatusTypeDef Downstream_ReceivePacket(SpiPacketReceivedCallbackTypeDef callback); HAL_StatusTypeDef Downstream_TransmitPacket(DownstreamPacketTypeDef* packetToWrite); diff --git a/Downstream/Inc/usbh_config.h b/Downstream/Inc/usbh_config.h index 9552859..c9d4812 100644 --- a/Downstream/Inc/usbh_config.h +++ b/Downstream/Inc/usbh_config.h @@ -59,7 +59,7 @@ #define USBH_MAX_NUM_CONFIGURATION 1 /*---------- -----------*/ -#define USBH_KEEP_CFG_DESCRIPTOR 1 +#define USBH_KEEP_CFG_DESCRIPTOR 0 /*---------- -----------*/ #define USBH_MAX_NUM_SUPPORTED_CLASS 2 diff --git a/Downstream/Middlewares/ST/STM32_USB_Host_Library/Class/HID/Inc/usbh_hid.h b/Downstream/Middlewares/ST/STM32_USB_Host_Library/Class/HID/Inc/usbh_hid.h index a611ace..3c630a3 100644 --- a/Downstream/Middlewares/ST/STM32_USB_Host_Library/Class/HID/Inc/usbh_hid.h +++ b/Downstream/Middlewares/ST/STM32_USB_Host_Library/Class/HID/Inc/usbh_hid.h @@ -61,7 +61,7 @@ */ #define HID_MIN_POLL 10 -#define HID_REPORT_SIZE 16 +#define HID_MAX_REPORT_SIZE 8 #define HID_MAX_USAGE 10 #define HID_MAX_NBR_REPORT_FMT 10 #define HID_QUEUE_SIZE 10 @@ -211,7 +211,7 @@ typedef struct -typedef void (*HID_InterruptReportCallback)(DownstreamPacketTypeDef* packetToSend); +typedef void (*HID_InterruptReportCallback)(uint8_t* reportBuffer); /* Structure for HID process */ @@ -230,8 +230,7 @@ typedef struct _HID_Process uint8_t Protocol; HID_DescTypeDef HID_Desc; HID_InterruptReportCallback ReportCallback; - DownstreamPacketTypeDef* hid_packet; - uint8_t* hid_packet_pbuf; + uint8_t Data[HID_MAX_REPORT_SIZE]; } HID_HandleTypeDef; @@ -325,8 +324,7 @@ uint16_t fifo_read(FIFO_TypeDef * f, void * buf, uint16_t nbytes); uint16_t fifo_write(FIFO_TypeDef * f, const void * buf, uint16_t nbytes); HAL_StatusTypeDef USBH_HID_GetInterruptReport(USBH_HandleTypeDef *phost, - HID_InterruptReportCallback callback, - DownstreamPacketTypeDef* packetToUse); + HID_InterruptReportCallback callback); /** diff --git a/Downstream/Middlewares/ST/STM32_USB_Host_Library/Class/HID/Src/usbh_hid.c b/Downstream/Middlewares/ST/STM32_USB_Host_Library/Class/HID/Src/usbh_hid.c index d6a4867..87fd1df 100644 --- a/Downstream/Middlewares/ST/STM32_USB_Host_Library/Class/HID/Src/usbh_hid.c +++ b/Downstream/Middlewares/ST/STM32_USB_Host_Library/Class/HID/Src/usbh_hid.c @@ -184,6 +184,11 @@ static USBH_StatusTypeDef USBH_HID_InterfaceInit (USBH_HandleTypeDef *phost) HID_Handle->poll = HID_MIN_POLL; } + if (HID_Handle->length > HID_MAX_REPORT_SIZE) + { + return USBH_FAIL; + } + /* Check fo available number of endpoints */ /* Find the number of EPs in the Interface Descriptor */ /* Choose the lower number in order not to overrun the buffer allocated */ @@ -294,25 +299,20 @@ static USBH_StatusTypeDef USBH_HID_ClassRequest(USBH_HandleTypeDef *phost) { USBH_HID_ParseHIDDesc(&HID_Handle->HID_Desc, phost->device.Data); - HID_Handle->ctl_state = HID_REQ_SET_IDLE; + HID_Handle->ctl_state = HID_REQ_GET_REPORT_DESC; } break; -// case HID_REQ_GET_REPORT_DESC: -// -// -// /* Get Report Desc */ -// if (USBH_HID_GetHIDReportDescriptor(phost, HID_Handle->HID_Desc.wItemLength) == USBH_OK) -// { -// /* The descriptor is available in phost->device.Data */ -// -// HID_Handle->ctl_state = HID_REQ_SET_IDLE; -// } -// -// break; + case HID_REQ_GET_REPORT_DESC: + /* Get Report Desc */ + if (USBH_HID_GetHIDReportDescriptor(phost, HID_Handle->HID_Desc.wItemLength) == USBH_OK) + { + /* The descriptor is available in phost->device.Data */ + HID_Handle->ctl_state = HID_REQ_SET_IDLE; + } + break; case HID_REQ_SET_IDLE: - classReqStatus = USBH_HID_SetIdle (phost, 0, 0); /* set Idle */ @@ -328,7 +328,7 @@ static USBH_StatusTypeDef USBH_HID_ClassRequest(USBH_HandleTypeDef *phost) case HID_REQ_SET_PROTOCOL: /* set protocol */ - if (USBH_HID_SetProtocol (phost, 0) == USBH_OK) + if (USBH_HID_SetProtocol (phost, 1) == USBH_OK) { HID_Handle->ctl_state = HID_REQ_IDLE; @@ -368,7 +368,7 @@ static USBH_StatusTypeDef USBH_HID_Process(USBH_HandleTypeDef *phost) if ((int32_t)(phost->Timer - HID_Handle->timer) >= HID_Handle->poll) { USBH_InterruptReceiveData(phost, - HID_Handle->hid_packet_pbuf, + HID_Handle->Data, HID_Handle->length, HID_Handle->InPipe); @@ -382,7 +382,7 @@ static USBH_StatusTypeDef USBH_HID_Process(USBH_HandleTypeDef *phost) if (urbStatus == USBH_URB_DONE) { - HID_Handle->ReportCallback(HID_Handle->hid_packet); + HID_Handle->ReportCallback(HID_Handle->Data); HID_Handle->state = HID_IDLE; break; } @@ -431,16 +431,13 @@ static USBH_StatusTypeDef USBH_HID_SOFProcess(USBH_HandleTypeDef *phost) //Downstream_HID calls into here at main() priority, //to request a new report for Upstream. HAL_StatusTypeDef USBH_HID_GetInterruptReport(USBH_HandleTypeDef *phost, - HID_InterruptReportCallback callback, - DownstreamPacketTypeDef* packetToUse) + HID_InterruptReportCallback callback) { HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData; if (HID_Handle->state == HID_IDLE) { HID_Handle->ReportCallback = callback; - HID_Handle->hid_packet = packetToUse; - HID_Handle->hid_packet_pbuf = packetToUse->Data; HID_Handle->state = HID_GET_DATA; return HAL_OK; } @@ -465,6 +462,11 @@ USBH_StatusTypeDef USBH_HID_GetHIDReportDescriptor (USBH_HandleTypeDef *phost, USBH_StatusTypeDef status; + if (length > USBH_MAX_DATA_BUFFER) + { + length = USBH_MAX_DATA_BUFFER; + } + status = USBH_GetDescriptor(phost, USB_REQ_RECIPIENT_INTERFACE | USB_REQ_TYPE_STANDARD, USB_DESC_HID_REPORT, @@ -495,6 +497,11 @@ USBH_StatusTypeDef USBH_HID_GetHIDDescriptor (USBH_HandleTypeDef *phost, USBH_StatusTypeDef status; + if (length > USBH_MAX_DATA_BUFFER) + { + length = USBH_MAX_DATA_BUFFER; + } + status = USBH_GetDescriptor( phost, USB_REQ_RECIPIENT_INTERFACE | USB_REQ_TYPE_STANDARD, USB_DESC_HID, diff --git a/Downstream/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_ctlreq.c b/Downstream/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_ctlreq.c index b853762..0130e34 100644 --- a/Downstream/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_ctlreq.c +++ b/Downstream/Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_ctlreq.c @@ -116,6 +116,11 @@ USBH_StatusTypeDef USBH_Get_DevDesc(USBH_HandleTypeDef *phost, uint8_t length) { USBH_StatusTypeDef status; + if (length > USBH_MAX_DATA_BUFFER) + { + length = USBH_MAX_DATA_BUFFER; + } + if((status = USBH_GetDescriptor(phost, USB_REQ_RECIPIENT_DEVICE | USB_REQ_TYPE_STANDARD, USB_DESC_DEVICE, @@ -191,6 +196,12 @@ USBH_StatusTypeDef USBH_Get_StringDesc(USBH_HandleTypeDef *phost, uint16_t length) { USBH_StatusTypeDef status; + + if (length > USBH_MAX_DATA_BUFFER) + { + length = USBH_MAX_DATA_BUFFER; + } + if((status = USBH_GetDescriptor(phost, USB_REQ_RECIPIENT_DEVICE | USB_REQ_TYPE_STANDARD, USB_DESC_STRING | string_index, diff --git a/Downstream/Src/downstream_hid.c b/Downstream/Src/downstream_hid.c index f49c874..79c2f9c 100644 --- a/Downstream/Src/downstream_hid.c +++ b/Downstream/Src/downstream_hid.c @@ -14,32 +14,222 @@ #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 3 +#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) + if (HID_Handle->Protocol != HID_MOUSE_BOOT_CODE) { - if ((HID_Handle->length >= HID_MOUSE_DATA_LEN) && (HID_Handle->length <= HID_MAX_REPORT_LEN)) + 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) { - return COMMAND_CLASS_HID_MOUSE; + case HID_ITEM_USAGE_PAGE: + currentUsagePage = ItemData; + 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 (xUsageIndex != 0xFF) + { + ReportWheelBitOffset = currentReportBitIndex + (currentReportSize * wheelUsageIndex); + ReportWheelBitLength = currentReportSize; + } + } + break; + } + currentReportBitIndex += (currentReportSize * currentReportCount); + if (currentReportBitIndex < (HID_MAX_REPORT_LEN * 8)) + { + return HAL_ERROR; + } + break; } } - return COMMAND_CLASS_INTERFACE; //fail + //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; } @@ -52,9 +242,10 @@ void Downstream_HID_PacketProcessor(DownstreamPacketTypeDef* receivedPacket) return; } + SavedPacket = receivedPacket; + if (USBH_HID_GetInterruptReport(&hUsbHostFS, - Downstream_HID_InterruptReportCallback, - receivedPacket) != HAL_OK) //Don't free the packet, USBH_HID will use it then return it to InterruptReportCallback below + Downstream_HID_InterruptReportCallback) != HAL_OK) { Downstream_PacketProcessor_FreakOut(); } @@ -62,11 +253,12 @@ void Downstream_HID_PacketProcessor(DownstreamPacketTypeDef* receivedPacket) } -void Downstream_HID_InterruptReportCallback(DownstreamPacketTypeDef* packetToSend) +void Downstream_HID_InterruptReportCallback(uint8_t* reportBuffer) { if (ConfiguredDeviceClass == COMMAND_CLASS_HID_MOUSE) { - packetToSend->Length16 = ((HID_MOUSE_DATA_LEN + 1) / 2) + DOWNSTREAM_PACKET_HEADER_LEN_16; + Downstream_HID_Mouse_ExtractDataFromReport(reportBuffer); + SavedPacket->Length16 = ((HID_MOUSE_DATA_LEN + 1) / 2) + DOWNSTREAM_PACKET_HEADER_LEN_16; } //else if... else @@ -75,7 +267,35 @@ void Downstream_HID_InterruptReportCallback(DownstreamPacketTypeDef* packetToSen return; } - Downstream_PacketProcessor_ClassReply(packetToSend); + 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; } diff --git a/Downstream/Src/downstream_msc.c b/Downstream/Src/downstream_msc.c index 23ef28f..8e81eb3 100644 --- a/Downstream/Src/downstream_msc.c +++ b/Downstream/Src/downstream_msc.c @@ -57,7 +57,7 @@ InterfaceCommandClassTypeDef Downstream_MSC_ApproveConnectedDevice(void) return COMMAND_CLASS_INTERFACE; //fail } - return COMMAND_CLASS_MASS_STORAGE; + return COMMAND_CLASS_MASS_STORAGE; //success! }