Files
lib_xua/lib_xua/src/hid/hid_report.c
Michael Banther 769bfa1a5a Remove unnecessary HID functionality (#243)
* Include HID report functionality only when the HID feature is enabled
* Define HID_CONTROLS=1 so HID unit tests work correctly
2022-02-21 13:20:52 +00:00

858 lines
29 KiB
C

// Copyright 2021-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#if( 0 < HID_CONTROLS )
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <xs1.h>
#include "descriptor_defs.h"
#include "xua_hid_report.h"
#include "hid_report_descriptor.h"
#include "swlock.h"
#define HID_REPORT_ITEM_LOCATION_SIZE ( 1 )
#define HID_REPORT_DESCRIPTOR_ITEM_COUNT ( sizeof hidReportDescriptorItems / sizeof ( USB_HID_Short_Item_t* ))
#define HID_REPORT_DESCRIPTOR_MAX_LENGTH ( HID_REPORT_DESCRIPTOR_ITEM_COUNT * \
( sizeof ( USB_HID_Short_Item_t ) - HID_REPORT_ITEM_LOCATION_SIZE ))
swlock_t hidStaticVarLock = SWLOCK_INITIAL_VALUE;
/*
* Each element in s_hidChangePending corresponds to an element in hidReports.
*/
static unsigned s_hidChangePending[ HID_REPORT_COUNT ];
static unsigned char s_hidReportDescriptor[ HID_REPORT_DESCRIPTOR_MAX_LENGTH ];
static size_t s_hidReportDescriptorLength;
static unsigned s_hidReportDescriptorPrepared;
static unsigned s_hidCurrentPeriod[ HID_REPORT_COUNT ];
static unsigned s_hidIdleActive[ HID_REPORT_COUNT ];
static unsigned s_hidNextReportTime[ HID_REPORT_COUNT ];
static unsigned s_hidReportTime[ HID_REPORT_COUNT ];
/**
* @brief Get the bit position from the location of a report element
*
* Parameters:
*
* @param[in] location The \c location field from a \c USB_HID_Report_Element_t
*
* @return The bit position of the report element
*/
static unsigned hidGetElementBitLocation( const unsigned short location );
/**
* @brief Get the byte position from the location of a report element
*
* Parameters:
*
* @param[in] location The \c location field from a \c USB_HID_Report_Element_t
*
* @return The byte position of the report element within the HID report
*/
static unsigned hidGetElementByteLocation( const unsigned short location );
/**
* @brief Get the report identifier from the location of a report element
*
* Parameters:
*
* @param[in] location The \c location field from a \c USB_HID_Report_Element_t
*
* @return The report id of the report element within the HID report
*/
static unsigned hidGetElementReportId( const unsigned short location );
/**
* @brief Get the report length from the location of a report element
*
* Parameters:
*
* @param[in] location The \c location field from a \c USB_HID_Report_Element_t
*
* @return The length of the HID report
*/
static size_t hidGetElementReportLength( const unsigned short location );
/**
* @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 Get the Usage Page number for a given byte in the HID Report
*
* Parameters:
*
* @param[in] id The HID Report ID for the Usage Page.
* A value of zero means the application does not use Report IDs.
*
* @return The USB HID Usage Page code or zero if the \a id parameter is out-of-range
*/
static unsigned hidGetUsagePage( const unsigned id );
/**
* @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] outPtrPtr A pointer to a pointer to the next available space in the raw
* byte buffer. Passed as a pointer to a pointer to allow this
* function to return the updated pointer to the raw byte buffer.
*
* @return The number of bytes placed in the raw byte buffer
*/
static size_t hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char** outPtrPtr );
unsigned hidIsReportIdInUse ( void ) {
return !hidIsReportIdValid(0U);
}
void hidCalcNextReportTime( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
s_hidNextReportTime[ idx ] = s_hidReportTime[ idx ] + s_hidCurrentPeriod[ idx ];
}
}
swlock_release(&hidStaticVarLock);
}
void hidCaptureReportTime( const unsigned id, const unsigned time )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
s_hidReportTime[ idx ] = time;
}
}
swlock_release(&hidStaticVarLock);
}
void hidClearChangePending( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if(( id == 0U ) || ( id == hidGetElementReportId( hidReports[ idx ]->location ))) {
s_hidChangePending[ idx ] = 0U;
break;
}
}
swlock_release(&hidStaticVarLock);
}
static unsigned hidGetElementBitLocation( const unsigned short location )
{
unsigned bBit = ( location & HID_REPORT_ELEMENT_LOC_BIT_MASK ) >> HID_REPORT_ELEMENT_LOC_BIT_SHIFT;
return bBit;
}
static unsigned hidGetElementByteLocation( const unsigned short location )
{
unsigned bByte = ( location & HID_REPORT_ELEMENT_LOC_BYTE_MASK ) >> HID_REPORT_ELEMENT_LOC_BYTE_SHIFT;
return bByte;
}
static unsigned hidGetElementReportId( const unsigned short location )
{
unsigned bReportId = ( location & HID_REPORT_ELEMENT_LOC_ID_MASK ) >> HID_REPORT_ELEMENT_LOC_ID_SHIFT;
return bReportId;
}
static size_t hidGetElementReportLength( const unsigned short location )
{
size_t bReportLen = (size_t)( location & HID_REPORT_ELEMENT_LOC_LEN_MASK ) >> HID_REPORT_ELEMENT_LOC_LEN_SHIFT;
return bReportLen;
}
static unsigned hidGetItemSize( const unsigned char header )
{
unsigned bSize = ( header & HID_REPORT_ITEM_HDR_SIZE_MASK ) >> HID_REPORT_ITEM_HDR_SIZE_SHIFT;
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 hidGetNextReportTime( const unsigned id ) {
swlock_acquire(&hidStaticVarLock);
unsigned retVal = 0U;
for ( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
retVal = s_hidNextReportTime[ idx ];
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned char* hidGetReportDescriptor( void )
{
unsigned char* retVal = NULL;
swlock_acquire(&hidStaticVarLock);
if( s_hidReportDescriptorPrepared ) {
retVal = s_hidReportDescriptor;
}
swlock_release(&hidStaticVarLock);
return retVal;
}
size_t hidGetReportDescriptorLength( void )
{
swlock_acquire(&hidStaticVarLock);
size_t retVal = ( s_hidReportDescriptorPrepared ) ? s_hidReportDescriptorLength : 0U;
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidGetReportIdLimit ( void ) {
unsigned retVal = 0U;
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
unsigned reportId = hidGetElementReportId( hidReports[ idx ]->location );
if( reportId >= retVal ) {
retVal = reportId + 1;
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidGetNextValidReportId ( unsigned idPrev ) {
size_t retIndex = 0;
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
unsigned reportId = hidGetElementReportId( hidReports[ idx ]->location );
if( reportId == idPrev ) {
retIndex = (idx + 1) % HID_REPORT_COUNT;
break;
}
}
unsigned retVal = hidGetElementReportId( hidReports[ retIndex ]->location );
swlock_release(&hidStaticVarLock);
return retVal;
}
#define HID_CONFIGURABLE_ELEMENT_COUNT ( sizeof hidConfigurableElements / sizeof ( USB_HID_Report_Element_t* ))
unsigned hidGetReportItem(
const unsigned id,
const unsigned byte,
const unsigned bit,
unsigned char* const page,
unsigned char* const header,
unsigned char data[]
)
{
unsigned retVal = HID_STATUS_BAD_ID;
for( size_t elementIdx = 0U; elementIdx < HID_CONFIGURABLE_ELEMENT_COUNT; ++elementIdx ) {
swlock_acquire(&hidStaticVarLock);
USB_HID_Report_Element_t element = *hidConfigurableElements[ elementIdx ];
swlock_release(&hidStaticVarLock);
unsigned bBit = hidGetElementBitLocation( element.location );
unsigned bByte = hidGetElementByteLocation( element.location );
unsigned bId = hidGetElementReportId( element.location );
if( id == bId ) {
retVal = HID_STATUS_BAD_LOCATION;
if(( bit == bBit ) && ( byte == bByte )) {
*page = hidGetUsagePage( id );
*header = element.item.header;
for( size_t dataIdx = 0U; dataIdx < HID_REPORT_ITEM_MAX_SIZE; ++data, ++dataIdx ) {
*data = element.item.data[ dataIdx ];
}
retVal = HID_STATUS_GOOD;
break;
}
}
}
return retVal;
}
size_t hidGetReportLength( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
size_t retVal = 0U;
if( s_hidReportDescriptorPrepared ) {
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
retVal = hidGetElementReportLength( hidReports[ idx ]->location );
}
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidGetReportPeriod( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
unsigned retVal = 0U;
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
retVal = s_hidCurrentPeriod[ idx ];
break;
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidGetReportTime( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
unsigned retVal = 0U;
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
retVal = s_hidReportTime[ idx ];
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
static unsigned hidGetUsagePage( const unsigned id )
{
unsigned retVal = 0U;
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
retVal = hidReports[ idx ]->item.data[ 0 ];
break;
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidIsChangePending( const unsigned id )
{
unsigned retVal = 0U;
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
retVal = ( s_hidChangePending[ idx ] != 0U );
break;
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidIsIdleActive( const unsigned id )
{
unsigned retVal = 0U;
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
retVal = ( s_hidIdleActive[ idx ] != 0U );
break;
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidIsReportDescriptorPrepared( void )
{
swlock_acquire(&hidStaticVarLock);
unsigned retVal = s_hidReportDescriptorPrepared;
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidIsReportIdValid ( unsigned id ) {
size_t retVal = 0;
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
unsigned reportId = hidGetElementReportId( hidReports[ idx ]->location );
if( reportId == id ) {
retVal = 1;
break;
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
void hidPrepareReportDescriptor( void )
{
swlock_acquire(&hidStaticVarLock);
if( !s_hidReportDescriptorPrepared ) {
s_hidReportDescriptorLength = 0U;
unsigned char* ptr = s_hidReportDescriptor;
for( size_t idx = 0U; idx < HID_REPORT_DESCRIPTOR_ITEM_COUNT; ++idx ) {
s_hidReportDescriptorLength += hidTranslateItem( hidReportDescriptorItems[ idx ], &ptr );
}
s_hidReportDescriptorPrepared = 1U;
}
swlock_release(&hidStaticVarLock);
}
void hidReportInit( void )
{
swlock_acquire(&hidStaticVarLock);
for( unsigned idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
s_hidCurrentPeriod[ idx ] = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS * HID_REPORT_COUNT;
}
memset( s_hidIdleActive, 0, sizeof( s_hidIdleActive ) );
memset( s_hidChangePending, 0, sizeof( s_hidChangePending ) );
swlock_release(&hidStaticVarLock);
}
void hidResetReportDescriptor( void )
{
swlock_acquire(&hidStaticVarLock);
s_hidReportDescriptorPrepared = 0U;
swlock_release(&hidStaticVarLock);
}
void hidSetChangePending( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
s_hidChangePending[ idx ] = 1U;
break;
}
}
swlock_release(&hidStaticVarLock);
}
void hidSetIdle( const unsigned id, const unsigned state )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
s_hidIdleActive[ idx ] = ( state != 0U );
break;
}
}
swlock_release(&hidStaticVarLock);
}
void hidSetNextReportTime( const unsigned id, const unsigned time )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
s_hidNextReportTime[ idx ] = time;
}
}
swlock_release(&hidStaticVarLock);
}
unsigned hidSetReportItem(
const unsigned id,
const unsigned byte,
const unsigned bit,
const unsigned char page,
const unsigned char header,
const unsigned char data[]
)
{
unsigned retVal = HID_STATUS_IN_USE;
if( !s_hidReportDescriptorPrepared ) {
retVal = HID_STATUS_BAD_ID;
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_TYPE != bType )) {
retVal = HID_STATUS_BAD_HEADER;
} else {
for( size_t elementIdx = 0U; elementIdx < HID_CONFIGURABLE_ELEMENT_COUNT; ++elementIdx ) {
swlock_acquire(&hidStaticVarLock);
USB_HID_Report_Element_t element = *hidConfigurableElements[ elementIdx ];
swlock_release(&hidStaticVarLock);
unsigned bBit = hidGetElementBitLocation( element.location );
unsigned bByte = hidGetElementByteLocation( element.location );
unsigned bId = hidGetElementReportId( element.location );
if( id == bId ) {
retVal = HID_STATUS_BAD_PAGE;
unsigned pg = hidGetUsagePage( id );
if( page == pg ) {
retVal = HID_STATUS_BAD_LOCATION;
if(( bit == bBit ) && ( byte == bByte )) {
element.item.header = header;
for( size_t dataIdx = 0U; dataIdx < bSize; ++dataIdx ) {
element.item.data[ dataIdx ] = data[ dataIdx ];
}
for( size_t dataIdx = bSize; dataIdx < HID_REPORT_ITEM_MAX_SIZE; ++dataIdx ) {
element.item.data[ dataIdx ] = 0U;
}
swlock_acquire(&hidStaticVarLock);
*hidConfigurableElements[ elementIdx ] = element;
swlock_release(&hidStaticVarLock);
retVal = HID_STATUS_GOOD;
break;
}
}
}
}
}
}
return retVal;
}
void hidSetReportPeriod( const unsigned id, const unsigned period )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
s_hidCurrentPeriod[ idx ] = period;
break;
}
}
swlock_release(&hidStaticVarLock);
}
static size_t hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char** outPtrPtr )
{
size_t count = 0U;
*(*outPtrPtr)++ = inPtr->header;
++count;
unsigned dataLength = hidGetItemSize( inPtr->header );
for( size_t idx = 0U; idx < dataLength; ++idx ) {
*(*outPtrPtr)++ = inPtr->data[ idx ];
++count;
}
return count;
}
// hid_report_descriptor.h validation functions for development purposes
/**
* @brief Internal HID Report Descriptor validation state
*/
struct HID_validation_info {
int collectionOpenedCount; //!< Current count of open collections (to track that they are all closed)
int reportCount; //!< Current count of defined reports (to count them)
int currentReportIdx; //!< Index of current report in hidReports array
int currentConfigurableElementIdx; //!< Index of current configurable element in hidConfigurableElements array
unsigned char reportIds[HID_REPORT_COUNT]; // Array of report IDs (for general validation & duplication detection)
unsigned reportUsagePage[HID_REPORT_COUNT]; // Array of the usage page for each report (for general validation)
unsigned current_bit_size; // State tracker for the current set report bit width (todo: should technically be a stack)
unsigned current_bit_count; // State tracker for the current set report count (todo: should technically be a stack)
unsigned current_bit_offset; // Current bit offset into this report (for location validation)
};
/**
* @brief Validation step for hidReportValidate, checking the info struct to ensure correctness of Report IDs
*
* @param info The info struct that has been built by hidReportValidate to check
* @return unsigned HID_STATUS value
*/
static unsigned hidReportValidateInfoStructReportIDs( struct HID_validation_info *info ) {
if ( info->reportCount != HID_REPORT_COUNT) {
if ( !( info->reportCount == 0 && HID_REPORT_COUNT == 1 ) ) {
// (Only if report IDs are being used)
printf("Error: The number of actual reports does not match HID_REPORT_COUNT.\n");
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
}
for ( size_t idx1 = 0; idx1 < HID_REPORT_COUNT; ++idx1 ) {
for ( size_t idx2 = idx1 + 1; idx2 < HID_REPORT_COUNT; ++idx2 ) {
if ( info->reportIds[idx1] == info->reportIds[idx2] ) {
printf("Error: Duplicate report ID 0x%02x.\n", info->reportIds[idx1]);
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
}
}
for ( size_t idx = 0; idx < HID_REPORT_COUNT; ++idx ) {
if ( info->reportIds[idx] != hidGetElementReportId( hidReports[idx]->location ) ) {
printf("Error: Report ID in descriptor does not match report ID in hidReports.\n");
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
if ( info->reportCount && info->reportIds[idx] == 0 ) {
printf("Error: Report ID 0 is invalid.\n");
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
}
return HID_STATUS_GOOD;
}
/**
* @brief Validation step for hidReportValidate, checking reports are the correct length specified in their location field
*
* @param info The info struct that has been built by hidReportValidate to check
* @return unsigned HID_STATUS value
*/
static unsigned hidReportValidateInfoStructReportLength( struct HID_validation_info *info ) {
if ( info->current_bit_offset % 8 ) {
printf("Error: HID Report not byte aligned (%d bits).\n", info->current_bit_offset);
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
if ( ( info->current_bit_offset / 8 ) != hidGetElementReportLength( hidReports[info->currentReportIdx]->location ) ) {
printf("Error: Actual report length does not match value in location field %d != %d.\n",
( info->current_bit_offset / 8 ),
hidGetElementReportLength( hidReports[info->currentReportIdx]->location ));
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
return HID_STATUS_GOOD;
}
/**
* @brief Validation step for hidReportValidate, collections are correctly opened and closed
*
* @param info The info struct that has been built by hidReportValidate to check
* @return unsigned HID_STATUS value
*/
static unsigned hidReportValidateInfoStructCollections( struct HID_validation_info *info ) {
if ( info->collectionOpenedCount ) {
printf("Error: Collections not equally opened and closed.\n");
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
return HID_STATUS_GOOD;
}
/**
* @brief Validation step for hidReportValidate, High level - Checks the summarised information in the info struct by calling
* the subroutines for checking.
*
* @param info The info struct that has been built by hidReportValidate to check
* @return unsigned HID_STATUS value
*/
static unsigned hidReportValidateInfoStruct( struct HID_validation_info *info ) {
unsigned status = hidReportValidateInfoStructCollections( info );
if( status == HID_STATUS_GOOD ) {
status = hidReportValidateInfoStructReportIDs( info );
}
if( status == HID_STATUS_GOOD ) {
status = hidReportValidateInfoStructReportLength( info );
}
return status;
}
/**
* @brief Preparation step for hidReportValidate, Adds a report ID field into the information struct for validation
*
* @param info The info struct being built by hidReportValidate
* @param item The ReportId item being added
* @return unsigned HID_STATUS value
*/
static unsigned hidReportValidateAddReportId( struct HID_validation_info *info, const USB_HID_Short_Item_t *item ) {
if ( info->reportCount == 0 ) {
if ( info->current_bit_offset ) {
printf("Error: Some elements not associated with report ID.\n");
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
info->reportUsagePage[0] = 0;
} else {
unsigned status = hidReportValidateInfoStructReportLength( info );
if ( status ) {
return status;
}
}
if ( hidGetItemSize(item->header) != 1 ) {
printf("Error: ReportId field has invalid length %d (expected 1)\n", hidGetItemSize(item->header));
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
info->reportIds[info->reportCount] = item->data[0];
info->currentReportIdx = info->reportCount;
info->reportCount += 1;
info->current_bit_offset = 0;
if ( info->reportCount > HID_REPORT_COUNT ) {
printf("Error: HID_REPORT_COUNT does not match number of report IDs in descriptor.\n");
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
return HID_STATUS_GOOD;
}
/**
* @brief Preparation step for hidReportValidate, Adds a Usage Page field into the information struct for validation
*
* @param info The info struct being built by hidReportValidate
* @param item The UsagePage item being added
* @return unsigned HID_STATUS value
*/
static unsigned hidReportValidateAddUsagePageItem( struct HID_validation_info *info, const USB_HID_Short_Item_t *item ) {
if ( info->collectionOpenedCount == 0 ) {
return HID_STATUS_GOOD;
}
if ( info->reportUsagePage[info->currentReportIdx] ) {
printf("Error: Multiple usage pages per report ID not supported by this implementation.\n");
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
switch (hidGetItemSize(item->header)){
case 1:
info->reportUsagePage[info->currentReportIdx] = item->data[0];
break;
case 2:
info->reportUsagePage[info->currentReportIdx] = ((unsigned) item->data[1] << 8) + item->data[0];
break;
default:
printf("Error: Invalid size for UsagePage report descriptor item.\n");
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
return HID_STATUS_GOOD;
}
/**
* @brief Preparation step for hidReportValidate, Adds a Usage field into the information struct for validation
*
* @param info The info struct being built by hidReportValidate
* @param item The Usage item being added
* @return unsigned HID_STATUS value
*/
static unsigned hidReportValidateAddUsageItem( struct HID_validation_info *info, const USB_HID_Short_Item_t *item) {
if ( ( info->currentConfigurableElementIdx < HID_CONFIGURABLE_ELEMENT_COUNT ) &&
( &(hidConfigurableElements[info->currentConfigurableElementIdx]->item) == item ) ) {
USB_HID_Report_Element_t *element = hidConfigurableElements[info->currentConfigurableElementIdx];
unsigned bBit = hidGetElementBitLocation( element->location );
unsigned bByte = hidGetElementByteLocation( element->location );
unsigned bReportId = hidGetElementReportId( element->location );
if ( bBit != ( info->current_bit_offset % 8 ) || bByte != ( info->current_bit_offset / 8 ) ) {
printf("Error: Locator bit/byte setting incorrect for configurable element index %d.\n", info->currentConfigurableElementIdx);
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
if ( bReportId != info->reportIds[info->currentReportIdx] ) {
printf("Error: Locator report ID setting incorrect for configurable element index %d.\n", info->currentConfigurableElementIdx);
return HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
info->currentConfigurableElementIdx += 1;
}
return HID_STATUS_GOOD;
}
unsigned hidReportValidate( void )
{
struct HID_validation_info info = {};
unsigned status = HID_STATUS_GOOD;
// Fill in the validation info struct by iterating through the hid report items
for ( size_t idx = 0; idx < HID_REPORT_DESCRIPTOR_ITEM_COUNT; ++idx ) {
const USB_HID_Short_Item_t *item = hidReportDescriptorItems[idx];
unsigned bTag = hidGetItemTag ( item->header );
unsigned bType = hidGetItemType( item->header );
if ( bTag == HID_REPORT_ITEM_TAG_COLLECTION && bType == HID_REPORT_ITEM_TYPE_MAIN ) {
info.collectionOpenedCount += 1;
}
else if ( bTag == HID_REPORT_ITEM_TAG_END_COLLECTION && bType == HID_REPORT_ITEM_TYPE_MAIN ) {
info.collectionOpenedCount -= 1;
if ( info.collectionOpenedCount < 0 ) {
printf("Error: Collection closed while there is no collection open.\n");
status = HID_STATUS_BAD_REPORT_DESCRIPTOR;
}
}
else if ( bTag == HID_REPORT_ITEM_TAG_INPUT && bType == HID_REPORT_ITEM_TYPE_MAIN ) {
info.current_bit_offset += (info.current_bit_size * info.current_bit_count);
}
else if ( bTag == HID_REPORT_ITEM_TAG_REPORT_SIZE && bType == HID_REPORT_ITEM_TYPE_GLOBAL ) {
info.current_bit_size = item->data[0];
}
else if ( bTag == HID_REPORT_ITEM_TAG_REPORT_COUNT && bType == HID_REPORT_ITEM_TYPE_GLOBAL ) {
info.current_bit_count = item->data[0];
}
else if ( bTag == HID_REPORT_ITEM_TAG_REPORT_ID && bType == HID_REPORT_ITEM_TYPE_GLOBAL ) {
status = hidReportValidateAddReportId( &info, item );
}
else if ( bTag == HID_REPORT_ITEM_TAG_USAGE_PAGE && bType == HID_REPORT_ITEM_TYPE_GLOBAL ) {
status = hidReportValidateAddUsagePageItem( &info, item );
}
else if ( bTag == HID_REPORT_ITEM_TAG_USAGE && bType == HID_REPORT_ITEM_TYPE_LOCAL ) {
status = hidReportValidateAddUsageItem( &info, item );
}
if ( status ) {
break;
}
}
if(status) {
return status;
} else {
return hidReportValidateInfoStruct( &info );
}
}
#endif // ( 0 < HID_CONTROLS )