From a8ba18094f06d7635f8abb60dce345bd35ea023d Mon Sep 17 00:00:00 2001 From: mbanth Date: Tue, 18 May 2021 16:48:54 +0100 Subject: [PATCH] Initial implementation, compiles w/o error, not tested --- lib_xua/src/hid/hid_report_descriptor.c | 334 ++++++++++++++++++++ lib_xua/src/hid/xua_hid_report_descriptor.h | 83 +++++ 2 files changed, 417 insertions(+) create mode 100644 lib_xua/src/hid/hid_report_descriptor.c create mode 100644 lib_xua/src/hid/xua_hid_report_descriptor.h diff --git a/lib_xua/src/hid/hid_report_descriptor.c b/lib_xua/src/hid/hid_report_descriptor.c new file mode 100644 index 00000000..76983e89 --- /dev/null +++ b/lib_xua/src/hid/hid_report_descriptor.c @@ -0,0 +1,334 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include +#include +#include "xua_hid_report_descriptor.h" + +#define HID_REPORT_ITEM_HDR_SIZE_MASK ( 0x03 ) +#define HID_REPORT_ITEM_HDR_SIZE_SHIFT ( 0 ) + +#define HID_REPORT_ITEM_HDR_TAG_MASK ( 0xF0 ) +#define HID_REPORT_ITEM_HDR_TAG_SHIFT ( 4 ) + +#define HID_REPORT_ITEM_HDR_TYPE_MASK ( 0x0C ) +#define HID_REPORT_ITEM_HDR_TYPE_SHIFT ( 2 ) + +#define HID_REPORT_ITEM_LOC_BIT_MASK ( 0x70 ) +#define HID_REPORT_ITEM_LOC_BIT_SHIFT ( 4 ) + +#define HID_REPORT_ITEM_LOC_BYTE_MASK ( 0x0F ) +#define HID_REPORT_ITEM_LOC_BYTE_SHIFT ( 0 ) + +#define HID_REPORT_ITEM_USAGE_TAG ( 0 ) +#define HID_REPORT_ITEM_USAGE_TYPE ( 2 ) + +#if 0 +/* Existing static report descriptor kept for reference */ +unsigned char hidReportDescriptor[] = +{ + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x06, /* Usage (Keyboard) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x04, /* Report Count (4) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x00, /* Logical Maximum (0) */ + 0x81, 0x01, /* Input (Cnst, Ary, Abs, No Wrap, Lin, Pref, No Nul) */ + 0x95, 0x01, /* Report Count (1) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x05, 0x07, /* Usage Page (Key Codes) */ + 0x19, 0x17, /* Usage Minimum (Keyboard t or T) */ + 0x29, 0x17, /* Usage Maximum (Keyboard t or T) */ + 0x81, 0x02, /* Input (Data, Var, Abs, No Wrap, Lin, Pref, No Nul) */ + 0x05, 0x0C, /* Usage Page (Consumer) */ + 0x0a, 0x26, 0x02, /* Usage (AC Stop) */ + 0x81, 0x02, /* Input (Data, Var, Abs, No Wrap, Lin, Pref, No Nul) */ + 0x95, 0x02, /* Report Count (2) */ + 0x05, 0x07, /* Usage Page (Key Codes) */ + 0x19, 0x72, /* Usage Minimum (Keyboard F23) */ + 0x29, 0x73, /* Usage Maximum (Keyboard F24) */ + 0x81, 0x02, /* Input (Data, Var, Abs, No Wrap, Lin, Pref, No Nul) */ + 0xc0 /* End collection (Application) */ +}; +#endif + +static const USB_HID_Short_Item_t hidCollectionApplication = { .header = 0xA1, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidCollectionEnd = { .header = 0xC0, .data = { 0x00, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidCollectionLogical = { .header = 0xA1, .data = { 0x02, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidInputConstArray = { .header = 0x81, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidInputDataVar = { .header = 0x81, .data = { 0x02, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidLogicalMaximum0 = { .header = 0x25, .data = { 0x00, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMaximum1 = { .header = 0x25, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMinimum0 = { .header = 0x15, .data = { 0x00, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidReportCount1 = { .header = 0x95, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportCount4 = { .header = 0x95, .data = { 0x04, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportCount6 = { .header = 0x95, .data = { 0x06, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportSize1 = { .header = 0x75, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsageConsumerControl = { .header = 0x09, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsagePageConsumer = { .header = 0x05, .data = { 0x0C, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidUsagePageKeyboard = { .header = 0x05, .data = { 0x07, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidUsagePageTelephony = { .header = 0x05, .data = { 0x0B, 0x00 }, .location = 0x00 }; + +static USB_HID_Short_Item_t hidUsageByte0Bit3 = { .header = 0x09, .data = { 0x73, 0x00 }, .location = 0x30 }; // F24 +static USB_HID_Short_Item_t hidUsageByte0Bit2 = { .header = 0x09, .data = { 0x72, 0x00 }, .location = 0x20 }; // F23 +static USB_HID_Short_Item_t hidUsageByte0Bit0 = { .header = 0x09, .data = { 0x17, 0x00 }, .location = 0x00 }; // 't' + +static USB_HID_Short_Item_t hidUsageByte1Bit7 = { .header = 0x09, .data = { 0xEA, 0x00 }, .location = 0x71 }; // Vol- +static USB_HID_Short_Item_t hidUsageByte1Bit6 = { .header = 0x09, .data = { 0xE9, 0x00 }, .location = 0x61 }; // Vol+ +static USB_HID_Short_Item_t hidUsageByte1Bit4 = { .header = 0x09, .data = { 0x00, 0x00 }, .location = 0x41 }; // Voice Command +static USB_HID_Short_Item_t hidUsageByte1Bit2 = { .header = 0x09, .data = { 0xE2, 0x00 }, .location = 0x21 }; // Mute +static USB_HID_Short_Item_t hidUsageByte1Bit1 = { .header = 0x09, .data = { 0x00, 0x00 }, .location = 0x11 }; // AC Search +static USB_HID_Short_Item_t hidUsageByte1Bit0 = { .header = 0x09, .data = { 0x00, 0x00 }, .location = 0x01 }; // AC Stop + +static USB_HID_Short_Item_t hidUsageByte2Bit1 = { .header = 0x09, .data = { 0x2F, 0x00 }, .location = 0x12 }; // Phone Mute +static USB_HID_Short_Item_t hidUsageByte2Bit0 = { .header = 0x09, .data = { 0x20, 0x00 }, .location = 0x02 }; // Hook Switch + +static USB_HID_Short_Item_t* const hidConfigurableItems[] = { + &hidUsageByte0Bit0, + &hidUsageByte0Bit2, + &hidUsageByte0Bit3, + &hidUsageByte1Bit0, + &hidUsageByte1Bit1, + &hidUsageByte1Bit2, + &hidUsageByte1Bit4, + &hidUsageByte1Bit6, + &hidUsageByte1Bit7, + &hidUsageByte2Bit0, + &hidUsageByte2Bit1 +}; + +static const USB_HID_Short_Item_t* const hidReportDescriptorItems[] = { + &hidUsagePageConsumer, + &hidUsageConsumerControl, + &hidCollectionApplication, + &hidReportSize1, + &hidLogicalMinimum0, + &hidCollectionLogical, // Byte 0 + &hidUsagePageKeyboard, + &hidLogicalMaximum1, + &hidReportCount1, + &hidUsageByte0Bit0, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidInputConstArray, + &hidLogicalMaximum1, + &hidUsageByte0Bit2, + &hidInputDataVar, + &hidUsageByte0Bit3, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount4, + &hidInputConstArray, + &hidCollectionEnd, + &hidCollectionLogical, // Byte 1 + &hidUsagePageConsumer, + &hidLogicalMaximum1, + &hidReportCount1, + &hidUsageByte1Bit0, + &hidInputDataVar, + &hidUsageByte1Bit1, + &hidInputDataVar, + &hidUsageByte1Bit2, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidInputConstArray, + &hidLogicalMaximum1, + &hidUsageByte1Bit4, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidInputConstArray, + &hidLogicalMaximum1, + &hidUsageByte1Bit6, + &hidInputDataVar, + &hidUsageByte1Bit7, + &hidInputDataVar, + &hidCollectionEnd, + &hidCollectionLogical, // Byte 2 + &hidUsagePageTelephony, + &hidUsageByte2Bit0, + &hidInputDataVar, + &hidUsageByte2Bit1, + &hidLogicalMaximum0, + &hidReportCount6, + &hidInputConstArray, + &hidCollectionEnd, + &hidCollectionEnd +}; + +#define HID_REPORT_ITEM_LOCATION_SIZE ( 1 ) +#define HID_REPORT_DESCRIPTOR_MAX_LENGTH ( sizeof hidReportDescriptorItems / sizeof ( USB_HID_Short_Item_t ) * \ + ( sizeof ( USB_HID_Short_Item_t ) - HID_REPORT_ITEM_LOCATION_SIZE )) + +static unsigned char hidReportDescriptor[ HID_REPORT_DESCRIPTOR_MAX_LENGTH ]; +static unsigned hidReportDescriptorInitialised = 0; + +/** + * @brief Get the bit position from the location of an Item + * + * Parameters: + * + * @param[in] location The \c location field from a \c USB_HID_Short_Item + * + * @return The bit position of the Item + */ +static unsigned hidGetItemBitLocation( const unsigned char header ); + +/** + * @brief Get the byte position from the location of an Item + * + * Parameters: + * + * @param[in] location The \c location field from a \c USB_HID_Short_Item + * + * @return The byte position of the Item within the HID Report + */ +static unsigned hidGetItemByteLocation( const unsigned char header ); + +/** + * @brief Get the number of data bytes from the header of an Item + * + * Parameters: + * + * @param[in] header The \c header field from a \c USB_HID_Short_Item + * + * @return The amount of data for the Item + */ +static unsigned hidGetItemSize( const unsigned char header ); + +/** + * @brief Get the Tag from the header of an Item + * + * Parameters: + * + * @param[in] header The \c header field from a \c USB_HID_Short_Item + * + * @return The Tag of the Item + */ +static unsigned hidGetItemTag( const unsigned char header ); + +/** + * @brief Get the Type from the header of an Item + * + * Parameters: + * + * @param[in] header The \c header field from a \c USB_HID_Short_Item + * + * @return The Type of the Item + */ +static unsigned hidGetItemType( const unsigned char header ); + +/** + * @brief Translate an Item from the \c USB_HID_Short_Item format to raw bytes + * + * Parameters: + * + * @param[in] inPtr A pointer to a \c USB_HID_Short_Item + * @param[in,out] outPtr A pointer to the next available space in the raw byte buffer + * + * @return The updated \a outPtr + */ +static unsigned char* hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char* outPtr ); + +static unsigned hidGetItemBitLocation( const unsigned char location ) +{ + unsigned bBit = ( location & HID_REPORT_ITEM_LOC_BIT_MASK ) >> HID_REPORT_ITEM_LOC_BIT_SHIFT; + return bBit; +} + +static unsigned hidGetItemByteLocation( const unsigned char location ) +{ + unsigned bByte = ( location & HID_REPORT_ITEM_LOC_BYTE_MASK ) >> HID_REPORT_ITEM_LOC_BYTE_SHIFT; + return bByte; +} + +static unsigned hidGetItemSize( const unsigned char header ) +{ + unsigned bSize = ( header & HID_REPORT_ITEM_HDR_SIZE_MASK ) >> HID_REPORT_ITEM_HDR_SIZE_SHIFT; + assert( bSize <= HID_REPORT_ITEM_MAX_SIZE ); + return bSize; +} + +static unsigned hidGetItemTag( const unsigned char header ) +{ + unsigned bTag = ( header & HID_REPORT_ITEM_HDR_TAG_MASK ) >> HID_REPORT_ITEM_HDR_TAG_SHIFT; + return bTag; +} + +static unsigned hidGetItemType( const unsigned char header ) +{ + unsigned bType = ( header & HID_REPORT_ITEM_HDR_TYPE_MASK ) >> HID_REPORT_ITEM_HDR_TYPE_SHIFT; + return bType; +} + +unsigned char* hidGetReportDescriptor( void ) +{ + unsigned char* retVal = NULL; + + if( hidReportDescriptorInitialised ) { + retVal = hidReportDescriptor; + } + + return retVal; +} + +void hidInitReportDescriptor( void ) +{ + unsigned char* ptr = hidReportDescriptor; + for( unsigned idx = 0; idx < sizeof hidReportDescriptorItems / sizeof( USB_HID_Short_Item_t ); ++idx ) { + ptr = hidTranslateItem( hidReportDescriptorItems[ idx ], ptr ); + } + + hidReportDescriptorInitialised = 1; +} + +unsigned hidSetReportItem( const unsigned byte, const unsigned bit, const unsigned char header, const unsigned char data[] ) +{ + unsigned retVal = 2; + unsigned bSize = hidGetItemSize( header ); + unsigned bTag = hidGetItemTag ( header ); + unsigned bType = hidGetItemType( header ); + + if(( HID_REPORT_ITEM_MAX_SIZE < bSize ) && + ( HID_REPORT_ITEM_USAGE_TAG == bTag ) && + ( HID_REPORT_ITEM_USAGE_TAG == bType )) { + retVal = 1; + } else { + for( unsigned itemIdx = 0; itemIdx < sizeof hidConfigurableItems / sizeof( USB_HID_Short_Item_t ); ++itemIdx ) { + USB_HID_Short_Item_t item = *hidConfigurableItems[ itemIdx ]; + unsigned bBit = hidGetItemBitLocation( item.location ); + unsigned bByte = hidGetItemByteLocation( item.location ); + + if(( bit == bBit ) && ( byte == bByte )) { + item.header = header; + + for( unsigned dataIdx = 0; dataIdx < bSize; ++dataIdx ) { + item.data[ dataIdx ] = data[ dataIdx ]; + } + + *hidConfigurableItems[ itemIdx ] = item; + hidReportDescriptorInitialised = 0; + retVal = 0; + } + } + } + + return retVal; +} + +static unsigned char* hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char* outPtr ) +{ + *outPtr++ = inPtr->header; + + unsigned dataLength = hidGetItemSize( inPtr->header ); + for( unsigned idx = 0; idx < dataLength; ++idx ) { + *outPtr++ = inPtr->data[ idx ]; + } + + return outPtr; +} diff --git a/lib_xua/src/hid/xua_hid_report_descriptor.h b/lib_xua/src/hid/xua_hid_report_descriptor.h new file mode 100644 index 00000000..2195ddc3 --- /dev/null +++ b/lib_xua/src/hid/xua_hid_report_descriptor.h @@ -0,0 +1,83 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/** + * @brief Human Interface Device (HID) Report descriptor + * + * This file defines the structure and default content of the HID Report descriptor. + * Document section numbers refer to the HID Device Class Definition, version 1.11. + */ + +#ifndef _HID_REPORT_DESCRIPTOR_ +#define _HID_REPORT_DESCRIPTOR_ + +#define HID_REPORT_ITEM_MAX_SIZE ( 2 ) + +#define HID_STATUS_GOOD ( 0 ) +#define HID_STATUS_BAD_HEADER ( 1 ) +#define HID_STATUS_BAD_LOCATION ( 2 ) + +/** + * @brief USB HID Report Descriptor. Short Item + * + * @note + * To reduce memory use, this type does not support Short Items with 4 data bytes. + * See section 6.2.2.2 + * + * Elements: + * + * header - the item prefix containing the size, type and tag fields (see 6.2.2.2) + * Format (bit range): bSize (0:1), bType (2:3), bTag (4:7) + * data - a two byte array for holding the item's data + * The bSize field indicates which data bytes are in use + * location - a non-standard extension locating the item within the HID Report + * Format (bit range): iByte (0:3), iBit (4:6), Reserved (7) + */ +typedef struct +{ + unsigned char header; + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ]; + unsigned char location; +} USB_HID_Short_Item_t; + +/** + * @brief Get the HID Report descriptor + * + * This function returns a pointer to the USB HID Report descriptor. + * It returns NULL if the Report descriptor has not been initialised, + * i.e., no one has called \c hidInitReportDescriptor(). + * + * @return A pointer to a list of unsigned char containing the Report descriptor + */ +unsigned char* hidGetReportDescriptor( void ); + +/** + * @brief Modify a HID Report descriptor item + * + * Parameters: + * + * @param[in] byte The byte position of the control within the HID Report + * @param[in] bit The bit position of the control within the \a byte + * @param[in] header The LSB of the Item containing the bSize, bType and bTag fields (see 6.2.2.2) + * @param[in] data An array containing data bytes or NULL for an Item with no data + * + * @return A status value + * @retval \c HID_STATUS_GOOD Item successfully updated + * @retval \c HID_STATUS_BAD_HEADER The Item header specified a data size greater than 2 or + * a Tag or Type inconsistent with a Usage Item + * @retval \c HID_STATUS_BAD_LOCATION The \a bit or \a byte arguments specify a location outside + * of the HID Report + */ +unsigned hidSetReportItem( const unsigned byte, const unsigned bit, const unsigned char header, const unsigned char data[] ); + +/** + * @brief Initialise the USB HID Report descriptor + * + * After initialisation, \c hidGetReportDescriptor() returns a list suitable + * for transmission over USB. + * + * Call this function after altering one or more Report Items using \c hidSetReportItem(). + */ +void hidInitReportDescriptor( void ); + +#endif // _HID_REPORT_DESCRIPTOR_