Initial implementation of the USB HID Set_Idle Request.

This code builds successfully.  It has not been tested even a little.
This commit is contained in:
Michael Banther
2019-10-10 17:21:21 +01:00
parent 8ba9a0064c
commit 1808d7affa
4 changed files with 196 additions and 20 deletions

View File

@@ -60,4 +60,8 @@ enum USBInterfaceNumber
INTERFACE_COUNT /* End marker */
};
#if( 0 < HID_CONTROLS )
#define ENDPOINT_INT_INTERVAL_IN_HID 0x08
#endif
#endif

View File

@@ -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
};

View File

@@ -1,26 +1,192 @@
#include <xs1.h>
#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;
}

View File

@@ -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 );