forked from PAWPAW-Mirror/lib_xua
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:
@@ -60,4 +60,8 @@ enum USBInterfaceNumber
|
|||||||
INTERFACE_COUNT /* End marker */
|
INTERFACE_COUNT /* End marker */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if( 0 < HID_CONTROLS )
|
||||||
|
#define ENDPOINT_INT_INTERVAL_IN_HID 0x08
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2184,7 +2184,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2=
|
|||||||
ENDPOINT_ADDRESS_IN_HID, /* 2 bEndpointAddress */
|
ENDPOINT_ADDRESS_IN_HID, /* 2 bEndpointAddress */
|
||||||
3, /* 3 bmAttributes (INTERRUPT) */
|
3, /* 3 bmAttributes (INTERRUPT) */
|
||||||
64, /* 4 wMaxPacketSize */
|
64, /* 4 wMaxPacketSize */
|
||||||
8, /* 6 bInterval */
|
ENDPOINT_INT_INTERVAL_IN_HID, /* 6 bInterval */
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2862,7 +2862,7 @@ unsigned char cfgDesc_Audio1[] =
|
|||||||
0x03, /* 3 bmAttributes (INTERRUPT) */
|
0x03, /* 3 bmAttributes (INTERRUPT) */
|
||||||
0x40, /* 4 wMaxPacketSize */
|
0x40, /* 4 wMaxPacketSize */
|
||||||
0x00, /* 5 wMaxPacketSize */
|
0x00, /* 5 wMaxPacketSize */
|
||||||
0x08, /* 6 bInterval */
|
ENDPOINT_INT_INTERVAL_IN_HID, /* 6 bInterval */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,26 +1,192 @@
|
|||||||
#include <xs1.h>
|
#include <xs1.h>
|
||||||
#include "xud.h"
|
#include "descriptor_defs.h"
|
||||||
#include "hid.h"
|
#include "hid.h"
|
||||||
|
#include "xud.h"
|
||||||
#include "xud_std_requests.h"
|
#include "xud_std_requests.h"
|
||||||
#include "xua_hid.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) {
|
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:
|
case HID_SET_IDLE:
|
||||||
printstr("HID_SET_IDLE\n");
|
printstr("HID_SET_IDLE\n");
|
||||||
hidSetIdle = 1; // TODO implement duration
|
/*
|
||||||
return XUD_DoSetRequestStatus(c_ep0_in);
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return XUD_RES_ERR;
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,13 @@
|
|||||||
#include "xud.h"
|
#include "xud.h"
|
||||||
#include "xud_std_requests.h"
|
#include "xud_std_requests.h"
|
||||||
|
|
||||||
XUD_Result_t HidInterfaceClassRequests(XUD_ep c_ep0_out, XUD_ep c_ep0_in,
|
void HidCalcNextReportTime( void );
|
||||||
REFERENCE_PARAM(USB_SetupPacket_t, sp));
|
|
||||||
|
|
||||||
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 );
|
||||||
|
|||||||
Reference in New Issue
Block a user