Merge pull request #3 from michaelb/pendragon_hid_add_requests

Pendragon hid add requests
This commit is contained in:
larry
2019-10-17 14:10:09 +01:00
committed by GitHub Enterprise
6 changed files with 271 additions and 21 deletions

View File

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

View File

@@ -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)
@@ -60,4 +65,8 @@ enum USBInterfaceNumber
INTERFACE_COUNT /* End marker */
};
#if( 0 < HID_CONTROLS )
#define ENDPOINT_INT_INTERVAL_IN_HID 0x08
#endif
#endif

View File

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

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,221 @@
#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;
#if( 0 < HID_CONTROLS )
#define MS_IN_TICKS 100000U
unsigned HidIsSetIdleSilenced(void)
static unsigned s_hidIdleActive = 0U;
static unsigned s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS;
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 )
{
return hidSetIdle;
s_hidNextReportTime = s_hidReportTime + s_hidCurrentPeriod;
}
XUD_Result_t HidInterfaceClassRequests(XUD_ep c_ep0_out, XUD_ep c_ep0_in,
USB_SetupPacket_t &sp)
void HidCaptureReportTime( void )
{
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;
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:
result = HidProcessSetIdleRequest( c_ep0_out, c_ep0_in, sp );
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 = ( s_hidIndefiniteDuration || ( timeafter( s_hidNextReportTime, currentTime )));
}
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 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 ) && ( INTERFACE_NUMBER_HID == interfaceNum )) {
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;
}
return XUD_RES_ERR;
result = XUD_DoSetRequestStatus( c_ep0_in );
}
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;
}
#endif /* ( 0 < HID_CONTROLS ) */

View File

@@ -3,7 +3,52 @@
#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));
/**
* \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 );
unsigned HidIsSetIdleSilenced(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,
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 );