diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7f4c9fdd..74f0cf97 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,9 @@ lib_xua Change Log * CHANGED: Move HID descriptors to ease maintenance * CHANGED: Move legacy tests to separate directory * CHANGED: Replace unused GPI-specific HID event names with generic ones + * ADDED: Build default HID Report descriptor at boot-time + * ADDED: Function to return length of HID Report + * CHANGED: HID Report to return multiple bytes 1.2.0 ----- diff --git a/Jenkinsfile b/Jenkinsfile index ef501285..905b2301 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -27,7 +27,7 @@ pipeline { xcoreLibraryChecks("${REPO}") } } - stage('Tests') { + stage('XS2 Tests') { failFast true parallel { stage('Legacy tests') { @@ -35,8 +35,62 @@ pipeline { runXmostest("${REPO}", 'legacy_tests') } } + stage('Unit tests') { + steps { + dir("${REPO}") { + dir('tests') { + dir('xua_unit_tests') { + withVenv { + runWaf('.', "configure clean build --target=xcore200") +// runWaf('.', "configure clean build --target=xcoreai") +// stash name: 'xua_unit_tests', includes: 'bin/*xcoreai.xe, ' + viewEnv() { + runPython("TARGET=XCORE200 pytest -n 1") + } + } + } + } + } + } + } } } +// stage('xcore.ai Verification') { +// agent { +// label 'xcore.ai-explorer' +// } +// options { +// skipDefaultCheckout() +// } +// stages{ +// stage('Get View') { +// steps { +// xcorePrepareSandbox("${VIEW}", "${REPO}") +// } +// } +// stage('Unit tests') { +// steps { +// dir("${REPO}") { +// dir('tests') { +// dir('xua_unit_tests') { +// withVenv { +// unstash 'xua_unit_tests' +// viewEnv() { +// runPython("TARGET=XCOREAI pytest -s") +// } +// } +// } +// } +// } +// } +// } +// } // stages +// post { +// cleanup { +// cleanWs() +// } +// } +// } stage('xCORE builds') { steps { dir("${REPO}") { diff --git a/examples/AN00246_xua_example/Makefile b/examples/AN00246_xua_example/Makefile index 9bdea14b..699e31b2 100644 --- a/examples/AN00246_xua_example/Makefile +++ b/examples/AN00246_xua_example/Makefile @@ -3,7 +3,7 @@ APP_NAME = app_xua_simple TARGET = xk-audio-216-mc.xn # The flags passed to xcc when building the application -XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -O3 -report -fsubword-select -save-temps \ +XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -O3 -report -save-temps \ -g -Wno-unused-function -Wno-timing -DXUD_SERIES_SUPPORT=XUD_X200_SERIES -DUSB_TILE=tile[1] -fxscope #-DSDA_HIGH=2 -DSCL_HIGH=1 -fxscope diff --git a/examples/AN00246_xua_example/src/hid_report_descriptor.h b/examples/AN00246_xua_example/src/hid_report_descriptor.h new file mode 100644 index 00000000..b183beda --- /dev/null +++ b/examples/AN00246_xua_example/src/hid_report_descriptor.h @@ -0,0 +1,105 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef __hid_report_descriptor_h__ +#define __hid_report_descriptor_h__ + +#include "xua_hid_report_descriptor.h" + +#if 0 +/* Existing static report descriptor kept for reference */ +unsigned char hidReportDescriptor[] = +{ + 0x05, 0x0c, /* Usage Page (Consumer Device) */ + 0x09, 0x01, /* Usage (Consumer Control) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x09, 0xb0, /* Usage (Play) */ + 0x09, 0xb5, /* Usage (Scan Next Track) */ + 0x09, 0xb6, /* Usage (Scan Previous Track) */ + 0x09, 0xe9, /* Usage (Volume Up) */ + 0x09, 0xea, /* Usage (Volume Down) */ + 0x09, 0xe2, /* Usage (Mute) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x06, /* Report Count (6) */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x02, /* Report Count (2) */ + 0x81, 0x01, /* Input (Cnst, Ary, Abs) */ + 0xc0 /* End collection */ +}; +#endif + +/* + * Define non-configurable items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t hidCollectionApplication = { .header = 0xA1, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidCollectionEnd = { .header = 0xC0, .data = { 0x00, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidInputConstArray = { .header = 0x81, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidInputDataVar = { .header = 0x81, .data = { 0x02, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidLogicalMaximum0 = { .header = 0x25, .data = { 0x00, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMaximum1 = { .header = 0x25, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMinimum0 = { .header = 0x15, .data = { 0x00, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidReportCount2 = { .header = 0x95, .data = { 0x02, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportCount6 = { .header = 0x95, .data = { 0x06, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportSize1 = { .header = 0x75, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsageConsumerControl = { .header = 0x09, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsagePageConsumer = { .header = 0x05, .data = { 0x0C, 0x00 }, .location = 0x00 }; + +/* + * Define configurable items in the HID Report descriptor. + */ +static USB_HID_Short_Item_t hidUsageByte0Bit5 = { .header = 0x09, .data = { 0xE2, 0x00 }, .location = 0x50 }; // Mute +static USB_HID_Short_Item_t hidUsageByte0Bit4 = { .header = 0x09, .data = { 0xEA, 0x00 }, .location = 0x40 }; // Vol- +static USB_HID_Short_Item_t hidUsageByte0Bit3 = { .header = 0x09, .data = { 0xE9, 0x00 }, .location = 0x30 }; // Vol+ +static USB_HID_Short_Item_t hidUsageByte0Bit2 = { .header = 0x09, .data = { 0xB6, 0x00 }, .location = 0x20 }; // Scan Prev +static USB_HID_Short_Item_t hidUsageByte0Bit1 = { .header = 0x09, .data = { 0xB5, 0x00 }, .location = 0x10 }; // Scan Next +static USB_HID_Short_Item_t hidUsageByte0Bit0 = { .header = 0x09, .data = { 0xB0, 0x00 }, .location = 0x00 }; // Play + +/* + * List the configurable items in the HID Report descriptor. + */ +static USB_HID_Short_Item_t* const hidConfigurableItems[] = { + &hidUsageByte0Bit0, + &hidUsageByte0Bit1, + &hidUsageByte0Bit2, + &hidUsageByte0Bit3, + &hidUsageByte0Bit4, + &hidUsageByte0Bit5 +}; + +/* + * List all items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t* const hidReportDescriptorItems[] = { + &hidUsagePageConsumer, + &hidUsageConsumerControl, + &hidCollectionApplication, + &hidLogicalMinimum0, + &hidLogicalMaximum1, + &hidUsageByte0Bit0, + &hidUsageByte0Bit1, + &hidUsageByte0Bit2, + &hidUsageByte0Bit3, + &hidUsageByte0Bit4, + &hidUsageByte0Bit5, + &hidReportSize1, + &hidReportCount6, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount2, + &hidInputConstArray, + &hidCollectionEnd +}; + +/* + * Define the length of the HID Report. + * This value must match the number of Report bytes defined by hidReportDescriptorItems. + */ +#define HID_REPORT_LENGTH ( 1 ) + +#endif // __hid_report_descriptor_h__ diff --git a/examples/AN00246_xua_example/src/xk-audio-216-mc.xn b/examples/AN00246_xua_example/src/xk-audio-216-mc.xn index c2e90f16..88e6d699 100644 --- a/examples/AN00246_xua_example/src/xk-audio-216-mc.xn +++ b/examples/AN00246_xua_example/src/xk-audio-216-mc.xn @@ -75,7 +75,7 @@ - + diff --git a/examples/AN00247_xua_example_spdif_tx/Makefile b/examples/AN00247_xua_example_spdif_tx/Makefile index cedcb1c7..2003fdd8 100644 --- a/examples/AN00247_xua_example_spdif_tx/Makefile +++ b/examples/AN00247_xua_example_spdif_tx/Makefile @@ -3,7 +3,7 @@ APP_NAME = app_xua_simple TARGET = xk-audio-216-mc.xn # The flags passed to xcc when building the application -XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -O3 -report -fsubword-select -save-temps \ +XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -O3 -report -save-temps \ -g -Wno-unused-function -Wno-timing -DXUD_SERIES_SUPPORT=XUD_X200_SERIES -DUSB_TILE=tile[1] \ -DSDA_HIGH=2 -DSCL_HIGH=1 -fxscope diff --git a/examples/AN00247_xua_example_spdif_tx/src/hid_report_descriptor.h b/examples/AN00247_xua_example_spdif_tx/src/hid_report_descriptor.h new file mode 100644 index 00000000..b183beda --- /dev/null +++ b/examples/AN00247_xua_example_spdif_tx/src/hid_report_descriptor.h @@ -0,0 +1,105 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef __hid_report_descriptor_h__ +#define __hid_report_descriptor_h__ + +#include "xua_hid_report_descriptor.h" + +#if 0 +/* Existing static report descriptor kept for reference */ +unsigned char hidReportDescriptor[] = +{ + 0x05, 0x0c, /* Usage Page (Consumer Device) */ + 0x09, 0x01, /* Usage (Consumer Control) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x09, 0xb0, /* Usage (Play) */ + 0x09, 0xb5, /* Usage (Scan Next Track) */ + 0x09, 0xb6, /* Usage (Scan Previous Track) */ + 0x09, 0xe9, /* Usage (Volume Up) */ + 0x09, 0xea, /* Usage (Volume Down) */ + 0x09, 0xe2, /* Usage (Mute) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x06, /* Report Count (6) */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x02, /* Report Count (2) */ + 0x81, 0x01, /* Input (Cnst, Ary, Abs) */ + 0xc0 /* End collection */ +}; +#endif + +/* + * Define non-configurable items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t hidCollectionApplication = { .header = 0xA1, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidCollectionEnd = { .header = 0xC0, .data = { 0x00, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidInputConstArray = { .header = 0x81, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidInputDataVar = { .header = 0x81, .data = { 0x02, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidLogicalMaximum0 = { .header = 0x25, .data = { 0x00, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMaximum1 = { .header = 0x25, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMinimum0 = { .header = 0x15, .data = { 0x00, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidReportCount2 = { .header = 0x95, .data = { 0x02, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportCount6 = { .header = 0x95, .data = { 0x06, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportSize1 = { .header = 0x75, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsageConsumerControl = { .header = 0x09, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsagePageConsumer = { .header = 0x05, .data = { 0x0C, 0x00 }, .location = 0x00 }; + +/* + * Define configurable items in the HID Report descriptor. + */ +static USB_HID_Short_Item_t hidUsageByte0Bit5 = { .header = 0x09, .data = { 0xE2, 0x00 }, .location = 0x50 }; // Mute +static USB_HID_Short_Item_t hidUsageByte0Bit4 = { .header = 0x09, .data = { 0xEA, 0x00 }, .location = 0x40 }; // Vol- +static USB_HID_Short_Item_t hidUsageByte0Bit3 = { .header = 0x09, .data = { 0xE9, 0x00 }, .location = 0x30 }; // Vol+ +static USB_HID_Short_Item_t hidUsageByte0Bit2 = { .header = 0x09, .data = { 0xB6, 0x00 }, .location = 0x20 }; // Scan Prev +static USB_HID_Short_Item_t hidUsageByte0Bit1 = { .header = 0x09, .data = { 0xB5, 0x00 }, .location = 0x10 }; // Scan Next +static USB_HID_Short_Item_t hidUsageByte0Bit0 = { .header = 0x09, .data = { 0xB0, 0x00 }, .location = 0x00 }; // Play + +/* + * List the configurable items in the HID Report descriptor. + */ +static USB_HID_Short_Item_t* const hidConfigurableItems[] = { + &hidUsageByte0Bit0, + &hidUsageByte0Bit1, + &hidUsageByte0Bit2, + &hidUsageByte0Bit3, + &hidUsageByte0Bit4, + &hidUsageByte0Bit5 +}; + +/* + * List all items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t* const hidReportDescriptorItems[] = { + &hidUsagePageConsumer, + &hidUsageConsumerControl, + &hidCollectionApplication, + &hidLogicalMinimum0, + &hidLogicalMaximum1, + &hidUsageByte0Bit0, + &hidUsageByte0Bit1, + &hidUsageByte0Bit2, + &hidUsageByte0Bit3, + &hidUsageByte0Bit4, + &hidUsageByte0Bit5, + &hidReportSize1, + &hidReportCount6, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount2, + &hidInputConstArray, + &hidCollectionEnd +}; + +/* + * Define the length of the HID Report. + * This value must match the number of Report bytes defined by hidReportDescriptorItems. + */ +#define HID_REPORT_LENGTH ( 1 ) + +#endif // __hid_report_descriptor_h__ diff --git a/examples/AN00247_xua_example_spdif_tx/src/xk-audio-216-mc.xn b/examples/AN00247_xua_example_spdif_tx/src/xk-audio-216-mc.xn index c2e90f16..88e6d699 100644 --- a/examples/AN00247_xua_example_spdif_tx/src/xk-audio-216-mc.xn +++ b/examples/AN00247_xua_example_spdif_tx/src/xk-audio-216-mc.xn @@ -75,7 +75,7 @@ - + diff --git a/examples/AN00248_xua_example_pdm_mics/Makefile b/examples/AN00248_xua_example_pdm_mics/Makefile index 8681d0b5..9eefd8bc 100644 --- a/examples/AN00248_xua_example_pdm_mics/Makefile +++ b/examples/AN00248_xua_example_pdm_mics/Makefile @@ -3,7 +3,7 @@ APP_NAME = app_xua_simple TARGET = mic_array_ref.xn # The flags passed to xcc when building the application -XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -O3 -report -fsubword-select -save-temps \ +XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -O3 -report -save-temps \ -g -Wno-unused-function -Wno-timing -DXUD_SERIES_SUPPORT=XUD_X200_SERIES -DUSB_TILE=tile[1] \ -DSDA_HIGH=2 -DSCL_HIGH=1 -fxscope diff --git a/examples/AN00248_xua_example_pdm_mics/src/hid_report_descriptor.h b/examples/AN00248_xua_example_pdm_mics/src/hid_report_descriptor.h new file mode 100644 index 00000000..b183beda --- /dev/null +++ b/examples/AN00248_xua_example_pdm_mics/src/hid_report_descriptor.h @@ -0,0 +1,105 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef __hid_report_descriptor_h__ +#define __hid_report_descriptor_h__ + +#include "xua_hid_report_descriptor.h" + +#if 0 +/* Existing static report descriptor kept for reference */ +unsigned char hidReportDescriptor[] = +{ + 0x05, 0x0c, /* Usage Page (Consumer Device) */ + 0x09, 0x01, /* Usage (Consumer Control) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x09, 0xb0, /* Usage (Play) */ + 0x09, 0xb5, /* Usage (Scan Next Track) */ + 0x09, 0xb6, /* Usage (Scan Previous Track) */ + 0x09, 0xe9, /* Usage (Volume Up) */ + 0x09, 0xea, /* Usage (Volume Down) */ + 0x09, 0xe2, /* Usage (Mute) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x06, /* Report Count (6) */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x02, /* Report Count (2) */ + 0x81, 0x01, /* Input (Cnst, Ary, Abs) */ + 0xc0 /* End collection */ +}; +#endif + +/* + * Define non-configurable items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t hidCollectionApplication = { .header = 0xA1, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidCollectionEnd = { .header = 0xC0, .data = { 0x00, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidInputConstArray = { .header = 0x81, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidInputDataVar = { .header = 0x81, .data = { 0x02, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidLogicalMaximum0 = { .header = 0x25, .data = { 0x00, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMaximum1 = { .header = 0x25, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMinimum0 = { .header = 0x15, .data = { 0x00, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidReportCount2 = { .header = 0x95, .data = { 0x02, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportCount6 = { .header = 0x95, .data = { 0x06, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportSize1 = { .header = 0x75, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsageConsumerControl = { .header = 0x09, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsagePageConsumer = { .header = 0x05, .data = { 0x0C, 0x00 }, .location = 0x00 }; + +/* + * Define configurable items in the HID Report descriptor. + */ +static USB_HID_Short_Item_t hidUsageByte0Bit5 = { .header = 0x09, .data = { 0xE2, 0x00 }, .location = 0x50 }; // Mute +static USB_HID_Short_Item_t hidUsageByte0Bit4 = { .header = 0x09, .data = { 0xEA, 0x00 }, .location = 0x40 }; // Vol- +static USB_HID_Short_Item_t hidUsageByte0Bit3 = { .header = 0x09, .data = { 0xE9, 0x00 }, .location = 0x30 }; // Vol+ +static USB_HID_Short_Item_t hidUsageByte0Bit2 = { .header = 0x09, .data = { 0xB6, 0x00 }, .location = 0x20 }; // Scan Prev +static USB_HID_Short_Item_t hidUsageByte0Bit1 = { .header = 0x09, .data = { 0xB5, 0x00 }, .location = 0x10 }; // Scan Next +static USB_HID_Short_Item_t hidUsageByte0Bit0 = { .header = 0x09, .data = { 0xB0, 0x00 }, .location = 0x00 }; // Play + +/* + * List the configurable items in the HID Report descriptor. + */ +static USB_HID_Short_Item_t* const hidConfigurableItems[] = { + &hidUsageByte0Bit0, + &hidUsageByte0Bit1, + &hidUsageByte0Bit2, + &hidUsageByte0Bit3, + &hidUsageByte0Bit4, + &hidUsageByte0Bit5 +}; + +/* + * List all items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t* const hidReportDescriptorItems[] = { + &hidUsagePageConsumer, + &hidUsageConsumerControl, + &hidCollectionApplication, + &hidLogicalMinimum0, + &hidLogicalMaximum1, + &hidUsageByte0Bit0, + &hidUsageByte0Bit1, + &hidUsageByte0Bit2, + &hidUsageByte0Bit3, + &hidUsageByte0Bit4, + &hidUsageByte0Bit5, + &hidReportSize1, + &hidReportCount6, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount2, + &hidInputConstArray, + &hidCollectionEnd +}; + +/* + * Define the length of the HID Report. + * This value must match the number of Report bytes defined by hidReportDescriptorItems. + */ +#define HID_REPORT_LENGTH ( 1 ) + +#endif // __hid_report_descriptor_h__ diff --git a/examples/AN00248_xua_example_pdm_mics/src/mic_array_ref.xn b/examples/AN00248_xua_example_pdm_mics/src/mic_array_ref.xn index 66caa132..a9e1b9b4 100644 --- a/examples/AN00248_xua_example_pdm_mics/src/mic_array_ref.xn +++ b/examples/AN00248_xua_example_pdm_mics/src/mic_array_ref.xn @@ -81,7 +81,7 @@ - + diff --git a/legacy_tests/app_test_i2s_loopback/hid_report_descriptor.h b/legacy_tests/app_test_i2s_loopback/hid_report_descriptor.h new file mode 100644 index 00000000..b183beda --- /dev/null +++ b/legacy_tests/app_test_i2s_loopback/hid_report_descriptor.h @@ -0,0 +1,105 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef __hid_report_descriptor_h__ +#define __hid_report_descriptor_h__ + +#include "xua_hid_report_descriptor.h" + +#if 0 +/* Existing static report descriptor kept for reference */ +unsigned char hidReportDescriptor[] = +{ + 0x05, 0x0c, /* Usage Page (Consumer Device) */ + 0x09, 0x01, /* Usage (Consumer Control) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x09, 0xb0, /* Usage (Play) */ + 0x09, 0xb5, /* Usage (Scan Next Track) */ + 0x09, 0xb6, /* Usage (Scan Previous Track) */ + 0x09, 0xe9, /* Usage (Volume Up) */ + 0x09, 0xea, /* Usage (Volume Down) */ + 0x09, 0xe2, /* Usage (Mute) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x06, /* Report Count (6) */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x02, /* Report Count (2) */ + 0x81, 0x01, /* Input (Cnst, Ary, Abs) */ + 0xc0 /* End collection */ +}; +#endif + +/* + * Define non-configurable items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t hidCollectionApplication = { .header = 0xA1, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidCollectionEnd = { .header = 0xC0, .data = { 0x00, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidInputConstArray = { .header = 0x81, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidInputDataVar = { .header = 0x81, .data = { 0x02, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidLogicalMaximum0 = { .header = 0x25, .data = { 0x00, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMaximum1 = { .header = 0x25, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMinimum0 = { .header = 0x15, .data = { 0x00, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidReportCount2 = { .header = 0x95, .data = { 0x02, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportCount6 = { .header = 0x95, .data = { 0x06, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportSize1 = { .header = 0x75, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsageConsumerControl = { .header = 0x09, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsagePageConsumer = { .header = 0x05, .data = { 0x0C, 0x00 }, .location = 0x00 }; + +/* + * Define configurable items in the HID Report descriptor. + */ +static USB_HID_Short_Item_t hidUsageByte0Bit5 = { .header = 0x09, .data = { 0xE2, 0x00 }, .location = 0x50 }; // Mute +static USB_HID_Short_Item_t hidUsageByte0Bit4 = { .header = 0x09, .data = { 0xEA, 0x00 }, .location = 0x40 }; // Vol- +static USB_HID_Short_Item_t hidUsageByte0Bit3 = { .header = 0x09, .data = { 0xE9, 0x00 }, .location = 0x30 }; // Vol+ +static USB_HID_Short_Item_t hidUsageByte0Bit2 = { .header = 0x09, .data = { 0xB6, 0x00 }, .location = 0x20 }; // Scan Prev +static USB_HID_Short_Item_t hidUsageByte0Bit1 = { .header = 0x09, .data = { 0xB5, 0x00 }, .location = 0x10 }; // Scan Next +static USB_HID_Short_Item_t hidUsageByte0Bit0 = { .header = 0x09, .data = { 0xB0, 0x00 }, .location = 0x00 }; // Play + +/* + * List the configurable items in the HID Report descriptor. + */ +static USB_HID_Short_Item_t* const hidConfigurableItems[] = { + &hidUsageByte0Bit0, + &hidUsageByte0Bit1, + &hidUsageByte0Bit2, + &hidUsageByte0Bit3, + &hidUsageByte0Bit4, + &hidUsageByte0Bit5 +}; + +/* + * List all items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t* const hidReportDescriptorItems[] = { + &hidUsagePageConsumer, + &hidUsageConsumerControl, + &hidCollectionApplication, + &hidLogicalMinimum0, + &hidLogicalMaximum1, + &hidUsageByte0Bit0, + &hidUsageByte0Bit1, + &hidUsageByte0Bit2, + &hidUsageByte0Bit3, + &hidUsageByte0Bit4, + &hidUsageByte0Bit5, + &hidReportSize1, + &hidReportCount6, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount2, + &hidInputConstArray, + &hidCollectionEnd +}; + +/* + * Define the length of the HID Report. + * This value must match the number of Report bytes defined by hidReportDescriptorItems. + */ +#define HID_REPORT_LENGTH ( 1 ) + +#endif // __hid_report_descriptor_h__ diff --git a/legacy_tests/app_test_i2s_loopback/xk_216_mc/xk-audio-216-mc.xn b/legacy_tests/app_test_i2s_loopback/xk_216_mc/xk-audio-216-mc.xn index 8e2887b8..cffd732f 100644 --- a/legacy_tests/app_test_i2s_loopback/xk_216_mc/xk-audio-216-mc.xn +++ b/legacy_tests/app_test_i2s_loopback/xk_216_mc/xk-audio-216-mc.xn @@ -83,7 +83,7 @@ - + diff --git a/legacy_tests/app_test_i2s_loopback/xua_conf.h b/legacy_tests/app_test_i2s_loopback/xua_conf.h index bec236b1..a1120f49 100644 --- a/legacy_tests/app_test_i2s_loopback/xua_conf.h +++ b/legacy_tests/app_test_i2s_loopback/xua_conf.h @@ -4,7 +4,7 @@ #define __custom_defines_h__ #define EXCLUDE_USB_AUDIO_MAIN -#define NUM_PDM_MICS 0 +#define XUA_NUM_PDM_MICS 0 #define XUD_TILE 1 #define AUDIO_IO_TILE 0 #define MIXER 0 diff --git a/lib_xua/src/core/buffer/ep/ep_buffer.xc b/lib_xua/src/core/buffer/ep/ep_buffer.xc index 50f475bd..eb6b373a 100644 --- a/lib_xua/src/core/buffer/ep/ep_buffer.xc +++ b/lib_xua/src/core/buffer/ep/ep_buffer.xc @@ -21,8 +21,9 @@ #include "testct_byref.h" #if( 0 < HID_CONTROLS ) +#include "xua_hid_report_descriptor.h" #include "user_hid.h" -unsigned char g_hidData[HID_DATA_BYTES] = {0}; +unsigned char g_hidData[HID_MAX_DATA_BYTES] = {0}; #endif void GetADCCounts(unsigned samFreq, int &min, int &mid, int &max); @@ -371,7 +372,10 @@ void XUA_Buffer_Ep(register chanend c_aud_out, #endif #if( 0 < HID_CONTROLS ) - XUD_SetReady_In(ep_hid, g_hidData, 1); + { + int hidDataLength = hidGetReportLength(); + XUD_SetReady_In(ep_hid, g_hidData, hidDataLength); + } #endif #if (AUDIO_CLASS == 1) @@ -885,9 +889,9 @@ void XUA_Buffer_Ep(register chanend c_aud_out, /* HID Report Data */ case XUD_SetData_Select(c_hid, ep_hid, result): { - g_hidData[0]=0; + int hidDataLength = hidGetReportLength(); UserHIDGetData(g_hidData); - XUD_SetReady_In(ep_hid, g_hidData, 1); + XUD_SetReady_In(ep_hid, g_hidData, hidDataLength); } break; #endif diff --git a/lib_xua/src/core/endpoint0/xua_endpoint0.c b/lib_xua/src/core/endpoint0/xua_endpoint0.c index 40064918..9af03eb2 100755 --- a/lib_xua/src/core/endpoint0/xua_endpoint0.c +++ b/lib_xua/src/core/endpoint0/xua_endpoint0.c @@ -23,18 +23,24 @@ #include "vendorrequests.h" #include "xc_ptr.h" #include "xua_ep0_uacreqs.h" + #if( 0 < HID_CONTROLS ) #include "hid.h" +#include "xua_hid.h" +#include "xua_hid_report_descriptor.h" #endif + #if DSD_CHANS_DAC > 0 #include "dsd_support.h" #endif + #define DEBUG_UNIT XUA_EP0 + #ifndef DEBUG_PRINT_ENABLE_XUA_EP0 #define DEBUG_PRINT_ENABLE_XUA_EP0 0 #endif // DEBUG_PRINT_ENABLE_XUA_EP0 -#include "debug_print.h" +#include "debug_print.h" #include "xua_usb_params_funcs.h" #ifndef __XC__ @@ -51,7 +57,7 @@ #if ((AUDIO_CLASS == 1) || (AUDIO_CLASS_FALLBACK)) && defined(DFU) #warning DFU will not be enabled in AUDIO 1.0 mode due to Windows requesting driver #endif -#endif +#endif // FORCE_UAC1_DFU /* MIDI not supported in Audio 1.0 mode */ #if ((AUDIO_CLASS == 1) || (AUDIO_CLASS_FALLBACK)) && defined(MIDI) @@ -85,9 +91,6 @@ extern void device_reboot(void); #endif -#if( 0 < HID_CONTROLS ) -#include "xua_hid.h" -#endif #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif @@ -518,6 +521,22 @@ void XUA_Endpoint0_init(chanend c_ep0_out, chanend c_ep0_in, chanend c_audioCont #endif +#if( 0 < HID_CONTROLS ) + hidPrepareReportDescriptor(); + + size_t hidReportDescriptorLength = hidGetReportDescriptorLength(); + unsigned char hidReportDescriptorLengthLo = hidReportDescriptorLength & 0xFF; + unsigned char hidReportDescriptorLengthHi = (hidReportDescriptorLength & 0xFF00) >> 8; + +#if( AUDIO_CLASS == 1 ) + cfgDesc_Audio1[USB_HID_DESCRIPTOR_OFFSET + HID_DESCRIPTOR_LENGTH_FIELD_OFFSET ] = hidReportDescriptorLengthLo; + cfgDesc_Audio1[USB_HID_DESCRIPTOR_OFFSET + HID_DESCRIPTOR_LENGTH_FIELD_OFFSET + 1] = hidReportDescriptorLengthHi; +#endif + + hidDescriptor[HID_DESCRIPTOR_LENGTH_FIELD_OFFSET ] = hidReportDescriptorLengthLo; + hidDescriptor[HID_DESCRIPTOR_LENGTH_FIELD_OFFSET + 1] = hidReportDescriptorLengthHi; +#endif // 0 < HID_CONTROLS + } void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0_out, chanend c_ep0_in, chanend c_audioControl, @@ -729,15 +748,22 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0 switch (descriptorType) { case HID_HID: - /* Return HID Descriptor */ - result = XUD_DoGetRequest(ep0_out, ep0_in, hidDescriptor, - sizeof(hidDescriptor), sp.wLength); + { + /* Return HID Descriptor */ + result = XUD_DoGetRequest(ep0_out, ep0_in, hidDescriptor, + sizeof(hidDescriptor), sp.wLength); + } break; case HID_REPORT: - /* Return HID report descriptor */ - result = XUD_DoGetRequest(ep0_out, ep0_in, hidReportDescriptor, - sizeof(hidReportDescriptor), sp.wLength); - break; + { + /* Return HID report descriptor */ + unsigned char* hidReportDescriptorPtr; + hidReportDescriptorPtr = hidGetReportDescriptor(); + size_t hidReportDescriptorLength = hidGetReportDescriptorLength(); + result = XUD_DoGetRequest(ep0_out, ep0_in, hidReportDescriptorPtr, + hidReportDescriptorLength, sp.wLength); + } + break; } } break; diff --git a/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h b/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h index 47991bf9..d8513888 100644 --- a/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h +++ b/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h @@ -580,33 +580,6 @@ unsigned char devQualDesc_Null[] = #define MIXER_LENGTH (0) #endif -#if( 0 < HID_CONTROLS ) -unsigned char hidReportDescriptor[] = -{ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x06, /* Usage (Keyboard) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x75, 0x01, /* Report Size (1) */ - 0x95, 0x04, /* Report Count (4) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x00, /* Logical Maximum (0) */ - 0x81, 0x01, /* Input (Cnst, Ary, Abs, No Wrap, Lin, Pref, No Nul) */ - 0x95, 0x01, /* Report Count (1) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x05, 0x0C, /* Usage Page (Consumer) */ - 0x0a, 0x21, 0x02, /* Usage (AC Search) */ - 0x81, 0x02, /* Input (Data, Var, Abs, No Wrap, Lin, Pref, No Nul) */ - 0x0a, 0x26, 0x02, /* Usage (AC Stop) */ - 0x81, 0x02, /* Input (Data, Var, Abs, No Wrap, Lin, Pref, No Nul) */ - 0x95, 0x02, /* Report Count (2) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0x72, /* Usage Minimum (Keyboard F23) */ - 0x29, 0x73, /* Usage Maximum (Keyboard F24) */ - 0x81, 0x02, /* Input (Data, Var, Abs, No Wrap, Lin, Pref, No Nul) */ - 0xc0 /* End collection (Application) */ -}; -#endif - /* Max packet sizes: * Samples per channel. e.g (192000+7999/8000) = 24 * Must allow 1 sample extra per chan (24 + 1) = 25 @@ -2373,6 +2346,10 @@ const unsigned num_freqs_a1 = MAX(3, (0 #endif +#if( 0 < HID_CONTROLS ) + #define USB_HID_DESCRIPTOR_OFFSET (18 + AC_TOTAL_LENGTH + (INPUT_INTERFACES_A1 * (49 + num_freqs_a1 * 3)) + (OUTPUT_INTERFACES_A1 * (49 + num_freqs_a1 * 3)) + CONTROL_INTERFACE_BYTES + DFU_INTERFACE_BYTES + INTERFACE_DESCRIPTOR_BYTES) +#endif + #define CHARIFY_SR(x) (x & 0xff),((x & 0xff00)>> 8),((x & 0xff0000)>> 16) #if (MIN_FREQ_FS < 12000) && (MAX_FREQ_FS > 48000) diff --git a/lib_xua/src/core/user/hid/user_hid.h b/lib_xua/src/core/user/hid/user_hid.h index 7eced6ca..1e5ade42 100644 --- a/lib_xua/src/core/user/hid/user_hid.h +++ b/lib_xua/src/core/user/hid/user_hid.h @@ -47,7 +47,7 @@ typedef enum hidEventId_t { HID_EVENT_ID_INVALID = 0xffffffff, } hidEventId_t; -#define HID_DATA_BYTES 4 +#define HID_MAX_DATA_BYTES 4 #if( 0 < HID_CONTROLS ) @@ -60,7 +60,7 @@ typedef enum hidEventId_t { * * \param{out} hidData The HID data */ -void UserHIDGetData( unsigned char hidData[ HID_DATA_BYTES ]); +void UserHIDGetData( unsigned char hidData[ HID_MAX_DATA_BYTES ]); /** * \brief Initialize HID processing @@ -70,17 +70,15 @@ void UserHIDInit( void ); /** * \brief Record that a HID event has occurred * - * \param{in} hidEventId The identifier of an event which has occurred - * \param{in} hidEventData A list of data associated with the event - * \param{in} hidEventDataSize The length of the event data list - * - * \note At present, this function only takes a single element of event data, i.e. - * hidEventDataSize must equal 1. - * - * \note At present, this function treats the event data as a Boolean flag. - * Zero means False; all other values mean True. + * \param{in} hidEventId The identifier of an event which has occurred. + * Each event corresponds to bit in the HID Report: + * Events 0- 7 to bits 0-7 of byte 0, + * Events 8-15 to bits 0-7 of byte 1, etc. + * \param{in} hidEventData A Boolean indicating the state of the event: + * Zero = deasserted, + * Any other value = asserted. */ -void UserHIDRecordEvent( const hidEventId_t hidEventId, const int * hidEventData, const unsigned hidEventDataSize ); +void UserHIDRecordEvent( const hidEventId_t hidEventId, const unsigned hidEventData ); #endif /* ( 0 < HID_CONTROLS ) */ #endif /* __USER_HID_H__ */ diff --git a/lib_xua/src/hid/hid_report_descriptor.c b/lib_xua/src/hid/hid_report_descriptor.c new file mode 100644 index 00000000..022085ed --- /dev/null +++ b/lib_xua/src/hid/hid_report_descriptor.c @@ -0,0 +1,212 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include +#include +#include "xua_hid_report_descriptor.h" +#include "hid_report_descriptor.h" + +#include + +#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 )) + +static unsigned char hidReportDescriptor[ HID_REPORT_DESCRIPTOR_MAX_LENGTH ]; +static size_t hidReportDescriptorLength = 0; +static unsigned hidReportDescriptorPrepared = 0; + +/** + * @brief Get the bit position from the location of an Item + * + * Parameters: + * + * @param[in] location The \c location field from a \c USB_HID_Short_Item + * + * @return The bit position of the Item + */ +static unsigned hidGetItemBitLocation( const unsigned char header ); + +/** + * @brief Get the byte position from the location of an Item + * + * Parameters: + * + * @param[in] location The \c location field from a \c USB_HID_Short_Item + * + * @return The byte position of the Item within the HID Report + */ +static unsigned hidGetItemByteLocation( const unsigned char header ); + +/** + * @brief Get the number of data bytes from the header of an Item + * + * Parameters: + * + * @param[in] header The \c header field from a \c USB_HID_Short_Item + * + * @return The amount of data for the Item + */ +static unsigned hidGetItemSize( const unsigned char header ); + +/** + * @brief Get the Tag from the header of an Item + * + * Parameters: + * + * @param[in] header The \c header field from a \c USB_HID_Short_Item + * + * @return The Tag of the Item + */ +static unsigned hidGetItemTag( const unsigned char header ); + +/** + * @brief Get the Type from the header of an Item + * + * Parameters: + * + * @param[in] header The \c header field from a \c USB_HID_Short_Item + * + * @return The Type of the Item + */ +static unsigned hidGetItemType( const unsigned char header ); + +/** + * @brief Translate an Item from the \c USB_HID_Short_Item format to raw bytes + * + * Parameters: + * + * @param[in] inPtr A pointer to a \c USB_HID_Short_Item + * @param[in,out] outPtrPtr A pointer to a pointer to the next available space in the raw + * byte buffer. Passed as a pointer to a pointer to allow this + * function to return the updated pointer to the raw byte buffer. + * + * @return The number of bytes placed in the raw byte buffer + */ +static size_t hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char** outPtrPtr ); + + +static unsigned hidGetItemBitLocation( const unsigned char location ) +{ + unsigned bBit = ( location & HID_REPORT_ITEM_LOC_BIT_MASK ) >> HID_REPORT_ITEM_LOC_BIT_SHIFT; + return bBit; +} + +static unsigned hidGetItemByteLocation( const unsigned char location ) +{ + unsigned bByte = ( location & HID_REPORT_ITEM_LOC_BYTE_MASK ) >> HID_REPORT_ITEM_LOC_BYTE_SHIFT; + return bByte; +} + +static unsigned hidGetItemSize( const unsigned char header ) +{ + unsigned bSize = ( header & HID_REPORT_ITEM_HDR_SIZE_MASK ) >> HID_REPORT_ITEM_HDR_SIZE_SHIFT; + return bSize; +} + +static unsigned hidGetItemTag( const unsigned char header ) +{ + unsigned bTag = ( header & HID_REPORT_ITEM_HDR_TAG_MASK ) >> HID_REPORT_ITEM_HDR_TAG_SHIFT; + return bTag; +} + +static unsigned hidGetItemType( const unsigned char header ) +{ + unsigned bType = ( header & HID_REPORT_ITEM_HDR_TYPE_MASK ) >> HID_REPORT_ITEM_HDR_TYPE_SHIFT; + return bType; +} + +unsigned char* hidGetReportDescriptor( void ) +{ + unsigned char* retVal = NULL; + + if( hidReportDescriptorPrepared ) { + retVal = hidReportDescriptor; + } + + return retVal; +} + +size_t hidGetReportDescriptorLength( void ) +{ + size_t retVal = ( hidReportDescriptorPrepared ) ? hidReportDescriptorLength : 0; + return retVal; +} + +size_t hidGetReportLength( void ) +{ + size_t retVal = ( hidReportDescriptorPrepared ) ? HID_REPORT_LENGTH : 0; + return retVal; +} + +void hidPrepareReportDescriptor( void ) +{ + if( !hidReportDescriptorPrepared ) { + hidReportDescriptorLength = 0; + unsigned char* ptr = hidReportDescriptor; + for( unsigned idx = 0; idx < HID_REPORT_DESCRIPTOR_ITEM_COUNT; ++idx ) { + hidReportDescriptorLength += hidTranslateItem( hidReportDescriptorItems[ idx ], &ptr ); + } + + hidReportDescriptorPrepared = 1; + } +} + +void hidResetReportDescriptor( void ) +{ + hidReportDescriptorPrepared = 0; +} + +#define HID_CONFIGURABLE_ITEM_COUNT ( sizeof hidConfigurableItems / sizeof ( USB_HID_Short_Item_t* )) +unsigned hidSetReportItem( const unsigned byte, const unsigned bit, const unsigned char header, const unsigned char data[] ) +{ + unsigned retVal = HID_STATUS_IN_USE; + + if( !hidReportDescriptorPrepared ) { + retVal = HID_STATUS_BAD_LOCATION; + unsigned bSize = hidGetItemSize( header ); + unsigned bTag = hidGetItemTag ( header ); + unsigned bType = hidGetItemType( header ); + + if(( HID_REPORT_ITEM_MAX_SIZE < bSize ) || + ( HID_REPORT_ITEM_USAGE_TAG != bTag ) || + ( HID_REPORT_ITEM_USAGE_TYPE != bType )) { + retVal = HID_STATUS_BAD_HEADER; + } else { + for( unsigned itemIdx = 0; itemIdx < HID_CONFIGURABLE_ITEM_COUNT; ++itemIdx ) { + USB_HID_Short_Item_t item = *hidConfigurableItems[ itemIdx ]; + unsigned bBit = hidGetItemBitLocation( item.location ); + unsigned bByte = hidGetItemByteLocation( item.location ); + + if(( bit == bBit ) && ( byte == bByte )) { + item.header = header; + + for( unsigned dataIdx = 0; dataIdx < bSize; ++dataIdx ) { + item.data[ dataIdx ] = data[ dataIdx ]; + } + + *hidConfigurableItems[ itemIdx ] = item; + retVal = HID_STATUS_GOOD; + } + } + } + } + + return retVal; +} + +static size_t hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char** outPtrPtr ) +{ + size_t count = 0; + *(*outPtrPtr)++ = inPtr->header; + ++count; + + unsigned dataLength = hidGetItemSize( inPtr->header ); + for( unsigned idx = 0; idx < dataLength; ++idx ) { + *(*outPtrPtr)++ = inPtr->data[ idx ]; + ++count; + } + + return count; +} diff --git a/lib_xua/src/hid/xua_hid_descriptor.h b/lib_xua/src/hid/xua_hid_descriptor.h index 4fed07a2..4dd44007 100644 --- a/lib_xua/src/hid/xua_hid_descriptor.h +++ b/lib_xua/src/hid/xua_hid_descriptor.h @@ -11,6 +11,8 @@ #ifndef _HID_DESCRIPTOR_ #define _HID_DESCRIPTOR_ +#define HID_DESCRIPTOR_LENGTH_FIELD_OFFSET ( 7 ) + /* USB HID Descriptor (section 6.2.1) */ typedef struct { diff --git a/lib_xua/src/hid/xua_hid_descriptor_contents.h b/lib_xua/src/hid/xua_hid_descriptor_contents.h index d91e7b15..766c323e 100644 --- a/lib_xua/src/hid/xua_hid_descriptor_contents.h +++ b/lib_xua/src/hid/xua_hid_descriptor_contents.h @@ -12,15 +12,15 @@ #include "xua_hid_descriptor.h" -#define HID_DESCRIPTOR_LENGTH_0 0x09 /* Size of descriptor in Bytes */ -#define HID_DESCRIPTOR_TYPE_0 0x21 /* HID 0x21 */ -#define HID_BCD_VERSION_LO 0x10 /* HID class specification release */ -#define HID_BCD_VERSION_HI 0x01 -#define HID_COUNTRY_CODE 0x00 /* Country code of localized hardware */ -#define HID_NUM_DESCRIPTORS 0x01 /* Number of class descriptors */ -#define HID_DESCRIPTOR_TYPE_1 0x22 /* Type of 1st class descriptor, Report 0x22 */ -#define HID_DESCRIPTOR_LENGTH_1_LO sizeof(hidReportDescriptor) & 0xff -#define HID_DESCRIPTOR_LENGTH_1_HI sizeof(hidReportDescriptor) >> 8 +#define HID_DESCRIPTOR_LENGTH_0 ( 0x09 ) /* Size of descriptor in Bytes */ +#define HID_DESCRIPTOR_TYPE_0 ( 0x21 ) /* HID 0x21 */ +#define HID_BCD_VERSION_LO ( 0x10 ) /* HID class specification release */ +#define HID_BCD_VERSION_HI ( 0x01 ) +#define HID_COUNTRY_CODE ( 0x00 ) /* Country code of localized hardware */ +#define HID_NUM_DESCRIPTORS ( 0x01 ) /* Number of class descriptors */ +#define HID_DESCRIPTOR_TYPE_1 ( 0x22 ) /* Type of 1st class descriptor, Report 0x22 */ +#define HID_DESCRIPTOR_LENGTH_1_LO ( 0x00 ) /* Length of 1st class descriptor, set to zero */ +#define HID_DESCRIPTOR_LENGTH_1_HI ( 0x00 ) /* since only pre-processor directives allowed here */ #endif // _HID_DESCRIPTOR_CONTENTS_ diff --git a/lib_xua/src/hid/xua_hid_endpoint_descriptor_contents.h b/lib_xua/src/hid/xua_hid_endpoint_descriptor_contents.h index bba85c72..d6bcc6b3 100644 --- a/lib_xua/src/hid/xua_hid_endpoint_descriptor_contents.h +++ b/lib_xua/src/hid/xua_hid_endpoint_descriptor_contents.h @@ -12,11 +12,11 @@ #include "descriptor_defs.h" -#define HID_ENDPOINT_DESCRIPTOR_LENGTH 0x07 /* Size of descriptor in Bytes */ -#define HID_ENDPOINT_DESCRIPTOR_TYPE 0x05 /* Endpoint 0x05 */ -#define HID_ENDPOINT_ATTRIBUTES 0x03 /* Interrupt */ -#define HID_ENDPOINT_DESCRIPTOR_PACKET_SIZE_LO 0x40 -#define HID_ENDPOINT_DESCRIPTOR_PACKET_SIZE_HI 0x00 +#define HID_ENDPOINT_DESCRIPTOR_LENGTH ( 0x07 ) /* Size of descriptor in Bytes */ +#define HID_ENDPOINT_DESCRIPTOR_TYPE ( 0x05 ) /* Endpoint 0x05 */ +#define HID_ENDPOINT_ATTRIBUTES ( 0x03 ) /* Interrupt */ +#define HID_ENDPOINT_DESCRIPTOR_PACKET_SIZE_LO ( 0x40 ) +#define HID_ENDPOINT_DESCRIPTOR_PACKET_SIZE_HI ( 0x00 ) #endif // _HID_ENDPOINT_DESCRIPTOR_CONTENTS_ diff --git a/lib_xua/src/hid/xua_hid_interface_descriptor_contents.h b/lib_xua/src/hid/xua_hid_interface_descriptor_contents.h index 20df1815..83d69fbd 100644 --- a/lib_xua/src/hid/xua_hid_interface_descriptor_contents.h +++ b/lib_xua/src/hid/xua_hid_interface_descriptor_contents.h @@ -12,14 +12,14 @@ #include "descriptor_defs.h" -#define HID_INTERFACE_DESCRIPTOR_LENGTH 0x09 /* Size of descriptor in Bytes */ -#define HID_INTERFACE_DESCRIPTOR_TYPE 0x04 /* Interface 0x04 */ -#define HID_INTERFACE_ALTERNATE_SETTING 0x00 /* Value used alternate interfaces using SetInterface Request */ -#define HID_INTERFACE_NUMBER_OF_ENDPOINTS 0x01 /* Number of endpoitns for this interface (excluding 0) */ -#define HID_INTERFACE_CLASS 0x03 -#define HID_INTERFACE_SUBCLASS 0x00 /* No boot device */ -#define HID_INTERFACE_PROTOCOL 0x00 -#define HID_INTERFACE_STRING_DESCRIPTOR_INDEX 0x00 +#define HID_INTERFACE_DESCRIPTOR_LENGTH ( 0x09 ) /* Size of descriptor in Bytes */ +#define HID_INTERFACE_DESCRIPTOR_TYPE ( 0x04 ) /* Interface 0x04 */ +#define HID_INTERFACE_ALTERNATE_SETTING ( 0x00 ) /* Value used alternate interfaces using SetInterface Request */ +#define HID_INTERFACE_NUMBER_OF_ENDPOINTS ( 0x01 ) /* Number of endpoitns for this interface (excluding 0) */ +#define HID_INTERFACE_CLASS ( 0x03 ) +#define HID_INTERFACE_SUBCLASS ( 0x00 ) /* No boot device */ +#define HID_INTERFACE_PROTOCOL ( 0x00 ) +#define HID_INTERFACE_STRING_DESCRIPTOR_INDEX ( 0x00 ) #endif // _HID_INTERFACE_DESCRIPTOR_CONTENTS_ diff --git a/lib_xua/src/hid/xua_hid_report_descriptor.h b/lib_xua/src/hid/xua_hid_report_descriptor.h new file mode 100644 index 00000000..9d936c2b --- /dev/null +++ b/lib_xua/src/hid/xua_hid_report_descriptor.h @@ -0,0 +1,147 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/** + * @brief Human Interface Device (HID) Report descriptor + * + * This file defines the structure of the HID Report descriptor and declares + * functions for manipulating it. + * Because the Report descriptor defines the length of the HID Report, this file + * declares a function for obtaining the Report length as well. + * The using application has the responsibility to define the report descriptor + * structure and default contents in their hid_report_descriptor.h file. + * Document section numbers refer to the HID Device Class Definition, version 1.11. + */ + +#ifndef _HID_REPORT_DESCRIPTOR_ +#define _HID_REPORT_DESCRIPTOR_ + +#define HID_REPORT_ITEM_HDR_SIZE_MASK ( 0x03 ) +#define HID_REPORT_ITEM_HDR_SIZE_SHIFT ( 0 ) + +#define HID_REPORT_ITEM_HDR_TAG_MASK ( 0xF0 ) +#define HID_REPORT_ITEM_HDR_TAG_SHIFT ( 4 ) + +#define HID_REPORT_ITEM_HDR_TYPE_MASK ( 0x0C ) +#define HID_REPORT_ITEM_HDR_TYPE_SHIFT ( 2 ) + +#define HID_REPORT_ITEM_LOC_BIT_MASK ( 0x70 ) +#define HID_REPORT_ITEM_LOC_BIT_SHIFT ( 4 ) + +#define HID_REPORT_ITEM_LOC_BYTE_MASK ( 0x0F ) +#define HID_REPORT_ITEM_LOC_BYTE_SHIFT ( 0 ) + +#define HID_REPORT_ITEM_MAX_SIZE ( 2 ) + +#define HID_REPORT_ITEM_USAGE_TAG ( 0 ) +#define HID_REPORT_ITEM_USAGE_TYPE ( 2 ) + +#define HID_STATUS_GOOD ( 0 ) +#define HID_STATUS_BAD_HEADER ( 1 ) +#define HID_STATUS_BAD_LOCATION ( 2 ) +#define HID_STATUS_IN_USE ( 3 ) + +/** + * @brief USB HID Report Descriptor. Short Item + * + * @note + * To reduce memory use, this type does not support Short Items with 4 data bytes. + * See section 6.2.2.2 + * + * Elements: + * + * header - the item prefix containing the size, type and tag fields (see 6.2.2.2) + * Format (bit range): bSize (0:1), bType (2:3), bTag (4:7) + * data - a two byte array for holding the item's data + * The bSize field indicates which data bytes are in use + * location - a non-standard extension locating the item within the HID Report + * Format (bit range): iByte (0:3), iBit (4:6), Reserved (7) + */ +typedef struct +{ + unsigned char header; + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ]; + unsigned char location; +} USB_HID_Short_Item_t; + +/** + * @brief Get the HID Report descriptor + * + * This function returns a pointer to the USB HID Report descriptor. + * It returns NULL if the Report descriptor has not been prepared, + * i.e., no one has called \c hidPrepareReportDescriptor(). + * + * @note An XC-callable version of this function has not been provided. + * XC requires explicit declaration of the kind of pointer returned, + * hence an XC implementation of the function. + * + * @return A pointer to a list of unsigned char containing the Report descriptor + */ +#if !defined(__XC__) +unsigned char* hidGetReportDescriptor( void ); +#endif + +/** + * @brief Get the length of the HID Report descriptor + * + * This function returns the length of the USB HID Report descriptor. + * It returns zero if the Report descriptor has not been prepared, + * i.e., no one has called \c hidPrepareReportDescriptor(). + * + * @return The length of the Report descriptor in bytes + */ +size_t hidGetReportDescriptorLength( void ); + +/** + * @brief Get the length of the HID Report + * + * This function returns the length of the USB HID Report. + * It returns zero if the Report descriptor has not been prepared, + * i.e., no one has called \c hidPrepareReportDescriptor(). + * + * @return The length of the Report in bytes + */ +size_t hidGetReportLength( void ); + +/** + * @brief Prepare the USB HID Report descriptor + * + * After preparation, \c hidGetReportDescriptor() returns a list suitable for transmission over USB. + * Call this function after altering one or more Report Items using \c hidSetReportItem(). + */ +void hidPrepareReportDescriptor( void ); + +/** + * @brief Reset the USB HID Report descriptor + * + * After reset, \c hidGetReportDescriptor() returns NULL until a subsequent call to + * \c hidPrepareReportDescriptor() occurs. + * Call this function before altering one or more Report Items using \c hidSetReportItem(). + */ +void hidResetReportDescriptor( void ); + +/** + * @brief Modify a HID Report descriptor item + * + * @warning This function does not check that the length of the \a data array matches the value of + * the bSize field in the \a header. For safe operation use a \a data array of at least + * \c HID_REPORT_ITEM_MAX_SIZE bytes in length. + * + * Parameters: + * + * @param[in] byte The byte position of the control within the HID Report + * @param[in] bit The bit position of the control within the \a byte + * @param[in] header The LSB of the Item containing the bSize, bType and bTag fields (see 6.2.2.2) + * @param[in] data An array containing data bytes or NULL for an Item with no data + * + * @return A status value + * @retval \c HID_STATUS_GOOD Item successfully updated + * @retval \c HID_STATUS_BAD_HEADER The Item header specified a data size greater than 2 or + * a Tag or Type inconsistent with a Usage Item + * @retval \c HID_STATUS_BAD_LOCATION The \a bit or \a byte arguments specify a location outside + * of the HID Report + * @retval \c HID_STATUS_IN_USE The Report descriptor is in use + */ +unsigned hidSetReportItem( const unsigned byte, const unsigned bit, const unsigned char header, const unsigned char data[] ); + +#endif // _HID_REPORT_DESCRIPTOR_ diff --git a/python/setup.py b/python/setup.py index 98d3d6a5..d3a7c32c 100644 --- a/python/setup.py +++ b/python/setup.py @@ -14,6 +14,8 @@ setuptools.setup( packages=setuptools.find_packages(), install_requires=[ 'flake8~=3.8', + 'pytest~=6.0', + 'pytest-xdist~=1.34', ], dependency_links=[ ], diff --git a/requirements.txt b/requirements.txt index 99e7dd7b..a9ebc4e9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,6 +18,8 @@ # same modules should appear in the setup.py list as given below. flake8==3.8.3 +pytest==6.0.0 +pytest-xdist==1.34.0 # Development dependencies # diff --git a/tests/.gitignore b/tests/.gitignore index f0bcfb69..3bbde6ff 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1 +1,2 @@ +*/runners/* test_results.csv diff --git a/tests/xua_unit_tests/config.xscope b/tests/xua_unit_tests/config.xscope new file mode 100644 index 00000000..bfdf1f86 --- /dev/null +++ b/tests/xua_unit_tests/config.xscope @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/xua_unit_tests/conftest.py b/tests/xua_unit_tests/conftest.py new file mode 100644 index 00000000..e2e1a54a --- /dev/null +++ b/tests/xua_unit_tests/conftest.py @@ -0,0 +1,115 @@ +# Copyright 2021 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +from __future__ import print_function +from builtins import str +import os.path +import pytest +import subprocess + +target = os.environ.get('TARGET', 'all_possible') +print("target = ", target) + +def pytest_collect_file(parent, path): + if(path.ext == ".xe"): + if(target == 'all_possible'): + return UnityTestSource.from_parent(parent, fspath=path) + if(target == 'XCOREAI' and ('xcoreai' in path.basename)): + return UnityTestSource.from_parent(parent, fspath=path) + if(target == 'XCORE200' and ('xcore200' in path.basename)): + return UnityTestSource.from_parent(parent, fspath=path) + + +class UnityTestSource(pytest.File): + def collect(self): + # Find the binary built from the runner for this test file + # + # Assume the following directory layout: + # unit_tests/ <- Test root directory + # |-- bin/ <- Compiled binaries of the test runners + # |-- conftest.py <- This file + # |-- runners/ <- Auto-generated buildable source of test binaries + # |-- src/ <- Unity test functions + # `-- wscript <- Build system file used to generate/build runners + xe_name = ((os.path.basename(self.name)).split("."))[0] + ".xe" + test_bin_path = os.path.join('bin', xe_name) + + yield UnityTestExecutable.from_parent(self, name=self.name) + + +class UnityTestExecutable(pytest.Item): + def __init__(self, name, parent): + super(UnityTestExecutable, self).__init__(name, parent) + self._nodeid = self.name # Override the naming to suit C better + + def runtest(self): + # Run the binary in the simulator + simulator_fail = False + test_output = None + try: + if('xcore200' in self.name): + print("run axe for executable ", self.name) + test_output = subprocess.check_output(['axe', self.name], text=True) + else: + print("run xrun for executable ", self.name) + test_output = subprocess.check_output(['xrun', '--io', '--id', '0', self.name], text=True, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + # Unity exits non-zero if an assertion fails + simulator_fail = True + test_output = e.output + + # Parse the Unity output + unity_pass = False + test_output = test_output.split('\n') + for line in test_output: + if 'test' in line: + test_report = line.split(':') + # Unity output is as follows: + # :::PASS + # :::FAIL: + test_source = test_report[0] + line_number = test_report[1] + test_case = test_report[2] + result = test_report[3] + failure_reason = None + print(('\n {}()'.format(test_case)), end=' ') + if result == 'PASS': + unity_pass = True + continue + if result == 'FAIL': + failure_reason = test_report[4] + print('') # Insert line break after test_case print + raise UnityTestException(self, {'test_source': test_source, + 'line_number': line_number, + 'test_case': test_case, + 'failure_reason': + failure_reason}) + + if simulator_fail: + raise Exception(self, "Simulation failed.") + if not unity_pass: + raise Exception(self, "Unity test output not found.") + print('') # Insert line break after final test_case which passed + + def repr_failure(self, excinfo): + if isinstance(excinfo.value, UnityTestException): + return '\n'.join([str(self.parent).strip('<>'), + '{}:{}:{}()'.format( + excinfo.value[1]['test_source'], + excinfo.value[1]['line_number'], + excinfo.value[1]['test_case']), + 'Failure reason:', + excinfo.value[1]['failure_reason']]) + else: + return str(excinfo.value) + + def reportinfo(self): + # It's not possible to give sensible line number info for an executable + # so we return it as 0. + # + # The source line number will instead be recovered from the Unity print + # statements. + return self.fspath, 0, self.name + + +class UnityTestException(Exception): + pass diff --git a/tests/xua_unit_tests/generate_unity_runners.py b/tests/xua_unit_tests/generate_unity_runners.py new file mode 100644 index 00000000..f2fe0f04 --- /dev/null +++ b/tests/xua_unit_tests/generate_unity_runners.py @@ -0,0 +1,120 @@ +# Copyright 2021 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import glob +import os.path +import subprocess +import sys + +UNITY_TEST_DIR = 'src' +UNITY_TEST_PREFIX = 'test_' +UNITY_RUNNER_DIR = 'runners' +UNITY_RUNNER_SUFFIX = '_Runner' +project_root = os.path.join('..', '..', '..') + +def get_ruby(): + """ + Check ruby is avaliable and return the command to invoke it. + """ + interpreter_name = 'ruby' + try: + dev_null = open(os.devnull, 'w') + # Call the version command to check the interpreter can be run + subprocess.check_call([interpreter_name, '--version'], + stdout=dev_null, + close_fds=True) + except OSError as e: + print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + + return interpreter_name + +def get_unity_runner_generator(project_root_path): + """ + Check the Unity generate_test_runner script is avaliable, and return the + path to it. + """ + unity_runner_generator = os.path.join( + project_root_path, 'Unity', 'auto', 'generate_test_runner.rb') + if not os.path.exists(unity_runner_generator): + print("Unity repo not found in workspace", file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + return unity_runner_generator + + +def get_test_name(test_path): + """ + Return the test name by removing the extension from the filename. + """ + return os.path.splitext(os.path.basename(test_path))[0] + + +def get_file_type(filename): + """ + Return the extension from the filename. + """ + return filename.rsplit('.')[-1:][0] + + +def generate_unity_runner(project_root_path, unity_test_path, unity_runner_dir, + unity_runner_suffix): + """ + Invoke the Unity runner generation script for the given test file, and + return the path to the generated file. The output directory will be created + if it does not already exist. + """ + runner_path = os.path.join(os.path.join(unity_runner_dir, get_test_name(unity_test_path))) + if not os.path.exists(runner_path): + os.makedirs(runner_path) + + unity_runner_path = os.path.join( + runner_path, get_test_name(unity_test_path) + unity_runner_suffix + + '.' + 'c') + + try: + subprocess.check_call([get_ruby(), + get_unity_runner_generator(project_root_path), + unity_test_path, + unity_runner_path]) + except OSError as e: + print("Ruby generator failed for {}\n\t{}".format(unity_test_path, e), + file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + + +def find_unity_test_paths(unity_test_dir, unity_test_prefix): + """ + Return a list of all file paths with the unity_test_prefix found in the + unity_test_dir. + """ + return glob.glob(os.path.join(unity_test_dir, unity_test_prefix+'*')) + + +def find_unity_tests(unity_test_dir, unity_test_prefix): + """ + Return a dictionary of all {test names, test language} pairs with the + unity_test_prefix found in the unity_test_dir. + """ + unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) + print('unity_test_paths = ', unity_test_paths) + return {get_test_name(path): get_file_type(path) + for path in unity_test_paths} + +def find_unity_test_paths(unity_test_dir, unity_test_prefix): + """ + Return a list of all file paths with the unity_test_prefix found in the + unity_test_dir. + """ + return glob.glob(os.path.join(unity_test_dir, unity_test_prefix+'*')) + + +def generate_runners(): + UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX) + print('UNITY_TESTS = ',UNITY_TESTS) + unity_test_paths = find_unity_test_paths(UNITY_TEST_DIR, UNITY_TEST_PREFIX) + print('unity_test_paths = ',unity_test_paths) + for unity_test_path in unity_test_paths: + generate_unity_runner(project_root, unity_test_path, UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX) + + +if __name__ == "__main__": + generate_runners() diff --git a/tests/xua_unit_tests/src/hid_report_descriptor.h b/tests/xua_unit_tests/src/hid_report_descriptor.h new file mode 100644 index 00000000..faf3e7f7 --- /dev/null +++ b/tests/xua_unit_tests/src/hid_report_descriptor.h @@ -0,0 +1,94 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#ifndef __hid_report_descriptor_h__ +#define __hid_report_descriptor_h__ + +#include "xua_hid_report_descriptor.h" + +#define MAX_VALID_BIT ( 7 ) +#define MAX_VALID_BYTE ( 1 ) + +#define MIN_VALID_BIT ( 0 ) +#define MIN_VALID_BYTE ( 0 ) + +/* + * Define non-configurable items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t hidCollectionApplication = { .header = 0xA1, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidCollectionEnd = { .header = 0xC0, .data = { 0x00, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidCollectionLogical = { .header = 0xA1, .data = { 0x02, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidInputConstArray = { .header = 0x81, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidInputDataVar = { .header = 0x81, .data = { 0x02, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidLogicalMaximum0 = { .header = 0x25, .data = { 0x00, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMaximum1 = { .header = 0x25, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidLogicalMinimum0 = { .header = 0x15, .data = { 0x00, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidReportCount1 = { .header = 0x95, .data = { 0x01, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportCount6 = { .header = 0x95, .data = { 0x06, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportCount7 = { .header = 0x95, .data = { 0x07, 0x00 }, .location = 0x00 }; +static const USB_HID_Short_Item_t hidReportSize1 = { .header = 0x75, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsageConsumerControl = { .header = 0x09, .data = { 0x01, 0x00 }, .location = 0x00 }; + +static const USB_HID_Short_Item_t hidUsagePageConsumer = { .header = 0x05, .data = { 0x0C, 0x00 }, .location = 0x00 }; + +/* + * Define configurable items in the HID Report descriptor. + */ +static USB_HID_Short_Item_t hidUsageByte0Bit0 = { .header = 0x09, .data = { 0xE2, 0x00 }, .location = 0x00 }; // Mute + +static USB_HID_Short_Item_t hidUsageByte1Bit7 = { .header = 0x09, .data = { 0xEA, 0x00 }, .location = 0x71 }; // Vol- +static USB_HID_Short_Item_t hidUsageByte1Bit0 = { .header = 0x09, .data = { 0xE9, 0x00 }, .location = 0x01 }; // Vol+ + +/* + * List the configurable items in the HID Report descriptor. + */ +static USB_HID_Short_Item_t* const hidConfigurableItems[] = { + &hidUsageByte0Bit0, + &hidUsageByte1Bit0, + &hidUsageByte1Bit7 +}; + +/* + * List all items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t* const hidReportDescriptorItems[] = { + &hidUsagePageConsumer, + &hidUsageConsumerControl, + &hidCollectionApplication, + &hidReportSize1, + &hidLogicalMinimum0, + &hidCollectionLogical, // Byte 0 + &hidLogicalMaximum1, + &hidReportCount1, + &hidUsageByte0Bit0, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount7, + &hidInputConstArray, + &hidCollectionEnd, + &hidCollectionLogical, // Byte 1 + &hidLogicalMaximum1, + &hidReportCount1, + &hidUsageByte1Bit0, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount6, + &hidInputConstArray, + &hidLogicalMaximum1, + &hidUsageByte1Bit7, + &hidInputDataVar, + &hidCollectionEnd, + &hidCollectionEnd +}; + +/* + * Define the length of the HID Report. + * This value must match the number of Report bytes defined by hidReportDescriptorItems. + */ +#define HID_REPORT_LENGTH ( 2 ) + +#endif // __hid_report_descriptor_h__ diff --git a/tests/xua_unit_tests/src/test_hid.c b/tests/xua_unit_tests/src/test_hid.c new file mode 100644 index 00000000..7c014a2c --- /dev/null +++ b/tests/xua_unit_tests/src/test_hid.c @@ -0,0 +1,333 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include + +#include "xua_unit_tests.h" +#include "xua_hid_report_descriptor.h" +#include "hid_report_descriptor.h" + +#define HID_REPORT_ITEM_TYPE_GLOBAL ( 0x01 ) +#define HID_REPORT_ITEM_TYPE_LOCAL ( 0x02 ) +#define HID_REPORT_ITEM_TYPE_MAIN ( 0x00 ) +#define HID_REPORT_ITEM_TYPE_RESERVED ( 0x03 ) + +#define LOUDNESS_CONTROL ( 0xE7 ) + +static unsigned construct_usage_header( unsigned size ) +{ + unsigned header = 0x00; + + header |= ( HID_REPORT_ITEM_USAGE_TAG << HID_REPORT_ITEM_HDR_TAG_SHIFT ) & HID_REPORT_ITEM_HDR_TAG_MASK; + header |= ( HID_REPORT_ITEM_USAGE_TYPE << HID_REPORT_ITEM_HDR_TYPE_SHIFT ) & HID_REPORT_ITEM_HDR_TYPE_MASK; + + header |= ( size << HID_REPORT_ITEM_HDR_SIZE_SHIFT ) & HID_REPORT_ITEM_HDR_SIZE_MASK; + + return header; +} + +void setUp( void ) +{ + hidResetReportDescriptor(); +} + +// Basic report descriptor tests +void test_unprepared_hidGetReportDescriptor( void ) +{ + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NULL( reportDescPtr ); + + unsigned reportLength = hidGetReportLength(); + TEST_ASSERT_EQUAL_UINT( 0, reportLength ); +} + +void test_prepared_hidGetReportDescriptor( void ) +{ + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); + + unsigned reportLength = hidGetReportLength(); + TEST_ASSERT_EQUAL_UINT( HID_REPORT_LENGTH, reportLength ); +} + +void test_reset_unprepared_hidGetReportDescriptor( void ) +{ + hidPrepareReportDescriptor(); + hidResetReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NULL( reportDescPtr ); +} + +void test_reset_prepared_hidGetReportDescriptor( void ) +{ + hidPrepareReportDescriptor(); + hidResetReportDescriptor(); + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); +} + +// Configurable and non-configurable item tests +void test_configurable_item_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + + unsigned retVal = hidSetReportItem( byte, bit, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_nonconfigurable_item_hidSetReportItem( void ) +{ + const unsigned bit = MAX_VALID_BIT; // This bit and byte combination should not appear in the + const unsigned byte = MIN_VALID_BYTE; // hidConfigurableItems list in hid_report_descriptors.c. + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + + unsigned retVal = hidSetReportItem( byte, bit, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +// Bit range tests +void test_max_bit_hidSetReportItem( void ) +{ + const unsigned bit = MAX_VALID_BIT; // Only byte 1 has bit 7 not reserved, See the + const unsigned byte = MAX_VALID_BYTE; // hidConfigurableItems list in hid_report_descriptors.c. + const unsigned char header = construct_usage_header( 0 ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_min_bit_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0 ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_overflow_bit_hidSetReportItem( void ) +{ + const unsigned bit = MAX_VALID_BIT + 1; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0 ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +void test_underflow_bit_hidSetReportItem( void ) +{ + const int bit = MIN_VALID_BIT - 1; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0 ); + + unsigned retVal = hidSetReportItem( byte, ( unsigned ) bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +// Byte range tests +void test_max_byte_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MAX_VALID_BYTE; + const unsigned char header = construct_usage_header( 0 ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_min_byte_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0 ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_overflow_byte_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MAX_VALID_BYTE + 1; + const unsigned char header = construct_usage_header( 0 ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +void test_underflow_byte_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const int byte = MIN_VALID_BYTE - 1; + const unsigned char header = construct_usage_header( 0 ); + + unsigned retVal = hidSetReportItem( ( unsigned ) byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +// Size range tests +void test_max_size_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0x00 }; + const unsigned char header = construct_usage_header( HID_REPORT_ITEM_MAX_SIZE ); + + unsigned retVal = hidSetReportItem( byte, bit, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_min_size_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0x00 ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_unsupported_size_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0x03 ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_HEADER, retVal ); +} + +// Header tag and type tests +void test_bad_tag_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char good_header = construct_usage_header( 0x00 ); + + for( unsigned tag = 0x01; tag <= 0x0F; ++tag ) { + unsigned char bad_header = good_header | (( 0x0F << HID_REPORT_ITEM_HDR_TAG_SHIFT ) & HID_REPORT_ITEM_HDR_TAG_MASK ); + unsigned retVal = hidSetReportItem( byte, bit, bad_header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_HEADER, retVal ); + } +} + +void test_global_type_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = ( construct_usage_header( 0x00 ) & ~HID_REPORT_ITEM_HDR_TYPE_MASK ) | + (( HID_REPORT_ITEM_TYPE_GLOBAL << HID_REPORT_ITEM_HDR_TYPE_SHIFT ) & HID_REPORT_ITEM_HDR_TYPE_MASK ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_HEADER, retVal ); +} + +void test_local_type_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = ( construct_usage_header( 0x00 ) & ~HID_REPORT_ITEM_HDR_TYPE_MASK ) | + (( HID_REPORT_ITEM_TYPE_LOCAL << HID_REPORT_ITEM_HDR_TYPE_SHIFT ) & HID_REPORT_ITEM_HDR_TYPE_MASK ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_main_type_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = ( construct_usage_header( 0x00 ) & ~HID_REPORT_ITEM_HDR_TYPE_MASK ) | + (( HID_REPORT_ITEM_TYPE_MAIN << HID_REPORT_ITEM_HDR_TYPE_SHIFT ) & HID_REPORT_ITEM_HDR_TYPE_MASK ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_HEADER, retVal ); +} + +void test_reserved_type_hidSetReportItem( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = ( construct_usage_header( 0x00 ) & ~HID_REPORT_ITEM_HDR_TYPE_MASK ) | + (( HID_REPORT_ITEM_TYPE_RESERVED << HID_REPORT_ITEM_HDR_TYPE_SHIFT ) & HID_REPORT_ITEM_HDR_TYPE_MASK ); + + unsigned retVal = hidSetReportItem( byte, bit, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_HEADER, retVal ); +} + +// Combined function tests +void test_initial_modification_without_subsequent_preparation( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + + unsigned retVal = hidSetReportItem( byte, bit, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NULL( reportDescPtr ); +} + +void test_initial_modification_with_subsequent_preparation( void ) +{ + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + + unsigned retVal = hidSetReportItem( byte, bit, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); +} + +void test_modification_without_subsequent_preparation( void ) +{ + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); + + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + + hidResetReportDescriptor(); + unsigned retVal = hidSetReportItem( byte, bit, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NULL( reportDescPtr ); +} + +void test_modification_with_subsequent_preparation( void ) +{ + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); + + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + + hidResetReportDescriptor(); + unsigned retVal = hidSetReportItem( byte, bit, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + hidPrepareReportDescriptor(); + reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); +} diff --git a/tests/xua_unit_tests/src/xua_conf.h b/tests/xua_unit_tests/src/xua_conf.h new file mode 100644 index 00000000..798c5e43 --- /dev/null +++ b/tests/xua_unit_tests/src/xua_conf.h @@ -0,0 +1,37 @@ +// Copyright 2017-2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#define NUM_USB_CHAN_OUT 2 +#define NUM_USB_CHAN_IN 2 +#define I2S_CHANS_DAC 2 +#define I2S_CHANS_ADC 2 +#define MCLK_441 (512 * 44100) +#define MCLK_48 (512 * 48000) +#define MIN_FREQ 48000 +#define MAX_FREQ 48000 + +#define EXCLUDE_USB_AUDIO_MAIN +#define XUA_NUM_PDM_MICS 0 + +#define PDM_TILE 2 +#define XUD_TILE 1 +#define AUDIO_IO_TILE 1 + +#define MIXER 0 + +#define SPDIF_TX_INDEX 0 +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "XMOS USB Audio Class" +#define PRODUCT_STR_A1 "XMOS USB Audio Class" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define AUDIO_CLASS 2 +#define AUDIO_CLASS_FALLBACK 0 +#define BCD_DEVICE 0x1234 +#define XUA_DFU_EN 0 + +/* TODO */ +#define XUA_DFU XUA_DFU_EN + +#define FB_USE_REF_CLOCK 1 diff --git a/tests/xua_unit_tests/src/xua_unit_test_helper.xc b/tests/xua_unit_tests/src/xua_unit_test_helper.xc new file mode 100644 index 00000000..ccb35260 --- /dev/null +++ b/tests/xua_unit_tests/src/xua_unit_test_helper.xc @@ -0,0 +1,28 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifdef __XC__ + +#include +#include +#include + +#endif // __XC__ + + +in port p_mclk_in = XS1_PORT_1D; + +/* Clock-block declarations */ +clock clk_audio_bclk = on tile[0]: XS1_CLKBLK_1; /* Bit clock */ +clock clk_audio_mclk = on tile[0]: XS1_CLKBLK_3; /* Master clock */ + +// Supply missing but unused function +void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) +{ + ; // nothing +} + +// Supply missing but unused function +void AudioHwInit() +{ + ; // nothing +} diff --git a/tests/xua_unit_tests/src/xua_unit_tests.h b/tests/xua_unit_tests/src/xua_unit_tests.h new file mode 100644 index 00000000..0b1d82d9 --- /dev/null +++ b/tests/xua_unit_tests/src/xua_unit_tests.h @@ -0,0 +1,9 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef XUA_UNIT_TESTS_H_ +#define XUA_UNIT_TESTS_H_ + +#include "unity.h" +#include "xua_conf.h" + +#endif /* XUA_UNIT_TESTS_H_ */ diff --git a/tests/xua_unit_tests/wscript b/tests/xua_unit_tests/wscript new file mode 100644 index 00000000..0f162c19 --- /dev/null +++ b/tests/xua_unit_tests/wscript @@ -0,0 +1,250 @@ +from __future__ import print_function +import glob +import os.path +import subprocess +import sys +from waflib import Options +from waflib.Build import BuildContext, CleanContext + +TARGETS = ['xcore200', 'xcoreai'] + +def get_ruby(): + """ + Check ruby is avaliable and return the command to invoke it. + """ + interpreter_name = 'ruby' + try: + dev_null = open(os.devnull, 'w') + # Call the version command to check the interpreter can be run + subprocess.check_call([interpreter_name, '--version'], + stdout=dev_null, + close_fds=True) + except OSError as e: + print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + + return interpreter_name + + +def get_unity_runner_generator(project_root_path): + """ + Check the Unity generate_test_runner script is avaliable, and return the + path to it. + """ + unity_runner_generator = os.path.join( + project_root_path, 'Unity', 'auto', 'generate_test_runner.rb') + if not os.path.exists(unity_runner_generator): + print("Unity repo not found in workspace", file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + return unity_runner_generator + + +def get_test_name(test_path): + """ + Return the test name by removing the extension from the filename. + """ + return os.path.splitext(os.path.basename(test_path))[0] + + +def get_file_type(filename): + """ + Return the extension from the filename. + """ + return filename.rsplit('.')[-1:][0] + + +def generate_unity_runner(project_root_path, unity_test_path, unity_runner_dir, + unity_runner_suffix): + """ + Invoke the Unity runner generation script for the given test file, and + return the path to the generated file. The output directory will be created + if it does not already exist. + """ + runner_path = os.path.join(os.path.join(unity_runner_dir, get_test_name(unity_test_path))) + if not os.path.exists(runner_path): + os.makedirs(runner_path) + + unity_runner_path = os.path.join( + runner_path, get_test_name(unity_test_path) + unity_runner_suffix + + '.' + 'c') + + try: + subprocess.check_call([get_ruby(), + get_unity_runner_generator(project_root_path), + unity_test_path, + unity_runner_path]) + except OSError as e: + print("Ruby generator failed for {}\n\t{}".format(unity_test_path, e), + file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + + +def add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path, + unity_runner_build_flags, target): + """ + Add a config to xwaf to build each Unity test runner into an xCORE + executable. + """ + print(f"get_test_name(unity_test_path) = {get_test_name(unity_test_path)}. target = {target}") + waf_conf.setenv(get_test_name(unity_test_path) + '_' + target) + waf_conf.load('xwaf.compiler_xcc') + waf_conf.env.XCC_FLAGS = unity_runner_build_flags + waf_conf.env.PROJECT_ROOT = project_root_path + # TODO: can the xwaf boilerplate help here? + + +def prepare_unity_test_for_build(waf_conf, project_root_path, unity_test_path, + unity_runner_dir, unity_runner_suffix, target): + generate_unity_runner(project_root_path, unity_test_path, + unity_runner_dir, unity_runner_suffix) + runner_build_flags = '' # Could extract flags from the test name + add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path, + runner_build_flags, target) + + +def find_unity_test_paths(unity_test_dir, unity_test_prefix): + """ + Return a list of all file paths with the unity_test_prefix found in the + unity_test_dir. + """ + return glob.glob(os.path.join(unity_test_dir, unity_test_prefix+'*')) + + +def find_unity_tests(unity_test_dir, unity_test_prefix): + """ + Return a dictionary of all {test names, test language} pairs with the + unity_test_prefix found in the unity_test_dir. + """ + unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) + return {get_test_name(path): get_file_type(path) + for path in unity_test_paths} + + +def generate_all_unity_runners(waf_conf, project_root_path, + unity_test_dir, unity_test_prefix, + unity_runner_dir, unity_runner_suffix): + """ + Generate a runner and a build config for each test file in the + unity_test_dir. + """ + # FIXME: pass unity_tests in? + unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) + for trgt in TARGETS: + for unity_test_path in unity_test_paths: + prepare_unity_test_for_build(waf_conf, project_root_path, + unity_test_path, + unity_runner_dir, unity_runner_suffix, trgt) + + +# TODO: can the xwaf boilerplate help here? +def create_waf_contexts(configs): + for trgt in TARGETS: + for test_name, test_language in configs.items(): + print(f"test_name {test_name}, test_language {test_language}") + for ctx in (BuildContext, CleanContext): + raw_context = ctx.__name__.replace('Context', '').lower() + + class tmp(ctx): + cmd = raw_context + '_' + test_name + '_' + trgt + variant = test_name + '_' + trgt + #cmd = raw_context + '_' + test_name + #variant = test_name + language = test_language + target = trgt + runner = test_name + print(f"cmd {cmd}, variant {variant}, language {language}") + + +UNITY_TEST_DIR = 'src' +UNITY_TEST_PREFIX = 'test_' +UNITY_RUNNER_DIR = 'runners' +UNITY_RUNNER_SUFFIX = '_Runner' +UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX) + +create_waf_contexts(UNITY_TESTS) + +def options(opt): + opt.add_option('--target', action='store', default='xcore200') + opt.load('xwaf.xcommon') + +def configure(conf): + # TODO: move the call to generate_all_unity_runners() to build() + project_root = os.path.join('..', '..', '..') + generate_all_unity_runners(conf, project_root, + UNITY_TEST_DIR, UNITY_TEST_PREFIX, + UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX) + conf.load('xwaf.xcommon') + + +def build(bld): + if not bld.variant: + print('Adding test runners to build queue') + trgt = [ + c for c in TARGETS if c == bld.options.target + ] + + if len(trgt) == 0: + bld.fatal('specify a target with --target.\nAvailable targets: {}'.format(', '.join(TARGETS))) + return + + for name in UNITY_TESTS: + Options.commands.insert(0, 'build_' + name + '_' + trgt[0]) + #Options.commands.insert(0, 'build_' + name) + print('Build queue {}'.format(Options.commands)) + else: + print('Building runner {}'.format(bld.runner)) + bld.env.XSCOPE = bld.path.find_resource('config.xscope') + + depends_on = ['lib_xua', + 'lib_xud', + 'lib_spdif', + 'lib_mic_array', + 'lib_logging', + 'lib_xassert', + 'Unity'] + + makefile_opts = {} + makefile_opts['SOURCE_DIRS'] = ['src', os.path.join('runners',bld.runner)] + if(bld.target == 'xcoreai'): + print('TARGET XCOREAI') + makefile_opts['TARGET'] = ['XCORE-AI-EXPLORER'] + else: + print('TARGET XCORE200') + makefile_opts['TARGET'] = ['XCORE-200-EXPLORER'] + + makefile_opts['INCLUDE_DIRS'] = ['src', + '../../lib_xua/api', + '../../lib_xua/src/core/pdm_mics', + '../../lib_xua/src/hid', + '../../../lib_xud/lib_xud/src/user/class'] + + makefile_opts['XCC_FLAGS'] = ['-O2', + '-g', + '-Wall', + '-DUNITY_SUPPORT_64', + '-DUNITY_INCLUDE_DOUBLE', + '-DXUD_CORE_CLOCK=600', + '-DXUD_SERIES_SUPPORT=4'] + + makefile_opts['APP_NAME'] = [bld.variant] + makefile_opts['USED_MODULES'] = depends_on + makefile_opts['XCOMMON_MAKEFILE'] = ['Makefile.common'] + bld.do_xcommon(makefile_opts) + + +def test(bld): + # Call pytest to run Unity tests inside axe or xsim + try: + test_output = subprocess.check_output(['pytest']) + except subprocess.CalledProcessError as e: + # pytest exits non-zero if an assertion fails + test_output = e.output + print(test_output) + + +# TODO: ensure clean deletes the runners dir/ +def dist(ctx): + ctx.load('xwaf.xcommon') + +def distcheck(ctx): + ctx.load('xwaf.xcommon')