Merge pull request #236 from ACascarino/feature/set_idle_work

Add swlocks to HID files, and correct operation of SetIdle
This commit is contained in:
Michael Banther
2022-01-12 16:35:39 +00:00
committed by GitHub
11 changed files with 373 additions and 328 deletions

View File

@@ -10,6 +10,10 @@ lib_xua Change Log
* CHANGED: Renamed the HID API file xua_hid_report_descriptor.h to
xua_hid_report.h
* Changes to dependencies:
- lib_locks: Added dependency 2.1.0
2.1.1
-----

View File

@@ -4,7 +4,8 @@ DEPENDENT_MODULES = lib_logging(>=3.0.0) \
lib_xassert(>=4.0.0) \
lib_xud(>=2.0.1) \
lib_spdif(>=4.0.0) \
lib_mic_array(>=4.0.0)
lib_mic_array(>=4.0.0) \
lib_locks(>=2.0.3)
MODULE_XCC_FLAGS = $(XCC_FLAGS) \
-O3 \

View File

@@ -1,4 +1,4 @@
// Copyright 2011-2021 XMOS LIMITED.
// Copyright 2011-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include "xua.h"
#if XUA_USB_EN
@@ -93,7 +93,7 @@ unsigned int fb_clocks[4];
void XUA_Buffer(
register chanend c_aud_out,
#if (NUM_USB_CHAN_IN > 0)
register chanend c_aud_in,
register chanend c_aud_in,
#endif
#if (NUM_USB_CHAN_IN == 0) || defined (UAC_FORCE_FEEDBACK_EP)
chanend c_aud_fb,
@@ -198,7 +198,7 @@ unsafe{volatile unsigned * unsafe masterClockFreq_ptr;}
*/
void XUA_Buffer_Ep(register chanend c_aud_out,
#if (NUM_USB_CHAN_IN > 0)
register chanend c_aud_in,
register chanend c_aud_in,
#endif
#if (NUM_USB_CHAN_IN == 0) || defined (UAC_FORCE_FEEDBACK_EP)
chanend c_aud_fb,
@@ -373,15 +373,13 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#endif
#if( 0 < HID_CONTROLS )
UserHIDInit();
while (!hidIsReportDescriptorPrepared())
;
/* Get the a report - we don't really care which it is, so long as there's some data we can grab. */
int hidReportLength = (int) UserHIDGetData(hidGetNextValidReportId(0), g_hidData);
XUD_SetReady_In(ep_hid, g_hidData, hidReportLength);
UserHIDInit();
unsigned hid_ready_flag = 0U;
unsigned hid_ready_id = 0U;
#endif
@@ -392,6 +390,8 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#endif
#endif
timer tmr;
while(1)
{
XUD_Result_t result;
@@ -448,9 +448,9 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
if(receivedSampleFreq != AUDIO_STOP_FOR_DFU)
{
sampleFreq = receivedSampleFreq;
#ifdef FB_TOLERANCE_TEST
#ifdef FB_TOLERANCE_TEST
expected_fb = ((sampleFreq * 0x2000) / frameTime);
#endif
#endif
/* Reset FB */
/* Note, Endpoint 0 will hold off host for a sufficient period to allow our feedback
* to stabilise (i.e. sofCount == 128 to fire) */
@@ -711,7 +711,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
#endif
sofCount++;
}
break;
break;
#if (NUM_USB_CHAN_IN > 0)
/* Sent audio packet DEVICE -> HOST */
@@ -719,8 +719,8 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
{
/* Inform stream that buffer sent */
SET_SHARED_GLOBAL0(g_aud_to_host_flag, bufferIn+1);
break;
}
break;
#endif
#if (NUM_USB_CHAN_OUT > 0)
@@ -740,8 +740,8 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
{
XUD_SetReady_In(ep_aud_fb, (fb_clocks, unsigned char[]), 3);
}
break;
}
break;
#endif
/* Received Audio packet HOST -> DEVICE. Datalength written to length */
case XUD_GetData_Select(c_aud_out, ep_aud_out, length, result):
@@ -752,175 +752,165 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
/* Sync with decouple thread */
SET_SHARED_GLOBAL0(g_aud_from_host_flag, 1);
}
break;
}
#endif
#ifdef MIDI
case XUD_GetData_Select(c_midi_from_host, ep_midi_from_host, length, result):
case XUD_GetData_Select(c_midi_from_host, ep_midi_from_host, length, result):
if((result == XUD_RES_OKAY) && (length > 0))
{
/* Get buffer data from host - MIDI OUT from host always into a single buffer */
midi_data_remaining_to_device = length;
midi_from_host_rdptr = midi_from_host_buffer;
if (midi_data_remaining_to_device)
if((result == XUD_RES_OKAY) && (length > 0))
{
read_via_xc_ptr(datum, midi_from_host_rdptr);
outuint(c_midi, datum);
midi_from_host_rdptr += 4;
midi_data_remaining_to_device -= 4;
/* Get buffer data from host - MIDI OUT from host always into a single buffer */
midi_data_remaining_to_device = length;
midi_from_host_rdptr = midi_from_host_buffer;
if (midi_data_remaining_to_device)
{
read_via_xc_ptr(datum, midi_from_host_rdptr);
outuint(c_midi, datum);
midi_from_host_rdptr += 4;
midi_data_remaining_to_device -= 4;
}
}
}
break;
break;
/* MIDI IN to host */
case XUD_SetData_Select(c_midi_to_host, ep_midi_to_host, result):
/* MIDI IN to host */
case XUD_SetData_Select(c_midi_to_host, ep_midi_to_host, result):
/* The buffer has been sent to the host, so we can ack the midi thread */
if (midi_data_collected_from_device != 0)
{
/* Swap the collecting and sending buffer */
swap(midi_to_host_buffer_being_collected, midi_to_host_buffer_being_sent);
/* The buffer has been sent to the host, so we can ack the midi thread */
if (midi_data_collected_from_device != 0)
{
/* Swap the collecting and sending buffer */
swap(midi_to_host_buffer_being_collected, midi_to_host_buffer_being_sent);
/* Request to send packet */
XUD_SetReady_InPtr(ep_midi_to_host, midi_to_host_buffer_being_sent, midi_data_collected_from_device);
/* Request to send packet */
XUD_SetReady_InPtr(ep_midi_to_host, midi_to_host_buffer_being_sent, midi_data_collected_from_device);
/* Mark as waiting for host to poll us */
midi_waiting_on_send_to_host = 1;
/* Reset the collected data count */
midi_data_collected_from_device = 0;
}
else
{
midi_waiting_on_send_to_host = 0;
}
break;
/* Mark as waiting for host to poll us */
midi_waiting_on_send_to_host = 1;
/* Reset the collected data count */
midi_data_collected_from_device = 0;
}
else
{
midi_waiting_on_send_to_host = 0;
}
break;
#endif
#ifdef IAP
/* IAP OUT from host. Datalength writen to tmp */
case XUD_GetData_Select(c_iap_from_host, ep_iap_from_host, length, result):
/* IAP OUT from host. Datalength writen to tmp */
case XUD_GetData_Select(c_iap_from_host, ep_iap_from_host, length, result):
if((result == XUD_RES_OKAY) && (length > 0))
{
iap_data_remaining_to_device = length;
if(iap_data_remaining_to_device)
if((result == XUD_RES_OKAY) && (length > 0))
{
// Send length first so iAP thread knows how much data to expect
// Don't expect ack from this to make it simpler
outuint(c_iap, iap_data_remaining_to_device);
iap_data_remaining_to_device = length;
/* Send out first byte in buffer */
datum_iap = iap_from_host_buffer[0];
outuint(c_iap, datum_iap);
if(iap_data_remaining_to_device)
{
// Send length first so iAP thread knows how much data to expect
// Don't expect ack from this to make it simpler
outuint(c_iap, iap_data_remaining_to_device);
/* Set read ptr to next byte in buffer */
iap_from_host_rdptr = 1;
iap_data_remaining_to_device -= 1;
/* Send out first byte in buffer */
datum_iap = iap_from_host_buffer[0];
outuint(c_iap, datum_iap);
/* Set read ptr to next byte in buffer */
iap_from_host_rdptr = 1;
iap_data_remaining_to_device -= 1;
}
}
}
break;
break;
/* IAP IN to host */
case XUD_SetData_Select(c_iap_to_host, ep_iap_to_host, result):
/* IAP IN to host */
case XUD_SetData_Select(c_iap_to_host, ep_iap_to_host, result):
if(result == XUD_RES_RST)
{
XUD_ResetEndpoint(ep_iap_to_host, null);
if(result == XUD_RES_RST)
{
XUD_ResetEndpoint(ep_iap_to_host, null);
#ifdef IAP_INT_EP
XUD_ResetEndpoint(ep_iap_to_host_int, null);
XUD_ResetEndpoint(ep_iap_to_host_int, null);
#endif
iap_send_reset(c_iap);
iap_draining_chan = 1; // Drain c_iap until a reset is sent back
iap_data_collected_from_device = 0;
iap_data_remaining_to_device = -1;
iap_expected_data_length = 0;
iap_from_host_rdptr = 0;
}
else
{
/* Send out an iAP packet to host, ACK last msg from iAP to let it know we can move on..*/
iap_send_ack(c_iap);
}
break; /* IAP IN to host */
iap_send_reset(c_iap);
iap_draining_chan = 1; // Drain c_iap until a reset is sent back
iap_data_collected_from_device = 0;
iap_data_remaining_to_device = -1;
iap_expected_data_length = 0;
iap_from_host_rdptr = 0;
}
else
{
/* Send out an iAP packet to host, ACK last msg from iAP to let it know we can move on..*/
iap_send_ack(c_iap);
}
break; /* IAP IN to host */
#ifdef IAP_INT_EP
case XUD_SetData_Select(c_iap_to_host_int, ep_iap_to_host_int, result):
case XUD_SetData_Select(c_iap_to_host_int, ep_iap_to_host_int, result):
/* Do nothing.. */
/* Note, could get a reset notification here, but deal with it in the case above */
break;
/* Do nothing.. */
/* Note, could get a reset notification here, but deal with it in the case above */
break;
#endif
#ifdef IAP_EA_NATIVE_TRANS
/* iAP EA Native Transport OUT from host */
case XUD_GetData_Select(c_iap_ea_native_out, ep_iap_ea_native_out, iap_ea_native_rx_length, result):
if ((result == XUD_RES_OKAY) && iap_ea_native_rx_length > 0)
{
// Notify EA Protocol user code we have iOS app data from XUD
iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_DATA);
}
break;
/* iAP EA Native Transport OUT from host */
case XUD_GetData_Select(c_iap_ea_native_out, ep_iap_ea_native_out, iap_ea_native_rx_length, result):
if ((result == XUD_RES_OKAY) && iap_ea_native_rx_length > 0)
{
// Notify EA Protocol user code we have iOS app data from XUD
iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_DATA);
}
break;
/* iAP EA Native Transport IN to host */
case XUD_SetData_Select(c_iap_ea_native_in, ep_iap_ea_native_in, result):
switch (result)
{
case XUD_RES_RST:
XUD_ResetEndpoint(ep_iap_ea_native_in, null);
// Notify user code of USB reset to allow any state to be cleared
iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_CONTROL);
// Set up the control flag to send to EA Protocol user code when it responds
iap_ea_native_control_flag = EA_NATIVE_RESET;
iap_ea_native_control_to_send = 1;
break;
/* iAP EA Native Transport IN to host */
case XUD_SetData_Select(c_iap_ea_native_in, ep_iap_ea_native_in, result):
switch (result)
{
case XUD_RES_RST:
XUD_ResetEndpoint(ep_iap_ea_native_in, null);
// Notify user code of USB reset to allow any state to be cleared
iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_CONTROL);
// Set up the control flag to send to EA Protocol user code when it responds
iap_ea_native_control_flag = EA_NATIVE_RESET;
iap_ea_native_control_to_send = 1;
break;
case XUD_RES_OKAY: // EA Protocol user data successfully passed to XUD
// Notify user code
iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_CONTROL);
// Set up the control flag to send to EA Protocol user code when it responds
iap_ea_native_control_flag = EA_NATIVE_DATA_SENT;
iap_ea_native_control_to_send = 1;
break;
}
break;
//::
case XUD_RES_OKAY: // EA Protocol user data successfully passed to XUD
// Notify user code
iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_CONTROL);
// Set up the control flag to send to EA Protocol user code when it responds
iap_ea_native_control_flag = EA_NATIVE_DATA_SENT;
iap_ea_native_control_to_send = 1;
break;
}
break;
//::
#endif
#endif
#if( 0 < HID_CONTROLS )
/* HID Report Data */
case (hidIsChangePending(0U) || !HidIsSetIdleSilenced(0U)) => XUD_SetData_Select(c_hid, ep_hid, result):
{
timer tmr;
/* HID Report Data */
case XUD_SetData_Select(c_hid, ep_hid, result):
hid_ready_flag = 0U;
unsigned reportTime;
tmr :> reportTime;
for(unsigned id = hidIsReportIdInUse(); 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(id);
hidClearChangePending(id);
break;
}
}
}
break;
hidCaptureReportTime(hid_ready_id, reportTime);
hidCalcNextReportTime(hid_ready_id);
hidClearChangePending(hid_ready_id);
break;
#endif
#ifdef MIDI
/* Received word from MIDI thread - Check for ACK or Data */
/* Received word from MIDI thread - Check for ACK or Data */
case midi_get_ack_or_data(c_midi, is_ack, datum):
if (is_ack)
{
/* An ack from the midi/uart thread means it has accepted some data we sent it
* we are okay to send another word */
* we are okay to send another word */
if (midi_data_remaining_to_device <= 0)
{
/* We have read an entire packet - Mark ready to receive another */
@@ -985,7 +975,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
if (is_ack_iap)
{
/* An ack from the iap/uart thread means it has accepted some data we sent it
* we are okay to send another word */
* we are okay to send another word */
if (iap_data_remaining_to_device == 0)
{
/* We have read an entire packet - Mark ready to receive another */
@@ -1018,7 +1008,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
}
else
{
// Too many events from device - drop
// Too many events from device - drop
}
/* Once we have the whole message, sent it to host */
@@ -1056,7 +1046,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
}
break;
# if IAP_EA_NATIVE_TRANS
# if IAP_EA_NATIVE_TRANS
/* Change of EA Native Transport interface setting */
case inuint_byref(c_iap_ea_native_ctrl, iap_ea_native_interface_alt_setting):
/* Handshake */
@@ -1114,6 +1104,26 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
break;
}
break;
default:
#if ( 0 < HID_CONTROLS )
if (!hid_ready_flag)
{
for (unsigned id = hidIsReportIdInUse(); id < hidGetReportIdLimit(); id++)
{
if ( hidIsChangePending(id) || !HidIsSetIdleSilenced(id) )
{
int hidDataLength = (int) UserHIDGetData(id, g_hidData);
XUD_SetReady_In(ep_hid, g_hidData, hidDataLength);
hid_ready_id = id;
hid_ready_flag = 1U;
break;
}
}
}
#endif
break;
//::
#endif
@@ -1121,7 +1131,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out,
}
}
}
#endif /* XUA_USB_EN */

View File

@@ -1,4 +1,4 @@
// Copyright 2019-2021 XMOS LIMITED.
// Copyright 2019-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdint.h>
#include <xs1.h>
@@ -9,6 +9,10 @@
#include "xua_hid.h"
#include "xua_hid_report.h"
#define DEBUG_UNIT HID_XC
#define DEBUG_PRINT_ENABLE_HID_XC 0
#include "debug_print.h"
#if( 0 < HID_CONTROLS )
static unsigned HidCalcNewReportTime( const unsigned currentPeriod, const unsigned reportTime, const unsigned reportToSetIdleInterval, const unsigned newPeriod );
static unsigned HidCalcReportToSetIdleInterval( const unsigned reportTime );
@@ -37,26 +41,26 @@ XUD_Result_t HidInterfaceClassRequests(
unsigned HidIsSetIdleSilenced( const unsigned id )
{
unsigned isSilenced = hidIsIdleActive( id );
unsigned isSilenced = hidIsIdleActive( id );
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 = ( 0U == hidGetReportPeriod( id ) || ( timeafter( hidGetNextReportTime( id ), currentTime )));
}
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 = ( 0U == hidGetReportPeriod( id ) || ( timeafter( hidGetNextReportTime( id ), currentTime )));
}
return isSilenced;
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
* Interface Devices (HID) Version 1.11, 'currently executing period' and 'current period' have
* been interpreted 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.
*
@@ -147,8 +151,6 @@ static void HidUpdateReportPeriod( unsigned reportId, unsigned reportDuration )
unsigned nextReportTime = HidCalcNewReportTime( currentPeriod, reportTime, reportToSetIdleInterval, reportDuration * MS_IN_TICKS );
hidSetNextReportTime( reportId, nextReportTime );
currentPeriod = reportDuration * MS_IN_TICKS;
} else {
currentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS;
}
hidSetReportPeriod( reportId, currentPeriod );

View File

@@ -1,4 +1,4 @@
// Copyright 2021 XMOS LIMITED.
// Copyright 2021-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <assert.h>
#include <stddef.h>
@@ -9,15 +9,20 @@
#include "descriptor_defs.h"
#include "xua_hid_report.h"
#include "hid_report_descriptor.h"
#include "swlock.h"
#define HID_REPORT_ITEM_LOCATION_SIZE ( 1 )
#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 ))
swlock_t hidStaticVarLock = SWLOCK_INITIAL_VALUE;
/*
* Each element in s_hidChangePending corresponds to an element in hidReports.
*/
static unsigned s_hidChangePending[ HID_REPORT_COUNT ];
static unsigned char s_hidReportDescriptor[ HID_REPORT_DESCRIPTOR_MAX_LENGTH ];
static size_t s_hidReportDescriptorLength;
@@ -131,33 +136,42 @@ static unsigned hidGetUsagePage( const unsigned id );
*/
static size_t hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char** outPtrPtr );
unsigned hidIsReportIdInUse ( void ) {
return !hidIsReportIdValid(0U);
}
void hidCalcNextReportTime( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
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 ];
}
}
swlock_release(&hidStaticVarLock);
}
void hidCaptureReportTime( const unsigned id, const unsigned time )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
s_hidReportTime[ idx ] = time;
}
}
swlock_release(&hidStaticVarLock);
}
void hidClearChangePending( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if(( id == 0U ) || ( id == hidGetElementReportId( hidReports[ idx ]->location ))) {
s_hidChangePending[ idx ] = 0U;
break;
}
}
swlock_release(&hidStaticVarLock);
}
static unsigned hidGetElementBitLocation( const unsigned short location )
@@ -203,74 +217,56 @@ static unsigned hidGetItemType( const unsigned char header )
}
unsigned hidGetNextReportTime( const unsigned id ) {
swlock_acquire(&hidStaticVarLock);
unsigned retVal = 0U;
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
for ( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
retVal = s_hidNextReportTime[ idx ];
}
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidIsReportDescriptorPrepared( void )
{
return s_hidReportDescriptorPrepared;
}
unsigned char* hidGetReportDescriptor( void )
{
unsigned char* retVal = NULL;
swlock_acquire(&hidStaticVarLock);
if( s_hidReportDescriptorPrepared ) {
retVal = s_hidReportDescriptor;
}
swlock_release(&hidStaticVarLock);
return retVal;
}
size_t hidGetReportDescriptorLength( void )
{
swlock_acquire(&hidStaticVarLock);
size_t retVal = ( s_hidReportDescriptorPrepared ) ? s_hidReportDescriptorLength : 0U;
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidGetReportIdLimit ( void ) {
unsigned retVal = 0U;
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
unsigned reportId = hidGetElementReportId( hidReports[ idx ]->location );
if( reportId >= retVal ) {
retVal = reportId + 1;
}
}
return retVal;
}
unsigned hidIsReportIdInUse ( void ) {
if ( hidGetElementReportId( hidReports[ 0 ]->location ) ) {
return 1;
}
return 0;
}
unsigned hidIsReportIdValid ( unsigned id ) {
size_t retVal = 0;
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
unsigned reportId = hidGetElementReportId( hidReports[ idx ]->location );
if( reportId == id ) {
retVal = 1;
break;
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidGetNextValidReportId ( unsigned idPrev ) {
size_t retIndex = 0;
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
unsigned reportId = hidGetElementReportId( hidReports[ idx ]->location );
if( reportId == idPrev ) {
@@ -279,7 +275,9 @@ unsigned hidGetNextValidReportId ( unsigned idPrev ) {
}
}
return hidGetElementReportId( hidReports[ retIndex ]->location );
unsigned retVal = hidGetElementReportId( hidReports[ retIndex ]->location );
swlock_release(&hidStaticVarLock);
return retVal;
}
#define HID_CONFIGURABLE_ELEMENT_COUNT ( sizeof hidConfigurableElements / sizeof ( USB_HID_Report_Element_t* ))
@@ -294,7 +292,10 @@ unsigned hidGetReportItem(
{
unsigned retVal = HID_STATUS_BAD_ID;
for( size_t elementIdx = 0U; elementIdx < HID_CONFIGURABLE_ELEMENT_COUNT; ++elementIdx ) {
swlock_acquire(&hidStaticVarLock);
USB_HID_Report_Element_t element = *hidConfigurableElements[ elementIdx ];
swlock_release(&hidStaticVarLock);
unsigned bBit = hidGetElementBitLocation( element.location );
unsigned bByte = hidGetElementByteLocation( element.location );
unsigned bId = hidGetElementReportId( element.location );
@@ -320,6 +321,7 @@ unsigned hidGetReportItem(
size_t hidGetReportLength( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
size_t retVal = 0U;
if( s_hidReportDescriptorPrepared ) {
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
@@ -328,11 +330,13 @@ size_t hidGetReportLength( const unsigned id )
}
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidGetReportPeriod( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
unsigned retVal = 0U;
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
@@ -340,10 +344,13 @@ unsigned hidGetReportPeriod( const unsigned id )
break;
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidGetReportTime( const unsigned id ) {
unsigned hidGetReportTime( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
unsigned retVal = 0U;
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
@@ -351,55 +358,85 @@ unsigned hidGetReportTime( const unsigned id ) {
retVal = s_hidReportTime[ idx ];
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
static unsigned hidGetUsagePage( const unsigned id )
{
unsigned retVal = 0U;
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
retVal = hidReports[ idx ]->item.data[ 0 ];
break;
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidIsChangePending( const unsigned id )
{
unsigned retVal = 0U;
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == 0U && s_hidChangePending[ idx ] != 0U ) {
retVal = 1;
} else if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
retVal = ( s_hidChangePending[ idx ] != 0U );
break;
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidIsIdleActive( const unsigned id )
{
unsigned retVal = 0U;
if( 0U == id ) {
retVal = 1U;
}
swlock_acquire(&hidStaticVarLock);
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 )) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
retVal = ( s_hidIdleActive[ idx ] != 0U );
break;
}
}
return retVal;
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidIsReportDescriptorPrepared( void )
{
swlock_acquire(&hidStaticVarLock);
unsigned retVal = s_hidReportDescriptorPrepared;
swlock_release(&hidStaticVarLock);
return retVal;
}
unsigned hidIsReportIdValid ( unsigned id ) {
size_t retVal = 0;
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
unsigned reportId = hidGetElementReportId( hidReports[ idx ]->location );
if( reportId == id ) {
retVal = 1;
break;
}
}
swlock_release(&hidStaticVarLock);
return retVal;
}
void hidPrepareReportDescriptor( void )
{
swlock_acquire(&hidStaticVarLock);
if( !s_hidReportDescriptorPrepared ) {
s_hidReportDescriptorLength = 0U;
unsigned char* ptr = s_hidReportDescriptor;
@@ -410,49 +447,60 @@ void hidPrepareReportDescriptor( void )
s_hidReportDescriptorPrepared = 1U;
}
swlock_release(&hidStaticVarLock);
}
void hidReportInit( void )
{
swlock_acquire(&hidStaticVarLock);
for( unsigned idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
s_hidCurrentPeriod[ idx ] = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS;
s_hidCurrentPeriod[ idx ] = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS * HID_REPORT_COUNT;
}
memset( s_hidIdleActive, 0, sizeof( s_hidIdleActive ) );
memset( s_hidChangePending, 0, sizeof( s_hidChangePending ) );
swlock_release(&hidStaticVarLock);
}
void hidResetReportDescriptor( void )
{
swlock_acquire(&hidStaticVarLock);
s_hidReportDescriptorPrepared = 0U;
swlock_release(&hidStaticVarLock);
}
void hidSetChangePending( const unsigned id )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
s_hidChangePending[ idx ] = 1U;
break;
}
}
swlock_release(&hidStaticVarLock);
}
void hidSetIdle( const unsigned id, const unsigned state )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
s_hidIdleActive[ idx ] = ( state != 0U );
break;
}
}
swlock_release(&hidStaticVarLock);
}
void hidSetNextReportTime( const unsigned id, const unsigned time )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
s_hidNextReportTime[ idx ] = time;
}
}
swlock_release(&hidStaticVarLock);
}
unsigned hidSetReportItem(
@@ -478,7 +526,10 @@ unsigned hidSetReportItem(
retVal = HID_STATUS_BAD_HEADER;
} else {
for( size_t elementIdx = 0U; elementIdx < HID_CONFIGURABLE_ELEMENT_COUNT; ++elementIdx ) {
swlock_acquire(&hidStaticVarLock);
USB_HID_Report_Element_t element = *hidConfigurableElements[ elementIdx ];
swlock_release(&hidStaticVarLock);
unsigned bBit = hidGetElementBitLocation( element.location );
unsigned bByte = hidGetElementByteLocation( element.location );
unsigned bId = hidGetElementReportId( element.location );
@@ -501,7 +552,9 @@ unsigned hidSetReportItem(
element.item.data[ dataIdx ] = 0U;
}
swlock_acquire(&hidStaticVarLock);
*hidConfigurableElements[ elementIdx ] = element;
swlock_release(&hidStaticVarLock);
retVal = HID_STATUS_GOOD;
break;
}
@@ -510,18 +563,19 @@ unsigned hidSetReportItem(
}
}
}
return retVal;
}
void hidSetReportPeriod( const unsigned id, const unsigned period )
{
swlock_acquire(&hidStaticVarLock);
for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) {
if( id == hidGetElementReportId( hidReports[ idx ]->location )) {
s_hidCurrentPeriod[ idx ] = period;
break;
}
}
swlock_release(&hidStaticVarLock);
}
static size_t hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char** outPtrPtr )
@@ -796,4 +850,4 @@ unsigned hidReportValidate( void )
} else {
return hidReportValidateInfoStruct( &info );
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2019-2021 XMOS LIMITED.
// Copyright 2019-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/**

View File

@@ -1,4 +1,4 @@
// Copyright 2021 XMOS LIMITED.
// Copyright 2021-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
/**
@@ -113,6 +113,45 @@ typedef struct
unsigned short location;
} USB_HID_Report_Element_t;
/**
* \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 Register that a previously changed HID Report data has been sent
* to the USB Host.
@@ -134,22 +173,24 @@ typedef struct
* HID data has been reported to the USB Host.
*
* \warning This function will fail silently if given an id that is not
* either the value zero, or a Report ID that is in use.
* either the value zero (in the case that Report IDs are not in use),
* or a Report ID that is in use.
*
* \param[in] id A HID Report ID.
* Zero clears the pending status of all Report IDs.
* Use zero if the application does not use Report IDs.
*/
void hidClearChangePending( const unsigned id );
/**
* @brief Indicate if the HID Report descriptor has been prepared
*
* \returns A Boolean indicating whether the HID Report descriptor has been prepared.
* \retval True The HID Report descriptor has been prepared.
* \retval False The HID Report descriptor has not been prepared.
* @brief Get the next valid report ID - iterator style.
*
* This function will loop around and start returning the first report ID again once it has
* returned all valid report IDs.
*
* @param idPrev The previous returned id, or 0 if this is the first call
* @return unsigned The next valid report ID.
*/
unsigned hidIsReportDescriptorPrepared( void );
unsigned hidGetNextValidReportId ( unsigned idPrev );
/**
* @brief Get the HID Report descriptor
@@ -190,44 +231,6 @@ size_t hidGetReportDescriptorLength( void );
*/
unsigned hidGetReportIdLimit ( void );
/**
* @brief Does the application use Report IDs?
*
* If the application is not using Report IDs, then the id value that is passed around
* everywhere can just be zero. Otherwise zero is an invalid ID that has a special meaning
* in some cases (read the documentation for each function).
*
* @return Boolean
* @retval 1 Report IDs are in use
* @retval 0 Report IDs are not in use
*/
unsigned hidIsReportIdInUse ( void );
/**
* @brief Is the provided report ID valid for passing to other functions.
*
* e.g If Report IDs are not in use, then only 0 will return true.
* e.g If Report IDs are in use, then 0 will return false and the report IDs that
* are in use will return true when passed to thsi function.
*
* @param id The ID to check
* @return boolean
* @retval 0 The report ID is not valid, other functions may fail silently
* @retval 1 The report ID is valid and can be used as the argument to other functions
*/
unsigned hidIsReportIdValid ( unsigned id );
/**
* @brief Get the next valid report ID - iterator style.
*
* This function will loop around and start returning the first report ID again once it has
* returned all valid report IDs.
*
* @param idPrev The previous returned id, or 0 if this is the first call
* @return unsigned The next valid report ID.
*/
unsigned hidGetNextValidReportId ( unsigned idPrev );
/**
* @brief Get a HID Report descriptor item
*
@@ -265,45 +268,6 @@ 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 for the given \a id
*
@@ -378,18 +342,16 @@ unsigned hidGetReportTime( const unsigned id );
* whether unreported HID data exists for that Report ID.
*
* \warning This function will return zero if given an id that is not
* either the value zero, or a Report ID that is in use.
* either the value zero (in the case that Report IDs are not in use),
* or a Report ID that is in use.
*
* \param[in] id A HID Report ID.
* Zero reports the pending status of all Report IDs.
* Use zero if the application does not use Report IDs.
*
* \returns A Boolean indicating whether the given \a id has a changed
* HID Report not yet sent to the USB Host.
* \retval True The given \a id has changed HID Report data.
* For an \a id of zero, some HID Report has changed data.
* \retval False The given \a id does not have changed HID Report data.
* For an \a id of zero, no HID Report has changed data.
*/
unsigned hidIsChangePending( const unsigned id );
@@ -399,16 +361,48 @@ unsigned hidIsChangePending( const unsigned 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 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 Indicate if the HID Report descriptor has been prepared
*
* \returns A Boolean indicating whether the HID Report descriptor has been prepared.
* \retval True The HID Report descriptor has been prepared.
* \retval False The HID Report descriptor has not been prepared.
*/
unsigned hidIsReportDescriptorPrepared( void );
/**
* @brief Does the application use Report IDs?
*
* If the application is not using Report IDs, then the id value that is passed around
* everywhere can just be zero. Otherwise zero is an invalid ID.
*
* @return Boolean
* @retval 1 Report IDs are in use
* @retval 0 Report IDs are not in use
*/
unsigned hidIsReportIdInUse ( void );
/**
* @brief Is the provided report ID valid for passing to other functions.
*
* e.g If Report IDs are not in use, then only 0 will return true.
* e.g If Report IDs are in use, then 0 will return false and the report IDs that
* are in use will return true when passed to this function.
*
* @param id The ID to check
* @return boolean
* @retval 0 The report ID is not valid, other functions may fail silently
* @retval 1 The report ID is valid and can be used as the argument to other functions
*/
unsigned hidIsReportIdValid ( unsigned id );
/**
* @brief Prepare the USB HID Report descriptor
*
@@ -454,7 +448,8 @@ void hidResetReportDescriptor( void );
* Host.
*
* \warning This function will fail silently if given an id that is not
* either the value zero, or a Report ID that is in use.
* either the value zero (in the case that Report IDs are not in use),
* or a Report ID that is in use.
*
* \param[in] id A HID Report ID.
* Use zero if the application does not use Report IDs.

7
module_locks/README.rst Normal file
View File

@@ -0,0 +1,7 @@
module_locks Readme
===================
:scope: Dummy module
:description: Allows compiling legacy_tests in lib_xua with lib_locks. sc_i2c is used in the tests and it requires module_locks from sc_util, but the definitions in sc_utils conflict with the ones in lib_locks.
:keywords: dummy
:boards: XMOS Dev Kit

View File

@@ -0,0 +1 @@
# Dummy module used in legacy_tests. See README.rst for more details.

View File

@@ -1,4 +1,4 @@
// Copyright 2021 XMOS LIMITED.
// Copyright 2021-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stddef.h>
#include <stdio.h>
@@ -775,7 +775,6 @@ void test_initial_modification_with_subsequent_verification_2( void )
//setIdle and associated timing functionality tests
void test_set_idle( void )
{
unsigned reportIdAll = 0;
unsigned reportId = 1;
unsigned reportId2 = 2;
@@ -785,23 +784,16 @@ void test_set_idle( void )
setIdle = hidIsIdleActive( reportId2 );
TEST_ASSERT_EQUAL_UINT( 0, setIdle );
setIdle = hidIsIdleActive( reportIdAll );
TEST_ASSERT_EQUAL_UINT( 0, setIdle );
hidSetIdle( reportId, 1 );
setIdle = hidIsIdleActive( reportId );
TEST_ASSERT_EQUAL_UINT( 1, setIdle );
setIdle = hidIsIdleActive( reportIdAll );
TEST_ASSERT_EQUAL_UINT( 0, setIdle );
setIdle = hidIsIdleActive( reportId2 );
TEST_ASSERT_EQUAL_UINT( 0, setIdle );
}
void test_set_all_idle( void )
{
unsigned reportIdAll = 0;
unsigned reportId = 1;
unsigned reportId2 = 2;
@@ -811,22 +803,15 @@ void test_set_all_idle( void )
setIdle = hidIsIdleActive( reportId2 );
TEST_ASSERT_EQUAL_UINT( 0, setIdle );
setIdle = hidIsIdleActive( reportIdAll );
TEST_ASSERT_EQUAL_UINT( 0, setIdle );
for ( reportId = 1; reportId <= HID_REPORT_COUNT; ++reportId ) {
hidSetIdle( reportId, 1 );
setIdle = hidIsIdleActive( reportId );
TEST_ASSERT_EQUAL_UINT( 1, setIdle );
}
setIdle = hidIsIdleActive( reportIdAll );
TEST_ASSERT_EQUAL_UINT( 1, setIdle );
}
void test_change_pending( void )
{
unsigned reportIdAll = 0;
unsigned reportId = 1;
unsigned reportId2 = 2;
@@ -836,39 +821,26 @@ void test_change_pending( void )
changePending = hidIsChangePending( reportId2 );
TEST_ASSERT_EQUAL_UINT( 0, changePending );
changePending = hidIsChangePending( reportIdAll );
TEST_ASSERT_EQUAL_UINT( 0, changePending );
hidSetChangePending( reportId );
changePending = hidIsChangePending( reportId );
TEST_ASSERT_EQUAL_UINT( 1, changePending );
changePending = hidIsChangePending( reportIdAll );
TEST_ASSERT_EQUAL_UINT( 1, changePending );
changePending = hidIsChangePending( reportId2 );
TEST_ASSERT_EQUAL_UINT( 0, changePending );
}
void test_change_pending_all( void )
{
unsigned reportIdAll = 0;
unsigned reportId = 1;
unsigned changePending = hidIsChangePending( reportId );
TEST_ASSERT_EQUAL_UINT( 0, changePending );
changePending = hidIsChangePending( reportIdAll );
TEST_ASSERT_EQUAL_UINT( 0, changePending );
for ( reportId = 1; reportId <= HID_REPORT_COUNT; ++reportId ) {
hidSetChangePending( reportId );
changePending = hidIsChangePending( reportId );
TEST_ASSERT_EQUAL_UINT( 1, changePending );
}
changePending = hidIsChangePending( reportIdAll );
TEST_ASSERT_EQUAL_UINT( 1, changePending );
}
void test_report_time( void )

View File

@@ -1,4 +1,4 @@
// Copyright 2021 XMOS LIMITED.
// Copyright 2021-2022 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stddef.h>
#include <stdio.h>