From 1808d7affac01baa37e203cd7d32aeb0607b3238 Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Thu, 10 Oct 2019 17:21:21 +0100 Subject: [PATCH 01/16] Initial implementation of the USB HID Set_Idle Request. This code builds successfully. It has not been tested even a little. --- lib_xua/src/core/endpoint0/descriptor_defs.h | 4 + .../src/core/endpoint0/xua_ep0_descriptors.h | 4 +- lib_xua/src/hid/hid.xc | 196 ++++++++++++++++-- lib_xua/src/hid/xua_hid.h | 12 +- 4 files changed, 196 insertions(+), 20 deletions(-) diff --git a/lib_xua/src/core/endpoint0/descriptor_defs.h b/lib_xua/src/core/endpoint0/descriptor_defs.h index b348d6f1..53ba28ed 100644 --- a/lib_xua/src/core/endpoint0/descriptor_defs.h +++ b/lib_xua/src/core/endpoint0/descriptor_defs.h @@ -60,4 +60,8 @@ enum USBInterfaceNumber INTERFACE_COUNT /* End marker */ }; +#if( 0 < HID_CONTROLS ) +#define ENDPOINT_INT_INTERVAL_IN_HID 0x08 +#endif + #endif diff --git a/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h b/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h index 0c2c3eb5..066319f4 100644 --- a/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h +++ b/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h @@ -2184,7 +2184,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= ENDPOINT_ADDRESS_IN_HID, /* 2 bEndpointAddress */ 3, /* 3 bmAttributes (INTERRUPT) */ 64, /* 4 wMaxPacketSize */ - 8, /* 6 bInterval */ + ENDPOINT_INT_INTERVAL_IN_HID, /* 6 bInterval */ } #endif @@ -2862,7 +2862,7 @@ unsigned char cfgDesc_Audio1[] = 0x03, /* 3 bmAttributes (INTERRUPT) */ 0x40, /* 4 wMaxPacketSize */ 0x00, /* 5 wMaxPacketSize */ - 0x08, /* 6 bInterval */ + ENDPOINT_INT_INTERVAL_IN_HID, /* 6 bInterval */ #endif }; diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index fd7cb32b..1e92e283 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -1,26 +1,192 @@ #include -#include "xud.h" +#include "descriptor_defs.h" #include "hid.h" +#include "xud.h" #include "xud_std_requests.h" #include "xua_hid.h" -static unsigned hidSetIdle = 0; +#define MS_IN_TICKS 100000U -unsigned HidIsSetIdleSilenced(void) +static unsigned s_hidIdleActive = 0; +static unsigned s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; +static unsigned s_hidNextReportTime = 0; +static unsigned s_hidReportTime = 0; + +/** + * \brief Calculate the difference between two points in time + * + * This function calculates the difference between two two points in time. + * It always returns a positive value even if the timer used to obtain the two + * time measurements has wrapped around. + * + * \warning If time values have been obtained from a timer that has wrapped + * more than once in between the two measurements, this function returns an + * incorrect value. + * + * \param[in] earlierTime -- A value from a timer + * \param[in] laterTime -- A value from a timer taken after \a earlierTime + * + * \return The interval between the two points in time + */ +static unsigned HidTimeDiff( const unsigned earlierTime, const unsigned laterTime ) { - return hidSetIdle; + return ( earlierTime < laterTime ) ? laterTime - earlierTime : UINT_MAX - earlierTime + laterTime; } -XUD_Result_t HidInterfaceClassRequests(XUD_ep c_ep0_out, XUD_ep c_ep0_in, - USB_SetupPacket_t &sp) +/** + * \brief Calculate the time interval between the most recent HID Report and a subsequent Set Idle Request + * + * \warning For this function to produce an accurate interval measument, it must be called without delay + * upon receiving a Set Idle Request from the USB Host. + * + * \param[in] reportTime -- The time at which the last HID Report was sent + * + * \return The time interval between receiving the Set Idle Request and sending the most recent HID Report + */ +static unsigned HidCalcReportToSetIdleInterval( const unsigned reportTime ) { - switch (sp.bRequest) { - case HID_SET_IDLE: - printstr("HID_SET_IDLE\n"); - hidSetIdle = 1; // TODO implement duration - return XUD_DoSetRequestStatus(c_ep0_in); - default: - break; - } - return XUD_RES_ERR; + timer tmr; + unsigned setIdleTime; + + tmr :> setIdleTime; + unsigned result = HidTimeDiff( reportTime, setIdleTime ); + return result; +} + +/** + * \brief Indicate if activation of the Set Idle Request happens at the previous or next HID Report + * + * Section 7.2.4 Set_Idle Request of the USB Device Class Definition for Human Interface + * Devices (HID) Version 1.11 makes two statements about the activation point for starting the + * duration of the request: + * - 'A new request will be executed as if it were issued immediately after the last report, if + * the new request is received at least 4 milliseconds before the end of the currently executing + * period.' + * - 'If the new request is received within 4 milliseconds of the end of the current period, then + * the new request will have no effect until after the report.' + * + * \param[in] currentPeriod -- The duration of the current period + * \param[in] timeWithinPeriod -- The current point in time relative to the current period + * + * \return A Boolean indicating where the activation of the Set Idle Request Duration occurs. + * \retval 1 -- Activate immediately after the next HID Report + * \retval 0 -- Activate immediately after the previous HID Report + */ +static unsigned HidFindSetIdleActivationPoint( const unsigned currentPeriod, const unsigned timeWithinPeriod ) +{ + unsigned result = (( currentPeriod - timeWithinPeriod ) < ( 4U * MS_IN_TICKS )) ? 1 : 0; + + return result; +} + +/** + * \brief Calculate the timer value for sending the next HID Report. + * + * With regard to Section 7.2.4 Set_Idle Request of the USB Device Class Definition for Human + * Interface Devices (HID) Version 1.11, I've interpreted 'currently executing period' and + * 'current period' to mean the previously established Set Idle duration if one has been + * established or the polling interval from the HID Report Descriptor if a Set Idle duration + * has not been established. + * + * \param[in] currentPeriod -- The duration of the current period in timer ticks + * \param[in] reportTime -- The time at which the last HID Report was sent + * \param[in] reportToSetIdleInterval -- The time interval between receiving the Set Idle Request + * and sending the most recent HID Report + * \param[in] nextPeriod -- The new period value in timer ticks + * + * \return The time at which the next HID Report should be sent + */ +static unsigned HidCalcNewReportTime( const unsigned currentPeriod, const unsigned reportTime, const unsigned reportToSetIdleInterval, const unsigned nextPeriod ) +{ + unsigned nextReportTime = 0; + + if( HidFindSetIdleActivationPoint( currentPeriod, reportToSetIdleInterval )) { + /* Activate immediately after sending the next HID Report */ + nextReportTime = reportTime + currentPeriod; + } else { + /* Activate immediately after sending the most recent HID Report */ + nextReportTime = reportTime + nextPeriod; + } + + return nextReportTime; +} + +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, + USB_SetupPacket_t &sp ) +{ + XUD_Result_t result = XUD_RES_ERR; + + switch ( sp.bRequest ) { + case HID_SET_IDLE: + printstr("HID_SET_IDLE\n"); + /* + The Set Idle request wValue field contains two sub-fields: + - Duration in the MSB; and + - Report ID in the LSB. + + The Duration field specifies how long the USB Device responds with NAK provided the HID data hasn't changed. + Zero means indefinitely. + The value is in units of 4ms. + + The Report ID identifies the HID report that the USB Host wishes to silence. + + The Set Idle request xIndex field contains the interface number. + */ + uint8_t duration = ( sp.wValue & 0xFF00 ) >> 6; // Transform into units of 1ms. + uint8_t reportId = sp.wValue & 0x00FF; + uint16_t interfaceNum = ( sp.wIndex & 0xFF00 ) >> 8; + + /* + As long as our HID Report Descriptor does not include a Report ID, any Report ID value other than zero + indicates an error by the USB Host (see xua_ep0_descriptors.h for the definition of the HID + Report Descriptor). + + Any Interface value other than INTERFACE_NUMBER_HID indicates an error by the USB Host. + */ + if(( 0U == reportId ) && ( interfaceNum == INTERFACE_NUMBER_HID )) { + s_hidIdleActive = !(( 0 < duration ) && ( duration < ENDPOINT_INT_INTERVAL_IN_HID )); + + 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; + } else { + s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; + } + + result = XUD_DoSetRequestStatus( c_ep0_in ); + } + break; + + default: + break; + } + + return result; +} + +unsigned HidIsSetIdleSilenced( void ) +{ + unsigned isSilenced = s_hidIdleActive; + + if( s_hidIdleActive ) { + unsigned currentTime; + asm volatile( "gettime %0" : "=r" ( currentTime )); // Use inline assembly to access the time without creating a side-effect + isSilenced = timeafter( s_hidNextReportTime, currentTime ); + } + + return isSilenced; } diff --git a/lib_xua/src/hid/xua_hid.h b/lib_xua/src/hid/xua_hid.h index df89dc55..c3dfcc66 100644 --- a/lib_xua/src/hid/xua_hid.h +++ b/lib_xua/src/hid/xua_hid.h @@ -3,7 +3,13 @@ #include "xud.h" #include "xud_std_requests.h" -XUD_Result_t HidInterfaceClassRequests(XUD_ep c_ep0_out, XUD_ep c_ep0_in, - REFERENCE_PARAM(USB_SetupPacket_t, sp)); +void HidCalcNextReportTime( void ); -unsigned HidIsSetIdleSilenced(void); +void HidCaptureReportTime( void ); + +XUD_Result_t HidInterfaceClassRequests( + XUD_ep c_ep0_out, + XUD_ep c_ep0_in, + REFERENCE_PARAM( USB_SetupPacket_t, sp )); + +unsigned HidIsSetIdleSilenced( void ); From 9f98e13342b2b4fb4bfe19da9da3e0e8df7bc9e2 Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Mon, 14 Oct 2019 16:42:34 +0100 Subject: [PATCH 02/16] Use the HID_CONTROLS pre-processor symbol consistently. --- lib_xua/src/core/endpoint0/xua_endpoint0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_xua/src/core/endpoint0/xua_endpoint0.c b/lib_xua/src/core/endpoint0/xua_endpoint0.c index c59886c6..7b42909b 100755 --- a/lib_xua/src/core/endpoint0/xua_endpoint0.c +++ b/lib_xua/src/core/endpoint0/xua_endpoint0.c @@ -60,7 +60,7 @@ extern void device_reboot(void); #endif -#if HID_CONTROLS +#if( 0 < HID_CONTROLS ) #include "xua_hid.h" #endif @@ -993,7 +993,7 @@ void XUA_Endpoint0_lite_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend } } #endif -#if HID_CONTROLS +#if( 0 < HID_CONTROLS ) if (interfaceNum == INTERFACE_NUMBER_HID) { result = HidInterfaceClassRequests(ep0_out, ep0_in, &sp); From 962e91adeca2238307e5e30eb2c71512754fb24b Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Mon, 14 Oct 2019 16:47:59 +0100 Subject: [PATCH 03/16] Include xua.h to pick up the NUM_USB_CHAN_IN and NUM_USB_CHAN_OUT pre-processor symbols. They're used further down in the file. If not present through an #include of xua_h outside of and before the #include of descriptor_defs.h or through definition in the Make file, the enumeration of interface numbers silently mis-assigns values. --- lib_xua/src/core/endpoint0/descriptor_defs.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib_xua/src/core/endpoint0/descriptor_defs.h b/lib_xua/src/core/endpoint0/descriptor_defs.h index 53ba28ed..1f29d58e 100644 --- a/lib_xua/src/core/endpoint0/descriptor_defs.h +++ b/lib_xua/src/core/endpoint0/descriptor_defs.h @@ -3,6 +3,11 @@ #ifndef __DESCRIPTOR_DEFS_H__ #define __DESCRIPTOR_DEFS_H__ +/* + Include xua.h to pick up the #defines of NUM_USB_CHAN_IN and NUM_USB_CHAN_OUT. + */ +#include "xua.h" + #if (NUM_USB_CHAN_IN > 0) && (NUM_USB_CHAN_OUT > 0) #define AUDIO_INTERFACE_COUNT 3 #elif (NUM_USB_CHAN_IN > 0) || (NUM_USB_CHAN_OUT > 0) From 97e32331203f7f590d8ae33f80d2df691a724d22 Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Tue, 15 Oct 2019 10:42:33 +0100 Subject: [PATCH 04/16] Rename parameter to a less ambiguous name. --- lib_xua/src/hid/hid.xc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index 1e92e283..972158ff 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -92,11 +92,11 @@ static unsigned HidFindSetIdleActivationPoint( const unsigned currentPeriod, con * \param[in] reportTime -- The time at which the last HID Report was sent * \param[in] reportToSetIdleInterval -- The time interval between receiving the Set Idle Request * and sending the most recent HID Report - * \param[in] nextPeriod -- The new period value in timer ticks + * \param[in] newPeriod -- The new period value in timer ticks * * \return The time at which the next HID Report should be sent */ -static unsigned HidCalcNewReportTime( const unsigned currentPeriod, const unsigned reportTime, const unsigned reportToSetIdleInterval, const unsigned nextPeriod ) +static unsigned HidCalcNewReportTime( const unsigned currentPeriod, const unsigned reportTime, const unsigned reportToSetIdleInterval, const unsigned newPeriod ) { unsigned nextReportTime = 0; @@ -105,7 +105,7 @@ static unsigned HidCalcNewReportTime( const unsigned currentPeriod, const unsign nextReportTime = reportTime + currentPeriod; } else { /* Activate immediately after sending the most recent HID Report */ - nextReportTime = reportTime + nextPeriod; + nextReportTime = reportTime + newPeriod; } return nextReportTime; From aeb1d58bf347bed3008dbd76d76ce1720fcc6608 Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Tue, 15 Oct 2019 10:44:34 +0100 Subject: [PATCH 05/16] The interface number occupies the entire sp.wIndex field. --- lib_xua/src/hid/hid.xc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index 972158ff..0152b071 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -147,7 +147,7 @@ XUD_Result_t HidInterfaceClassRequests( */ uint8_t duration = ( sp.wValue & 0xFF00 ) >> 6; // Transform into units of 1ms. uint8_t reportId = sp.wValue & 0x00FF; - uint16_t interfaceNum = ( sp.wIndex & 0xFF00 ) >> 8; + uint16_t interfaceNum = sp.wIndex; /* As long as our HID Report Descriptor does not include a Report ID, any Report ID value other than zero From 3331c9e97bc168753b97a48bb04ed41aad421b8f Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Tue, 15 Oct 2019 10:45:20 +0100 Subject: [PATCH 06/16] Add handling for an indefinite duration. --- lib_xua/src/hid/hid.xc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index 0152b071..794e244b 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -9,6 +9,7 @@ static unsigned s_hidIdleActive = 0; static unsigned s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; +static unsigned s_hidIndefiniteDuration = 0; static unsigned s_hidNextReportTime = 0; static unsigned s_hidReportTime = 0; @@ -163,8 +164,10 @@ XUD_Result_t HidInterfaceClassRequests( 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 ); } else { s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; + s_hidIndefiniteDuration = 0; } result = XUD_DoSetRequestStatus( c_ep0_in ); @@ -185,7 +188,7 @@ unsigned HidIsSetIdleSilenced( void ) if( s_hidIdleActive ) { unsigned currentTime; asm volatile( "gettime %0" : "=r" ( currentTime )); // Use inline assembly to access the time without creating a side-effect - isSilenced = timeafter( s_hidNextReportTime, currentTime ); + isSilenced = ( s_hidIndefiniteDuration || ( timeafter( s_hidNextReportTime, currentTime ))); } return isSilenced; From e4f0b73a88928bc004dcdc029722930298e5fb59 Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Tue, 15 Oct 2019 15:38:16 +0100 Subject: [PATCH 07/16] Explicitly label assignment of zero as unsigned when used with unsigned variables. --- lib_xua/src/hid/hid.xc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index 794e244b..88ced493 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -7,11 +7,11 @@ #define MS_IN_TICKS 100000U -static unsigned s_hidIdleActive = 0; +static unsigned s_hidIdleActive = 0U; static unsigned s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; -static unsigned s_hidIndefiniteDuration = 0; -static unsigned s_hidNextReportTime = 0; -static unsigned s_hidReportTime = 0; +static unsigned s_hidIndefiniteDuration = 0U; +static unsigned s_hidNextReportTime = 0U; +static unsigned s_hidReportTime = 0U; /** * \brief Calculate the difference between two points in time @@ -167,7 +167,7 @@ XUD_Result_t HidInterfaceClassRequests( s_hidIndefiniteDuration = ( 0U == duration ); } else { s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; - s_hidIndefiniteDuration = 0; + s_hidIndefiniteDuration = 0U; } result = XUD_DoSetRequestStatus( c_ep0_in ); From a5f17c46fc3d7a22b2a547236aa576e4326b934c Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Tue, 15 Oct 2019 15:39:14 +0100 Subject: [PATCH 08/16] Use a variable large enough to hold the Set Idle duration in ms without overflow. --- lib_xua/src/hid/hid.xc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index 88ced493..40014575 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -146,7 +146,7 @@ XUD_Result_t HidInterfaceClassRequests( The Set Idle request xIndex field contains the interface number. */ - uint8_t duration = ( sp.wValue & 0xFF00 ) >> 6; // Transform into units of 1ms. + uint16_t duration = ( sp.wValue & 0xFF00 ) >> 6; // Transform from units of 4ms into units of 1ms. uint8_t reportId = sp.wValue & 0x00FF; uint16_t interfaceNum = sp.wIndex; From 5caca37177cfc34e88f1b903f196dcbc9ff3f705 Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Tue, 15 Oct 2019 15:40:26 +0100 Subject: [PATCH 09/16] Change the representation, but not the logic, of a multi-part conditional expression to make it easier to understand. --- lib_xua/src/hid/hid.xc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index 40014575..cee8d49a 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -158,7 +158,7 @@ XUD_Result_t HidInterfaceClassRequests( Any Interface value other than INTERFACE_NUMBER_HID indicates an error by the USB Host. */ if(( 0U == reportId ) && ( interfaceNum == INTERFACE_NUMBER_HID )) { - s_hidIdleActive = !(( 0 < duration ) && ( duration < ENDPOINT_INT_INTERVAL_IN_HID )); + s_hidIdleActive = (( 0U == duration ) || ( ENDPOINT_INT_INTERVAL_IN_HID < duration )); if( s_hidIdleActive ) { unsigned reportToSetIdleInterval = HidCalcReportToSetIdleInterval( s_hidReportTime ); From 6afb4ab7e452175791ec43ec2f78a8cd5bd31d29 Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Tue, 15 Oct 2019 15:43:05 +0100 Subject: [PATCH 10/16] Remove debugging print. --- lib_xua/src/hid/hid.xc | 1 - 1 file changed, 1 deletion(-) diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index cee8d49a..606349b2 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -132,7 +132,6 @@ XUD_Result_t HidInterfaceClassRequests( switch ( sp.bRequest ) { case HID_SET_IDLE: - printstr("HID_SET_IDLE\n"); /* The Set Idle request wValue field contains two sub-fields: - Duration in the MSB; and From 8820ddf2699e9635f03f5e45f9b7f157bc2dd863 Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Wed, 16 Oct 2019 11:37:27 +0100 Subject: [PATCH 11/16] Add documentation. --- lib_xua/src/hid/xua_hid.h | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/lib_xua/src/hid/xua_hid.h b/lib_xua/src/hid/xua_hid.h index c3dfcc66..ab8b2e05 100644 --- a/lib_xua/src/hid/xua_hid.h +++ b/lib_xua/src/hid/xua_hid.h @@ -3,8 +3,31 @@ #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( @@ -12,4 +35,20 @@ XUD_Result_t HidInterfaceClassRequests( XUD_ep c_ep0_in, REFERENCE_PARAM( USB_SetupPacket_t, sp )); +/** + * \brief Indicate whether to send a HID Report based on elapsed time. + * + * 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 monitors the passage of time and reports to the caller + * whether or not the time to send the next periodic HID Report has + * elapsed. + * + * \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 ); From 9a7c2d85b20ff9f76ea25935e8f528e767c5351c Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Wed, 16 Oct 2019 11:47:32 +0100 Subject: [PATCH 12/16] Reorganise file layout for easier maintenance and reading. --- lib_xua/src/hid/hid.xc | 201 +++++++++++++++++++++-------------------- 1 file changed, 103 insertions(+), 98 deletions(-) diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index 606349b2..da4f5c78 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -13,104 +13,10 @@ static unsigned s_hidIndefiniteDuration = 0U; static unsigned s_hidNextReportTime = 0U; static unsigned s_hidReportTime = 0U; -/** - * \brief Calculate the difference between two points in time - * - * This function calculates the difference between two two points in time. - * It always returns a positive value even if the timer used to obtain the two - * time measurements has wrapped around. - * - * \warning If time values have been obtained from a timer that has wrapped - * more than once in between the two measurements, this function returns an - * incorrect value. - * - * \param[in] earlierTime -- A value from a timer - * \param[in] laterTime -- A value from a timer taken after \a earlierTime - * - * \return The interval between the two points in time - */ -static unsigned HidTimeDiff( const unsigned earlierTime, const unsigned laterTime ) -{ - return ( earlierTime < laterTime ) ? laterTime - earlierTime : UINT_MAX - earlierTime + laterTime; -} - -/** - * \brief Calculate the time interval between the most recent HID Report and a subsequent Set Idle Request - * - * \warning For this function to produce an accurate interval measument, it must be called without delay - * upon receiving a Set Idle Request from the USB Host. - * - * \param[in] reportTime -- The time at which the last HID Report was sent - * - * \return The time interval between receiving the Set Idle Request and sending the most recent HID Report - */ -static unsigned HidCalcReportToSetIdleInterval( const unsigned reportTime ) -{ - timer tmr; - unsigned setIdleTime; - - tmr :> setIdleTime; - unsigned result = HidTimeDiff( reportTime, setIdleTime ); - return result; -} - -/** - * \brief Indicate if activation of the Set Idle Request happens at the previous or next HID Report - * - * Section 7.2.4 Set_Idle Request of the USB Device Class Definition for Human Interface - * Devices (HID) Version 1.11 makes two statements about the activation point for starting the - * duration of the request: - * - 'A new request will be executed as if it were issued immediately after the last report, if - * the new request is received at least 4 milliseconds before the end of the currently executing - * period.' - * - 'If the new request is received within 4 milliseconds of the end of the current period, then - * the new request will have no effect until after the report.' - * - * \param[in] currentPeriod -- The duration of the current period - * \param[in] timeWithinPeriod -- The current point in time relative to the current period - * - * \return A Boolean indicating where the activation of the Set Idle Request Duration occurs. - * \retval 1 -- Activate immediately after the next HID Report - * \retval 0 -- Activate immediately after the previous HID Report - */ -static unsigned HidFindSetIdleActivationPoint( const unsigned currentPeriod, const unsigned timeWithinPeriod ) -{ - unsigned result = (( currentPeriod - timeWithinPeriod ) < ( 4U * MS_IN_TICKS )) ? 1 : 0; - - return result; -} - -/** - * \brief Calculate the timer value for sending the next HID Report. - * - * With regard to Section 7.2.4 Set_Idle Request of the USB Device Class Definition for Human - * Interface Devices (HID) Version 1.11, I've interpreted 'currently executing period' and - * 'current period' to mean the previously established Set Idle duration if one has been - * established or the polling interval from the HID Report Descriptor if a Set Idle duration - * has not been established. - * - * \param[in] currentPeriod -- The duration of the current period in timer ticks - * \param[in] reportTime -- The time at which the last HID Report was sent - * \param[in] reportToSetIdleInterval -- The time interval between receiving the Set Idle Request - * and sending the most recent HID Report - * \param[in] newPeriod -- The new period value in timer ticks - * - * \return The time at which the next HID Report should be sent - */ -static unsigned HidCalcNewReportTime( const unsigned currentPeriod, const unsigned reportTime, const unsigned reportToSetIdleInterval, const unsigned newPeriod ) -{ - unsigned nextReportTime = 0; - - if( HidFindSetIdleActivationPoint( currentPeriod, reportToSetIdleInterval )) { - /* Activate immediately after sending the next HID Report */ - nextReportTime = reportTime + currentPeriod; - } else { - /* Activate immediately after sending the most recent HID Report */ - nextReportTime = reportTime + newPeriod; - } - - return nextReportTime; -} +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 unsigned HidTimeDiff( const unsigned earlierTime, const unsigned laterTime ); void HidCalcNextReportTime( void ) { @@ -192,3 +98,102 @@ unsigned HidIsSetIdleSilenced( void ) return isSilenced; } + +/** + * \brief Calculate the timer value for sending the next HID Report. + * + * With regard to Section 7.2.4 Set_Idle Request of the USB Device Class Definition for Human + * Interface Devices (HID) Version 1.11, I've interpreted 'currently executing period' and + * 'current period' to mean the previously established Set Idle duration if one has been + * established or the polling interval from the HID Report Descriptor if a Set Idle duration + * has not been established. + * + * \param[in] currentPeriod -- The duration of the current period in timer ticks + * \param[in] reportTime -- The time at which the last HID Report was sent + * \param[in] reportToSetIdleInterval -- The time interval between receiving the Set Idle Request + * and sending the most recent HID Report + * \param[in] newPeriod -- The new period value in timer ticks + * + * \return The time at which the next HID Report should be sent + */ +static unsigned HidCalcNewReportTime( const unsigned currentPeriod, const unsigned reportTime, const unsigned reportToSetIdleInterval, const unsigned newPeriod ) +{ + unsigned nextReportTime = 0; + + if( HidFindSetIdleActivationPoint( currentPeriod, reportToSetIdleInterval )) { + /* Activate immediately after sending the next HID Report */ + nextReportTime = reportTime + currentPeriod; + } else { + /* Activate immediately after sending the most recent HID Report */ + nextReportTime = reportTime + newPeriod; + } + + return nextReportTime; +} + +/** + * \brief Calculate the time interval between the most recent HID Report and a subsequent Set Idle Request + * + * \warning For this function to produce an accurate interval measument, it must be called without delay + * upon receiving a Set Idle Request from the USB Host. + * + * \param[in] reportTime -- The time at which the last HID Report was sent + * + * \return The time interval between receiving the Set Idle Request and sending the most recent HID Report + */ +static unsigned HidCalcReportToSetIdleInterval( const unsigned reportTime ) +{ + timer tmr; + unsigned setIdleTime; + + tmr :> setIdleTime; + unsigned result = HidTimeDiff( reportTime, setIdleTime ); + return result; +} + +/** + * \brief Indicate if activation of the Set Idle Request happens at the previous or next HID Report + * + * Section 7.2.4 Set_Idle Request of the USB Device Class Definition for Human Interface + * Devices (HID) Version 1.11 makes two statements about the activation point for starting the + * duration of the request: + * - 'A new request will be executed as if it were issued immediately after the last report, if + * the new request is received at least 4 milliseconds before the end of the currently executing + * period.' + * - 'If the new request is received within 4 milliseconds of the end of the current period, then + * the new request will have no effect until after the report.' + * + * \param[in] currentPeriod -- The duration of the current period + * \param[in] timeWithinPeriod -- The current point in time relative to the current period + * + * \return A Boolean indicating where the activation of the Set Idle Request Duration occurs. + * \retval 1 -- Activate immediately after the next HID Report + * \retval 0 -- Activate immediately after the previous HID Report + */ +static unsigned HidFindSetIdleActivationPoint( const unsigned currentPeriod, const unsigned timeWithinPeriod ) +{ + unsigned result = (( currentPeriod - timeWithinPeriod ) < ( 4U * MS_IN_TICKS )) ? 1 : 0; + + return result; +} + +/** + * \brief Calculate the difference between two points in time + * + * This function calculates the difference between two two points in time. + * It always returns a positive value even if the timer used to obtain the two + * time measurements has wrapped around. + * + * \warning If time values have been obtained from a timer that has wrapped + * more than once in between the two measurements, this function returns an + * incorrect value. + * + * \param[in] earlierTime -- A value from a timer + * \param[in] laterTime -- A value from a timer taken after \a earlierTime + * + * \return The interval between the two points in time + */ +static unsigned HidTimeDiff( const unsigned earlierTime, const unsigned laterTime ) +{ + return ( earlierTime < laterTime ) ? laterTime - earlierTime : UINT_MAX - earlierTime + laterTime; +} From 21ec3cf7bdb7169733634342a628263387714f86 Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Wed, 16 Oct 2019 12:18:50 +0100 Subject: [PATCH 13/16] Put the processing of the Set Idle request in a static function. This reorganisation prepares this file for adding other HID Class-specific requests without the --- lib_xua/src/hid/hid.xc | 105 ++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 43 deletions(-) diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index da4f5c78..dd988f9c 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -13,10 +13,11 @@ 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 unsigned HidTimeDiff( const unsigned earlierTime, const unsigned laterTime ); +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 ) { @@ -38,45 +39,7 @@ XUD_Result_t HidInterfaceClassRequests( switch ( sp.bRequest ) { case HID_SET_IDLE: - /* - The Set Idle request wValue field contains two sub-fields: - - Duration in the MSB; and - - Report ID in the LSB. - - The Duration field specifies how long the USB Device responds with NAK provided the HID data hasn't changed. - Zero means indefinitely. - The value is in units of 4ms. - - The Report ID identifies the HID report that the USB Host wishes to silence. - - The Set Idle request xIndex field contains the interface number. - */ - uint16_t duration = ( sp.wValue & 0xFF00 ) >> 6; // Transform from units of 4ms into units of 1ms. - uint8_t reportId = sp.wValue & 0x00FF; - uint16_t interfaceNum = sp.wIndex; - - /* - As long as our HID Report Descriptor does not include a Report ID, any Report ID value other than zero - indicates an error by the USB Host (see xua_ep0_descriptors.h for the definition of the HID - Report Descriptor). - - Any Interface value other than INTERFACE_NUMBER_HID indicates an error by the USB Host. - */ - if(( 0U == reportId ) && ( interfaceNum == INTERFACE_NUMBER_HID )) { - s_hidIdleActive = (( 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 ); - } else { - s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; - s_hidIndefiniteDuration = 0U; - } - - result = XUD_DoSetRequestStatus( c_ep0_in ); - } + result = HidProcessSetIdleRequest( c_ep0_out, c_ep0_in, sp ); break; default: @@ -177,6 +140,62 @@ static unsigned HidFindSetIdleActivationPoint( const unsigned currentPeriod, con return result; } +/** + * \brief Process a Set Idle request + * + * \param[in] c_ep0_out -- the channel that carries data from Endpoint 0 + * \param[in] c_ep0_in -- the channel that carries data for Endpoint 0 + * \param[in] sp -- a structure containing the Set Idle data + * + * \return An XUD status value + */ +static XUD_Result_t HidProcessSetIdleRequest( XUD_ep c_ep0_out, XUD_ep c_ep0_in, USB_SetupPacket_t &sp ) +{ + XUD_Result_t result = XUD_RES_ERR; + + /* + The Set Idle request wValue field contains two sub-fields: + - Duration in the MSB; and + - Report ID in the LSB. + + The Duration field specifies how long the USB Device responds with NAK provided the HID data hasn't changed. + Zero means indefinitely. + The value is in units of 4ms. + + The Report ID identifies the HID report that the USB Host wishes to silence. + + The Set Idle request xIndex field contains the interface number. + */ + uint16_t duration = ( sp.wValue & 0xFF00 ) >> 6; // Transform from units of 4ms into units of 1ms. + uint8_t reportId = sp.wValue & 0x00FF; + uint16_t interfaceNum = sp.wIndex; + + /* + As long as our HID Report Descriptor does not include a Report ID, any Report ID value other than zero + indicates an error by the USB Host (see xua_ep0_descriptors.h for the definition of the HID + Report Descriptor). + + Any Interface value other than INTERFACE_NUMBER_HID indicates an error by the USB Host. + */ + if(( 0U == reportId ) && ( interfaceNum == INTERFACE_NUMBER_HID )) { + s_hidIdleActive = (( 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 ); + } else { + s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; + s_hidIndefiniteDuration = 0U; + } + + result = XUD_DoSetRequestStatus( c_ep0_in ); + } + + return result; +} + /** * \brief Calculate the difference between two points in time * From 024c0304f1008a49f628ac8c4139ce56e1855c63 Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Wed, 16 Oct 2019 14:57:33 +0100 Subject: [PATCH 14/16] Reverse the order of operands in an equals comparison to allow the compiler to catch a mistaken use of '=' instead of '=='. --- lib_xua/src/hid/hid.xc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index dd988f9c..72a4fea6 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -177,7 +177,7 @@ 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 ) && ( interfaceNum == INTERFACE_NUMBER_HID )) { + if(( 0U == reportId ) && ( INTERFACE_NUMBER_HID == interfaceNum )) { s_hidIdleActive = (( 0U == duration ) || ( ENDPOINT_INT_INTERVAL_IN_HID < duration )); if( s_hidIdleActive ) { From a7c0ac623420344d20b919ad0d93a9d8efb6934b Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Wed, 16 Oct 2019 15:58:06 +0100 Subject: [PATCH 15/16] Protect configurations that do not include HID functionality from HID code. --- lib_xua/src/hid/hid.xc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index 72a4fea6..bdd5aef3 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -5,6 +5,7 @@ #include "xud_std_requests.h" #include "xua_hid.h" +#if( 0 < HID_CONTROLS ) #define MS_IN_TICKS 100000U static unsigned s_hidIdleActive = 0U; @@ -216,3 +217,5 @@ static unsigned HidTimeDiff( const unsigned earlierTime, const unsigned laterTim { return ( earlierTime < laterTime ) ? laterTime - earlierTime : UINT_MAX - earlierTime + laterTime; } + +#endif /* ( 0 < HID_CONTROLS ) */ From a6f3daf5817d6ff3da694086fa511dd52620e4a2 Mon Sep 17 00:00:00 2001 From: Michael Banther Date: Wed, 16 Oct 2019 16:03:52 +0100 Subject: [PATCH 16/16] Update change log. --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 35c363bc..e3d99159 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,7 @@ lib_xua Change Log ---------- * ADDED: UAC1 HID support with simulated Voice Command detection reported every 10 seconds + * ADDED: Support for USB HID Set Idle request 0.2.0 -----