Changes to support the Set Idle request with multiple Report IDs

This commit is contained in:
mbanth
2021-12-07 18:50:28 +00:00
parent b4d5cb93f1
commit 8c9e8b11b9
5 changed files with 275 additions and 65 deletions

View File

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

View File

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

View File

@@ -3,6 +3,7 @@
#include <assert.h>
#include <stddef.h>
#include <xs1.h>
#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;

View File

@@ -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__

View File

@@ -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_