diff --git a/lib_xua/src/core/buffer/ep/ep_buffer.xc b/lib_xua/src/core/buffer/ep/ep_buffer.xc index 80a913f5..27b25751 100644 --- a/lib_xua/src/core/buffer/ep/ep_buffer.xc +++ b/lib_xua/src/core/buffer/ep/ep_buffer.xc @@ -900,14 +900,18 @@ void XUA_Buffer_Ep(register chanend c_aud_out, #if( 0 < HID_CONTROLS ) /* HID Report Data */ - case hidIsChangePending(0U) || !HidIsSetIdleSilenced() => XUD_SetData_Select(c_hid, ep_hid, result): + case hidIsChangePending(0U) || !HidIsSetIdleSilenced(0U) => XUD_SetData_Select(c_hid, ep_hid, result): { - HidCaptureReportTime(); - for(unsigned id = 0; id < hidGetReportIdLimit(); ++id) { - if(hidIsChangePending(id)) { + timer tmr; + unsigned reportTime; + tmr :> reportTime; + + for(unsigned id = 0U; id < hidGetReportIdLimit(); ++id) { + if(0U == id || (hidIsChangePending(id) || !HidIsSetIdleSilenced(id))) { + hidCaptureReportTime(id, reportTime); int hidDataLength = (int) UserHIDGetData(id, g_hidData); XUD_SetReady_In(ep_hid, g_hidData, hidDataLength); - HidCalcNextReportTime(); + hidCalcNextReportTime(id); hidClearChangePending(id); break; } diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index e51e6f1f..83286bce 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -7,33 +7,15 @@ #include "xud.h" #include "xud_std_requests.h" #include "xua_hid.h" +#include "xua_hid_report_descriptor.h" #if( 0 < HID_CONTROLS ) -#define MS_IN_TICKS 100000U - -static unsigned s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; -static unsigned s_hidIdleActive = 0U; -static unsigned s_hidIndefiniteDuration = 0U; -static unsigned s_hidNextReportTime = 0U; -static unsigned s_hidReportTime = 0U; - static unsigned HidCalcNewReportTime( const unsigned currentPeriod, const unsigned reportTime, const unsigned reportToSetIdleInterval, const unsigned newPeriod ); static unsigned HidCalcReportToSetIdleInterval( const unsigned reportTime ); static unsigned HidFindSetIdleActivationPoint( const unsigned currentPeriod, const unsigned timeWithinPeriod ); static XUD_Result_t HidProcessSetIdleRequest( XUD_ep c_ep0_out, XUD_ep c_ep0_in, USB_SetupPacket_t &sp ); static unsigned HidTimeDiff( const unsigned earlierTime, const unsigned laterTime ); -void HidCalcNextReportTime( void ) -{ - s_hidNextReportTime = s_hidReportTime + s_hidCurrentPeriod; -} - -void HidCaptureReportTime( void ) -{ - timer tmr; - tmr :> s_hidReportTime; -} - XUD_Result_t HidInterfaceClassRequests( XUD_ep c_ep0_out, XUD_ep c_ep0_in, @@ -53,17 +35,17 @@ XUD_Result_t HidInterfaceClassRequests( return result; } -unsigned HidIsSetIdleSilenced( void ) +unsigned HidIsSetIdleSilenced( const unsigned id ) { - unsigned isSilenced = s_hidIdleActive; + unsigned isSilenced = hidIsIdleActive( id ); - if( s_hidIdleActive ) { + if( isSilenced ) { unsigned currentTime; // Use inline assembly to access the time without creating a side-effect. // The mapper complains if the time comes from an XC timer because this function is called in the guard of a select case. // Appearently the use of a timer creates a side-effect that prohibits the operation of the select functionality. asm volatile( "gettime %0" : "=r" ( currentTime )); - isSilenced = ( s_hidIndefiniteDuration || ( timeafter( s_hidNextReportTime, currentTime ))); + isSilenced = ( 0U == hidGetReportPeriod( id ) || ( timeafter( hidGetNextReportTime( id ), currentTime ))); } return isSilenced; @@ -185,18 +167,20 @@ static XUD_Result_t HidProcessSetIdleRequest( XUD_ep c_ep0_out, XUD_ep c_ep0_in, Any Interface value other than INTERFACE_NUMBER_HID indicates an error by the USB Host. */ if(( 0U == reportId ) && ( INTERFACE_NUMBER_HID == interfaceNum )) { - s_hidIdleActive = (( 0U == duration ) || ( ENDPOINT_INT_INTERVAL_IN_HID < duration )); + hidSetIdle( reportId, ( 0U == duration ) || ( ENDPOINT_INT_INTERVAL_IN_HID < duration )); - if( s_hidIdleActive ) { - unsigned reportToSetIdleInterval = HidCalcReportToSetIdleInterval( s_hidReportTime ); - s_hidNextReportTime = HidCalcNewReportTime( s_hidCurrentPeriod, s_hidReportTime, reportToSetIdleInterval, duration * MS_IN_TICKS ); - s_hidCurrentPeriod = duration * MS_IN_TICKS; - s_hidIndefiniteDuration = ( 0U == duration ); + unsigned currentPeriod = hidGetReportPeriod( reportId ); + if( hidIsIdleActive( reportId )) { + unsigned reportTime = hidGetReportTime( reportId ); + unsigned reportToSetIdleInterval = HidCalcReportToSetIdleInterval( reportTime ); + unsigned nextReportTime = HidCalcNewReportTime( currentPeriod, reportTime, reportToSetIdleInterval, duration * MS_IN_TICKS ); + hidSetNextReportTime( reportId, nextReportTime ); + currentPeriod = duration * MS_IN_TICKS; } else { - s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; - s_hidIndefiniteDuration = 0U; + currentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; } + hidSetReportPeriod( reportId, currentPeriod ); result = XUD_DoSetRequestStatus( c_ep0_in ); } diff --git a/lib_xua/src/hid/hid_report_descriptor.c b/lib_xua/src/hid/hid_report_descriptor.c index c19ec3a0..b046a2e5 100644 --- a/lib_xua/src/hid/hid_report_descriptor.c +++ b/lib_xua/src/hid/hid_report_descriptor.c @@ -3,6 +3,7 @@ #include #include #include +#include "descriptor_defs.h" #include "xua_hid_report_descriptor.h" #include "hid_report_descriptor.h" @@ -12,7 +13,6 @@ #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 )) - /* * Each element in s_hidChangePending corresponds to an element in hidReports. */ @@ -21,6 +21,11 @@ static unsigned char s_hidReportDescriptor[ HID_REPORT_DESCRIPTOR_MAX_LENGTH ]; static size_t s_hidReportDescriptorLength = 0U; static unsigned s_hidReportDescriptorPrepared = 0U; +static unsigned s_hidCurrentPeriod[ HID_REPORT_COUNT ] = { ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS }; +static unsigned s_hidIdleActive[ HID_REPORT_COUNT ] = { 0U }; +static unsigned s_hidNextReportTime[ HID_REPORT_COUNT ] = { 0U }; +static unsigned s_hidReportTime[ HID_REPORT_COUNT ] = { 0U }; + /** * @brief Get the bit position from the location of a report element * @@ -125,6 +130,24 @@ static unsigned hidGetUsagePage( const unsigned id ); static size_t hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char** outPtrPtr ); +void hidCalcNextReportTime( const unsigned id ) +{ + 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 ]; + } + } +} + +void hidCaptureReportTime( const unsigned id, const unsigned time ) +{ + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + s_hidNextReportTime[ idx ] = time; + } + } +} + void hidClearChangePending( const unsigned id ) { for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { @@ -177,6 +200,17 @@ static unsigned hidGetItemType( const unsigned char header ) return bType; } +unsigned hidGetNextReportTime( const unsigned id ) { + unsigned retVal = 0U; + + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + retVal = s_hidNextReportTime[ idx ]; + } + } + return retVal; +} + unsigned char* hidGetReportDescriptor( void ) { unsigned char* retVal = NULL; @@ -247,6 +281,29 @@ size_t hidGetReportLength( const unsigned id ) return retVal; } +unsigned hidGetReportPeriod( const unsigned id ) +{ + unsigned retVal = 0U; + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + retVal = s_hidCurrentPeriod[ idx ]; + break; + } + } + return retVal; +} + +unsigned hidGetReportTime( const unsigned id ) { + unsigned retVal = 0U; + + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + retVal = s_hidReportTime[ idx ]; + } + } + return retVal; +} + static unsigned hidGetUsagePage( const unsigned id ) { unsigned retVal = 0U; @@ -273,6 +330,24 @@ unsigned hidIsChangePending( const unsigned id ) return retVal; } +unsigned hidIsIdleActive( const unsigned id ) +{ + unsigned retVal = 0U; + if( 0U == id ) { + retVal = 1U; + } + + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if( id == 0U ) { + retVal &= ( s_hidIdleActive[ idx ] != 0U ); + } else if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + retVal = ( s_hidIdleActive[ idx ] != 0U ); + break; + } + } + return retVal; +} + void hidPrepareReportDescriptor( void ) { if( !s_hidReportDescriptorPrepared ) { @@ -302,6 +377,25 @@ void hidSetChangePending( const unsigned id ) } } +void hidSetIdle( const unsigned id, const unsigned state ) +{ + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + s_hidIdleActive[ idx ] = ( state != 0U ); + break; + } + } +} + +void hidSetNextReportTime( const unsigned id, const unsigned time ) +{ + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + s_hidNextReportTime[ idx ] = time; + } + } +} + unsigned hidSetReportItem( const unsigned id, const unsigned byte, @@ -361,6 +455,16 @@ unsigned hidSetReportItem( return retVal; } +void hidSetReportPeriod( const unsigned id, const unsigned period ) +{ + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + s_hidCurrentPeriod[ idx ] = period; + break; + } + } +} + static size_t hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char** outPtrPtr ) { size_t count = 0U; diff --git a/lib_xua/src/hid/xua_hid.h b/lib_xua/src/hid/xua_hid.h index 9d8f1f82..6e5d31b2 100644 --- a/lib_xua/src/hid/xua_hid.h +++ b/lib_xua/src/hid/xua_hid.h @@ -1,6 +1,12 @@ // Copyright 2019-2021 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. +/** + * @brief Human Interface Device (HID) Class Request functions + * + * Document section numbers refer to the HID Device Class Definition, version 1.11. + */ + #ifndef __XUA_HID_H__ #define __XUA_HID_H__ @@ -9,33 +15,6 @@ #include "xud.h" #include "xud_std_requests.h" -/** - * \brief Calculate the next time to respond with a HID Report. - * - * If the USB Host has previously sent a valid HID Set_Idle request with - * a duration of zero or greater than the default reporting interval, - * the device sends HID Reports periodically or when the value of the - * payload has changed. - * - * This function calculates the time for sending the next periodic - * HID Report. - */ -void HidCalcNextReportTime( void ); - -/** - * \brief Capture the time of sending the current HID Report. - * - * If the USB Host has previously sent a valid HID Set_Idle request with - * a duration of zero or greater than the default reporting interval, - * the device sends HID Reports periodically or when the value of the - * payload has changed. - * - * This function captures the time when the HID Report was sent so that - * a subsequent call to HidCalNextReportTime() can calculate the time - * to send the next periodic HID Report. - */ -void HidCaptureReportTime( void ); - XUD_Result_t HidInterfaceClassRequests( XUD_ep c_ep0_out, XUD_ep c_ep0_in, @@ -53,10 +32,15 @@ XUD_Result_t HidInterfaceClassRequests( * whether or not the time to send the next periodic HID Report has * elapsed. * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * * \return A Boolean value indicating whether or not to send the HID Report. * \retval 1 -- Do not send the HID Report * \retval 0 -- Send the HID Report */ -unsigned HidIsSetIdleSilenced( void ); +unsigned HidIsSetIdleSilenced( const unsigned id ); #endif // __XUA_HID_H__ diff --git a/lib_xua/src/hid/xua_hid_report_descriptor.h b/lib_xua/src/hid/xua_hid_report_descriptor.h index 5c37972d..8043d1c3 100644 --- a/lib_xua/src/hid/xua_hid_report_descriptor.h +++ b/lib_xua/src/hid/xua_hid_report_descriptor.h @@ -57,6 +57,8 @@ #define HID_STATUS_BAD_PAGE ( 4U ) #define HID_STATUS_IN_USE ( 5U ) +#define MS_IN_TICKS 100000U + /** * @brief USB HID Report Descriptor Short Item * @@ -193,6 +195,57 @@ unsigned hidGetReportItem( unsigned char data[]); #endif +/** + * \brief Calculate the next time to respond with a HID Report. + * + * If the USB Host has previously sent a valid HID Set_Idle request with + * a duration of zero or greater than the default reporting interval, + * the device sends HID Reports periodically or when the value of the + * payload has changed. + * + * This function calculates the time for sending the next periodic + * HID Report. + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + */ +void hidCalcNextReportTime( const unsigned id ); + +/** + * \brief Capture the time of sending the current HID Report. + * + * If the USB Host has previously sent a valid HID Set_Idle request with + * a duration of zero or greater than the default reporting interval, + * the device sends HID Reports periodically or when the value of the + * payload has changed. + * + * This function captures the time when the HID Report was sent so that + * a subsequent call to HidCalNextReportTime() can calculate the time + * to send the next periodic HID Report. + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @param[in] time The time when the HID Report for the given \a id was sent. + */ +void hidCaptureReportTime( const unsigned id, const unsigned time ); + +/** + * @brief Get the time to send the next HID Report time for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @returns The time at which to send the next HID Report for the given \a id + */ +unsigned hidGetNextReportTime( const unsigned id ); + /** * @brief Get the length of the HID Report * @@ -210,6 +263,31 @@ unsigned hidGetReportItem( */ size_t hidGetReportLength( const unsigned id ); +/** + * @brief Get the HID Report period for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @returns The period for the given HID Report \a id in units of ms. + * The value zero means the period is indefinite. + */ +unsigned hidGetReportPeriod( const unsigned id ); + +/** + * @brief Get the HID Report time for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @returns The time of the last call to \c hidCaptureReportTime() + */ +unsigned hidGetReportTime( const unsigned id ); + /** * \brief Indicate if a change to the HID Report data has been received. * @@ -248,6 +326,22 @@ size_t hidGetReportLength( const unsigned id ); */ unsigned hidIsChangePending( const unsigned id ); +/** + * @brief Indicate if the HID report for the given \a id is idle + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero returns the collective Idle state. + * + * \returns A Boolean indicating whether the HID Report for the given \a id is idle. + * \retval True The HID Report is idle. + * For an \a id of zero, all HID Reports are idle. + * \retval False The HID Report is not idle. + * For an \a id of zero, at least one HID Report is not idle. + */ +unsigned hidIsIdleActive( const unsigned id ); + /** * @brief Prepare the USB HID Report descriptor * @@ -296,6 +390,32 @@ void hidResetReportDescriptor( void ); */ void hidSetChangePending( const unsigned id ); +/** + * @brief Set the HID Report Idle state for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @param[in] state A Boolean indicating the Idle state + * If true, the HID Report for the given \a id is Idle, otherwise it + * is not Idle. + */ +void hidSetIdle( const unsigned id, const unsigned state ); + +/** + * @brief Set the time to send the HID Report for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @param[in] time The time to send the HID Report for the given \a id. + */ +void hidSetNextReportTime( const unsigned id, const unsigned time ); + /** * @brief Modify a HID Report descriptor item * @@ -332,4 +452,18 @@ unsigned hidSetReportItem( const unsigned char header, const unsigned char data[]); +/** + * @brief Set the HID Report period for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @param[in] period The period for sending the HID Report in units of ms. + * This period must be a multiple of 4 ms. + * Use zero to indicate an indefinite period. + */ +void hidSetReportPeriod( const unsigned id, const unsigned period ); + #endif // _HID_REPORT_DESCRIPTOR_