forked from PAWPAW-Mirror/lib_xua
Merge pull request #3 from michaelb/pendragon_hid_add_requests
Pendragon hid add requests
This commit is contained in:
@@ -5,6 +5,7 @@ lib_xua Change Log
|
|||||||
----------
|
----------
|
||||||
* ADDED: UAC1 HID support with simulated Voice Command detection reported
|
* ADDED: UAC1 HID support with simulated Voice Command detection reported
|
||||||
every 10 seconds
|
every 10 seconds
|
||||||
|
* ADDED: Support for USB HID Set Idle request
|
||||||
|
|
||||||
0.2.0
|
0.2.0
|
||||||
-----
|
-----
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
#ifndef __DESCRIPTOR_DEFS_H__
|
#ifndef __DESCRIPTOR_DEFS_H__
|
||||||
#define __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)
|
#if (NUM_USB_CHAN_IN > 0) && (NUM_USB_CHAN_OUT > 0)
|
||||||
#define AUDIO_INTERFACE_COUNT 3
|
#define AUDIO_INTERFACE_COUNT 3
|
||||||
#elif (NUM_USB_CHAN_IN > 0) || (NUM_USB_CHAN_OUT > 0)
|
#elif (NUM_USB_CHAN_IN > 0) || (NUM_USB_CHAN_OUT > 0)
|
||||||
@@ -60,4 +65,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
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
extern void device_reboot(void);
|
extern void device_reboot(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HID_CONTROLS
|
#if( 0 < HID_CONTROLS )
|
||||||
#include "xua_hid.h"
|
#include "xua_hid.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -993,7 +993,7 @@ void XUA_Endpoint0_lite_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if HID_CONTROLS
|
#if( 0 < HID_CONTROLS )
|
||||||
if (interfaceNum == INTERFACE_NUMBER_HID)
|
if (interfaceNum == INTERFACE_NUMBER_HID)
|
||||||
{
|
{
|
||||||
result = HidInterfaceClassRequests(ep0_out, ep0_in, &sp);
|
result = HidInterfaceClassRequests(ep0_out, ep0_in, &sp);
|
||||||
|
|||||||
@@ -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,221 @@
|
|||||||
#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;
|
#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,
|
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 )
|
USB_SetupPacket_t &sp )
|
||||||
{
|
{
|
||||||
|
XUD_Result_t result = XUD_RES_ERR;
|
||||||
|
|
||||||
switch ( sp.bRequest ) {
|
switch ( sp.bRequest ) {
|
||||||
case HID_SET_IDLE:
|
case HID_SET_IDLE:
|
||||||
printstr("HID_SET_IDLE\n");
|
result = HidProcessSetIdleRequest( c_ep0_out, c_ep0_in, sp );
|
||||||
hidSetIdle = 1; // TODO implement duration
|
break;
|
||||||
return XUD_DoSetRequestStatus(c_ep0_in);
|
|
||||||
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 = ( 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ) */
|
||||||
|
|||||||
@@ -3,7 +3,52 @@
|
|||||||
#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,
|
/**
|
||||||
|
* \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,
|
||||||
REFERENCE_PARAM( USB_SetupPacket_t, sp ));
|
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 );
|
unsigned HidIsSetIdleSilenced( void );
|
||||||
|
|||||||
Reference in New Issue
Block a user