From 960fb37d6bc888596955724e608361f2b361a22c Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Sat, 15 Nov 2025 09:09:50 +0100 Subject: [PATCH 01/67] Update teensy core --- .../audio_board/vendor/cores/teensy/FS.h | 2 +- .../audio_board/vendor/cores/teensy3/FS.h | 2 +- .../vendor/cores/teensy3/core_cm4.h | 3514 ++++++++--------- .../vendor/cores/teensy3/math_helper.c | 878 ++-- .../vendor/cores/teensy4/AudioStream.h | 2 +- .../vendor/cores/teensy4/CrashReport.cpp | 40 +- .../vendor/cores/teensy4/CrashReport.h | 30 + .../vendor/cores/teensy4/DMAChannel.cpp | 2 + .../vendor/cores/teensy4/DMAChannel.h | 3 +- .../audio_board/vendor/cores/teensy4/FS.h | 2 +- .../vendor/cores/teensy4/HardwareSerial1.cpp | 1 - .../vendor/cores/teensy4/MIDIUSB.h | 30 + .../vendor/cores/teensy4/MTP_Const.h | 159 + .../vendor/cores/teensy4/MTP_Storage.cpp | 1930 +++++++++ .../vendor/cores/teensy4/MTP_Storage.h | 306 ++ .../vendor/cores/teensy4/MTP_Teensy.cpp | 2557 ++++++++++++ .../vendor/cores/teensy4/MTP_Teensy.h | 317 ++ .../vendor/cores/teensy4/WProgram.h | 2 +- .../audio_board/vendor/cores/teensy4/analog.c | 30 + .../vendor/cores/teensy4/bootdata.c | 30 + .../vendor/cores/teensy4/clockspeed.c | 30 + .../vendor/cores/teensy4/debugprintf.c | 30 + .../audio_board/vendor/cores/teensy4/delay.c | 30 + .../vendor/cores/teensy4/digital.c | 29 + .../audio_board/vendor/cores/teensy4/extmem.c | 30 + .../audio_board/vendor/cores/teensy4/fuse.c | 30 + .../audio_board/vendor/cores/teensy4/imxrt.h | 33 + .../vendor/cores/teensy4/imxrt1062_t41.ld | 2 +- .../vendor/cores/teensy4/interrupt.c | 30 + .../vendor/cores/teensy4/keylayouts.h | 142 + .../vendor/cores/teensy4/pgmspace.h | 2 + .../audio_board/vendor/cores/teensy4/pwm.c | 30 + .../vendor/cores/teensy4/startup.c | 181 +- .../vendor/cores/teensy4/tempmon.c | 30 + .../audio_board/vendor/cores/teensy4/usb.c | 30 + .../vendor/cores/teensy4/usb_desc.c | 13 + .../vendor/cores/teensy4/usb_desc.h | 12 +- .../vendor/cores/teensy4/usb_dev.h | 30 + .../vendor/cores/teensy4/usb_keyboard.c | 64 +- .../vendor/cores/teensy4/usb_keyboard.h | 6 + .../vendor/cores/teensy4/wiring_private.h | 2 + 41 files changed, 8361 insertions(+), 2262 deletions(-) create mode 100644 hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Const.h create mode 100644 hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Storage.cpp create mode 100644 hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Storage.h create mode 100644 hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Teensy.cpp create mode 100644 hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Teensy.h diff --git a/hardware/firmware/audio_board/vendor/cores/teensy/FS.h b/hardware/firmware/audio_board/vendor/cores/teensy/FS.h index 5cc3ea3d..5519bb87 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy/FS.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy/FS.h @@ -281,7 +281,7 @@ class FS return true; } virtual const char * name() { - return true; + return ""; } // for compatibility with String input File open(const String &filepath, uint8_t mode = FILE_READ) { diff --git a/hardware/firmware/audio_board/vendor/cores/teensy3/FS.h b/hardware/firmware/audio_board/vendor/cores/teensy3/FS.h index dabc8c89..b31d0020 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy3/FS.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy3/FS.h @@ -270,7 +270,7 @@ class File final : public Stream { class FS { public: - FS() {} + constexpr FS() {} virtual ~FS() {} virtual File open(const char *filename, uint8_t mode = FILE_READ) = 0; virtual bool exists(const char *filepath) = 0; diff --git a/hardware/firmware/audio_board/vendor/cores/teensy3/core_cm4.h b/hardware/firmware/audio_board/vendor/cores/teensy3/core_cm4.h index 2a67fa2a..32ff390b 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy3/core_cm4.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy3/core_cm4.h @@ -1,1757 +1,1757 @@ -/**************************************************************************//** - * @file core_cm4.h - * @brief CMSIS Cortex-M4 Core Peripheral Access Layer Header File - * @version V3.01 - * @date 22. March 2012 - * - * @note - * Copyright (C) 2009-2012 ARM Limited. All rights reserved. - * - * @par - * ARM Limited (ARM) is supplying this software for use with Cortex-M - * processor based microcontrollers. This file can be freely distributed - * within development tools that are supporting such ARM based processors. - * - * @par - * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED - * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. - * ARM SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR - * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. - * - ******************************************************************************/ -#if defined ( __ICCARM__ ) - #pragma system_include /* treat file as system include file for MISRA check */ -#endif - -#ifdef __cplusplus - extern "C" { -#endif - -#ifndef __CORE_CM4_H_GENERIC -#define __CORE_CM4_H_GENERIC - -/** \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions - CMSIS violates the following MISRA-C:2004 rules: - - \li Required Rule 8.5, object/function definition in header file.
- Function definitions in header files are used to allow 'inlining'. - - \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
- Unions are used for effective representation of core registers. - - \li Advisory Rule 19.7, Function-like macro defined.
- Function-like macros are used to allow more efficient code. - */ - - -/******************************************************************************* - * CMSIS definitions - ******************************************************************************/ -/** \ingroup Cortex_M4 - @{ - */ - -/* CMSIS CM4 definitions */ -#define __CM4_CMSIS_VERSION_MAIN (0x03) /*!< [31:16] CMSIS HAL main version */ -#define __CM4_CMSIS_VERSION_SUB (0x01) /*!< [15:0] CMSIS HAL sub version */ -#define __CM4_CMSIS_VERSION ((__CM4_CMSIS_VERSION_MAIN << 16) | \ - __CM4_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ - -#define __CORTEX_M (0x04) /*!< Cortex-M Core */ - - -#if defined ( __CC_ARM ) - #define __ASM __asm /*!< asm keyword for ARM Compiler */ - #define __INLINE __inline /*!< inline keyword for ARM Compiler */ - #define __STATIC_INLINE static __inline - -#elif defined ( __ICCARM__ ) - #define __ASM __asm /*!< asm keyword for IAR Compiler */ - #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ - #define __STATIC_INLINE static inline - -#elif defined ( __TMS470__ ) - #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __GNUC__ ) - #define __ASM __asm /*!< asm keyword for GNU Compiler */ - #define __INLINE inline /*!< inline keyword for GNU Compiler */ - #define __STATIC_INLINE static inline - -#elif defined ( __TASKING__ ) - #define __ASM __asm /*!< asm keyword for TASKING Compiler */ - #define __INLINE inline /*!< inline keyword for TASKING Compiler */ - #define __STATIC_INLINE static inline - -#endif - -/** __FPU_USED indicates whether an FPU is used or not. For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. -*/ -#if defined ( __CC_ARM ) - #if defined __TARGET_FPU_VFP - #if (__FPU_PRESENT == 1) - #define __FPU_USED 1 - #else - #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0 - #endif - #else - #define __FPU_USED 0 - #endif - -#elif defined ( __ICCARM__ ) - #if defined __ARMVFP__ - #if (__FPU_PRESENT == 1) - #define __FPU_USED 1 - #else - #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0 - #endif - #else - #define __FPU_USED 0 - #endif - -#elif defined ( __TMS470__ ) - #if defined __TI_VFP_SUPPORT__ - #if (__FPU_PRESENT == 1) - #define __FPU_USED 1 - #else - #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0 - #endif - #else - #define __FPU_USED 0 - #endif - -#elif defined ( __GNUC__ ) - #if defined (__VFP_FP__) && !defined(__SOFTFP__) - #if (__FPU_PRESENT == 1) - #define __FPU_USED 1 - #else - #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0 - #endif - #else - #define __FPU_USED 0 - #endif - -#elif defined ( __TASKING__ ) - #if defined __FPU_VFP__ - #if (__FPU_PRESENT == 1) - #define __FPU_USED 1 - #else - #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" - #define __FPU_USED 0 - #endif - #else - #define __FPU_USED 0 - #endif -#endif - -#include /* standard types definitions */ -#include /* Core Instruction Access */ -#include /* Core Function Access */ -#include /* Compiler specific SIMD Intrinsics */ - -#endif /* __CORE_CM4_H_GENERIC */ - -#ifndef __CMSIS_GENERIC - -#ifndef __CORE_CM4_H_DEPENDANT -#define __CORE_CM4_H_DEPENDANT - -/* check device defines and use defaults */ -#if defined __CHECK_DEVICE_DEFINES - #ifndef __CM4_REV - #define __CM4_REV 0x0000 - #warning "__CM4_REV not defined in device header file; using default!" - #endif - - #ifndef __FPU_PRESENT - #define __FPU_PRESENT 0 - #warning "__FPU_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __MPU_PRESENT - #define __MPU_PRESENT 0 - #warning "__MPU_PRESENT not defined in device header file; using default!" - #endif - - #ifndef __NVIC_PRIO_BITS - #define __NVIC_PRIO_BITS 4 - #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" - #endif - - #ifndef __Vendor_SysTickConfig - #define __Vendor_SysTickConfig 0 - #warning "__Vendor_SysTickConfig not defined in device header file; using default!" - #endif -#endif - -/* IO definitions (access restrictions to peripheral registers) */ -/** - \defgroup CMSIS_glob_defs CMSIS Global Defines - - IO Type Qualifiers are used - \li to specify the access to peripheral variables. - \li for automatic generation of peripheral register debug information. -*/ -#ifdef __cplusplus - #define __I volatile /*!< Defines 'read only' permissions */ -#else - #define __I volatile const /*!< Defines 'read only' permissions */ -#endif -#define __O volatile /*!< Defines 'write only' permissions */ -#define __IO volatile /*!< Defines 'read / write' permissions */ - -/*@} end of group Cortex_M4 */ - - - -/******************************************************************************* - * Register Abstraction - Core Register contain: - - Core Register - - Core NVIC Register - - Core SCB Register - - Core SysTick Register - - Core Debug Register - - Core MPU Register - - Core FPU Register - ******************************************************************************/ -/** \defgroup CMSIS_core_register Defines and Type Definitions - \brief Type definitions and defines for Cortex-M processor based devices. -*/ - -/** \ingroup CMSIS_core_register - \defgroup CMSIS_CORE Status and Control Registers - \brief Core Register type definitions. - @{ - */ - -/** \brief Union type to access the Application Program Status Register (APSR). - */ -typedef union -{ - struct - { -#if (__CORTEX_M != 0x04) - uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ -#else - uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ - uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ - uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ -#endif - uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} APSR_Type; - - -/** \brief Union type to access the Interrupt Program Status Register (IPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ - uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} IPSR_Type; - - -/** \brief Union type to access the Special-Purpose Program Status Registers (xPSR). - */ -typedef union -{ - struct - { - uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ -#if (__CORTEX_M != 0x04) - uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ -#else - uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ - uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ - uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ -#endif - uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ - uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ - uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ - uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ - uint32_t C:1; /*!< bit: 29 Carry condition code flag */ - uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ - uint32_t N:1; /*!< bit: 31 Negative condition code flag */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} xPSR_Type; - - -/** \brief Union type to access the Control Registers (CONTROL). - */ -typedef union -{ - struct - { - uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ - uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ - uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ - uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ - } b; /*!< Structure used for bit access */ - uint32_t w; /*!< Type used for word access */ -} CONTROL_Type; - -/*@} end of group CMSIS_CORE */ - - -/** \ingroup CMSIS_core_register - \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) - \brief Type definitions for the NVIC Registers - @{ - */ - -/** \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). - */ -typedef struct -{ - __IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ - uint32_t RESERVED0[24]; - __IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ - uint32_t RSERVED1[24]; - __IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ - uint32_t RESERVED2[24]; - __IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ - uint32_t RESERVED3[24]; - __IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ - uint32_t RESERVED4[56]; - __IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ - uint32_t RESERVED5[644]; - __O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ -} NVIC_Type; - -/* Software Triggered Interrupt Register Definitions */ -#define NVIC_STIR_INTID_Pos 0 /*!< STIR: INTLINESNUM Position */ -#define NVIC_STIR_INTID_Msk (0x1FFUL << NVIC_STIR_INTID_Pos) /*!< STIR: INTLINESNUM Mask */ - -/*@} end of group CMSIS_NVIC */ - - -/** \ingroup CMSIS_core_register - \defgroup CMSIS_SCB System Control Block (SCB) - \brief Type definitions for the System Control Block Registers - @{ - */ - -/** \brief Structure type to access the System Control Block (SCB). - */ -typedef struct -{ - __I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ - __IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ - __IO uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ - __IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ - __IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ - __IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ - __IO uint8_t SHP[12]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ - __IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ - __IO uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ - __IO uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ - __IO uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ - __IO uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ - __IO uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ - __IO uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ - __I uint32_t PFR[2]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ - __I uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ - __I uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ - __I uint32_t MMFR[4]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ - __I uint32_t ISAR[5]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ - uint32_t RESERVED0[5]; - __IO uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ -} SCB_Type; - -/* SCB CPUID Register Definitions */ -#define SCB_CPUID_IMPLEMENTER_Pos 24 /*!< SCB CPUID: IMPLEMENTER Position */ -#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ - -#define SCB_CPUID_VARIANT_Pos 20 /*!< SCB CPUID: VARIANT Position */ -#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ - -#define SCB_CPUID_ARCHITECTURE_Pos 16 /*!< SCB CPUID: ARCHITECTURE Position */ -#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ - -#define SCB_CPUID_PARTNO_Pos 4 /*!< SCB CPUID: PARTNO Position */ -#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ - -#define SCB_CPUID_REVISION_Pos 0 /*!< SCB CPUID: REVISION Position */ -#define SCB_CPUID_REVISION_Msk (0xFUL << SCB_CPUID_REVISION_Pos) /*!< SCB CPUID: REVISION Mask */ - -/* SCB Interrupt Control State Register Definitions */ -#define SCB_ICSR_NMIPENDSET_Pos 31 /*!< SCB ICSR: NMIPENDSET Position */ -#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ - -#define SCB_ICSR_PENDSVSET_Pos 28 /*!< SCB ICSR: PENDSVSET Position */ -#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ - -#define SCB_ICSR_PENDSVCLR_Pos 27 /*!< SCB ICSR: PENDSVCLR Position */ -#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ - -#define SCB_ICSR_PENDSTSET_Pos 26 /*!< SCB ICSR: PENDSTSET Position */ -#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ - -#define SCB_ICSR_PENDSTCLR_Pos 25 /*!< SCB ICSR: PENDSTCLR Position */ -#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ - -#define SCB_ICSR_ISRPREEMPT_Pos 23 /*!< SCB ICSR: ISRPREEMPT Position */ -#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ - -#define SCB_ICSR_ISRPENDING_Pos 22 /*!< SCB ICSR: ISRPENDING Position */ -#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ - -#define SCB_ICSR_VECTPENDING_Pos 12 /*!< SCB ICSR: VECTPENDING Position */ -#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ - -#define SCB_ICSR_RETTOBASE_Pos 11 /*!< SCB ICSR: RETTOBASE Position */ -#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ - -#define SCB_ICSR_VECTACTIVE_Pos 0 /*!< SCB ICSR: VECTACTIVE Position */ -#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL << SCB_ICSR_VECTACTIVE_Pos) /*!< SCB ICSR: VECTACTIVE Mask */ - -/* SCB Vector Table Offset Register Definitions */ -#define SCB_VTOR_TBLOFF_Pos 7 /*!< SCB VTOR: TBLOFF Position */ -#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ - -/* SCB Application Interrupt and Reset Control Register Definitions */ -#define SCB_AIRCR_VECTKEY_Pos 16 /*!< SCB AIRCR: VECTKEY Position */ -#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ - -#define SCB_AIRCR_VECTKEYSTAT_Pos 16 /*!< SCB AIRCR: VECTKEYSTAT Position */ -#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ - -#define SCB_AIRCR_ENDIANESS_Pos 15 /*!< SCB AIRCR: ENDIANESS Position */ -#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ - -#define SCB_AIRCR_PRIGROUP_Pos 8 /*!< SCB AIRCR: PRIGROUP Position */ -#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ - -#define SCB_AIRCR_SYSRESETREQ_Pos 2 /*!< SCB AIRCR: SYSRESETREQ Position */ -#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ - -#define SCB_AIRCR_VECTCLRACTIVE_Pos 1 /*!< SCB AIRCR: VECTCLRACTIVE Position */ -#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ - -#define SCB_AIRCR_VECTRESET_Pos 0 /*!< SCB AIRCR: VECTRESET Position */ -#define SCB_AIRCR_VECTRESET_Msk (1UL << SCB_AIRCR_VECTRESET_Pos) /*!< SCB AIRCR: VECTRESET Mask */ - -/* SCB System Control Register Definitions */ -#define SCB_SCR_SEVONPEND_Pos 4 /*!< SCB SCR: SEVONPEND Position */ -#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ - -#define SCB_SCR_SLEEPDEEP_Pos 2 /*!< SCB SCR: SLEEPDEEP Position */ -#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ - -#define SCB_SCR_SLEEPONEXIT_Pos 1 /*!< SCB SCR: SLEEPONEXIT Position */ -#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ - -/* SCB Configuration Control Register Definitions */ -#define SCB_CCR_STKALIGN_Pos 9 /*!< SCB CCR: STKALIGN Position */ -#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ - -#define SCB_CCR_BFHFNMIGN_Pos 8 /*!< SCB CCR: BFHFNMIGN Position */ -#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ - -#define SCB_CCR_DIV_0_TRP_Pos 4 /*!< SCB CCR: DIV_0_TRP Position */ -#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ - -#define SCB_CCR_UNALIGN_TRP_Pos 3 /*!< SCB CCR: UNALIGN_TRP Position */ -#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ - -#define SCB_CCR_USERSETMPEND_Pos 1 /*!< SCB CCR: USERSETMPEND Position */ -#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ - -#define SCB_CCR_NONBASETHRDENA_Pos 0 /*!< SCB CCR: NONBASETHRDENA Position */ -#define SCB_CCR_NONBASETHRDENA_Msk (1UL << SCB_CCR_NONBASETHRDENA_Pos) /*!< SCB CCR: NONBASETHRDENA Mask */ - -/* SCB System Handler Control and State Register Definitions */ -#define SCB_SHCSR_USGFAULTENA_Pos 18 /*!< SCB SHCSR: USGFAULTENA Position */ -#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ - -#define SCB_SHCSR_BUSFAULTENA_Pos 17 /*!< SCB SHCSR: BUSFAULTENA Position */ -#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ - -#define SCB_SHCSR_MEMFAULTENA_Pos 16 /*!< SCB SHCSR: MEMFAULTENA Position */ -#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ - -#define SCB_SHCSR_SVCALLPENDED_Pos 15 /*!< SCB SHCSR: SVCALLPENDED Position */ -#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ - -#define SCB_SHCSR_BUSFAULTPENDED_Pos 14 /*!< SCB SHCSR: BUSFAULTPENDED Position */ -#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ - -#define SCB_SHCSR_MEMFAULTPENDED_Pos 13 /*!< SCB SHCSR: MEMFAULTPENDED Position */ -#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ - -#define SCB_SHCSR_USGFAULTPENDED_Pos 12 /*!< SCB SHCSR: USGFAULTPENDED Position */ -#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ - -#define SCB_SHCSR_SYSTICKACT_Pos 11 /*!< SCB SHCSR: SYSTICKACT Position */ -#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ - -#define SCB_SHCSR_PENDSVACT_Pos 10 /*!< SCB SHCSR: PENDSVACT Position */ -#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ - -#define SCB_SHCSR_MONITORACT_Pos 8 /*!< SCB SHCSR: MONITORACT Position */ -#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ - -#define SCB_SHCSR_SVCALLACT_Pos 7 /*!< SCB SHCSR: SVCALLACT Position */ -#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ - -#define SCB_SHCSR_USGFAULTACT_Pos 3 /*!< SCB SHCSR: USGFAULTACT Position */ -#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ - -#define SCB_SHCSR_BUSFAULTACT_Pos 1 /*!< SCB SHCSR: BUSFAULTACT Position */ -#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ - -#define SCB_SHCSR_MEMFAULTACT_Pos 0 /*!< SCB SHCSR: MEMFAULTACT Position */ -#define SCB_SHCSR_MEMFAULTACT_Msk (1UL << SCB_SHCSR_MEMFAULTACT_Pos) /*!< SCB SHCSR: MEMFAULTACT Mask */ - -/* SCB Configurable Fault Status Registers Definitions */ -#define SCB_CFSR_USGFAULTSR_Pos 16 /*!< SCB CFSR: Usage Fault Status Register Position */ -#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ - -#define SCB_CFSR_BUSFAULTSR_Pos 8 /*!< SCB CFSR: Bus Fault Status Register Position */ -#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ - -#define SCB_CFSR_MEMFAULTSR_Pos 0 /*!< SCB CFSR: Memory Manage Fault Status Register Position */ -#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL << SCB_CFSR_MEMFAULTSR_Pos) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ - -/* SCB Hard Fault Status Registers Definitions */ -#define SCB_HFSR_DEBUGEVT_Pos 31 /*!< SCB HFSR: DEBUGEVT Position */ -#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ - -#define SCB_HFSR_FORCED_Pos 30 /*!< SCB HFSR: FORCED Position */ -#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ - -#define SCB_HFSR_VECTTBL_Pos 1 /*!< SCB HFSR: VECTTBL Position */ -#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ - -/* SCB Debug Fault Status Register Definitions */ -#define SCB_DFSR_EXTERNAL_Pos 4 /*!< SCB DFSR: EXTERNAL Position */ -#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ - -#define SCB_DFSR_VCATCH_Pos 3 /*!< SCB DFSR: VCATCH Position */ -#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ - -#define SCB_DFSR_DWTTRAP_Pos 2 /*!< SCB DFSR: DWTTRAP Position */ -#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ - -#define SCB_DFSR_BKPT_Pos 1 /*!< SCB DFSR: BKPT Position */ -#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ - -#define SCB_DFSR_HALTED_Pos 0 /*!< SCB DFSR: HALTED Position */ -#define SCB_DFSR_HALTED_Msk (1UL << SCB_DFSR_HALTED_Pos) /*!< SCB DFSR: HALTED Mask */ - -/*@} end of group CMSIS_SCB */ - - -/** \ingroup CMSIS_core_register - \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) - \brief Type definitions for the System Control and ID Register not in the SCB - @{ - */ - -/** \brief Structure type to access the System Control and ID Register not in the SCB. - */ -typedef struct -{ - uint32_t RESERVED0[1]; - __I uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ - __IO uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ -} SCnSCB_Type; - -/* Interrupt Controller Type Register Definitions */ -#define SCnSCB_ICTR_INTLINESNUM_Pos 0 /*!< ICTR: INTLINESNUM Position */ -#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL << SCnSCB_ICTR_INTLINESNUM_Pos) /*!< ICTR: INTLINESNUM Mask */ - -/* Auxiliary Control Register Definitions */ -#define SCnSCB_ACTLR_DISOOFP_Pos 9 /*!< ACTLR: DISOOFP Position */ -#define SCnSCB_ACTLR_DISOOFP_Msk (1UL << SCnSCB_ACTLR_DISOOFP_Pos) /*!< ACTLR: DISOOFP Mask */ - -#define SCnSCB_ACTLR_DISFPCA_Pos 8 /*!< ACTLR: DISFPCA Position */ -#define SCnSCB_ACTLR_DISFPCA_Msk (1UL << SCnSCB_ACTLR_DISFPCA_Pos) /*!< ACTLR: DISFPCA Mask */ - -#define SCnSCB_ACTLR_DISFOLD_Pos 2 /*!< ACTLR: DISFOLD Position */ -#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ - -#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1 /*!< ACTLR: DISDEFWBUF Position */ -#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ - -#define SCnSCB_ACTLR_DISMCYCINT_Pos 0 /*!< ACTLR: DISMCYCINT Position */ -#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL << SCnSCB_ACTLR_DISMCYCINT_Pos) /*!< ACTLR: DISMCYCINT Mask */ - -/*@} end of group CMSIS_SCnotSCB */ - - -/** \ingroup CMSIS_core_register - \defgroup CMSIS_SysTick System Tick Timer (SysTick) - \brief Type definitions for the System Timer Registers. - @{ - */ - -/** \brief Structure type to access the System Timer (SysTick). - */ -typedef struct -{ - __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ - __IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ - __IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ - __I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ -} SysTick_Type; - -/* SysTick Control / Status Register Definitions */ -#define SysTick_CTRL_COUNTFLAG_Pos 16 /*!< SysTick CTRL: COUNTFLAG Position */ -#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ - -#define SysTick_CTRL_CLKSOURCE_Pos 2 /*!< SysTick CTRL: CLKSOURCE Position */ -#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ - -#define SysTick_CTRL_TICKINT_Pos 1 /*!< SysTick CTRL: TICKINT Position */ -#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ - -#define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */ -#define SysTick_CTRL_ENABLE_Msk (1UL << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */ - -/* SysTick Reload Register Definitions */ -#define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */ -#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */ - -/* SysTick Current Register Definitions */ -#define SysTick_VAL_CURRENT_Pos 0 /*!< SysTick VAL: CURRENT Position */ -#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick VAL: CURRENT Mask */ - -/* SysTick Calibration Register Definitions */ -#define SysTick_CALIB_NOREF_Pos 31 /*!< SysTick CALIB: NOREF Position */ -#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ - -#define SysTick_CALIB_SKEW_Pos 30 /*!< SysTick CALIB: SKEW Position */ -#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ - -#define SysTick_CALIB_TENMS_Pos 0 /*!< SysTick CALIB: TENMS Position */ -#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick CALIB: TENMS Mask */ - -/*@} end of group CMSIS_SysTick */ - - -/** \ingroup CMSIS_core_register - \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) - \brief Type definitions for the Instrumentation Trace Macrocell (ITM) - @{ - */ - -/** \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). - */ -typedef struct -{ - __O union - { - __O uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ - __O uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ - __O uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ - } PORT [32]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ - uint32_t RESERVED0[864]; - __IO uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ - uint32_t RESERVED1[15]; - __IO uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ - uint32_t RESERVED2[15]; - __IO uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ - uint32_t RESERVED3[29]; - __O uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ - __I uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ - __IO uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ - uint32_t RESERVED4[43]; - __O uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ - __I uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ - uint32_t RESERVED5[6]; - __I uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ - __I uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ - __I uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ - __I uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ - __I uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ - __I uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ - __I uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ - __I uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ - __I uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ - __I uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ - __I uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ - __I uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ -} ITM_Type; - -/* ITM Trace Privilege Register Definitions */ -#define ITM_TPR_PRIVMASK_Pos 0 /*!< ITM TPR: PRIVMASK Position */ -#define ITM_TPR_PRIVMASK_Msk (0xFUL << ITM_TPR_PRIVMASK_Pos) /*!< ITM TPR: PRIVMASK Mask */ - -/* ITM Trace Control Register Definitions */ -#define ITM_TCR_BUSY_Pos 23 /*!< ITM TCR: BUSY Position */ -#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ - -#define ITM_TCR_TraceBusID_Pos 16 /*!< ITM TCR: ATBID Position */ -#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ - -#define ITM_TCR_GTSFREQ_Pos 10 /*!< ITM TCR: Global timestamp frequency Position */ -#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ - -#define ITM_TCR_TSPrescale_Pos 8 /*!< ITM TCR: TSPrescale Position */ -#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ - -#define ITM_TCR_SWOENA_Pos 4 /*!< ITM TCR: SWOENA Position */ -#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ - -#define ITM_TCR_DWTENA_Pos 3 /*!< ITM TCR: DWTENA Position */ -#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ - -#define ITM_TCR_SYNCENA_Pos 2 /*!< ITM TCR: SYNCENA Position */ -#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ - -#define ITM_TCR_TSENA_Pos 1 /*!< ITM TCR: TSENA Position */ -#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ - -#define ITM_TCR_ITMENA_Pos 0 /*!< ITM TCR: ITM Enable bit Position */ -#define ITM_TCR_ITMENA_Msk (1UL << ITM_TCR_ITMENA_Pos) /*!< ITM TCR: ITM Enable bit Mask */ - -/* ITM Integration Write Register Definitions */ -#define ITM_IWR_ATVALIDM_Pos 0 /*!< ITM IWR: ATVALIDM Position */ -#define ITM_IWR_ATVALIDM_Msk (1UL << ITM_IWR_ATVALIDM_Pos) /*!< ITM IWR: ATVALIDM Mask */ - -/* ITM Integration Read Register Definitions */ -#define ITM_IRR_ATREADYM_Pos 0 /*!< ITM IRR: ATREADYM Position */ -#define ITM_IRR_ATREADYM_Msk (1UL << ITM_IRR_ATREADYM_Pos) /*!< ITM IRR: ATREADYM Mask */ - -/* ITM Integration Mode Control Register Definitions */ -#define ITM_IMCR_INTEGRATION_Pos 0 /*!< ITM IMCR: INTEGRATION Position */ -#define ITM_IMCR_INTEGRATION_Msk (1UL << ITM_IMCR_INTEGRATION_Pos) /*!< ITM IMCR: INTEGRATION Mask */ - -/* ITM Lock Status Register Definitions */ -#define ITM_LSR_ByteAcc_Pos 2 /*!< ITM LSR: ByteAcc Position */ -#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ - -#define ITM_LSR_Access_Pos 1 /*!< ITM LSR: Access Position */ -#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ - -#define ITM_LSR_Present_Pos 0 /*!< ITM LSR: Present Position */ -#define ITM_LSR_Present_Msk (1UL << ITM_LSR_Present_Pos) /*!< ITM LSR: Present Mask */ - -/*@}*/ /* end of group CMSIS_ITM */ - - -/** \ingroup CMSIS_core_register - \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) - \brief Type definitions for the Data Watchpoint and Trace (DWT) - @{ - */ - -/** \brief Structure type to access the Data Watchpoint and Trace Register (DWT). - */ -typedef struct -{ - __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ - __IO uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ - __IO uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ - __IO uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ - __IO uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ - __IO uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ - __IO uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ - __I uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ - __IO uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ - __IO uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ - __IO uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ - uint32_t RESERVED0[1]; - __IO uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ - __IO uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ - __IO uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ - uint32_t RESERVED1[1]; - __IO uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ - __IO uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ - __IO uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ - uint32_t RESERVED2[1]; - __IO uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ - __IO uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ - __IO uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ -} DWT_Type; - -/* DWT Control Register Definitions */ -#define DWT_CTRL_NUMCOMP_Pos 28 /*!< DWT CTRL: NUMCOMP Position */ -#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ - -#define DWT_CTRL_NOTRCPKT_Pos 27 /*!< DWT CTRL: NOTRCPKT Position */ -#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ - -#define DWT_CTRL_NOEXTTRIG_Pos 26 /*!< DWT CTRL: NOEXTTRIG Position */ -#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ - -#define DWT_CTRL_NOCYCCNT_Pos 25 /*!< DWT CTRL: NOCYCCNT Position */ -#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ - -#define DWT_CTRL_NOPRFCNT_Pos 24 /*!< DWT CTRL: NOPRFCNT Position */ -#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ - -#define DWT_CTRL_CYCEVTENA_Pos 22 /*!< DWT CTRL: CYCEVTENA Position */ -#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ - -#define DWT_CTRL_FOLDEVTENA_Pos 21 /*!< DWT CTRL: FOLDEVTENA Position */ -#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ - -#define DWT_CTRL_LSUEVTENA_Pos 20 /*!< DWT CTRL: LSUEVTENA Position */ -#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ - -#define DWT_CTRL_SLEEPEVTENA_Pos 19 /*!< DWT CTRL: SLEEPEVTENA Position */ -#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ - -#define DWT_CTRL_EXCEVTENA_Pos 18 /*!< DWT CTRL: EXCEVTENA Position */ -#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ - -#define DWT_CTRL_CPIEVTENA_Pos 17 /*!< DWT CTRL: CPIEVTENA Position */ -#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ - -#define DWT_CTRL_EXCTRCENA_Pos 16 /*!< DWT CTRL: EXCTRCENA Position */ -#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ - -#define DWT_CTRL_PCSAMPLENA_Pos 12 /*!< DWT CTRL: PCSAMPLENA Position */ -#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ - -#define DWT_CTRL_SYNCTAP_Pos 10 /*!< DWT CTRL: SYNCTAP Position */ -#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ - -#define DWT_CTRL_CYCTAP_Pos 9 /*!< DWT CTRL: CYCTAP Position */ -#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ - -#define DWT_CTRL_POSTINIT_Pos 5 /*!< DWT CTRL: POSTINIT Position */ -#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ - -#define DWT_CTRL_POSTPRESET_Pos 1 /*!< DWT CTRL: POSTPRESET Position */ -#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ - -#define DWT_CTRL_CYCCNTENA_Pos 0 /*!< DWT CTRL: CYCCNTENA Position */ -#define DWT_CTRL_CYCCNTENA_Msk (0x1UL << DWT_CTRL_CYCCNTENA_Pos) /*!< DWT CTRL: CYCCNTENA Mask */ - -/* DWT CPI Count Register Definitions */ -#define DWT_CPICNT_CPICNT_Pos 0 /*!< DWT CPICNT: CPICNT Position */ -#define DWT_CPICNT_CPICNT_Msk (0xFFUL << DWT_CPICNT_CPICNT_Pos) /*!< DWT CPICNT: CPICNT Mask */ - -/* DWT Exception Overhead Count Register Definitions */ -#define DWT_EXCCNT_EXCCNT_Pos 0 /*!< DWT EXCCNT: EXCCNT Position */ -#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL << DWT_EXCCNT_EXCCNT_Pos) /*!< DWT EXCCNT: EXCCNT Mask */ - -/* DWT Sleep Count Register Definitions */ -#define DWT_SLEEPCNT_SLEEPCNT_Pos 0 /*!< DWT SLEEPCNT: SLEEPCNT Position */ -#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL << DWT_SLEEPCNT_SLEEPCNT_Pos) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ - -/* DWT LSU Count Register Definitions */ -#define DWT_LSUCNT_LSUCNT_Pos 0 /*!< DWT LSUCNT: LSUCNT Position */ -#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL << DWT_LSUCNT_LSUCNT_Pos) /*!< DWT LSUCNT: LSUCNT Mask */ - -/* DWT Folded-instruction Count Register Definitions */ -#define DWT_FOLDCNT_FOLDCNT_Pos 0 /*!< DWT FOLDCNT: FOLDCNT Position */ -#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL << DWT_FOLDCNT_FOLDCNT_Pos) /*!< DWT FOLDCNT: FOLDCNT Mask */ - -/* DWT Comparator Mask Register Definitions */ -#define DWT_MASK_MASK_Pos 0 /*!< DWT MASK: MASK Position */ -#define DWT_MASK_MASK_Msk (0x1FUL << DWT_MASK_MASK_Pos) /*!< DWT MASK: MASK Mask */ - -/* DWT Comparator Function Register Definitions */ -#define DWT_FUNCTION_MATCHED_Pos 24 /*!< DWT FUNCTION: MATCHED Position */ -#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ - -#define DWT_FUNCTION_DATAVADDR1_Pos 16 /*!< DWT FUNCTION: DATAVADDR1 Position */ -#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ - -#define DWT_FUNCTION_DATAVADDR0_Pos 12 /*!< DWT FUNCTION: DATAVADDR0 Position */ -#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ - -#define DWT_FUNCTION_DATAVSIZE_Pos 10 /*!< DWT FUNCTION: DATAVSIZE Position */ -#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ - -#define DWT_FUNCTION_LNK1ENA_Pos 9 /*!< DWT FUNCTION: LNK1ENA Position */ -#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ - -#define DWT_FUNCTION_DATAVMATCH_Pos 8 /*!< DWT FUNCTION: DATAVMATCH Position */ -#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ - -#define DWT_FUNCTION_CYCMATCH_Pos 7 /*!< DWT FUNCTION: CYCMATCH Position */ -#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ - -#define DWT_FUNCTION_EMITRANGE_Pos 5 /*!< DWT FUNCTION: EMITRANGE Position */ -#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ - -#define DWT_FUNCTION_FUNCTION_Pos 0 /*!< DWT FUNCTION: FUNCTION Position */ -#define DWT_FUNCTION_FUNCTION_Msk (0xFUL << DWT_FUNCTION_FUNCTION_Pos) /*!< DWT FUNCTION: FUNCTION Mask */ - -/*@}*/ /* end of group CMSIS_DWT */ - - -/** \ingroup CMSIS_core_register - \defgroup CMSIS_TPI Trace Port Interface (TPI) - \brief Type definitions for the Trace Port Interface (TPI) - @{ - */ - -/** \brief Structure type to access the Trace Port Interface Register (TPI). - */ -typedef struct -{ - __IO uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ - __IO uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ - uint32_t RESERVED0[2]; - __IO uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ - uint32_t RESERVED1[55]; - __IO uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ - uint32_t RESERVED2[131]; - __I uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ - __IO uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ - __I uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ - uint32_t RESERVED3[759]; - __I uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ - __I uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ - __I uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ - uint32_t RESERVED4[1]; - __I uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ - __I uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ - __IO uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ - uint32_t RESERVED5[39]; - __IO uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ - __IO uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ - uint32_t RESERVED7[8]; - __I uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ - __I uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ -} TPI_Type; - -/* TPI Asynchronous Clock Prescaler Register Definitions */ -#define TPI_ACPR_PRESCALER_Pos 0 /*!< TPI ACPR: PRESCALER Position */ -#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL << TPI_ACPR_PRESCALER_Pos) /*!< TPI ACPR: PRESCALER Mask */ - -/* TPI Selected Pin Protocol Register Definitions */ -#define TPI_SPPR_TXMODE_Pos 0 /*!< TPI SPPR: TXMODE Position */ -#define TPI_SPPR_TXMODE_Msk (0x3UL << TPI_SPPR_TXMODE_Pos) /*!< TPI SPPR: TXMODE Mask */ - -/* TPI Formatter and Flush Status Register Definitions */ -#define TPI_FFSR_FtNonStop_Pos 3 /*!< TPI FFSR: FtNonStop Position */ -#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ - -#define TPI_FFSR_TCPresent_Pos 2 /*!< TPI FFSR: TCPresent Position */ -#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ - -#define TPI_FFSR_FtStopped_Pos 1 /*!< TPI FFSR: FtStopped Position */ -#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ - -#define TPI_FFSR_FlInProg_Pos 0 /*!< TPI FFSR: FlInProg Position */ -#define TPI_FFSR_FlInProg_Msk (0x1UL << TPI_FFSR_FlInProg_Pos) /*!< TPI FFSR: FlInProg Mask */ - -/* TPI Formatter and Flush Control Register Definitions */ -#define TPI_FFCR_TrigIn_Pos 8 /*!< TPI FFCR: TrigIn Position */ -#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ - -#define TPI_FFCR_EnFCont_Pos 1 /*!< TPI FFCR: EnFCont Position */ -#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ - -/* TPI TRIGGER Register Definitions */ -#define TPI_TRIGGER_TRIGGER_Pos 0 /*!< TPI TRIGGER: TRIGGER Position */ -#define TPI_TRIGGER_TRIGGER_Msk (0x1UL << TPI_TRIGGER_TRIGGER_Pos) /*!< TPI TRIGGER: TRIGGER Mask */ - -/* TPI Integration ETM Data Register Definitions (FIFO0) */ -#define TPI_FIFO0_ITM_ATVALID_Pos 29 /*!< TPI FIFO0: ITM_ATVALID Position */ -#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ - -#define TPI_FIFO0_ITM_bytecount_Pos 27 /*!< TPI FIFO0: ITM_bytecount Position */ -#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ - -#define TPI_FIFO0_ETM_ATVALID_Pos 26 /*!< TPI FIFO0: ETM_ATVALID Position */ -#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ - -#define TPI_FIFO0_ETM_bytecount_Pos 24 /*!< TPI FIFO0: ETM_bytecount Position */ -#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ - -#define TPI_FIFO0_ETM2_Pos 16 /*!< TPI FIFO0: ETM2 Position */ -#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ - -#define TPI_FIFO0_ETM1_Pos 8 /*!< TPI FIFO0: ETM1 Position */ -#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ - -#define TPI_FIFO0_ETM0_Pos 0 /*!< TPI FIFO0: ETM0 Position */ -#define TPI_FIFO0_ETM0_Msk (0xFFUL << TPI_FIFO0_ETM0_Pos) /*!< TPI FIFO0: ETM0 Mask */ - -/* TPI ITATBCTR2 Register Definitions */ -#define TPI_ITATBCTR2_ATREADY_Pos 0 /*!< TPI ITATBCTR2: ATREADY Position */ -#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL << TPI_ITATBCTR2_ATREADY_Pos) /*!< TPI ITATBCTR2: ATREADY Mask */ - -/* TPI Integration ITM Data Register Definitions (FIFO1) */ -#define TPI_FIFO1_ITM_ATVALID_Pos 29 /*!< TPI FIFO1: ITM_ATVALID Position */ -#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ - -#define TPI_FIFO1_ITM_bytecount_Pos 27 /*!< TPI FIFO1: ITM_bytecount Position */ -#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ - -#define TPI_FIFO1_ETM_ATVALID_Pos 26 /*!< TPI FIFO1: ETM_ATVALID Position */ -#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ - -#define TPI_FIFO1_ETM_bytecount_Pos 24 /*!< TPI FIFO1: ETM_bytecount Position */ -#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ - -#define TPI_FIFO1_ITM2_Pos 16 /*!< TPI FIFO1: ITM2 Position */ -#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ - -#define TPI_FIFO1_ITM1_Pos 8 /*!< TPI FIFO1: ITM1 Position */ -#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ - -#define TPI_FIFO1_ITM0_Pos 0 /*!< TPI FIFO1: ITM0 Position */ -#define TPI_FIFO1_ITM0_Msk (0xFFUL << TPI_FIFO1_ITM0_Pos) /*!< TPI FIFO1: ITM0 Mask */ - -/* TPI ITATBCTR0 Register Definitions */ -#define TPI_ITATBCTR0_ATREADY_Pos 0 /*!< TPI ITATBCTR0: ATREADY Position */ -#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL << TPI_ITATBCTR0_ATREADY_Pos) /*!< TPI ITATBCTR0: ATREADY Mask */ - -/* TPI Integration Mode Control Register Definitions */ -#define TPI_ITCTRL_Mode_Pos 0 /*!< TPI ITCTRL: Mode Position */ -#define TPI_ITCTRL_Mode_Msk (0x1UL << TPI_ITCTRL_Mode_Pos) /*!< TPI ITCTRL: Mode Mask */ - -/* TPI DEVID Register Definitions */ -#define TPI_DEVID_NRZVALID_Pos 11 /*!< TPI DEVID: NRZVALID Position */ -#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ - -#define TPI_DEVID_MANCVALID_Pos 10 /*!< TPI DEVID: MANCVALID Position */ -#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ - -#define TPI_DEVID_PTINVALID_Pos 9 /*!< TPI DEVID: PTINVALID Position */ -#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ - -#define TPI_DEVID_MinBufSz_Pos 6 /*!< TPI DEVID: MinBufSz Position */ -#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ - -#define TPI_DEVID_AsynClkIn_Pos 5 /*!< TPI DEVID: AsynClkIn Position */ -#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ - -#define TPI_DEVID_NrTraceInput_Pos 0 /*!< TPI DEVID: NrTraceInput Position */ -#define TPI_DEVID_NrTraceInput_Msk (0x1FUL << TPI_DEVID_NrTraceInput_Pos) /*!< TPI DEVID: NrTraceInput Mask */ - -/* TPI DEVTYPE Register Definitions */ -#define TPI_DEVTYPE_SubType_Pos 0 /*!< TPI DEVTYPE: SubType Position */ -#define TPI_DEVTYPE_SubType_Msk (0xFUL << TPI_DEVTYPE_SubType_Pos) /*!< TPI DEVTYPE: SubType Mask */ - -#define TPI_DEVTYPE_MajorType_Pos 4 /*!< TPI DEVTYPE: MajorType Position */ -#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ - -/*@}*/ /* end of group CMSIS_TPI */ - - -#if (__MPU_PRESENT == 1) -/** \ingroup CMSIS_core_register - \defgroup CMSIS_MPU Memory Protection Unit (MPU) - \brief Type definitions for the Memory Protection Unit (MPU) - @{ - */ - -/** \brief Structure type to access the Memory Protection Unit (MPU). - */ -typedef struct -{ - __I uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ - __IO uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ - __IO uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ - __IO uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ - __IO uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ - __IO uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ - __IO uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ - __IO uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ - __IO uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ - __IO uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ - __IO uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ -} MPU_Type; - -/* MPU Type Register */ -#define MPU_TYPE_IREGION_Pos 16 /*!< MPU TYPE: IREGION Position */ -#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ - -#define MPU_TYPE_DREGION_Pos 8 /*!< MPU TYPE: DREGION Position */ -#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ - -#define MPU_TYPE_SEPARATE_Pos 0 /*!< MPU TYPE: SEPARATE Position */ -#define MPU_TYPE_SEPARATE_Msk (1UL << MPU_TYPE_SEPARATE_Pos) /*!< MPU TYPE: SEPARATE Mask */ - -/* MPU Control Register */ -#define MPU_CTRL_PRIVDEFENA_Pos 2 /*!< MPU CTRL: PRIVDEFENA Position */ -#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ - -#define MPU_CTRL_HFNMIENA_Pos 1 /*!< MPU CTRL: HFNMIENA Position */ -#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ - -#define MPU_CTRL_ENABLE_Pos 0 /*!< MPU CTRL: ENABLE Position */ -#define MPU_CTRL_ENABLE_Msk (1UL << MPU_CTRL_ENABLE_Pos) /*!< MPU CTRL: ENABLE Mask */ - -/* MPU Region Number Register */ -#define MPU_RNR_REGION_Pos 0 /*!< MPU RNR: REGION Position */ -#define MPU_RNR_REGION_Msk (0xFFUL << MPU_RNR_REGION_Pos) /*!< MPU RNR: REGION Mask */ - -/* MPU Region Base Address Register */ -#define MPU_RBAR_ADDR_Pos 5 /*!< MPU RBAR: ADDR Position */ -#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ - -#define MPU_RBAR_VALID_Pos 4 /*!< MPU RBAR: VALID Position */ -#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ - -#define MPU_RBAR_REGION_Pos 0 /*!< MPU RBAR: REGION Position */ -#define MPU_RBAR_REGION_Msk (0xFUL << MPU_RBAR_REGION_Pos) /*!< MPU RBAR: REGION Mask */ - -/* MPU Region Attribute and Size Register */ -#define MPU_RASR_ATTRS_Pos 16 /*!< MPU RASR: MPU Region Attribute field Position */ -#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ - -#define MPU_RASR_XN_Pos 28 /*!< MPU RASR: ATTRS.XN Position */ -#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ - -#define MPU_RASR_AP_Pos 24 /*!< MPU RASR: ATTRS.AP Position */ -#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ - -#define MPU_RASR_TEX_Pos 19 /*!< MPU RASR: ATTRS.TEX Position */ -#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ - -#define MPU_RASR_S_Pos 18 /*!< MPU RASR: ATTRS.S Position */ -#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ - -#define MPU_RASR_C_Pos 17 /*!< MPU RASR: ATTRS.C Position */ -#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ - -#define MPU_RASR_B_Pos 16 /*!< MPU RASR: ATTRS.B Position */ -#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ - -#define MPU_RASR_SRD_Pos 8 /*!< MPU RASR: Sub-Region Disable Position */ -#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ - -#define MPU_RASR_SIZE_Pos 1 /*!< MPU RASR: Region Size Field Position */ -#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ - -#define MPU_RASR_ENABLE_Pos 0 /*!< MPU RASR: Region enable bit Position */ -#define MPU_RASR_ENABLE_Msk (1UL << MPU_RASR_ENABLE_Pos) /*!< MPU RASR: Region enable bit Disable Mask */ - -/*@} end of group CMSIS_MPU */ -#endif - - -#if (__FPU_PRESENT == 1) -/** \ingroup CMSIS_core_register - \defgroup CMSIS_FPU Floating Point Unit (FPU) - \brief Type definitions for the Floating Point Unit (FPU) - @{ - */ - -/** \brief Structure type to access the Floating Point Unit (FPU). - */ -typedef struct -{ - uint32_t RESERVED0[1]; - __IO uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ - __IO uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ - __IO uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ - __I uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ - __I uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ -} FPU_Type; - -/* Floating-Point Context Control Register */ -#define FPU_FPCCR_ASPEN_Pos 31 /*!< FPCCR: ASPEN bit Position */ -#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ - -#define FPU_FPCCR_LSPEN_Pos 30 /*!< FPCCR: LSPEN Position */ -#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ - -#define FPU_FPCCR_MONRDY_Pos 8 /*!< FPCCR: MONRDY Position */ -#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ - -#define FPU_FPCCR_BFRDY_Pos 6 /*!< FPCCR: BFRDY Position */ -#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ - -#define FPU_FPCCR_MMRDY_Pos 5 /*!< FPCCR: MMRDY Position */ -#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ - -#define FPU_FPCCR_HFRDY_Pos 4 /*!< FPCCR: HFRDY Position */ -#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ - -#define FPU_FPCCR_THREAD_Pos 3 /*!< FPCCR: processor mode bit Position */ -#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ - -#define FPU_FPCCR_USER_Pos 1 /*!< FPCCR: privilege level bit Position */ -#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ - -#define FPU_FPCCR_LSPACT_Pos 0 /*!< FPCCR: Lazy state preservation active bit Position */ -#define FPU_FPCCR_LSPACT_Msk (1UL << FPU_FPCCR_LSPACT_Pos) /*!< FPCCR: Lazy state preservation active bit Mask */ - -/* Floating-Point Context Address Register */ -#define FPU_FPCAR_ADDRESS_Pos 3 /*!< FPCAR: ADDRESS bit Position */ -#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ - -/* Floating-Point Default Status Control Register */ -#define FPU_FPDSCR_AHP_Pos 26 /*!< FPDSCR: AHP bit Position */ -#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ - -#define FPU_FPDSCR_DN_Pos 25 /*!< FPDSCR: DN bit Position */ -#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ - -#define FPU_FPDSCR_FZ_Pos 24 /*!< FPDSCR: FZ bit Position */ -#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ - -#define FPU_FPDSCR_RMode_Pos 22 /*!< FPDSCR: RMode bit Position */ -#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ - -/* Media and FP Feature Register 0 */ -#define FPU_MVFR0_FP_rounding_modes_Pos 28 /*!< MVFR0: FP rounding modes bits Position */ -#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ - -#define FPU_MVFR0_Short_vectors_Pos 24 /*!< MVFR0: Short vectors bits Position */ -#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ - -#define FPU_MVFR0_Square_root_Pos 20 /*!< MVFR0: Square root bits Position */ -#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ - -#define FPU_MVFR0_Divide_Pos 16 /*!< MVFR0: Divide bits Position */ -#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ - -#define FPU_MVFR0_FP_excep_trapping_Pos 12 /*!< MVFR0: FP exception trapping bits Position */ -#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ - -#define FPU_MVFR0_Double_precision_Pos 8 /*!< MVFR0: Double-precision bits Position */ -#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ - -#define FPU_MVFR0_Single_precision_Pos 4 /*!< MVFR0: Single-precision bits Position */ -#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ - -#define FPU_MVFR0_A_SIMD_registers_Pos 0 /*!< MVFR0: A_SIMD registers bits Position */ -#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL << FPU_MVFR0_A_SIMD_registers_Pos) /*!< MVFR0: A_SIMD registers bits Mask */ - -/* Media and FP Feature Register 1 */ -#define FPU_MVFR1_FP_fused_MAC_Pos 28 /*!< MVFR1: FP fused MAC bits Position */ -#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ - -#define FPU_MVFR1_FP_HPFP_Pos 24 /*!< MVFR1: FP HPFP bits Position */ -#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ - -#define FPU_MVFR1_D_NaN_mode_Pos 4 /*!< MVFR1: D_NaN mode bits Position */ -#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ - -#define FPU_MVFR1_FtZ_mode_Pos 0 /*!< MVFR1: FtZ mode bits Position */ -#define FPU_MVFR1_FtZ_mode_Msk (0xFUL << FPU_MVFR1_FtZ_mode_Pos) /*!< MVFR1: FtZ mode bits Mask */ - -/*@} end of group CMSIS_FPU */ -#endif - - -/** \ingroup CMSIS_core_register - \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) - \brief Type definitions for the Core Debug Registers - @{ - */ - -/** \brief Structure type to access the Core Debug Register (CoreDebug). - */ -typedef struct -{ - __IO uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ - __O uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ - __IO uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ - __IO uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ -} CoreDebug_Type; - -/* Debug Halting Control and Status Register */ -#define CoreDebug_DHCSR_DBGKEY_Pos 16 /*!< CoreDebug DHCSR: DBGKEY Position */ -#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ - -#define CoreDebug_DHCSR_S_RESET_ST_Pos 25 /*!< CoreDebug DHCSR: S_RESET_ST Position */ -#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ - -#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24 /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ -#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ - -#define CoreDebug_DHCSR_S_LOCKUP_Pos 19 /*!< CoreDebug DHCSR: S_LOCKUP Position */ -#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ - -#define CoreDebug_DHCSR_S_SLEEP_Pos 18 /*!< CoreDebug DHCSR: S_SLEEP Position */ -#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ - -#define CoreDebug_DHCSR_S_HALT_Pos 17 /*!< CoreDebug DHCSR: S_HALT Position */ -#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ - -#define CoreDebug_DHCSR_S_REGRDY_Pos 16 /*!< CoreDebug DHCSR: S_REGRDY Position */ -#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ - -#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5 /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ -#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ - -#define CoreDebug_DHCSR_C_MASKINTS_Pos 3 /*!< CoreDebug DHCSR: C_MASKINTS Position */ -#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ - -#define CoreDebug_DHCSR_C_STEP_Pos 2 /*!< CoreDebug DHCSR: C_STEP Position */ -#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ - -#define CoreDebug_DHCSR_C_HALT_Pos 1 /*!< CoreDebug DHCSR: C_HALT Position */ -#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ - -#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0 /*!< CoreDebug DHCSR: C_DEBUGEN Position */ -#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL << CoreDebug_DHCSR_C_DEBUGEN_Pos) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ - -/* Debug Core Register Selector Register */ -#define CoreDebug_DCRSR_REGWnR_Pos 16 /*!< CoreDebug DCRSR: REGWnR Position */ -#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ - -#define CoreDebug_DCRSR_REGSEL_Pos 0 /*!< CoreDebug DCRSR: REGSEL Position */ -#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL << CoreDebug_DCRSR_REGSEL_Pos) /*!< CoreDebug DCRSR: REGSEL Mask */ - -/* Debug Exception and Monitor Control Register */ -#define CoreDebug_DEMCR_TRCENA_Pos 24 /*!< CoreDebug DEMCR: TRCENA Position */ -#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ - -#define CoreDebug_DEMCR_MON_REQ_Pos 19 /*!< CoreDebug DEMCR: MON_REQ Position */ -#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ - -#define CoreDebug_DEMCR_MON_STEP_Pos 18 /*!< CoreDebug DEMCR: MON_STEP Position */ -#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ - -#define CoreDebug_DEMCR_MON_PEND_Pos 17 /*!< CoreDebug DEMCR: MON_PEND Position */ -#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ - -#define CoreDebug_DEMCR_MON_EN_Pos 16 /*!< CoreDebug DEMCR: MON_EN Position */ -#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ - -#define CoreDebug_DEMCR_VC_HARDERR_Pos 10 /*!< CoreDebug DEMCR: VC_HARDERR Position */ -#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ - -#define CoreDebug_DEMCR_VC_INTERR_Pos 9 /*!< CoreDebug DEMCR: VC_INTERR Position */ -#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ - -#define CoreDebug_DEMCR_VC_BUSERR_Pos 8 /*!< CoreDebug DEMCR: VC_BUSERR Position */ -#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ - -#define CoreDebug_DEMCR_VC_STATERR_Pos 7 /*!< CoreDebug DEMCR: VC_STATERR Position */ -#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ - -#define CoreDebug_DEMCR_VC_CHKERR_Pos 6 /*!< CoreDebug DEMCR: VC_CHKERR Position */ -#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ - -#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5 /*!< CoreDebug DEMCR: VC_NOCPERR Position */ -#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ - -#define CoreDebug_DEMCR_VC_MMERR_Pos 4 /*!< CoreDebug DEMCR: VC_MMERR Position */ -#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ - -#define CoreDebug_DEMCR_VC_CORERESET_Pos 0 /*!< CoreDebug DEMCR: VC_CORERESET Position */ -#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL << CoreDebug_DEMCR_VC_CORERESET_Pos) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ - -/*@} end of group CMSIS_CoreDebug */ - - -/** \ingroup CMSIS_core_register - \defgroup CMSIS_core_base Core Definitions - \brief Definitions for base addresses, unions, and structures. - @{ - */ - -/* Memory mapping of Cortex-M4 Hardware */ -#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ -#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ -#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ -#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ -#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ -#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ -#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ -#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ - -#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ -#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ -#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ -#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ -#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ -#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ -#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ -#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ - -#if (__MPU_PRESENT == 1) - #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ - #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ -#endif - -#if (__FPU_PRESENT == 1) - #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ - #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ -#endif - -/*@} */ - - - -/******************************************************************************* - * Hardware Abstraction Layer - Core Function Interface contains: - - Core NVIC Functions - - Core SysTick Functions - - Core Debug Functions - - Core Register Access Functions - ******************************************************************************/ -/** \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference -*/ - - - -/* ########################## NVIC functions #################################### */ -/** \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_NVICFunctions NVIC Functions - \brief Functions that manage interrupts and exceptions via the NVIC. - @{ - */ - -/** \brief Set Priority Grouping - - The function sets the priority grouping field using the required unlock sequence. - The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. - Only values from 0..7 are used. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. - - \param [in] PriorityGroup Priority grouping field. - */ -__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) -{ - uint32_t reg_value; - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07); /* only values 0..7 are used */ - - reg_value = SCB->AIRCR; /* read old register configuration */ - reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); /* clear bits to change */ - reg_value = (reg_value | - ((uint32_t)0x5FA << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8)); /* Insert write key and priorty group */ - SCB->AIRCR = reg_value; -} - - -/** \brief Get Priority Grouping - - The function reads the priority grouping field from the NVIC Interrupt Controller. - - \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). - */ -__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) -{ - return ((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos); /* read priority grouping field */ -} - - -/** \brief Enable External Interrupt - - The function enables a device-specific interrupt in the NVIC interrupt controller. - - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) -{ -/* NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); enable interrupt */ - NVIC->ISER[(uint32_t)((int32_t)IRQn) >> 5] = (uint32_t)(1 << ((uint32_t)((int32_t)IRQn) & (uint32_t)0x1F)); /* enable interrupt */ -} - - -/** \brief Disable External Interrupt - - The function disables a device-specific interrupt in the NVIC interrupt controller. - - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) -{ - NVIC->ICER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* disable interrupt */ -} - - -/** \brief Get Pending Interrupt - - The function reads the pending register in the NVIC and returns the pending bit - for the specified interrupt. - - \param [in] IRQn Interrupt number. - - \return 0 Interrupt status is not pending. - \return 1 Interrupt status is pending. - */ -__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) -{ - return((uint32_t) ((NVIC->ISPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if pending else 0 */ -} - - -/** \brief Set Pending Interrupt - - The function sets the pending bit of an external interrupt. - - \param [in] IRQn Interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ISPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* set interrupt pending */ -} - - -/** \brief Clear Pending Interrupt - - The function clears the pending bit of an external interrupt. - - \param [in] IRQn External interrupt number. Value cannot be negative. - */ -__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) -{ - NVIC->ICPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */ -} - - -/** \brief Get Active Interrupt - - The function reads the active register in NVIC and returns the active bit. - - \param [in] IRQn Interrupt number. - - \return 0 Interrupt status is not active. - \return 1 Interrupt status is active. - */ -__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) -{ - return((uint32_t)((NVIC->IABR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if active else 0 */ -} - - -/** \brief Set Interrupt Priority - - The function sets the priority of an interrupt. - - \note The priority cannot be set for every core interrupt. - - \param [in] IRQn Interrupt number. - \param [in] priority Priority to set. - */ -__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) -{ - if(IRQn < 0) { - SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M System Interrupts */ - else { - NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */ -} - - -/** \brief Get Interrupt Priority - - The function reads the priority of an interrupt. The interrupt - number can be positive to specify an external (device specific) - interrupt, or negative to specify an internal (core) interrupt. - - - \param [in] IRQn Interrupt number. - \return Interrupt Priority. Value is aligned automatically to the implemented - priority bits of the microcontroller. - */ -__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) -{ - - if(IRQn < 0) { - return((uint32_t)(SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for Cortex-M system interrupts */ - else { - return((uint32_t)(NVIC->IP[(uint32_t)(IRQn)] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for device specific interrupts */ -} - - -/** \brief Encode Priority - - The function encodes the priority for an interrupt with the given priority group, - preemptive priority value, and subpriority value. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS), the samllest possible priority group is set. - - \param [in] PriorityGroup Used priority group. - \param [in] PreemptPriority Preemptive priority value (starting from 0). - \param [in] SubPriority Subpriority value (starting from 0). - \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). - */ -__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) -{ - uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ - uint32_t PreemptPriorityBits; - uint32_t SubPriorityBits; - - PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; - SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; - - return ( - ((PreemptPriority & ((1 << (PreemptPriorityBits)) - 1)) << SubPriorityBits) | - ((SubPriority & ((1 << (SubPriorityBits )) - 1))) - ); -} - - -/** \brief Decode Priority - - The function decodes an interrupt priority value with a given priority group to - preemptive priority value and subpriority value. - In case of a conflict between priority grouping and available - priority bits (__NVIC_PRIO_BITS) the samllest possible priority group is set. - - \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). - \param [in] PriorityGroup Used priority group. - \param [out] pPreemptPriority Preemptive priority value (starting from 0). - \param [out] pSubPriority Subpriority value (starting from 0). - */ -__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* pPreemptPriority, uint32_t* pSubPriority) -{ - uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ - uint32_t PreemptPriorityBits; - uint32_t SubPriorityBits; - - PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; - SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; - - *pPreemptPriority = (Priority >> SubPriorityBits) & ((1 << (PreemptPriorityBits)) - 1); - *pSubPriority = (Priority ) & ((1 << (SubPriorityBits )) - 1); -} - - -/** \brief System Reset - - The function initiates a system reset request to reset the MCU. - */ -__STATIC_INLINE void NVIC_SystemReset(void) -{ - __DSB(); /* Ensure all outstanding memory accesses included - buffered write are completed before reset */ - SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | - (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | - SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */ - __DSB(); /* Ensure completion of memory access */ - while(1); /* wait until reset */ -} - -/*@} end of CMSIS_Core_NVICFunctions */ - - - -/* ################################## SysTick function ############################################ */ -/** \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_Core_SysTickFunctions SysTick Functions - \brief Functions that configure the System. - @{ - */ - -#if (__Vendor_SysTickConfig == 0) - -/** \brief System Tick Configuration - - The function initializes the System Timer and its interrupt, and starts the System Tick Timer. - Counter is in free running mode to generate periodic interrupts. - - \param [in] ticks Number of ticks between two interrupts. - - \return 0 Function succeeded. - \return 1 Function failed. - - \note When the variable __Vendor_SysTickConfig is set to 1, then the - function SysTick_Config is not included. In this case, the file device.h - must contain a vendor-specific implementation of this function. - - */ -__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) -{ - if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ - - SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */ - NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */ - SysTick->VAL = 0; /* Load the SysTick Counter Value */ - SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | - SysTick_CTRL_TICKINT_Msk | - SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ - return (0); /* Function successful */ -} - -#endif - -/*@} end of CMSIS_Core_SysTickFunctions */ - - - -/* ##################################### Debug In/Output function ########################################### */ -/** \ingroup CMSIS_Core_FunctionInterface - \defgroup CMSIS_core_DebugFunctions ITM Functions - \brief Functions that access the ITM debug interface. - @{ - */ - -extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ -#define ITM_RXBUFFER_EMPTY 0x5AA55AA5 /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ - - -/** \brief ITM Send Character - - The function transmits a character via the ITM channel 0, and - \li Just returns when no debugger is connected that has booked the output. - \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. - - \param [in] ch Character to transmit. - - \returns Character to transmit. - */ -__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) -{ - if ((ITM->TCR & ITM_TCR_ITMENA_Msk) && /* ITM enabled */ - (ITM->TER & (1UL << 0) ) ) /* ITM Port #0 enabled */ - { - while (ITM->PORT[0].u32 == 0); - ITM->PORT[0].u8 = (uint8_t) ch; - } - return (ch); -} - - -/** \brief ITM Receive Character - - The function inputs a character via the external variable \ref ITM_RxBuffer. - - \return Received character. - \return -1 No character pending. - */ -__STATIC_INLINE int32_t ITM_ReceiveChar (void) { - int32_t ch = -1; /* no character available */ - - if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) { - ch = ITM_RxBuffer; - ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ - } - - return (ch); -} - - -/** \brief ITM Check Character - - The function checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. - - \return 0 No character available. - \return 1 Character available. - */ -__STATIC_INLINE int32_t ITM_CheckChar (void) { - - if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) { - return (0); /* no character available */ - } else { - return (1); /* character available */ - } -} - -/*@} end of CMSIS_core_DebugFunctions */ - -#endif /* __CORE_CM4_H_DEPENDANT */ - -#endif /* __CMSIS_GENERIC */ - -#ifdef __cplusplus -} -#endif +/**************************************************************************//** + * @file core_cm4.h + * @brief CMSIS Cortex-M4 Core Peripheral Access Layer Header File + * @version V3.01 + * @date 22. March 2012 + * + * @note + * Copyright (C) 2009-2012 ARM Limited. All rights reserved. + * + * @par + * ARM Limited (ARM) is supplying this software for use with Cortex-M + * processor based microcontrollers. This file can be freely distributed + * within development tools that are supporting such ARM based processors. + * + * @par + * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. + * ARM SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. + * + ******************************************************************************/ +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#ifndef __CORE_CM4_H_GENERIC +#define __CORE_CM4_H_GENERIC + +/** \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** \ingroup Cortex_M4 + @{ + */ + +/* CMSIS CM4 definitions */ +#define __CM4_CMSIS_VERSION_MAIN (0x03) /*!< [31:16] CMSIS HAL main version */ +#define __CM4_CMSIS_VERSION_SUB (0x01) /*!< [15:0] CMSIS HAL sub version */ +#define __CM4_CMSIS_VERSION ((__CM4_CMSIS_VERSION_MAIN << 16) | \ + __CM4_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + +#define __CORTEX_M (0x04) /*!< Cortex-M Core */ + + +#if defined ( __CC_ARM ) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + #define __STATIC_INLINE static __inline + +#elif defined ( __ICCARM__ ) + #define __ASM __asm /*!< asm keyword for IAR Compiler */ + #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ + #define __STATIC_INLINE static inline + +#elif defined ( __TMS470__ ) + #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __GNUC__ ) + #define __ASM __asm /*!< asm keyword for GNU Compiler */ + #define __INLINE inline /*!< inline keyword for GNU Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __TASKING__ ) + #define __ASM __asm /*!< asm keyword for TASKING Compiler */ + #define __INLINE inline /*!< inline keyword for TASKING Compiler */ + #define __STATIC_INLINE static inline + +#endif + +/** __FPU_USED indicates whether an FPU is used or not. For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. +*/ +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1 + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0 + #endif + #else + #define __FPU_USED 0 + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1 + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0 + #endif + #else + #define __FPU_USED 0 + #endif + +#elif defined ( __TMS470__ ) + #if defined __TI_VFP_SUPPORT__ + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1 + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0 + #endif + #else + #define __FPU_USED 0 + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1 + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0 + #endif + #else + #define __FPU_USED 0 + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1 + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0 + #endif + #else + #define __FPU_USED 0 + #endif +#endif + +#include /* standard types definitions */ +#include /* Core Instruction Access */ +#include /* Core Function Access */ +#include /* Compiler specific SIMD Intrinsics */ + +#endif /* __CORE_CM4_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM4_H_DEPENDANT +#define __CORE_CM4_H_DEPENDANT + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM4_REV + #define __CM4_REV 0x0000 + #warning "__CM4_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0 + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0 + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 4 + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0 + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/*@} end of group Cortex_M4 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core FPU Register + ******************************************************************************/ +/** \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ +#else + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ +#endif + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + + +/** \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + + +/** \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ +#else + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ +#endif + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + + +/** \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/*@} end of group CMSIS_CORE */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24]; + __IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[24]; + __IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24]; + __IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24]; + __IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56]; + __IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644]; + __O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0 /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL << NVIC_STIR_INTID_Pos) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IO uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IO uint8_t SHP[12]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IO uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IO uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IO uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IO uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IO uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IO uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __I uint32_t PFR[2]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __I uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __I uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __I uint32_t MMFR[4]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __I uint32_t ISAR[5]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5]; + __IO uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24 /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20 /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16 /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4 /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0 /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL << SCB_CPUID_REVISION_Pos) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31 /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28 /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27 /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26 /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25 /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23 /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22 /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12 /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11 /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0 /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL << SCB_ICSR_VECTACTIVE_Pos) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7 /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16 /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16 /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15 /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8 /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2 /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1 /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0 /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL << SCB_AIRCR_VECTRESET_Pos) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4 /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2 /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1 /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9 /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8 /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4 /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3 /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1 /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0 /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL << SCB_CCR_NONBASETHRDENA_Pos) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18 /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17 /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16 /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15 /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14 /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13 /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12 /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11 /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10 /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8 /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7 /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3 /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1 /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0 /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL << SCB_SHCSR_MEMFAULTACT_Pos) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Registers Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16 /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8 /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0 /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL << SCB_CFSR_MEMFAULTSR_Pos) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* SCB Hard Fault Status Registers Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31 /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30 /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1 /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4 /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3 /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2 /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1 /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0 /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL << SCB_DFSR_HALTED_Pos) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1]; + __I uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ + __IO uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0 /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL << SCnSCB_ICTR_INTLINESNUM_Pos) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ +#define SCnSCB_ACTLR_DISOOFP_Pos 9 /*!< ACTLR: DISOOFP Position */ +#define SCnSCB_ACTLR_DISOOFP_Msk (1UL << SCnSCB_ACTLR_DISOOFP_Pos) /*!< ACTLR: DISOOFP Mask */ + +#define SCnSCB_ACTLR_DISFPCA_Pos 8 /*!< ACTLR: DISFPCA Position */ +#define SCnSCB_ACTLR_DISFPCA_Msk (1UL << SCnSCB_ACTLR_DISFPCA_Pos) /*!< ACTLR: DISFPCA Mask */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2 /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1 /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0 /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL << SCnSCB_ACTLR_DISMCYCINT_Pos) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16 /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2 /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1 /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0 /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31 /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30 /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0 /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __O union + { + __O uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __O uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __O uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864]; + __IO uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15]; + __IO uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15]; + __IO uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29]; + __O uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __I uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IO uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43]; + __O uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __I uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6]; + __I uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __I uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __I uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __I uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __I uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __I uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __I uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __I uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __I uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __I uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __I uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __I uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0 /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFUL << ITM_TPR_PRIVMASK_Pos) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23 /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TraceBusID_Pos 16 /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10 /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPrescale_Pos 8 /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4 /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3 /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2 /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1 /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0 /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL << ITM_TCR_ITMENA_Pos) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0 /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL << ITM_IWR_ATVALIDM_Pos) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0 /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL << ITM_IRR_ATREADYM_Pos) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0 /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL << ITM_IMCR_INTEGRATION_Pos) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2 /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1 /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0 /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL << ITM_LSR_Present_Pos) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IO uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IO uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IO uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IO uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IO uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IO uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __I uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IO uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IO uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IO uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1]; + __IO uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IO uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IO uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1]; + __IO uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IO uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IO uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1]; + __IO uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IO uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IO uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28 /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27 /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26 /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25 /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24 /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22 /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21 /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20 /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19 /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18 /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17 /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16 /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12 /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10 /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9 /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5 /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1 /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0 /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL << DWT_CTRL_CYCCNTENA_Pos) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0 /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL << DWT_CPICNT_CPICNT_Pos) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0 /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL << DWT_EXCCNT_EXCCNT_Pos) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0 /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL << DWT_SLEEPCNT_SLEEPCNT_Pos) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0 /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL << DWT_LSUCNT_LSUCNT_Pos) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0 /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL << DWT_FOLDCNT_FOLDCNT_Pos) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0 /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL << DWT_MASK_MASK_Pos) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24 /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16 /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12 /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10 /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9 /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8 /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7 /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5 /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0 /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL << DWT_FUNCTION_FUNCTION_Pos) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IO uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IO uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2]; + __IO uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55]; + __IO uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131]; + __I uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IO uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __I uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759]; + __I uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __I uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __I uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1]; + __I uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __I uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IO uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39]; + __IO uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IO uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8]; + __I uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __I uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0 /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL << TPI_ACPR_PRESCALER_Pos) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0 /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL << TPI_SPPR_TXMODE_Pos) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3 /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2 /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1 /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0 /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL << TPI_FFSR_FlInProg_Pos) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8 /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1 /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0 /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL << TPI_TRIGGER_TRIGGER_Pos) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29 /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27 /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26 /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24 /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16 /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8 /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0 /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL << TPI_FIFO0_ETM0_Pos) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY_Pos 0 /*!< TPI ITATBCTR2: ATREADY Position */ +#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL << TPI_ITATBCTR2_ATREADY_Pos) /*!< TPI ITATBCTR2: ATREADY Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29 /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27 /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26 /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24 /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16 /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8 /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0 /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL << TPI_FIFO1_ITM0_Pos) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY_Pos 0 /*!< TPI ITATBCTR0: ATREADY Position */ +#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL << TPI_ITATBCTR0_ATREADY_Pos) /*!< TPI ITATBCTR0: ATREADY Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0 /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x1UL << TPI_ITCTRL_Mode_Pos) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11 /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10 /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9 /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6 /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5 /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0 /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL << TPI_DEVID_NrTraceInput_Pos) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 0 /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL << TPI_DEVTYPE_SubType_Pos) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 4 /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if (__MPU_PRESENT == 1) +/** \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __I uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IO uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IO uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IO uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IO uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IO uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IO uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IO uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IO uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IO uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IO uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +/* MPU Type Register */ +#define MPU_TYPE_IREGION_Pos 16 /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8 /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0 /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL << MPU_TYPE_SEPARATE_Pos) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register */ +#define MPU_CTRL_PRIVDEFENA_Pos 2 /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1 /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0 /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL << MPU_CTRL_ENABLE_Pos) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register */ +#define MPU_RNR_REGION_Pos 0 /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL << MPU_RNR_REGION_Pos) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register */ +#define MPU_RBAR_ADDR_Pos 5 /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4 /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0 /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL << MPU_RBAR_REGION_Pos) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register */ +#define MPU_RASR_ATTRS_Pos 16 /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28 /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24 /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19 /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18 /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17 /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16 /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8 /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1 /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0 /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL << MPU_RASR_ENABLE_Pos) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +#if (__FPU_PRESENT == 1) +/** \ingroup CMSIS_core_register + \defgroup CMSIS_FPU Floating Point Unit (FPU) + \brief Type definitions for the Floating Point Unit (FPU) + @{ + */ + +/** \brief Structure type to access the Floating Point Unit (FPU). + */ +typedef struct +{ + uint32_t RESERVED0[1]; + __IO uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ + __IO uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ + __IO uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ + __I uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ + __I uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ +} FPU_Type; + +/* Floating-Point Context Control Register */ +#define FPU_FPCCR_ASPEN_Pos 31 /*!< FPCCR: ASPEN bit Position */ +#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ + +#define FPU_FPCCR_LSPEN_Pos 30 /*!< FPCCR: LSPEN Position */ +#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ + +#define FPU_FPCCR_MONRDY_Pos 8 /*!< FPCCR: MONRDY Position */ +#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ + +#define FPU_FPCCR_BFRDY_Pos 6 /*!< FPCCR: BFRDY Position */ +#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ + +#define FPU_FPCCR_MMRDY_Pos 5 /*!< FPCCR: MMRDY Position */ +#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ + +#define FPU_FPCCR_HFRDY_Pos 4 /*!< FPCCR: HFRDY Position */ +#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ + +#define FPU_FPCCR_THREAD_Pos 3 /*!< FPCCR: processor mode bit Position */ +#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ + +#define FPU_FPCCR_USER_Pos 1 /*!< FPCCR: privilege level bit Position */ +#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ + +#define FPU_FPCCR_LSPACT_Pos 0 /*!< FPCCR: Lazy state preservation active bit Position */ +#define FPU_FPCCR_LSPACT_Msk (1UL << FPU_FPCCR_LSPACT_Pos) /*!< FPCCR: Lazy state preservation active bit Mask */ + +/* Floating-Point Context Address Register */ +#define FPU_FPCAR_ADDRESS_Pos 3 /*!< FPCAR: ADDRESS bit Position */ +#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ + +/* Floating-Point Default Status Control Register */ +#define FPU_FPDSCR_AHP_Pos 26 /*!< FPDSCR: AHP bit Position */ +#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ + +#define FPU_FPDSCR_DN_Pos 25 /*!< FPDSCR: DN bit Position */ +#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ + +#define FPU_FPDSCR_FZ_Pos 24 /*!< FPDSCR: FZ bit Position */ +#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ + +#define FPU_FPDSCR_RMode_Pos 22 /*!< FPDSCR: RMode bit Position */ +#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ + +/* Media and FP Feature Register 0 */ +#define FPU_MVFR0_FP_rounding_modes_Pos 28 /*!< MVFR0: FP rounding modes bits Position */ +#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ + +#define FPU_MVFR0_Short_vectors_Pos 24 /*!< MVFR0: Short vectors bits Position */ +#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ + +#define FPU_MVFR0_Square_root_Pos 20 /*!< MVFR0: Square root bits Position */ +#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ + +#define FPU_MVFR0_Divide_Pos 16 /*!< MVFR0: Divide bits Position */ +#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ + +#define FPU_MVFR0_FP_excep_trapping_Pos 12 /*!< MVFR0: FP exception trapping bits Position */ +#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ + +#define FPU_MVFR0_Double_precision_Pos 8 /*!< MVFR0: Double-precision bits Position */ +#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ + +#define FPU_MVFR0_Single_precision_Pos 4 /*!< MVFR0: Single-precision bits Position */ +#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ + +#define FPU_MVFR0_A_SIMD_registers_Pos 0 /*!< MVFR0: A_SIMD registers bits Position */ +#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL << FPU_MVFR0_A_SIMD_registers_Pos) /*!< MVFR0: A_SIMD registers bits Mask */ + +/* Media and FP Feature Register 1 */ +#define FPU_MVFR1_FP_fused_MAC_Pos 28 /*!< MVFR1: FP fused MAC bits Position */ +#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ + +#define FPU_MVFR1_FP_HPFP_Pos 24 /*!< MVFR1: FP HPFP bits Position */ +#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ + +#define FPU_MVFR1_D_NaN_mode_Pos 4 /*!< MVFR1: D_NaN mode bits Position */ +#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ + +#define FPU_MVFR1_FtZ_mode_Pos 0 /*!< MVFR1: FtZ mode bits Position */ +#define FPU_MVFR1_FtZ_mode_Msk (0xFUL << FPU_MVFR1_FtZ_mode_Pos) /*!< MVFR1: FtZ mode bits Mask */ + +/*@} end of group CMSIS_FPU */ +#endif + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IO uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __O uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IO uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IO uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16 /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25 /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24 /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19 /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18 /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17 /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16 /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5 /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3 /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2 /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1 /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0 /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL << CoreDebug_DHCSR_C_DEBUGEN_Pos) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register */ +#define CoreDebug_DCRSR_REGWnR_Pos 16 /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0 /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL << CoreDebug_DCRSR_REGSEL_Pos) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register */ +#define CoreDebug_DEMCR_TRCENA_Pos 24 /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19 /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18 /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17 /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16 /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10 /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9 /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8 /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7 /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6 /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5 /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4 /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0 /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL << CoreDebug_DEMCR_VC_CORERESET_Pos) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Cortex-M4 Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if (__MPU_PRESENT == 1) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +#if (__FPU_PRESENT == 1) + #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ + #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ +#endif + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +/** \brief Set Priority Grouping + + The function sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8)); /* Insert write key and priorty group */ + SCB->AIRCR = reg_value; +} + + +/** \brief Get Priority Grouping + + The function reads the priority grouping field from the NVIC Interrupt Controller. + + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +{ + return ((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos); /* read priority grouping field */ +} + + +/** \brief Enable External Interrupt + + The function enables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +{ +/* NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); enable interrupt */ + NVIC->ISER[(uint32_t)((int32_t)IRQn) >> 5] = (uint32_t)(1 << ((uint32_t)((int32_t)IRQn) & (uint32_t)0x1F)); /* enable interrupt */ +} + + +/** \brief Disable External Interrupt + + The function disables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +{ + NVIC->ICER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* disable interrupt */ +} + + +/** \brief Get Pending Interrupt + + The function reads the pending register in the NVIC and returns the pending bit + for the specified interrupt. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + */ +__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + return((uint32_t) ((NVIC->ISPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if pending else 0 */ +} + + +/** \brief Set Pending Interrupt + + The function sets the pending bit of an external interrupt. + + \param [in] IRQn Interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ISPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* set interrupt pending */ +} + + +/** \brief Clear Pending Interrupt + + The function clears the pending bit of an external interrupt. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ICPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */ +} + + +/** \brief Get Active Interrupt + + The function reads the active register in NVIC and returns the active bit. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + */ +__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +{ + return((uint32_t)((NVIC->IABR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if active else 0 */ +} + + +/** \brief Set Interrupt Priority + + The function sets the priority of an interrupt. + + \note The priority cannot be set for every core interrupt. + + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + */ +__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if(IRQn < 0) { + SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M System Interrupts */ + else { + NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */ +} + + +/** \brief Get Interrupt Priority + + The function reads the priority of an interrupt. The interrupt + number can be positive to specify an external (device specific) + interrupt, or negative to specify an internal (core) interrupt. + + + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented + priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +{ + + if(IRQn < 0) { + return((uint32_t)(SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for Cortex-M system interrupts */ + else { + return((uint32_t)(NVIC->IP[(uint32_t)(IRQn)] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for device specific interrupts */ +} + + +/** \brief Encode Priority + + The function encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the samllest possible priority group is set. + + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + return ( + ((PreemptPriority & ((1 << (PreemptPriorityBits)) - 1)) << SubPriorityBits) | + ((SubPriority & ((1 << (SubPriorityBits )) - 1))) + ); +} + + +/** \brief Decode Priority + + The function decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the samllest possible priority group is set. + + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* pPreemptPriority, uint32_t* pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + *pPreemptPriority = (Priority >> SubPriorityBits) & ((1 << (PreemptPriorityBits)) - 1); + *pSubPriority = (Priority ) & ((1 << (SubPriorityBits )) - 1); +} + + +/** \brief System Reset + + The function initiates a system reset request to reset the MCU. + */ +__STATIC_INLINE void NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + while(1); /* wait until reset */ +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if (__Vendor_SysTickConfig == 0) + +/** \brief System Tick Configuration + + The function initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + + \param [in] ticks Number of ticks between two interrupts. + + \return 0 Function succeeded. + \return 1 Function failed. + + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ + + SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY 0x5AA55AA5 /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** \brief ITM Send Character + + The function transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + + \param [in] ch Character to transmit. + + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if ((ITM->TCR & ITM_TCR_ITMENA_Msk) && /* ITM enabled */ + (ITM->TER & (1UL << 0) ) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0].u32 == 0); + ITM->PORT[0].u8 = (uint8_t) ch; + } + return (ch); +} + + +/** \brief ITM Receive Character + + The function inputs a character via the external variable \ref ITM_RxBuffer. + + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) { + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** \brief ITM Check Character + + The function checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) { + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) { + return (0); /* no character available */ + } else { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + +#endif /* __CORE_CM4_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ + +#ifdef __cplusplus +} +#endif diff --git a/hardware/firmware/audio_board/vendor/cores/teensy3/math_helper.c b/hardware/firmware/audio_board/vendor/cores/teensy3/math_helper.c index a2d998a8..7ca2f2eb 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy3/math_helper.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy3/math_helper.c @@ -1,439 +1,439 @@ -/* ---------------------------------------------------------------------- -* Copyright (C) 2010 ARM Limited. All rights reserved. -* -* $Date: 29. November 2010 -* $Revision: V1.0.3 -* -* Project: CMSIS DSP Library -* -* Title: math_helper.c -* -* Description: Definition of all helper functions required. -* -* Target Processor: Cortex-M4/Cortex-M3 -* -* Version 1.0.3 2010/11/29 -* Re-organized the CMSIS folders and updated documentation. -* -* Version 1.0.2 2010/11/11 -* Documentation updated. -* -* Version 1.0.1 2010/10/05 -* Production release and review comments incorporated. -* -* Version 1.0.0 2010/09/20 -* Production release and review comments incorporated. -* -* Version 0.0.7 2010/06/10 -* Misra-C changes done -* -------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- -* Include standard header files -* -------------------------------------------------------------------- */ -#include - -/* ---------------------------------------------------------------------- -* Include project header files -* -------------------------------------------------------------------- */ -#include "math_helper.h" - -/** - * @brief Caluclation of SNR - * @param float* Pointer to the reference buffer - * @param float* Pointer to the test buffer - * @param uint32_t total number of samples - * @return float SNR - * The function Caluclates signal to noise ratio for the reference output - * and test output - */ - -float arm_snr_f32(float *pRef, float *pTest, uint32_t buffSize) -{ - float EnergySignal = 0.0; - float EnergyError = 0.0; - uint32_t i; - float SNR; - int temp; - int *test; - - for (i = 0; i < buffSize; i++) { - /* Checking for a NAN value in pRef array */ - test = (int *)(&pRef[i]); - temp = *test; - - if (temp == 0x7FC00000) { - return(0); - } - - /* Checking for a NAN value in pTest array */ - test = (int *)(&pTest[i]); - temp = *test; - - if (temp == 0x7FC00000) { - return(0); - } - EnergySignal += pRef[i] * pRef[i]; - EnergyError += (pRef[i] - pTest[i]) * (pRef[i] - pTest[i]); - } - - /* Checking for a NAN value in EnergyError */ - test = (int *)(&EnergyError); - temp = *test; - - if(temp == 0x7FC00000) { - return(0); - } - - SNR = 10 * log10f(EnergySignal / EnergyError); - return (SNR); -} - - -/** - * @brief Provide guard bits for Input buffer - * @param q15_t* Pointer to input buffer - * @param uint32_t blockSize - * @param uint32_t guard_bits - * @return none - * The function Provides the guard bits for the buffer - * to avoid overflow - */ - -void arm_provide_guard_bits_q15 (q15_t * input_buf, uint32_t blockSize, - uint32_t guard_bits) -{ - uint32_t i; - - for (i = 0; i < blockSize; i++) - { - input_buf[i] = input_buf[i] >> guard_bits; - } -} - -/** - * @brief Converts float to fixed in q12.20 format - * @param uint32_t number of samples in the buffer - * @return none - * The function converts floating point values to fixed point(q12.20) values - */ - -void arm_float_to_q12_20(float *pIn, q31_t * pOut, uint32_t numSamples) -{ - uint32_t i; - - for (i = 0; i < numSamples; i++) { - /* 1048576.0f corresponds to pow(2, 20) */ - pOut[i] = (q31_t) (pIn[i] * 1048576.0f); - - pOut[i] += pIn[i] > 0 ? 0.5 : -0.5; - - if (pIn[i] == (float) 1.0) { - pOut[i] = 0x000FFFFF; - } - } -} - -/** - * @brief Compare MATLAB Reference Output and ARM Test output - * @param q15_t* Pointer to Ref buffer - * @param q15_t* Pointer to Test buffer - * @param uint32_t number of samples in the buffer - * @return none - */ - -uint32_t arm_compare_fixed_q15(q15_t *pIn, q15_t * pOut, uint32_t numSamples) -{ - uint32_t i; - int32_t diff, diffCrnt = 0; - uint32_t maxDiff = 0; - - for (i = 0; i < numSamples; i++) - { - diff = pIn[i] - pOut[i]; - diffCrnt = (diff > 0) ? diff : -diff; - - if(diffCrnt > maxDiff) - { - maxDiff = diffCrnt; - } - } - - return(maxDiff); -} - -/** - * @brief Compare MATLAB Reference Output and ARM Test output - * @param q31_t* Pointer to Ref buffer - * @param q31_t* Pointer to Test buffer - * @param uint32_t number of samples in the buffer - * @return none - */ - -uint32_t arm_compare_fixed_q31(q31_t *pIn, q31_t * pOut, uint32_t numSamples) -{ - uint32_t i; - int32_t diff, diffCrnt = 0; - uint32_t maxDiff = 0; - - for (i = 0; i < numSamples; i++) - { - diff = pIn[i] - pOut[i]; - diffCrnt = (diff > 0) ? diff : -diff; - - if(diffCrnt > maxDiff) - { - maxDiff = diffCrnt; - } - } - - return(maxDiff); -} - -/** - * @brief Provide guard bits for Input buffer - * @param q31_t* Pointer to input buffer - * @param uint32_t blockSize - * @param uint32_t guard_bits - * @return none - * The function Provides the guard bits for the buffer - * to avoid overflow - */ - -void arm_provide_guard_bits_q31 (q31_t * input_buf, - uint32_t blockSize, - uint32_t guard_bits) -{ - uint32_t i; - - for (i = 0; i < blockSize; i++) - { - input_buf[i] = input_buf[i] >> guard_bits; - } -} - -/** - * @brief Provide guard bits for Input buffer - * @param q31_t* Pointer to input buffer - * @param uint32_t blockSize - * @param uint32_t guard_bits - * @return none - * The function Provides the guard bits for the buffer - * to avoid overflow - */ - -void arm_provide_guard_bits_q7 (q7_t * input_buf, - uint32_t blockSize, - uint32_t guard_bits) -{ - uint32_t i; - - for (i = 0; i < blockSize; i++) - { - input_buf[i] = input_buf[i] >> guard_bits; - } -} - - - -/** - * @brief Caluclates number of guard bits - * @param uint32_t number of additions - * @return none - * The function Caluclates the number of guard bits - * depending on the numtaps - */ - -uint32_t arm_calc_guard_bits (uint32_t num_adds) -{ - uint32_t i = 1, j = 0; - - if (num_adds == 1) - { - return (0); - } - - while (i < num_adds) - { - i = i * 2; - j++; - } - - return (j); -} - -/** - * @brief Converts Q15 to floating-point - * @param uint32_t number of samples in the buffer - * @return none - */ - -void arm_apply_guard_bits (float32_t * pIn, - uint32_t numSamples, - uint32_t guard_bits) -{ - uint32_t i; - - for (i = 0; i < numSamples; i++) - { - pIn[i] = pIn[i] * arm_calc_2pow(guard_bits); - } -} - -/** - * @brief Calculates pow(2, numShifts) - * @param uint32_t number of shifts - * @return pow(2, numShifts) - */ -uint32_t arm_calc_2pow(uint32_t numShifts) -{ - - uint32_t i, val = 1; - - for (i = 0; i < numShifts; i++) - { - val = val * 2; - } - - return(val); -} - - - -/** - * @brief Converts float to fixed q14 - * @param uint32_t number of samples in the buffer - * @return none - * The function converts floating point values to fixed point values - */ - -void arm_float_to_q14 (float *pIn, q15_t * pOut, - uint32_t numSamples) -{ - uint32_t i; - - for (i = 0; i < numSamples; i++) - { - /* 16384.0f corresponds to pow(2, 14) */ - pOut[i] = (q15_t) (pIn[i] * 16384.0f); - - pOut[i] += pIn[i] > 0 ? 0.5 : -0.5; - - if (pIn[i] == (float) 2.0) - { - pOut[i] = 0x7FFF; - } - - } - -} - - -/** - * @brief Converts float to fixed q30 format - * @param uint32_t number of samples in the buffer - * @return none - * The function converts floating point values to fixed point values - */ - -void arm_float_to_q30 (float *pIn, q31_t * pOut, - uint32_t numSamples) -{ - uint32_t i; - - for (i = 0; i < numSamples; i++) - { - /* 1073741824.0f corresponds to pow(2, 30) */ - pOut[i] = (q31_t) (pIn[i] * 1073741824.0f); - - pOut[i] += pIn[i] > 0 ? 0.5 : -0.5; - - if (pIn[i] == (float) 2.0) - { - pOut[i] = 0x7FFFFFFF; - } - } -} - -/** - * @brief Converts float to fixed q30 format - * @param uint32_t number of samples in the buffer - * @return none - * The function converts floating point values to fixed point values - */ - -void arm_float_to_q29 (float *pIn, q31_t * pOut, - uint32_t numSamples) -{ - uint32_t i; - - for (i = 0; i < numSamples; i++) - { - /* 1073741824.0f corresponds to pow(2, 30) */ - pOut[i] = (q31_t) (pIn[i] * 536870912.0f); - - pOut[i] += pIn[i] > 0 ? 0.5 : -0.5; - - if (pIn[i] == (float) 4.0) - { - pOut[i] = 0x7FFFFFFF; - } - } -} - - -/** - * @brief Converts float to fixed q28 format - * @param uint32_t number of samples in the buffer - * @return none - * The function converts floating point values to fixed point values - */ - -void arm_float_to_q28 (float *pIn, q31_t * pOut, - uint32_t numSamples) -{ - uint32_t i; - - for (i = 0; i < numSamples; i++) - { - /* 268435456.0f corresponds to pow(2, 28) */ - pOut[i] = (q31_t) (pIn[i] * 268435456.0f); - - pOut[i] += pIn[i] > 0 ? 0.5 : -0.5; - - if (pIn[i] == (float) 8.0) - { - pOut[i] = 0x7FFFFFFF; - } - } -} - -/** - * @brief Clip the float values to +/- 1 - * @param pIn input buffer - * @param numSamples number of samples in the buffer - * @return none - * The function converts floating point values to fixed point values - */ - -void arm_clip_f32 (float *pIn, uint32_t numSamples) -{ - uint32_t i; - - for (i = 0; i < numSamples; i++) - { - if(pIn[i] > 1.0f) - { - pIn[i] = 1.0; - } - else if( pIn[i] < -1.0f) - { - pIn[i] = -1.0; - } - - } -} - - - - +/* ---------------------------------------------------------------------- +* Copyright (C) 2010 ARM Limited. All rights reserved. +* +* $Date: 29. November 2010 +* $Revision: V1.0.3 +* +* Project: CMSIS DSP Library +* +* Title: math_helper.c +* +* Description: Definition of all helper functions required. +* +* Target Processor: Cortex-M4/Cortex-M3 +* +* Version 1.0.3 2010/11/29 +* Re-organized the CMSIS folders and updated documentation. +* +* Version 1.0.2 2010/11/11 +* Documentation updated. +* +* Version 1.0.1 2010/10/05 +* Production release and review comments incorporated. +* +* Version 1.0.0 2010/09/20 +* Production release and review comments incorporated. +* +* Version 0.0.7 2010/06/10 +* Misra-C changes done +* -------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- +* Include standard header files +* -------------------------------------------------------------------- */ +#include + +/* ---------------------------------------------------------------------- +* Include project header files +* -------------------------------------------------------------------- */ +#include "math_helper.h" + +/** + * @brief Caluclation of SNR + * @param float* Pointer to the reference buffer + * @param float* Pointer to the test buffer + * @param uint32_t total number of samples + * @return float SNR + * The function Caluclates signal to noise ratio for the reference output + * and test output + */ + +float arm_snr_f32(float *pRef, float *pTest, uint32_t buffSize) +{ + float EnergySignal = 0.0; + float EnergyError = 0.0; + uint32_t i; + float SNR; + int temp; + int *test; + + for (i = 0; i < buffSize; i++) { + /* Checking for a NAN value in pRef array */ + test = (int *)(&pRef[i]); + temp = *test; + + if (temp == 0x7FC00000) { + return(0); + } + + /* Checking for a NAN value in pTest array */ + test = (int *)(&pTest[i]); + temp = *test; + + if (temp == 0x7FC00000) { + return(0); + } + EnergySignal += pRef[i] * pRef[i]; + EnergyError += (pRef[i] - pTest[i]) * (pRef[i] - pTest[i]); + } + + /* Checking for a NAN value in EnergyError */ + test = (int *)(&EnergyError); + temp = *test; + + if(temp == 0x7FC00000) { + return(0); + } + + SNR = 10 * log10f(EnergySignal / EnergyError); + return (SNR); +} + + +/** + * @brief Provide guard bits for Input buffer + * @param q15_t* Pointer to input buffer + * @param uint32_t blockSize + * @param uint32_t guard_bits + * @return none + * The function Provides the guard bits for the buffer + * to avoid overflow + */ + +void arm_provide_guard_bits_q15 (q15_t * input_buf, uint32_t blockSize, + uint32_t guard_bits) +{ + uint32_t i; + + for (i = 0; i < blockSize; i++) + { + input_buf[i] = input_buf[i] >> guard_bits; + } +} + +/** + * @brief Converts float to fixed in q12.20 format + * @param uint32_t number of samples in the buffer + * @return none + * The function converts floating point values to fixed point(q12.20) values + */ + +void arm_float_to_q12_20(float *pIn, q31_t * pOut, uint32_t numSamples) +{ + uint32_t i; + + for (i = 0; i < numSamples; i++) { + /* 1048576.0f corresponds to pow(2, 20) */ + pOut[i] = (q31_t) (pIn[i] * 1048576.0f); + + pOut[i] += pIn[i] > 0 ? 0.5 : -0.5; + + if (pIn[i] == (float) 1.0) { + pOut[i] = 0x000FFFFF; + } + } +} + +/** + * @brief Compare MATLAB Reference Output and ARM Test output + * @param q15_t* Pointer to Ref buffer + * @param q15_t* Pointer to Test buffer + * @param uint32_t number of samples in the buffer + * @return none + */ + +uint32_t arm_compare_fixed_q15(q15_t *pIn, q15_t * pOut, uint32_t numSamples) +{ + uint32_t i; + int32_t diff, diffCrnt = 0; + uint32_t maxDiff = 0; + + for (i = 0; i < numSamples; i++) + { + diff = pIn[i] - pOut[i]; + diffCrnt = (diff > 0) ? diff : -diff; + + if(diffCrnt > maxDiff) + { + maxDiff = diffCrnt; + } + } + + return(maxDiff); +} + +/** + * @brief Compare MATLAB Reference Output and ARM Test output + * @param q31_t* Pointer to Ref buffer + * @param q31_t* Pointer to Test buffer + * @param uint32_t number of samples in the buffer + * @return none + */ + +uint32_t arm_compare_fixed_q31(q31_t *pIn, q31_t * pOut, uint32_t numSamples) +{ + uint32_t i; + int32_t diff, diffCrnt = 0; + uint32_t maxDiff = 0; + + for (i = 0; i < numSamples; i++) + { + diff = pIn[i] - pOut[i]; + diffCrnt = (diff > 0) ? diff : -diff; + + if(diffCrnt > maxDiff) + { + maxDiff = diffCrnt; + } + } + + return(maxDiff); +} + +/** + * @brief Provide guard bits for Input buffer + * @param q31_t* Pointer to input buffer + * @param uint32_t blockSize + * @param uint32_t guard_bits + * @return none + * The function Provides the guard bits for the buffer + * to avoid overflow + */ + +void arm_provide_guard_bits_q31 (q31_t * input_buf, + uint32_t blockSize, + uint32_t guard_bits) +{ + uint32_t i; + + for (i = 0; i < blockSize; i++) + { + input_buf[i] = input_buf[i] >> guard_bits; + } +} + +/** + * @brief Provide guard bits for Input buffer + * @param q31_t* Pointer to input buffer + * @param uint32_t blockSize + * @param uint32_t guard_bits + * @return none + * The function Provides the guard bits for the buffer + * to avoid overflow + */ + +void arm_provide_guard_bits_q7 (q7_t * input_buf, + uint32_t blockSize, + uint32_t guard_bits) +{ + uint32_t i; + + for (i = 0; i < blockSize; i++) + { + input_buf[i] = input_buf[i] >> guard_bits; + } +} + + + +/** + * @brief Caluclates number of guard bits + * @param uint32_t number of additions + * @return none + * The function Caluclates the number of guard bits + * depending on the numtaps + */ + +uint32_t arm_calc_guard_bits (uint32_t num_adds) +{ + uint32_t i = 1, j = 0; + + if (num_adds == 1) + { + return (0); + } + + while (i < num_adds) + { + i = i * 2; + j++; + } + + return (j); +} + +/** + * @brief Converts Q15 to floating-point + * @param uint32_t number of samples in the buffer + * @return none + */ + +void arm_apply_guard_bits (float32_t * pIn, + uint32_t numSamples, + uint32_t guard_bits) +{ + uint32_t i; + + for (i = 0; i < numSamples; i++) + { + pIn[i] = pIn[i] * arm_calc_2pow(guard_bits); + } +} + +/** + * @brief Calculates pow(2, numShifts) + * @param uint32_t number of shifts + * @return pow(2, numShifts) + */ +uint32_t arm_calc_2pow(uint32_t numShifts) +{ + + uint32_t i, val = 1; + + for (i = 0; i < numShifts; i++) + { + val = val * 2; + } + + return(val); +} + + + +/** + * @brief Converts float to fixed q14 + * @param uint32_t number of samples in the buffer + * @return none + * The function converts floating point values to fixed point values + */ + +void arm_float_to_q14 (float *pIn, q15_t * pOut, + uint32_t numSamples) +{ + uint32_t i; + + for (i = 0; i < numSamples; i++) + { + /* 16384.0f corresponds to pow(2, 14) */ + pOut[i] = (q15_t) (pIn[i] * 16384.0f); + + pOut[i] += pIn[i] > 0 ? 0.5 : -0.5; + + if (pIn[i] == (float) 2.0) + { + pOut[i] = 0x7FFF; + } + + } + +} + + +/** + * @brief Converts float to fixed q30 format + * @param uint32_t number of samples in the buffer + * @return none + * The function converts floating point values to fixed point values + */ + +void arm_float_to_q30 (float *pIn, q31_t * pOut, + uint32_t numSamples) +{ + uint32_t i; + + for (i = 0; i < numSamples; i++) + { + /* 1073741824.0f corresponds to pow(2, 30) */ + pOut[i] = (q31_t) (pIn[i] * 1073741824.0f); + + pOut[i] += pIn[i] > 0 ? 0.5 : -0.5; + + if (pIn[i] == (float) 2.0) + { + pOut[i] = 0x7FFFFFFF; + } + } +} + +/** + * @brief Converts float to fixed q30 format + * @param uint32_t number of samples in the buffer + * @return none + * The function converts floating point values to fixed point values + */ + +void arm_float_to_q29 (float *pIn, q31_t * pOut, + uint32_t numSamples) +{ + uint32_t i; + + for (i = 0; i < numSamples; i++) + { + /* 1073741824.0f corresponds to pow(2, 30) */ + pOut[i] = (q31_t) (pIn[i] * 536870912.0f); + + pOut[i] += pIn[i] > 0 ? 0.5 : -0.5; + + if (pIn[i] == (float) 4.0) + { + pOut[i] = 0x7FFFFFFF; + } + } +} + + +/** + * @brief Converts float to fixed q28 format + * @param uint32_t number of samples in the buffer + * @return none + * The function converts floating point values to fixed point values + */ + +void arm_float_to_q28 (float *pIn, q31_t * pOut, + uint32_t numSamples) +{ + uint32_t i; + + for (i = 0; i < numSamples; i++) + { + /* 268435456.0f corresponds to pow(2, 28) */ + pOut[i] = (q31_t) (pIn[i] * 268435456.0f); + + pOut[i] += pIn[i] > 0 ? 0.5 : -0.5; + + if (pIn[i] == (float) 8.0) + { + pOut[i] = 0x7FFFFFFF; + } + } +} + +/** + * @brief Clip the float values to +/- 1 + * @param pIn input buffer + * @param numSamples number of samples in the buffer + * @return none + * The function converts floating point values to fixed point values + */ + +void arm_clip_f32 (float *pIn, uint32_t numSamples) +{ + uint32_t i; + + for (i = 0; i < numSamples; i++) + { + if(pIn[i] > 1.0f) + { + pIn[i] = 1.0; + } + else if( pIn[i] < -1.0f) + { + pIn[i] = -1.0; + } + + } +} + + + + diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/AudioStream.h b/hardware/firmware/audio_board/vendor/cores/teensy4/AudioStream.h index 90f0b0da..116daad0 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/AudioStream.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/AudioStream.h @@ -108,7 +108,7 @@ class AudioConnection #define AudioMemory(num) ({ \ - static DMAMEM audio_block_t data[num]; \ + static DMAMEM __attribute__((aligned(4))) audio_block_t data[num]; \ AudioStream::initialize_memory(data, num); \ }) diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/CrashReport.cpp b/hardware/firmware/audio_board/vendor/cores/teensy4/CrashReport.cpp index 4b3937a1..86241f57 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/CrashReport.cpp +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/CrashReport.cpp @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include #include @@ -68,16 +98,16 @@ size_t CrashReportClass::printTo(Print& p) const p.print("\t(MMARVALID) Accessed Address: 0x"); p.print(info->mmfar, HEX); if (info->mmfar < 32) { - p.print(" (nullptr)\n\t Check code at 0x"); + p.print(" (nullptr)\r\n\t Check code at 0x"); p.print(info->ret, HEX); - p.print(" - very likely a bug!\n\t Run \"addr2line -e mysketch.ino.elf 0x"); + p.print(" - very likely a bug!\r\n\t Run \"addr2line -e mysketch.ino.elf 0x"); p.print(info->ret, HEX); p.print("\" for filename & line number."); // TODO: in some perfect future, maybe we'll build part of the ELF debug_line // section (maybe just the .ino files) into CrashReport and be able to report // the actual filename and line number. Wouldn't that be awesome?! } else if ((info->mmfar >= (uint32_t)&_ebss) && (info->mmfar < (uint32_t)&_ebss + 32)) { - p.print(" (Stack problem)\n\t Check for stack overflows, array bounds, etc."); + p.print(" (Stack problem)\r\n\t Check for stack overflows, array bounds, etc."); } p.println(); } @@ -131,12 +161,12 @@ size_t CrashReportClass::printTo(Print& p) const p.print(" Temperature inside the chip was "); p.print(info->temp); - p.print(" °C\n"); + p.println(" °C"); // TODO: fault handler should read the CCM & PLL registers to log actual speed at crash p.print(" Startup CPU clock speed is "); p.print( F_CPU_ACTUAL/1000000); - p.print( "MHz\n"); + p.println( "MHz"); //p.print(" MMFAR: "); diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/CrashReport.h b/hardware/firmware/audio_board/vendor/cores/teensy4/CrashReport.h index 2763a79f..10caa887 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/CrashReport.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/CrashReport.h @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/DMAChannel.cpp b/hardware/firmware/audio_board/vendor/cores/teensy4/DMAChannel.cpp index bc4e8b77..b2fdb27d 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/DMAChannel.cpp +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/DMAChannel.cpp @@ -86,6 +86,8 @@ void DMAChannel::begin(bool force_initialization) *p++ = 0; *p++ = 0; *p++ = 0; + volatile uint8_t *dchpri = &DMA_DCHPRI3; + dchpri[(ch & 0x1C) | (3 - (ch & 0x03))] |= DMA_DCHPRI_ECP | DMA_DCHPRI_DPA; } void DMAChannel::release(void) diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/DMAChannel.h b/hardware/firmware/audio_board/vendor/cores/teensy4/DMAChannel.h index cb2c1a9e..0d3ebc1e 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/DMAChannel.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/DMAChannel.h @@ -376,12 +376,13 @@ class DMABaseClass { static inline void copy_tcd(TCD_t *dst, const TCD_t *src) { dst->CSR &= ~DMA_TCD_CSR_DONE; const uint32_t *p = (const uint32_t *)src; - uint32_t *q = (uint32_t *)dst; + volatile uint32_t *q = (uint32_t *)dst; uint32_t t1, t2, t3, t4; t1 = *p++; t2 = *p++; t3 = *p++; t4 = *p++; *q++ = t1; *q++ = t2; *q++ = t3; *q++ = t4; t1 = *p++; t2 = *p++; t3 = *p++; t4 = *p++; *q++ = t1; *q++ = t2; *q++ = t3; *q++ = t4; + asm volatile("dmb"); } }; diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/FS.h b/hardware/firmware/audio_board/vendor/cores/teensy4/FS.h index 93b7d753..33ecdcce 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/FS.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/FS.h @@ -272,7 +272,7 @@ class File final : public Stream { class FS { public: - FS() {} + constexpr FS() {} virtual ~FS() {} virtual File open(const char *filename, uint8_t mode = FILE_READ) = 0; virtual bool exists(const char *filepath) = 0; diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/HardwareSerial1.cpp b/hardware/firmware/audio_board/vendor/cores/teensy4/HardwareSerial1.cpp index 549e2245..f8b8e731 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/HardwareSerial1.cpp +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/HardwareSerial1.cpp @@ -1,4 +1,3 @@ - /* Teensyduino Core Library * http://www.pjrc.com/teensy/ * Copyright (c) 2019 PJRC.COM, LLC. diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/MIDIUSB.h b/hardware/firmware/audio_board/vendor/cores/teensy4/MIDIUSB.h index 09786e40..505987ba 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/MIDIUSB.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/MIDIUSB.h @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #ifndef MIDIUSB_h #define MIDIUSB_h diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Const.h b/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Const.h new file mode 100644 index 00000000..75a9bc59 --- /dev/null +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Const.h @@ -0,0 +1,159 @@ +// MTP.cpp - Teensy MTP Responder library +// Copyright (C) 2017 Fredrik Hubinette +// +// With updates from MichaelMC and Yoong Hor Meng +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// modified for SDFS by WMXZ + +#pragma once + +// Container Types +#define MTP_CONTAINER_TYPE_UNDEFINED 0 +#define MTP_CONTAINER_TYPE_COMMAND 1 +#define MTP_CONTAINER_TYPE_DATA 2 +#define MTP_CONTAINER_TYPE_RESPONSE 3 +#define MTP_CONTAINER_TYPE_EVENT 4 + +// Container Offsets +#define MTP_CONTAINER_LENGTH_OFFSET 0 +#define MTP_CONTAINER_TYPE_OFFSET 4 +#define MTP_CONTAINER_CODE_OFFSET 6 +#define MTP_CONTAINER_TRANSACTION_ID_OFFSET 8 +#define MTP_CONTAINER_PARAMETER_OFFSET 12 +#define MTP_CONTAINER_HEADER_SIZE 12 + +// MTP Operation Codes +#define MTP_OPERATION_GET_DEVICE_INFO 0x1001 +#define MTP_OPERATION_OPEN_SESSION 0x1002 +#define MTP_OPERATION_CLOSE_SESSION 0x1003 +#define MTP_OPERATION_GET_STORAGE_IDS 0x1004 +#define MTP_OPERATION_GET_STORAGE_INFO 0x1005 +#define MTP_OPERATION_GET_NUM_OBJECTS 0x1006 +#define MTP_OPERATION_GET_OBJECT_HANDLES 0x1007 +#define MTP_OPERATION_GET_OBJECT_INFO 0x1008 +#define MTP_OPERATION_GET_OBJECT 0x1009 +#define MTP_OPERATION_GET_THUMB 0x100A +#define MTP_OPERATION_DELETE_OBJECT 0x100B +#define MTP_OPERATION_SEND_OBJECT_INFO 0x100C +#define MTP_OPERATION_SEND_OBJECT 0x100D +#define MTP_OPERATION_INITIATE_CAPTURE 0x100E +#define MTP_OPERATION_FORMAT_STORE 0x100F +#define MTP_OPERATION_RESET_DEVICE 0x1010 +#define MTP_OPERATION_SELF_TEST 0x1011 +#define MTP_OPERATION_SET_OBJECT_PROTECTION 0x1012 +#define MTP_OPERATION_POWER_DOWN 0x1013 +#define MTP_OPERATION_GET_DEVICE_PROP_DESC 0x1014 +#define MTP_OPERATION_GET_DEVICE_PROP_VALUE 0x1015 +#define MTP_OPERATION_SET_DEVICE_PROP_VALUE 0x1016 +#define MTP_OPERATION_RESET_DEVICE_PROP_VALUE 0x1017 +#define MTP_OPERATION_TERMINATE_OPEN_CAPTURE 0x1018 +#define MTP_OPERATION_MOVE_OBJECT 0x1019 +#define MTP_OPERATION_COPY_OBJECT 0x101A +#define MTP_OPERATION_GET_PARTIAL_OBJECT 0x101B +#define MTP_OPERATION_INITIATE_OPEN_CAPTURE 0x101C +#define MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED 0x9801 +#define MTP_OPERATION_GET_OBJECT_PROP_DESC 0x9802 +#define MTP_OPERATION_GET_OBJECT_PROP_VALUE 0x9803 +#define MTP_OPERATION_SET_OBJECT_PROP_VALUE 0x9804 +#define MTP_OPERATION_GET_OBJECT_PROP_LIST 0x9805 +#define MTP_OPERATION_SET_OBJECT_PROP_LIST 0x9806 +#define MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC 0x9807 +#define MTP_OPERATION_SEND_OBJECT_PROP_LIST 0x9808 +#define MTP_OPERATION_GET_OBJECT_REFERENCES 0x9810 +#define MTP_OPERATION_SET_OBJECT_REFERENCES 0x9811 +#define MTP_OPERATION_SKIP 0x9820 + +#define MTP_PROPERTY_STORAGE_ID 0xDC01 +#define MTP_PROPERTY_OBJECT_FORMAT 0xDC02 +#define MTP_PROPERTY_PROTECTION_STATUS 0xDC03 +#define MTP_PROPERTY_OBJECT_SIZE 0xDC04 +#define MTP_PROPERTY_OBJECT_FILE_NAME 0xDC07 +#define MTP_PROPERTY_DATE_CREATED 0xDC08 +#define MTP_PROPERTY_DATE_MODIFIED 0xDC09 +#define MTP_PROPERTY_PARENT_OBJECT 0xDC0B +#define MTP_PROPERTY_PERSISTENT_UID 0xDC41 +#define MTP_PROPERTY_NAME 0xDC44 + +#define MTP_EVENT_UNDEFINED 0x4000 +#define MTP_EVENT_CANCEL_TRANSACTION 0x4001 +#define MTP_EVENT_OBJECT_ADDED 0x4002 +#define MTP_EVENT_OBJECT_REMOVED 0x4003 +#define MTP_EVENT_STORE_ADDED 0x4004 +#define MTP_EVENT_STORE_REMOVED 0x4005 +#define MTP_EVENT_DEVICE_PROP_CHANGED 0x4006 +#define MTP_EVENT_OBJECT_INFO_CHANGED 0x4007 +#define MTP_EVENT_DEVICE_INFO_CHANGED 0x4008 +#define MTP_EVENT_REQUEST_OBJECT_TRANSFER 0x4009 +#define MTP_EVENT_STORE_FULL 0x400A +#define MTP_EVENT_DEVICE_RESET 0x400B +#define MTP_EVENT_STORAGE_INFO_CHANGED 0x400C +#define MTP_EVENT_CAPTURE_COMPLETE 0x400D +#define MTP_EVENT_UNREPORTED_STATUS 0x400E +#define MTP_EVENT_OBJECT_PROP_CHANGED 0xC801 +#define MTP_EVENT_OBJECT_PROP_DESC_CHANGED 0xC802 +#define MTP_EVENT_OBJECT_REFERENCES_CHANGED 0xC803 + +// Responses +#define MTP_RESPONSE_UNDEFINED 0x2000 +#define MTP_RESPONSE_OK 0x2001 +#define MTP_RESPONSE_GENERAL_ERROR 0x2002 +#define MTP_RESPONSE_SESSION_NOT_OPEN 0x2003 +#define MTP_RESPONSE_INVALID_TRANSACTION_ID 0x2004 +#define MTP_RESPONSE_OPERATION_NOT_SUPPORTED 0x2005 +#define MTP_RESPONSE_PARAMETER_NOT_SUPPORTED 0x2006 +#define MTP_RESPONSE_INCOMPLETE_TRANSFER 0x2007 +#define MTP_RESPONSE_INVALID_STORAGE_ID 0x2008 +#define MTP_RESPONSE_INVALID_OBJECT_HANDLE 0x2009 +#define MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED 0x200A +#define MTP_RESPONSE_INVALID_OBJECT_FORMAT_CODE 0x200B +#define MTP_RESPONSE_STORAGE_FULL 0x200C +#define MTP_RESPONSE_OBJECT_WRITE_PROTECTED 0x200D +#define MTP_RESPONSE_STORE_READ_ONLY 0x200E +#define MTP_RESPONSE_ACCESS_DENIED 0x200F +#define MTP_RESPONSE_NO_THUMBNAIL_PRESENT 0x2010 +#define MTP_RESPONSE_SELF_TEST_FAILED 0x2011 +#define MTP_RESPONSE_PARTIAL_DELETION 0x2012 +#define MTP_RESPONSE_STORE_NOT_AVAILABLE 0x2013 +#define MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED 0x2014 +#define MTP_RESPONSE_NO_VALID_OBJECT_INFO 0x2015 +#define MTP_RESPONSE_INVALID_CODE_FORMAT 0x2016 +#define MTP_RESPONSE_UNKNOWN_VENDOR_CODE 0x2017 +#define MTP_RESPONSE_CAPTURE_ALREADY_TERMINATED 0x2018 +#define MTP_RESPONSE_DEVICE_BUSY 0x2019 +#define MTP_RESPONSE_INVALID_PARENT_OBJECT 0x201A +#define MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT 0x201B +#define MTP_RESPONSE_INVALID_DEVICE_PROP_VALUE 0x201C +#define MTP_RESPONSE_INVALID_PARAMETER 0x201D +#define MTP_RESPONSE_SESSION_ALREADY_OPEN 0x201E +#define MTP_RESPONSE_TRANSACTION_CANCELLED 0x201F +#define MTP_RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED 0x2020 +#define MTP_RESPONSE_INVALID_OBJECT_PROP_CODE 0xA801 +#define MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT 0xA802 +#define MTP_RESPONSE_INVALID_OBJECT_PROP_VALUE 0xA803 +#define MTP_RESPONSE_INVALID_OBJECT_REFERENCE 0xA804 +#define MTP_RESPONSE_GROUP_NOT_SUPPORTED 0xA805 +#define MTP_RESPONSE_INVALID_DATASET 0xA806 +#define MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED 0xA807 +#define MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED 0xA808 +#define MTP_RESPONSE_OBJECT_TOO_LARGE 0xA809 +#define MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED 0xA80A + diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Storage.cpp b/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Storage.cpp new file mode 100644 index 00000000..8c3837b8 --- /dev/null +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Storage.cpp @@ -0,0 +1,1930 @@ +// Storage.cpp - Teensy MTP Responder library +// Copyright (C) 2017 Fredrik Hubinette +// +// With updates from MichaelMC and Yoong Hor Meng +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// modified for SDFS by WMXZ +// Nov 2020 adapted to SdFat-beta / SD combo + +#if defined(USB_MTPDISK) || defined(USB_MTPDISK_SERIAL) + +#include "core_pins.h" +#include "usb_dev.h" +#include "usb_serial.h" + +#include "MTP_Teensy.h" +#include "MTP_Storage.h" +#include +#if defined __has_include +#if __has_include() +#include +#endif +#endif + +//============================================================================= +// Define some static data +//============================================================================= +#if MTP_RECORD_BLOCKS +MTPStorage::RecordBlock MTPStorage::recordBlocks_[MTP_RECORD_BLOCKS] DMAMEM; +MTPStorage::RecordBlockInfo MTPStorage::recordBlocksInfo_[MTP_RECORD_BLOCKS] = {{0}}; +#endif + +#define DEBUG 0 + +#if DEBUG > 0 +#define USE_DBG_MACROS 1 +#else +#define USE_DBG_MACROS 0 +#endif + +#define DBG_FILE "Storage.cpp" + +#if USE_DBG_MACROS == 1 +static void dbgPrint(uint16_t line) { + MTP_class::PrintStream()->print(F("DBG_FAIL: ")); + MTP_class::PrintStream()->print(F(DBG_FILE)); + MTP_class::PrintStream()->write('.'); + MTP_class::PrintStream()->println(line); +} + +#define DBG_PRINT_IF(b) \ + if (b) { \ + MTP_class::PrintStream()->print(F(__FILE__)); \ + MTP_class::PrintStream()->println(__LINE__); \ + } +#define DBG_HALT_IF(b) \ + if (b) { \ + MTP_class::PrintStream()->print(F("DBG_HALT ")); \ + MTP_class::PrintStream()->print(F(__FILE__)); \ + MTP_class::PrintStream()->println(__LINE__); \ + while (true) { \ + } \ + } +#define DBG_FAIL_MACRO dbgPrint(__LINE__); +#else // USE_DBG_MACROS +#define DBG_FAIL_MACRO +#define DBG_PRINT_IF(b) +#define DBG_HALT_IF(b) +#endif // USE_DBG_MACROS + +#if DEBUG > 1 +#define DBGPrintf(...) MTP_class::PrintStream()->printf(__VA_ARGS__) +#define DBGFlush() MTP_class::PrintStream()->flush() + +#else +#define DBGPrintf(...) +#define DBGFlush() +#endif + +#define sd_getName(x, y, n) strlcpy(y, x.name(), n) + +#define indexFile "/mtpindex.dat" + +//============================================================================= +// Experiment using Memory Filesystem specific for this case... +// Sort of based off of work of BogusFS as well by Frank's and my MemFile. +// But then extended to allow the FIle to grow and Shrink... +//============================================================================= + +class IndexMemFS; + +class MemIndexFile : public FileImpl +{ +public: + //Currently we will extend file if you try to extend beyond the end of current + // note in our case we only writing like 2048 chunks. At some point may allocate + // multiples of these. + //So..just let the mcu crash if something is wrong. + virtual size_t write(const void *buf, size_t nbyte); + virtual int peek() { //""Returns the next character" + if (_storage_ptr == nullptr) return 0; + int p = _current_file_index + 1; + if (p > (int)_storage_size) return -1; + return *(_storage_ptr + p); + } + virtual int available() { + if (_storage_ptr == nullptr) return 0; + int s = _storage_size - _current_file_index; + if (s < 0) s = 0; + return s; + } + virtual void flush() {} + virtual size_t read(void *buf, size_t nbyte); + virtual bool truncate(uint64_t size); + virtual bool seek(uint64_t pos, int mode = SeekSet); + virtual uint64_t position() { + if (_storage_ptr == nullptr) return 0; + return _current_file_index; + } + virtual uint64_t size() { + if (_storage_ptr == nullptr) return 0; + return _storage_size; + } + virtual void close(); + virtual bool isOpen() { + return _open; + } + virtual operator bool() { + return _open; + } + virtual const char * name() { + return _storage_ptr != nullptr ? "Index" : nullptr; + } + virtual boolean isDirectory(void) { + return false; + } + virtual File openNextFile(uint8_t mode) { + return File(); + } + virtual void rewindDirectory(void) { + } + bool getCreateTime(DateTimeFields &tm) { + return false; + } + bool getModifyTime(DateTimeFields &tm) { + return false; + } + bool setCreateTime(const DateTimeFields &tm) { + return false; + } + bool setModifyTime(const DateTimeFields &tm) { + return false; + } +protected: + friend class IndexMemFS; + MemIndexFile() {_open = true;} + uint8_t *_storage_ptr = nullptr; + uint32_t _current_file_index = 0; + uint32_t _storage_size = 0; + bool _open = false; +}; + +//Currently we will extend file if you try to extend beyond the end of current +// note in our case we only writing like 2048 chunks. At some point may allocate +// multiples of these. +//So..just let the mcu crash if something is wrong. +size_t MemIndexFile::write(const void *buf, size_t nbyte) { + if ((_current_file_index + nbyte) > _storage_size) { + // We need to allocate or reallocate ... + uint32_t new_size = _current_file_index + nbyte; + if (_storage_ptr == nullptr) { + #if defined(__IMXRT1062__) + _storage_ptr = (uint8_t*)extmem_malloc(new_size); + #else + _storage_ptr = (uint8_t*)malloc(new_size); + #endif + Serial.printf("\t$$%p = malloc(%u)\n", _storage_ptr, new_size);// hard coded, dont care just give me the file + if (!_storage_ptr) return -1; + _storage_size = new_size; + } else { + #if defined(__IMXRT1062__) + uint8_t *new_storage_ptr = (uint8_t*)extmem_realloc(_storage_ptr, new_size); + #else + uint8_t *new_storage_ptr = (uint8_t*)realloc(_storage_ptr, new_size); + #endif + Serial.printf("\t$$%p = realloc(%p, %u)\n", new_storage_ptr, _storage_ptr, new_size); // hard coded, dont care just give me the file + if (!new_storage_ptr) return -1; + _storage_ptr = new_storage_ptr; + _storage_size = new_size; + } + } + DBGPrintf("!!!MemIndexFile::write (%p %u): %p\n", buf, nbyte, _storage_ptr + _current_file_index); // hard coded, dont care just give me the file + if (nbyte > 0) memcpy(_storage_ptr + _current_file_index, buf, nbyte); + _current_file_index += nbyte; + return nbyte; +} + +size_t MemIndexFile::read(void *buf, size_t nbyte) { + DBGPrintf("!!!MemIndexFile::read(%p, %u): %p\n", buf, nbyte, _storage_ptr + _current_file_index); // hard coded, dont care just give me the file + if (_storage_ptr == nullptr) return 0; + if (_current_file_index + nbyte > (unsigned)_storage_size) nbyte = _storage_size - _current_file_index; + if (nbyte > 0) memcpy(buf, _storage_ptr + _current_file_index, nbyte); + _current_file_index += nbyte; + return nbyte; +} + +bool MemIndexFile::truncate(uint64_t size) { + DBGPrintf("!!!MemIndexFile::truncate %u\n", (uint32_t)size); // hard coded, dont care just give me the file + if (_storage_ptr) { + #if defined(__IMXRT1062__) + uint8_t *new_storage_ptr = (uint8_t*)extmem_realloc(_storage_ptr, size); + #else + uint8_t *new_storage_ptr = (uint8_t*)realloc(_storage_ptr, size); + #endif + if (!new_storage_ptr) return false; + _storage_ptr = new_storage_ptr; + _storage_size = size; + return true; + } + return false; +} + +bool MemIndexFile::seek(uint64_t pos, int mode) { + if ((_storage_ptr == nullptr) && (pos > 0)) return false; + int p = pos; + if (mode == SeekCur) p = _current_file_index + pos; + else if (mode == SeekEnd) p = (int)(_current_file_index + _storage_size) - (int)pos; + if (p < 0 || p > (int)_storage_size) return false; + _current_file_index = p; + DBGPrintf("!!!MemIndexFile::seek %u %d %p\n", (uint32_t)pos, mode, _storage_ptr + _current_file_index); // hard coded, dont care just give me the file + return true; +} + +void MemIndexFile::close() { + Serial.printf("!!!MemIndexFile::close\n"); // hard coded, dont care just give me the file + #if defined(__IMXRT1062__) + if (_storage_ptr) extmem_free(_storage_ptr); + #else + if (_storage_ptr) free(_storage_ptr); + #endif + + _storage_ptr = nullptr; + _storage_size = 0; + _current_file_index = 0; + _open = false; +} + + +class IndexMemFS : public FS +{ +public: + IndexMemFS() { } + + File open(const char *name, uint8_t mode) { + Serial.println("!!!IndexMemFS::open"); // hard coded, dont care just give me the file + _file = File(new MemIndexFile()); + return _file; + } + + bool exists(const char *filepath) { + return true; + } + bool mkdir(const char *filepath) { + return false; + } + bool rename(const char *oldfilepath, const char *newfilepath) { + return false; + } + bool remove(const char *filepath) { + if (_file) _file.close(); // this will free up the memory... + return false; + } + bool rmdir(const char *filepath) { + return false; + } + uint64_t usedSize() { + return _file.size(); + } + uint64_t totalSize() { + return _file.size(); + } + +protected: + File _file; +}; + +IndexMemFS g_indexMemFS; + +// TODO: +// support serialflash +// partial object fetch/receive +// events (notify usb host when local storage changes) (But, this seems too +// difficult) + +// These should probably be weak. +void mtp_yield() {} +void mtp_lock_storage(bool lock) {} + + +void MTPStorage::CloseIndex() +{ + mtp_lock_storage(true); + if (user_index_file_) { + // maybe truncate the file + index_.seek(0, SeekSet); + index_.truncate(); + } else if (index_) { + index_.close(); + } + mtp_lock_storage(false); + index_generated_ = false; + index_entries_ = 0; +} + +void MTPStorage::OpenIndex() +{ + if (index_) return; // only once + mtp_lock_storage(true); + #if MTP_RECORD_BLOCKS + if (index_file_storage_ != INDEX_STORE_MEM_FILE) { + index_ = open(index_file_storage_, indexFile, FILE_WRITE_BEGIN); + if (!index_) { + MTP_class::PrintStream()->printf("Failed to open Index file on storage number %d try memory File\n", index_file_storage_); + index_ = g_indexMemFS.open(indexFile, FILE_WRITE_BEGIN); + } + } else + index_ = g_indexMemFS.open(indexFile, FILE_WRITE_BEGIN); + #else + // We only support the memory index on those machines which we have setup to use MTP_RECORD_BLOCKS + index_ = open(index_file_storage_, indexFile, FILE_WRITE_BEGIN); + #endif + + if (!index_) MTP_class::PrintStream()->println("cannot open Index file"); + + mtp_lock_storage(false); + user_index_file_ = false; // opened up default file so make sure off + DBGPrintf("MTPStorage::OpenIndex Record Size:%u %u\n", sizeof(Record), sizeof(RecordFixed)); + #if MTP_RECORD_BLOCKS + DBGPrintf("MTPStorage::OpenIndex:\n"); + for(uint8_t i = 0; i < MTP_RECORD_BLOCKS; i++) { + DBGPrintf(" %u: %d %u %u\n", i, + recordBlocksInfo_[i].block_index, + recordBlocksInfo_[i].last_cycle_used, + recordBlocksInfo_[i].dirty); + } + memset(recordBlocksInfo_, 0, sizeof(recordBlocksInfo_)); + memset(recordBlocks_, 0, sizeof(recordBlocks_)); + map_objectid_to_block_cycle_ = 0; + for(uint8_t i = 1; i < MTP_RECORD_BLOCKS; i++) { + recordBlocksInfo_[i].block_index = 0xffff; + } + maxrecordBlockWritten_ = -1; // make sure we assume nothing written. + #endif +} + + +void MTPStorage::ResetIndex() +{ + if (!index_) return; + CloseIndex(); + all_scanned_ = false; + open_file_ = 0xFFFFFFFEUL; +} + + +// debug code +void MTPStorage::printClearRecordReadWriteCounts() { + MTP_class::PrintStream()->println("*** Storage Record Read/Write counts ***\n"); + MTP_class::PrintStream()->printf(" Writes: %u to FS:%u\n", debug_write_record_count_, debug_fs_write_record_count_); + MTP_class::PrintStream()->printf(" Reads: %u from FS:%u\n", debug_read_record_count_, debug_fs_read_record_count_); + debug_write_record_count_ = debug_fs_write_record_count_ = 0; + debug_read_record_count_ = debug_fs_read_record_count_ = 0; + + #if MTP_RECORD_BLOCKS + MTP_class::PrintStream()->printf("Record Info Blocks (index\tblock_index\tlast hit\tdirty):\n"); + for(uint8_t i = 0; i < MTP_RECORD_BLOCKS; i++) { + uint16_t delta_cycles = map_objectid_to_block_cycle_ - recordBlocksInfo_[i].last_cycle_used; + MTP_class::PrintStream()->printf(" %u:\t%u\t%u(%u)\t%u\n", i, + recordBlocksInfo_[i].block_index, + recordBlocksInfo_[i].last_cycle_used, delta_cycles, + recordBlocksInfo_[i].dirty); + if (recordBlocksInfo_[i].block_index != 0xffff) { + MTP_class::PrintStream()->printf(" Count:%u NF: %u\n", recordBlocks_[i].recordCount, recordBlocks_[i].dataIndexNextFree); + + #ifdef _memoryhexdump_h_ + MemoryHexDump(Serial, recordBlocks_[i].data, BLOCK_SIZE_DATA, true, nullptr, -1, 0); + #endif + for (uint8_t j=0; j < recordBlocks_[i].recordCount; j++) { + uint16_t ro = recordBlocks_[i].recordOffsets[j]; + MTP_class::PrintStream()->printf("%u(%x):", j, ro); + if (ro >= BLOCK_SIZE_DATA) { + MTP_class::PrintStream()->printf("error offset > size\n"); + } else { + Record *pr = (Record *)&recordBlocks_[i].data[ro]; + printRecordIncludeName(j, pr); + } + } + + MTP_class::PrintStream()->printf("-----------------------------------------------\n"); + } + + } + + #endif + + + +} + +#if MTP_RECORD_BLOCKS +//============================================================================= +// Version that compresses objects into blocks. +//============================================================================= +// function to take care to make sure the block we want is in +// the block cache... +void MTPStorage::ClearRecordBlock(uint8_t index) { + // overkill for now + DBGPrintf("$$$ClearRB(%u) enter\n", index); + memset(&recordBlocks_[index], 0, sizeof(RecordBlock)); +} + +uint8_t MTPStorage::CacheRecordBlock(uint16_t block_index) +{ + map_objectid_to_block_cycle_++; + DBGPrintf("CacheRB(%u %u) enter\n", map_objectid_to_block_cycle_, block_index); +#if MTP_RECORD_BLOCKS > 1 + int biIndex = -1; + int biIndexReplace = -1; + uint16_t max_cycles = 0; + for (int index=0; index < MTP_RECORD_BLOCKS; index++) { + if (recordBlocksInfo_[index].block_index == block_index) { + biIndex = index; + break; + } else if (recordBlocksInfo_[index].block_index == 0xffff) { + ClearRecordBlock(index); // make sure we have cleared out the block + recordBlocksInfo_[index].block_index = block_index; + biIndex = index; + DBGPrintf(" >> Claim unused block: %u\n", index); + break; + } else { + uint16_t delta_cycles = map_objectid_to_block_cycle_ - recordBlocksInfo_[index].last_cycle_used; + if (delta_cycles > max_cycles) { + max_cycles = delta_cycles; + biIndexReplace = index; + } + } + } + if (biIndex != -1) { + DBGPrintf(" >> cache return: %u\n", biIndex); + recordBlocksInfo_[biIndex].last_cycle_used = map_objectid_to_block_cycle_; + return biIndex; + } + biIndex = biIndexReplace; + #else + #define biIndex 0 + if (recordBlocksInfo_[0].block_index == block_index) return 0; + #endif + // not in cache and we know cache is full + // see if we need to write out the current contents of the block + if (recordBlocksInfo_[biIndex].dirty) { + DBGPrintf(" >> cache write: %u %u MPI:%d\n", biIndex, recordBlocksInfo_[biIndex].block_index, maxrecordBlockWritten_); + debug_fs_write_record_count_++; + + uint64_t seek_pos = recordBlocksInfo_[biIndex].block_index * sizeof(RecordBlock); + //MTP_class::PrintStream()->printf(">> cache write: %u %u %d %llu\n", biIndex, recordBlocksInfo_[biIndex].block_index, maxrecordBlockWritten_, seek_pos); + + int block_index_m1 = recordBlocksInfo_[biIndex].block_index - 1; + if (block_index_m1 > maxrecordBlockWritten_) { + //MTP_class::PrintStream()->printf("\tExtend index\n"); + seek_pos = (maxrecordBlockWritten_+1) * sizeof(RecordBlock); + if (!index_.seek(seek_pos, SeekSet)) + MTP_class::PrintStream()->printf("$$$ Failed to seek to extend Index record:%u addr:%llu\n", + maxrecordBlockWritten_+1, seek_pos); + while (block_index_m1 > maxrecordBlockWritten_) { + maxrecordBlockWritten_++; + for (int index=0; index < MTP_RECORD_BLOCKS; index++) { + if (recordBlocksInfo_[index].block_index == maxrecordBlockWritten_) { + size_t bytes_written = index_.write((char *)&recordBlocks_[index], sizeof(RecordBlock)); + recordBlocksInfo_[index].dirty = false; // we wrote the data out. + if ((bytes_written != sizeof(RecordBlock))) + MTP_class::PrintStream()->printf(F("$$$ Failed to write Index record: %u bytes written: %u\n"), + recordBlocksInfo_[index].block_index, bytes_written); + break; + } + } + } + } else { + + bool seek_ok = index_.seek(seek_pos, SeekSet); + if (!seek_ok) { + MTP_class::PrintStream()->printf(F("$$$ Failed to seek for write Index record: %u addr:%llu Cur: %llu\n"), + recordBlocksInfo_[biIndex].block_index, seek_pos, index_.position()); + } + } + + size_t bytes_written = index_.write((char *)&recordBlocks_[biIndex], sizeof(RecordBlock)); + if (bytes_written != sizeof(RecordBlock)) { + MTP_class::PrintStream()->printf(F("$$$ Failed to write Index record: %u bytes written: %u\n"), recordBlocksInfo_[biIndex].block_index, bytes_written); + } + if (recordBlocksInfo_[biIndex].block_index > maxrecordBlockWritten_) + maxrecordBlockWritten_ = recordBlocksInfo_[biIndex].block_index; + } + // Now read in the previous contents + recordBlocksInfo_[biIndex].dirty = false; + recordBlocksInfo_[biIndex].block_index = block_index; + recordBlocksInfo_[biIndex].last_cycle_used = map_objectid_to_block_cycle_; + if (block_index <= maxrecordBlockWritten_) { + debug_fs_read_record_count_++; + DBGPrintf(" >> cache read: %u %u\n", biIndex, block_index); + uint64_t seek_pos = block_index * sizeof(RecordBlock); + bool seek_ok = index_.seek(seek_pos, SeekSet); + if (!seek_ok) { + MTP_class::PrintStream()->printf(F("$$$ Failed to seek for read Index record: %u addr:%llu Cur: %llu\n"), + recordBlocksInfo_[biIndex].block_index, seek_pos, index_.position()); + } + size_t bytes_read = index_.read((char *)&recordBlocks_[biIndex], sizeof(RecordBlock)); + if (!seek_ok || (bytes_read != sizeof(RecordBlock))) { + MTP_class::PrintStream()->printf(F("$$$ Failed to read Index record: %u seek:%u bytes Read: %u\n"), block_index, seek_ok, bytes_read); + } + } else { + + DBGPrintf(" >> clear out new record: %u %u\n", biIndex, block_index); + ClearRecordBlock(biIndex); // make sure we have cleared out the block + } + + return biIndex; +} + +uint32_t MTPStorage::AppendIndexRecord(const Record &r) +{ + + uint32_t new_record = index_entries_++; + if (new_record < MTPD_MAX_FILESYSTEMS) { + WriteIndexRecord(new_record, r); + return new_record; + } + + mtp_lock_storage(true); + debug_write_record_count_++; + // so now dealing with normal stuff + uint16_t block_index = ((new_record - MTPD_MAX_FILESYSTEMS) >> 6); // lower 6 bits is index into block + uint8_t record_index = (new_record - MTPD_MAX_FILESYSTEMS) & 0x3f; + uint8_t biIndex = CacheRecordBlock(block_index); // lets map to block index; + DBGPrintf("Cache Append IR: (%u %u): %u %s\n", block_index, record_index, biIndex, r.name); + + // now see if our record will fit with enough fudge. + uint16_t size_new_name = (strlen(r.name) + 4) & 0xfffc; // size is strlen + \0 and rounded up to 4 byte increments. + + if ((recordBlocks_[biIndex].dataIndexNextFree + sizeof(RecordFixed) + + size_new_name + BLOCK_SIZE_NAME_FUDGE) > BLOCK_SIZE_DATA) { + // new item won't fit, so increment to next on... + block_index++; // need to go to next block + record_index = 0; + new_record = (block_index << 6) + MTPD_MAX_FILESYSTEMS; // round up to start of next block + index_entries_ = new_record + 1; + + biIndex = CacheRecordBlock(block_index); // lets map to block index; + DBGPrintf(" >> $$$ new block %u %u bi:%u\n", new_record, block_index, biIndex); + } + + // now move the append part out of the write record + // new record to add to end + recordBlocks_[biIndex].recordCount++; + recordBlocks_[biIndex].recordOffsets[record_index] = recordBlocks_[biIndex].dataIndexNextFree; + DBGPrintf(" >> AIR new C:%u O:%u\n", recordBlocks_[biIndex].recordCount, recordBlocks_[biIndex].recordOffsets[record_index]); + uint16_t record_data_index = recordBlocks_[biIndex].recordOffsets[record_index]; + RecordFixed *prBlock = (RecordFixed*)&recordBlocks_[biIndex].data[record_data_index]; + DBGPrintf(" >> %p = %p\n", prBlock, &r); + + RecordFixed *pr = (RecordFixed*)&r; // use structure without the name... + *prBlock = *pr; + recordBlocksInfo_[biIndex].dirty = true; + strcpy(prBlock->name, pr->name); + recordBlocks_[biIndex].dataIndexNextFree += sizeof(RecordFixed) + size_new_name; // increment to next position add slop for alignment up + mtp_lock_storage(false); + + return new_record; +} + + +bool MTPStorage::WriteIndexRecord(uint32_t i, const Record &r) +{ + OpenIndex(); + mtp_lock_storage(true); + bool write_succeeded = true; + if (i < MTPD_MAX_FILESYSTEMS) { + // all we need is the first child pointer and if it was scanned + store_first_child_[i] = r.child; + store_scanned_[i] = r.scanned; + } else { + i -= MTPD_MAX_FILESYSTEMS; + debug_write_record_count_++; + + // now lets convert this to block and index within block. + uint16_t block_index = i >> 6; // lower 6 bits is index into block + uint8_t record_index = i & 0x3f; + DBGPrintf("CacheWIR: %u(%u %u): %s\n", i, block_index, record_index, r.name); DBGFlush(); + + int size_new_name = (strlen(r.name) + 4) & 0xfffc; // size is strlen + \0 and rounded up to 4 byte increments. + + RecordFixed *pr = (RecordFixed*)&r; // use structure without the name... + + uint8_t biIndex = CacheRecordBlock(block_index); + RecordBlockInfo *prbinfo = &recordBlocksInfo_[biIndex]; + RecordBlock *prb = &recordBlocks_[biIndex]; + + uint16_t record_data_index = prb->recordOffsets[record_index]; + + // maybe should assert (record_index < prb->recordCount) { + DBGPrintf(" >> WIR Update\n"); + // So this record has been stored before; + RecordFixed *prBlock = (RecordFixed*)&prb->data[record_data_index]; + int size_old_name = (strlen(prBlock->name) + 4) & 0xfffc; + if (memcmp(pr, prBlock, sizeof(RecordFixed)) != 0) { + *prBlock = *pr; + prbinfo->dirty = true; + } + if (strcmp(r.name, prBlock->name) != 0) { + // name change ... arg... May have to muck up index list for items after us + prbinfo->dirty = true; + uint16_t rdiNext = prb->recordOffsets[record_index + 1]; + int delta_size = size_new_name - size_new_name; + if (delta_size) { + // the sizes rounded up to 4 byte incmenets has changed + if ((prb->dataIndexNextFree + delta_size) >= (int)BLOCK_SIZE_DATA) { + // arg not enough room in block + delta_size = BLOCK_SIZE_DATA - (prb->dataIndexNextFree + 4); // fudge + } + // can bypass some work if we are the last item. + if (record_index != (prb->recordCount - 1)) { + // Need to move the data + if (delta_size) { + memmove(&prb->data[rdiNext + delta_size], &prb->data[rdiNext], delta_size); + for (uint8_t i = record_index + 1; i < prb->recordCount; i++) prb->recordOffsets[i] += delta_size; + } + } + prb->dataIndexNextFree += delta_size; + rdiNext += delta_size; // + // need to fix after strncpy as it may not zero terminate. + strncpy(prBlock->name, r.name, size_old_name + delta_size - 1); + prBlock->name[size_old_name + delta_size - 1] = '\0'; // strncpy does not alway 0 terminate.. + } else { + // simple case fits in same space as was previously allocated. + strcpy(prBlock->name, r.name); + } + } + } + mtp_lock_storage(false); + return write_succeeded; +} + + + +MTPStorage::Record MTPStorage::ReadIndexRecord(uint32_t i) +{ + + Record ret; + //memset(&ret, 0, sizeof(ret)); + if (i > index_entries_) { + memset(&ret, 0, sizeof(ret)); + return ret; + } + OpenIndex(); // bugbug is this valid, if not open should error otu... maybe... + mtp_lock_storage(true); + if (i < MTPD_MAX_FILESYSTEMS) { + // Build it on the fly... + ret.store = i; + ret.parent = 0xFFFFUL; + ret.sibling = 0; + ret.child = store_first_child_[i]; + ret.isdir = true; + ret.scanned = store_scanned_[i]; + ret.dtModify = 0; + ret.dtCreate = 0; + strcpy(ret.name, "/"); + } else { + i -= MTPD_MAX_FILESYSTEMS; + debug_read_record_count_++; + // now lets convert this to block and index within block. + uint16_t block_index = i >> 6; // lower 6 bits is index into block + uint8_t record_index = i & 0x3f; + + uint8_t biIndex = CacheRecordBlock(block_index); + DBGPrintf("CacheRIR: %u(%u %u)", i, block_index, record_index); DBGFlush(); + + if (record_index < recordBlocks_[biIndex].recordCount) { + uint16_t record_data_index = recordBlocks_[biIndex].recordOffsets[record_index]; + Record *precord = (Record*)&recordBlocks_[biIndex].data[record_data_index]; + ret = *precord; + DBGPrintf(": %s\n",ret.name); DBGFlush(); + } else { + memset(&ret, 0, sizeof(ret)); + } + } + + mtp_lock_storage(false); + return ret; +} + + +#else +//============================================================================= +// Un compressed version... +//============================================================================= +bool MTPStorage::WriteIndexRecord(uint32_t i, const Record &r) +{ + OpenIndex(); + mtp_lock_storage(true); + bool write_succeeded = true; + if (i < MTPD_MAX_FILESYSTEMS) { + // all we need is the first child pointer and if it was scanned + store_first_child_[i] = r.child; + store_scanned_[i] = r.scanned; + } else { + debug_write_record_count_++; + + + debug_fs_write_record_count_++; + index_.seek((i - MTPD_MAX_FILESYSTEMS) * sizeof(r)); + size_t bytes_written = index_.write((char *)&r, sizeof(r)); + if (bytes_written != sizeof(r)) { + MTP_class::PrintStream()->printf(F("$$$ Failed to write Index record: %u bytes written: %u\n"), i, bytes_written); + write_succeeded = false; + } + } + mtp_lock_storage(false); + return write_succeeded; +} + +uint32_t MTPStorage::AppendIndexRecord(const Record &r) +{ + uint32_t new_record = index_entries_++; + WriteIndexRecord(new_record, r); + return new_record; +} + + +MTPStorage::Record MTPStorage::ReadIndexRecord(uint32_t i) +{ + + Record ret; + //memset(&ret, 0, sizeof(ret)); + if (i > index_entries_) { + memset(&ret, 0, sizeof(ret)); + return ret; + } + OpenIndex(); + mtp_lock_storage(true); + if (i < MTPD_MAX_FILESYSTEMS) { + // Build it on the fly... + ret.store = i; + ret.parent = 0xFFFFUL; + ret.sibling = 0; + ret.child = store_first_child_[i]; + ret.isdir = true; + ret.scanned = store_scanned_[i]; + ret.dtModify = 0; + ret.dtCreate = 0; + strcpy(ret.name, "/"); + } else { + debug_read_record_count_++; + debug_fs_read_record_count_++; + bool seek_ok __attribute__((unused)) = index_.seek((i - MTPD_MAX_FILESYSTEMS) * sizeof(ret), SeekSet); + int cb_read = index_.read((char *)&ret, sizeof(ret)); + if (cb_read != sizeof(ret)) { + MTP_class::PrintStream()->printf("$$$ Failed to read Index Record(%u): %u %d %s\n", i, seek_ok, cb_read, ret.name); + memset(&ret, 0, sizeof(ret)); + } + } + + mtp_lock_storage(false); + return ret; +} +#endif + +// construct filename rexursively +uint16_t MTPStorage::ConstructFilename(int i, char *out, int len) +{ + Record tmp = ReadIndexRecord(i); + if (tmp.parent == 0xFFFFUL) { // flags the root object + strcpy(out, "/"); + return tmp.store; + } else { + ConstructFilename(tmp.parent, out, len); + if (out[strlen(out) - 1] != '/') { + strlcat(out, "/", len); + } + strlcat(out, tmp.name, len); + return tmp.store; + } +} + +// returns true if same file same mode... +bool MTPStorage::OpenFileByIndex(uint32_t i, uint32_t mode) +{ + //DBGPrintf("*** OpenFileIndex(%u, %x)\n", i, mode); DBGFlush(); + bool file_is_open = file_; // check to see if file is open + if (file_is_open && (open_file_ == i) && (mode_ == mode)) { + return true; + } + char filename[MTP_MAX_PATH_LEN]; + uint16_t store = ConstructFilename(i, filename, MTP_MAX_PATH_LEN); + //DBGPrintf("\t>>Store:%u path:%s open:%u\n", store, filename, file_is_open); DBGFlush(); + mtp_lock_storage(true); + if (file_is_open) { + file_.close(); + //DBGPrintf("\t>>after close\n"); DBGFlush(); + } + file_ = open(store, filename, mode); + //DBGPrintf("\t>>after open\n"); DBGFlush(); + if (!file_) { + DBGPrintf( + "OpenFileByIndex failed to open (%u):%s mode: %u\n", i, filename, mode); + open_file_ = 0xFFFFFFFEUL; + } else { + open_file_ = i; + mode_ = mode; + } + mtp_lock_storage(false); + return false; +} + + +// MTP object handles should not change or be re-used during a session. +// This would be easy if we could just have a list of all files in memory. +// Since our RAM is limited, we'll keep the index in a file instead. +void MTPStorage::GenerateIndex(uint32_t store) +{ + if (index_generated_) return; + index_generated_ = true; + #if DEBUG + MTP_class::PrintStream()->println("*** MTPStorage::GenerateIndex called ***"); + #endif + // first remove old index file + mtp_lock_storage(true); + if (user_index_file_) { + // maybe truncate the file + index_.seek(0, SeekSet); + index_.truncate(); + } else { + DBGPrintf(" remove called: %u %s\n", index_file_storage_, indexFile); + remove(index_file_storage_, indexFile); + } + mtp_lock_storage(false); + //num_storage = get_FSCount(); + index_entries_ = 0; + Record r; + // BugBug - will generate index for max file systems count... + // Note hacked up storage of these items... + for (int ii = 0; ii < MTPD_MAX_FILESYSTEMS; ii++) { + r.store = ii; + r.parent = 0xFFFFUL; + r.sibling = 0; + r.child = 0; + r.isdir = true; + r.scanned = false; + r.dtModify = 0; + r.dtCreate = 0; + strcpy(r.name, "/"); + AppendIndexRecord(r); + + #if DEBUG + printRecordIncludeName(ii, &r); + #endif + } +} + +void MTPStorage::ScanDir(uint32_t store, uint32_t i) +{ + //DBGPrintf("** ScanDir called %u %u\n", store, i); DBGFlush(); + if (i == 0xFFFFUL) i = store; + Record record = ReadIndexRecord(i); + if (record.isdir && !record.scanned) { + //DBGPrintf("\t>>After ReadIndexRecord\n"); DBGFlush(); + OpenFileByIndex(i); + //DBGPrintf("\t>>After OpenFileByIndex\n"); DBGFlush(); + if (!file_) return; + int sibling = 0; + while (true) { + mtp_lock_storage(true); + child_ = file_.openNextFile(); + //DBGPrintf("\t>>After openNextFile\n"); DBGFlush(); + mtp_lock_storage(false); + if (!child_) break; + Record r; + r.store = record.store; + r.parent = i; + r.sibling = sibling; + r.isdir = child_.isDirectory(); + r.child = r.isdir ? 0 : child_.size(); + r.scanned = false; + strlcpy(r.name, child_.name(), MTP_MAX_FILENAME_LEN); + DateTimeFields dtf; + r.dtModify = child_.getModifyTime(dtf) ? makeTime(dtf) : 0; + r.dtCreate = child_.getCreateTime(dtf) ? makeTime(dtf) : 0; + sibling = AppendIndexRecord(r); + #if DEBUG + MTP_class::PrintStream()->print(" >> "); + printRecordIncludeName(sibling, &r); + #endif + child_.close(); + } + record.scanned = true; + record.child = sibling; + WriteIndexRecord(i, record); + } + // Lets try closing the file_ to see if that help minimize crash with removing SD and reinsert... + file_.close(); + open_file_ = 0xFFFFFFFEUL; + //DBGPrintf("** ScanDir completed***\n"); DBGFlush(); +} + +void MTPStorage::ScanAll(uint32_t store) +{ + if (all_scanned_) return; + all_scanned_ = true; + GenerateIndex(store); + for (uint32_t i = 0; i < index_entries_; i++) { + ScanDir(store, i); + } +} + + +void MTPStorage::StartGetObjectHandles(uint32_t store, uint32_t parent) +{ + //DBGPrintf("** StartGetObjectHandles called %u %u\n", store, parent); DBGFlush(); + GenerateIndex(store); + DBGPrintf("\t>> After GenerateIndex\n"); DBGFlush(); + if (parent) { + if ((parent == 0xFFFFUL) || (parent == 0xFFFFFFFFUL)) { + parent = store; // As per initizalization + } + ScanDir(store, parent); + //DBGPrintf("\t>> After ScanDir\n"); DBGFlush(); + follow_sibling_ = true; + // Root folder? + next_ = ReadIndexRecord(parent).child; + } else { + ScanAll(store); + follow_sibling_ = false; + next_ = 1; + } + //DBGPrintf("\t>>end StartGetObjectHandles\n"); DBGFlush(); +} + +uint32_t MTPStorage::GetNextObjectHandle(uint32_t store) +{ + while (true) { + if (next_ == 0) return 0; + int ret = next_; + Record r = ReadIndexRecord(ret); + if (follow_sibling_) { + next_ = r.sibling; + } else { + next_++; + if (next_ >= index_entries_) next_ = 0; + } + if (r.name[0]) return ret; + } +} + + +void MTPStorage::GetObjectInfo(uint32_t handle, char *name, uint64_t *size, + uint32_t *parent, uint16_t *store) +{ + Record r = ReadIndexRecord(handle); + strcpy(name, r.name); + *parent = r.parent; + *size = r.isdir ? (uint64_t)-1 : r.child; + *store = r.store; +} + + +uint64_t MTPStorage::GetSize(uint32_t handle) { + return ReadIndexRecord(handle).child; +} + +bool MTPStorage::getModifyTime(uint32_t handle, uint32_t &dt) +{ + Record r = ReadIndexRecord(handle); + dt = r.dtModify; + return (r.dtModify) ? true : false; +} + +bool MTPStorage::getCreateTime(uint32_t handle, uint32_t &dt) +{ + Record r = ReadIndexRecord(handle); + dt = r.dtCreate; + return (r.dtCreate) ? true : false; +} + +static inline uint16_t MTPFS_DATE(uint16_t year, uint8_t month, uint8_t day) { + year -= 1980; + return year > 127 || month > 12 || day > 31 ? 0 + : year << 9 | month << 5 | day; +} +static inline uint16_t MTPFS_YEAR(uint16_t fatDate) { + return 1980 + (fatDate >> 9); +} +static inline uint8_t MTPFS_MONTH(uint16_t fatDate) { + return (fatDate >> 5) & 0XF; +} +static inline uint8_t MTPFS_DAY(uint16_t fatDate) { return fatDate & 0X1F; } +static inline uint16_t MTPFS_TIME(uint8_t hour, uint8_t minute, + uint8_t second) { + return hour > 23 || minute > 59 || second > 59 + ? 0 + : hour << 11 | minute << 5 | second >> 1; +} +static inline uint8_t MTPFS_HOUR(uint16_t fatTime) { return fatTime >> 11; } +static inline uint8_t MTPFS_MINUTE(uint16_t fatTime) { + return (fatTime >> 5) & 0X3F; +} +static inline uint8_t MTPFS_SECOND(uint16_t fatTime) { + return 2 * (fatTime & 0X1F); +} + +bool MTPStorage::updateDateTimeStamps(uint32_t handle, uint32_t dtCreated, uint32_t dtModified) +{ + Record r = ReadIndexRecord(handle); + DateTimeFields dtf; + if ((dtCreated == 0) && (dtModified == 0)) { + DBGPrintf("&&DT (0,0) (%u,%u)\n", r.dtCreate, r.dtModify); + return true; + + } + OpenFileByIndex(handle, FILE_READ); + if (!file_) { + DBGPrintf( + "MTPStorage::updateDateTimeStamps failed to open file\n"); + return false; + } + mtp_lock_storage(true); + r.dtModify = dtModified; + breakTime(dtModified, dtf); + file_.setModifyTime(dtf); + r.dtCreate = dtCreated; + breakTime(dtCreated, dtf); + file_.setCreateTime(dtf); + WriteIndexRecord(handle, r); +// file_.close(); + mtp_lock_storage(false); + return true; +} + +uint32_t MTPStorage::read(uint32_t handle, uint64_t pos, char *out, uint32_t bytes) +{ + // some real hack, to bypass doing seek unless we have to. + static uint64_t last_pos = (uint64_t)-1; + // if the file was not te same or the like clear last position. + if (!OpenFileByIndex(handle)) { + //Serial.write('@'); + last_pos = (uint64_t)-1; + } + mtp_lock_storage(true); + if (pos != last_pos) { + file_.seek(pos, SeekSet); + //Serial.write("$"); + } + uint32_t cb_read = file_.read(out, bytes); + last_pos = pos + cb_read; + mtp_lock_storage(false); + return cb_read; +} + + +void MTPStorage::removeFile(uint32_t store, const char *file) +{ + char tname[MTP_MAX_PATH_LEN]; + + File f1 = open(store, file, 0); + if (f1.isDirectory()) { + File f2; + while (f2 = f1.openNextFile()) { + snprintf(tname, sizeof(tname), "%s/%s", file, f2.name()); + if (f2.isDirectory()) { + removeFile(store, tname); + } else { + remove(store, tname); + } + } + rmdir(store, file); + } else { + remove(store, file); + } +} + +bool MTPStorage::DeleteObject(uint32_t object) +{ + // don't do anything if trying to delete a root directory see below + if (object == 0xFFFFUL) return true; + + // first create full filename + char filename[MTP_MAX_PATH_LEN]; + ConstructFilename(object, filename, MTP_MAX_PATH_LEN); + + Record r = ReadIndexRecord(object); + + // remove file from storage (assume it is always working) + mtp_lock_storage(true); + removeFile(r.store, filename); + mtp_lock_storage(false); + + // mark object as deleted + r.name[0] = 0; + WriteIndexRecord(object, r); + + // update index file + Record t = ReadIndexRecord(r.parent); + if (t.child == object) { // we are the youngest, simply relink parent to older sibling + t.child = r.sibling; + WriteIndexRecord(r.parent, t); + } else { // link younger to older sibling + // find younger sibling + uint32_t is = t.child; + Record x = ReadIndexRecord(is); + while ((x.sibling != object)) { + is = x.sibling; + x = ReadIndexRecord(is); + } + // is points now to junder sibling + x.sibling = r.sibling; + WriteIndexRecord(is, x); + } + return 1; +} + + + +uint32_t MTPStorage::Create(uint32_t store, uint32_t parent, bool folder, const char *filename) +{ + DBGPrintf("MTPStorage::create(%u, %u, %u, %s)\n", store, parent, folder, filename); + uint32_t ret; + if ((parent == 0xFFFFUL) || (parent == 0xFFFFFFFFUL)) parent = store; // does this ever get used? + ScanDir(store, parent); // make sure the parent is scanned... + Record p = ReadIndexRecord(parent); + Record r; + + // See if the name already exists in the parent + // 32 bits is probably sufficient for index child is now 64 bits + uint32_t index = p.child; + while (index) { + r = ReadIndexRecord(index); + if (strcmp(filename, r.name) == 0) break; // found a match + index = r.sibling; + } + + if (index) { + // found that name in our list + DBGPrintf(" >> Parent (%u) already contains %s(%u)\n", parent, filename, index); + if (folder != r.isdir) { + DBGPrintf(" >> Not same type: cur:%u new:%u\n", r.isdir, folder); + return 0xFFFFFFFFUL; + } + + DBGPrintf(" >> using index\n", index); + return index; + } + + strlcpy(r.name, filename, MTP_MAX_PATH_LEN); + r.store = p.store; + r.parent = parent; + r.child = 0; + r.sibling = p.child; + r.isdir = folder; + r.dtModify = 0; + r.dtCreate = 0; + // New folder is empty, scanned = true. + r.scanned = 1; + ret = p.child = AppendIndexRecord(r); + WriteIndexRecord(parent, p); + #if DEBUG + printRecordIncludeName(parent, &p); + printRecordIncludeName(ret, &r); + dumpIndexList(); + #endif + if (folder) { + char filename[MTP_MAX_PATH_LEN]; + ConstructFilename(ret, filename, MTP_MAX_PATH_LEN); + DBGPrintf(" >>(%u, %s)\n", ret, filename); + mtp_lock_storage(true); + mkdir(store, filename); + MTP_class::PrintStream()->println(" >> After mkdir"); MTP_class::PrintStream()->flush(); + mtp_lock_storage(false); + OpenFileByIndex(ret, FILE_READ); + MTP_class::PrintStream()->println(" >> After OpenFileByIndex"); MTP_class::PrintStream()->flush(); + if (!file_) { + DBGPrintf( + "MTPStorage::Create %s failed to open folder\n", filename); + } else { + DateTimeFields dtf; + r.dtModify = file_.getModifyTime(dtf) ? makeTime(dtf) : 0; + r.dtCreate = file_.getCreateTime(dtf) ? makeTime(dtf) : 0; + // does not do any good if we don't save the data! + WriteIndexRecord(ret, r); + file_.close(); + open_file_ = 0xFFFFFFFEUL; + } + } else { + OpenFileByIndex(ret, FILE_WRITE_BEGIN); + // lets check to see if we opened the file or not... + if (!file_) { + DBGPrintf( + "MTPStorage::Create %s failed to create file\n", filename); + DeleteObject(ret); // note this will mark that new item as deleted... + ret = 0xFFFFFFFFUL; // return an error code... + } else { + DateTimeFields dtf; + r.dtModify = file_.getModifyTime(dtf) ? makeTime(dtf) : 0; + r.dtCreate = file_.getCreateTime(dtf) ? makeTime(dtf) : 0; + // does not do any good if we don't save the data! + WriteIndexRecord(ret, r); + } + } +#if DEBUG > 1 + MTP_class::PrintStream()->print("Create "); + MTP_class::PrintStream()->print(ret); + MTP_class::PrintStream()->print(" "); + MTP_class::PrintStream()->print(store); + MTP_class::PrintStream()->print(" "); + MTP_class::PrintStream()->print(parent); + MTP_class::PrintStream()->print(" "); + MTP_class::PrintStream()->print(folder); + MTP_class::PrintStream()->print(" "); + MTP_class::PrintStream()->print(r.dtModify); + MTP_class::PrintStream()->print(" "); + MTP_class::PrintStream()->print(r.dtCreate); + MTP_class::PrintStream()->print(" "); + MTP_class::PrintStream()->println(filename); +#endif + return ret; +} + + +size_t MTPStorage::write(const char *data, uint32_t bytes) +{ + mtp_lock_storage(true); + // make sure it does not fall through to default Print version of buffer write + size_t ret = file_.write((void*)data, bytes); + mtp_lock_storage(false); + return ret; +} + + +void MTPStorage::close() +{ + mtp_lock_storage(true); + uint64_t size = file_.size(); + file_.close(); + mtp_lock_storage(false); + // update record with file size + Record r = ReadIndexRecord(open_file_); + if (!r.isdir) { + r.child = size; + WriteIndexRecord(open_file_, r); + } + open_file_ = 0xFFFFFFFEUL; +} + + +bool MTPStorage::rename(uint32_t handle, const char *name) +{ + char oldName[MTP_MAX_PATH_LEN]; + char newName[MTP_MAX_PATH_LEN]; + char temp[MTP_MAX_PATH_LEN]; + + uint16_t store = ConstructFilename(handle, oldName, MTP_MAX_PATH_LEN); + MTP_class::PrintStream()->println(oldName); + + Record p1 = ReadIndexRecord(handle); + strlcpy(temp, p1.name, MTP_MAX_PATH_LEN); + strlcpy(p1.name, name, MTP_MAX_PATH_LEN); + + WriteIndexRecord(handle, p1); + ConstructFilename(handle, newName, MTP_MAX_PATH_LEN); + MTP_class::PrintStream()->println(newName); + + if (rename(store, oldName, newName)) return true; + + // rename failed; undo index update + strlcpy(p1.name, temp, MTP_MAX_FILENAME_LEN); + WriteIndexRecord(handle, p1); + return false; +} + +void MTPStorage::dumpIndexList(Stream &stream) +{ + if (index_entries_ == 0) return; + uint32_t fsCount = get_FSCount(); + uint32_t skip_start_index = 0; + for (uint32_t ii = 0; ii < index_entries_; ii++) { + if ((ii < fsCount) || (ii >= MTPD_MAX_FILESYSTEMS)) { + Record p = ReadIndexRecord(ii); + // try to detect invalid/delected items: name[0] = 0... + if (p.name[0] == '\0') { + if (skip_start_index == 0) skip_start_index = ii; + } else { + if (skip_start_index) { + stream.printf("< Skipped %u - %u >\n", skip_start_index, ii-1); + skip_start_index = 0; + } + stream.printf("%d: %d %d %u %d %d %lld %u %u %s\n", + ii, p.store, p.isdir, p.scanned, p.parent, p.sibling, p.child, + p.dtCreate, p.dtModify, p.name); + } + } + } + if (skip_start_index) { // not likely to happen but + stream.printf("< Skipped %u - %u >\n", skip_start_index, index_entries_-1); + } +} + + +void MTPStorage::printRecord(int h, Record *p) +{ + MTP_class::PrintStream()->printf("%d: %d %d %d %d %ld\n", h, p->store, p->isdir, + p->parent, p->sibling, p->child); +} + +void MTPStorage::printRecordIncludeName(int h, Record *p) +{ + MTP_class::PrintStream()->printf("%d: %u %u %u %u %u %lu %u %u %s\n", h, p->store, + p->isdir, p->scanned, p->parent, p->sibling, + p->child, p->dtModify, p->dtCreate, p->name); +} + +/* + * //index list management for moving object around + * p1 is record of handle + * p2 is record of new dir + * p3 is record of old dir + * + * // remove from old direcory + * if p3.child == handle / handle is last in old dir + * p3.child = p1.sibling / simply relink old dir + * save p3 + * else + * px record of p3.child + * while( px.sibling != handle ) update px = record of px.sibling + * px.sibling = p1.sibling + * save px + * + * // add to new directory + * p1.parent = new + * p1.sibling = p2.child + * p2.child = handle + * save p1 + * save p2 + * +*/ + +//============================================================================= +// move: process the mtp moveObject command +//============================================================================= +// lets see if we are just doing a simple rename or if we need to do full +// move. +bool MTPStorage::move(uint32_t handle, uint32_t newStore, uint32_t newParent) +{ + DBGPrintf("MTPStorage::move %d -> %d %d\n", handle, newStore, newParent); + setLastError(NO_ERROR); + Record p1 = ReadIndexRecord(handle); + + ScanDir(newStore, newParent); // make sure the new parent has been enumerated. + + char oldName[MTP_MAX_PATH_LEN]; + ConstructFilename(handle, oldName, MTP_MAX_PATH_LEN); + + // try hack use temporary one to generate new path name. + char newName[MTP_MAX_PATH_LEN]; + // first get the path name for new parent + ConstructFilename(newParent, newName, MTP_MAX_PATH_LEN); + if (newName[1] != 0) strlcat(newName, "/", MTP_MAX_PATH_LEN); + strlcat(newName, p1.name, MTP_MAX_PATH_LEN); + DBGPrintf(" >>From:%u %s to:%u %s\n", p1.store, oldName, newStore, newName); + if (p1.store == newStore) { + MTP_class::PrintStream()->println(" >> Move same storage"); + if (!rename(newStore, oldName, newName)) { + DBG_FAIL_MACRO; + setLastError(RENAME_FAIL); + // failed, so simply return... did not change anything. + return false; + } + } else if (!p1.isdir) { + MTP_class::PrintStream()->println(" >> Move differnt storage file"); + if (CopyByPathNames(p1.store, oldName, newStore, newName)) { + remove(p1.store, oldName); + } else { + DBG_FAIL_MACRO; + return false; + } + } else { // move directory cross mtp-disks + MTP_class::PrintStream()->println(" >> Move differnt storage directory"); + if (!moveDir(p1.store, oldName, newStore, newName)) { + DBG_FAIL_MACRO; + return false; + } + } + + // remove index from old parent + Record p2 = ReadIndexRecord(p1.parent); + if (p2.child == handle) { // was first on in list. + p2.child = p1.sibling; + WriteIndexRecord(p1.parent, p2); + } else { + uint32_t jx = p2.child; + Record px = ReadIndexRecord(jx); + while (handle != px.sibling) { + jx = px.sibling; + px = ReadIndexRecord(jx); + } + px.sibling = p1.sibling; + WriteIndexRecord(jx, px); + } + + // add to new parent + p2 = ReadIndexRecord(newParent); + p1.parent = newParent; + p1.store = p2.store; + p1.sibling = p2.child; + p2.child = handle; + WriteIndexRecord(handle, p1); + WriteIndexRecord(newParent, p2); + + // Should be done + return true; + +} + +// old and new are directory paths +bool MTPStorage::moveDir(uint32_t store0, char *oldfilename, uint32_t store1, char *newfilename) +{ + char tmp0Name[MTP_MAX_PATH_LEN]; + char tmp1Name[MTP_MAX_PATH_LEN]; + + if (!mkdir(store1, newfilename)) { + setLastError(MKDIR_FAIL); + DBG_FAIL_MACRO; + return false; + } + File f1 = open(store0, oldfilename, FILE_READ); + if (!f1) { + setLastError(SOURCE_OPEN_FAIL); + DBG_FAIL_MACRO; + return false; + } + while (1) { + strlcpy(tmp0Name, oldfilename, MTP_MAX_PATH_LEN); + if (tmp0Name[strlen(tmp0Name) - 1] != '/') { + strlcat(tmp0Name, "/", MTP_MAX_PATH_LEN); + } + strlcpy(tmp1Name, newfilename, MTP_MAX_PATH_LEN); + if (tmp1Name[strlen(tmp1Name) - 1] != '/') { + strlcat(tmp1Name, "/", MTP_MAX_PATH_LEN); + } + File f2 = f1.openNextFile(); + if (!f2) break; + { + // generate filenames + strlcat(tmp0Name, f2.name(), MTP_MAX_PATH_LEN); + strlcat(tmp1Name, f2.name(), MTP_MAX_PATH_LEN); + if (f2.isDirectory()) { + if (!moveDir(store0, tmp0Name, store1, tmp1Name)) { + DBG_FAIL_MACRO; + return false; + } + } else { + if (!CopyByPathNames(store0, tmp0Name, store1, tmp1Name)) { + DBG_FAIL_MACRO; + return false; + } + if (!remove(store0, tmp0Name)) { + setLastError(REMOVE_FAIL); + DBG_FAIL_MACRO; + return false; + } + } + } + } + if (rmdir(store0, oldfilename)) return true; + setLastError(RMDIR_FAIL); + DBG_FAIL_MACRO; + return false; + +} + +//============================================================================= +// copy: process the copy command, some functions below used by move as well +//============================================================================= + +uint32_t MTPStorage::copy(uint32_t handle, uint32_t newStore, uint32_t newParent) +{ + setLastError(NO_ERROR); + DBGPrintf("MTPStorage::copy(%u, %u, %u)\n", handle, newStore, newParent); + if (newParent == 0xFFFFUL) newParent = newStore; + Record p1 = ReadIndexRecord(handle); + Record p2 = ReadIndexRecord(newParent); // 0 means root of store + + uint32_t newHandle = Create(p2.store, newParent, p1.isdir, p1.name); + if (newHandle == 0xFFFFFFFFUL) { + setLastError(DEST_OPEN_FAIL); + DBG_FAIL_MACRO; + } else if (p1.isdir) { + ScanDir(p1.store, handle); + CopyFiles(handle, newHandle); + } else { + CompleteCopyFile(handle, newHandle); + } + return newHandle; +} + + +// assume handle and newHandle point to existing directories +bool MTPStorage::CopyFiles(uint32_t handle, uint32_t newHandle) +{ + DBGPrintf("%d -> %d\n", handle, newHandle); + bool copy_completed_without_error = true; + Record r = ReadIndexRecord(handle); + uint32_t source_store = r.store; + uint32_t ix = r.child; + uint32_t iy = 0; + + r = ReadIndexRecord(newHandle); + uint32_t target_store = r.store; + while (ix) { // get child + Record px = ReadIndexRecord(ix); + iy = Create(target_store, newHandle, px.isdir, px.name); + if (iy != 0xFFFFFFFFUL) { + if (px.isdir) { + ScanDir(source_store, ix); + if (!CopyFiles(ix, iy)) { + copy_completed_without_error = false; + break; + } + } else { + if (!CompleteCopyFile(ix, iy)) { + copy_completed_without_error = false; + break; + } + } + } + ix = px.sibling; + } + r.child = iy; + WriteIndexRecord(newHandle, r); + return copy_completed_without_error; +} + +#if defined(__IMXRT1062__) +#define COPY_BUFFER MTP_class::disk_buffer_ +#define COPY_BUFFER_SIZE MTP_class::DISK_BUFFER_SIZE +#else +#define COPY_BUFFER (copy_buffer) +#define COPY_BUFFER_SIZE 512 +#endif + +bool MTPStorage::CompleteCopyFile(uint32_t from, uint32_t to) +{ + + Record r = ReadIndexRecord(to); + bool copy_completed_without_error = true; + + // open the source file... + // the Create call should have opened the target. +#if !defined(__IMXRT1062__) + uint8_t *copy_buffer = (uint8_t *)malloc(COPY_BUFFER_SIZE); + if (!copy_buffer) return false; +#endif + + char source_file_name[MTP_MAX_PATH_LEN]; + uint32_t store0 = ConstructFilename(from, source_file_name, MTP_MAX_PATH_LEN); + DBGPrintf(" >> CompleteCopyFile(%u, %u) %s\n", from, to, source_file_name); + File f1 = open(store0, source_file_name, FILE_READ); + if (!f1) { + setLastError(DEST_OPEN_FAIL); + DBG_FAIL_MACRO; +#if !defined(__IMXRT1062__) + free(copy_buffer); +#endif + return false; + } + file_.truncate(); // make sure to remove old data... Should we do this at end? + + + int nd = -1; + + while (f1.available() > 0) { + nd = f1.read(COPY_BUFFER, COPY_BUFFER_SIZE); + if (nd < 0) { // read error + setLastError(READ_ERROR); + copy_completed_without_error = false; + DBG_FAIL_MACRO; + break; + } + size_t cb_written = file_.write(COPY_BUFFER, nd); + if (cb_written < (uint32_t)nd) { + setLastError(WRITE_ERROR); + DBG_FAIL_MACRO; + copy_completed_without_error = false; + break; + } + if ((uint32_t)nd < COPY_BUFFER_SIZE) break; // end of file + } + + DateTimeFields dtf; + if (f1.getModifyTime(dtf)) { + DBGPrintf(" >> Updated Modify Date\n"); + file_.setModifyTime(dtf); + r.dtModify = makeTime(dtf); + } + +#if !defined(__IMXRT1062__) + free(copy_buffer); +#endif + // close source file + f1.close(); + + mtp_lock_storage(true); + r.child = (uint32_t)file_.size(); + WriteIndexRecord(to, r); +#if DEBUG > 1 + MTP_class::PrintStream()->print(" >>"); printRecordIncludeName(to, &r); +#endif + file_.close(); + mtp_lock_storage(false); + DBGPrintf(" >> return %u\n", copy_completed_without_error); + return copy_completed_without_error; +} +#if 1 +bool MTPStorage::CopyByPathNames(uint32_t store0, char *oldfilename, uint32_t store1, char *newfilename) +{ +#if !defined(__IMXRT1062__) + uint8_t *copy_buffer = (uint8_t *)malloc(COPY_BUFFER_SIZE); + if (!copy_buffer) return false; +#endif + int nd = -1; + +#if DEBUG > 1 + MTP_class::PrintStream()->print("MTPStorage::CopyByPathNames - From "); + MTP_class::PrintStream()->print(store0); + MTP_class::PrintStream()->print(": "); + MTP_class::PrintStream()->println(oldfilename); + MTP_class::PrintStream()->print("To "); + MTP_class::PrintStream()->print(store1); + MTP_class::PrintStream()->print(": "); + MTP_class::PrintStream()->println(newfilename); +#endif + + File f1 = open(store0, oldfilename, FILE_READ); + if (!f1) { + DBG_FAIL_MACRO; +#if !defined(__IMXRT1062__) + free(copy_buffer); +#endif + return false; + } + File f2 = open(store1, newfilename, FILE_WRITE_BEGIN); + if (!f2) { + f1.close(); + DBG_FAIL_MACRO; +#if !defined(__IMXRT1062__) + free(copy_buffer); +#endif + return false; + } + while (f1.available() > 0) { + nd = f1.read(COPY_BUFFER, COPY_BUFFER_SIZE); + if (nd < 0) break; // read error + f2.write(COPY_BUFFER, nd); + if ((uint32_t)nd < COPY_BUFFER_SIZE) break; // end of file + } + // Lets see if we can set the modify date of the new file to that of the + // file we are copying from. + DateTimeFields tm; + if (f1.getModifyTime(tm)) { + DBGPrintf(" >> Updated Modify Date"); + f2.setModifyTime(tm); + } + + // close all files + f1.close(); + f2.close(); +#if !defined(__IMXRT1062__) + free(copy_buffer); +#endif + if (nd < 0) { + DBG_FAIL_MACRO; + return false; + } + return true; +} +#endif + +bool MTPStorage::addFilesystem(FS &disk, const char *diskname) +{ + int store; + if (fsCount < MTPD_MAX_FILESYSTEMS) { + store = fsCount++; + } else { + // See if we can reuse store + bool found = false; + for (int i=0; i < MTPD_MAX_FILESYSTEMS; i++) { + if (fs[i] == nullptr) { + store = i; + found = true; + break; + } + } + if (!found) return false; // no room left + } + if (!diskname) diskname = ""; // TODO: get volume name? + name[store] = diskname; + fs[store] = &disk; + store_first_child_[store] = 0; + store_scanned_[store] = false; + totalSize(store); // update totalSize & usedSize for interrupt-safe GetStorageInfo + usedSize(store); + const char *volname = fs[store]->name(); + if (!volname) volname = "Untitled"; + DBGPrintf("addFilesystem: %d %s %x %s\n", fsCount, diskname, (uint32_t)fs[store], volname); + media_present[store] = disk.mediaPresent(); + if (media_present[store]) { + MTP.send_StoreAddedEvent(store); + } + return true; +} + + +bool MTPStorage::removeFilesystem(uint32_t store) +{ + if ((store < fsCount) && (name[store])) { + name[store] = nullptr; + fs[store] = nullptr; + // Now lets see about pruning + clearStoreIndexItems(store); + + return true; + } + return false; +} + +bool MTPStorage::clearStoreIndexItems(uint32_t store) +{ + if (store >= fsCount) return false; + if (store_first_child_[store] == 0) return true; + // first pass simple... + store_first_child_[store] = 0; + store_scanned_[store] = 0; + + return true; +} + + +uint32_t MTPStorage::MapFileNameToIndex(uint32_t storage, const char *pathname, + bool addLastNode, bool *node_added) +{ + const char *path_parser = pathname; + DBGPrintf( + "MTPStorage_SD::MapFileNameToIndex %u %s add:%d\n", + storage, pathname, addLastNode); + // We will only walk as far as we have enumerated + if (node_added) *node_added = false; + if (!index_generated_ || (path_parser == nullptr) || (*path_parser == '\0')) { + return 0xFFFFFFFFUL; // no index + } + char filename[MTP_MAX_FILENAME_LEN]; + Record record = ReadIndexRecord(storage); + uint32_t index; + + printRecordIncludeName(storage, &record); + for (;;) { + if (!record.isdir || !record.scanned) { + return 0xFFFFFFFFUL; // This storage has not been scanned. + } + // Copy the nex section of file name + if (*path_parser == '/') { + path_parser++; // advance from the previous / + } + char *psz = filename; + while (*path_parser && (*path_parser != '/')) { + *psz++ = *path_parser++; + } + *psz = '\0'; // terminate the string. + + // Now lets see if we can find this item in the record list. + DBGPrintf("Looking for: %s\n", filename); + index = record.child; + while (index) { + record = ReadIndexRecord(index); + printRecordIncludeName(index, &record); + if (strcmp(filename, record.name) == 0) break; // found a match + index = record.sibling; + } + + if (index) { + // found a match. return it. + if (*path_parser == '\0') { + DBGPrintf("Found Node: %d\n", index); + return index; + } + + } else { + // item not found + MTP_class::PrintStream()->println("Node Not found"); + + if ((*path_parser != '\0') || !addLastNode) { + return 0xFFFFFFFFUL; // not found nor added + } + // need to add item + uint32_t parent = record.parent; + record = ReadIndexRecord(parent); + Record r; // could probably reuse the other one, but... + /////////// + // Question is should more data be passed in like is this a + // directory and size or should we ask for it now, and/or + // should we mark it that we have not grabbed that info and + // wait for someone to ask for it? + + strlcpy(r.name, filename, MTP_MAX_FILENAME_LEN); + r.store = storage; + r.parent = parent; + r.child = 0; + r.scanned = false; + + mtp_lock_storage(true); + if (file_) file_.close(); + file_ = open(storage, pathname, FILE_READ); + mtp_lock_storage(false); + + if (file_) { + r.isdir = file_.isDirectory(); + if (!r.isdir) { + r.child = (uint64_t)file_.size(); + } + mtp_lock_storage(true); + file_.close(); + mtp_lock_storage(false); + } else { + r.isdir = false; + } + r.sibling = record.child; + // New folder is empty, scanned = true. + index = record.child = AppendIndexRecord(r); + WriteIndexRecord(parent, record); + + DBGPrintf("New node created: %d\n", index); + record = ReadIndexRecord(index); + printRecordIncludeName(index, &record); + if (node_added) *node_added = true; + return index; + } + } + return 0xFFFFFFFFUL; +} + +bool MTPStorage::setIndexStore(uint32_t storage) { + Serial.printf(" MTPStorage::setIndexStore: %d\n", (int)storage); // hard coded, dont care just give me the file + #if MTP_RECORD_BLOCKS + if ((storage != INDEX_STORE_MEM_FILE) && (storage >= get_FSCount())) + return false; // out of range + #else + if (storage >= get_FSCount()) + return false; // out of range + #endif + CloseIndex(); + index_file_storage_ = storage; + user_index_file_ = false; + return true; +} + + +//============================================================================= +// check for removable drives inserted or removed +//============================================================================= + +void MTPStorage::loop() { + if (time_between_device_checks_ms_ == (uint32_t)-1) return; + if ((uint32_t)(millis() - millis_atlast_device_check_) < time_between_device_checks_ms_) return; + millis_atlast_device_check_ = millis(); +#if 0 + static unsigned int count=0; + MTP_class::PrintStream()->printf("media change check #%u\n", ++count); +#endif + for (unsigned int store = 0; store < fsCount; store++) { + if (fs[store] == nullptr) continue; + bool media_present_now = fs[store]->mediaPresent(); + bool media_present_before = media_present[store]; + if (media_present_now && !media_present_before) { + const char *volname = fs[store]->name(); + if (!volname) volname = "Untitled"; + MTP_class::PrintStream()->printf("\nMedia inserted \"%s\"(%u) \"%s\"\n", + get_FSName(store), store, volname); + totalSize(store); // update totalSize & usedSize for interrupt-safe GetStorageInfo + usedSize(store); + MTP.send_StoreAddedEvent(store); // page 275 + media_present[store] = true; + } + if (!media_present_now && media_present_before) { + clearStoreIndexItems(store); + MTP_class::PrintStream()->printf("\nMedia removed \"%s\"(%u)\n", get_FSName(store), store); + MTP.send_StoreRemovedEvent(store); // page 276 + //MTP.send_StorageInfoChangedEvent(store); // page 278 - do not use + //MTP.send_DeviceResetEvent(); // page 277 - definitely do not use! + + // TODO: how to forget info about files in mtpindex.dat? + // TODO: how to forget info about files memory index? + // TODO: what if the removed media had the mtpindex.dat file for other drives? + media_present[store] = false; + } + } +} + +#endif // USB_MTPDISK or USB_MTPDISK_SERIAL diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Storage.h b/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Storage.h new file mode 100644 index 00000000..a8ed728e --- /dev/null +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Storage.h @@ -0,0 +1,306 @@ +// Storage.h - Teensy MTP Responder library +// Copyright (C) 2017 Fredrik Hubinette +// +// With updates from MichaelMC and Yoong Hor Meng +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// modified for SDFS by WMXZ +// Nov 2020 adapted to SdFat-beta / SD combo +// 19-nov-2020 adapted to FS + + +#pragma once + +#if defined(USB_MTPDISK) || defined(USB_MTPDISK_SERIAL) + +#include "core_pins.h" + +#include "FS.h" + +#ifndef FILE_WRITE_BEGIN +#define FILE_WRITE_BEGIN 2 +#endif +#define MTPD_MAX_FILESYSTEMS 0x10 // 16 +#ifndef MTP_MAX_FILENAME_LEN +#define MTP_MAX_FILENAME_LEN 256 // reduced by size of record header stuff so fits in 256 +#endif +#ifndef MTP_MAX_PATH_LEN +#define MTP_MAX_PATH_LEN 260 +#endif +#ifndef MTP_FSTYPE_MAX +#define MTP_FSTYPE_MAX 5 +#endif + +#if defined(__MK20DX128__) || defined(__MK20DX256__) +#define MTP_RECORD_BLOCKS 0 +#else +#define MTP_RECORD_BLOCKS 4 +#endif + + + +class MTPStorage final { +public: + +// Start of with same record structure... + struct __attribute__((__packed__)) Record { + uint32_t dtModify; + uint32_t dtCreate; + // increase size to 64 bits for large files plus moved up to better bit align + uint64_t child; // size stored here for files - increased to 64 bits + uint16_t parent; + uint16_t sibling; + uint8_t store; // index int physical storage (0 ... num_storages-1) + uint8_t isdir:1; + uint8_t scanned:1; + char name[MTP_MAX_FILENAME_LEN]; + }; + + struct __attribute__((__packed__)) RecordFixed { + uint32_t dtModify; + uint32_t dtCreate; + uint64_t child; // size stored here for files + uint16_t parent; + uint16_t sibling; + uint8_t store; // index int physical storage (0 ... num_storages-1) + uint8_t isdir:1; + uint8_t scanned:1; + char name[0]; + }; + +#if MTP_RECORD_BLOCKS + // Maybe group records so no need to hold store. + // but maybe hold it, max of 16 or 32... + // blocks if we have 2k blocks and if average names are 16 bytes we could hold + // average of 64items + // Packing records and knowing which block is going to be simple + // bottom 6 bits of object id is the index within a block + // upper 10 (or 26 if I go back to 32 bits) will be the 2K record number to + // read and write. + enum {MAX_RECORDS_PER_BLOCK=64, BLOCK_SIZE=2048, BLOCK_SIZE_DATA=BLOCK_SIZE - (2*MAX_RECORDS_PER_BLOCK + 3), + BLOCK_SIZE_NAME_FUDGE=64, INDEX_STORE_MEM_FILE=(uint32_t)-2}; + struct RecordBlock { + uint16_t recordOffsets[MAX_RECORDS_PER_BLOCK]; + uint16_t dataIndexNextFree; + uint8_t recordCount; + uint8_t data[BLOCK_SIZE_DATA]; + }; + + struct RecordBlockInfo { + uint16_t block_index; // which block is it. + uint16_t last_cycle_used; // remember the last cycle through map we used this page + uint8_t dirty; // is this block dirty + }; +#endif + + constexpr MTPStorage() { } + + // Add a file system to the list of storages that will be seen by + // the host computer. Returns true for success. + bool addFilesystem(FS &disk, const char *diskname); + + // Remove a file system from the list of storages + // Example might be if the device was removed. + bool removeFilesystem(uint32_t store); + bool clearStoreIndexItems(uint32_t store); + bool setIndexStore(uint32_t storage = 0); + uint32_t getStoreID(const char *fsname) { + for (unsigned int i = 0; i < fsCount; i++) { + if (name[i] != nullptr && strcmp(fsname, name[i]) == 0) return i; + } + return 0xFFFFFFFFUL; + } + enum {NO_ERROR=0, SOURCE_OPEN_FAIL, DEST_OPEN_FAIL, READ_ERROR, WRITE_ERROR, + RENAME_FAIL, MKDIR_FAIL, REMOVE_FAIL, RMDIR_FAIL}; + inline uint8_t getLastError() {return last_error_;} + inline void setLastError(uint8_t error) {last_error_ = error;} + const char *getStoreName(uint32_t store) { + if (store < (uint32_t)fsCount) return name[store]; + return nullptr; + } + + FS *getStoreFS(uint32_t store) { + if (store < (uint32_t)fsCount) return fs[store]; + return nullptr; + } + + // Return count of files systems in storage list + uint32_t get_FSCount(void) { return fsCount; } + + // Return count of files systems in storage list + const char *get_FSName(uint32_t store) { return name[store]; } + + File open(uint32_t store, const char *filename, uint32_t mode) { + if (fs[store] == nullptr) return File(); + return fs[store]->open(filename, mode); + } + bool mkdir(uint32_t store, char *filename) { + if (fs[store] == nullptr) return false; + return fs[store]->mkdir(filename); + } + bool rename(uint32_t store, char *oldfilename, char *newfilename) { + if (fs[store] == nullptr) return false; + return fs[store]->rename(oldfilename, newfilename); + } + bool remove(uint32_t store, const char *filename) { + if (fs[store] == nullptr) return false; + return fs[store]->remove(filename); + } + bool rmdir(uint32_t store, const char *filename) { + if (fs[store] == nullptr) return false; + return fs[store]->rmdir(filename); + } + uint64_t totalSize(uint32_t store, bool mediaAccessAllowed=true) { + if (fs[store] == nullptr) { + Serial.printf("$$$ MTPStorage::totalsize nullptr %u\n", store); + return (uint64_t)-1; + } + if (mediaAccessAllowed) { + uint64_t total = fs[store]->totalSize(); + capacity_mb_[store] = total >> 20; + return total; + } else { + return (uint64_t)capacity_mb_[store] << 20; + } + } + uint64_t usedSize(uint32_t store, bool mediaAccessAllowed=true) { + if (fs[store] == nullptr) return (uint64_t)-1; + if (mediaAccessAllowed) { + uint64_t used = fs[store]->usedSize(); + used_mb_[store] = used >> 20; + return used; + } else { + return (uint64_t)used_mb_[store] << 20; + } + } + bool CompleteCopyFile(uint32_t from, uint32_t to); + bool CopyByPathNames(uint32_t store0, char *oldfilename, uint32_t store1, char *newfilename); + bool moveDir(uint32_t store0, char *oldfilename, uint32_t store1, char *newfilename); + bool formatStore(uint32_t store, uint32_t p2) { + return fs[store]->format((int)p2, '*')? 1 : 0; + } + bool readonly(uint32_t storage) { + return false; + } + bool has_directories(uint32_t storage) { + return true; + } + bool isMediaPresent(uint32_t store) { + return media_present[store]; + } + void StartGetObjectHandles(uint32_t storage, uint32_t parent); + uint32_t GetNextObjectHandle(uint32_t storage); + void GetObjectInfo(uint32_t handle, char *name, uint64_t *size, + uint32_t *parent, uint16_t *store); + uint64_t GetSize(uint32_t handle); + bool getModifyTime(uint32_t handle, uint32_t &dt); + bool getCreateTime(uint32_t handle, uint32_t &dt); + bool updateDateTimeStamps(uint32_t handle, uint32_t dtCreated, uint32_t dtModified); + uint32_t Create(uint32_t storage, uint32_t parent, bool folder, const char *filename); + uint32_t read(uint32_t handle, uint64_t pos, char *buffer, uint32_t bytes); + size_t write(const char *data, uint32_t size); + void close(); + bool DeleteObject(uint32_t object); + void CloseIndex(); + void ResetIndex(); + bool rename(uint32_t handle, const char *name); + bool move(uint32_t handle, uint32_t newStorage, uint32_t newParent); + uint32_t copy(uint32_t handle, uint32_t newStorage, uint32_t newParent); + bool CopyFiles(uint32_t handle, uint32_t newHandle); + uint32_t MapFileNameToIndex(uint32_t storage, const char *pathname, + bool addLastNode = false, bool *node_added = nullptr); + void OpenIndex(); + void GenerateIndex(uint32_t storage); + void ScanDir(uint32_t storage, uint32_t i); + void ScanAll(uint32_t storage); + void removeFile(uint32_t store, const char *filename); + bool WriteIndexRecord(uint32_t i, const Record &r); + uint32_t AppendIndexRecord(const Record &r); + Record ReadIndexRecord(uint32_t i); + uint16_t ConstructFilename(int i, char *out, int len); + bool OpenFileByIndex(uint32_t i, uint32_t mode = FILE_READ); + void printRecord(int h, Record *p); + void printRecordIncludeName(int h, Record *p); + void dumpIndexList(Stream &stream = Serial); + void loop(); + void printClearRecordReadWriteCounts(); + + // returns the minimum time in ms of how often we will check the state of some devices + // in the loop() function. + uint32_t get_DeltaDeviceCheckTimeMS() { return time_between_device_checks_ms_; } + + // sets the minimum time in ms of how often we will check the state of some devices + // in the loop() function. Note: set to (uint32_t)-1 will disable this code + void set_DeltaDeviceCheckTimeMS(uint32_t delta_time) { time_between_device_checks_ms_ = delta_time; } + + +private: + unsigned int fsCount = 0; + const char *name[MTPD_MAX_FILESYSTEMS] = {nullptr}; + FS *fs[MTPD_MAX_FILESYSTEMS] = {nullptr}; + uint16_t store_first_child_[MTPD_MAX_FILESYSTEMS] = {0}; + uint8_t store_scanned_[MTPD_MAX_FILESYSTEMS] = {0}; + uint32_t capacity_mb_[MTPD_MAX_FILESYSTEMS] = {0}; + uint32_t used_mb_[MTPD_MAX_FILESYSTEMS] = {0}; + uint32_t index_entries_ = 0; + bool index_generated_ = false; + bool all_scanned_ = false; + uint32_t next_ = 0; + bool follow_sibling_ = 0; + File index_; + File file_; + File child_; + uint32_t index_file_storage_ = 0; + bool user_index_file_ = false; + int num_storage = 0; + const char **sd_str = 0; + uint32_t mode_ = 0; + uint32_t open_file_ = 0xFFFFFFFEUL; + uint8_t last_error_ = 0; + + // probably temporary... + uint32_t debug_read_record_count_ = 0; + uint32_t debug_fs_read_record_count_ = 0; + uint32_t debug_write_record_count_ = 0; + uint32_t debug_fs_write_record_count_ = 0; + + // experiment with building in SD Checking + // 0-not tested yet, 1-inserted, 0xff-not inserted + //static bool s_loop_fstypes_per_instance[MTP_FSTYPE_MAX]; + + uint32_t millis_atlast_device_check_ = 0; // can not use elapsedMillis as per const... + enum {DEFAULT_TIME_BETWEEN_DEVICE_CHECKS_MS = 200}; + uint32_t time_between_device_checks_ms_ = DEFAULT_TIME_BETWEEN_DEVICE_CHECKS_MS; + uint8_t media_present[MTPD_MAX_FILESYSTEMS] = {0}; + + #if MTP_RECORD_BLOCKS + static RecordBlock recordBlocks_[MTP_RECORD_BLOCKS]; + static RecordBlockInfo recordBlocksInfo_[MTP_RECORD_BLOCKS]; + uint16_t map_objectid_to_block_cycle_ = 0; // used to measure how long ago a block was touched... + void ClearRecordBlock(uint8_t index); + uint8_t CacheRecordBlock(uint16_t block_index); + int16_t maxrecordBlockWritten_ = -1; + #endif +}; + +void mtp_yield(void); + +#endif // USB_MTPDISK or USB_MTPDISK_SERIAL diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Teensy.cpp b/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Teensy.cpp new file mode 100644 index 00000000..e58d9ffc --- /dev/null +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Teensy.cpp @@ -0,0 +1,2557 @@ +// MTP.cpp - Teensy MTP Responder library +// Copyright (C) 2017 Fredrik Hubinette +// +// With updates from MichaelMC and Yoong Hor Meng +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// modified for SDFS by WMXZ + +#if defined(USB_MTPDISK) || defined(USB_MTPDISK_SERIAL) + +#define USE_DISK_BUFFER // only currently on T4.x + +#include "MTP_Teensy.h" +#include "MTP_Const.h" +#undef USB_DESC_LIST_DEFINE +#include "usb_desc.h" + +#if defined(__IMXRT1062__) +// following only while usb_mtp is not included in cores +#include "usb_mtp.h" +#endif + +#include "usb_names.h" +extern struct usb_string_descriptor_struct usb_string_serial_number; + +// define global mtpd object; +MTP_class MTP; + + +// Define some of the static members +Stream *MTP_class::printStream_ = &Serial; + +#if defined(__IMXRT1062__) +DMAMEM uint8_t MTP_class::disk_buffer_[DISK_BUFFER_SIZE] __attribute__((aligned(32))); +#endif + +#define DEBUG 0 +#if DEBUG > 0 +#define printf(...) printStream_->printf(__VA_ARGS__) +#else +#define printf(...) +#endif +#if DEBUG > 2 +#define DBGPRINTF(...) printf_debug(__VA_ARGS__) +extern "C" { +void printf_debug(const char *format, ...); +} +#else +#define DBGPRINTF(...) +#endif + + +// Define global(static) members +uint32_t MTP_class::sessionID_ = 0; + + +//*************************************************************************** +// Top level - public functions called by user program +//*************************************************************************** +// test/hack have usb_mtp.c call us on first usb message.. +void usb_mtp_first_rx_cb() { + MTP.begin(); // +} + + +int MTP_class::begin() { + // maybe don't do anything if already running interval timer + if (g_pmtpd_interval) { + printf("MTP.begin previously called"); + return 0; + } + // lets set up to check for MTP messages and tell + // other side we are busy... Maybe should be function: +#if defined(__IMXRT1062__) + transmit_packet_size_mask = usb_mtp_rxSize() - 1; +#endif + g_pmtpd_interval = this; + printf("\n\n*** Start Interval Timer at ms:%u ***\n", millis()); + g_intervaltimer.begin(&_interval_timer_handler, 50000); // 20 Hz + return usb_init_events(); +} + +void MTP_class::loop(void) { + if (g_pmtpd_interval) { + g_pmtpd_interval = nullptr; // clear out timer. + g_intervaltimer.end(); // try maybe 20 times per second... + printf("*** end Interval Timer ***\n"); + } + if (receive_bulk(0)) { + if (receive_buffer.len >= 12 && receive_buffer.len <= 32) { + // This container holds the operation code received from host + // Commands which transmit a 12 byte header as the first part + // of their data phase will reuse this container, overwriting + // the len & type fields, but keeping op and transaction_id. + // Then this container is again reused to transmit the final + // response code, keeping the original transaction_id, but + // the other 3 header fields are based on "return_code". If + // the response requires parameters, they are written into + // this container's parameter list. + struct MTPContainer container; + memset(&container, 0, sizeof(container)); + memcpy(&container, receive_buffer.data, receive_buffer.len); + free_received_bulk(); + printContainer(&container, "loop:"); + + int p1 = container.params[0]; + int p2 = container.params[1]; + int p3 = container.params[2]; + TID = container.transaction_id; + + // The low 16 bits of return_code have the response code + // operation field. The top 4 bits indicate the number + // of parameters to transmit with the response code. + int return_code = 0x2001; // OK use as default value + bool send_reset_event = false; + + if (container.type == MTP_CONTAINER_TYPE_COMMAND) { + switch (container.op) { + case 0x1001: // GetDeviceInfo + return_code = GetDeviceInfo(container); + break; + case 0x1002: // OpenSession + return_code = OpenSession(container); + break; + case 0x1003: // CloseSession + printf("MTP_class::CloseSession\n"); + sessionID_ = 0; // + break; + case 0x1004: // GetStorageIDs + return_code = GetStorageIDs(container); + break; + case 0x1005: // GetStorageInfo + return_code = GetStorageInfo(container); + break; + case 0x1006: // GetNumObjects + return_code = GetNumObjects(container); + break; + case 0x1007: // GetObjectHandles + return_code = GetObjectHandles(container); + break; + case 0x1008: // GetObjectInfo + return_code = GetObjectInfo(container); + break; + case 0x1009: // GetObject + return_code = GetObject(container); + break; + case 0x100B: // DeleteObject + if (p2) { + return_code = 0x2014; // spec by format unsupported + } else { + if (!storage_.DeleteObject(p1)) { + return_code = 0x2012; // partial deletion + } + } + break; + case 0x100C: // SendObjectInfo + return_code = SendObjectInfo(container); + break; + case 0x100D: // SendObject + return_code = SendObject(container); + break; + case 0x100F: // FormatStore + return_code = formatStore(container); + if (return_code == MTP_RESPONSE_OK) send_reset_event = true; + break; + case 0x1014: // GetDevicePropDesc + return_code = GetDevicePropDesc(container); + break; + case 0x1015: // GetDevicePropvalue + return_code = GetDevicePropValue(container); + break; + case 0x1010: // Reset + return_code = 0x2005; + break; + case 0x1019: // MoveObject + return_code = moveObject(p1, p2, p3); + break; + case 0x101A: // CopyObject + return_code = copyObject(p1, p2, p3); + if (!return_code) { + return_code = 0x2005; + } else { + container.params[0] = return_code; + uint8_t error_code = storage_.getLastError(); + switch (error_code) { + default: + return_code = 0x2001; + break; + case MTPStorage::RMDIR_FAIL: + case MTPStorage::WRITE_ERROR: + case MTPStorage::DEST_OPEN_FAIL: + return_code = MTP_RESPONSE_STORAGE_FULL; + break; + } + return_code |= (1<<28); + } + break; + case 0x101B: // GetPartialObject + return_code = GetPartialObject(container); + break; + case 0x9801: // GetObjectPropsSupported + return_code = GetObjectPropsSupported(container); + break; + case 0x9802: // GetObjectPropDesc + return_code = GetObjectPropDesc(container); + break; + case 0x9803: // GetObjectPropertyValue + return_code = GetObjectPropValue(container); + break; + case 0x9804: // setObjectPropertyValue + return_code = setObjectPropValue(container); + break; + default: + return_code = 0x2005; // operation not supported + break; + } + } else { + return_code = 0x2005; // we should only get cmds + printContainer(&container, "!!! unexpected/unknown message:"); + } + if (return_code && usb_mtp_status == 0x01) { + container.len = 12 + (return_code >> 28) * 4; // top 4 bits is number of parameters + container.type = MTP_CONTAINER_TYPE_RESPONSE; + container.op = (return_code & 0xFFFF); // low 16 bits is op response code + // container.transaction_id reused from original received command + #if DEBUG > 1 + printContainer(&container); // to switch on set debug to 2 at beginning of file + #endif + write(&container, container.len); + write_finish(); + + // Maybe some operations might need to tell host to do reset + // right now try after a format store. + if (send_reset_event) { + send_DeviceResetEvent(); + } + + } + } else { + printf("ERROR: loop received command with %u bytes\n", receive_buffer.len); + free_received_bulk(); + // TODO: what is the proper way to handle this error? + // Still Image Class spec 1.0 says on page 20: + // "If the number of bytes transferred in the Command phase is less than + // that specified in the first four bytes of the Command Block then the + // device has received an invalid command and should STALL the Bulk-Pipe + // (refer to Clause 7.2)." + // What are we supposed to do is too much data arrives? Or other invalid cmds? + } + } + + // check here to mske sure the USB status is reset + if (usb_mtp_status != 0x01) { + printf("MTP_class::Loop usb_mtp_status %x != 0x1 reset\n", usb_mtp_status); + usb_mtp_status = 0x01; + } + + // Storage loop() handles removable media insert / remove + storage_.loop(); +} + + +// IntervalTimer runs a mini version of loop() at 20 Hz, to keep quick response to host +// +MTP_class *MTP_class::g_pmtpd_interval = nullptr; +IntervalTimer MTP_class::g_intervaltimer; + +void MTP_class::_interval_timer_handler() { + if (g_pmtpd_interval) + g_pmtpd_interval->processIntervalTimer(); +} + +void MTP_class::processIntervalTimer() { + if (receive_bulk(0)) { + if (receive_buffer.len >= 12 && receive_buffer.len <= 32) { + struct MTPContainer container; + memset(&container, 0, sizeof(container)); + memcpy(&container, receive_buffer.data, receive_buffer.len); + free_received_bulk(); + printContainer(&container, "timer:"); // to switch on set debug to 1 at beginning of file + + TID = container.transaction_id; + uint32_t return_code = 0x2001; // 0x2001=OK + if (container.type == 1) { // command + switch (container.op) { + case MTP_OPERATION_GET_DEVICE_INFO: // GetDescription 0x1001 + return_code = GetDeviceInfo(container); + break; + case MTP_OPERATION_OPEN_SESSION: // open session 0x1002 + return_code = OpenSession(container); + break; + case MTP_OPERATION_GET_DEVICE_PROP_DESC: // 1014 + return_code = GetDevicePropDesc(container); + break; + case 0x1004: // GetStorageIDs 1004, needed by MacOS Android File Transfer app + return_code = GetStorageIDs(container); + break; + case 0x1005: // GetStorageInfo + return_code = GetStorageInfo(container, false); // media access not allowed for ISR + break; + case 0x9801: // GetObjectPropsSupported + return_code = GetObjectPropsSupported(container); + break; + default: + return_code = MTP_RESPONSE_DEVICE_BUSY; // busy 0x2019 + break; + } + } else { + // TODO: should this send 0x2005 MTP_RESPONSE_OPERATION_NOT_SUPPORTED ?? + return_code = MTP_RESPONSE_UNDEFINED; // undefined 0x2000 + } + container.type = 3; + container.len = 12; + container.op = return_code; +#if DEBUG > 1 + printContainer(&container); +#endif + allocate_transmit_bulk(); + memcpy(transmit_buffer.data, &container, container.len); + transmit_buffer.len = container.len; + transmit_bulk(); + } else { + printf("ERROR: intervaltimer received command with %u bytes\n", receive_buffer.len); + free_received_bulk(); + } + } +} + + +//*************************************************************************** +// MTP Commands - File Transfer and File Operations +//*************************************************************************** + +/* + struct MTPHeader { + uint32_t len; // 0 + uint16_t type; // 4 + uint16_t op; // 6 + uint32_t transaction_id; // 8 + }; + + struct MTPContainer { + uint32_t len; // 0 + uint16_t type; // 4 + uint16_t op; // 6 + uint32_t transaction_id; // 8 + uint32_t params[5]; // 12 + }; +*/ + +// When the host (your PC) wants to put a new file onto any of Teensy's drives +// first uses SendObjectInfo to tell us info about the file. Then SendObject +// is used to actually transfer the file's data. +// +// SendObjectInfo, MTP 1.1 spec, page 223 +// Command: 2 parameters: Destination StorageID, Parent ObjectHandle +// Data: PC->Teensy: ObjectInfo +// Response: 3 parameters: Destination StorageID, Parent ObjectHandle, ObjectHandle +uint32_t MTP_class::SendObjectInfo(struct MTPContainer &cmd) { // MTP 1.1 spec, page 223 + uint32_t storage = cmd.params[0]; + uint32_t parent = cmd.params[1]; + printf("SendObjectInfo: %x %x ", storage, parent); + uint32_t store = Storage2Store(storage); + struct MTPHeader header; + if (!readDataPhaseHeader(&header)) return MTP_RESPONSE_INVALID_DATASET; + printf("Dataset len=%u\n", header.len); + // receive ObjectInfo Dataset, MTP 1.1 spec, page 50 + char filename[MTP_MAX_FILENAME_LEN]; + uint16_t oformat; + uint32_t file_size; + if (read(NULL, 4) // StorageID (unused) + && read16(&oformat) // ObjectFormatCode + && read(NULL, 2) // Protection Status (unused) + && read32(&file_size) // Object Compressed Size + && read(NULL, 40) // Image info (unused) + && readstring(filename, sizeof(filename)) // Filename + && readDateTimeString(&dtCreated_) // Date Created + && readDateTimeString(&dtModified_) // Date Modified + && readstring(NULL, 0) // Keywords + && (true)) { // TODO: read complete function (handle ZLP) + printf("%s ", (oformat == 0x3001) ? "Dir" : "File"); + printf("\"%s\" ", filename); + printf("size:%u ", file_size); + printf("Created:%x ", dtCreated_); + printf("Modified:%x\n", dtModified_); + if (receive_buffer.data == NULL) { + printf(" read consumed all data (TODO: how to check ZLP)\n"); + // TODO: need to check for ZLP here.... + } else { + printf(" ERROR, receive buffer has %u bytes unused!\n", + receive_buffer.len - receive_buffer.index); + } + } else { + return MTP_RESPONSE_INVALID_DATASET; + } + // Lets see if we have enough room to store this file: + uint64_t free_space = storage_.totalSize(store) - storage_.usedSize(store); + if (file_size == 0xFFFFFFFFUL) { + printf("Size of object == 0xffffffff - Indicates >= 4GB file!\n \t?TODO: query real size? FS supports this - FAT32 no?\n"); + } + if (file_size > free_space) { + printf("Size of object:%u is > free space: %llu\n", file_size, free_space); + return MTP_RESPONSE_STORAGE_FULL; + } + const bool dir = (oformat == 0x3001); + object_id_ = storage_.Create(store, parent, dir, filename); + if (object_id_ == 0xFFFFFFFFUL) { + return MTP_RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED; + } + if (dir) { + // lets see if we should update the date and time stamps. + // if it is dirctory, then sendObject will not be called, so do it now. + if (!storage_.updateDateTimeStamps(object_id_, dtCreated_, dtModified_)) { + // BUGBUG: failed to update, maybe FS needs little time to settle in + // before trying this. + for (uint8_t i = 0; i < 10; i++) { + printf("!!!(%d) Try delay and call update time stamps again\n", i); + delay(25); + if (storage_.updateDateTimeStamps(object_id_, dtCreated_, dtModified_)) + break; + } + } + storage_.close(); + } + cmd.params[2] = object_id_; + return MTP_RESPONSE_OK | (3<<28); // response with 3 params +} + +// SendObject, MTP 1.1 spec, page 225 +// Command: no parameters +// Data: PC->Teensy: Binary Data +// Response: no parameters +#if defined(__IMXRT1062__) && defined(USE_DISK_BUFFER) +uint32_t MTP_class::SendObject(struct MTPContainer &cmd) { + MTPHeader header; + if (!readDataPhaseHeader(&header)) return MTP_RESPONSE_PARAMETER_NOT_SUPPORTED; + uint64_t size = header.len - sizeof(header); + printf("SendObject: %llu(0x%llx) bytes, id=%x\n", size, size, object_id_); + // TODO: check size matches file_size from SendObjectInfo + // TODO: check if object_id_ + // TODO: should we do storage_.Create() here? Can we preallocate file size? + uint32_t ret = MTP_RESPONSE_OK; + uint64_t pos = 0; + + // index into our disk buffer. + + bool huge_file = (size == 0xfffffffful); + if (huge_file) size = (uint64_t)-1; + uint64_t cb_left = size; + + #if DEBUG + elapsedMillis em_send; + elapsedMillis emPrint; + uint32_t count_reads = 0; + uint32_t to_copy_prev = 0; + #endif + + // lets go ahead and copy the rest of the first receive buffer into + // our disk buffer, so we don't have to play with starting index and the like... + uint16_t disk_buffer_index = receive_buffer.len - receive_buffer.index; + memcpy((char*)disk_buffer_, (char *)&receive_buffer.data[receive_buffer.index], disk_buffer_index); + pos = disk_buffer_index; + free_received_bulk(); + + + while (huge_file || (pos < size)) { + if (!receive_bulk(100)) { + if (pos <= 0xfffffffful) { + printf("SO: receive failed pos:%llu size:%llu\n", pos, size); + ret = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; + } else { + printf("SO: receive failed pos:%llu large file EOF\n", pos); + } + break; + } + + + uint32_t to_copy = receive_buffer.len; + + #if DEBUG + count_reads++; + if ((to_copy != to_copy_prev) || (emPrint > 15000)) { + printf("SO RC:%u CB:%u pos:%llu\n", count_reads, to_copy, pos); + to_copy_prev = to_copy; + emPrint = 0; + } + #endif + + uint16_t cb_buffer_avail = sizeof(disk_buffer_) - disk_buffer_index; + // See if this will fill the buffer; + if (cb_buffer_avail <= to_copy) { + memcpy(&disk_buffer_[disk_buffer_index], (char*)&receive_buffer.data[receive_buffer.index], cb_buffer_avail); + disk_buffer_index = 0; + if (storage_.write((char*)disk_buffer_, sizeof(disk_buffer_)) != sizeof(disk_buffer_)) { + ret = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; // TODO: best response for write error?? + // maybe send MTP_EVENT_CANCEL_TRANSACTION event?? + break; + } + if (cb_buffer_avail != to_copy) { + // copy in the remaining. + disk_buffer_index = to_copy - cb_buffer_avail; + memcpy(disk_buffer_, (char*)&receive_buffer.data[cb_buffer_avail], disk_buffer_index); + } + } else { + memcpy(&disk_buffer_[disk_buffer_index], (char*)receive_buffer.data, to_copy); + disk_buffer_index += to_copy; + } + + pos += to_copy; + cb_left -= to_copy; // + + free_received_bulk(); + if ((to_copy < 512) && (size == (uint64_t)-1) && (pos > 0xfffffffful)){ + printf("SendObject large EOF Detected: %lluu\n", pos); + break; + } + } + + + // clear out any trailing. + + + while (pos < size) { + // consume remaining incoming data, if we aborted for any reason + if (receive_buffer.data == NULL && !receive_bulk(250)) break; + uint16_t cb_packet = receive_buffer.len - receive_buffer.index; + pos += cb_packet; + free_received_bulk(); + if (cb_packet < 512) break; + } + // write out anything left in our disk buffer... + if (disk_buffer_index) { + if (storage_.write((char*)disk_buffer_, disk_buffer_index) != disk_buffer_index) { + ret = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; // TODO: best response for write error?? + // maybe send MTP_EVENT_CANCEL_TRANSACTION event?? + } + + } + + // TODO: check no lingering buffered data, and ZLP is present if expected + #if DEBUG + printf("SendObject complete pos:%u dt:%u\n", pos, (uint32_t)em_send / 1000); + #endif + + storage_.updateDateTimeStamps(object_id_, dtCreated_, dtModified_); + storage_.close(); + + if (ret == MTP_RESPONSE_OK) object_id_ = 0; // SendObjectInfo can not be reused after success + return ret; +} + +#else +uint32_t MTP_class::SendObject(struct MTPContainer &cmd) { + MTPHeader header; + if (!readDataPhaseHeader(&header)) return MTP_RESPONSE_PARAMETER_NOT_SUPPORTED; + uint64_t size = header.len - sizeof(header); + printf("SendObject: %llu(0x%llx) bytes, id=%x\n", size, size, object_id_); + // TODO: check size matches file_size from SendObjectInfo + // TODO: check if object_id_ + // TODO: should we do storage_.Create() here? Can we preallocate file size? + uint32_t ret = MTP_RESPONSE_OK; + uint64_t pos = 0; + uint32_t count_reads = 0; + uint32_t to_copy_prev = 0; + bool huge_file = (size == 0xfffffffful); + if (huge_file) size = (uint64_t)-1; + uint64_t cb_left = size; + while (huge_file || (pos < size)) { + if (receive_buffer.data == NULL) { + if (!receive_bulk(100)) { + printf("SO: receive failed pos:%llu size:%llu\n", pos, size); + ret = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; + break; + } + } + uint32_t to_copy = receive_buffer.len - receive_buffer.index; + count_reads++; + if (to_copy != to_copy_prev) { + printf("SO RC:%u CB:%u pos:%llu\n", count_reads, to_copy, pos); + to_copy_prev = to_copy;; + } + + + //if (to_copy > size) to_copy = size; // did not make sense to me + if (to_copy > cb_left) to_copy = cb_left; + + //printf("SendObject, pos=%u, write=%u, size=%u\n", pos, to_copy, size); + bool ok = storage_.write((char *)(receive_buffer.data + receive_buffer.index), to_copy); + if (!ok) { + ret = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; // TODO: best response for write error?? + // maybe send MTP_EVENT_CANCEL_TRANSACTION event?? + break; + } + pos += to_copy; + cb_left -= to_copy; // + receive_buffer.index += to_copy; + if (receive_buffer.index >= receive_buffer.len) { + free_received_bulk(); + #if 1 + if ((to_copy < 512) && (pos > 0xfffffffful)) { + printf(">4gb file EOF detected pos:%llu\n", pos); + break; + } + #endif + } + } + while ((pos < size) && (pos < 0xfffffffful)) { + // consume remaining incoming data, if we aborted for any reason + if (receive_buffer.data == NULL && !receive_bulk(250)) break; + pos += receive_buffer.len - receive_buffer.index; + free_received_bulk(); + } + // TODO: check no lingering buffered data, and ZLP is present if expected + printf("SendObject complete\n"); + storage_.updateDateTimeStamps(object_id_, dtCreated_, dtModified_); + storage_.close(); + + if (ret == MTP_RESPONSE_OK) object_id_ = 0; // SendObjectInfo can not be reused after success + return ret; +} +#endif + + +// When the host (your PC) wants to put a read a file from any of Teensy's drives +// first it uses GetObjectInfo to request all the file's metadata. Then GetObject +// is used to read the actual file data. +// +// GetObjectInfo, MTP 1.1 spec, page 218 +// Command: 1 parameter: ObjectHandle +// Data: Teensy->PC: ObjectInfo +// Response: no parameters +uint32_t MTP_class::GetObjectInfo(struct MTPContainer &cmd) { + uint32_t handle = cmd.params[0]; + uint32_t parent, dt; + uint64_t size; + char filename[MTP_MAX_FILENAME_LEN], ctimebuf[16], mtimebuf[16]; + DateTimeFields dtf; + uint16_t store; + storage_.GetObjectInfo(handle, filename, &size, &parent, &store); + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wformat-truncation" /* Or "-Wformat-truncation" */ + if (storage_.getCreateTime(handle, dt)) { + breakTime(dt, dtf); + snprintf(ctimebuf, sizeof(ctimebuf), "%04u%02u%02uT%02u%02u%02u", + dtf.year + 1900, dtf.mon + 1, dtf.mday, dtf.hour, dtf.min, + dtf.sec); + } else { + ctimebuf[0] = 0; + } + if (storage_.getModifyTime(handle, dt)) { + breakTime(dt, dtf); + snprintf(mtimebuf, sizeof(mtimebuf), "%04u%02u%02uT%02u%02u%02u", + dtf.year + 1900, dtf.mon + 1, dtf.mday, dtf.hour, dtf.min, + dtf.sec); + } else { + mtimebuf[0] = 0; + } +#pragma GCC diagnostic pop + + writeDataPhaseHeader(cmd, + 4 + 2 + 2 + 4 + 2 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + 2 + 4 + 4 + + writestringlen(filename) + writestringlen(ctimebuf) + + writestringlen(mtimebuf) + writestringlen("")); + + // if size is > 4gb we need to send the size 0xfffffffful and it may then ask us for real size. + bool is_directory = (size == (uint64_t)-1); + if (size > 0xfffffffful) size = 0xfffffffful; + + uint32_t storage = Store2Storage(store); + write32(storage); // storage + write16(is_directory ? 0x3001 : 0x0000); // format + write16(0); // protection + write32(size); // size + write16(0); // thumb format + write32(0); // thumb size + write32(0); // thumb width + write32(0); // thumb height + write32(0); // pix width + write32(0); // pix height + write32(0); // bit depth + write32(parent); // parent + write16(is_directory ? 1 : 0); // association type + write32(0); // association description + write32(0); // sequence number + writestring(filename); // filename + writestring(ctimebuf); // date created + writestring(mtimebuf); // date modified + writestring(""); // keywords + write_finish(); + return MTP_RESPONSE_OK; +} + + +// GetObject, MTP 1.1 spec, page 219 +// Command: 1 parameter: ObjectHandle +// Data: Teensy->PC: Binary Data +// Response: no parameters +#if defined(__IMXRT1062__) && defined(USE_DISK_BUFFER) +// experiment again on T4.x use 4k buffer disk_buffer_ +uint32_t MTP_class::GetObject(struct MTPContainer &cmd) { + uint16_t cb_read = 0; + uint64_t disk_pos = 0; + uint64_t pos = 0; + + const int object_id = cmd.params[0]; + uint64_t size = storage_.GetSize(object_id); + uint64_t count_remaining = size; + + writeDataPhaseHeader(cmd, (size > 0xfffffffful)? 0xfffffffful : size); + + printf("GetObject, size=%llu\n", size); + #if DEBUG + elapsedMillis emTotal; + elapsedMillis emPrint; + #endif + while (count_remaining) { + if (usb_mtp_status != 0x01) { + printf("GetObject, abort status:%x\n", usb_mtp_status); + return 0; + } + + // Lets make it real simple for now. + cb_read = storage_.read(object_id, disk_pos, (char*)disk_buffer_, sizeof(disk_buffer_)); + if (cb_read == 0) { + break; + } + size_t cb_written = write(disk_buffer_, cb_read); + if (cb_written != cb_read) { + printf("GetObject, write count error: %u != %u\n", cb_written, cb_read); + break; + } + count_remaining -= cb_read; + pos += cb_read; + disk_pos += cb_read; + + #if DEBUG + if (emPrint >= 15000) { + uint32_t percent_done = (pos * 100ull) / size; + printf("\tdt:%u, pos:%llu %u%%\n", (uint32_t)emTotal/1000, pos, percent_done); + emPrint = 0; + } + #endif + } + write_finish(); + printf("GetObject, done pos:%llu size:%llu dt:%u\n", pos, size, (uint32_t)emTotal/1000); + return MTP_RESPONSE_OK; +} + +#else +uint32_t MTP_class::GetObject(struct MTPContainer &cmd) { + const int object_id = cmd.params[0]; + uint64_t size = storage_.GetSize(object_id); + printf("GetObject, size=%llu\n", size); + writeDataPhaseHeader(cmd, (size > 0xfffffffful)? 0xfffffffful : size); + uint64_t pos = 0; + #if DEBUG + elapsedMillis emPrint; + #endif + while (pos < size) { + if (usb_mtp_status != 0x01) { + printf("GetObject, abort status:%x\n", usb_mtp_status); + return 0; + } + if (transmit_buffer.data == NULL) allocate_transmit_bulk(); + uint32_t avail = transmit_buffer.size - transmit_buffer.len; + uint64_t to_copy = size - pos; + if (to_copy > avail) to_copy = avail; + // Read directly from storage into usb buffer. + uint32_t cb_read = storage_.read(object_id, pos, + (char *)(transmit_buffer.data + transmit_buffer.len), to_copy); + #if DEBUG + if ((emPrint >= 15000) || (cb_read != 512)) { + printf("\tGO: read=%u, pos=%llu, Read:%u\n", to_copy, pos, cb_read); + emPrint = 0; + } + #endif + if (cb_read == 0) break; + pos += to_copy; + transmit_buffer.len += to_copy; + if (transmit_buffer.len >= transmit_buffer.size) { + transmit_bulk(); + } + } + write_finish(); + printf("GetObject, done pos:%llu size:%llu\n", pos, size); + return MTP_RESPONSE_OK; +} +#endif + +// GetPartialObject, MTP 1.1 spec, page 240 +// Command: 3 parameters: ObjectHandle, Offset in bytes, Maximum number of bytes +// Data: Teensy->PC: binary data +// Response: 1 parameter: Actual number of bytes sent +uint32_t MTP_class::GetPartialObject(struct MTPContainer &cmd) { + uint32_t object_id = cmd.params[0]; + uint32_t offset = cmd.params[1]; + uint64_t NumBytes = cmd.params[2]; + if (NumBytes == 0xfffffffful) NumBytes = (uint64_t)-1; + uint64_t size = storage_.GetSize(object_id); + printf("GetPartialObject: %x Of:%u NB:%u CB:%llu, FS:%llu\n", object_id, offset, cmd.params[2], NumBytes, size); + if (offset >= size) { + // writeDataPhaseHeader(cmd, 0); ??? + return MTP_RESPONSE_INVALID_PARAMETER; + } + if (NumBytes > size - offset) { + NumBytes = size - offset; + } + writeDataPhaseHeader(cmd, NumBytes); + uint32_t pos = offset; // into data + uint32_t end = offset + NumBytes; + while (pos < end) { + if (usb_mtp_status != 0x01) { + printf("GetPartialObject, abort\n"); + return 0; + } + if (transmit_buffer.data == NULL) allocate_transmit_bulk(); + uint32_t avail = transmit_buffer.size - transmit_buffer.len; + uint32_t to_copy = end - pos; + if (to_copy > avail) to_copy = avail; + storage_.read(object_id, pos, + (char *)(transmit_buffer.data + transmit_buffer.len), to_copy); + pos += to_copy; + transmit_buffer.len += to_copy; + if (transmit_buffer.len >= transmit_buffer.size) { + transmit_bulk(); + } + } + write_finish(); + cmd.params[0] = (NumBytes < 0xfffffffful)? NumBytes : 0xfffffffful; + return MTP_RESPONSE_OK + (1<<28); +} + + +// DeleteObject, MTP 1.1 spec, page 221 +// Command: 3 parameters: ObjectHandle, [ObjectFormatCode] +// Data: none +// Response: no parameters +uint32_t MTP_class::deleteObject(uint32_t handle) { + if (!storage_.DeleteObject(handle)) { + return 0x2012; // partial deletion + } + return 0x2001; +} + +// MoveObject, MTP 1.1 spec, page 238 +// Command: 3 parameters: ObjectHandle, StorageID target, ObjectHandle of the new ParentObject +// Data: none +// Response: no parameters +uint32_t MTP_class::moveObject(uint32_t handle, uint32_t newStorage, + uint32_t newHandle) { + uint32_t store1 = Storage2Store(newStorage); + if (newHandle == 0) newHandle = store1; + + if (storage_.move(handle, store1, newHandle)) + return 0x2001; + else + return 0x2005; +} + +// CopyObject, MTP 1.1 spec, page 239 +// Command: 3 parameters: ObjectHandle, StorageID target, ObjectHandle of the new ParentObject +// Data: none +// Response: no parameters +uint32_t MTP_class::copyObject(uint32_t handle, uint32_t newStorage, + uint32_t newHandle) { + uint32_t store1 = Storage2Store(newStorage); + if (newHandle == 0) newHandle = store1; + + return storage_.copy(handle, store1, newHandle); +} + +// FormatStore, MTP 1.1 spec, page 228 +// Command: 2 parameters: StorageID, [FileSystem Format] +// Data: none +// Response: no parameters +uint32_t MTP_class::formatStore(struct MTPContainer &cmd) { + printf("formatStore begin\n"); + const uint32_t store = Storage2Store(cmd.params[0]); + const uint32_t format = cmd.params[1]; + g_pmtpd_interval = this; + dtFormatStart_ = millis(); // remember when format started + g_intervaltimer.begin(&_interval_timer_handler, 50000); // 20 Hz + #if DEBUG > 0 + elapsedMillis msec = 0; + #endif + uint8_t success = storage_.formatStore(store, format); + if (g_pmtpd_interval) g_intervaltimer.end(); + printf("formatStore success=%u, format took %u ms\n", success, (uint32_t)msec); + if (success) { + // The caller of this funciton will call device reset. + // which when new session is started should reset the index + //storage_.ResetIndex(); // maybe should add a less of sledge hammer here. + // send_DeviceResetEvent(); + return MTP_RESPONSE_OK; + } else { + return MTP_RESPONSE_OPERATION_NOT_SUPPORTED; + } +} + + + + + +//*************************************************************************** +// MTP Commands - Metadata, Capability Detection, and Boring Stuff +//*************************************************************************** + + +// GetStorageIDs, MTP 1.1 spec, page 213 +// Command: no parameters +// Data: Teensy->PC: StorageID array (page 45) +// Response: no parameters +uint32_t MTP_class::GetStorageIDs(struct MTPContainer &cmd) { + uint32_t num = storage_.get_FSCount(); + // first count the number of filesystems + uint32_t num_valid = 0; + for (uint32_t store = 0; store < num; store++) { + FS *fs = storage_.getStoreFS(store); + if (fs && storage_.isMediaPresent(store)) num_valid++; + } + writeDataPhaseHeader(cmd, 4 + num_valid * 4); + write32(num_valid); // number of storages (disks) + for (uint32_t store = 0; store < num; store++) { + FS *fs = storage_.getStoreFS(store); + if (fs && storage_.isMediaPresent(store)) { + // page 213 says "Removable storages with no inserted media shall be returned + // in the dataset returned by this operation as well, though they would contain + // a value of 0x0000 in the lower 16 bits indicating that they are not present" + // However, Linux seems to get confused by these StorageIDs. Because Windows + // just hides them anyway, we'll not send these StorageID for removed media. + uint32_t StorageID = Store2Storage(store); + printf("\t%u(%s %s) StorageID=%08X\n", store, // FIXME: printing maybe ISR unsafe? // FIXME: printing maybe ISR unsafe? + storage_.get_FSName(store), fs->name(), StorageID); + write32(StorageID); // storage id + } + } + write_finish(); + storage_ids_sent_ = true; + return MTP_RESPONSE_OK; +} + +// GetStorageInfo, MTP 1.1 spec, page 214 +// Command: 1 parameter: StorageID +// Data: Teensy->PC: StorageInfo (page 46) +// Response: no parameters +uint32_t MTP_class::GetStorageInfo(struct MTPContainer &cmd, bool mediaAccessAllowed) { + uint32_t storage = cmd.params[0]; + uint32_t store = Storage2Store(storage); + FS *fs = storage_.getStoreFS(store); + if (fs == nullptr) { + printf("MTP_class::GetStorageInfo %u is not valid (FS nullptr)\n", store); + return MTP_RESPONSE_STORE_NOT_AVAILABLE; + } + if (!storage_.isMediaPresent(store)) { + printf("MTP_class::GetStorageInfo %u(%s) removable media not present\n", store, name); + // TODO: is this correct response for removable media not present? + return MTP_RESPONSE_STORE_NOT_AVAILABLE; + } + const char *name = storage_.get_FSName(store); + const char *volname = fs->name(); // assume no media access if previously called + if (volname) { + name = volname; + } else if (!name) { + name = "Untitled"; + } + uint32_t size = 2 + 2 + 2 + 8 + 8 + 4 + writestringlen(name) + writestringlen(""); + writeDataPhaseHeader(cmd, size); + // StorageInfo, MTP 1.1 spec, page 46 + write16(storage_.readonly(store) ? 0x0001 + : 0x0004); // storage type (removable RAM) + write16(storage_.has_directories(store) + ? 0x0002 + : 0x0001); // filesystem type (generic hierarchical) + write16(0x0000); // access capability (read-write) + + //elapsedMillis em; + uint64_t ntotal = storage_.totalSize(store, mediaAccessAllowed); + write64(ntotal); // max capacity + uint64_t nused = storage_.usedSize(store, mediaAccessAllowed); + write64((ntotal - nused)); // free space (100M) + //printf("GetStorageInfo dt:%u tot:%lu, used: %lu\n", (uint32_t)em, ntotal, nused); + write32(0xFFFFFFFFUL); // free space (objects) + writestring(name); // storage descriptor + writestring(""); // volume identifier (neither Windows nor Linux seem to use this) + write_finish(); + printf("\t%x name:%s\n", storage, name); + return MTP_RESPONSE_OK; +} + +// GetNumObjects, MTP 1.1 spec, page 215 +// Command: 3 parameters: StorageID, [ObjectFormatCode], [Parent folder] +// Data: none +// Response: 1 parameter: NumObjects +uint32_t MTP_class::GetNumObjects(struct MTPContainer &cmd) { + uint32_t storage = cmd.params[0]; + uint32_t format = cmd.params[1]; + uint32_t parent = cmd.params[2]; + if (format) { + return MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED; + } + unsigned int num = 0; + uint32_t store = Storage2Store(storage); + if (storage_.isMediaPresent(store)) { + storage_.StartGetObjectHandles(store, parent); + while (storage_.GetNextObjectHandle(store)) { + num++; + } + } + cmd.params[0] = num; + return MTP_RESPONSE_OK | (1<<28); +} + +// GetObjectHandles, MTP 1.1 spec, page 217 +// Command: 3 parameters: StorageID, [ObjectFormatCode], [Parent folder] +// Data: Teensy->PC: ObjectHandle array +// Response: no parameters +uint32_t MTP_class::GetObjectHandles(struct MTPContainer &cmd) { + uint32_t storage = cmd.params[0]; + uint32_t format = cmd.params[1]; + uint32_t parent = cmd.params[2]; + if (format) { + writeDataPhaseHeader(cmd, 4); + write32(0); // empty ObjectHandle array + write_finish(); + return MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED; + } + const uint32_t store = Storage2Store(storage); + uint32_t num_handles = 0; + if (storage_.isMediaPresent(store)) { + storage_.StartGetObjectHandles(store, parent); + while (storage_.GetNextObjectHandle(store)) { + num_handles++; + } + } + writeDataPhaseHeader(cmd, 4 + num_handles*4); + if (storage_.isMediaPresent(store)) { + // ObjectHandle array, page 23 (ObjectHandle), page 20 (array) + write32(num_handles); + uint32_t handle; + storage_.StartGetObjectHandles(store, parent); + while ((handle = storage_.GetNextObjectHandle(store)) != 0) { + write32(handle); + } + } + write_finish(); + return MTP_RESPONSE_OK; +} + +// GetDevicePropValue, MTP 1.1 spec, page 234 +// Command: 1 parameter: DevicePropCode +// Data: Teensy->PC: DeviceProp Value +// Response: no parameters +uint32_t MTP_class::GetDevicePropValue(struct MTPContainer &cmd) { + const uint32_t property = cmd.params[0]; + switch (property) { + case 0xd402: // friendly name + writeDataPhaseHeader(cmd, writestringlen(MTP_NAME)); + // This is the name we'll actually see in the windows explorer. + // Should probably be configurable. + writestring(MTP_NAME); + write_finish(); + return MTP_RESPONSE_OK; + break; + } + writeDataPhaseHeader(cmd, 0); + return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; +} + +// GetObjectPropDesc, MTP 1.1 spec, page 244 +// Command: 2 parameter: ObjectPropCode, Object Format Code +// Data: Teensy->PC: ObjectPropDesc +// Response: no parameters +uint32_t MTP_class::GetObjectPropDesc(struct MTPContainer &cmd) { + uint32_t property = cmd.params[0]; + //uint32_t format = cmd.params[1]; // TODO: does this matter? + uint32_t size; + switch (property) { + case MTP_PROPERTY_STORAGE_ID: // 0xDC01: + size = 2 + 2 + 1 + 4 + 4 + 1; + break; + case MTP_PROPERTY_OBJECT_FORMAT: // 0xDC02: + size = 2 + 2 + 1 + 2 + 4 + 1; + break; + case MTP_PROPERTY_PROTECTION_STATUS: // 0xDC03: + size = 2 + 2 + 1 + 2 + 4 + 1; + break; + case MTP_PROPERTY_OBJECT_SIZE: // 0xDC04: + size = 2 + 2 + 1 + 8 + 4 + 1; + break; + case MTP_PROPERTY_OBJECT_FILE_NAME: // 0xDC07: + size = 2 + 2 + 1 + 1 + 4 + 1; + break; + case MTP_PROPERTY_DATE_CREATED: // 0xDC08: + size = 2 + 2 + 1 + 1 + 4 + 1; + break; + case MTP_PROPERTY_DATE_MODIFIED: // 0xDC09: + size = 2 + 2 + 1 + 1 + 4 + 1; + break; + case MTP_PROPERTY_PARENT_OBJECT: // 0xDC0B: + size = 2 + 2 + 1 + 4 + 4 + 1; + break; + case MTP_PROPERTY_PERSISTENT_UID: // 0xDC41: + size = 2 + 2 + 1 + 8 + 8 + 4 + 1; + break; + case MTP_PROPERTY_NAME: // 0xDC44: + size = 2 + 2 + 1 + 1 + 4 + 1; + break; + default: + size = 0; + break; + } + writeDataPhaseHeader(cmd, size); + if (size == 0) { + write_finish(); // TODO: remove this when change to split header/data + return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE; + } + // ObjectPropDesc, MTP 1.1 spec, Table 5-1, page 56 + switch (property) { + case MTP_PROPERTY_STORAGE_ID: // 0xDC01: + write16(0xDC01); + write16(0x006); // 6=uint32_t + write8(0); // get + write32(0); + write32(0); + write8(0); + break; + case MTP_PROPERTY_OBJECT_FORMAT: // 0xDC02: + write16(0xDC02); + write16(0x004); // 4=uint16_t + write8(0); // get + write16(0); + write32(0); + write8(0); + break; + case MTP_PROPERTY_PROTECTION_STATUS: // 0xDC03: + write16(0xDC03); + write16(0x004); // 4=uint16_t + write8(0); // get + write16(0); + write32(0); + write8(0); + break; + case MTP_PROPERTY_OBJECT_SIZE: // 0xDC04: + write16(0xDC04); + write16(0x008); // 8=uint64_t + write8(0); // get + write64(0); + write32(0); + write8(0); + break; + case MTP_PROPERTY_OBJECT_FILE_NAME: // 0xDC07: + write16(0xDC07); + write16(0xFFFF); // FFFF=string + write8(1); // get/set + write8(0); + write32(0); + write8(0); + break; + case MTP_PROPERTY_DATE_CREATED: // 0xDC08: + write16(0xDC08); + write16(0xFFFF); // FFFF=string + write8(1); // get + write8(0); + write32(0); + write8(0); // TODO: should this be 3 (Form Flag on page 56) + break; + case MTP_PROPERTY_DATE_MODIFIED: // 0xDC09: + write16(0xDC09); + write16(0xFFFF); // FFFF=string + write8(1); // may be both get set? + write8(0); + write32(0); + write8(0); // TODO: should this be 3 (Form Flag on page 56) + break; + case MTP_PROPERTY_PARENT_OBJECT: // 0xDC0B: + write16(0xDC0B); + write16(6); // 6=uint32_t + write8(0); // get + write32(0); + write32(0); + write8(0); + break; + case MTP_PROPERTY_PERSISTENT_UID: // 0xDC41: + write16(0xDC41); + write16(0x0A); // A=uint128_t + write8(0); // get + write64(0); + write64(0); + write32(0); + write8(0); + break; + case MTP_PROPERTY_NAME: // 0xDC44: + write16(0xDC44); + write16(0xFFFF); + write8(0); // get + write8(0); + write32(0); + write8(0); + break; + } + write_finish(); + return MTP_RESPONSE_OK; +} + +// GetObjectPropValue, MTP 1.1 spec, page 245 +// Command: 2 parameters: ObjectHandle, ObjectPropCode +// Data: Teensy->PC: ObjectProp Value +// Response: no parameters +uint32_t MTP_class::GetObjectPropValue(struct MTPContainer &cmd) { + const uint32_t handle = cmd.params[0]; + const uint32_t property = cmd.params[1]; + uint32_t data_size = 0; + char name[MTP_MAX_FILENAME_LEN]; + uint64_t file_size; + uint32_t parent; + uint16_t store; + uint32_t dt; + DateTimeFields dtf; + uint32_t storage = 0; + bool info_needed = true; + + // first check if storage info is needed, and if data size is known + switch (property) { + case MTP_PROPERTY_STORAGE_ID: // 0xDC01: + data_size = 4; + break; + case MTP_PROPERTY_OBJECT_FORMAT: // 0xDC02: + data_size = 2; + break; + case MTP_PROPERTY_PROTECTION_STATUS: // 0xDC03: + data_size = 2; + info_needed = false; + break; + case MTP_PROPERTY_OBJECT_SIZE: // 0xDC04: + data_size = 8; + break; + case MTP_PROPERTY_NAME: // 0xDC44: + case MTP_PROPERTY_OBJECT_FILE_NAME: // 0xDC07: + break; + case MTP_PROPERTY_DATE_CREATED: // 0xDC08: + if (storage_.getCreateTime(handle, dt)) { + breakTime(dt, dtf); + snprintf(name, MTP_MAX_FILENAME_LEN, "%04u%02u%02uT%02u%02u%02u", + dtf.year + 1900, dtf.mon + 1, dtf.mday, dtf.hour, dtf.min, + dtf.sec); + } else { + name[0] = 0; + } + data_size = writestringlen(name); + info_needed = false; + break; + case MTP_PROPERTY_DATE_MODIFIED: // 0xDC09: + if (storage_.getModifyTime(handle, dt)) { + breakTime(dt, dtf); + snprintf(name, MTP_MAX_FILENAME_LEN, "%04u%02u%02uT%02u%02u%02u", + dtf.year + 1900, dtf.mon + 1, dtf.mday, dtf.hour, dtf.min, + dtf.sec); + } else { + name[0] = 0; + } + data_size = writestringlen(name); + info_needed = false; + break; + case MTP_PROPERTY_PARENT_OBJECT: // 0xDC0B: + data_size = 4; + break; + case MTP_PROPERTY_PERSISTENT_UID: // 0xDC41: + data_size = 4 + 4 + 4 + 4; + break; + } + + // get actual storage info, if needed + if (info_needed) { + storage_.GetObjectInfo(handle, name, &file_size, &parent, &store); + if (property == MTP_PROPERTY_OBJECT_FILE_NAME || property == MTP_PROPERTY_NAME) { + data_size = writestringlen(name); + } + storage = Store2Storage(store); + } + + // begin data phase + writeDataPhaseHeader(cmd, data_size); + if (data_size == 0) { + write_finish(); // TODO: remove this when change to split header/data + return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE; + } + + // transmit actual data + switch (property) { + case MTP_PROPERTY_STORAGE_ID: // 0xDC01: + write32(storage); + break; + case MTP_PROPERTY_OBJECT_FORMAT: // 0xDC02: + write16((file_size == (uint64_t)-1) ? 0x3001 /*directory*/ : 0x3000 /*file*/); + break; + case MTP_PROPERTY_PROTECTION_STATUS: // 0xDC03: + write16(0); + break; + case MTP_PROPERTY_OBJECT_SIZE: // 0xDC04: + write64(file_size); + printf("\tMTP_PROPERTY_OBJECT_SIZE: %s %llx\n", name, file_size); + //write32(file_size & 0xfffffffful); + //write32(file_size >> 32); + break; + case MTP_PROPERTY_OBJECT_FILE_NAME: // 0xDC07: + case MTP_PROPERTY_NAME: // 0xDC44: + case MTP_PROPERTY_DATE_CREATED: // 0xDC08: + case MTP_PROPERTY_DATE_MODIFIED: // 0xDC09: + writestring(name); + break; + case MTP_PROPERTY_PARENT_OBJECT: // 0xDC0B: + write32((store == parent) ? 0 : parent); + break; + case MTP_PROPERTY_PERSISTENT_UID: // 0xDC41: + write32(handle); + write32(parent); + write32(storage); + write32(0); + break; + } + write_finish(); + return MTP_RESPONSE_OK; +} + +// SetObjectPropValue, MTP 1.1 spec, page 246 +// Command: 2 parameters: ObjectHandle, ObjectPropCode +// Data: PC->Teensy: ObjectProp Value +// Response: no parameters +uint32_t MTP_class::setObjectPropValue(struct MTPContainer &cmd) { + uint32_t object_id = cmd.params[0]; + uint32_t property_code = cmd.params[1]; + + struct MTPHeader header; + if (!readDataPhaseHeader(&header)) return MTP_RESPONSE_INVALID_DATASET; + + if (property_code == 0xDC07) { + char filename[MTP_MAX_FILENAME_LEN]; + if (readstring(filename, sizeof(filename)) + && (true)) { // TODO: read complete function (handle ZLP) + printf("setObjectPropValue, rename id=%x to \"%s\"\n", object_id, filename); + storage_.rename(object_id, filename); + return MTP_RESPONSE_OK; + } else { + return MTP_RESPONSE_OPERATION_NOT_SUPPORTED; + } + } + read(NULL, header.len - sizeof(header)); // discard ObjectProp Value + return MTP_RESPONSE_OPERATION_NOT_SUPPORTED; +} + +// GetDevicePropDesc, MTP 1.1 spec, page 233 +// Command: 1 parameter: DevicePropCode +// Data: Teensy->PC: DevicePropDesc +// Response: no parameters +uint32_t MTP_class::GetDevicePropDesc(struct MTPContainer &cmd) { + const uint32_t property = cmd.params[0]; + switch (property) { + case 0xd402: // friendly name + writeDataPhaseHeader(cmd, 5 + writestringlen(MTP_NAME)*2 + 1); + // DevicePropDesc Dataset, MTP 1.1 spec, page 42 + write16(property); // Device Property Code + write16(0xFFFF); // Datatype, string type + write8(0); // read-only + writestring(MTP_NAME); // Factory Default Value + writestring(MTP_NAME); // Current Value + write8(0); // no form + write_finish(); + return MTP_RESPONSE_OK; + } + writeDataPhaseHeader(cmd, 0); + return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; +} + +// GetObjectPropsSupported, MTP 1.1 spec, page 243 +// Command: 1 parameter: ObjectFormatCode +// Data: Teensy->PC: ObjectPropCode Array +// Response: no parameters +uint32_t MTP_class::GetObjectPropsSupported(struct MTPContainer &cmd) { + PROGMEM static const uint16_t propertyList[] = { + MTP_PROPERTY_STORAGE_ID, // 0xDC01 + MTP_PROPERTY_OBJECT_FORMAT, // 0xDC02 + MTP_PROPERTY_PROTECTION_STATUS, // 0xDC03 + MTP_PROPERTY_OBJECT_SIZE, // 0xDC04 + MTP_PROPERTY_OBJECT_FILE_NAME, // 0xDC07 + MTP_PROPERTY_DATE_CREATED, // 0xDC08 + MTP_PROPERTY_DATE_MODIFIED, // 0xDC09 + MTP_PROPERTY_PARENT_OBJECT, // 0xDC0B + MTP_PROPERTY_PERSISTENT_UID, // 0xDC41 + MTP_PROPERTY_NAME // 0xDC44 + }; + const uint32_t propertyListNum = sizeof(propertyList) / sizeof(propertyList[0]); + //uint32_t format = cmd.params[0]; // TODO: does this matter? + writeDataPhaseHeader(cmd, 4 + sizeof(propertyList)); + write32(propertyListNum); + write(propertyList, sizeof(propertyList)); + write_finish(); + return MTP_RESPONSE_OK; +} + +// GetDeviceInfo, MTP 1.1 spec, page 210 +// Command: no parameters +// Data: Teensy->PC: DeviceInfo +// Response: no parameters +uint32_t MTP_class::GetDeviceInfo(struct MTPContainer &cmd) { + PROGMEM static const unsigned short supported_op[] = { + MTP_OPERATION_GET_DEVICE_INFO, // 0x1001 + MTP_OPERATION_OPEN_SESSION, // 0x1002 + MTP_OPERATION_CLOSE_SESSION, // 0x1003 + MTP_OPERATION_GET_STORAGE_IDS, // 0x1004 + MTP_OPERATION_GET_STORAGE_INFO, // 0x1005 + // MTP_OPERATION_GET_NUM_OBJECTS ,//0x1006 + MTP_OPERATION_GET_OBJECT_HANDLES, // 0x1007 + MTP_OPERATION_GET_OBJECT_INFO, // 0x1008 + MTP_OPERATION_GET_OBJECT, // 0x1009 + // MTP_OPERATION_GET_THUMB ,//0x100A + MTP_OPERATION_DELETE_OBJECT, // 0x100B + MTP_OPERATION_SEND_OBJECT_INFO, // 0x100C + MTP_OPERATION_SEND_OBJECT, // 0x100D + MTP_OPERATION_FORMAT_STORE, // 0x100F + MTP_OPERATION_GET_DEVICE_PROP_DESC, // 0x1014 + MTP_OPERATION_GET_DEVICE_PROP_VALUE, // 0x1015 + // MTP_OPERATION_SET_DEVICE_PROP_VALUE ,//0x1016 + // MTP_OPERATION_RESET_DEVICE_PROP_VALUE ,//0x1017 + MTP_OPERATION_MOVE_OBJECT, // 0x1019 + MTP_OPERATION_COPY_OBJECT, // 0x101A + MTP_OPERATION_GET_PARTIAL_OBJECT, // 0x101B + MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, // 0x9801 + MTP_OPERATION_GET_OBJECT_PROP_DESC, // 0x9802 + MTP_OPERATION_GET_OBJECT_PROP_VALUE, // 0x9803 + MTP_OPERATION_SET_OBJECT_PROP_VALUE // 0x9804 + // MTP_OPERATION_GET_OBJECT_PROP_LIST ,//0x9805 + // MTP_OPERATION_GET_OBJECT_REFERENCES ,//0x9810 + // MTP_OPERATION_SET_OBJECT_REFERENCES ,//0x9811 + // MTP_OPERATION_GET_PARTIAL_OBJECT_64 ,//0x95C1 + // MTP_OPERATION_SEND_PARTIAL_OBJECT ,//0x95C2 + // MTP_OPERATION_TRUNCATE_OBJECT ,//0x95C3 + // MTP_OPERATION_BEGIN_EDIT_OBJECT ,//0x95C4 + // MTP_OPERATION_END_EDIT_OBJECT //0x95C5 + }; + const int supported_op_num = sizeof(supported_op) / sizeof(supported_op[0]); + PROGMEM static const uint16_t supported_events[] = { + // MTP_EVENT_UNDEFINED ,//0x4000 + MTP_EVENT_CANCEL_TRANSACTION, // 0x4001 + MTP_EVENT_OBJECT_ADDED, // 0x4002 + MTP_EVENT_OBJECT_REMOVED, // 0x4003 + MTP_EVENT_STORE_ADDED, // 0x4004 + MTP_EVENT_STORE_REMOVED, // 0x4005 + // MTP_EVENT_DEVICE_PROP_CHANGED ,//0x4006 + // MTP_EVENT_OBJECT_INFO_CHANGED ,//0x4007 + // MTP_EVENT_DEVICE_INFO_CHANGED ,//0x4008 + // MTP_EVENT_REQUEST_OBJECT_TRANSFER ,//0x4009 + // MTP_EVENT_STORE_FULL ,//0x400A + MTP_EVENT_DEVICE_RESET, // 0x400B + MTP_EVENT_STORAGE_INFO_CHANGED, // 0x400C + // MTP_EVENT_CAPTURE_COMPLETE ,//0x400D + MTP_EVENT_UNREPORTED_STATUS, // 0x400E + MTP_EVENT_OBJECT_PROP_CHANGED, // 0xC801 + // MTP_EVENT_OBJECT_PROP_DESC_CHANGED ,//0xC802 + // MTP_EVENT_OBJECT_REFERENCES_CHANGED //0xC803 + }; + const int supported_event_num = sizeof(supported_events) / sizeof(supported_events[0]); + char buf[20]; + dtostrf((float)(TEENSYDUINO / 100.0f), 3, 2, buf); + strlcat(buf, " / MTP " MTP_VERS, sizeof(buf)); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" + char sernum[10]; + for (size_t i = 0; i < 10; i++) + sernum[i] = usb_string_serial_number.wString[i]; +#pragma GCC diagnostic pop + // DeviceInfo Dataset, MTP 1.1 spec, page 40 + uint32_t size = 2 + 4 + 2 + writestringlen("microsoft.com: 1.0;") + 2 + + 4 + sizeof(supported_op) + + 4 + sizeof(supported_events) + + 4 + 2 + 4 + 4 + 2*2 + + writestringlen(MTP_MANUF) + + writestringlen(MTP_MODEL) + + writestringlen(buf) + + writestringlen(sernum); + printf("GetDeviceInfo size=%u\n", size); + writeDataPhaseHeader(cmd, size); + write16(100); // MTP version + write32(6); // MTP extension + write16(100); // MTP version + writestring("microsoft.com: 1.0;"); + write16(1); // functional mode + write32(supported_op_num); // Supported operations (array of uint16) + write(supported_op, sizeof(supported_op)); + write32(supported_event_num); // Events (array of uint16) + write(supported_events, sizeof(supported_events)); + write32(1); // Device properties (array of uint16) + write16(0xd402); // Device friendly name + write32(0); // Capture formats (array of uint16) + write32(2); // Playback formats (array of uint16) + write16(0x3000); // Undefined format + write16(0x3001); // Folders (associations) + writestring(MTP_MANUF); // Manufacturer + writestring(MTP_MODEL); // Model + writestring(buf); // version + writestring(sernum); // serial number + write_finish(); + return MTP_RESPONSE_OK; +} + +// OpenSession, MTP 1.1 spec, page 211 +// Command: 1 parameter: SessionID +// Data: none +// Response: no parameters +uint32_t MTP_class::OpenSession(struct MTPContainer &cmd) { + sessionID_ = cmd.params[0]; + storage_.ResetIndex(); + return MTP_RESPONSE_OK; +} + + + + + + +//*************************************************************************** +// Generic data read / write +//*************************************************************************** + + +bool MTP_class::readDataPhaseHeader(struct MTPHeader *header) { + if (!read(header, sizeof(struct MTPHeader))) return false; + if (header && header->type != MTP_CONTAINER_TYPE_DATA) return false; + // TODO: when we later implement split header + data USB optimization + // described in MTP 1.1 spec pages 281-282, we should check that + // receive_buffer.data is NULL. Return false if unexpected data. + return true; +} + +bool MTP_class::readstring(char *buffer, uint32_t buffer_size) { + uint8_t len; + if (!read8(&len)) return false; + if (len == 0) { + if (buffer) *buffer = 0; // empty string + return true; + } + unsigned int buffer_index = 0; + for (unsigned int string_index = 0; string_index < len; string_index++) { + uint16_t c; + if (!read16(&c)) return false; + if (string_index == (unsigned)(len-1) && c != 0) return false; // last char16 must be zero + if (buffer) { + // encode Unicode16 -> UTF8 + if (c < 0x80 && buffer_index < buffer_size-2) { + buffer[buffer_index++] = c & 0x7F; + } else if (c < 0x800 && buffer_index < buffer_size-3) { + buffer[buffer_index++] = 0xC0 | ((c >> 6) & 0x1F); + buffer[buffer_index++] = 0x80 | (c & 0x3F); + } else if (buffer_index < buffer_size-4) { + buffer[buffer_index++] = 0xE0 | ((c >> 12) & 0x0F); + buffer[buffer_index++] = 0x80 | ((c >> 6) & 0x3F); + buffer[buffer_index++] = 0x80 | (c & 0x3F); + } else { + while (buffer_index < buffer_size) buffer[buffer_index++] = 0; + buffer = nullptr; + } + } + } + if (buffer) buffer[buffer_index] = 0; // redundant?? (last char16 must be zero) + return true; +} + +bool MTP_class::readDateTimeString(uint32_t *pdt) { + char dtb[20]; // let it take care of the conversions. + if (!readstring(dtb, sizeof(dtb))) return false; + if (dtb[0] == 0) { + // host sent empty string, use time from Teensy's RTC + *pdt = Teensy3Clock.get(); + return true; + } + //printf(" DateTime string: %s\n", dtb); + // 01234567890123456 + // format of expected String: YYYYMMDDThhmmss.s + if (strlen(dtb) < 15) return false; + for (int i=0; i < 15; i++) { + if (i == 8) { + if (dtb[i] != 'T') return false; + } else { + if (dtb[i] < '0' || dtb[i] > '9') return false; + } + } + DateTimeFields dtf; + // Quick and dirty! + uint16_t year = ((dtb[0] - '0') * 1000) + ((dtb[1] - '0') * 100) + + ((dtb[2] - '0') * 10) + (dtb[3] - '0'); + dtf.year = year - 1900; // range 70-206 + dtf.mon = ((dtb[4] - '0') * 10) + (dtb[5] - '0') - 1; // zero based not 1 + dtf.mday = ((dtb[6] - '0') * 10) + (dtb[7] - '0'); + dtf.wday = 0; // hopefully not needed... + dtf.hour = ((dtb[9] - '0') * 10) + (dtb[10] - '0'); + dtf.min = ((dtb[11] - '0') * 10) + (dtb[12] - '0'); + dtf.sec = ((dtb[13] - '0') * 10) + (dtb[14] - '0'); + *pdt = makeTime(dtf); + //printf(">> date/Time: %x %u/%u/%u %u:%u:%u\n", *pdt, dtf.mon + 1, dtf.mday, + //year, dtf.hour, dtf.min, dtf.sec); + return true; +} + + +bool MTP_class::read(void *ptr, uint32_t size) { + char *data = (char *)ptr; + while (size > 0) { + if (receive_buffer.data == NULL) { + if (!receive_bulk(100)) { + if (data) memset(data, 0, size); + return false; + } + } + // TODO: what happens if read spans multiple packets? Do any cases exist? + uint32_t to_copy = receive_buffer.len - receive_buffer.index; + if (to_copy > size) to_copy = size; + if (data) { + memcpy(data, receive_buffer.data + receive_buffer.index, to_copy); + data += to_copy; + } + size -= to_copy; + receive_buffer.index += to_copy; + if (receive_buffer.index >= receive_buffer.len) { + free_received_bulk(); + } + } + return true; +} + + + + +void MTP_class::writeDataPhaseHeader(struct MTPContainer &container, uint32_t data_size) +{ + container.len = data_size + 12; + container.type = MTP_CONTAINER_TYPE_DATA; + // container.op reused from received command container + // container.transaction_id reused from received command container + write(&container, 12); + // TODO: when we later implement split header + data USB optimization + // described in MTP 1.1 spec pages 281-282, we will need to + // call transmit_bulk() here to transmit a partial packet +} + + +static int utf8_strlen(const char *str) { + if (!str) return 0; + int len=0, count=0; + while (1) { + unsigned int c = *str++; + if (c == 0) return len; + if ((c & 0x80) == 0) { + len++; + count = 0; + } else if ((c & 0xC0) == 0x80 && count > 0) { + if (--count == 0) len++; + } else if ((c & 0xE0) == 0xC0) { + count = 1; + } else if ((c & 0xF0) == 0xE0) { + count = 2; + } else { + count = 0; + } + } +} + +void MTP_class::writestring(const char *str) { + int len = utf8_strlen(str); + if (len > 0) { + write8(len + 1); + int count = 0; + uint16_t char16 = 0; + while (1) { + unsigned int c = *str++; + if (c == 0) break; + if ((c & 0x80) == 0) { // chars 1-127 + write16(c); + count = 0; + } else if ((c & 0xC0) == 0x80 && count > 0) { // extra 6 bits + char16 = (char16 << 6) | (c & 0x3F); + if (--count == 0) write16(char16); + } else if ((c & 0xE0) == 0xC0) { // begin char 128-2047 + char16 = c & 0x1F; + count = 1; + } else if ((c & 0xF0) == 0xE0) { // begin char 2048-65535 + char16 = c & 0x0F; + count = 2; + } else { // chars 65536+ not supported + count = 0; + } + } + write16(0); + } else { + write8(0); + } +} + +uint32_t MTP_class::writestringlen(const char *str) { + int len = utf8_strlen(str); + if (len == 0) return 1; + return len*2 + 2 + 1; +} + +size_t MTP_class::write(const void *ptr, size_t len) { + size_t len_in = len; + const char *data = (const char *)ptr; + while (len > 0) { + if (usb_mtp_status != 0x01) { + printf("write, abort\n"); + return 0; + } + + if (transmit_buffer.data == NULL) allocate_transmit_bulk(); + unsigned int avail = transmit_buffer.size - transmit_buffer.len; + unsigned int to_copy = len; + if (to_copy > avail) to_copy = avail; + memcpy(transmit_buffer.data + transmit_buffer.len, data, to_copy); + data += to_copy; + len -= to_copy; + transmit_buffer.len += to_copy; + if (transmit_buffer.len >= transmit_buffer.size) { + transmit_bulk(); + } + } + return len_in; // for now we are not detecting errors. +} + +void MTP_class::write_finish() { + if (transmit_buffer.data == NULL) { + if (!write_transfer_open) return; + printf("send a ZLP\n"); + allocate_transmit_bulk(); + } + transmit_bulk(); +} + + + + +//*************************************************************************** +// USB bulk endpoint low-level input & output +//*************************************************************************** + +#if defined(__MK20DX128__) || defined(__MK20DX256__) || \ + defined(__MK64FX512__) || defined(__MK66FX1M0__) + +bool MTP_class::receive_bulk(uint32_t timeout) { // T3 + elapsedMillis msec = 0; + while (msec <= timeout) { + usb_packet_t *packet = usb_rx(MTP_RX_ENDPOINT); + if (packet) { + receive_buffer.len = packet->len; + receive_buffer.index = 0; + receive_buffer.size = sizeof(packet->buf); + receive_buffer.data = packet->buf; + receive_buffer.usb = packet; + return true; + } + } + return false; +} + +void MTP_class::free_received_bulk() { // T3 + if (receive_buffer.usb) { + usb_free((usb_packet_t *)receive_buffer.usb); + } + receive_buffer.len = 0; + receive_buffer.data = NULL; + receive_buffer.usb = NULL; +} + +void MTP_class::allocate_transmit_bulk() { // T3 + while (1) { + usb_packet_t *packet = usb_malloc(); + if (packet) { + transmit_buffer.len = 0; + transmit_buffer.index = 0; + transmit_buffer.size = sizeof(packet->buf); + transmit_buffer.data = packet->buf; + transmit_buffer.usb = packet; + return; + } + mtp_yield(); + } +} + +int MTP_class::transmit_bulk() { // T3 + usb_packet_t *packet = (usb_packet_t *)transmit_buffer.usb; + int len = transmit_buffer.len; + packet->len = len; + write_transfer_open = (len == 64); + usb_tx(MTP_TX_ENDPOINT, packet); + transmit_buffer.len = 0; + transmit_buffer.index = 0; + transmit_buffer.data = NULL; + transmit_buffer.usb = NULL; + return len; +} + +// TODO: core library not yet implementing cancel on Teensy 3.x +uint8_t MTP_class::usb_mtp_status = 0x01; + + +#elif defined(__IMXRT1062__) + +bool MTP_class::receive_bulk(uint32_t timeout) { // T4 + if (usb_mtp_status != 0x01) { + receive_buffer.data = NULL; + return false; + } + receive_buffer.index = 0; + receive_buffer.size = MTP_RX_SIZE; + receive_buffer.usb = NULL; + receive_buffer.len = usb_mtp_recv(rx_data_buffer, timeout); + if (receive_buffer.len > 0) { + // FIXME: need way to receive ZLP + receive_buffer.data = rx_data_buffer; + return true; + } else { + receive_buffer.data = NULL; + return false; + } +} + +void MTP_class::free_received_bulk() { // T4 + receive_buffer.len = 0; + receive_buffer.data = NULL; +} + +void MTP_class::allocate_transmit_bulk() { // T4 + transmit_buffer.len = 0; + transmit_buffer.index = 0; + transmit_buffer.size = usb_mtp_txSize(); + transmit_buffer.data = tx_data_buffer; + transmit_buffer.usb = NULL; +} + +int MTP_class::transmit_bulk() { // T4 + int r = 0; + if (usb_mtp_status == 0x01) { + write_transfer_open = (transmit_buffer.len > 0 && (transmit_buffer.len & transmit_packet_size_mask) == 0); + usb_mtp_send(transmit_buffer.data, transmit_buffer.len, 50); + } + transmit_buffer.len = 0; + transmit_buffer.index = 0; + transmit_buffer.data = NULL; + return r; +} + +#endif // __IMXRT1062__ + + + + +//*************************************************************************** +// MTP Events - inform the host of changes Teensy makes to files +//*************************************************************************** + + +#if USE_EVENTS == 1 + +#if defined(__MK20DX128__) || defined(__MK20DX256__) || \ + defined(__MK64FX512__) || defined(__MK66FX1M0__) + +#include "usb_mtp.h" +extern "C" { +usb_packet_t *tx_event_packet = NULL; + +int usb_init_events(void) { // T3 + // tx_event_packet = usb_malloc(); + // if(tx_event_packet) return 1; else return 0; + return 1; +} + +#define EVENT_TX_PACKET_LIMIT 4 + +int usb_mtp_sendEvent(const void *buffer, uint32_t len, uint32_t timeout) { // T3 +// digitalWriteFast(4, HIGH); + usb_packet_t *event_packet; + // printf("usb_mtp_sendEvent: called %x %x\n", (uint32_t)buffer, len); + struct MTPContainer { + uint32_t len; // 0 + uint16_t type; // 4 + uint16_t op; // 6 + uint32_t transaction_id; // 8 + uint32_t params[5]; // 12 + } __attribute__((__may_alias__)); + + //const MTPContainer *pe = (const MTPContainer *)buffer; + // printf(" op:%x len:%d type:%d tid:%d Params: ", pe->op, pe->len, + // pe->type, pe->transaction_id); + //if(pe->len>12) printf(" %x", pe->params[0]); + //if(pe->len>16) printf(" %x", pe->params[1]); + //if(pe->len>20) printf(" %x", pe->params[2]); + //printf("\n"); + + if (!usb_configuration) + return -1; + elapsedMillis em = 0; + while (1) { + if (!usb_configuration) { +// digitalWriteFast(4, LOW); + return -1; + } + if (usb_tx_packet_count(MTP_EVENT_ENDPOINT) < EVENT_TX_PACKET_LIMIT) { + event_packet = usb_malloc(); + if (event_packet) + break; + } + if (em > timeout) { +// digitalWriteFast(4, LOW); + return -1; + } + yield(); + } + + memcpy(event_packet->buf, buffer, len); + event_packet->len = len; + usb_tx(MTP_EVENT_ENDPOINT, event_packet); +// digitalWriteFast(4, LOW); + return len; +} +} // extern c + +#elif defined(__IMXRT1062__) +// keep this here until cores is upgraded + +#include "usb_mtp.h" +extern "C" { +static transfer_t tx_event_transfer[1] __attribute__((used, aligned(32))); +static uint8_t tx_event_buffer[MTP_EVENT_SIZE] + __attribute__((used, aligned(32))); + +int usb_init_events(void) { // T4 + // usb_config_tx(MTP_EVENT_ENDPOINT, MTP_EVENT_SIZE, 0, txEvent_event); + // + // usb_config_rx(MTP_EVENT_ENDPOINT, MTP_EVENT_SIZE, 0, rxEvent_event); + // usb_prepare_transfer(rx_event_transfer + 0, rx_event_buffer, + // MTP_EVENT_SIZE, 0); + // usb_receive(MTP_EVENT_ENDPOINT, rx_event_transfer + 0); + return 1; +} + +static int usb_mtp_wait(transfer_t *xfer, uint32_t timeout) { // T4 + uint32_t wait_begin_at = systick_millis_count; + while (1) { + if (!usb_configuration) + return -1; // usb not enumerated by host + uint32_t status = usb_transfer_status(xfer); + if (!(status & 0x80)) + break; // transfer descriptor ready + if (systick_millis_count - wait_begin_at > timeout) + return 0; + yield(); + } + return 1; +} + +int usb_mtp_recvEvent(void *buffer, uint32_t len, uint32_t timeout) { // T4 +#if 0 + int ret= usb_mtp_wait(rx_event_transfer, timeout); if(ret<=0) return ret; + + memcpy(buffer, rx_event_buffer, len); + memset(rx_event_transfer, 0, sizeof(rx_event_transfer)); + + NVIC_DISABLE_IRQ(IRQ_USB1); + usb_prepare_transfer(rx_event_transfer + 0, rx_event_buffer, MTP_EVENT_SIZE, 0); + usb_receive(MTP_EVENT_ENDPOINT, rx_event_transfer + 0); + NVIC_ENABLE_IRQ(IRQ_USB1); +#endif + return MTP_EVENT_SIZE; +} + +int usb_mtp_sendEvent(const void *buffer, uint32_t len, uint32_t timeout) { // T4 + transfer_t *xfer = tx_event_transfer; + int ret = usb_mtp_wait(xfer, timeout); + if (ret <= 0) + return ret; + + uint8_t *eventdata = tx_event_buffer; + memcpy(eventdata, buffer, len); + usb_prepare_transfer(xfer, eventdata, len, 0); + usb_transmit(MTP_EVENT_ENDPOINT, xfer); + return len; +} +} // extern "C" + +#endif // __IMXRT1062__ + + + +const uint32_t EVENT_TIMEOUT = 60; + +int MTP_class::send_Event(uint16_t eventCode) { + printf("*MTP_class::send_Event(%x)\n", eventCode); + MTPContainer event; + event.len = 12; + event.op = eventCode; + event.type = MTP_CONTAINER_TYPE_EVENT; + event.transaction_id = TID; + event.params[0] = 0; + event.params[1] = 0; + event.params[2] = 0; + return usb_mtp_sendEvent((const void *)&event, event.len, EVENT_TIMEOUT); +} +int MTP_class::send_Event(uint16_t eventCode, uint32_t p1) { + printf("*MTP_class::send_Event(%x) %x\n", eventCode, p1); + MTPContainer event; + event.len = 16; + event.op = eventCode; + event.type = MTP_CONTAINER_TYPE_EVENT; + event.transaction_id = TID; + event.params[0] = p1; + event.params[1] = 0; + event.params[2] = 0; + return usb_mtp_sendEvent((const void *)&event, event.len, EVENT_TIMEOUT); +} +int MTP_class::send_Event(uint16_t eventCode, uint32_t p1, uint32_t p2) { + printf("*MTP_class::send_Event(%x) %x %x\n", eventCode, p1, p2); + MTPContainer event; + event.len = 20; + event.op = eventCode; + event.type = MTP_CONTAINER_TYPE_EVENT; + event.transaction_id = TID; + event.params[0] = p1; + event.params[1] = p2; + event.params[2] = 0; + return usb_mtp_sendEvent((const void *)&event, event.len, EVENT_TIMEOUT); +} +int MTP_class::send_Event(uint16_t eventCode, uint32_t p1, uint32_t p2, + uint32_t p3) { + printf("*MTP_class::send_Event(%x) %x %x %x\n", eventCode, p1, p2, p3); + MTPContainer event; + event.len = 24; + event.op = eventCode; + event.type = MTP_CONTAINER_TYPE_EVENT; + event.transaction_id = TID; + event.params[0] = p1; + event.params[1] = p2; + event.params[2] = p3; + return usb_mtp_sendEvent((const void *)&event, event.len, EVENT_TIMEOUT); +} + +int MTP_class::send_DeviceResetEvent(void) { + storage_ids_sent_ = false; // clear it for now + return send_Event(MTP_EVENT_DEVICE_RESET); +} +// following WIP +int MTP_class::send_StorageInfoChangedEvent(uint32_t p1) { + return send_Event(MTP_EVENT_STORAGE_INFO_CHANGED, Store2Storage(p1)); +} + +// following not tested +int MTP_class::send_addObjectEvent(uint32_t p1) { + return send_Event(MTP_EVENT_OBJECT_ADDED, p1); +} +int MTP_class::send_removeObjectEvent(uint32_t p1) { + return send_Event(MTP_EVENT_OBJECT_REMOVED, p1); +} + +int MTP_class::send_StoreAddedEvent(uint32_t store) { + if (!storage_ids_sent_) return 0; // Don't need to send. + + return send_Event(MTP_EVENT_STORE_ADDED, Store2Storage(store)); +} + +int MTP_class::send_StoreRemovedEvent(uint32_t store) { + if (!storage_ids_sent_) return 0; // Don't need to send. + return send_Event(MTP_EVENT_STORE_REMOVED, Store2Storage(store)); +} + +bool MTP_class::send_addObjectEvent(uint32_t store, const char *pathname) { + bool node_added = false; + uint32_t handle = + storage_.MapFileNameToIndex(store, pathname, true, &node_added); + printf("notifyFileCreated: %x:%x maps to handle: %x\n", store, pathname, + handle); + if (handle != 0xFFFFFFFFUL) { + send_addObjectEvent(handle); + return true; + } + return false; +} + +bool MTP_class::send_removeObjectEvent(uint32_t store, const char *pathname) { + uint32_t handle = + storage_.MapFileNameToIndex(store, pathname, false, nullptr); + printf("notifyFileRemoved: %x:%x maps to handle: %x\n", store, pathname, + handle); + if (handle != 0xFFFFFFFFUL) { + send_removeObjectEvent(handle); + return true; + } + return false; +} + +#endif // USE_EVENTS + + + + +//*************************************************************************** +// Debug printing +//*************************************************************************** + + +void MTP_class::printFilesystemsInfo(Stream &stream) { + unsigned int count = storage_.get_FSCount(); + stream.println(); + stream.print("Storage List, "); + stream.print(count); + stream.println(" Filesystems"); + for (unsigned int i=0; i < count; i++) { + FS *fs = storage_.getStoreFS(i); + if (fs != nullptr) { + stream.print(" store:"); + stream.print(i); + stream.print(" storage:"); + stream.print(Store2Storage(i), HEX); + stream.print(" present:"); + stream.print(storage_.isMediaPresent(i) ? "Yes" : "No "); + stream.print(" fs:"); + stream.print((uint32_t)fs, HEX); + stream.print(" name:\""); + stream.print(storage_.get_FSName(i)); + stream.print("\" fsname:\""); + const char *volname = fs->name(); // requires updated core lib + if (!volname) volname = "Untitled"; + stream.print(volname); + stream.println("\""); + } + } +} + +void MTP_class::printContainer(const void *container, const char *msg) { + const struct MTPContainer *c = (const struct MTPContainer *)container; +#ifndef MTP_VERBOSE_PRINT_CONTAINER + printf("%x %d %d %d: ", c->op, c->len, c->type, c->transaction_id); + if (c->len > 12) { + printf(" %x", c->params[0]); + } + if (c->len > 16) { + printf(" %x", c->params[1]); + } + if (c->len > 20) { + printf(" %x", c->params[2]); + } + printf("\n"); +} +#else // MTP_VERBOSE_PRINT_CONTAINER + int print_property_name = -1; // no + if (msg) { + printf("%s", msg); + DBGPRINTF("%s", msg); + } + printf("%u ", millis()); + switch (c->type) { + default: + printf(" UNKWN: %x\n", c->type); + DBGPRINTF("UNKWN: %x l:%d\n", c->op, c->len); + //MemoryHexDump(*printStream_, (void*)c, 512, true); + printf(" UNKWN: %x\n", c->type); // print it again... + break; + case MTP_CONTAINER_TYPE_COMMAND: + printf(F("CMD: ")); + DBGPRINTF("CMD: %x l:%d\n", c->op, c->len); + break; + case MTP_CONTAINER_TYPE_DATA: + printf(F("DATA:")); + DBGPRINTF("DATA: %x l:%d\n", c->op, c->len); + break; + case MTP_CONTAINER_TYPE_RESPONSE: + printf(F("RESP:")); + DBGPRINTF("RESP: %x l:%d\n", c->op, c->len); + break; + case MTP_CONTAINER_TYPE_EVENT: + printf(F("EVENT: ")); + DBGPRINTF("EVENT: %x\n", c->op); + break; + } + printf(F("%x"), c->op); + switch (c->op) { + case MTP_OPERATION_GET_DEVICE_INFO: + printf(F("(GET_DEVICE_INFO)")); + break; + case MTP_OPERATION_OPEN_SESSION: + printf(F("(OPEN_SESSION)")); + break; + case MTP_OPERATION_CLOSE_SESSION: + printf(F("(CLOSE_SESSION)")); + break; + case MTP_OPERATION_GET_STORAGE_IDS: + printf(F("(GET_STORAGE_IDS)")); + break; + case MTP_OPERATION_GET_STORAGE_INFO: + printf(F("(GET_STORAGE_INFO)")); + break; + case MTP_OPERATION_GET_NUM_OBJECTS: + printf(F("(GET_NUM_OBJECTS)")); + break; + case MTP_OPERATION_GET_OBJECT_HANDLES: + printf(F("(GET_OBJECT_HANDLES)")); + break; + case MTP_OPERATION_GET_OBJECT_INFO: + printf(F("(GET_OBJECT_INFO)")); + break; + case MTP_OPERATION_GET_OBJECT: + printf(F("(GET_OBJECT)")); + break; + case MTP_OPERATION_GET_THUMB: + printf(F("(GET_THUMB)")); + break; + case MTP_OPERATION_DELETE_OBJECT: + printf(F("(DELETE_OBJECT)")); + break; + case MTP_OPERATION_SEND_OBJECT_INFO: + printf(F("(SEND_OBJECT_INFO)")); + break; + case MTP_OPERATION_SEND_OBJECT: + printf(F("(SEND_OBJECT)")); + break; + case MTP_OPERATION_INITIATE_CAPTURE: + printf(F("(INITIATE_CAPTURE)")); + break; + case MTP_OPERATION_FORMAT_STORE: + printf(F("(FORMAT_STORE)")); + break; + case MTP_OPERATION_RESET_DEVICE: + printf(F("(RESET_DEVICE)")); + break; + case MTP_OPERATION_SELF_TEST: + printf(F("(SELF_TEST)")); + break; + case MTP_OPERATION_SET_OBJECT_PROTECTION: + printf(F("(SET_OBJECT_PROTECTION)")); + break; + case MTP_OPERATION_POWER_DOWN: + printf(F("(POWER_DOWN)")); + break; + case MTP_OPERATION_GET_DEVICE_PROP_DESC: + printf(F("(GET_DEVICE_PROP_DESC)")); + break; + case MTP_OPERATION_GET_DEVICE_PROP_VALUE: + printf(F("(GET_DEVICE_PROP_VALUE)")); + break; + case MTP_OPERATION_SET_DEVICE_PROP_VALUE: + printf(F("(SET_DEVICE_PROP_VALUE)")); + break; + case MTP_OPERATION_RESET_DEVICE_PROP_VALUE: + printf(F("(RESET_DEVICE_PROP_VALUE)")); + break; + case MTP_OPERATION_TERMINATE_OPEN_CAPTURE: + printf(F("(TERMINATE_OPEN_CAPTURE)")); + break; + case MTP_OPERATION_MOVE_OBJECT: + printf(F("(MOVE_OBJECT)")); + break; + case MTP_OPERATION_COPY_OBJECT: + printf(F("(COPY_OBJECT)")); + break; + case MTP_OPERATION_GET_PARTIAL_OBJECT: + printf(F("(GET_PARTIAL_OBJECT)")); + break; + case MTP_OPERATION_INITIATE_OPEN_CAPTURE: + printf(F("(INITIATE_OPEN_CAPTURE)")); + break; + case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED: + printf(F("(GET_OBJECT_PROPS_SUPPORTED)")); + break; + case MTP_OPERATION_GET_OBJECT_PROP_DESC: + printf(F("(GET_OBJECT_PROP_DESC)")); + print_property_name = 0; + break; + case MTP_OPERATION_GET_OBJECT_PROP_VALUE: + printf(F("(GET_OBJECT_PROP_VALUE)")); + print_property_name = 1; + break; + case MTP_OPERATION_SET_OBJECT_PROP_VALUE: + printf(F("(SET_OBJECT_PROP_VALUE)")); + break; + case MTP_OPERATION_GET_OBJECT_PROP_LIST: + printf(F("(GET_OBJECT_PROP_LIST)")); + break; + case MTP_OPERATION_SET_OBJECT_PROP_LIST: + printf(F("(SET_OBJECT_PROP_LIST)")); + break; + case MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC: + printf(F("(GET_INTERDEPENDENT_PROP_DESC)")); + break; + case MTP_OPERATION_SEND_OBJECT_PROP_LIST: + printf(F("(SEND_OBJECT_PROP_LIST)")); + break; + case MTP_OPERATION_GET_OBJECT_REFERENCES: + printf(F("(GET_OBJECT_REFERENCES)")); + break; + case MTP_OPERATION_SET_OBJECT_REFERENCES: + printf(F("(SET_OBJECT_REFERENCES)")); + break; + case MTP_OPERATION_SKIP: + printf(F("(SKIP)")); + break; + // RESPONSES + case MTP_RESPONSE_UNDEFINED: + printf(F("(RSP:UNDEFINED)")); + break; + case MTP_RESPONSE_OK: + printf(F("(RSP:OK)")); + break; + case MTP_RESPONSE_GENERAL_ERROR: + printf(F("(RSP:GENERAL_ERROR)")); + break; + case MTP_RESPONSE_SESSION_NOT_OPEN: + printf(F("(RSP:SESSION_NOT_OPEN)")); + break; + case MTP_RESPONSE_INVALID_TRANSACTION_ID: + printf(F("(RSP:INVALID_TRANSACTION_ID)")); + break; + case MTP_RESPONSE_OPERATION_NOT_SUPPORTED: + printf(F("(RSP:OPERATION_NOT_SUPPORTED)")); + break; + case MTP_RESPONSE_PARAMETER_NOT_SUPPORTED: + printf(F("(RSP:PARAMETER_NOT_SUPPORTED)")); + break; + case MTP_RESPONSE_INCOMPLETE_TRANSFER: + printf(F("(RSP:INCOMPLETE_TRANSFER)")); + break; + case MTP_RESPONSE_INVALID_STORAGE_ID: + printf(F("(RSP:INVALID_STORAGE_ID)")); + break; + case MTP_RESPONSE_INVALID_OBJECT_HANDLE: + printf(F("(RSP:INVALID_OBJECT_HANDLE)")); + break; + case MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED: + printf(F("(RSP:DEVICE_PROP_NOT_SUPPORTED)")); + break; + case MTP_RESPONSE_INVALID_OBJECT_FORMAT_CODE: + printf(F("(RSP:INVALID_OBJECT_FORMAT_CODE)")); + break; + case MTP_RESPONSE_STORAGE_FULL: + printf(F("(RSP:STORAGE_FULL)")); + break; + case MTP_RESPONSE_OBJECT_WRITE_PROTECTED: + printf(F("(RSP:OBJECT_WRITE_PROTECTED)")); + break; + case MTP_RESPONSE_STORE_READ_ONLY: + printf(F("(RSP:STORE_READ_ONLY)")); + break; + case MTP_RESPONSE_ACCESS_DENIED: + printf(F("(RSP:ACCESS_DENIED)")); + break; + case MTP_RESPONSE_NO_THUMBNAIL_PRESENT: + printf(F("(RSP:NO_THUMBNAIL_PRESENT)")); + break; + case MTP_RESPONSE_SELF_TEST_FAILED: + printf(F("(RSP:SELF_TEST_FAILED)")); + break; + case MTP_RESPONSE_PARTIAL_DELETION: + printf(F("(RSP:PARTIAL_DELETION)")); + break; + case MTP_RESPONSE_STORE_NOT_AVAILABLE: + printf(F("(RSP:STORE_NOT_AVAILABLE)")); + break; + case MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED: + printf(F("(RSP:SPECIFICATION_BY_FORMAT_UNSUPPORTED)")); + break; + case MTP_RESPONSE_NO_VALID_OBJECT_INFO: + printf(F("(RSP:NO_VALID_OBJECT_INFO)")); + break; + case MTP_RESPONSE_INVALID_CODE_FORMAT: + printf(F("(RSP:INVALID_CODE_FORMAT)")); + break; + case MTP_RESPONSE_UNKNOWN_VENDOR_CODE: + printf(F("(RSP:UNKNOWN_VENDOR_CODE)")); + break; + case MTP_RESPONSE_CAPTURE_ALREADY_TERMINATED: + printf(F("(RSP:CAPTURE_ALREADY_TERMINATED)")); + break; + case MTP_RESPONSE_DEVICE_BUSY: + printf(F("(RSP:DEVICE_BUSY)")); + break; + case MTP_RESPONSE_INVALID_PARENT_OBJECT: + printf(F("(RSP:INVALID_PARENT_OBJECT)")); + break; + case MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT: + printf(F("(RSP:INVALID_DEVICE_PROP_FORMAT)")); + break; + case MTP_RESPONSE_INVALID_DEVICE_PROP_VALUE: + printf(F("(RSP:INVALID_DEVICE_PROP_VALUE)")); + break; + case MTP_RESPONSE_INVALID_PARAMETER: + printf(F("(RSP:INVALID_PARAMETER)")); + break; + case MTP_RESPONSE_SESSION_ALREADY_OPEN: + printf(F("(RSP:SESSION_ALREADY_OPEN)")); + break; + case MTP_RESPONSE_TRANSACTION_CANCELLED: + printf(F("(RSP:TRANSACTION_CANCELLED)")); + break; + case MTP_RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED: + printf(F("(RSP:SPECIFICATION_OF_DESTINATION_UNSUPPORTED)")); + break; + case MTP_RESPONSE_INVALID_OBJECT_PROP_CODE: + printf(F("(RSP:INVALID_OBJECT_PROP_CODE)")); + break; + case MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT: + printf(F("(RSP:INVALID_OBJECT_PROP_FORMAT)")); + break; + case MTP_RESPONSE_INVALID_OBJECT_PROP_VALUE: + printf(F("(RSP:INVALID_OBJECT_PROP_VALUE)")); + break; + case MTP_RESPONSE_INVALID_OBJECT_REFERENCE: + printf(F("(RSP:INVALID_OBJECT_REFERENCE)")); + break; + case MTP_RESPONSE_GROUP_NOT_SUPPORTED: + printf(F("(RSP:GROUP_NOT_SUPPORTED)")); + break; + case MTP_RESPONSE_INVALID_DATASET: + printf(F("(RSP:INVALID_DATASET)")); + break; + case MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED: + printf(F("(RSP:SPECIFICATION_BY_GROUP_UNSUPPORTED)")); + break; + case MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED: + printf(F("(RSP:SPECIFICATION_BY_DEPTH_UNSUPPORTED)")); + break; + case MTP_RESPONSE_OBJECT_TOO_LARGE: + printf(F("(RSP:OBJECT_TOO_LARGE)")); + break; + case MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED: + printf(F("(RSP:OBJECT_PROP_NOT_SUPPORTED)")); + break; + case MTP_EVENT_UNDEFINED: + printf(F("(EVT:UNDEFINED)")); + break; + case MTP_EVENT_CANCEL_TRANSACTION: + printf(F("(EVT:CANCEL_TRANSACTION)")); + break; + case MTP_EVENT_OBJECT_ADDED: + printf(F("(EVT:OBJECT_ADDED)")); + break; + case MTP_EVENT_OBJECT_REMOVED: + printf(F("(EVT:OBJECT_REMOVED)")); + break; + case MTP_EVENT_STORE_ADDED: + printf(F("(EVT:STORE_ADDED)")); + break; + case MTP_EVENT_STORE_REMOVED: + printf(F("(EVT:STORE_REMOVED)")); + break; + case MTP_EVENT_DEVICE_PROP_CHANGED: + printf(F("(EVT:DEVICE_PROP_CHANGED)")); + break; + case MTP_EVENT_OBJECT_INFO_CHANGED: + printf(F("(EVT:OBJECT_INFO_CHANGED)")); + break; + case MTP_EVENT_DEVICE_INFO_CHANGED: + printf(F("(EVT:DEVICE_INFO_CHANGED)")); + break; + case MTP_EVENT_REQUEST_OBJECT_TRANSFER: + printf(F("(EVT:REQUEST_OBJECT_TRANSFER)")); + break; + case MTP_EVENT_STORE_FULL: + printf(F("(EVT:STORE_FULL)")); + break; + case MTP_EVENT_DEVICE_RESET: + printf(F("(EVT:DEVICE_RESET)")); + break; + case MTP_EVENT_STORAGE_INFO_CHANGED: + printf(F("(EVT:STORAGE_INFO_CHANGED)")); + break; + case MTP_EVENT_CAPTURE_COMPLETE: + printf(F("(EVT:CAPTURE_COMPLETE)")); + break; + case MTP_EVENT_UNREPORTED_STATUS: + printf(F("(EVT:UNREPORTED_STATUS)")); + break; + case MTP_EVENT_OBJECT_PROP_CHANGED: + printf(F("(EVT:OBJECT_PROP_CHANGED)")); + break; + case MTP_EVENT_OBJECT_PROP_DESC_CHANGED: + printf(F("(EVT:OBJECT_PROP_DESC_CHANGED)")); + break; + case MTP_EVENT_OBJECT_REFERENCES_CHANGED: + printf(F("(EVT:OBJECT_REFERENCES_CHANGED)")); + break; + } + printf("l: %d", c->len); + printf(F(" T:%x"), c->transaction_id); + if (c->len >= 16) + printf(F(" : %x"), c->params[0]); + if (c->len >= 20) + printf(F(" %x"), c->params[1]); + if (c->len >= 24) + printf(F(" %x"), c->params[2]); + if (c->len >= 28) + printf(F(" %x"), c->params[3]); + if (c->len >= 32) + printf(F(" %x"), c->params[4]); + if (print_property_name >= 0) { + switch (c->params[print_property_name]) { + case MTP_PROPERTY_STORAGE_ID: + printf(" (STORAGE_ID)"); + break; + case MTP_PROPERTY_OBJECT_FORMAT: + printf(" (FORMAT)"); + break; + case MTP_PROPERTY_PROTECTION_STATUS: + printf(" (PROTECTION)"); + break; + case MTP_PROPERTY_OBJECT_SIZE: + printf(" (SIZE)"); + break; + case MTP_PROPERTY_OBJECT_FILE_NAME: + printf(" (OBJECT NAME)"); + break; + case MTP_PROPERTY_DATE_CREATED: + printf(" (CREATED)"); + break; + case MTP_PROPERTY_DATE_MODIFIED: + printf(" (MODIFIED)"); + break; + case MTP_PROPERTY_PARENT_OBJECT: + printf(" (PARENT)"); + break; + case MTP_PROPERTY_PERSISTENT_UID: + printf(" (PERSISTENT_UID)"); + break; + case MTP_PROPERTY_NAME: + printf(" (NAME)"); + break; + } + } + printf("\n"); +} +#endif // MTP_VERBOSE_PRINT_CONTAINER + + + +#endif // USB_MTPDISK or USB_MTPDISK_SERIAL diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Teensy.h b/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Teensy.h new file mode 100644 index 00000000..a4904bae --- /dev/null +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/MTP_Teensy.h @@ -0,0 +1,317 @@ +// MTP.h - Teensy MTP Responder library +// Copyright (C) 2017 Fredrik Hubinette +// +// With updates from MichaelMC and Yoong Hor Meng +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// modified for SDFS by WMXZ +// Modified by KurtE and mjs513 for Teensy Integration. + +#pragma once + +#if defined(USB_MTPDISK) || defined(USB_MTPDISK_SERIAL) + +#include "IntervalTimer.h" +#include "core_pins.h" +#include "usb_dev.h" +extern "C" int usb_mtp_sendEvent(const void *buffer, uint32_t len, + uint32_t timeout); +extern "C" int usb_init_events(void); + +#include "MTP_Storage.h" +// modify strings if needed (see MTP.cpp how they are used) +#define MTP_MANUF "PJRC" +#define MTP_MODEL "Teensy" +#define MTP_VERS "1.0" +#define MTP_SERNR "1234" +#define MTP_NAME "Teensy" + +#define USE_EVENTS 1 + +// probably ok to default larger verbose output on these for now +#if defined(__IMXRT1062__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) +#define MTP_VERBOSE_PRINT_CONTAINER 1 +#endif + +extern "C" { +extern volatile uint8_t usb_configuration; +} + + +// MTP Responder. +class MTP_class { +public: + explicit constexpr MTP_class() {} + int begin(); + void loop(void); + + void reset() { + // TODO check if session open + // TODO clear storage info + // TODO other state to zero or initialize? + send_DeviceResetEvent(); + } + + // Add a file system to the list of storages that will be seen by + // the host computer. Returns the index of the item within the list + bool addFilesystem(FS &disk, const char *diskname) { + return storage_.addFilesystem(disk, diskname); + } + + + operator bool() { return usb_configuration && (sessionID_ != 0); } + +#if 1 + // returns the count of file systems that have been added to the storage list + //inline uint32_t getFilesystemCount(void) { return storage_.get_FSCount(); } + + FS* getFilesystemByIndex(uint32_t store) { + if (store >= storage_.get_FSCount()) return nullptr; + return storage_.getStoreFS(store); + } + + // Return the storage name that with the given store index + const char *getNameByIndex(uint32_t store) { + if (store >= storage_.get_FSCount()) return nullptr; + return storage_.getStoreName(store); + } +#endif + + // Set which of the file systems should be used to store our storage index. This index is used + // to remember the mappings of object IDs to underlying file system object. By default the system + // uses the first storage tht was added. + bool useFilesystemForIndexList(FS &disk) { + unsigned int count = storage_.get_FSCount(); + for (unsigned int store=0; store < count; store++) { + if (storage_.getStoreFS(store) == &disk) { + return storage_.setIndexStore(store); + } + } + return false; + } + + //inline bool useFileSystemIndexFileStore(uint32_t store = 0) { return storage_.setIndexStore(store); } + + // maps a file system name (The diskname parameter in addFilesystem) + // and returns the file system index. + //inline uint32_t getFilesystemIndexFromName(const char *fsname) { return storage_.getStoreID(fsname); } + + // Reurns a pointer to stream object that is being used within MTP_Teensy + // code to output debug and informational messages. By default it + // is a pointer to the Serial object. + static inline Stream *PrintStream(void) { return printStream_; } + + // Set what stream object should be used to output debug and information + // messages. By default the system uses the Serial object. + static void PrintStream(Stream *stream) { printStream_ = stream; } + + // Print info about internal data + void printFilesystemsInfo(Stream &stream = Serial); + void printIndexList(Stream &stream = Serial) { storage_.dumpIndexList(stream); } + + // Returns a pointer to the underlying MTPStorage object. Most sketches + // do not need this, but it does allow access to things such as + // debug functions. + //MTPStorage *storage() {return &storage_ ;} + + // Test to set file name to 232 as overhead of 24 in storage... + enum {MAX_FILENAME_LEN=232, MAX_PATH_LEN=256}; + +private: + friend class MTPStorage; + static Stream *printStream_; + + struct MTPHeader { + uint32_t len; // 0 + uint16_t type; // 4 + uint16_t op; // 6 + uint32_t transaction_id; // 8 + }; + + struct MTPContainer { + uint32_t len; // 0 + uint16_t type; // 4 + uint16_t op; // 6 + uint32_t transaction_id; // 8 + uint32_t params[5]; // 12 + }; + + typedef struct { + uint16_t len; // number of data bytes + uint16_t index; // position in processing data + uint16_t size; // total size of buffer + uint8_t *data; // pointer to the data + void *usb; // packet info (needed on Teensy 3) + } packet_buffer_t; + + packet_buffer_t receive_buffer = {0, 0, 0, NULL, NULL}; + packet_buffer_t transmit_buffer = {0, 0, 0, NULL, NULL}; + packet_buffer_t event_buffer = {0, 0, 0, NULL, NULL}; + bool receive_bulk(uint32_t timeout); + void free_received_bulk(); + void allocate_transmit_bulk(); + int transmit_bulk(); + void allocate_transmit_event(); + int transmit_event(); + +#if defined(__MK20DX128__) || defined(__MK20DX256__) || \ + defined(__MK64FX512__) || defined(__MK66FX1M0__) + + static uint8_t usb_mtp_status; + +#elif defined(__IMXRT1062__) +#define MTP_RX_SIZE MTP_RX_SIZE_480 +#define MTP_TX_SIZE MTP_TX_SIZE_480 + + uint8_t tx_data_buffer[MTP_TX_SIZE] __attribute__((aligned(32))) = {0}; + static const uint32_t DISK_BUFFER_SIZE = 4 * 1024; // used by MTP_Storage + uint8_t rx_data_buffer[MTP_RX_SIZE] __attribute__((aligned(32))) = {0}; + static uint8_t disk_buffer_[DISK_BUFFER_SIZE] __attribute__((aligned(32))); + uint16_t transmit_packet_size_mask = 0x01FF; + +#endif + + static uint32_t sessionID_; + + bool write_transfer_open = false; + size_t write(const void *ptr, size_t len); + void write_finish(); + + void write8(uint8_t x) { write(&x, sizeof(x)); } + void write16(uint16_t x) { write(&x, sizeof(x)); } + void write32(uint32_t x) { write(&x, sizeof(x)); } + void write64(uint64_t x) { write(&x, sizeof(x)); } + + void writestring(const char *str); + uint32_t writestringlen(const char *str); + void writeDataPhaseHeader(struct MTPContainer &container, uint32_t data_size); + + uint32_t GetDeviceInfo(struct MTPContainer &cmd); + void WriteDescriptor(); + uint32_t GetStorageIDs(struct MTPContainer &cmd); + uint32_t GetStorageInfo(struct MTPContainer &cmd, bool mediaAccessAllowed=true); + + uint32_t GetNumObjects(struct MTPContainer &cmd); + + uint32_t GetObjectHandles(struct MTPContainer &cmd); + void GetObjectHandles(uint32_t storage, uint32_t parent); + + uint32_t GetObjectInfo(struct MTPContainer &cmd); + uint32_t GetObject(struct MTPContainer &cmd); + uint32_t GetPartialObject(struct MTPContainer &cmd); + + bool read(void *ptr, uint32_t size); + bool read8(uint8_t *n) { return read(n, 1); } + bool read16(uint16_t *n) { return read(n, 2); } + bool read32(uint32_t *n) { return read(n, 4); } + bool readDataPhaseHeader(struct MTPHeader *header=nullptr); + bool readstring(char *buffer, uint32_t buffer_size); + bool readDateTimeString(uint32_t *pdt); + + uint32_t SendObjectInfo(struct MTPContainer &cmd); + uint32_t SendObject(struct MTPContainer &cmd); + + uint32_t GetDevicePropValue(struct MTPContainer &cmd); + uint32_t GetDevicePropDesc(struct MTPContainer &cmd); + uint32_t GetObjectPropsSupported(struct MTPContainer &cmd); + + uint32_t GetObjectPropDesc(struct MTPContainer &cmd); + uint32_t GetObjectPropValue(struct MTPContainer &cmd); + + uint32_t setObjectPropValue(struct MTPContainer &cmd); + uint32_t formatStore(struct MTPContainer &cmd); + + static MTP_class *g_pmtpd_interval; + static void _interval_timer_handler(); + static IntervalTimer g_intervaltimer; + void processIntervalTimer(); + + uint32_t deleteObject(uint32_t p1); + uint32_t copyObject(uint32_t p1, uint32_t p2, uint32_t p3/*, int &object_id*/); + uint32_t moveObject(uint32_t p1, uint32_t p2, uint32_t p3); + uint32_t OpenSession(struct MTPContainer &cmd); + + uint32_t TID = 0; +#if USE_EVENTS == 1 + int send_Event(uint16_t eventCode, uint32_t p1); + int send_Event(uint16_t eventCode, uint32_t p1, uint32_t p2); + int send_Event(uint16_t eventCode, uint32_t p1, uint32_t p2, uint32_t p3); +#endif + +private: + void addSendObjectBuffer( + char *pb, + uint32_t cb); // you can extend the send object buffer by this buffer + + // page 45: "StorageID ... most-significant 16 bits identify a physical storage + // location, such as a removable memory card or an internal memory bank. The + // least-significant 16 bits identify a logical partition of that physical + // storage." + inline uint32_t Store2Storage(uint32_t store) { + return ((store + 1) << 16) | 0x0001; + } + static inline uint32_t Storage2Store(uint32_t storage) { + return (storage >> 16) - 1; + } + +#if USE_EVENTS == 1 + int send_Event(uint16_t eventCode); + int send_addObjectEvent(uint32_t p1); + int send_removeObjectEvent(uint32_t p1); + int send_StorageInfoChangedEvent(uint32_t p1); + + // Send a device reset event, when processed by the host + // they will start a new session, at which point we will + // clear our file system store file as the object ids are + // only valid during a sesion. + + int send_DeviceResetEvent(void); + + // Send an event telling the host, that we added another storeage + // to our list. Example: USBHost detects a new USB device has + // been inserted, and we wish to show the new filesystem(s) + int send_StoreAddedEvent(uint32_t store); + + + // Send an event telling the host, that a file system is no longer + // available and for the host to remove it from their list. + int send_StoreRemovedEvent(uint32_t store); + + // higer level version of sending events + // unclear if should pass in pfs or store? + bool send_addObjectEvent(uint32_t store, const char *pathname); + bool send_removeObjectEvent(uint32_t store, const char *pathname); + void printContainer(const void *container, const char *msg = nullptr); +#endif + // Support for SendObject, holding parameters from SendObjectInfo. + uint32_t object_id_ = 0; + uint32_t dtCreated_ = 0; + uint32_t dtModified_ = 0; + uint32_t dtFormatStart_ = 0; + static const uint32_t MAX_FORMAT_TIME_ = 2750; // give a little time. + bool storage_ids_sent_ = false; + MTPStorage storage_; + +}; + +extern MTP_class MTP; + +#endif // USB_MTPDISK or USB_MTPDISK_SERIAL diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/WProgram.h b/hardware/firmware/audio_board/vendor/cores/teensy4/WProgram.h index cd2ca98b..259b7b2f 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/WProgram.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/WProgram.h @@ -58,7 +58,7 @@ #include "usb_midi.h" #include "usb_rawhid.h" #include "usb_flightsim.h" -//#include "usb_mtp.h" +#include "MTP_Teensy.h" #include "usb_audio.h" #include "usb_touch.h" //#include "usb_undef.h" // do not allow usb_desc.h stuff to leak to user programs diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/analog.c b/hardware/firmware/audio_board/vendor/cores/teensy4/analog.c index f9110b59..01c64b99 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/analog.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/analog.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "imxrt.h" #include "core_pins.h" #include "debug/printf.h" diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/bootdata.c b/hardware/firmware/audio_board/vendor/cores/teensy4/bootdata.c index 9e63341f..b9c7ca7e 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/bootdata.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/bootdata.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + //#include "imxrt.h" #include diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/clockspeed.c b/hardware/firmware/audio_board/vendor/cores/teensy4/clockspeed.c index 64bc873f..6d1c0734 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/clockspeed.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/clockspeed.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include #include "imxrt.h" #include "wiring.h" diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/debugprintf.c b/hardware/firmware/audio_board/vendor/cores/teensy4/debugprintf.c index 84f79204..c6b57f31 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/debugprintf.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/debugprintf.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "debug/printf.h" #ifdef PRINT_DEBUG_STUFF diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/delay.c b/hardware/firmware/audio_board/vendor/cores/teensy4/delay.c index 277deae6..ee871ed1 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/delay.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/delay.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "core_pins.h" #include "arm_math.h" // micros() synchronization diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/digital.c b/hardware/firmware/audio_board/vendor/cores/teensy4/digital.c index 587effd4..7b4c3b30 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/digital.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/digital.c @@ -1,3 +1,32 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #include "core_pins.h" diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/extmem.c b/hardware/firmware/audio_board/vendor/cores/teensy4/extmem.c index 27db8423..7fc87951 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/extmem.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/extmem.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + // External memory allocation functions. Attempt to use external memory, // but automatically fall back to internal RAM if external RAM can't be used. diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/fuse.c b/hardware/firmware/audio_board/vendor/cores/teensy4/fuse.c index d580de05..2e427e55 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/fuse.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/fuse.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "core_pins.h" #include "imxrt.h" diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/imxrt.h b/hardware/firmware/audio_board/vendor/cores/teensy4/imxrt.h index 83f2a127..9c000c76 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/imxrt.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/imxrt.h @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include @@ -2139,6 +2169,9 @@ typedef struct { #define DMA_CINT_CINT(n) ((uint8_t)((n) & 0x1F)) // Clear Interrupt Request #define DMA_CINT_CAIR ((uint8_t)1<<6) // Clear All Interrupt Requests #define DMA_CINT_NOP ((uint8_t)1<<7) // NOP +#define DMA_DCHPRI_ECP ((uint8_t)1<<7) // Enable Preemption +#define DMA_DCHPRI_DPA ((uint8_t)1<<6) // Disable Preempt Ability +#define DMA_DCHPRI_CHPRI(n) ((uint8_t)((n) & 0x0F)) // Normally these Transfer Control Descriptor (TCD) registers are accessed through // DMAChannel instances. See DMAChannel.h for details. Or refer to libraries which diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/imxrt1062_t41.ld b/hardware/firmware/audio_board/vendor/cores/teensy4/imxrt1062_t41.ld index 6220e8cc..5335e4c3 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/imxrt1062_t41.ld +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/imxrt1062_t41.ld @@ -4,7 +4,7 @@ MEMORY DTCM (rwx): ORIGIN = 0x20000000, LENGTH = 512K RAM (rwx): ORIGIN = 0x20200000, LENGTH = 512K FLASH (rwx): ORIGIN = 0x60000000, LENGTH = 7936K - ERAM (rwx): ORIGIN = 0x70000000, LENGTH = 16384K + ERAM (rwx): ORIGIN = 0x70000000, LENGTH = 32768K } ENTRY(ImageVectorTable) diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/interrupt.c b/hardware/firmware/audio_board/vendor/cores/teensy4/interrupt.c index 851f199e..430e6816 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/interrupt.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/interrupt.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "Arduino.h" #include "pins_arduino.h" #include "debug/printf.h" diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/keylayouts.h b/hardware/firmware/audio_board/vendor/cores/teensy4/keylayouts.h index 0f006977..7ed3b04f 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/keylayouts.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/keylayouts.h @@ -102,6 +102,148 @@ extern "C"{ #define KEY_MEDIA_VOLUME_INC ( 0xE9 | 0xE400 ) #define KEY_MEDIA_VOLUME_DEC ( 0xEA | 0xE400 ) +// Software support for these GUI application control keys is spotty. +// For example, GIMP 3.0.4 running on Fedora Linux 41 does respond +// to KEY_GUI_ZOOM_IN & KEY_GUI_ZOOM_OUT, but Final Cut Pro 11.1.1 +// running on MacOS 14.7.4 Sonoma ignores them. +#define KEY_GUI_NEW ( 0x201 | 0xE400 ) +#define KEY_GUI_OPEN ( 0x202 | 0xE400 ) +#define KEY_GUI_CLOSE ( 0x203 | 0xE400 ) +#define KEY_GUI_EXIT ( 0x204 | 0xE400 ) +#define KEY_GUI_MAXIMIZE ( 0x205 | 0xE400 ) +#define KEY_GUI_MINIMIZE ( 0x206 | 0xE400 ) +#define KEY_GUI_SAVE ( 0x207 | 0xE400 ) +#define KEY_GUI_PRINT ( 0x208 | 0xE400 ) +#define KEY_GUI_PROPERTIES ( 0x209 | 0xE400 ) +#define KEY_GUI_UNDO ( 0x21A | 0xE400 ) +#define KEY_GUI_COPY ( 0x21B | 0xE400 ) +#define KEY_GUI_CUT ( 0x21C | 0xE400 ) +#define KEY_GUI_PASTE ( 0x21D | 0xE400 ) +#define KEY_GUI_SELECT_ALL ( 0x21E | 0xE400 ) +#define KEY_GUI_FIND ( 0x21F | 0xE400 ) +#define KEY_GUI_FIND_REPLACE ( 0x220 | 0xE400 ) +#define KEY_GUI_SEARCH ( 0x221 | 0xE400 ) +#define KEY_GUI_GO_TO ( 0x222 | 0xE400 ) +#define KEY_GUI_HOME ( 0x223 | 0xE400 ) +#define KEY_GUI_BACK ( 0x224 | 0xE400 ) +#define KEY_GUI_FORWARD ( 0x225 | 0xE400 ) +#define KEY_GUI_STOP ( 0x226 | 0xE400 ) +#define KEY_GUI_REFRESH ( 0x227 | 0xE400 ) +#define KEY_GUI_PREVIOUS_LINK ( 0x228 | 0xE400 ) +#define KEY_GUI_NEXT_LINK ( 0x209 | 0xE400 ) +#define KEY_GUI_BOOKMARKS ( 0x22A | 0xE400 ) +#define KEY_GUI_HISTORY ( 0x22B | 0xE400 ) +#define KEY_GUI_SUBSCRIPTIONS ( 0x22C | 0xE400 ) +#define KEY_GUI_ZOOM_IN ( 0x22D | 0xE400 ) +#define KEY_GUI_ZOOM_OUT ( 0x22E | 0xE400 ) +#define KEY_GUI_FULL_SCREEN_VIEW ( 0x230 | 0xE400 ) +#define KEY_GUI_NORMAL_VIEW ( 0x231 | 0xE400 ) +#define KEY_GUI_VIEW_TOGGLE ( 0x232 | 0xE400 ) +#define KEY_GUI_SCROLL_UP ( 0x233 | 0xE400 ) +#define KEY_GUI_SCROLL_DOWN ( 0x234 | 0xE400 ) +#define KEY_GUI_PAN_LEFT ( 0x236 | 0xE400 ) +#define KEY_GUI_PAN_RIGHT ( 0x237 | 0xE400 ) +#define KEY_GUI_NEW_WINDOW ( 0x239 | 0xE400 ) +#define KEY_GUI_TILE_HORIZONTALLY ( 0x23A | 0xE400 ) +#define KEY_GUI_TILE_VERTICALLY ( 0x23B | 0xE400 ) +#define KEY_GUI_FORMAT ( 0x23C | 0xE400 ) +#define KEY_GUI_EDIT ( 0x23D | 0xE400 ) +#define KEY_GUI_BOLD ( 0x23E | 0xE400 ) +#define KEY_GUI_ITALICS ( 0x23F | 0xE400 ) +#define KEY_GUI_UNDERLINE ( 0x240 | 0xE400 ) +#define KEY_GUI_STRIKETHROUGH ( 0x241 | 0xE400 ) +#define KEY_GUI_SUBSCRIPT ( 0x242 | 0xE400 ) +#define KEY_GUI_SUPERSCRIPT ( 0x243 | 0xE400 ) +#define KEY_GUI_ALL_CAPS ( 0x244 | 0xE400 ) +#define KEY_GUI_ROTATE ( 0x245 | 0xE400 ) +#define KEY_GUI_RESIZE ( 0x246 | 0xE400 ) +#define KEY_GUI_FLIP_HORIZONTAL ( 0x247 | 0xE400 ) +#define KEY_GUI_FLIP_VERTICAL ( 0x248 | 0xE400 ) +#define KEY_GUI_MIRROR_HORIZONTAL ( 0x249 | 0xE400 ) +#define KEY_GUI_MIRROR_VERTICAL ( 0x24A | 0xE400 ) +#define KEY_GUI_FONT_SELECT ( 0x24B | 0xE400 ) +#define KEY_GUI_FONT_COLOR ( 0x24C | 0xE400 ) +#define KEY_GUI_FONT_SIZE ( 0x24D | 0xE400 ) +#define KEY_GUI_JUSTIFY_LEFT ( 0x24E | 0xE400 ) +#define KEY_GUI_JUSTIFT_CENTER_H ( 0x24F | 0xE400 ) +#define KEY_GUI_JUSTIFY_RIGHT ( 0x250 | 0xE400 ) +#define KEY_GUI_JUSTIFY_BLOCK_H ( 0x251 | 0xE400 ) +#define KEY_GUI_JUSTIFY_TOP ( 0x252 | 0xE400 ) +#define KEY_GUI_JUSTIFY_CENTER_V ( 0x253 | 0xE400 ) +#define KEY_GUI_JUSTIFY_BOTTOM ( 0x254 | 0xE400 ) +#define KEY_GUI_JUSTIFY_BLOCK_V ( 0x255 | 0xE400 ) +#define KEY_GUI_INDENT_DECREASE ( 0x256 | 0xE400 ) +#define KEY_GUI_INDENT_INCREASE ( 0x257 | 0xE400 ) +#define KEY_GUI_NUMBERED_LIST ( 0x258 | 0xE400 ) +#define KEY_GUI_RESTART_NUMBERING ( 0x259 | 0xE400 ) +#define KEY_GUI_BULLETED_LIST ( 0x25A | 0xE400 ) +#define KEY_GUI_PROMOTE ( 0x25B | 0xE400 ) +#define KEY_GUI_DEMOTE ( 0x25C | 0xE400 ) +#define KEY_GUI_YES ( 0x25D | 0xE400 ) +#define KEY_GUI_NO ( 0x25E | 0xE400 ) +#define KEY_GUI_CANCEL ( 0x25F | 0xE400 ) +#define KEY_GUI_CATALOG ( 0x260 | 0xE400 ) +#define KEY_GUI_BUY_CHECKOUT ( 0x261 | 0xE400 ) +#define KEY_GUI_ADD_TO_CART ( 0x262 | 0xE400 ) +#define KEY_GUI_EXPAND ( 0x263 | 0xE400 ) +#define KEY_GUI_EXPAND_ALL ( 0x264 | 0xE400 ) +#define KEY_GUI_COLLAPSE ( 0x265 | 0xE400 ) +#define KEY_GUI_COLLAPSE_ALL ( 0x266 | 0xE400 ) +#define KEY_GUI_PRINT_PREVIEW ( 0x267 | 0xE400 ) +#define KEY_GUI_PASTE_SPECIAL ( 0x268 | 0xE400 ) +#define KEY_GUI_INSERT_MODE ( 0x269 | 0xE400 ) +#define KEY_GUI_DELETE ( 0x26A | 0xE400 ) +#define KEY_GUI_LOCK ( 0x26B | 0xE400 ) +#define KEY_GUI_UNLOCK ( 0x26C | 0xE400 ) +#define KEY_GUI_PROTECT ( 0x26D | 0xE400 ) +#define KEY_GUI_UNPROTECT ( 0x26E | 0xE400 ) +#define KEY_GUI_ATTACH_COMMENT ( 0x26F | 0xE400 ) +#define KEY_GUI_DELETE_COMMENT ( 0x270 | 0xE400 ) +#define KEY_GUI_VIEW_COMMENT ( 0x271 | 0xE400 ) +#define KEY_GUI_SELECT_WORD ( 0x272 | 0xE400 ) +#define KEY_GUI_SELECT_SENTENCE ( 0x273 | 0xE400 ) +#define KEY_GUI_SELECT_PARAGRAPH ( 0x274 | 0xE400 ) +#define KEY_GUI_SELECT_COLUMN ( 0x275 | 0xE400 ) +#define KEY_GUI_SELECT_ROW ( 0x276 | 0xE400 ) +#define KEY_GUI_SELECT_TABLE ( 0x277 | 0xE400 ) +#define KEY_GUI_SELECT_OBJECT ( 0x278 | 0xE400 ) +#define KEY_GUI_REDO ( 0x279 | 0xE400 ) +#define KEY_GUI_SORT ( 0x27A | 0xE400 ) +#define KEY_GUI_SORT_ASCENDING ( 0x27B | 0xE400 ) +#define KEY_GUI_SORT_DESCENDING ( 0x27C | 0xE400 ) +#define KEY_GUI_FILTER ( 0x27D | 0xE400 ) +#define KEY_GUI_SET_CLOCK ( 0x27E | 0xE400 ) +#define KEY_GUI_VIEW_CLOCK ( 0x27F | 0xE400 ) +#define KEY_GUI_SELECT_TIME_ZONE ( 0x280 | 0xE400 ) +#define KEY_GUI_EDIT_TIME_ZONES ( 0x281 | 0xE400 ) +#define KEY_GUI_SET_ALARM ( 0x282 | 0xE400 ) +#define KEY_GUI_CLEAR_ALARM ( 0x283 | 0xE400 ) +#define KEY_GUI_SNOOZE_ALARM ( 0x284 | 0xE400 ) +#define KEY_GUI_RESET_ALARM ( 0x285 | 0xE400 ) +#define KEY_GUI_SYNCHRONIZE ( 0x286 | 0xE400 ) +#define KEY_GUI_SEND_RECEIVE ( 0x287 | 0xE400 ) +#define KEY_GUI_SEND_TO ( 0x288 | 0xE400 ) +#define KEY_GUI_REPLY ( 0x289 | 0xE400 ) +#define KEY_GUI_REPLY_ALL ( 0x28A | 0xE400 ) +#define KEY_GUI_FORWARD_MSG ( 0x28B | 0xE400 ) +#define KEY_GUI_SEND ( 0x28C | 0xE400 ) +#define KEY_GUI_ATTACH_FILE ( 0x28D | 0xE400 ) +#define KEY_GUI_UPLOAD ( 0x28E | 0xE400 ) +#define KEY_GUI_DOWNLOAD ( 0x28F | 0xE400 ) +#define KEY_GUI_SET_BORDERS ( 0x290 | 0xE400 ) +#define KEY_GUI_INSERT_ROW ( 0x291 | 0xE400 ) +#define KEY_GUI_INSERT_COLUMN ( 0x292 | 0xE400 ) +#define KEY_GUI_INSERT_FILE ( 0x293 | 0xE400 ) +#define KEY_GUI_INSERT_PICTURE ( 0x294 | 0xE400 ) +#define KEY_GUI_INSERT_OBJECT ( 0x295 | 0xE400 ) +#define KEY_GUI_INSERT_SYMBOL ( 0x296 | 0xE400 ) +#define KEY_GUI_SAVE_AND_CLOSE ( 0x297 | 0xE400 ) +#define KEY_GUI_RENAME ( 0x298 | 0xE400 ) +#define KEY_GUI_MERGE ( 0x299 | 0xE400 ) +#define KEY_GUI_SPLIT ( 0x27A | 0xE400 ) +#define KEY_GUI_DISTRIBUTE_HORIZONTALLY ( 0x29B | 0xE400 ) +#define KEY_GUI_DISTRIBUTE_VERTICALLY ( 0x29C | 0xE400 ) + #define KEY_A ( 4 | 0xF000 ) #define KEY_B ( 5 | 0xF000 ) #define KEY_C ( 6 | 0xF000 ) diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/pgmspace.h b/hardware/firmware/audio_board/vendor/cores/teensy4/pgmspace.h index 36c553cd..857f7b9f 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/pgmspace.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/pgmspace.h @@ -1,2 +1,4 @@ // For compatibility with some ESP8266 programs #include + +// This header file is in the public domain. diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/pwm.c b/hardware/firmware/audio_board/vendor/cores/teensy4/pwm.c index 9800f4d7..377b2670 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/pwm.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/pwm.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "imxrt.h" #include "core_pins.h" #include "debug/printf.h" diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/startup.c b/hardware/firmware/audio_board/vendor/cores/teensy4/startup.c index 4bb3bd40..319b7705 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/startup.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/startup.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "imxrt.h" #include "wiring.h" #include "usb_dev.h" @@ -27,7 +57,6 @@ void (* volatile _VectorsRam[NVIC_NUM_INTERRUPTS+16])(void); static void memory_copy(uint32_t *dest, const uint32_t *src, uint32_t *dest_end); static void memory_clear(uint32_t *dest, uint32_t *dest_end); static void configure_systick(void); -static void reset_PFD(); extern void systick_isr(void); extern void pendablesrvreq_isr(void); void configure_cache(void); @@ -59,6 +88,12 @@ FLASHMEM void startup_debug_reset(void) { __asm__ volatile("nop"); } static void ResetHandler2(void); +// In theory we're supposed to gate off the PFD outputs, but +// in practice it causes strange crashing, especially with LTO. +// https://www.nxp.com/docs/en/engineering-bulletin/EB790.pdf +// Uncomment this if you want to try the "correct" way. +//#define GATE_PFD_WHILE_CHANGE + __attribute__((section(".startup"), naked)) void ResetHandler(void) { @@ -79,7 +114,8 @@ static void ResetHandler2(void) { unsigned int i; __asm__ volatile("dsb":::"memory"); -#if 1 +#if 0 + // TODO: can we safely delete this delay? // Some optimization with LTO won't start without this delay, but why? asm volatile("nop"); asm volatile("nop"); @@ -88,7 +124,30 @@ static void ResetHandler2(void) #endif startup_early_hook(); // must be in FLASHMEM, as ITCM is not yet initialized! PMU_MISC0_SET = 1<<3; //Use bandgap-based bias currents for best performance (Page 1175) -#if 1 + + // Configure PLL PFD outputs + // Sys PFD Frequency = 528 MHz * 18 / frac8 (where frac8 range is 12 to 35) + const uint32_t sys_pfd = 0x2018101B; // PFD3:297,PFD2:396,PFD1:594,PFD0:352 MHz + // USB PFD Freq uency= 480 MHz * 18 / frac8 (where frac8 range is 12 to 35) + const uint32_t usb_pfd = 0x13110D0C; // PFD3:454,PFD2:508,PFD1:664,PFD0:720 MHz +#ifdef GATE_PFD_WHILE_CHANGE + CCM_ANALOG_PFD_528_SET = 0x80808080; + CCM_ANALOG_PFD_528 = sys_pfd | 0x80808080; + CCM_ANALOG_PFD_528; + //while ((CCM_ANALOG_PFD_528 & 0x40404040) != 0x40404040) ; // wait for stable + CCM_ANALOG_PFD_528_CLR = 0x80808080; + CCM_ANALOG_PFD_480_SET = 0x80808080; + CCM_ANALOG_PFD_480 = usb_pfd | 0x80808080; + CCM_ANALOG_PFD_480; + //while ((CCM_ANALOG_PFD_480 & 0x40404040) != 0x40404040) ; // wait for stable + CCM_ANALOG_PFD_480_CLR = 0x80808080; +#else + CCM_ANALOG_PFD_528 = sys_pfd; + CCM_ANALOG_PFD_480 = usb_pfd; +#endif + +#if 0 + // TODO: can we safely delete this delay? // Some optimization with LTO won't start without this delay, but why? asm volatile("nop"); asm volatile("nop"); @@ -106,6 +165,12 @@ static void ResetHandler2(void) asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); + asm volatile("nop"); + asm volatile("nop"); + asm volatile("nop"); + asm volatile("nop"); + asm volatile("nop"); + asm volatile("nop"); #endif // pin 13 - if startup crashes, use this to turn on the LED early for troubleshooting //IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03 = 5; @@ -127,8 +192,6 @@ static void ResetHandler2(void) for (i=0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_PRIORITY(i, 128); SCB_VTOR = (uint32_t)_VectorsRam; - reset_PFD(); - // enable exception handling SCB_SHCSR |= SCB_SHCSR_MEMFAULTENA | SCB_SHCSR_BUSFAULTENA | SCB_SHCSR_USGFAULTENA; @@ -155,7 +218,6 @@ static void ResetHandler2(void) configure_cache(); configure_systick(); usb_pll_start(); - reset_PFD(); //TODO: is this really needed? #ifdef F_CPU set_arm_clock(F_CPU); #endif @@ -317,7 +379,7 @@ FLASHMEM void configure_cache(void) SCB_MPU_RASR = MEM_CACHE_WBWA | READONLY | SIZE_16M; SCB_MPU_RBAR = 0x70000000 | REGION(i++); // FlexSPI2 - SCB_MPU_RASR = MEM_CACHE_WBWA | READWRITE | NOEXEC | SIZE_16M; + SCB_MPU_RASR = MEM_CACHE_WBWA | READWRITE | NOEXEC | SIZE_32M; // We default RAM meant for data to NOEXEC as a proactive security measure. // If you wish to dynamically load code into RAM and execute it, start here: // https://forum.pjrc.com/index.php?threads/75610/#post-347791 @@ -373,9 +435,47 @@ FLASHMEM static uint32_t flexspi2_psram_id(uint32_t addr) while (!(FLEXSPI2_INTR & FLEXSPI_INTR_IPCMDDONE)); // wait uint32_t id = FLEXSPI2_RFDR0; FLEXSPI2_INTR = FLEXSPI_INTR_IPCMDDONE | FLEXSPI_INTR_IPRXWA; - return id & 0xFFFF; + return id; +} + + +/** + * \return size of PSRAM in MBytes, or 0 if not present + */ +FLASHMEM static uint8_t flexspi2_psram_size(uint32_t addr) +{ + uint8_t result = 0; // assume we don't have PSRAM at this address + flexspi2_command(0, addr); // exit quad mode + flexspi2_command(1, addr); // reset enable + flexspi2_command(2, addr); // reset (is this really necessary?) + uint32_t id = flexspi2_psram_id(addr); + + switch (id & 0xFFFF) + { + default: + break; + + case 0x5D0D: // AP / Ipus / ESP / Lyontek + result = 8; + break; + + case 0x5D9D: // ISSI + switch ((id >> 21) & 0x7) // get size (Datasheet Table 6.2) + { + case 0b011: + result = 8; + break; + case 0b100: + result = 16; + break; + } + break; + } + + return result; } + FLASHMEM void configure_external_ram() { // initialize pins @@ -404,9 +504,18 @@ FLASHMEM void configure_external_ram() IOMUXC_FLEXSPI2_IPP_IND_IO_FA_BIT3_SELECT_INPUT = 1; // GPIO_EMC_29 for Mode: ALT8 IOMUXC_FLEXSPI2_IPP_IND_SCK_FA_SELECT_INPUT = 1; // GPIO_EMC_25 for Mode: ALT8 - // turn on clock (TODO: increase clock speed later, slow & cautious for first release) + // turn on clock (QSPI flash & PSRAM chips usually spec max clock 100 to 133 MHz) CCM_CBCMR = (CCM_CBCMR & ~(CCM_CBCMR_FLEXSPI2_PODF_MASK | CCM_CBCMR_FLEXSPI2_CLK_SEL_MASK)) - | CCM_CBCMR_FLEXSPI2_PODF(5) | CCM_CBCMR_FLEXSPI2_CLK_SEL(3); // 88 MHz + //| CCM_CBCMR_FLEXSPI2_PODF(5) | CCM_CBCMR_FLEXSPI2_CLK_SEL(3); // 88.0 MHz + //| CCM_CBCMR_FLEXSPI2_PODF(3) | CCM_CBCMR_FLEXSPI2_CLK_SEL(0); // 99.0 MHz + //| CCM_CBCMR_FLEXSPI2_PODF(6) | CCM_CBCMR_FLEXSPI2_CLK_SEL(1); // 102.9 MHz + | CCM_CBCMR_FLEXSPI2_PODF(4) | CCM_CBCMR_FLEXSPI2_CLK_SEL(3); // 105.6 MHz + //| CCM_CBCMR_FLEXSPI2_PODF(5) | CCM_CBCMR_FLEXSPI2_CLK_SEL(2); // 110.8 MHz + //| CCM_CBCMR_FLEXSPI2_PODF(5) | CCM_CBCMR_FLEXSPI2_CLK_SEL(1); // 120.0 MHz + //| CCM_CBCMR_FLEXSPI2_PODF(3) | CCM_CBCMR_FLEXSPI2_CLK_SEL(3); // 132.0 MHz + //| CCM_CBCMR_FLEXSPI2_PODF(4) | CCM_CBCMR_FLEXSPI2_CLK_SEL(1); // 144.0 MHz + //| CCM_CBCMR_FLEXSPI2_PODF(3) | CCM_CBCMR_FLEXSPI2_CLK_SEL(2); // 166.2 MHz + //| CCM_CBCMR_FLEXSPI2_PODF(2) | CCM_CBCMR_FLEXSPI2_CLK_SEL(3); // 176.0 MHz CCM_CCGR7 |= CCM_CCGR7_FLEXSPI2(CCM_CCGR_ON); FLEXSPI2_MCR0 |= FLEXSPI_MCR0_MDIS; @@ -440,15 +549,13 @@ FLASHMEM void configure_external_ram() FLEXSPI2_IPTXFCR = (FLEXSPI_IPTXFCR & 0xFFFFFFC0) | FLEXSPI_IPTXFCR_CLRIPTXF; FLEXSPI2_INTEN = 0; - FLEXSPI2_FLSHA1CR0 = 0x2000; // 8 MByte - FLEXSPI2_FLSHA1CR1 = FLEXSPI_FLSHCR1_CSINTERVAL(2) - | FLEXSPI_FLSHCR1_TCSH(3) | FLEXSPI_FLSHCR1_TCSS(3); + FLEXSPI2_FLSHA1CR1 = FLEXSPI_FLSHCR1_CSINTERVAL(0) + | FLEXSPI_FLSHCR1_TCSH(1) | FLEXSPI_FLSHCR1_TCSS(1); FLEXSPI2_FLSHA1CR2 = FLEXSPI_FLSHCR2_AWRSEQID(6) | FLEXSPI_FLSHCR2_AWRSEQNUM(0) | FLEXSPI_FLSHCR2_ARDSEQID(5) | FLEXSPI_FLSHCR2_ARDSEQNUM(0); - FLEXSPI2_FLSHA2CR0 = 0x2000; // 8 MByte - FLEXSPI2_FLSHA2CR1 = FLEXSPI_FLSHCR1_CSINTERVAL(2) - | FLEXSPI_FLSHCR1_TCSH(3) | FLEXSPI_FLSHCR1_TCSS(3); + FLEXSPI2_FLSHA2CR1 = FLEXSPI_FLSHCR1_CSINTERVAL(0) + | FLEXSPI_FLSHCR1_TCSH(1) | FLEXSPI_FLSHCR1_TCSS(1); FLEXSPI2_FLSHA2CR2 = FLEXSPI_FLSHCR2_AWRSEQID(6) | FLEXSPI_FLSHCR2_AWRSEQNUM(0) | FLEXSPI_FLSHCR2_ARDSEQID(5) | FLEXSPI_FLSHCR2_ARDSEQNUM(0); @@ -483,22 +590,16 @@ FLASHMEM void configure_external_ram() FLEXSPI2_LUT25 = LUT0(WRITE_SDR, PINS4, 1); // look for the first PSRAM chip - flexspi2_command(0, 0); // exit quad mode - flexspi2_command(1, 0); // reset enable - flexspi2_command(2, 0); // reset (is this really necessary?) - if (flexspi2_psram_id(0) == 0x5D0D) { - // first PSRAM chip is present, look for a second PSRAM chip - flexspi2_command(4, 0); - flexspi2_command(0, 0x800000); // exit quad mode - flexspi2_command(1, 0x800000); // reset enable - flexspi2_command(2, 0x800000); // reset (is this really necessary?) - if (flexspi2_psram_id(0x800000) == 0x5D0D) { - flexspi2_command(4, 0x800000); - // Two PSRAM chips are present, 16 MByte - external_psram_size = 16; - } else { - // One PSRAM chip is present, 8 MByte - external_psram_size = 8; + uint8_t size1 = flexspi2_psram_size(0); + if (size1 > 0) { + FLEXSPI2_FLSHA1CR0 = size1 << 10; + flexspi2_command(4, 0); // enter QPI mode + // look for a second PSRAM chip + uint8_t size2 = flexspi2_psram_size(size1 << 20); + external_psram_size = size1 + size2; + if (size2 > 0) { + FLEXSPI2_FLSHA2CR0 = size2 << 10; + flexspi2_command(4, size1 << 20); // enter QPI mode } // TODO: zero uninitialized EXTMEM variables // TODO: copy from flash to initialize EXTMEM variables @@ -508,6 +609,7 @@ FLASHMEM void configure_external_ram() 1, NULL); } else { // No PSRAM + external_psram_size = 0; memset(&extmem_smalloc_pool, 0, sizeof(extmem_smalloc_pool)); } } @@ -517,6 +619,7 @@ FLASHMEM void configure_external_ram() FLASHMEM void usb_pll_start() { + // https://www.nxp.com/docs/en/engineering-bulletin/EB790.pdf while (1) { uint32_t n = CCM_ANALOG_PLL_USB1; // pg 759 printf("CCM_ANALOG_PLL_USB1=%08lX\n", n); @@ -532,7 +635,6 @@ FLASHMEM void usb_pll_start() } if (!(n & CCM_ANALOG_PLL_USB1_ENABLE)) { printf(" enable PLL\n"); - // TODO: should this be done so early, or later?? CCM_ANALOG_PLL_USB1_SET = CCM_ANALOG_PLL_USB1_ENABLE; continue; } @@ -559,15 +661,6 @@ FLASHMEM void usb_pll_start() } } -FLASHMEM void reset_PFD() -{ - //Reset PLL2 PFDs, set default frequencies: - CCM_ANALOG_PFD_528_SET = (1 << 31) | (1 << 23) | (1 << 15) | (1 << 7); - CCM_ANALOG_PFD_528 = 0x2018101B; // PFD0:352, PFD1:594, PFD2:396, PFD3:297 MHz - //PLL3: - CCM_ANALOG_PFD_480_SET = (1 << 31) | (1 << 23) | (1 << 15) | (1 << 7); - CCM_ANALOG_PFD_480 = 0x13110D0C; // PFD0:720, PFD1:664, PFD2:508, PFD3:454 MHz -} extern void usb_isr(void); @@ -669,13 +762,13 @@ __attribute__((section(".startup"), noinline)) static void memory_copy(uint32_t *dest, const uint32_t *src, uint32_t *dest_end) { #if 0 - if (dest == src) return; + if (dest == dest_end) return; do { *dest++ = *src++; } while (dest < dest_end); #else asm volatile( - " cmp %[src], %[dest] \n" + " cmp %[end], %[dest] \n" " beq.n 2f \n" "1: ldr.w r3, [%[src]], #4 \n" " str.w r3, [%[dest]], #4 \n" diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/tempmon.c b/hardware/firmware/audio_board/vendor/cores/teensy4/tempmon.c index 6543bd6e..b1c6a278 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/tempmon.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/tempmon.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "imxrt.h" #include "core_pins.h" #include "avr/pgmspace.h" diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/usb.c b/hardware/firmware/audio_board/vendor/cores/teensy4/usb.c index ed2d6b81..ab972c36 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/usb.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/usb.c @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #include "usb_dev.h" #define USB_DESC_LIST_DEFINE #include "usb_desc.h" diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.c b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.c index 010e3efb..653471fb 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.c @@ -182,9 +182,11 @@ static uint8_t keyboard_report_desc[] = { 0x15, 0x00, // Logical Minimum (0), 0x25, 0x01, // Logical Maximum (1), 0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier keys +#if KEYBOARD_SIZE == 8 0x95, 0x01, // Report Count (1), 0x75, 0x08, // Report Size (8), 0x81, 0x03, // Input (Constant), ;Reserved byte +#endif 0x95, 0x05, // Report Count (5), 0x75, 0x01, // Report Size (1), 0x05, 0x08, // Usage Page (LEDs), @@ -194,6 +196,7 @@ static uint8_t keyboard_report_desc[] = { 0x95, 0x01, // Report Count (1), 0x75, 0x03, // Report Size (3), 0x91, 0x03, // Output (Constant), ;LED report padding +#if KEYBOARD_SIZE == 8 0x95, 0x06, // Report Count (6), 0x75, 0x08, // Report Size (8), 0x15, 0x00, // Logical Minimum (0), @@ -202,6 +205,16 @@ static uint8_t keyboard_report_desc[] = { 0x19, 0x00, // Usage Minimum (0), 0x29, 0x7F, // Usage Maximum (104), 0x81, 0x00, // Input (Data, Array), ;Normal keys +#elif KEYBOARD_SIZE == 16 + 0x95, 0x78, // Report Count (120), + 0x75, 0x01, // Report Size (1), + 0x19, 0x00, // Usage Minimum (0), + 0x29, 0xE7, // Usage Maximum (119), + 0x05, 0x07, // Usage Page (Key Codes), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x81, 0x02, // Input (Data, Variable, Absolute), ;Normal keys NKRO +#endif 0xC0 // End Collection }; #endif diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.h b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.h index 08998552..03bd124d 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.h @@ -227,7 +227,7 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports #define SEREMU_RX_INTERVAL 2 // TODO: is this ok for 480 Mbit speed #define KEYBOARD_INTERFACE 0 // Keyboard #define KEYBOARD_ENDPOINT 3 - #define KEYBOARD_SIZE 8 + #define KEYBOARD_SIZE 8 // 8 = normal boot protocol, 16 = NKRO #define KEYBOARD_INTERVAL 1 // TODO: is this ok for 480 Mbit speed #define KEYMEDIA_INTERFACE 2 // Keyboard Media Keys #define KEYMEDIA_ENDPOINT 4 @@ -257,7 +257,7 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports #define SEREMU_RX_INTERVAL 2 #define KEYBOARD_INTERFACE 0 // Keyboard #define KEYBOARD_ENDPOINT 3 - #define KEYBOARD_SIZE 8 + #define KEYBOARD_SIZE 8 // 8 = normal boot protocol, 16 = NKRO #define KEYBOARD_INTERVAL 1 #define KEYMEDIA_INTERFACE 4 // Keyboard Media Keys #define KEYMEDIA_ENDPOINT 4 @@ -303,7 +303,7 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports #define CDC_TX_SIZE_12 64 #define KEYBOARD_INTERFACE 2 // Keyboard #define KEYBOARD_ENDPOINT 4 - #define KEYBOARD_SIZE 8 + #define KEYBOARD_SIZE 8 // 8 = normal boot protocol, 16 = NKRO #define KEYBOARD_INTERVAL 1 #define KEYMEDIA_INTERFACE 5 // Keyboard Media Keys #define KEYMEDIA_ENDPOINT 5 @@ -343,7 +343,7 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports #define SEREMU_RX_INTERVAL 2 #define KEYBOARD_INTERFACE 0 // Keyboard #define KEYBOARD_ENDPOINT 3 - #define KEYBOARD_SIZE 8 + #define KEYBOARD_SIZE 8 // 8 = normal boot protocol, 16 = NKRO #define KEYBOARD_INTERVAL 1 #define KEYMEDIA_INTERFACE 2 // Keyboard Media Keys #define KEYMEDIA_ENDPOINT 4 @@ -377,7 +377,7 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports #define SEREMU_RX_INTERVAL 2 #define KEYBOARD_INTERFACE 0 // Keyboard #define KEYBOARD_ENDPOINT 3 - #define KEYBOARD_SIZE 8 + #define KEYBOARD_SIZE 8 // 8 = normal boot protocol, 16 = NKRO #define KEYBOARD_INTERVAL 1 #define KEYMEDIA_INTERFACE 3 // Keyboard Media Keys #define KEYMEDIA_ENDPOINT 4 @@ -898,7 +898,7 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports #define MIDI_RX_SIZE 64 #define KEYBOARD_INTERFACE 3 // Keyboard #define KEYBOARD_ENDPOINT 4 - #define KEYBOARD_SIZE 8 + #define KEYBOARD_SIZE 8 // 8 = normal boot protocol, 16 = NKRO #define KEYBOARD_INTERVAL 1 #define MOUSE_INTERFACE 4 // Mouse #define MOUSE_ENDPOINT 5 diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_dev.h b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_dev.h index 3759d010..4a873742 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_dev.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_dev.h @@ -1,3 +1,33 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include "imxrt.h" diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_keyboard.c b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_keyboard.c index ac742e6a..dab9a079 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_keyboard.c +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_keyboard.c @@ -44,8 +44,12 @@ // 16=right ctrl, 32=right shift, 64=right alt, 128=right gui uint8_t keyboard_modifier_keys=0; +#if KEYBOARD_SIZE == 8 // which keys are currently pressed, up to 6 keys may be down at once uint8_t keyboard_keys[6]={0,0,0,0,0,0}; +#elif KEYBOARD_SIZE == 16 +uint8_t keyboard_bitmask[15]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#endif #ifdef KEYMEDIA_INTERFACE uint16_t keymedia_consumer_keys[4]; @@ -423,7 +427,7 @@ void usb_keyboard_release_keycode(uint16_t n) static void usb_keyboard_press_key(uint8_t key, uint8_t modifier) { - int i, send_required = 0; + int send_required = 0; if (modifier) { if ((keyboard_modifier_keys & modifier) != modifier) { @@ -431,7 +435,9 @@ static void usb_keyboard_press_key(uint8_t key, uint8_t modifier) send_required = 1; } } +#if KEYBOARD_SIZE == 8 if (key) { + int i; for (i=0; i < 6; i++) { if (keyboard_keys[i] == key) goto end; } @@ -444,13 +450,21 @@ static void usb_keyboard_press_key(uint8_t key, uint8_t modifier) } } end: +#elif KEYBOARD_SIZE == 16 + if (key > 0 && key < 120) { + if ((keyboard_bitmask[key >> 3] & (1 << (key & 7))) == 0) { + keyboard_bitmask[key >> 3] |= 1 << (key & 7); + send_required = 1; + } + } +#endif if (send_required) usb_keyboard_send(); } static void usb_keyboard_release_key(uint8_t key, uint8_t modifier) { - int i, send_required = 0; + int send_required = 0; if (modifier) { if ((keyboard_modifier_keys & modifier) != 0) { @@ -458,7 +472,9 @@ static void usb_keyboard_release_key(uint8_t key, uint8_t modifier) send_required = 1; } } +#if KEYBOARD_SIZE == 8 if (key) { + int i; for (i=0; i < 6; i++) { if (keyboard_keys[i] == key) { keyboard_keys[i] = 0; @@ -466,6 +482,14 @@ static void usb_keyboard_release_key(uint8_t key, uint8_t modifier) } } } +#elif KEYBOARD_SIZE == 16 + if (key > 0 && key < 120) { + if ((keyboard_bitmask[key >> 3] & (1 << (key & 7))) != 0) { + keyboard_bitmask[key >> 3] &= ~(1 << (key & 7)); + send_required = 1; + } + } +#endif if (send_required) usb_keyboard_send(); } @@ -475,10 +499,17 @@ void usb_keyboard_release_all(void) anybits = keyboard_modifier_keys; keyboard_modifier_keys = 0; +#if KEYBOARD_SIZE == 8 for (i=0; i < 6; i++) { anybits |= keyboard_keys[i]; keyboard_keys[i] = 0; } +#elif KEYBOARD_SIZE == 16 + for (i=0; i < 15; i++) { + anybits |= keyboard_bitmask[i]; + keyboard_bitmask[i] = 0; + } +#endif if (anybits) usb_keyboard_send(); #ifdef KEYMEDIA_INTERFACE anybits = 0; @@ -499,16 +530,25 @@ int usb_keyboard_press(uint8_t key, uint8_t modifier) { int r; keyboard_modifier_keys = modifier; +#if KEYBOARD_SIZE == 8 keyboard_keys[0] = key; keyboard_keys[1] = 0; keyboard_keys[2] = 0; keyboard_keys[3] = 0; keyboard_keys[4] = 0; keyboard_keys[5] = 0; +#elif KEYBOARD_SIZE == 16 + memset(keyboard_bitmask, 0, sizeof(keyboard_bitmask)); + if (key < 120) keyboard_bitmask[key >> 3] |= 1 << (key & 7); +#endif r = usb_keyboard_send(); if (r) return r; keyboard_modifier_keys = 0; +#if KEYBOARD_SIZE == 8 keyboard_keys[0] = 0; +#elif KEYBOARD_SIZE == 16 + if (key < 120) keyboard_bitmask[key >> 3] &= ~(1 << (key & 7)); +#endif return usb_keyboard_send(); } @@ -561,6 +601,7 @@ int usb_keyboard_send(void) { uint8_t buffer[KEYBOARD_SIZE]; buffer[0] = keyboard_modifier_keys; +#if KEYBOARD_SIZE == 8 buffer[1] = 0; buffer[2] = keyboard_keys[0]; buffer[3] = keyboard_keys[1]; @@ -568,6 +609,23 @@ int usb_keyboard_send(void) buffer[5] = keyboard_keys[3]; buffer[6] = keyboard_keys[4]; buffer[7] = keyboard_keys[5]; +#elif KEYBOARD_SIZE == 16 + buffer[1] = keyboard_bitmask[0]; + buffer[2] = keyboard_bitmask[1]; + buffer[3] = keyboard_bitmask[2]; + buffer[4] = keyboard_bitmask[3]; + buffer[5] = keyboard_bitmask[4]; + buffer[6] = keyboard_bitmask[5]; + buffer[7] = keyboard_bitmask[6]; + buffer[8] = keyboard_bitmask[7]; + buffer[9] = keyboard_bitmask[8]; + buffer[10] = keyboard_bitmask[9]; + buffer[11] = keyboard_bitmask[10]; + buffer[12] = keyboard_bitmask[11]; + buffer[13] = keyboard_bitmask[12]; + buffer[14] = keyboard_bitmask[13]; + buffer[15] = keyboard_bitmask[14]; +#endif return usb_keyboard_transmit(KEYBOARD_ENDPOINT, buffer, KEYBOARD_SIZE); } @@ -652,7 +710,7 @@ void usb_keymedia_release_all(void) if (anybits) usb_keymedia_send(); } -// send the contents of keyboard_keys and keyboard_modifier_keys +// send the contents of consumer and keymedia_system_keys static int usb_keymedia_send(void) { uint8_t buffer[8]; diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_keyboard.h b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_keyboard.h index 18932193..f3ca7d27 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_keyboard.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_keyboard.h @@ -54,7 +54,9 @@ int usb_keyboard_send(void); void usb_keymedia_release_all(void); #endif extern uint8_t keyboard_modifier_keys; +#if KEYBOARD_SIZE == 8 extern uint8_t keyboard_keys[6]; +#endif extern uint8_t keyboard_protocol; extern uint8_t keyboard_idle_config; extern uint8_t keyboard_idle_count; @@ -82,12 +84,16 @@ class usb_keyboard_class : public Print using Print::write; void write_unicode(uint16_t n) { usb_keyboard_write_unicode(n); } void set_modifier(uint16_t c) { keyboard_modifier_keys = (uint8_t)c; } +#if KEYBOARD_SIZE == 8 void set_key1(uint8_t c) { keyboard_keys[0] = c; } void set_key2(uint8_t c) { keyboard_keys[1] = c; } void set_key3(uint8_t c) { keyboard_keys[2] = c; } void set_key4(uint8_t c) { keyboard_keys[3] = c; } void set_key5(uint8_t c) { keyboard_keys[4] = c; } void set_key6(uint8_t c) { keyboard_keys[5] = c; } +#elif KEYBOARD_SIZE == 16 + // TODO, how to keep compatibility with this old 6KRO API? +#endif #ifdef KEYMEDIA_INTERFACE void set_media(uint16_t c) { if (c == 0) { diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/wiring_private.h b/hardware/firmware/audio_board/vendor/cores/teensy4/wiring_private.h index 01a6e49a..e3cc6d03 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/wiring_private.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/wiring_private.h @@ -1 +1,3 @@ // empty header file, here for libraries which try to include it (eg, Adafruit_GFX) + +// This header file is in the public domain. From fea95c8cdd79fdbf979471dee878992d7bd654e3 Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Sat, 15 Nov 2025 09:10:13 +0100 Subject: [PATCH 02/67] Update audio library --- .../vendor/Audio/control_sgtl5000.cpp | 64 ++++++------------- .../vendor/Audio/control_sgtl5000.h | 7 +- .../ADAT_DrumSamplePlayer.ino | 2 +- 3 files changed, 21 insertions(+), 52 deletions(-) diff --git a/hardware/firmware/audio_board/vendor/Audio/control_sgtl5000.cpp b/hardware/firmware/audio_board/vendor/Audio/control_sgtl5000.cpp index a63cd2b4..d9f614e0 100644 --- a/hardware/firmware/audio_board/vendor/Audio/control_sgtl5000.cpp +++ b/hardware/firmware/audio_board/vendor/Audio/control_sgtl5000.cpp @@ -26,6 +26,7 @@ #include #include "control_sgtl5000.h" +#include "Wire.h" #define CHIP_ID 0x0000 // 15:8 PARTID 0xA0 - 8 bit identifier for SGTL5000 @@ -498,18 +499,6 @@ #define SGTL5000_I2C_ADDR_CS_LOW 0x0A // CTRL_ADR0_CS pin low (normal configuration) #define SGTL5000_I2C_ADDR_CS_HIGH 0x2A // CTRL_ADR0_CS pin high -TwoWire* AudioControlSGTL5000::wires[] = {&Wire -#if defined(WIRE_IMPLEMENT_WIRE1) -,&Wire1 -#if defined(WIRE_IMPLEMENT_WIRE2) -,&Wire2 -#if defined(ARDUINO_TEENSY_MICROMOD) -,Wire3 -#endif// defined(ARDUINO_TEENSY_MICROMOD) -#endif // defined(WIRE_IMPLEMENT_WIRE2) -#endif // defined(WIRE_IMPLEMENT_WIRE1) -}; -#define MAX_WIRE (sizeof wires / sizeof wires[0] - 1) void AudioControlSGTL5000::setAddress(uint8_t level) { @@ -520,21 +509,6 @@ void AudioControlSGTL5000::setAddress(uint8_t level) } } -void AudioControlSGTL5000::setWire(uint8_t wnum, uint8_t level) -{ - setAddress(level); - if (wnum > MAX_WIRE) wnum = MAX_WIRE; - wire = wires[wnum]; -} -#undef MAX_WIRE - -void AudioControlSGTL5000::setWire(TwoWire& wref, uint8_t level) -{ - setAddress(level); - wire = &wref; -} - - bool AudioControlSGTL5000::enable(void) { #if defined(KINETISL) return enable(16000000); // SGTL as Master with 16MHz MCLK from Teensy LC @@ -543,10 +517,10 @@ bool AudioControlSGTL5000::enable(void) { #endif } - bool AudioControlSGTL5000::enable(const unsigned extMCLK, const uint32_t pllFreq) { - wire->begin(); + + Wire.begin(); delay(5); //Check if we are in Master Mode and if the Teensy had a reset: @@ -621,25 +595,25 @@ bool AudioControlSGTL5000::enable(const unsigned extMCLK, const uint32_t pllFreq unsigned int AudioControlSGTL5000::read(unsigned int reg) { unsigned int val; - wire->beginTransmission(i2c_addr); - wire->write(reg >> 8); - wire->write(reg); - if (wire->endTransmission(false) != 0) return 0; - if (wire->requestFrom((int)i2c_addr, 2) < 2) return 0; - val = wire->read() << 8; - val |= wire->read(); + Wire.beginTransmission(i2c_addr); + Wire.write(reg >> 8); + Wire.write(reg); + if (Wire.endTransmission(false) != 0) return 0; + if (Wire.requestFrom((int)i2c_addr, 2) < 2) return 0; + val = Wire.read() << 8; + val |= Wire.read(); return val; } bool AudioControlSGTL5000::write(unsigned int reg, unsigned int val) { if (reg == CHIP_ANA_CTRL) ana_ctrl = val; - wire->beginTransmission(i2c_addr); - wire->write(reg >> 8); - wire->write(reg); - wire->write(val >> 8); - wire->write(val); - if (wire->endTransmission() == 0) return true; + Wire.beginTransmission(i2c_addr); + Wire.write(reg >> 8); + Wire.write(reg); + Wire.write(val >> 8); + Wire.write(val); + if (Wire.endTransmission() == 0) return true; return false; } @@ -917,9 +891,9 @@ unsigned short AudioControlSGTL5000::autoVolumeControl(uint8_t maxGain, uint8_t if(maxGain>2) maxGain=2; lbiResponse&=3; hardLimit&=1; - uint16_t thresh=(pow(10,threshold/20)*0.636)*pow(2,15); - uint16_t att=(1-pow(10,-(attack/(20*44100))))*pow(2,19); - uint16_t dec=(1-pow(10,-(decay/(20*44100))))*pow(2,23); + uint8_t thresh=(pow(10,threshold/20)*0.636)*pow(2,15); + uint8_t att=(1-pow(10,-(attack/(20*44100))))*pow(2,19); + uint8_t dec=(1-pow(10,-(decay/(20*44100))))*pow(2,23); write(DAP_AVC_THRESHOLD,thresh); write(DAP_AVC_ATTACK,att); write(DAP_AVC_DECAY,dec); diff --git a/hardware/firmware/audio_board/vendor/Audio/control_sgtl5000.h b/hardware/firmware/audio_board/vendor/Audio/control_sgtl5000.h index 09de6031..a6a355b4 100644 --- a/hardware/firmware/audio_board/vendor/Audio/control_sgtl5000.h +++ b/hardware/firmware/audio_board/vendor/Audio/control_sgtl5000.h @@ -29,7 +29,6 @@ #include // github.com/PaulStoffregen/cores/blob/master/teensy4/AudioStream.h #include "AudioControl.h" -#include "Wire.h" // SGTL5000-specific defines for headphones #define AUDIO_HEADPHONE_DAC 0 @@ -38,10 +37,8 @@ class AudioControlSGTL5000 : public AudioControl { public: - AudioControlSGTL5000(void) : i2c_addr(0x0A), wire{wires[0]} { } + AudioControlSGTL5000(void) : i2c_addr(0x0A) { } void setAddress(uint8_t level); - void setWire(uint8_t wnum = 0, uint8_t level = LOW); - void setWire(TwoWire& wref = Wire, uint8_t level = LOW); bool enable(void);//For Teensy LC the SGTL acts as master, for all other Teensys as slave. bool enable(const unsigned extMCLK, const uint32_t pllFreq = (4096.0l * AUDIO_SAMPLE_RATE_EXACT) ); //With extMCLK > 0, the SGTL acts as Master bool disable(void) { return false; } @@ -123,8 +120,6 @@ class AudioControlSGTL5000 : public AudioControl bool semi_automated; void automate(uint8_t dap, uint8_t eq); void automate(uint8_t dap, uint8_t eq, uint8_t filterCount); - static TwoWire* wires[3]; - TwoWire* wire; }; //For Filter Type: 0 = LPF, 1 = HPF, 2 = BPF, 3 = NOTCH, 4 = PeakingEQ, 5 = LowShelf, 6 = HighShelf diff --git a/hardware/firmware/audio_board/vendor/Audio/examples/HardwareTesting/ADAT_DrumSamplePlayer/ADAT_DrumSamplePlayer.ino b/hardware/firmware/audio_board/vendor/Audio/examples/HardwareTesting/ADAT_DrumSamplePlayer/ADAT_DrumSamplePlayer.ino index aeba3317..3b777087 100644 --- a/hardware/firmware/audio_board/vendor/Audio/examples/HardwareTesting/ADAT_DrumSamplePlayer/ADAT_DrumSamplePlayer.ino +++ b/hardware/firmware/audio_board/vendor/Audio/examples/HardwareTesting/ADAT_DrumSamplePlayer/ADAT_DrumSamplePlayer.ino @@ -73,4 +73,4 @@ void loop() { delay(1000); -} +} From 63ef549be382185bf90a3e9c040e3c93d1769ff1 Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Sat, 15 Nov 2025 09:24:40 +0100 Subject: [PATCH 03/67] New DSP config --- .../audio_board/src/teensyaudio_generated.cpp | 276 +++++++++--------- 1 file changed, 138 insertions(+), 138 deletions(-) diff --git a/hardware/firmware/audio_board/src/teensyaudio_generated.cpp b/hardware/firmware/audio_board/src/teensyaudio_generated.cpp index d67ecc01..b439e390 100644 --- a/hardware/firmware/audio_board/src/teensyaudio_generated.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio_generated.cpp @@ -1,144 +1,144 @@ // clang-format off // GUItool: begin automatically generated code -AudioInputI2SQuad i2s_quad1; //xy=336,677 -AudioSynthNoisePink pink1; //xy=338,824 -AudioSynthWaveformSine sine1; //xy=340,784 -AudioInputUSB usb1; //xy=341,743 -AudioAnalyzePeak peak5; //xy=603,433 -AudioAnalyzePeak peak1; //xy=604,299 -AudioAnalyzePeak peak2; //xy=605,332 -AudioAnalyzePeak peak3; //xy=605,366 -AudioAnalyzePeak peak6; //xy=605,467 -AudioAnalyzePeak peak4; //xy=607,399 -AudioAnalyzeRMS rms5; //xy=618,1246 -AudioAnalyzeRMS rms6; //xy=620,1279 -AudioAnalyzeRMS rms3; //xy=621,1180 -AudioAnalyzeRMS rms1; //xy=622,1115 -AudioAnalyzeRMS rms2; //xy=622,1148 -AudioAnalyzeRMS rms4; //xy=622,1213 -AudioMixer4 mixer1; //xy=787,364 -AudioMixer4 mixer4; //xy=788,502 -AudioMixer4 mixer2; //xy=789,428 -AudioMixer4 mixer5; //xy=790,566 -AudioMixer4 mixer7; //xy=791,640 -AudioMixer4 mixer8; //xy=793,704 -AudioMixer4 mixer10; //xy=795,773 -AudioMixer4 mixer11; //xy=797,837 -AudioMixer4 mixer13; //xy=799,913 -AudioMixer4 mixer14; //xy=801,977 -AudioMixer4 mixer16; //xy=803,1046 -AudioMixer4 mixer17; //xy=805,1110 -AudioMixer4 mixer3; //xy=949,395 -AudioMixer4 mixer6; //xy=951,536 -AudioMixer4 mixer9; //xy=951,673 -AudioMixer4 mixer12; //xy=955,803 -AudioMixer4 mixer15; //xy=956,948 -AudioMixer4 mixer18; //xy=957,1075 -AudioOutputI2SQuad i2s_quad2; //xy=1324,614 -AudioOutputUSB usb2; //xy=1331,997 -AudioAnalyzeRMS rms7; //xy=1335,1094 -AudioAnalyzeRMS rms8; //xy=1336,1126 -AudioAnalyzeRMS rms11; //xy=1336,1221 -AudioAnalyzeRMS rms12; //xy=1336,1254 -AudioAnalyzeRMS rms9; //xy=1337,1158 -AudioAnalyzeRMS rms10; //xy=1338,1190 -AudioAnalyzePeak peak7; //xy=1342,270 -AudioAnalyzePeak peak9; //xy=1342,335 -AudioAnalyzePeak peak10; //xy=1342,366 -AudioAnalyzePeak peak8; //xy=1343,303 -AudioAnalyzePeak peak11; //xy=1343,397 -AudioAnalyzePeak peak12; //xy=1343,428 -AudioConnection patchCord9(i2s_quad1, 2, mixer1, 1); -AudioConnection patchCord10(i2s_quad1, 2, mixer4, 1); -AudioConnection patchCord11(i2s_quad1, 2, mixer7, 1); -AudioConnection patchCord12(i2s_quad1, 2, mixer10, 1); -AudioConnection patchCord13(i2s_quad1, 2, mixer13, 1); -AudioConnection patchCord14(i2s_quad1, 2, mixer16, 1); -AudioConnection patchCord15(i2s_quad1, 2, peak2, 0); -AudioConnection patchCord16(i2s_quad1, 2, rms2, 0); -AudioConnection patchCord1(i2s_quad1, 0, peak1, 0); -AudioConnection patchCord2(i2s_quad1, 0, rms1, 0); -AudioConnection patchCord3(i2s_quad1, 0, mixer1, 0); -AudioConnection patchCord4(i2s_quad1, 0, mixer4, 0); -AudioConnection patchCord5(i2s_quad1, 0, mixer7, 0); -AudioConnection patchCord6(i2s_quad1, 0, mixer10, 0); -AudioConnection patchCord7(i2s_quad1, 0, mixer13, 0); -AudioConnection patchCord8(i2s_quad1, 0, mixer16, 0); -AudioConnection patchCord25(i2s_quad1, 1, mixer1, 3); -AudioConnection patchCord26(i2s_quad1, 1, mixer4, 3); -AudioConnection patchCord27(i2s_quad1, 1, mixer7, 3); -AudioConnection patchCord28(i2s_quad1, 1, mixer10, 3); -AudioConnection patchCord29(i2s_quad1, 1, mixer13, 3); -AudioConnection patchCord30(i2s_quad1, 1, mixer16, 3); -AudioConnection patchCord31(i2s_quad1, 1, peak4, 0); -AudioConnection patchCord32(i2s_quad1, 1, rms4, 0); -AudioConnection patchCord17(i2s_quad1, 3, mixer1, 2); -AudioConnection patchCord18(i2s_quad1, 3, mixer4, 2); -AudioConnection patchCord19(i2s_quad1, 3, mixer7, 2); -AudioConnection patchCord20(i2s_quad1, 3, mixer10, 2); -AudioConnection patchCord21(i2s_quad1, 3, mixer13, 2); -AudioConnection patchCord22(i2s_quad1, 3, mixer16, 2); -AudioConnection patchCord23(i2s_quad1, 3, peak3, 0); -AudioConnection patchCord24(i2s_quad1, 3, rms3, 0); -AudioConnection patchCord33(pink1, 0, mixer2, 3); -AudioConnection patchCord34(pink1, 0, mixer5, 3); -AudioConnection patchCord35(pink1, 0, mixer8, 3); -AudioConnection patchCord36(pink1, 0, mixer11, 3); -AudioConnection patchCord37(pink1, 0, mixer14, 3); -AudioConnection patchCord38(pink1, 0, mixer17, 3); -AudioConnection patchCord39(sine1, 0, mixer2, 2); -AudioConnection patchCord40(sine1, 0, mixer5, 2); -AudioConnection patchCord41(sine1, 0, mixer8, 2); -AudioConnection patchCord42(sine1, 0, mixer11, 2); -AudioConnection patchCord43(sine1, 0, mixer14, 2); -AudioConnection patchCord44(sine1, 0, mixer17, 2); -AudioConnection patchCord45(usb1, 0, mixer2, 0); -AudioConnection patchCord46(usb1, 0, mixer5, 0); -AudioConnection patchCord47(usb1, 0, mixer8, 0); -AudioConnection patchCord48(usb1, 0, mixer11, 0); -AudioConnection patchCord49(usb1, 0, mixer14, 0); -AudioConnection patchCord50(usb1, 0, mixer17, 0); -AudioConnection patchCord51(usb1, 0, peak5, 0); -AudioConnection patchCord52(usb1, 0, rms5, 0); -AudioConnection patchCord53(usb1, 1, mixer2, 1); -AudioConnection patchCord54(usb1, 1, mixer5, 1); -AudioConnection patchCord55(usb1, 1, mixer8, 1); -AudioConnection patchCord56(usb1, 1, mixer11, 1); -AudioConnection patchCord57(usb1, 1, mixer14, 1); -AudioConnection patchCord58(usb1, 1, mixer17, 1); -AudioConnection patchCord59(usb1, 1, peak6, 0); -AudioConnection patchCord60(usb1, 1, rms6, 0); -AudioConnection patchCord61(mixer1, 0, mixer3, 0); -AudioConnection patchCord62(mixer4, 0, mixer6, 0); -AudioConnection patchCord63(mixer2, 0, mixer3, 1); -AudioConnection patchCord64(mixer5, 0, mixer6, 1); -AudioConnection patchCord65(mixer7, 0, mixer9, 0); -AudioConnection patchCord66(mixer8, 0, mixer9, 1); -AudioConnection patchCord67(mixer10, 0, mixer12, 0); -AudioConnection patchCord68(mixer11, 0, mixer12, 1); -AudioConnection patchCord69(mixer13, 0, mixer15, 0); -AudioConnection patchCord70(mixer14, 0, mixer15, 1); -AudioConnection patchCord71(mixer16, 0, mixer18, 0); -AudioConnection patchCord72(mixer17, 0, mixer18, 1); -AudioConnection patchCord73(mixer3, 0, i2s_quad2, 0); -AudioConnection patchCord74(mixer3, rms7); -AudioConnection patchCord75(mixer3, peak7); -AudioConnection patchCord76(mixer6, 0, i2s_quad2, 1); -AudioConnection patchCord77(mixer6, rms8); -AudioConnection patchCord78(mixer6, peak8); -AudioConnection patchCord79(mixer9, 0, i2s_quad2, 2); -AudioConnection patchCord80(mixer9, rms9); -AudioConnection patchCord81(mixer9, peak9); -AudioConnection patchCord82(mixer12, 0, i2s_quad2, 3); -AudioConnection patchCord83(mixer12, rms10); -AudioConnection patchCord84(mixer12, peak10); -AudioConnection patchCord85(mixer15, 0, usb2, 0); -AudioConnection patchCord86(mixer15, rms11); -AudioConnection patchCord87(mixer15, peak11); -AudioConnection patchCord88(mixer18, 0, usb2, 1); -AudioConnection patchCord89(mixer18, rms12); -AudioConnection patchCord90(mixer18, peak12); +AudioInputTDM tdm1; //xy=131,374 +AudioSynthNoisePink pink1; //xy=146,697 +AudioSynthWaveformSine sine1; //xy=148,657 +AudioInputUSB usb1; //xy=149,616 +AudioAnalyzePeak peak5; //xy=411,306 +AudioAnalyzePeak peak1; //xy=412,172 +AudioAnalyzePeak peak2; //xy=413,205 +AudioAnalyzePeak peak3; //xy=413,239 +AudioAnalyzePeak peak6; //xy=413,340 +AudioAnalyzePeak peak4; //xy=415,272 +AudioAnalyzeRMS rms5; //xy=426,1119 +AudioAnalyzeRMS rms6; //xy=428,1152 +AudioAnalyzeRMS rms3; //xy=429,1053 +AudioAnalyzeRMS rms1; //xy=430,988 +AudioAnalyzeRMS rms2; //xy=430,1021 +AudioAnalyzeRMS rms4; //xy=430,1086 +AudioMixer4 mixer1; //xy=595,237 +AudioMixer4 mixer4; //xy=596,375 +AudioMixer4 mixer2; //xy=597,301 +AudioMixer4 mixer5; //xy=598,439 +AudioMixer4 mixer7; //xy=599,513 +AudioMixer4 mixer8; //xy=601,577 +AudioMixer4 mixer10; //xy=603,646 +AudioMixer4 mixer11; //xy=605,710 +AudioMixer4 mixer13; //xy=607,786 +AudioMixer4 mixer14; //xy=609,850 +AudioMixer4 mixer16; //xy=611,919 +AudioMixer4 mixer17; //xy=613,983 +AudioMixer4 mixer3; //xy=757,268 +AudioMixer4 mixer6; //xy=759,409 +AudioMixer4 mixer9; //xy=759,546 +AudioMixer4 mixer12; //xy=763,676 +AudioMixer4 mixer15; //xy=764,821 +AudioMixer4 mixer18; //xy=765,948 +AudioOutputUSB usb2; //xy=1139,870 +AudioAnalyzeRMS rms7; //xy=1143,967 +AudioAnalyzeRMS rms8; //xy=1144,999 +AudioAnalyzeRMS rms11; //xy=1144,1094 +AudioAnalyzeRMS rms12; //xy=1144,1127 +AudioAnalyzeRMS rms9; //xy=1145,1031 +AudioAnalyzeRMS rms10; //xy=1146,1063 +AudioAnalyzePeak peak7; //xy=1150,143 +AudioAnalyzePeak peak9; //xy=1150,208 +AudioAnalyzePeak peak10; //xy=1150,239 +AudioAnalyzePeak peak8; //xy=1151,176 +AudioAnalyzePeak peak11; //xy=1151,270 +AudioAnalyzePeak peak12; //xy=1151,301 +AudioOutputTDM tdm2; //xy=1155,613 +AudioConnection patchCord1(tdm1, 0, peak1, 0); +AudioConnection patchCord2(tdm1, 0, rms1, 0); +AudioConnection patchCord3(tdm1, 0, mixer1, 0); +AudioConnection patchCord4(tdm1, 0, mixer4, 0); +AudioConnection patchCord5(tdm1, 0, mixer7, 0); +AudioConnection patchCord6(tdm1, 0, mixer10, 0); +AudioConnection patchCord7(tdm1, 0, mixer13, 0); +AudioConnection patchCord8(tdm1, 0, mixer16, 0); +AudioConnection patchCord9(tdm1, 1, peak2, 0); +AudioConnection patchCord10(tdm1, 1, rms2, 0); +AudioConnection patchCord11(tdm1, 1, mixer1, 1); +AudioConnection patchCord12(tdm1, 1, mixer4, 1); +AudioConnection patchCord13(tdm1, 1, mixer7, 1); +AudioConnection patchCord14(tdm1, 1, mixer10, 1); +AudioConnection patchCord15(tdm1, 1, mixer13, 1); +AudioConnection patchCord16(tdm1, 1, mixer16, 1); +AudioConnection patchCord17(tdm1, 2, peak3, 0); +AudioConnection patchCord18(tdm1, 2, rms3, 0); +AudioConnection patchCord19(tdm1, 2, mixer1, 2); +AudioConnection patchCord20(tdm1, 2, mixer4, 2); +AudioConnection patchCord21(tdm1, 2, mixer7, 2); +AudioConnection patchCord22(tdm1, 2, mixer10, 2); +AudioConnection patchCord23(tdm1, 2, mixer13, 2); +AudioConnection patchCord24(tdm1, 2, mixer16, 2); +AudioConnection patchCord25(tdm1, 3, peak4, 0); +AudioConnection patchCord26(tdm1, 3, rms4, 0); +AudioConnection patchCord27(tdm1, 3, mixer1, 3); +AudioConnection patchCord28(tdm1, 3, mixer4, 3); +AudioConnection patchCord29(tdm1, 3, mixer7, 3); +AudioConnection patchCord30(tdm1, 3, mixer10, 3); +AudioConnection patchCord31(tdm1, 3, mixer13, 3); +AudioConnection patchCord32(tdm1, 3, mixer16, 3); +AudioConnection patchCord33(pink1, 0, mixer2, 3); +AudioConnection patchCord34(pink1, 0, mixer5, 3); +AudioConnection patchCord35(pink1, 0, mixer8, 3); +AudioConnection patchCord36(pink1, 0, mixer11, 3); +AudioConnection patchCord37(pink1, 0, mixer14, 3); +AudioConnection patchCord38(pink1, 0, mixer17, 3); +AudioConnection patchCord39(sine1, 0, mixer2, 2); +AudioConnection patchCord40(sine1, 0, mixer5, 2); +AudioConnection patchCord41(sine1, 0, mixer8, 2); +AudioConnection patchCord42(sine1, 0, mixer11, 2); +AudioConnection patchCord43(sine1, 0, mixer14, 2); +AudioConnection patchCord44(sine1, 0, mixer17, 2); +AudioConnection patchCord45(usb1, 0, mixer2, 0); +AudioConnection patchCord46(usb1, 0, mixer5, 0); +AudioConnection patchCord47(usb1, 0, mixer8, 0); +AudioConnection patchCord48(usb1, 0, mixer11, 0); +AudioConnection patchCord49(usb1, 0, mixer14, 0); +AudioConnection patchCord50(usb1, 0, mixer17, 0); +AudioConnection patchCord51(usb1, 0, peak5, 0); +AudioConnection patchCord52(usb1, 0, rms5, 0); +AudioConnection patchCord53(usb1, 1, mixer2, 1); +AudioConnection patchCord54(usb1, 1, mixer5, 1); +AudioConnection patchCord55(usb1, 1, mixer8, 1); +AudioConnection patchCord56(usb1, 1, mixer11, 1); +AudioConnection patchCord57(usb1, 1, mixer14, 1); +AudioConnection patchCord58(usb1, 1, mixer17, 1); +AudioConnection patchCord59(usb1, 1, peak6, 0); +AudioConnection patchCord60(usb1, 1, rms6, 0); +AudioConnection patchCord61(mixer1, 0, mixer3, 0); +AudioConnection patchCord62(mixer4, 0, mixer6, 0); +AudioConnection patchCord63(mixer2, 0, mixer3, 1); +AudioConnection patchCord64(mixer5, 0, mixer6, 1); +AudioConnection patchCord65(mixer7, 0, mixer9, 0); +AudioConnection patchCord66(mixer8, 0, mixer9, 1); +AudioConnection patchCord67(mixer10, 0, mixer12, 0); +AudioConnection patchCord68(mixer11, 0, mixer12, 1); +AudioConnection patchCord69(mixer13, 0, mixer15, 0); +AudioConnection patchCord70(mixer14, 0, mixer15, 1); +AudioConnection patchCord71(mixer16, 0, mixer18, 0); +AudioConnection patchCord72(mixer17, 0, mixer18, 1); +AudioConnection patchCord73(mixer3, rms7); +AudioConnection patchCord74(mixer3, peak7); +AudioConnection patchCord75(mixer3, 0, tdm2, 12); +AudioConnection patchCord76(mixer6, rms8); +AudioConnection patchCord77(mixer6, peak8); +AudioConnection patchCord78(mixer6, 0, tdm2, 14); +AudioConnection patchCord79(mixer9, rms9); +AudioConnection patchCord80(mixer9, peak9); +AudioConnection patchCord81(mixer9, 0, tdm2, 8); +AudioConnection patchCord82(mixer12, rms10); +AudioConnection patchCord83(mixer12, peak10); +AudioConnection patchCord84(mixer15, 0, usb2, 0); +AudioConnection patchCord85(mixer15, rms11); +AudioConnection patchCord86(mixer15, peak11); +AudioConnection patchCord87(mixer15, 0, tdm2, 10); +AudioConnection patchCord88(mixer18, 0, usb2, 1); +AudioConnection patchCord89(mixer18, rms12); +AudioConnection patchCord90(mixer18, peak12); // GUItool: end automatically generated code // clang-format on From db177d6cd9add107c1081f26d6066bc65adc4335 Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Sat, 15 Nov 2025 09:08:51 +0100 Subject: [PATCH 04/67] Add TAA3040 driver --- hardware/firmware/audio_board/CMakeLists.txt | 1 + hardware/firmware/audio_board/src/taa3040.cpp | 179 ++++++++++++++++++ hardware/firmware/audio_board/src/taa3040.h | 42 ++++ 3 files changed, 222 insertions(+) create mode 100644 hardware/firmware/audio_board/src/taa3040.cpp create mode 100644 hardware/firmware/audio_board/src/taa3040.h diff --git a/hardware/firmware/audio_board/CMakeLists.txt b/hardware/firmware/audio_board/CMakeLists.txt index 5d442389..a1c36d4a 100644 --- a/hardware/firmware/audio_board/CMakeLists.txt +++ b/hardware/firmware/audio_board/CMakeLists.txt @@ -114,6 +114,7 @@ set(SOURCES src/display.cpp src/helpers.cpp src/teensyaudio.cpp + src/taa3040.cpp ) # Build this project diff --git a/hardware/firmware/audio_board/src/taa3040.cpp b/hardware/firmware/audio_board/src/taa3040.cpp new file mode 100644 index 00000000..bef2763a --- /dev/null +++ b/hardware/firmware/audio_board/src/taa3040.cpp @@ -0,0 +1,179 @@ + +#include "taa3040.h" +#include + +bool AudioControlTAA3040::enable(void) { + setRegister(0, REG_P0_SW_RESET, 1); + delay(100); + setRegister(0, REG_P0_SW_RESET, 0); + delay(100); + setRegister(0, REG_P0_SLEEP_CFG, bit(0) | bit(7)); // Wake from sleep, use internal AREG generator + delay(10); + setRegister(0, REG_P0_ASI_OUT_CH_EN, 0xF0); // Enable ASI output channel 0-3 + configASI0(0, 0, 0, 0, 0, 1); + + setRegister(0, REG_P0_PWR_CFG, 0xE0); // Power on PLL and ADC + delay(10); + setRegister(0, REG_P0_MST_CFG0, bit(3)); // 44.1Khz mode, TDM slave + return true; +} + +bool AudioControlTAA3040::disable(void) { + return true; +} + +bool AudioControlTAA3040::gain(uint8_t channel, uint8_t gain, uint8_t impedance) { + uint8_t offset = channel * 5; + setRegister(REG_P0_CH1_CFG0+offset, impedance << 2); + setRegister(REG_P0_CH1_CFG1+offset, gain << 2); + return true; +} +void AudioControlTAA3040::getAsiStatus() { + uint8_t raw = getRegister(0, REG_P0_ASI_STS); + if (raw == last_asi) { + return; + } + last_asi = raw; + uint8_t ratio = raw & 0x0F; + uint8_t rate = raw >> 4; + Serial.print("ASI Status: "); + switch(ratio) { + case 0: + SerialUSB1.print("ratio 16, "); + break; + case 1: + SerialUSB1.print("ratio 24, "); + break; + case 2: + SerialUSB1.print("ratio 32, "); + break; + case 3: + SerialUSB1.print("ratio 48, "); + break; + case 4: + SerialUSB1.print("ratio 64, "); + break; + case 5: + SerialUSB1.print("ratio 96, "); + break; + case 6: + SerialUSB1.print("ratio 128, "); + break; + case 7: + SerialUSB1.print("ratio 192, "); + break; + case 8: + SerialUSB1.print("ratio 256, "); + break; + case 9: + SerialUSB1.print("ratio 384, "); + break; + case 10: + SerialUSB1.print("ratio 512, "); + break; + case 11: + SerialUSB1.print("ratio 1024, "); + break; + case 12: + SerialUSB1.print("ratio 2048, "); + break; + case 13: + case 14: + SerialUSB1.printf("ratio RESERVED(%d), ", ratio); + break; + case 15: + SerialUSB1.print("ratio INVALID, "); + break; + } + switch(rate) { + case 0: + SerialUSB1.println("rate 7.35-8Khz"); + break; + case 1: + SerialUSB1.println("rate 14.7-16Khz"); + break; + case 2: + SerialUSB1.println("rate 22.05-24Khz"); + break; + case 3: + SerialUSB1.println("rate 29.4-32Khz"); + break; + case 4: + SerialUSB1.println("rate 44.1-48Khz"); + break; + case 5: + SerialUSB1.println("rate 88.2-96Khz"); + break; + case 6: + SerialUSB1.println("rate 176.4-192Khz"); + break; + case 7: + SerialUSB1.println("rate 352.8-384Khz"); + break; + case 8: + SerialUSB1.println("rate 705.6-768Khz"); + break; + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + SerialUSB1.printf("rate RESERVED(%d)\n", rate); + break; + case 15: + SerialUSB1.println("rate INVALID"); + break; + } + +} + +void AudioControlTAA3040::setRegister(uint8_t reg, uint8_t value) { + Wire1.beginTransmission(0x4e); + Wire1.write(reg); + Wire1.write(value); + if (Wire1.endTransmission() != 0) { + Serial.println("I2C error"); + } +} + +void AudioControlTAA3040::setRegister(uint8_t page, uint8_t reg, uint8_t value) { + Serial.print("REG "); + Serial.print(page, HEX); + Serial.print(":"); + Serial.print(reg, HEX); + Serial.print(" = "); + Serial.println(value, HEX); + + if (page != currentPage) { + setRegister(0, page); + currentPage = page; + } + setRegister(reg, value); +} + +uint8_t AudioControlTAA3040::getRegister(uint8_t page, uint8_t reg) { + if (page != currentPage) { + setRegister(0, page); + currentPage = page; + } + Wire1.beginTransmission(0x4e); + Wire1.write(reg); + Wire1.endTransmission(false); + Wire1.requestFrom(0x4e, 1, true); + const uint8_t res = Wire.read(); + if (Wire1.endTransmission() != 0) { + Serial.println("I2C error"); + } + return res; +} + +void AudioControlTAA3040::configASI0(uint8_t format, uint8_t wlen, uint8_t fsync_pol, uint8_t bclk_pol, uint8_t tx_edge, uint8_t tx_fill) { + uint8_t val = tx_fill; + val |= (tx_edge & 1) << 1; + val |= (bclk_pol & 1) << 2; + val |= (fsync_pol & 1) << 3; + val |= (wlen & 3) << 4; + val |= (format & 3) << 6; + setRegister(0, REG_P0_ASI_CFG0, val); +} \ No newline at end of file diff --git a/hardware/firmware/audio_board/src/taa3040.h b/hardware/firmware/audio_board/src/taa3040.h new file mode 100644 index 00000000..fadf9ff0 --- /dev/null +++ b/hardware/firmware/audio_board/src/taa3040.h @@ -0,0 +1,42 @@ +#ifndef TEENSY_AUDIO_TAA3040_H +#define TEENSY_AUDIO_TAA3040_H +#include + +#define REG_P0_SW_RESET (0x01) +#define REG_P0_SLEEP_CFG (0x02) +#define REG_P0_ASI_CFG0 (0x07) +#define REG_P0_ASI_CFG1 (0x08) +#define REG_P0_MST_CFG0 (0x13) +#define REG_P0_ASI_STS (0x15) +#define REG_P0_ASI_OUT_CH_EN (0x74) +#define REG_P0_PWR_CFG (0x75) +#define REG_P0_DEV_STS1 (0x77) +#define REG_P0_CH1_CFG0 (0x3C) +#define REG_P0_CH1_CFG1 (0x3D) + +#define IMPEDANCE_2k5 (0b00) +#define IMPEDANCE_10k (0b01) +#define IMPEDANCE_20k (0b10) + +class AudioControlTAA3040 +{ + public: + bool enable(); + bool disable(); + bool gain(uint8_t channel, uint8_t gain, uint8_t impedance); + + void getAsiStatus(); + +private: + void setRegister(uint8_t reg, uint8_t value); + void setRegister(uint8_t page, uint8_t reg, uint8_t value); + uint8_t getRegister(uint8_t page, uint8_t reg); + + void configASI0(uint8_t format, uint8_t wlen, uint8_t fsync_pol, uint8_t bclk_pol, uint8_t tx_edge, uint8_t tx_fill); + + uint8_t currentPage = 0; + uint8_t last_sts1 = 0; + uint8_t last_asi = 0; +}; + +#endif // TEENSY_AUDIO_TAA3040_H From 016bd1be8e6d42f88e5798310346cc9826ffb897 Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Sat, 15 Nov 2025 09:25:44 +0100 Subject: [PATCH 05/67] lower TDM drive strength --- hardware/firmware/audio_board/vendor/Audio/output_tdm.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hardware/firmware/audio_board/vendor/Audio/output_tdm.cpp b/hardware/firmware/audio_board/vendor/Audio/output_tdm.cpp index 8f3a6158..8414a3b1 100644 --- a/hardware/firmware/audio_board/vendor/Audio/output_tdm.cpp +++ b/hardware/firmware/audio_board/vendor/Audio/output_tdm.cpp @@ -333,9 +333,13 @@ void AudioOutputTDM::config_tdm(void) | I2S_RCR4_FSE | I2S_RCR4_FSD; I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); + CORE_PIN23_PADCONFIG = IOMUXC_PAD_DSE(7); CORE_PIN23_CONFIG = 3; //1:MCLK + CORE_PIN21_PADCONFIG = IOMUXC_PAD_DSE(7); CORE_PIN21_CONFIG = 3; //1:RX_BCLK CORE_PIN20_CONFIG = 3; //1:RX_SYNC + CORE_PIN20_PADCONFIG = IOMUXC_PAD_DSE(7); + #endif } From 0a3cd1c467dd7eb8af7bd6249173ab3ef6f0b1f9 Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Sat, 15 Nov 2025 09:26:09 +0100 Subject: [PATCH 06/67] Implement taa3040 --- .../firmware/audio_board/src/teensyaudio.cpp | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index 5bfb4d4b..a19d9fd3 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -12,11 +12,11 @@ #endif +#include "taa3040.h" #include "teensyaudio_defaults.cpp" #include "teensyaudio_generated.cpp" -AudioControlSGTL5000 sgtl5000_1; -AudioControlSGTL5000 sgtl5000_2; +AudioControlTAA3040 taa3040; AudioMixer4 *matrix[6][2] = { {&mixer1, &mixer2}, {&mixer4, &mixer5}, {&mixer7, &mixer8}, @@ -39,24 +39,14 @@ AudioState state; void audio_setup() { AudioMemory(64); - uint8_t adcGain = 0; + Wire.begin(); + Wire1.begin(); - sgtl5000_1.enable(); - sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN); - sgtl5000_1.volume(0.5); - sgtl5000_1.lineOutLevel(13); - sgtl5000_1.lineInLevel(adcGain, adcGain); - sgtl5000_1.adcHighPassFilterDisable(); - sgtl5000_1.muteHeadphone(); - - sgtl5000_2.setWire(1); - - sgtl5000_2.enable(); - sgtl5000_2.inputSelect(AUDIO_INPUT_LINEIN); - sgtl5000_2.volume(0.5); - sgtl5000_2.lineOutLevel(13); - sgtl5000_2.lineInLevel(adcGain, adcGain); - sgtl5000_2.adcHighPassFilterDisable(); + taa3040.enable(); + taa3040.gain(0, 36, IMPEDANCE_2k5); + taa3040.gain(1, 36, IMPEDANCE_2k5); + taa3040.gain(2, 36, IMPEDANCE_2k5); + taa3040.gain(3, 36, IMPEDANCE_2k5); } void audio_update_levels(Levels &levels) { @@ -74,6 +64,8 @@ void audio_update_levels(Levels &levels) { } levels.peak[i] = ent_peak[i]->read(); } + + taa3040.getAsiStatus(); } } From f16691bcb38815ae9fdb45979ac8a12f1e80039e Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Wed, 19 Nov 2025 13:46:37 +0100 Subject: [PATCH 07/67] Changes after the hackathon --- hardware/firmware/audio_board/src/taa3040.cpp | 57 +++++----- hardware/firmware/audio_board/src/taa3040.h | 2 +- .../firmware/audio_board/src/teensyaudio.cpp | 10 +- .../audio_board/src/teensyaudio_generated.cpp | 106 +++++++++--------- .../audio_board/vendor/Audio/output_tdm.cpp | 2 +- 5 files changed, 88 insertions(+), 89 deletions(-) diff --git a/hardware/firmware/audio_board/src/taa3040.cpp b/hardware/firmware/audio_board/src/taa3040.cpp index bef2763a..ed44a06a 100644 --- a/hardware/firmware/audio_board/src/taa3040.cpp +++ b/hardware/firmware/audio_board/src/taa3040.cpp @@ -22,12 +22,13 @@ bool AudioControlTAA3040::disable(void) { return true; } -bool AudioControlTAA3040::gain(uint8_t channel, uint8_t gain, uint8_t impedance) { +bool AudioControlTAA3040::gain(uint8_t channel, uint8_t gain, uint8_t impedance, uint8_t mode, uint8_t coupling) { uint8_t offset = channel * 5; - setRegister(REG_P0_CH1_CFG0+offset, impedance << 2); + setRegister(REG_P0_CH1_CFG0+offset, (impedance << 2)|(mode << 5)|(coupling<<4)); setRegister(REG_P0_CH1_CFG1+offset, gain << 2); return true; } + void AudioControlTAA3040::getAsiStatus() { uint8_t raw = getRegister(0, REG_P0_ASI_STS); if (raw == last_asi) { @@ -39,79 +40,79 @@ void AudioControlTAA3040::getAsiStatus() { Serial.print("ASI Status: "); switch(ratio) { case 0: - SerialUSB1.print("ratio 16, "); + Serial.print("ratio 16, "); break; case 1: - SerialUSB1.print("ratio 24, "); + Serial.print("ratio 24, "); break; case 2: - SerialUSB1.print("ratio 32, "); + Serial.print("ratio 32, "); break; case 3: - SerialUSB1.print("ratio 48, "); + Serial.print("ratio 48, "); break; case 4: - SerialUSB1.print("ratio 64, "); + Serial.print("ratio 64, "); break; case 5: - SerialUSB1.print("ratio 96, "); + Serial.print("ratio 96, "); break; case 6: - SerialUSB1.print("ratio 128, "); + Serial.print("ratio 128, "); break; case 7: - SerialUSB1.print("ratio 192, "); + Serial.print("ratio 192, "); break; case 8: - SerialUSB1.print("ratio 256, "); + Serial.print("ratio 256, "); break; case 9: - SerialUSB1.print("ratio 384, "); + Serial.print("ratio 384, "); break; case 10: - SerialUSB1.print("ratio 512, "); + Serial.print("ratio 512, "); break; case 11: - SerialUSB1.print("ratio 1024, "); + Serial.print("ratio 1024, "); break; case 12: - SerialUSB1.print("ratio 2048, "); + Serial.print("ratio 2048, "); break; case 13: case 14: - SerialUSB1.printf("ratio RESERVED(%d), ", ratio); + Serial.printf("ratio RESERVED(%d), ", ratio); break; case 15: - SerialUSB1.print("ratio INVALID, "); + Serial.print("ratio INVALID, "); break; } switch(rate) { case 0: - SerialUSB1.println("rate 7.35-8Khz"); + Serial.println("rate 7.35-8Khz"); break; case 1: - SerialUSB1.println("rate 14.7-16Khz"); + Serial.println("rate 14.7-16Khz"); break; case 2: - SerialUSB1.println("rate 22.05-24Khz"); + Serial.println("rate 22.05-24Khz"); break; case 3: - SerialUSB1.println("rate 29.4-32Khz"); + Serial.println("rate 29.4-32Khz"); break; case 4: - SerialUSB1.println("rate 44.1-48Khz"); + Serial.println("rate 44.1-48Khz"); break; case 5: - SerialUSB1.println("rate 88.2-96Khz"); + Serial.println("rate 88.2-96Khz"); break; case 6: - SerialUSB1.println("rate 176.4-192Khz"); + Serial.println("rate 176.4-192Khz"); break; case 7: - SerialUSB1.println("rate 352.8-384Khz"); + Serial.println("rate 352.8-384Khz"); break; case 8: - SerialUSB1.println("rate 705.6-768Khz"); + Serial.println("rate 705.6-768Khz"); break; case 9: case 10: @@ -119,10 +120,10 @@ void AudioControlTAA3040::getAsiStatus() { case 12: case 13: case 14: - SerialUSB1.printf("rate RESERVED(%d)\n", rate); + Serial.printf("rate RESERVED(%d)\n", rate); break; case 15: - SerialUSB1.println("rate INVALID"); + Serial.println("rate INVALID"); break; } diff --git a/hardware/firmware/audio_board/src/taa3040.h b/hardware/firmware/audio_board/src/taa3040.h index fadf9ff0..89ed5968 100644 --- a/hardware/firmware/audio_board/src/taa3040.h +++ b/hardware/firmware/audio_board/src/taa3040.h @@ -23,7 +23,7 @@ class AudioControlTAA3040 public: bool enable(); bool disable(); - bool gain(uint8_t channel, uint8_t gain, uint8_t impedance); + bool gain(uint8_t channel, uint8_t gain, uint8_t impedance,uint8_t mode, uint8_t coupling); void getAsiStatus(); diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index a19d9fd3..152d2a74 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -37,16 +37,16 @@ Levels levels; AudioState state; void audio_setup() { - AudioMemory(64); + AudioMemory(80); Wire.begin(); Wire1.begin(); taa3040.enable(); - taa3040.gain(0, 36, IMPEDANCE_2k5); - taa3040.gain(1, 36, IMPEDANCE_2k5); - taa3040.gain(2, 36, IMPEDANCE_2k5); - taa3040.gain(3, 36, IMPEDANCE_2k5); + taa3040.gain(0, 36, IMPEDANCE_2k5, 0, 0); + taa3040.gain(1, 36, IMPEDANCE_2k5, 0, 0); + taa3040.gain(2, 36, IMPEDANCE_2k5, 0,0); + taa3040.gain(3, 6, IMPEDANCE_10k, 0, 0); } void audio_update_levels(Levels &levels) { diff --git a/hardware/firmware/audio_board/src/teensyaudio_generated.cpp b/hardware/firmware/audio_board/src/teensyaudio_generated.cpp index b439e390..2290293b 100644 --- a/hardware/firmware/audio_board/src/teensyaudio_generated.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio_generated.cpp @@ -1,54 +1,54 @@ // clang-format off // GUItool: begin automatically generated code -AudioInputTDM tdm1; //xy=131,374 -AudioSynthNoisePink pink1; //xy=146,697 -AudioSynthWaveformSine sine1; //xy=148,657 -AudioInputUSB usb1; //xy=149,616 -AudioAnalyzePeak peak5; //xy=411,306 -AudioAnalyzePeak peak1; //xy=412,172 -AudioAnalyzePeak peak2; //xy=413,205 -AudioAnalyzePeak peak3; //xy=413,239 -AudioAnalyzePeak peak6; //xy=413,340 -AudioAnalyzePeak peak4; //xy=415,272 -AudioAnalyzeRMS rms5; //xy=426,1119 -AudioAnalyzeRMS rms6; //xy=428,1152 -AudioAnalyzeRMS rms3; //xy=429,1053 -AudioAnalyzeRMS rms1; //xy=430,988 -AudioAnalyzeRMS rms2; //xy=430,1021 -AudioAnalyzeRMS rms4; //xy=430,1086 -AudioMixer4 mixer1; //xy=595,237 -AudioMixer4 mixer4; //xy=596,375 -AudioMixer4 mixer2; //xy=597,301 -AudioMixer4 mixer5; //xy=598,439 -AudioMixer4 mixer7; //xy=599,513 -AudioMixer4 mixer8; //xy=601,577 -AudioMixer4 mixer10; //xy=603,646 -AudioMixer4 mixer11; //xy=605,710 -AudioMixer4 mixer13; //xy=607,786 -AudioMixer4 mixer14; //xy=609,850 -AudioMixer4 mixer16; //xy=611,919 -AudioMixer4 mixer17; //xy=613,983 -AudioMixer4 mixer3; //xy=757,268 -AudioMixer4 mixer6; //xy=759,409 -AudioMixer4 mixer9; //xy=759,546 -AudioMixer4 mixer12; //xy=763,676 -AudioMixer4 mixer15; //xy=764,821 -AudioMixer4 mixer18; //xy=765,948 -AudioOutputUSB usb2; //xy=1139,870 -AudioAnalyzeRMS rms7; //xy=1143,967 -AudioAnalyzeRMS rms8; //xy=1144,999 -AudioAnalyzeRMS rms11; //xy=1144,1094 -AudioAnalyzeRMS rms12; //xy=1144,1127 -AudioAnalyzeRMS rms9; //xy=1145,1031 -AudioAnalyzeRMS rms10; //xy=1146,1063 -AudioAnalyzePeak peak7; //xy=1150,143 -AudioAnalyzePeak peak9; //xy=1150,208 -AudioAnalyzePeak peak10; //xy=1150,239 -AudioAnalyzePeak peak8; //xy=1151,176 -AudioAnalyzePeak peak11; //xy=1151,270 -AudioAnalyzePeak peak12; //xy=1151,301 -AudioOutputTDM tdm2; //xy=1155,613 +AudioInputTDM tdm1; //xy=163,303 +AudioSynthNoisePink pink1; //xy=178,626 +AudioSynthWaveformSine sine1; //xy=180,586 +AudioInputUSB usb1; //xy=181,545 +AudioAnalyzePeak peak5; //xy=443,235 +AudioAnalyzePeak peak1; //xy=444,101 +AudioAnalyzePeak peak2; //xy=445,134 +AudioAnalyzePeak peak3; //xy=445,168 +AudioAnalyzePeak peak6; //xy=445,269 +AudioAnalyzePeak peak4; //xy=447,201 +AudioAnalyzeRMS rms5; //xy=458,1048 +AudioAnalyzeRMS rms6; //xy=460,1081 +AudioAnalyzeRMS rms3; //xy=461,982 +AudioAnalyzeRMS rms1; //xy=462,917 +AudioAnalyzeRMS rms2; //xy=462,950 +AudioAnalyzeRMS rms4; //xy=462,1015 +AudioMixer4 mixer1; //xy=627,166 +AudioMixer4 mixer4; //xy=628,304 +AudioMixer4 mixer2; //xy=629,230 +AudioMixer4 mixer5; //xy=630,368 +AudioMixer4 mixer7; //xy=631,442 +AudioMixer4 mixer8; //xy=633,506 +AudioMixer4 mixer10; //xy=635,575 +AudioMixer4 mixer11; //xy=637,639 +AudioMixer4 mixer13; //xy=639,715 +AudioMixer4 mixer14; //xy=641,779 +AudioMixer4 mixer16; //xy=643,848 +AudioMixer4 mixer17; //xy=645,912 +AudioMixer4 mixer3; //xy=789,197 +AudioMixer4 mixer6; //xy=791,338 +AudioMixer4 mixer9; //xy=791,475 +AudioMixer4 mixer12; //xy=795,605 +AudioMixer4 mixer15; //xy=796,750 +AudioMixer4 mixer18; //xy=797,877 +AudioOutputUSB usb2; //xy=1171,799 +AudioAnalyzeRMS rms7; //xy=1175,896 +AudioAnalyzeRMS rms8; //xy=1176,928 +AudioAnalyzeRMS rms11; //xy=1176,1023 +AudioAnalyzeRMS rms12; //xy=1176,1056 +AudioAnalyzeRMS rms9; //xy=1177,960 +AudioAnalyzeRMS rms10; //xy=1178,992 +AudioAnalyzePeak peak7; //xy=1182,72 +AudioAnalyzePeak peak9; //xy=1182,137 +AudioAnalyzePeak peak10; //xy=1182,168 +AudioAnalyzePeak peak8; //xy=1183,105 +AudioAnalyzePeak peak11; //xy=1183,199 +AudioAnalyzePeak peak12; //xy=1183,230 +AudioOutputTDM tdm2; //xy=1187,542 AudioConnection patchCord1(tdm1, 0, peak1, 0); AudioConnection patchCord2(tdm1, 0, rms1, 0); AudioConnection patchCord3(tdm1, 0, mixer1, 0); @@ -132,13 +132,11 @@ AudioConnection patchCord80(mixer9, peak9); AudioConnection patchCord81(mixer9, 0, tdm2, 8); AudioConnection patchCord82(mixer12, rms10); AudioConnection patchCord83(mixer12, peak10); -AudioConnection patchCord84(mixer15, 0, usb2, 0); -AudioConnection patchCord85(mixer15, rms11); -AudioConnection patchCord86(mixer15, peak11); -AudioConnection patchCord87(mixer15, 0, tdm2, 10); +AudioConnection patchCord84(mixer12, 0, tdm2, 10); +AudioConnection patchCord85(mixer15, 0, usb2, 0); +AudioConnection patchCord86(mixer15, rms11); +AudioConnection patchCord87(mixer15, peak11); AudioConnection patchCord88(mixer18, 0, usb2, 1); AudioConnection patchCord89(mixer18, rms12); AudioConnection patchCord90(mixer18, peak12); // GUItool: end automatically generated code - -// clang-format on diff --git a/hardware/firmware/audio_board/vendor/Audio/output_tdm.cpp b/hardware/firmware/audio_board/vendor/Audio/output_tdm.cpp index 8414a3b1..8faae6cb 100644 --- a/hardware/firmware/audio_board/vendor/Audio/output_tdm.cpp +++ b/hardware/firmware/audio_board/vendor/Audio/output_tdm.cpp @@ -320,7 +320,7 @@ void AudioOutputTDM::config_tdm(void) I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0); I2S1_TCR3 = I2S_TCR3_TCE; - I2S1_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF + I2S1_TCR4 = I2S_TCR4_FRSZ(3) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSD; I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31); From 809fc863a04f21fe0c0f0996f8b9df33dd6e0eea Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Thu, 4 Dec 2025 00:29:43 +0100 Subject: [PATCH 08/67] Change inputs and outputs to the ones on the rev. E board --- .../audio_board/src/teensyaudio_generated.cpp | 104 +++++++++--------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/hardware/firmware/audio_board/src/teensyaudio_generated.cpp b/hardware/firmware/audio_board/src/teensyaudio_generated.cpp index 2290293b..51ea5ac1 100644 --- a/hardware/firmware/audio_board/src/teensyaudio_generated.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio_generated.cpp @@ -1,54 +1,54 @@ // clang-format off // GUItool: begin automatically generated code -AudioInputTDM tdm1; //xy=163,303 -AudioSynthNoisePink pink1; //xy=178,626 -AudioSynthWaveformSine sine1; //xy=180,586 -AudioInputUSB usb1; //xy=181,545 -AudioAnalyzePeak peak5; //xy=443,235 -AudioAnalyzePeak peak1; //xy=444,101 -AudioAnalyzePeak peak2; //xy=445,134 -AudioAnalyzePeak peak3; //xy=445,168 -AudioAnalyzePeak peak6; //xy=445,269 -AudioAnalyzePeak peak4; //xy=447,201 -AudioAnalyzeRMS rms5; //xy=458,1048 -AudioAnalyzeRMS rms6; //xy=460,1081 -AudioAnalyzeRMS rms3; //xy=461,982 -AudioAnalyzeRMS rms1; //xy=462,917 -AudioAnalyzeRMS rms2; //xy=462,950 -AudioAnalyzeRMS rms4; //xy=462,1015 -AudioMixer4 mixer1; //xy=627,166 -AudioMixer4 mixer4; //xy=628,304 -AudioMixer4 mixer2; //xy=629,230 -AudioMixer4 mixer5; //xy=630,368 -AudioMixer4 mixer7; //xy=631,442 -AudioMixer4 mixer8; //xy=633,506 -AudioMixer4 mixer10; //xy=635,575 -AudioMixer4 mixer11; //xy=637,639 -AudioMixer4 mixer13; //xy=639,715 -AudioMixer4 mixer14; //xy=641,779 -AudioMixer4 mixer16; //xy=643,848 -AudioMixer4 mixer17; //xy=645,912 -AudioMixer4 mixer3; //xy=789,197 -AudioMixer4 mixer6; //xy=791,338 -AudioMixer4 mixer9; //xy=791,475 -AudioMixer4 mixer12; //xy=795,605 -AudioMixer4 mixer15; //xy=796,750 -AudioMixer4 mixer18; //xy=797,877 -AudioOutputUSB usb2; //xy=1171,799 -AudioAnalyzeRMS rms7; //xy=1175,896 -AudioAnalyzeRMS rms8; //xy=1176,928 -AudioAnalyzeRMS rms11; //xy=1176,1023 -AudioAnalyzeRMS rms12; //xy=1176,1056 -AudioAnalyzeRMS rms9; //xy=1177,960 -AudioAnalyzeRMS rms10; //xy=1178,992 -AudioAnalyzePeak peak7; //xy=1182,72 -AudioAnalyzePeak peak9; //xy=1182,137 -AudioAnalyzePeak peak10; //xy=1182,168 -AudioAnalyzePeak peak8; //xy=1183,105 -AudioAnalyzePeak peak11; //xy=1183,199 -AudioAnalyzePeak peak12; //xy=1183,230 -AudioOutputTDM tdm2; //xy=1187,542 +AudioInputTDM2 tdm1; //xy=103,324 +AudioSynthNoisePink pink1; //xy=118,647 +AudioSynthWaveformSine sine1; //xy=120,607 +AudioInputUSB usb1; //xy=121,566 +AudioAnalyzePeak peak5; //xy=383,256 +AudioAnalyzePeak peak1; //xy=384,122 +AudioAnalyzePeak peak2; //xy=385,155 +AudioAnalyzePeak peak3; //xy=385,189 +AudioAnalyzePeak peak6; //xy=385,290 +AudioAnalyzePeak peak4; //xy=387,222 +AudioAnalyzeRMS rms5; //xy=398,1069 +AudioAnalyzeRMS rms6; //xy=400,1102 +AudioAnalyzeRMS rms3; //xy=401,1003 +AudioAnalyzeRMS rms1; //xy=402,938 +AudioAnalyzeRMS rms2; //xy=402,971 +AudioAnalyzeRMS rms4; //xy=402,1036 +AudioMixer4 mixer1; //xy=567,187 +AudioMixer4 mixer4; //xy=568,325 +AudioMixer4 mixer2; //xy=569,251 +AudioMixer4 mixer5; //xy=570,389 +AudioMixer4 mixer7; //xy=571,463 +AudioMixer4 mixer8; //xy=573,527 +AudioMixer4 mixer10; //xy=575,596 +AudioMixer4 mixer11; //xy=577,660 +AudioMixer4 mixer13; //xy=579,736 +AudioMixer4 mixer14; //xy=581,800 +AudioMixer4 mixer16; //xy=583,869 +AudioMixer4 mixer17; //xy=585,933 +AudioMixer4 mixer3; //xy=729,218 +AudioMixer4 mixer6; //xy=731,359 +AudioMixer4 mixer9; //xy=731,496 +AudioMixer4 mixer12; //xy=735,626 +AudioMixer4 mixer15; //xy=736,771 +AudioMixer4 mixer18; //xy=737,898 +AudioOutputUSB usb2; //xy=1111,820 +AudioAnalyzeRMS rms7; //xy=1115,917 +AudioAnalyzeRMS rms8; //xy=1116,949 +AudioOutputI2SQuad i2s_quad1; //xy=1118,537 +AudioAnalyzeRMS rms11; //xy=1116,1044 +AudioAnalyzeRMS rms12; //xy=1116,1077 +AudioAnalyzeRMS rms9; //xy=1117,981 +AudioAnalyzeRMS rms10; //xy=1118,1013 +AudioAnalyzePeak peak7; //xy=1122,93 +AudioAnalyzePeak peak9; //xy=1122,158 +AudioAnalyzePeak peak10; //xy=1122,189 +AudioAnalyzePeak peak8; //xy=1123,126 +AudioAnalyzePeak peak11; //xy=1123,220 +AudioAnalyzePeak peak12; //xy=1123,251 AudioConnection patchCord1(tdm1, 0, peak1, 0); AudioConnection patchCord2(tdm1, 0, rms1, 0); AudioConnection patchCord3(tdm1, 0, mixer1, 0); @@ -123,16 +123,16 @@ AudioConnection patchCord71(mixer16, 0, mixer18, 0); AudioConnection patchCord72(mixer17, 0, mixer18, 1); AudioConnection patchCord73(mixer3, rms7); AudioConnection patchCord74(mixer3, peak7); -AudioConnection patchCord75(mixer3, 0, tdm2, 12); +AudioConnection patchCord75(mixer3, 0, i2s_quad1, 0); AudioConnection patchCord76(mixer6, rms8); AudioConnection patchCord77(mixer6, peak8); -AudioConnection patchCord78(mixer6, 0, tdm2, 14); +AudioConnection patchCord78(mixer6, 0, i2s_quad1, 1); AudioConnection patchCord79(mixer9, rms9); AudioConnection patchCord80(mixer9, peak9); -AudioConnection patchCord81(mixer9, 0, tdm2, 8); +AudioConnection patchCord81(mixer9, 0, i2s_quad1, 2); AudioConnection patchCord82(mixer12, rms10); AudioConnection patchCord83(mixer12, peak10); -AudioConnection patchCord84(mixer12, 0, tdm2, 10); +AudioConnection patchCord84(mixer12, 0, i2s_quad1, 3); AudioConnection patchCord85(mixer15, 0, usb2, 0); AudioConnection patchCord86(mixer15, rms11); AudioConnection patchCord87(mixer15, peak11); From 33c5ec852816db2247e0b7c9b9e9ab7ce6f56c4c Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Fri, 30 Jan 2026 16:33:15 +0000 Subject: [PATCH 09/67] Check result of slip.read() --- hardware/firmware/audio_board/src/main.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hardware/firmware/audio_board/src/main.cpp b/hardware/firmware/audio_board/src/main.cpp index 9515b89c..dc852f18 100644 --- a/hardware/firmware/audio_board/src/main.cpp +++ b/hardware/firmware/audio_board/src/main.cpp @@ -362,8 +362,13 @@ void loop() { if (slip.available()) { while (!slip.endofPacket()) { if ((size = slip.available()) > 0) { - while (size--) - msg.fill(slip.read()); + while (size) { + int c = slip.read(); + if (c >= 0 ) { + msg.fill(c); + size--; + } + } } } if (!msg.hasError()) { From 60383a0cc8f027524faaf82f8f2abdc40060945c Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 1 Feb 2026 09:21:36 +0100 Subject: [PATCH 10/67] lower gain --- hardware/firmware/audio_board/src/teensyaudio.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index 152d2a74..f2f8cd7c 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -43,9 +43,9 @@ void audio_setup() { Wire1.begin(); taa3040.enable(); - taa3040.gain(0, 36, IMPEDANCE_2k5, 0, 0); - taa3040.gain(1, 36, IMPEDANCE_2k5, 0, 0); - taa3040.gain(2, 36, IMPEDANCE_2k5, 0,0); + taa3040.gain(0, 6, IMPEDANCE_2k5, 0, 0); + taa3040.gain(1, 6, IMPEDANCE_2k5, 0, 0); + taa3040.gain(2, 6, IMPEDANCE_2k5, 0,0); taa3040.gain(3, 6, IMPEDANCE_10k, 0, 0); } From d90c61c15c35187b9f31eeda566e379f14f632e5 Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Fri, 20 Feb 2026 14:29:16 +0100 Subject: [PATCH 11/67] Use ADC gain for the input multiplier --- .../firmware/audio_board/src/teensyaudio.cpp | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index f2f8cd7c..93899cbe 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -19,17 +19,17 @@ AudioControlTAA3040 taa3040; AudioMixer4 *matrix[6][2] = { - {&mixer1, &mixer2}, {&mixer4, &mixer5}, {&mixer7, &mixer8}, + {&mixer1, &mixer2}, {&mixer4, &mixer5}, {&mixer7, &mixer8}, {&mixer10, &mixer11}, {&mixer13, &mixer14}, {&mixer16, &mixer17}, }; AudioAnalyzeRMS *ent_rms[12] = { - &rms1, &rms2, &rms3, &rms4, &rms5, &rms6, + &rms1, &rms2, &rms3, &rms4, &rms5, &rms6, &rms7, &rms8, &rms9, &rms10, &rms11, &rms12, }; AudioAnalyzePeak *ent_peak[12] = { - &peak1, &peak2, &peak3, &peak4, &peak5, &peak6, + &peak1, &peak2, &peak3, &peak4, &peak5, &peak6, &peak7, &peak8, &peak9, &peak10, &peak11, &peak12, }; @@ -45,7 +45,7 @@ void audio_setup() { taa3040.enable(); taa3040.gain(0, 6, IMPEDANCE_2k5, 0, 0); taa3040.gain(1, 6, IMPEDANCE_2k5, 0, 0); - taa3040.gain(2, 6, IMPEDANCE_2k5, 0,0); + taa3040.gain(2, 6, IMPEDANCE_2k5, 0, 0); taa3040.gain(3, 6, IMPEDANCE_10k, 0, 0); } @@ -100,8 +100,7 @@ bool is_muted(int channel, int bus) { } float calc_real_gain(int channel, int bus, int gain) { - return gain * !is_muted(channel, bus) * state.bus_multipliers[bus] * - state.channel_multipliers[channel]; + return gain * !is_muted(channel, bus) * state.bus_multipliers[bus]; } void set_gain(int channel, int bus, int gain) { @@ -130,6 +129,13 @@ void set_bus_multiplier(int bus, float multiplier) { float get_bus_multiplier(int bus) { return state.bus_multipliers[bus]; } void set_channel_multiplier(int channel, float multiplier) { + if (channel < 4) { + // Channel config for mic inputs + taa3040.gain(channel, (uint8_t)multiplier, IMPEDANCE_2k5, 0, 0); + } else { + // Channel config for line inputs + taa3040.gain(channel, (uint8_t)multiplier, IMPEDANCE_10k, 0, 0); + } state.channel_multipliers[channel] = multiplier; for (int bus = 0; bus < BUSES; ++bus) set_gain(channel, bus, state.gains[channel][bus]); @@ -149,6 +155,7 @@ void reset_bus_multipliers() { memcpy(state.bus_multipliers, default_bus_multipliers, BUSES * sizeof(float)); } + void reset_channel_multipliers() { memcpy(state.channel_multipliers, default_channel_multipliers, CHANNELS * sizeof(float)); @@ -215,4 +222,4 @@ void audio_load_state() { audio_reset_default_state(); #endif apply_all(); -} +} \ No newline at end of file From 144ba7ec6b311c749a05c3bdb70d04ac34ed071d Mon Sep 17 00:00:00 2001 From: dexterlb Date: Fri, 20 Mar 2026 19:13:16 +0100 Subject: [PATCH 12/67] add initial version of cli --- hardware/firmware/audio_board/CMakeLists.txt | 3 +- hardware/firmware/audio_board/src/cli/cli.cpp | 96 +++++++++++++++++++ hardware/firmware/audio_board/src/cli/cli.h | 24 +++++ hardware/firmware/audio_board/src/main.cpp | 31 ++---- hardware/firmware/audio_board/src/taa3040.cpp | 72 +++++++------- 5 files changed, 164 insertions(+), 62 deletions(-) create mode 100644 hardware/firmware/audio_board/src/cli/cli.cpp create mode 100644 hardware/firmware/audio_board/src/cli/cli.h diff --git a/hardware/firmware/audio_board/CMakeLists.txt b/hardware/firmware/audio_board/CMakeLists.txt index a1c36d4a..03c368ec 100644 --- a/hardware/firmware/audio_board/CMakeLists.txt +++ b/hardware/firmware/audio_board/CMakeLists.txt @@ -107,7 +107,7 @@ set(FEATURES USE_DISPLAY ) -set(SOURCES +set(SOURCES teensy4_usb_descriptor.c src/main.cpp src/storage.cpp @@ -115,6 +115,7 @@ set(SOURCES src/helpers.cpp src/teensyaudio.cpp src/taa3040.cpp + src/cli/cli.cpp ) # Build this project diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp new file mode 100644 index 00000000..d1aa83f0 --- /dev/null +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -0,0 +1,96 @@ +#include "cli.h" + +Cli::Cli(Stream* _port) : port(_port) {} + +void Cli::exec_cmd() { + if (this->hop_word("ping")) { + this->port->printf("pong %s\n", this->cmd); + } +} + +bool Cli::is_terminator(char c) { + return c == '\n' || c == '\r' || c == '\0'; +} + +bool Cli::is_terminator_or_whitespace(char c) { + return is_terminator(c) || c == ' ' || c == '\t'; +} + +void Cli::skip_whitespace_in(char** buf) { + while (**buf == ' ') { + (*buf)++; + } +} + +void Cli::skip_whitespace() { + skip_whitespace_in(&this->cmd); +} + +uint16_t Cli::parse_number() { + skip_whitespace(); + uint16_t result = 0; + while (*this->cmd >= '0' && *this->cmd <= '9') { + result *= 10; + result += (*this->cmd) - '0'; + this->cmd++; + } + + if (*this->cmd == ' ') { + this->cmd++; + } + return result; +} + +bool Cli::hop_word(const char* word) { + char* buf = this->cmd; + skip_whitespace_in(&buf); + + while (*word != 0 && !is_terminator_or_whitespace(*buf) && *buf == *word) { + buf++; + word++; + } + + + if (*word != '\0' || !is_terminator_or_whitespace(*buf)) { + return false; + } + + skip_whitespace_in(&buf); + + this->cmd = buf; + return true; +} + +void Cli::eat(char chr) { + if (this->input_pos > sizeof(this->input_buf) - 2) { + if (is_terminator(chr)) { + this->input_pos = 0; + this->input_buf[0] = '\0'; + this->port->write("fail line too long\n"); + } + return; + } + this->input_buf[this->input_pos] = chr; + if (!is_terminator(chr)) { + this->input_pos++; + return; + } + this->input_buf[this->input_pos] = '\0'; + this->input_pos = 0; + if (this->input_buf[0] == '\0') { + return; // empty line + } + + this->cmd = &this->input_buf[0]; + + exec_cmd(); +} + +void Cli::update() { + while (this->port->available()) { + int c = this->port->read(); + if (c > 0) { + this->eat((char)c); + } + } +} diff --git a/hardware/firmware/audio_board/src/cli/cli.h b/hardware/firmware/audio_board/src/cli/cli.h new file mode 100644 index 00000000..2dc91f6f --- /dev/null +++ b/hardware/firmware/audio_board/src/cli/cli.h @@ -0,0 +1,24 @@ +#include + +class Cli { + public: + Cli(Stream*); + void update(); + private: + Stream* port; + + char input_buf[100]; + uint8_t input_pos = 0; + + void exec_cmd(); + bool is_terminator(char c); + bool is_terminator_or_whitespace(char c); + void skip_whitespace_in(char** buf); + void skip_whitespace(); + uint16_t parse_number(); + bool hop_word(const char* word); + void eat(char chr); + + // state used only during exec_cmd(): + char* cmd; +}; diff --git a/hardware/firmware/audio_board/src/main.cpp b/hardware/firmware/audio_board/src/main.cpp index bea5ed63..99d5a33d 100644 --- a/hardware/firmware/audio_board/src/main.cpp +++ b/hardware/firmware/audio_board/src/main.cpp @@ -1,12 +1,12 @@ #include #include -#include #include "config.h" #include "helpers.h" #include #include "teensyaudio.h" +#include "cli/cli.h" #ifdef USE_EEPROM @@ -30,17 +30,9 @@ ChanInfo channel_info[] = { {CHAN_MAGENTA, "USB", "USB1", 1}, {CHAN_MAGENTA, "USB", "USB2", 2}, }; -SLIPEncodedUSBSerial slip(Serial); - void send(OSCMessage &msg) { - slip.beginPacket(); - msg.send(slip); - slip.endPacket(); } void send(OSCBundle &msg) { - slip.beginPacket(); - msg.send(slip); - slip.endPacket(); } void onOscChannel(OSCMessage &msg, int patternOffset) { @@ -349,30 +341,17 @@ void setup() { audio_setup(); - slip.begin(115200); + SerialUSB.begin(115200); SerialUSB1.println("board ready"); } +Cli the_cli(&SerialUSB); + unsigned long last_draw = 0; unsigned long last_save = 0; void loop() { int size; - OSCMessage msg; - if (slip.available()) { - while (!slip.endofPacket() && slip.available()) { - while (1) { - int c = slip.read(); - if (c >= 0 ) { - msg.fill(c); - break; - } - } - } - if (!msg.hasError()) { - onPacketReceived(msg); - } - } Levels &levels = audio_get_levels(); @@ -387,6 +366,8 @@ void loop() { } #endif + the_cli.update(); + #ifdef USE_EEPROM // save to EEPROM every 60 seconds if (last_save + 60000 < millis()) { diff --git a/hardware/firmware/audio_board/src/taa3040.cpp b/hardware/firmware/audio_board/src/taa3040.cpp index ed44a06a..d5fca31e 100644 --- a/hardware/firmware/audio_board/src/taa3040.cpp +++ b/hardware/firmware/audio_board/src/taa3040.cpp @@ -37,82 +37,82 @@ void AudioControlTAA3040::getAsiStatus() { last_asi = raw; uint8_t ratio = raw & 0x0F; uint8_t rate = raw >> 4; - Serial.print("ASI Status: "); + SerialUSB1.print("ASI Status: "); switch(ratio) { case 0: - Serial.print("ratio 16, "); + SerialUSB1.print("ratio 16, "); break; case 1: - Serial.print("ratio 24, "); + SerialUSB1.print("ratio 24, "); break; case 2: - Serial.print("ratio 32, "); + SerialUSB1.print("ratio 32, "); break; case 3: - Serial.print("ratio 48, "); + SerialUSB1.print("ratio 48, "); break; case 4: - Serial.print("ratio 64, "); + SerialUSB1.print("ratio 64, "); break; case 5: - Serial.print("ratio 96, "); + SerialUSB1.print("ratio 96, "); break; case 6: - Serial.print("ratio 128, "); + SerialUSB1.print("ratio 128, "); break; case 7: - Serial.print("ratio 192, "); + SerialUSB1.print("ratio 192, "); break; case 8: - Serial.print("ratio 256, "); + SerialUSB1.print("ratio 256, "); break; case 9: - Serial.print("ratio 384, "); + SerialUSB1.print("ratio 384, "); break; case 10: - Serial.print("ratio 512, "); + SerialUSB1.print("ratio 512, "); break; case 11: - Serial.print("ratio 1024, "); + SerialUSB1.print("ratio 1024, "); break; case 12: - Serial.print("ratio 2048, "); + SerialUSB1.print("ratio 2048, "); break; case 13: case 14: - Serial.printf("ratio RESERVED(%d), ", ratio); + SerialUSB1.printf("ratio RESERVED(%d), ", ratio); break; case 15: - Serial.print("ratio INVALID, "); + SerialUSB1.print("ratio INVALID, "); break; } switch(rate) { case 0: - Serial.println("rate 7.35-8Khz"); + SerialUSB1.println("rate 7.35-8Khz"); break; case 1: - Serial.println("rate 14.7-16Khz"); + SerialUSB1.println("rate 14.7-16Khz"); break; case 2: - Serial.println("rate 22.05-24Khz"); + SerialUSB1.println("rate 22.05-24Khz"); break; case 3: - Serial.println("rate 29.4-32Khz"); + SerialUSB1.println("rate 29.4-32Khz"); break; case 4: - Serial.println("rate 44.1-48Khz"); + SerialUSB1.println("rate 44.1-48Khz"); break; case 5: - Serial.println("rate 88.2-96Khz"); + SerialUSB1.println("rate 88.2-96Khz"); break; case 6: - Serial.println("rate 176.4-192Khz"); + SerialUSB1.println("rate 176.4-192Khz"); break; case 7: - Serial.println("rate 352.8-384Khz"); + SerialUSB1.println("rate 352.8-384Khz"); break; case 8: - Serial.println("rate 705.6-768Khz"); + SerialUSB1.println("rate 705.6-768Khz"); break; case 9: case 10: @@ -120,10 +120,10 @@ void AudioControlTAA3040::getAsiStatus() { case 12: case 13: case 14: - Serial.printf("rate RESERVED(%d)\n", rate); + SerialUSB1.printf("rate RESERVED(%d)\n", rate); break; case 15: - Serial.println("rate INVALID"); + SerialUSB1.println("rate INVALID"); break; } @@ -134,17 +134,17 @@ void AudioControlTAA3040::setRegister(uint8_t reg, uint8_t value) { Wire1.write(reg); Wire1.write(value); if (Wire1.endTransmission() != 0) { - Serial.println("I2C error"); + SerialUSB1.println("I2C error"); } } void AudioControlTAA3040::setRegister(uint8_t page, uint8_t reg, uint8_t value) { - Serial.print("REG "); - Serial.print(page, HEX); - Serial.print(":"); - Serial.print(reg, HEX); - Serial.print(" = "); - Serial.println(value, HEX); + SerialUSB1.print("REG "); + SerialUSB1.print(page, HEX); + SerialUSB1.print(":"); + SerialUSB1.print(reg, HEX); + SerialUSB1.print(" = "); + SerialUSB1.println(value, HEX); if (page != currentPage) { setRegister(0, page); @@ -164,7 +164,7 @@ uint8_t AudioControlTAA3040::getRegister(uint8_t page, uint8_t reg) { Wire1.requestFrom(0x4e, 1, true); const uint8_t res = Wire.read(); if (Wire1.endTransmission() != 0) { - Serial.println("I2C error"); + SerialUSB1.println("I2C error"); } return res; } @@ -177,4 +177,4 @@ void AudioControlTAA3040::configASI0(uint8_t format, uint8_t wlen, uint8_t fsync val |= (wlen & 3) << 4; val |= (format & 3) << 6; setRegister(0, REG_P0_ASI_CFG0, val); -} \ No newline at end of file +} From 90f60387d6537b53becda584758d73a733d5b6ce Mon Sep 17 00:00:00 2001 From: dexterlb Date: Fri, 20 Mar 2026 21:15:38 +0100 Subject: [PATCH 13/67] return levels --- hardware/firmware/audio_board/src/cli/cli.cpp | 56 +++++++++++++++++++ hardware/firmware/audio_board/src/cli/cli.h | 2 + 2 files changed, 58 insertions(+) diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp index d1aa83f0..90ab7f3f 100644 --- a/hardware/firmware/audio_board/src/cli/cli.cpp +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -1,11 +1,67 @@ #include "cli.h" +#include "../config.h" +#include "../teensyaudio.h" +#include "../helpers.h" + Cli::Cli(Stream* _port) : port(_port) {} void Cli::exec_cmd() { if (this->hop_word("ping")) { this->port->printf("pong %s\n", this->cmd); + return; + } + if (this->hop_word("levels")) { + Levels &levels = audio_get_levels(); + this->port->print("ok"); + for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { + this->port->print(" "); + this->print_float_fixed(rmsToDb(levels.rms[i]), 3, 5); + this->port->print(" "); + this->print_float_fixed(rmsToDb(levels.peak[i]), 3, 5); + this->port->print(" "); + this->print_float_fixed(rmsToDb(levels.smooth[i]), 3, 5); + } + this->port->println(); + } +} + +void Cli::print_float_fixed(float x, uint8_t whole_digits, uint8_t frac_digits) { + char buf[whole_digits + frac_digits + 3]; + + bool sign = x < 0; + if (sign) { + x = -x; + } + + buf[0] = sign ? '-' : '+'; + + if (x >= 10 * whole_digits) { + buf[1] = 'i'; + buf[2] = 'n'; + buf[3] = 'f'; + buf[4] = '\0'; + } else { + uint32_t whole = (uint32_t)x; + float frac = x - whole; + + for (uint8_t i = whole_digits; i >= 1; i--) { + buf[i] = '0' + (whole % 10); + whole /= 10; + } + + buf[whole_digits + 1] = '.'; + + for (uint8_t i = 0; i < frac_digits; i++) { + frac *= 10; + buf[whole_digits + 2 + i] = '0' + (uint32_t)frac; + frac -= (uint32_t)frac; + } + + buf[frac_digits + whole_digits + 2] = '\0'; } + + this->port->print(buf); } bool Cli::is_terminator(char c) { diff --git a/hardware/firmware/audio_board/src/cli/cli.h b/hardware/firmware/audio_board/src/cli/cli.h index 2dc91f6f..c80d41e4 100644 --- a/hardware/firmware/audio_board/src/cli/cli.h +++ b/hardware/firmware/audio_board/src/cli/cli.h @@ -19,6 +19,8 @@ class Cli { bool hop_word(const char* word); void eat(char chr); + void print_float_fixed(float x, uint8_t whole_digits, uint8_t frac_digits); + // state used only during exec_cmd(): char* cmd; }; From 556754c52a366647fd13fd4326f0c348521868b0 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Fri, 20 Mar 2026 21:52:11 +0100 Subject: [PATCH 14/67] gains and outs --- hardware/firmware/audio_board/src/cli/cli.cpp | 62 ++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp index 90ab7f3f..6f0bb404 100644 --- a/hardware/firmware/audio_board/src/cli/cli.cpp +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -11,7 +11,7 @@ void Cli::exec_cmd() { this->port->printf("pong %s\n", this->cmd); return; } - if (this->hop_word("levels")) { + if (this->hop_word("levels.db")) { Levels &levels = audio_get_levels(); this->port->print("ok"); for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { @@ -23,6 +23,59 @@ void Cli::exec_cmd() { this->print_float_fixed(rmsToDb(levels.smooth[i]), 3, 5); } this->port->println(); + return; + } + if (this->hop_word("levels")) { + Levels &levels = audio_get_levels(); + this->port->print("ok"); + for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { + this->port->print(" "); + this->print_float_fixed(levels.rms[i], 3, 5); + this->port->print(" "); + this->print_float_fixed(levels.peak[i], 3, 5); + this->port->print(" "); + this->print_float_fixed(levels.smooth[i], 3, 5); + } + this->port->println(); + return; + } + if (this->hop_word("matrix")) { + this->port->print("ok"); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + for (uint8_t bus = 0; bus < BUSES; bus++) { + if (is_muted(chan, bus)) { + this->port->print(" 1*"); + } else { + this->port->print(" 0*"); + } + this->print_float_fixed(get_gain(chan, bus), 3, 3); + } + } + this->port->println(); + return; + } + if (this->hop_word("gains")) { + this->port->print("ok"); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + this->port->print(" "); + this->print_float_fixed(get_channel_multiplier(chan), 3, 3); + } + this->port->println(); + return; + } + if (this->hop_word("outs")) { + this->port->print("ok"); + for (uint8_t bus = 0; bus < BUSES; bus++) { + this->port->print(" "); + this->print_float_fixed(get_bus_multiplier(bus), 3, 3); + } + this->port->println(); + return; + } + if (this->hop_word("factory-reset")) { + audio_reset_default_state(); + audio_eeprom_save_all(); + this->port->println("ok"); } } @@ -61,7 +114,12 @@ void Cli::print_float_fixed(float x, uint8_t whole_digits, uint8_t frac_digits) buf[frac_digits + whole_digits + 2] = '\0'; } - this->port->print(buf); + // FIXME: give this function to a year 1 uni student to make prettier + if (sign) { + this->port->print(buf); + } else { + this->port->print(buf + 1); + } } bool Cli::is_terminator(char c) { From 22beeab774a0301753bd738f68d4d9371aeb5dd7 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Fri, 20 Mar 2026 23:17:13 +0100 Subject: [PATCH 15/67] floats --- hardware/firmware/audio_board/src/cli/cli.cpp | 123 +++++++++++++++++- hardware/firmware/audio_board/src/cli/cli.h | 3 +- .../firmware/audio_board/src/teensyaudio.cpp | 6 +- .../firmware/audio_board/src/teensyaudio.h | 2 +- 4 files changed, 125 insertions(+), 9 deletions(-) diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp index 6f0bb404..6657edf6 100644 --- a/hardware/firmware/audio_board/src/cli/cli.cpp +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -44,9 +44,9 @@ void Cli::exec_cmd() { for (uint8_t chan = 0; chan < CHANNELS; chan++) { for (uint8_t bus = 0; bus < BUSES; bus++) { if (is_muted(chan, bus)) { - this->port->print(" 1*"); - } else { this->port->print(" 0*"); + } else { + this->port->print(" 1*"); } this->print_float_fixed(get_gain(chan, bus), 3, 3); } @@ -63,7 +63,7 @@ void Cli::exec_cmd() { this->port->println(); return; } - if (this->hop_word("outs")) { + if (this->hop_word("bus-levels")) { this->port->print("ok"); for (uint8_t bus = 0; bus < BUSES; bus++) { this->port->print(" "); @@ -72,10 +72,90 @@ void Cli::exec_cmd() { this->port->println(); return; } + if (this->hop_word("set-send")) { + uint16_t chan = parse_uint(); + uint16_t bus = parse_uint(); + uint16_t want_send = parse_uint(); + if (chan >= CHANNELS) { + this->port->printf("fail (chan %d is invalid)\n", chan); + return; + } + if (bus >= BUSES) { + this->port->printf("fail (bus %d is invalid)\n", bus); + return; + } + if (want_send > 0) { + unmute(chan, bus); + } else { + mute(chan, bus); + } + this->port->println("ok"); + return; + } + if (this->hop_word("set-fader")) { + uint16_t chan = parse_uint(); + uint16_t bus = parse_uint(); + float vol = parse_float(); + + if (chan >= CHANNELS) { + this->port->printf("fail (chan %d is invalid)\n", chan); + return; + } + if (bus >= BUSES) { + this->port->printf("fail (bus %d is invalid)\n", bus); + return; + } + if (vol < 0) { + this->port->printf("fail (vol should not be negative)\n"); + return; + } + + set_gain(chan, bus, vol); + + this->port->print("ok"); + return; + } + if (this->hop_word("set-gain")) { + uint16_t chan = parse_uint(); + float vol = parse_float(); + + if (chan >= CHANNELS) { + this->port->printf("fail (chan %d is invalid)\n", chan); + return; + } + if (vol < 0) { + this->port->printf("fail (vol should not be negative)\n"); + return; + } + + set_channel_multiplier(chan, vol); + + this->port->print("ok"); + return; + } + if (this->hop_word("set-bus-level")) { + uint16_t bus = parse_uint(); + float vol = parse_float(); + + if (bus >= BUSES) { + this->port->printf("fail (bus %d is invalid)\n", bus); + return; + } + if (vol < 0) { + this->port->printf("fail (vol should not be negative)\n"); + return; + } + + set_bus_multiplier(bus, vol); + + this->port->print("ok"); + return; + } if (this->hop_word("factory-reset")) { audio_reset_default_state(); audio_eeprom_save_all(); this->port->println("ok"); + return; } } @@ -140,7 +220,7 @@ void Cli::skip_whitespace() { skip_whitespace_in(&this->cmd); } -uint16_t Cli::parse_number() { +uint16_t Cli::parse_uint() { skip_whitespace(); uint16_t result = 0; while (*this->cmd >= '0' && *this->cmd <= '9') { @@ -155,6 +235,41 @@ uint16_t Cli::parse_number() { return result; } +float Cli::parse_float() { + skip_whitespace(); + + float sign = 1.0f; + if (*this->cmd == '-') { + sign = -1.0f; + this->cmd++; + } else if (*this->cmd == '+') { + this->cmd++; + } + + float result = 0.0f; + while (*this->cmd >= '0' && *this->cmd <= '9') { + result *= 10.0f; + result += (*this->cmd) - '0'; + this->cmd++; + } + + if (*this->cmd == '.') { + this->cmd++; + float place = 0.1f; + while (*this->cmd >= '0' && *this->cmd <= '9') { + result += ((*this->cmd) - '0') * place; + place *= 0.1f; + this->cmd++; + } + } + + if (*this->cmd == ' ') { + this->cmd++; + } + + return sign * result; +} + bool Cli::hop_word(const char* word) { char* buf = this->cmd; skip_whitespace_in(&buf); diff --git a/hardware/firmware/audio_board/src/cli/cli.h b/hardware/firmware/audio_board/src/cli/cli.h index c80d41e4..f88b0f3f 100644 --- a/hardware/firmware/audio_board/src/cli/cli.h +++ b/hardware/firmware/audio_board/src/cli/cli.h @@ -15,7 +15,8 @@ class Cli { bool is_terminator_or_whitespace(char c); void skip_whitespace_in(char** buf); void skip_whitespace(); - uint16_t parse_number(); + uint16_t parse_uint(); + float parse_float(); bool hop_word(const char* word); void eat(char chr); diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index 93899cbe..ba44e5ab 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -99,11 +99,11 @@ bool is_muted(int channel, int bus) { return !!(state.mutes & mute_mask(channel, bus)); } -float calc_real_gain(int channel, int bus, int gain) { +float calc_real_gain(int channel, int bus, float gain) { return gain * !is_muted(channel, bus) * state.bus_multipliers[bus]; } -void set_gain(int channel, int bus, int gain) { +void set_gain(int channel, int bus, float gain) { state.gains[channel][bus] = gain; raw_set_crosspoint(channel, bus, calc_real_gain(channel, bus, gain)); } @@ -222,4 +222,4 @@ void audio_load_state() { audio_reset_default_state(); #endif apply_all(); -} \ No newline at end of file +} diff --git a/hardware/firmware/audio_board/src/teensyaudio.h b/hardware/firmware/audio_board/src/teensyaudio.h index 5e3b85ae..d3be6be8 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.h +++ b/hardware/firmware/audio_board/src/teensyaudio.h @@ -18,7 +18,7 @@ bool is_muted(int channel, int bus); void mute(int channel, int bus); void unmute(int channel, int bus); -void set_gain(int channel, int bus, int gain); +void set_gain(int channel, int bus, float gain); float get_gain(int channel, int bus); float get_bus_multiplier(int bus); From be65d67781e44cf07c910d036f4d0e0d9c6062e1 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Fri, 20 Mar 2026 23:45:31 +0100 Subject: [PATCH 16/67] properly apply gains when reading from eeprom --- .../firmware/audio_board/src/teensyaudio.cpp | 63 +++++++++---------- .../firmware/audio_board/src/teensyaudio.h | 24 +++---- 2 files changed, 42 insertions(+), 45 deletions(-) diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index ba44e5ab..d90a2c93 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -43,16 +43,12 @@ void audio_setup() { Wire1.begin(); taa3040.enable(); - taa3040.gain(0, 6, IMPEDANCE_2k5, 0, 0); - taa3040.gain(1, 6, IMPEDANCE_2k5, 0, 0); - taa3040.gain(2, 6, IMPEDANCE_2k5, 0, 0); - taa3040.gain(3, 6, IMPEDANCE_10k, 0, 0); } void audio_update_levels(Levels &levels) { float temp; if (rms1.available()) { - for (int i = 0; i < CHANNELS + BUSES; i++) { + for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { temp = ent_rms[i]->read(); levels.smooth[i] = ((levels.smooth[i] * 9) + temp) / 10; temp = levels.smooth[i]; @@ -71,15 +67,15 @@ void audio_update_levels(Levels &levels) { Levels &audio_get_levels() { return levels; } -void raw_set_crosspoint(int channel, int bus, float gain) { +void raw_set_crosspoint(uint8_t channel, uint8_t bus, float gain) { matrix[bus][channel / 4]->gain(channel % 4, gain); } -float raw_get_crosspoint(int channel, int bus) { +float raw_get_crosspoint(uint8_t channel, uint8_t bus) { return matrix[bus][channel / 4]->getGain(channel % 4); } -void raw_set_mix(int bus, float in1, float in2, float in3, float in4, float in5, +void raw_set_mix(uint8_t bus, float in1, float in2, float in3, float in4, float in5, float in6) { matrix[bus][0]->gain(0, in1); matrix[bus][0]->gain(1, in2); @@ -95,53 +91,48 @@ uint64_t mute_mask(uint64_t channel, uint64_t bus) { return (uint64_t)1 << (uint64_t)((channel * CHANNELS) + bus); } -bool is_muted(int channel, int bus) { +bool is_muted(uint8_t channel, uint8_t bus) { return !!(state.mutes & mute_mask(channel, bus)); } -float calc_real_gain(int channel, int bus, float gain) { +float calc_real_gain(uint8_t channel, uint8_t bus, float gain) { return gain * !is_muted(channel, bus) * state.bus_multipliers[bus]; } -void set_gain(int channel, int bus, float gain) { +void set_gain(uint8_t channel, uint8_t bus, float gain) { state.gains[channel][bus] = gain; raw_set_crosspoint(channel, bus, calc_real_gain(channel, bus, gain)); } -float get_gain(int channel, int bus) { return state.gains[channel][bus]; } +float get_gain(uint8_t channel, uint8_t bus) { return state.gains[channel][bus]; } -void mute(int channel, int bus) { +void mute(uint8_t channel, uint8_t bus) { state.mutes |= mute_mask(channel, bus); set_gain(channel, bus, state.gains[channel][bus]); } -void unmute(int channel, int bus) { +void unmute(uint8_t channel, uint8_t bus) { state.mutes &= ~mute_mask(channel, bus); set_gain(channel, bus, state.gains[channel][bus]); } -void set_bus_multiplier(int bus, float multiplier) { +void set_bus_multiplier(uint8_t bus, float multiplier) { state.bus_multipliers[bus] = multiplier; - for (int channel = 0; channel < CHANNELS; ++channel) + for (uint8_t channel = 0; channel < CHANNELS; ++channel) { set_gain(channel, bus, state.gains[channel][bus]); + } } -float get_bus_multiplier(int bus) { return state.bus_multipliers[bus]; } +float get_bus_multiplier(uint8_t bus) { return state.bus_multipliers[bus]; } -void set_channel_multiplier(int channel, float multiplier) { - if (channel < 4) { - // Channel config for mic inputs - taa3040.gain(channel, (uint8_t)multiplier, IMPEDANCE_2k5, 0, 0); - } else { - // Channel config for line inputs - taa3040.gain(channel, (uint8_t)multiplier, IMPEDANCE_10k, 0, 0); - } +void set_channel_multiplier(uint8_t channel, float multiplier) { + taa3040.gain(channel, (uint8_t)multiplier, IMPEDANCE_10k, 0, 0); state.channel_multipliers[channel] = multiplier; - for (int bus = 0; bus < BUSES; ++bus) + for (uint8_t bus = 0; bus < BUSES; ++bus) set_gain(channel, bus, state.gains[channel][bus]); } -float get_channel_multiplier(int channel) { +float get_channel_multiplier(uint8_t channel) { return state.channel_multipliers[channel]; } @@ -162,10 +153,16 @@ void reset_channel_multipliers() { } void apply_all() { - int i, j; - for (i = 0; i < CHANNELS; ++i) - for (j = 0; j < BUSES; ++j) + uint8_t i, j; + for (i = 0; i < CHANNELS; ++i) { + set_channel_multiplier(i, state.channel_multipliers[i]); + for (j = 0; j < BUSES; ++j) { set_gain(i, j, state.gains[i][j]); + } + } + for (j = 0; j < BUSES; ++j) { + set_bus_multiplier(j, state.bus_multipliers[j]); + } } void audio_reset_default_state() { @@ -176,7 +173,7 @@ void audio_reset_default_state() { } bool gains_ok() { - int i, j; + uint8_t i, j; for (i = 0; i < CHANNELS; ++i) for (j = 0; j < BUSES; ++j) if (isnan(state.gains[i][j])) @@ -186,7 +183,7 @@ bool gains_ok() { } bool bus_multipliers_ok() { - int i; + uint8_t i; for (i = 0; i < BUSES; ++i) if (isnan(state.bus_multipliers[i])) return false; @@ -195,7 +192,7 @@ bool bus_multipliers_ok() { } bool channel_multipliers_ok() { - int i; + uint8_t i; for (i = 0; i < CHANNELS; ++i) if (isnan(state.channel_multipliers[i])) return false; diff --git a/hardware/firmware/audio_board/src/teensyaudio.h b/hardware/firmware/audio_board/src/teensyaudio.h index d3be6be8..06e3396f 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.h +++ b/hardware/firmware/audio_board/src/teensyaudio.h @@ -14,23 +14,23 @@ void update_levels(float levels_smooth[CHANNELS + BUSES], float levels_rms[CHANNELS + BUSES], float levels_peak[CHANNELS + BUSES]); -bool is_muted(int channel, int bus); -void mute(int channel, int bus); -void unmute(int channel, int bus); +bool is_muted(uint8_t channel, uint8_t bus); +void mute(uint8_t channel, uint8_t bus); +void unmute(uint8_t channel, uint8_t bus); -void set_gain(int channel, int bus, float gain); -float get_gain(int channel, int bus); +void set_gain(uint8_t channel, uint8_t bus, float gain); +float get_gain(uint8_t channel, uint8_t bus); -float get_bus_multiplier(int bus); -void set_bus_multiplier(int bus, float multiplier); +float get_bus_multiplier(uint8_t bus); +void set_bus_multiplier(uint8_t bus, float multiplier); -float get_channel_multiplier(int channel); -void set_channel_multiplier(int channel, float multiplier); +float get_channel_multiplier(uint8_t channel); +void set_channel_multiplier(uint8_t channel, float multiplier); -// void raw_set_crosspoint(int channel, int bus, float gain); -float raw_get_crosspoint(int channel, int bus); +// void raw_set_crosspoint(uint8_t channel, uint8_t bus, float gain); +float raw_get_crosspoint(uint8_t channel, uint8_t bus); -// void raw_set_mix(int bus, float channel_gains[CHANNELS]); +// void raw_set_mix(uint8_t bus, float channel_gains[CHANNELS]); void audio_update_levels(Levels &levels); Levels &audio_get_levels(); From 6a18119a99b5439496934d974a79f0149f57b20e Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 00:11:16 +0100 Subject: [PATCH 17/67] rename some stuff and remove osc shit --- hardware/firmware/audio_board/src/cli/cli.cpp | 4 +- hardware/firmware/audio_board/src/main.cpp | 299 ------------------ .../firmware/audio_board/src/teensyaudio.cpp | 78 +++-- .../firmware/audio_board/src/teensyaudio.h | 6 +- hardware/firmware/audio_board/src/types.h | 2 +- 5 files changed, 41 insertions(+), 348 deletions(-) diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp index 6657edf6..6983bf17 100644 --- a/hardware/firmware/audio_board/src/cli/cli.cpp +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -48,7 +48,7 @@ void Cli::exec_cmd() { } else { this->port->print(" 1*"); } - this->print_float_fixed(get_gain(chan, bus), 3, 3); + this->print_float_fixed(get_volume(chan, bus), 3, 3); } } this->port->println(); @@ -110,7 +110,7 @@ void Cli::exec_cmd() { return; } - set_gain(chan, bus, vol); + set_volume(chan, bus, vol); this->port->print("ok"); return; diff --git a/hardware/firmware/audio_board/src/main.cpp b/hardware/firmware/audio_board/src/main.cpp index 99d5a33d..75ba97aa 100644 --- a/hardware/firmware/audio_board/src/main.cpp +++ b/hardware/firmware/audio_board/src/main.cpp @@ -1,6 +1,3 @@ -#include -#include - #include "config.h" #include "helpers.h" #include @@ -30,302 +27,6 @@ ChanInfo channel_info[] = { {CHAN_MAGENTA, "USB", "USB1", 1}, {CHAN_MAGENTA, "USB", "USB2", 2}, }; -void send(OSCMessage &msg) { -} -void send(OSCBundle &msg) { -} - -void onOscChannel(OSCMessage &msg, int patternOffset) { - char buf[12]; - char address[22]; - int channel = -1; - int addr; - int offset; - - // /ch/ - for (int i = 0; i < 6; i++) { - sprintf(buf, "/%d", i); - offset = msg.match(buf, patternOffset); - if (offset) { - channel = i; - addr = offset + patternOffset; - break; - } - } - if (channel < 0) - return; - - if (msg.match("/config/name", addr) > 0) { - if (msg.isString(0)) { - msg.getString(0, channel_info[channel].desc, - sizeof(channel_info[channel].desc)); - send(msg); - } else { - snprintf(address, 22, "/ch/%d/config/name", channel); - OSCMessage response(address); - response.add(channel_info[channel].desc); - send(response); - } - return; - } else if (msg.match("/levels", addr) > 0) { - OSCBundle response; - Levels &levels = audio_get_levels(); - - snprintf(address, 22, "/ch/%d/levels/rms", channel); - response.add(address).add(rmsToDb(levels.rms[channel])); - - snprintf(address, 22, "/ch/%d/levels/peak", channel); - response.add(address).add(rmsToDb(levels.peak[channel])); - - snprintf(address, 22, "/ch/%d/levels/smooth", channel); - response.add(address).add(rmsToDb(levels.smooth[channel])); - - send(response); - return; - } else if (msg.match("/multiplier", addr) > 0) { - if (msg.isFloat(0)) { - set_channel_multiplier(channel, msg.getFloat(0)); - send(msg); - } else { - snprintf(address, 22, "/ch/%d/multiplier", channel); - OSCMessage response(address); - response.add(get_channel_multiplier(channel)); - - send(response); - } - } - - // /ch//mix - offset = msg.match("/mix", addr); - addr += offset; - if (offset < 1) - return; - - // /ch//mix//level - int bus = -1; - for (int i = 0; i < 6; i++) { - sprintf(buf, "/%d/level", i); - int offset = msg.match(buf, addr); - if (offset) { - bus = i; - break; - } - } - if (bus < 0) - goto mutes; - - if (msg.isFloat(0)) { - set_gain(channel, bus, msg.getFloat(0)); - send(msg); - } else { - snprintf(address, 22, "/ch/%d/mix/%d/level", channel, bus); - OSCMessage response(address); - response.add((float)get_gain(channel, bus)); - send(response); - } - - return; - -mutes: - for (int i = 0; i < 6; i++) { - sprintf(buf, "/%d/muted", i); - int offset = msg.match(buf, addr); - if (offset) { - bus = i; - break; - } - } - if (bus < 0) - return; - - if (msg.isBoolean(0)) { - if (msg.getBoolean(0)) - mute(channel, bus); - else - unmute(channel, bus); - send(msg); - } else { - snprintf(address, 22, "/ch/%d/mix/%d/muted", channel, bus); - OSCMessage response(address); - response.add(is_muted(channel, bus)); - send(response); - } -} - -void onOscInfo(OSCMessage &msg) { - char addrbuf[22]; - OSCBundle info; - - int i; - - info.add("/info/buses").add((uint8_t)BUSES); - info.add("/info/channels").add((uint8_t)CHANNELS); - info.add("/info/features").add(FEATURES); - - for (i = 0; i < BUSES; ++i) { - snprintf(addrbuf, 22, "/bus/%d/config/name", i); - info.add(addrbuf).add(channel_info[CHANNELS + i].desc); - } - for (i = 0; i < CHANNELS; ++i) { - snprintf(addrbuf, 22, "/ch/%d/config/name", i); - info.add(addrbuf).add(channel_info[i].desc); - } - - send(info); -} - -void onOscFactoryReset(OSCMessage &msg) { - SerialUSB1.println("factory reset requested"); - audio_reset_default_state(); - audio_eeprom_save_all(); - - send(msg); -} - -void onOscState(OSCMessage &msg, int patternOffset) { - char addrbuf[31]; - OSCBundle info; - - int i, j; - - for (i = 0; i < CHANNELS; ++i) { - for (j = 0; j < BUSES; ++j) { - snprintf(addrbuf, 30, "/ch/%d/mix/%d/level", i, j); - info.add(addrbuf).add(get_gain(i, j)); - snprintf(addrbuf, 30, "/ch/%d/mix/%d/muted", i, j); - info.add(addrbuf).add(!!is_muted(i, j)); - snprintf(addrbuf, 30, "/ch/%d/mix/%d/raw", i, j); - info.add(addrbuf).add(raw_get_crosspoint(i, j)); - } - } - - for (i = 0; i < CHANNELS; ++i) { - snprintf(addrbuf, 22, "/ch/%d/multiplier", i); - info.add(addrbuf).add(get_channel_multiplier(i)); - } - for (j = 0; j < BUSES; ++j) { - snprintf(addrbuf, 22, "/bus/%d/multiplier", j); - info.add(addrbuf).add(get_bus_multiplier(j)); - } - - send(info); -} - -void onOscBus(OSCMessage &msg, int patternOffset) { - char buf[12]; - char address[22]; - int bus = -1; - int addr; - int offset; - - // /bus/ - for (int i = 0; i < 6; i++) { - sprintf(buf, "/%d", i); - offset = msg.match(buf, patternOffset); - if (offset) { - bus = i; - addr = offset + patternOffset; - break; - } - } - if (bus < 0) - return; - - if (msg.match("/config/name", addr) > 0) { - if (msg.isString(0)) { - msg.getString(0, channel_info[CHANNELS + bus].desc, - sizeof(channel_info[CHANNELS + bus].desc)); - send(msg); - } else { - snprintf(address, 22, "/bus/%d/config/name", bus); - OSCMessage response(address); - response.add(channel_info[CHANNELS + bus].desc); - send(response); - } - return; - } else if (msg.match("/levels", addr) > 0) { - OSCBundle response; - Levels &levels = audio_get_levels(); - - snprintf(address, 22, "/bus/%d/levels/rms", bus); - response.add(address).add(rmsToDb(levels.rms[CHANNELS + bus])); - - snprintf(address, 22, "/bus/%d/levels/peak", bus); - response.add(address).add(rmsToDb(levels.peak[CHANNELS + bus])); - - snprintf(address, 22, "/bus/%d/levels/smooth", bus); - response.add(address).add(rmsToDb(levels.smooth[CHANNELS + bus])); - - send(response); - } else if (msg.match("/multiplier", addr) > 0) { - if (msg.isFloat(0)) { - set_bus_multiplier(bus, msg.getFloat(0)); - send(msg); - } else { - snprintf(address, 22, "/bus/%d/multiplier", bus); - OSCMessage response(address); - response.add(get_bus_multiplier(bus)); - send(response); - } - } -} - -void onOscLevels(OSCMessage &msg, int patternOffset) { - Levels &levels = audio_get_levels(); - OSCMessage response("/levels"); - - float payload[(CHANNELS + BUSES) * 3 + 1]; - size_t offset = CHANNELS + BUSES; - for (size_t i = 0; i < offset; i++) { - payload[1 + i] = levels.rms[i]; - payload[1 + i + offset] = levels.peak[i]; - payload[1 + i + (offset * 2)] = levels.smooth[i]; - } - uint8_t enc[sizeof(payload)] = {0}; - memcpy(&enc, &payload, sizeof(payload)); - enc[0] = (char)CHANNELS; - enc[1] = (char)BUSES; - enc[2] = 3; - enc[3] = (unsigned char)0xFF; - response.add(enc, sizeof(payload)); - - send(response); -} - -void onOscUnknown() { - OSCMessage response("/unknown"); - send(response); -} - -bool any(int start...) { - bool found; - if ((found = start)) - goto end; - - va_list args; - va_start(args, start); - - while (!(found = va_arg(args, int))) - ; - -end: - va_end(args); - - return found; -} - -void onPacketReceived(OSCMessage msg) { - // Note: all functions are invoked, so make sure there are no side-effects - // if `false` is returned - if (any(msg.route("/ch", onOscChannel), msg.route("/bus", onOscBus), - msg.route("/state", onOscState), msg.dispatch("/info", onOscInfo), - msg.dispatch("/factoryreset", onOscFactoryReset), - msg.route("/levels", onOscLevels))) - return; - - onOscUnknown(); -} - void setup() { if (CrashReport) { // Wait until the debug interface is ready diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index d90a2c93..7ffd785f 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -67,26 +67,14 @@ void audio_update_levels(Levels &levels) { Levels &audio_get_levels() { return levels; } -void raw_set_crosspoint(uint8_t channel, uint8_t bus, float gain) { - matrix[bus][channel / 4]->gain(channel % 4, gain); +void raw_set_crosspoint(uint8_t channel, uint8_t bus, float volume) { + matrix[bus][channel / 4]->gain(channel % 4, volume); } float raw_get_crosspoint(uint8_t channel, uint8_t bus) { return matrix[bus][channel / 4]->getGain(channel % 4); } -void raw_set_mix(uint8_t bus, float in1, float in2, float in3, float in4, float in5, - float in6) { - matrix[bus][0]->gain(0, in1); - matrix[bus][0]->gain(1, in2); - matrix[bus][0]->gain(2, in3); - matrix[bus][0]->gain(3, in4); - matrix[bus][1]->gain(0, in5); - matrix[bus][1]->gain(1, in6); - matrix[bus][1]->gain(2, 0.0f); - matrix[bus][1]->gain(3, 0.0f); -} - uint64_t mute_mask(uint64_t channel, uint64_t bus) { return (uint64_t)1 << (uint64_t)((channel * CHANNELS) + bus); } @@ -95,48 +83,51 @@ bool is_muted(uint8_t channel, uint8_t bus) { return !!(state.mutes & mute_mask(channel, bus)); } -float calc_real_gain(uint8_t channel, uint8_t bus, float gain) { - return gain * !is_muted(channel, bus) * state.bus_multipliers[bus]; +float calc_real_volume(uint8_t channel, uint8_t bus, float volume) { + return volume * !is_muted(channel, bus) * state.bus_multipliers[bus]; } -void set_gain(uint8_t channel, uint8_t bus, float gain) { - state.gains[channel][bus] = gain; - raw_set_crosspoint(channel, bus, calc_real_gain(channel, bus, gain)); +void apply_volume(uint8_t channel, uint8_t bus) { + float volume = state.matrix[channel][bus]; + raw_set_crosspoint(channel, bus, calc_real_volume(channel, bus, volume)); } -float get_gain(uint8_t channel, uint8_t bus) { return state.gains[channel][bus]; } +void set_volume(uint8_t channel, uint8_t bus, float volume) { + state.matrix[channel][bus] = volume; + apply_volume(channel, bus); +} + +float get_volume(uint8_t channel, uint8_t bus) { return state.matrix[channel][bus]; } void mute(uint8_t channel, uint8_t bus) { state.mutes |= mute_mask(channel, bus); - set_gain(channel, bus, state.gains[channel][bus]); + apply_volume(channel, bus); } void unmute(uint8_t channel, uint8_t bus) { state.mutes &= ~mute_mask(channel, bus); - set_gain(channel, bus, state.gains[channel][bus]); + apply_volume(channel, bus); } void set_bus_multiplier(uint8_t bus, float multiplier) { state.bus_multipliers[bus] = multiplier; for (uint8_t channel = 0; channel < CHANNELS; ++channel) { - set_gain(channel, bus, state.gains[channel][bus]); + apply_volume(channel, bus); } } float get_bus_multiplier(uint8_t bus) { return state.bus_multipliers[bus]; } void set_channel_multiplier(uint8_t channel, float multiplier) { - taa3040.gain(channel, (uint8_t)multiplier, IMPEDANCE_10k, 0, 0); state.channel_multipliers[channel] = multiplier; - for (uint8_t bus = 0; bus < BUSES; ++bus) - set_gain(channel, bus, state.gains[channel][bus]); + taa3040.gain(channel, (uint8_t)multiplier, IMPEDANCE_10k, 0, 0); } float get_channel_multiplier(uint8_t channel) { return state.channel_multipliers[channel]; } -void reset_gains() { memcpy(state.gains, default_gains, sizeof(state.gains)); } +void reset_matrix() { memcpy(state.matrix, default_gains, sizeof(state.matrix)); } void reset_mutes() { memcpy(&state.mutes, &default_mutes, sizeof(state.mutes)); @@ -155,47 +146,50 @@ void reset_channel_multipliers() { void apply_all() { uint8_t i, j; for (i = 0; i < CHANNELS; ++i) { - set_channel_multiplier(i, state.channel_multipliers[i]); for (j = 0; j < BUSES; ++j) { - set_gain(i, j, state.gains[i][j]); + apply_volume(i, j); } } - for (j = 0; j < BUSES; ++j) { - set_bus_multiplier(j, state.bus_multipliers[j]); - } } void audio_reset_default_state() { - reset_gains(); + reset_matrix(); reset_mutes(); reset_bus_multipliers(); reset_channel_multipliers(); } -bool gains_ok() { +bool matrix_ok() { uint8_t i, j; - for (i = 0; i < CHANNELS; ++i) - for (j = 0; j < BUSES; ++j) - if (isnan(state.gains[i][j])) + for (i = 0; i < CHANNELS; ++i) { + for (j = 0; j < BUSES; ++j) { + if (isnan(state.matrix[i][j])) { return false; + } + } + } return true; } bool bus_multipliers_ok() { uint8_t i; - for (i = 0; i < BUSES; ++i) - if (isnan(state.bus_multipliers[i])) + for (i = 0; i < BUSES; ++i) { + if (isnan(state.bus_multipliers[i])) { return false; + } + } return true; } bool channel_multipliers_ok() { uint8_t i; - for (i = 0; i < CHANNELS; ++i) - if (isnan(state.channel_multipliers[i])) + for (i = 0; i < CHANNELS; ++i) { + if (isnan(state.channel_multipliers[i])) { return false; + } + } return true; } @@ -210,7 +204,7 @@ void audio_load_state() { #ifdef USE_EEPROM eeprom_load_all(state, STATE_EEPROM_OFFSET); - if (!gains_ok() || !bus_multipliers_ok() || !channel_multipliers_ok()) { + if (!matrix_ok() || !bus_multipliers_ok() || !channel_multipliers_ok()) { audio_reset_default_state(); audio_eeprom_save_all(); } diff --git a/hardware/firmware/audio_board/src/teensyaudio.h b/hardware/firmware/audio_board/src/teensyaudio.h index 06e3396f..fa333e52 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.h +++ b/hardware/firmware/audio_board/src/teensyaudio.h @@ -18,8 +18,8 @@ bool is_muted(uint8_t channel, uint8_t bus); void mute(uint8_t channel, uint8_t bus); void unmute(uint8_t channel, uint8_t bus); -void set_gain(uint8_t channel, uint8_t bus, float gain); -float get_gain(uint8_t channel, uint8_t bus); +void set_volume(uint8_t channel, uint8_t bus, float gain); +float get_volume(uint8_t channel, uint8_t bus); float get_bus_multiplier(uint8_t bus); void set_bus_multiplier(uint8_t bus, float multiplier); @@ -30,8 +30,6 @@ void set_channel_multiplier(uint8_t channel, float multiplier); // void raw_set_crosspoint(uint8_t channel, uint8_t bus, float gain); float raw_get_crosspoint(uint8_t channel, uint8_t bus); -// void raw_set_mix(uint8_t bus, float channel_gains[CHANNELS]); - void audio_update_levels(Levels &levels); Levels &audio_get_levels(); diff --git a/hardware/firmware/audio_board/src/types.h b/hardware/firmware/audio_board/src/types.h index 0b6cb4aa..fc7904ce 100644 --- a/hardware/firmware/audio_board/src/types.h +++ b/hardware/firmware/audio_board/src/types.h @@ -5,7 +5,7 @@ #include typedef struct { - float gains[CHANNELS][BUSES]; + float matrix[CHANNELS][BUSES]; uint64_t mutes; From 8f8f0510ef21d09954da21c6dd5678c9afbc8cc7 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 00:40:49 +0100 Subject: [PATCH 18/67] more renames, proper gains --- hardware/firmware/audio_board/src/cli/cli.cpp | 33 ++++++----- .../firmware/audio_board/src/teensyaudio.cpp | 55 ++++++++++++------- .../firmware/audio_board/src/teensyaudio.h | 8 +-- .../audio_board/src/teensyaudio_defaults.cpp | 15 +++-- hardware/firmware/audio_board/src/types.h | 4 +- 5 files changed, 68 insertions(+), 47 deletions(-) diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp index 6983bf17..839827a7 100644 --- a/hardware/firmware/audio_board/src/cli/cli.cpp +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -58,16 +58,16 @@ void Cli::exec_cmd() { this->port->print("ok"); for (uint8_t chan = 0; chan < CHANNELS; chan++) { this->port->print(" "); - this->print_float_fixed(get_channel_multiplier(chan), 3, 3); + this->print_float_fixed(get_channel_input_gain_db(chan), 3, 3); } this->port->println(); return; } - if (this->hop_word("bus-levels")) { + if (this->hop_word("bus-volumes")) { this->port->print("ok"); for (uint8_t bus = 0; bus < BUSES; bus++) { this->port->print(" "); - this->print_float_fixed(get_bus_multiplier(bus), 3, 3); + this->print_float_fixed(get_bus_volume(bus), 3, 3); } this->port->println(); return; @@ -112,28 +112,28 @@ void Cli::exec_cmd() { set_volume(chan, bus, vol); - this->port->print("ok"); + this->port->println("ok"); return; } - if (this->hop_word("set-gain")) { + if (this->hop_word("set-in-gain")) { uint16_t chan = parse_uint(); - float vol = parse_float(); + float gain = parse_float(); if (chan >= CHANNELS) { this->port->printf("fail (chan %d is invalid)\n", chan); return; } - if (vol < 0) { - this->port->printf("fail (vol should not be negative)\n"); + if (gain < 0) { + this->port->printf("fail (gain should not be negative)\n"); return; } - set_channel_multiplier(chan, vol); + set_channel_input_gain_db(chan, gain); - this->port->print("ok"); + this->port->println("ok"); return; } - if (this->hop_word("set-bus-level")) { + if (this->hop_word("set-bus-volume")) { uint16_t bus = parse_uint(); float vol = parse_float(); @@ -146,9 +146,9 @@ void Cli::exec_cmd() { return; } - set_bus_multiplier(bus, vol); + set_bus_volume(bus, vol); - this->port->print("ok"); + this->port->println("ok"); return; } if (this->hop_word("factory-reset")) { @@ -169,7 +169,12 @@ void Cli::print_float_fixed(float x, uint8_t whole_digits, uint8_t frac_digits) buf[0] = sign ? '-' : '+'; - if (x >= 10 * whole_digits) { + float limit = 1; + for (uint8_t i = 0; i < whole_digits; i++) { + limit *= 10; + } + + if (x >= limit) { buf[1] = 'i'; buf[2] = 'n'; buf[3] = 'f'; diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index 7ffd785f..4e5d0cb0 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -84,7 +84,7 @@ bool is_muted(uint8_t channel, uint8_t bus) { } float calc_real_volume(uint8_t channel, uint8_t bus, float volume) { - return volume * !is_muted(channel, bus) * state.bus_multipliers[bus]; + return volume * !is_muted(channel, bus) * state.bus_volumes[bus]; } void apply_volume(uint8_t channel, uint8_t bus) { @@ -109,43 +109,56 @@ void unmute(uint8_t channel, uint8_t bus) { apply_volume(channel, bus); } -void set_bus_multiplier(uint8_t bus, float multiplier) { - state.bus_multipliers[bus] = multiplier; +void set_bus_volume(uint8_t bus, float vol) { + state.bus_volumes[bus] = vol; for (uint8_t channel = 0; channel < CHANNELS; ++channel) { apply_volume(channel, bus); } } -float get_bus_multiplier(uint8_t bus) { return state.bus_multipliers[bus]; } +float get_bus_volume(uint8_t bus) { return state.bus_volumes[bus]; } -void set_channel_multiplier(uint8_t channel, float multiplier) { - state.channel_multipliers[channel] = multiplier; - taa3040.gain(channel, (uint8_t)multiplier, IMPEDANCE_10k, 0, 0); +void set_channel_input_gain_db(uint8_t channel, float gain) { + uint8_t whole_gain = (uint8_t)(gain + 0.5); + if (gain < 1) { + whole_gain = 1; + } + if (gain > 42) { + whole_gain = 42; + } + + state.channel_input_gains[channel] = (float)whole_gain; + taa3040.gain(channel, whole_gain, IMPEDANCE_10k, 0, 0); +} + +void apply_channel_input_gain(uint8_t channel) { + set_channel_input_gain_db(channel, state.channel_input_gains[channel]); } -float get_channel_multiplier(uint8_t channel) { - return state.channel_multipliers[channel]; +float get_channel_input_gain_db(uint8_t channel) { + return state.channel_input_gains[channel]; } -void reset_matrix() { memcpy(state.matrix, default_gains, sizeof(state.matrix)); } +void reset_matrix() { memcpy(state.matrix, default_matrix, sizeof(state.matrix)); } void reset_mutes() { memcpy(&state.mutes, &default_mutes, sizeof(state.mutes)); } -void reset_bus_multipliers() { - memcpy(state.bus_multipliers, default_bus_multipliers, +void reset_bus_volumes() { + memcpy(state.bus_volumes, default_bus_volumes, BUSES * sizeof(float)); } -void reset_channel_multipliers() { - memcpy(state.channel_multipliers, default_channel_multipliers, +void reset_channel_input_gains() { + memcpy(state.channel_input_gains, default_channel_input_gains_db, CHANNELS * sizeof(float)); } void apply_all() { uint8_t i, j; for (i = 0; i < CHANNELS; ++i) { + apply_channel_input_gain(i); for (j = 0; j < BUSES; ++j) { apply_volume(i, j); } @@ -155,8 +168,8 @@ void apply_all() { void audio_reset_default_state() { reset_matrix(); reset_mutes(); - reset_bus_multipliers(); - reset_channel_multipliers(); + reset_bus_volumes(); + reset_channel_input_gains(); } bool matrix_ok() { @@ -172,10 +185,10 @@ bool matrix_ok() { return true; } -bool bus_multipliers_ok() { +bool bus_volumes_ok() { uint8_t i; for (i = 0; i < BUSES; ++i) { - if (isnan(state.bus_multipliers[i])) { + if (isnan(state.bus_volumes[i])) { return false; } } @@ -183,10 +196,10 @@ bool bus_multipliers_ok() { return true; } -bool channel_multipliers_ok() { +bool channel_input_gains_ok() { uint8_t i; for (i = 0; i < CHANNELS; ++i) { - if (isnan(state.channel_multipliers[i])) { + if (isnan(state.channel_input_gains[i])) { return false; } } @@ -204,7 +217,7 @@ void audio_load_state() { #ifdef USE_EEPROM eeprom_load_all(state, STATE_EEPROM_OFFSET); - if (!matrix_ok() || !bus_multipliers_ok() || !channel_multipliers_ok()) { + if (!matrix_ok() || !bus_volumes_ok() || !channel_input_gains_ok()) { audio_reset_default_state(); audio_eeprom_save_all(); } diff --git a/hardware/firmware/audio_board/src/teensyaudio.h b/hardware/firmware/audio_board/src/teensyaudio.h index fa333e52..279d5288 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.h +++ b/hardware/firmware/audio_board/src/teensyaudio.h @@ -21,11 +21,11 @@ void unmute(uint8_t channel, uint8_t bus); void set_volume(uint8_t channel, uint8_t bus, float gain); float get_volume(uint8_t channel, uint8_t bus); -float get_bus_multiplier(uint8_t bus); -void set_bus_multiplier(uint8_t bus, float multiplier); +float get_bus_volume(uint8_t bus); +void set_bus_volume(uint8_t bus, float volume); -float get_channel_multiplier(uint8_t channel); -void set_channel_multiplier(uint8_t channel, float multiplier); +float get_channel_input_gain_db(uint8_t channel); +void set_channel_input_gain_db(uint8_t channel, float gain); // void raw_set_crosspoint(uint8_t channel, uint8_t bus, float gain); float raw_get_crosspoint(uint8_t channel, uint8_t bus); diff --git a/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp b/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp index e3eaceac..957d7d7f 100644 --- a/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp @@ -1,13 +1,16 @@ -#define MIC (1.5f) +#define MIC (5.0f) -const PROGMEM float default_bus_multipliers[BUSES] = {1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f}; -const PROGMEM float default_channel_multipliers[CHANNELS] = {MIC, MIC, MIC, - 1.0f, 1.0f, 1.0f}; +const PROGMEM float default_bus_volumes[BUSES] = { + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f +}; + +const PROGMEM float default_channel_input_gains_db[CHANNELS] = { + MIC, MIC, MIC, 1.0f, 1.0f, 1.0f +}; // see helpers/generate_mutes.py const PROGMEM uint64_t default_mutes = 52361428992; -const PROGMEM float default_gains[CHANNELS][BUSES] = { +const PROGMEM float default_matrix[CHANNELS][BUSES] = { // outputs: OUT1, OUT2, HP1, HP2, USB1, USB2 // IN1 {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, diff --git a/hardware/firmware/audio_board/src/types.h b/hardware/firmware/audio_board/src/types.h index fc7904ce..cd83f167 100644 --- a/hardware/firmware/audio_board/src/types.h +++ b/hardware/firmware/audio_board/src/types.h @@ -9,8 +9,8 @@ typedef struct { uint64_t mutes; - float channel_multipliers[CHANNELS]; - float bus_multipliers[BUSES]; + float channel_input_gains[CHANNELS]; + float bus_volumes[BUSES]; } AudioState; typedef struct { From 93d3869a7f68f4c7d5669a289d04cfd9cca5f388 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 00:54:05 +0100 Subject: [PATCH 19/67] dummy phantom --- hardware/firmware/audio_board/src/cli/cli.cpp | 27 ++++++++++++++++ .../firmware/audio_board/src/teensyaudio.cpp | 31 +++++++++++++++++++ .../firmware/audio_board/src/teensyaudio.h | 4 +++ .../audio_board/src/teensyaudio_defaults.cpp | 2 ++ hardware/firmware/audio_board/src/types.h | 1 + 5 files changed, 65 insertions(+) diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp index 839827a7..b4cde1b9 100644 --- a/hardware/firmware/audio_board/src/cli/cli.cpp +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -63,6 +63,18 @@ void Cli::exec_cmd() { this->port->println(); return; } + if (this->hop_word("phantoms")) { + this->port->print("ok"); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + if (is_phantom_on(chan)) { + this->port->print(" 1"); + } else { + this->port->print(" 0"); + } + } + this->port->println(); + return; + } if (this->hop_word("bus-volumes")) { this->port->print("ok"); for (uint8_t bus = 0; bus < BUSES; bus++) { @@ -92,6 +104,21 @@ void Cli::exec_cmd() { this->port->println("ok"); return; } + if (this->hop_word("set-phantom")) { + uint16_t chan = parse_uint(); + uint16_t want_phantom = parse_uint(); + if (chan >= CHANNELS) { + this->port->printf("fail (chan %d is invalid)\n", chan); + return; + } + if (want_phantom > 0) { + set_phantom_on(chan); + } else { + set_phantom_off(chan); + } + this->port->println("ok"); + return; + } if (this->hop_word("set-fader")) { uint16_t chan = parse_uint(); uint16_t bus = parse_uint(); diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index 4e5d0cb0..a5a70b0b 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -79,6 +79,31 @@ uint64_t mute_mask(uint64_t channel, uint64_t bus) { return (uint64_t)1 << (uint64_t)((channel * CHANNELS) + bus); } +void apply_phantom(uint8_t channel) { + bool is_on = (state.phantoms & (1 << channel)) > 0; + // TODO: apply state +} + +bool is_phantom_on(uint8_t channel) { + return !!(state.phantoms & (1 << channel)); +} + +void set_phantom_on(uint8_t channel) { + if (channel > 3) { + return; + } + state.phantoms |= (1 << channel); + apply_phantom(channel); +} + +void set_phantom_off(uint8_t channel) { + if (channel > 3) { + return; + } + state.phantoms &= ~(1 << channel); + apply_phantom(channel); +} + bool is_muted(uint8_t channel, uint8_t bus) { return !!(state.mutes & mute_mask(channel, bus)); } @@ -145,6 +170,10 @@ void reset_mutes() { memcpy(&state.mutes, &default_mutes, sizeof(state.mutes)); } +void reset_phantoms() { + memcpy(&state.phantoms, &default_phantoms, sizeof(state.phantoms)); +} + void reset_bus_volumes() { memcpy(state.bus_volumes, default_bus_volumes, BUSES * sizeof(float)); @@ -159,6 +188,7 @@ void apply_all() { uint8_t i, j; for (i = 0; i < CHANNELS; ++i) { apply_channel_input_gain(i); + apply_phantom(i); for (j = 0; j < BUSES; ++j) { apply_volume(i, j); } @@ -168,6 +198,7 @@ void apply_all() { void audio_reset_default_state() { reset_matrix(); reset_mutes(); + reset_phantoms(); reset_bus_volumes(); reset_channel_input_gains(); } diff --git a/hardware/firmware/audio_board/src/teensyaudio.h b/hardware/firmware/audio_board/src/teensyaudio.h index 279d5288..a1e0be19 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.h +++ b/hardware/firmware/audio_board/src/teensyaudio.h @@ -35,6 +35,10 @@ Levels &audio_get_levels(); void audio_reset_default_state(); +bool is_phantom_on(uint8_t channel); +void set_phantom_on(uint8_t channel); +void set_phantom_off(uint8_t channel); + #ifdef USE_EEPROM uint8_t audio_eeprom_save_all(); #endif diff --git a/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp b/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp index 957d7d7f..73819c11 100644 --- a/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp @@ -10,6 +10,8 @@ const PROGMEM float default_channel_input_gains_db[CHANNELS] = { // see helpers/generate_mutes.py const PROGMEM uint64_t default_mutes = 52361428992; +const PROGMEM uint16_t default_phantoms = 0; + const PROGMEM float default_matrix[CHANNELS][BUSES] = { // outputs: OUT1, OUT2, HP1, HP2, USB1, USB2 // IN1 diff --git a/hardware/firmware/audio_board/src/types.h b/hardware/firmware/audio_board/src/types.h index cd83f167..8c172205 100644 --- a/hardware/firmware/audio_board/src/types.h +++ b/hardware/firmware/audio_board/src/types.h @@ -8,6 +8,7 @@ typedef struct { float matrix[CHANNELS][BUSES]; uint64_t mutes; + uint16_t phantoms; float channel_input_gains[CHANNELS]; float bus_volumes[BUSES]; From f347dd55f552033dcf8ac81be67bc7bf11d446cc Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 01:08:41 +0100 Subject: [PATCH 20/67] digitalWrite --- hardware/firmware/audio_board/src/config.h | 4 ++++ .../firmware/audio_board/src/teensyaudio.cpp | 20 ++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/hardware/firmware/audio_board/src/config.h b/hardware/firmware/audio_board/src/config.h index 4f9a7762..13c13abc 100644 --- a/hardware/firmware/audio_board/src/config.h +++ b/hardware/firmware/audio_board/src/config.h @@ -7,4 +7,8 @@ #define SCREEN_WIDTH 80 #define SCREEN_HEIGHT 160 +#define PIN_PHANTOM_IN1 37 +#define PIN_PHANTOM_IN2 36 +#define PIN_PHANTOM_IN3 35 + #endif diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index a5a70b0b..119748c3 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -42,6 +42,10 @@ void audio_setup() { Wire.begin(); Wire1.begin(); + pinMode(PIN_PHANTOM_IN1, OUTPUT); + pinMode(PIN_PHANTOM_IN2, OUTPUT); + pinMode(PIN_PHANTOM_IN3, OUTPUT); + taa3040.enable(); } @@ -81,7 +85,17 @@ uint64_t mute_mask(uint64_t channel, uint64_t bus) { void apply_phantom(uint8_t channel) { bool is_on = (state.phantoms & (1 << channel)) > 0; - // TODO: apply state + switch (channel) { + case 0: + digitalWrite(PIN_PHANTOM_IN1, is_on); + break; + case 1: + digitalWrite(PIN_PHANTOM_IN2, is_on); + break; + case 2: + digitalWrite(PIN_PHANTOM_IN3, is_on); + break; + } } bool is_phantom_on(uint8_t channel) { @@ -89,7 +103,7 @@ bool is_phantom_on(uint8_t channel) { } void set_phantom_on(uint8_t channel) { - if (channel > 3) { + if (channel > 2) { return; } state.phantoms |= (1 << channel); @@ -97,7 +111,7 @@ void set_phantom_on(uint8_t channel) { } void set_phantom_off(uint8_t channel) { - if (channel > 3) { + if (channel > 2) { return; } state.phantoms &= ~(1 << channel); From 95932186f12493a6ee953f69874a91aeba060b83 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 01:19:31 +0100 Subject: [PATCH 21/67] arduino is hell --- hardware/firmware/audio_board/src/config.h | 13 ++++++------- hardware/firmware/audio_board/src/teensyaudio.cpp | 6 +++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/hardware/firmware/audio_board/src/config.h b/hardware/firmware/audio_board/src/config.h index 13c13abc..3f73c9b5 100644 --- a/hardware/firmware/audio_board/src/config.h +++ b/hardware/firmware/audio_board/src/config.h @@ -1,5 +1,6 @@ -#ifndef _MIXER_CONFIG_H_ -#define _MIXER_CONFIG_H_ +#pragma once + +#include #define CHANNELS 6 #define BUSES 6 @@ -7,8 +8,6 @@ #define SCREEN_WIDTH 80 #define SCREEN_HEIGHT 160 -#define PIN_PHANTOM_IN1 37 -#define PIN_PHANTOM_IN2 36 -#define PIN_PHANTOM_IN3 35 - -#endif +#define PIN_PHANTOM_IN1 PIN_E5 +#define PIN_PHANTOM_IN2 PIN_E4 +#define PIN_PHANTOM_IN3 PIN_A7 diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index 119748c3..f0c13e4e 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -87,13 +87,13 @@ void apply_phantom(uint8_t channel) { bool is_on = (state.phantoms & (1 << channel)) > 0; switch (channel) { case 0: - digitalWrite(PIN_PHANTOM_IN1, is_on); + digitalWrite(PIN_PHANTOM_IN1, is_on ? HIGH : LOW); break; case 1: - digitalWrite(PIN_PHANTOM_IN2, is_on); + digitalWrite(PIN_PHANTOM_IN2, is_on ? HIGH : LOW); break; case 2: - digitalWrite(PIN_PHANTOM_IN3, is_on); + digitalWrite(PIN_PHANTOM_IN3, is_on ? HIGH : LOW); break; } } From fe946fb6df83b94033f137a09de6aae01a50828e Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 01:27:42 +0100 Subject: [PATCH 22/67] fix gpios --- hardware/firmware/audio_board/CMakeLists.txt | 2 +- hardware/firmware/audio_board/src/config.h | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hardware/firmware/audio_board/CMakeLists.txt b/hardware/firmware/audio_board/CMakeLists.txt index 03c368ec..5b3f7cc5 100644 --- a/hardware/firmware/audio_board/CMakeLists.txt +++ b/hardware/firmware/audio_board/CMakeLists.txt @@ -33,7 +33,7 @@ set(TEENSY_DEFINITIONS USB_MIDI_AUDIO_SERIAL LAYOUT_US_ENGLISH F_CPU=${CPU_CORE_SPEED} ${CPU_DEFINE} - ARDUINO_TEENSY40) + ARDUINO_TEENSY41) set(MCPU cortex-m7) add_compile_options(-mcpu=${MCPU} -mthumb -mthumb-interwork -mfloat-abi=hard -mfpu=fpv5-d16 -D${CPU_DEFINE} -DTEENSYDUINO=159 -DARDUINO=10607 -nostdlib) set(c_flags -MMD -fno-exceptions) diff --git a/hardware/firmware/audio_board/src/config.h b/hardware/firmware/audio_board/src/config.h index 3f73c9b5..13c13abc 100644 --- a/hardware/firmware/audio_board/src/config.h +++ b/hardware/firmware/audio_board/src/config.h @@ -1,6 +1,5 @@ -#pragma once - -#include +#ifndef _MIXER_CONFIG_H_ +#define _MIXER_CONFIG_H_ #define CHANNELS 6 #define BUSES 6 @@ -8,6 +7,8 @@ #define SCREEN_WIDTH 80 #define SCREEN_HEIGHT 160 -#define PIN_PHANTOM_IN1 PIN_E5 -#define PIN_PHANTOM_IN2 PIN_E4 -#define PIN_PHANTOM_IN3 PIN_A7 +#define PIN_PHANTOM_IN1 37 +#define PIN_PHANTOM_IN2 36 +#define PIN_PHANTOM_IN3 35 + +#endif From 6838e42cd81ac8f6c4af8125196147394f26f410 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 16:38:28 +0100 Subject: [PATCH 23/67] port the cli to fancy c++ stuff --- hardware/firmware/audio_board/CMakeLists.txt | 1 + hardware/firmware/audio_board/src/cli/cli.cpp | 205 +++---------- hardware/firmware/audio_board/src/cli/cli.h | 19 +- .../firmware/audio_board/src/cli/commands.cpp | 287 ++++++++++++++++++ .../firmware/audio_board/src/cli/commands.h | 0 5 files changed, 343 insertions(+), 169 deletions(-) create mode 100644 hardware/firmware/audio_board/src/cli/commands.cpp create mode 100644 hardware/firmware/audio_board/src/cli/commands.h diff --git a/hardware/firmware/audio_board/CMakeLists.txt b/hardware/firmware/audio_board/CMakeLists.txt index 5b3f7cc5..4b8648db 100644 --- a/hardware/firmware/audio_board/CMakeLists.txt +++ b/hardware/firmware/audio_board/CMakeLists.txt @@ -116,6 +116,7 @@ set(SOURCES src/teensyaudio.cpp src/taa3040.cpp src/cli/cli.cpp + src/cli/commands.cpp ) # Build this project diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp index b4cde1b9..6d5fb6f0 100644 --- a/hardware/firmware/audio_board/src/cli/cli.cpp +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -1,189 +1,60 @@ #include "cli.h" -#include "../config.h" -#include "../teensyaudio.h" -#include "../helpers.h" - Cli::Cli(Stream* _port) : port(_port) {} void Cli::exec_cmd() { - if (this->hop_word("ping")) { - this->port->printf("pong %s\n", this->cmd); - return; - } - if (this->hop_word("levels.db")) { - Levels &levels = audio_get_levels(); - this->port->print("ok"); - for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { - this->port->print(" "); - this->print_float_fixed(rmsToDb(levels.rms[i]), 3, 5); - this->port->print(" "); - this->print_float_fixed(rmsToDb(levels.peak[i]), 3, 5); - this->port->print(" "); - this->print_float_fixed(rmsToDb(levels.smooth[i]), 3, 5); - } - this->port->println(); + if (this->cmds[this->num_cmds].name == nullptr || strncmp("END", this->cmds[this->num_cmds].name, 3) != 0) { + this->port->println("fail (internal bug: num_cmds in cli.h is probably wrong)"); return; } - if (this->hop_word("levels")) { - Levels &levels = audio_get_levels(); - this->port->print("ok"); - for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { - this->port->print(" "); - this->print_float_fixed(levels.rms[i], 3, 5); - this->port->print(" "); - this->print_float_fixed(levels.peak[i], 3, 5); - this->port->print(" "); - this->print_float_fixed(levels.smooth[i], 3, 5); - } - this->port->println(); - return; - } - if (this->hop_word("matrix")) { - this->port->print("ok"); - for (uint8_t chan = 0; chan < CHANNELS; chan++) { - for (uint8_t bus = 0; bus < BUSES; bus++) { - if (is_muted(chan, bus)) { - this->port->print(" 0*"); - } else { - this->port->print(" 1*"); - } - this->print_float_fixed(get_volume(chan, bus), 3, 3); - } - } - this->port->println(); - return; - } - if (this->hop_word("gains")) { - this->port->print("ok"); - for (uint8_t chan = 0; chan < CHANNELS; chan++) { - this->port->print(" "); - this->print_float_fixed(get_channel_input_gain_db(chan), 3, 3); - } - this->port->println(); - return; - } - if (this->hop_word("phantoms")) { - this->port->print("ok"); - for (uint8_t chan = 0; chan < CHANNELS; chan++) { - if (is_phantom_on(chan)) { - this->port->print(" 1"); - } else { - this->port->print(" 0"); - } - } - this->port->println(); - return; - } - if (this->hop_word("bus-volumes")) { - this->port->print("ok"); - for (uint8_t bus = 0; bus < BUSES; bus++) { - this->port->print(" "); - this->print_float_fixed(get_bus_volume(bus), 3, 3); - } - this->port->println(); - return; - } - if (this->hop_word("set-send")) { - uint16_t chan = parse_uint(); - uint16_t bus = parse_uint(); - uint16_t want_send = parse_uint(); - if (chan >= CHANNELS) { - this->port->printf("fail (chan %d is invalid)\n", chan); - return; - } - if (bus >= BUSES) { - this->port->printf("fail (bus %d is invalid)\n", bus); - return; - } - if (want_send > 0) { - unmute(chan, bus); - } else { - mute(chan, bus); - } - this->port->println("ok"); + + const Cli::CmdDescr* cmd = this->hop_cmd(); + if (!cmd) { + this->port->println("fail (unknown command; use `help` for help)"); return; } - if (this->hop_word("set-phantom")) { - uint16_t chan = parse_uint(); - uint16_t want_phantom = parse_uint(); - if (chan >= CHANNELS) { - this->port->printf("fail (chan %d is invalid)\n", chan); - return; - } - if (want_phantom > 0) { - set_phantom_on(chan); - } else { - set_phantom_off(chan); + + uint8_t num_spaces = 0; + for (uint8_t i = 0; this->input_buf[i] != '\0'; i++) { + if (this->input_buf[i] == ' ') { + num_spaces++; } - this->port->println("ok"); - return; } - if (this->hop_word("set-fader")) { - uint16_t chan = parse_uint(); - uint16_t bus = parse_uint(); - float vol = parse_float(); - - if (chan >= CHANNELS) { - this->port->printf("fail (chan %d is invalid)\n", chan); - return; - } - if (bus >= BUSES) { - this->port->printf("fail (bus %d is invalid)\n", bus); - return; - } - if (vol < 0) { - this->port->printf("fail (vol should not be negative)\n"); - return; - } - - set_volume(chan, bus, vol); - this->port->println("ok"); + if (cmd->num_args >= 0 && cmd->num_args != num_spaces) { + this->port->print("fail ("); + this->print_usage(*cmd); + this->port->println(")"); return; } - if (this->hop_word("set-in-gain")) { - uint16_t chan = parse_uint(); - float gain = parse_float(); - if (chan >= CHANNELS) { - this->port->printf("fail (chan %d is invalid)\n", chan); - return; - } - if (gain < 0) { - this->port->printf("fail (gain should not be negative)\n"); - return; - } - - set_channel_input_gain_db(chan, gain); + cmd->callback(this); +} - this->port->println("ok"); - return; +void Cli::print_usage(const Cli::CmdDescr& cmd) { + this->port->print("usage: "); + this->port->print(cmd.name); + if (cmd.arghelp[0] != '\0') { + this->port->print(" "); + this->port->print(cmd.arghelp); } - if (this->hop_word("set-bus-volume")) { - uint16_t bus = parse_uint(); - float vol = parse_float(); + this->port->print(" -- "); + this->port->print(cmd.help); +} - if (bus >= BUSES) { - this->port->printf("fail (bus %d is invalid)\n", bus); - return; - } - if (vol < 0) { - this->port->printf("fail (vol should not be negative)\n"); - return; +const Cli::CmdDescr* Cli::hop_cmd() { + for (uint8_t i = 0; i < Cli::num_cmds; i++) { + const Cli::CmdDescr& cmd = this->cmds[i]; + if (cmd.name == nullptr || cmd.help == nullptr || cmd.arghelp == nullptr) { + this->port->printf("fail (internal bug: entry %d in commands.cpp is corrupted)", i); + return nullptr; } - set_bus_volume(bus, vol); - - this->port->println("ok"); - return; - } - if (this->hop_word("factory-reset")) { - audio_reset_default_state(); - audio_eeprom_save_all(); - this->port->println("ok"); - return; + if (this->hop_word(cmd.name)) { + return &cmd; + } } + return nullptr; } void Cli::print_float_fixed(float x, uint8_t whole_digits, uint8_t frac_digits) { @@ -252,7 +123,7 @@ void Cli::skip_whitespace() { skip_whitespace_in(&this->cmd); } -uint16_t Cli::parse_uint() { +uint16_t Cli::hop_uint() { skip_whitespace(); uint16_t result = 0; while (*this->cmd >= '0' && *this->cmd <= '9') { @@ -267,7 +138,7 @@ uint16_t Cli::parse_uint() { return result; } -float Cli::parse_float() { +float Cli::hop_float() { skip_whitespace(); float sign = 1.0f; diff --git a/hardware/firmware/audio_board/src/cli/cli.h b/hardware/firmware/audio_board/src/cli/cli.h index f88b0f3f..a629230b 100644 --- a/hardware/firmware/audio_board/src/cli/cli.h +++ b/hardware/firmware/audio_board/src/cli/cli.h @@ -7,6 +7,17 @@ class Cli { private: Stream* port; + struct CmdDescr{ + const char* name; + const char* help; + const char* arghelp; + int32_t num_args; + std::function callback; + }; + + static const uint8_t num_cmds = 15; + static const CmdDescr cmds[num_cmds + 1]; + char input_buf[100]; uint8_t input_pos = 0; @@ -15,12 +26,16 @@ class Cli { bool is_terminator_or_whitespace(char c); void skip_whitespace_in(char** buf); void skip_whitespace(); - uint16_t parse_uint(); - float parse_float(); + + uint16_t hop_uint(); + float hop_float(); bool hop_word(const char* word); + const Cli::CmdDescr* hop_cmd(); + void eat(char chr); void print_float_fixed(float x, uint8_t whole_digits, uint8_t frac_digits); + void print_usage(const Cli::CmdDescr& cmd); // state used only during exec_cmd(): char* cmd; diff --git a/hardware/firmware/audio_board/src/cli/commands.cpp b/hardware/firmware/audio_board/src/cli/commands.cpp new file mode 100644 index 00000000..63a16251 --- /dev/null +++ b/hardware/firmware/audio_board/src/cli/commands.cpp @@ -0,0 +1,287 @@ +#include "cli.h" + +#include "../config.h" +#include "../teensyaudio.h" +#include "../helpers.h" + +const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { + { + .name = "ping", + .help = "returns `pong `", + .arghelp = "", + .num_args = -1, + .callback = [](Cli* cli){ + cli->port->printf("pong %s\n", cli->cmd); + } + }, + { + .name = "levels.db", + .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels in db", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli){ + Levels &levels = audio_get_levels(); + cli->port->print("ok"); + for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { + cli->port->print(" "); + cli->print_float_fixed(rmsToDb(levels.rms[i]), 3, 5); + cli->port->print(" "); + cli->print_float_fixed(rmsToDb(levels.peak[i]), 3, 5); + cli->port->print(" "); + cli->print_float_fixed(rmsToDb(levels.smooth[i]), 3, 5); + } + cli->port->println(); + } + }, + { + .name = "levels", + .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli){ + Levels &levels = audio_get_levels(); + cli->port->print("ok"); + for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { + cli->port->print(" "); + cli->print_float_fixed(levels.rms[i], 3, 5); + cli->port->print(" "); + cli->print_float_fixed(levels.peak[i], 3, 5); + cli->port->print(" "); + cli->print_float_fixed(levels.smooth[i], 3, 5); + } + cli->port->println(); + } + }, + { + .name = "matrix", + .help = "for each input channel x outpub bus combination, outputs an item in the form *, where send is 0/1 and volume is a float", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli){ + cli->port->print("ok"); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + for (uint8_t bus = 0; bus < BUSES; bus++) { + if (is_muted(chan, bus)) { + cli->port->print(" 0*"); + } else { + cli->port->print(" 1*"); + } + cli->print_float_fixed(get_volume(chan, bus), 3, 3); + } + } + cli->port->println(); + } + }, + { + .name = "gains", + .help = "for each input channel, returns its gain in db", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli){ + cli->port->print("ok"); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + cli->port->print(" "); + cli->print_float_fixed(get_channel_input_gain_db(chan), 3, 3); + } + cli->port->println(); + } + }, + { + .name = "phantoms", + .help = "for each input channel, returns its phantom power on/off status (0/1)", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli){ + cli->port->print("ok"); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + if (is_phantom_on(chan)) { + cli->port->print(" 1"); + } else { + cli->port->print(" 0"); + } + } + cli->port->println(); + } + }, + { + .name = "bus-volumes", + .help = "for each output bus, returns its volume", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli){ + cli->port->print("ok"); + for (uint8_t bus = 0; bus < BUSES; bus++) { + cli->port->print(" "); + cli->print_float_fixed(get_bus_volume(bus), 3, 3); + } + cli->port->println(); + } + }, + { + .name = "set-send", + .help = "for the given channel/bus crosspoint, set the send bit in the matrix", + .arghelp = " (0|1)", + .num_args = 3, + .callback = [](Cli* cli){ + uint16_t chan = cli->hop_uint(); + uint16_t bus = cli->hop_uint(); + uint16_t want_send = cli->hop_uint(); + if (chan >= CHANNELS) { + cli->port->printf("fail (chan %d is invalid)\n", chan); + return; + } + if (bus >= BUSES) { + cli->port->printf("fail (bus %d is invalid)\n", bus); + return; + } + if (want_send > 0) { + unmute(chan, bus); + } else { + mute(chan, bus); + } + cli->port->println("ok"); + } + }, + { + .name = "set-phantom", + .help = "for the given input channel, set the phantom power on/off bit", + .arghelp = " (0|1)", + .num_args = 2, + .callback = [](Cli* cli){ + uint16_t chan = cli->hop_uint(); + uint16_t want_phantom = cli->hop_uint(); + if (chan >= CHANNELS) { + cli->port->printf("fail (chan %d is invalid)\n", chan); + return; + } + if (want_phantom > 0) { + set_phantom_on(chan); + } else { + set_phantom_off(chan); + } + cli->port->println("ok"); + } + }, + { + .name = "set-volume", + .help = "for the given channel/bus crosspoint, set the volume in the matrix", + .arghelp = " ", + .num_args = 3, + .callback = [](Cli* cli){ + uint16_t chan = cli->hop_uint(); + uint16_t bus = cli->hop_uint(); + float vol = cli->hop_float(); + + if (chan >= CHANNELS) { + cli->port->printf("fail (chan %d is invalid)\n", chan); + return; + } + if (bus >= BUSES) { + cli->port->printf("fail (bus %d is invalid)\n", bus); + return; + } + if (vol < 0) { + cli->port->printf("fail (vol should not be negative)\n"); + return; + } + + set_volume(chan, bus, vol); + + cli->port->println("ok"); + }, + }, + { + .name = "set-in-gain", + .help = "for the given input channel, set the input gain in decibels", + .arghelp = " ", + .num_args = 2, + .callback = [](Cli* cli){ + uint16_t chan = cli->hop_uint(); + float gain = cli->hop_float(); + + if (chan >= CHANNELS) { + cli->port->printf("fail (chan %d is invalid)\n", chan); + return; + } + if (gain < 0) { + cli->port->printf("fail (gain should not be negative)\n"); + return; + } + + set_channel_input_gain_db(chan, gain); + + cli->port->println("ok"); + } + }, + { + .name = "set-bus-volume", + .help = "for the given out bus, set its global volume to the given value", + .arghelp = " ", + .num_args = 2, + .callback = [](Cli* cli){ + uint16_t bus = cli->hop_uint(); + float vol = cli->hop_float(); + + if (bus >= BUSES) { + cli->port->printf("fail (bus %d is invalid)\n", bus); + return; + } + if (vol < 0) { + cli->port->printf("fail (vol should not be negative)\n"); + return; + } + + set_bus_volume(bus, vol); + + cli->port->println("ok"); + } + }, + { + .name = "factory-reset", + .help = "clear all settings and state stored in the EEPROM", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli){ + audio_reset_default_state(); + audio_eeprom_save_all(); + cli->port->println("ok"); + } + }, + { + .name = "commands", + .help = "get a list of available commands", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli){ + cli->port->print("ok"); + for (uint8_t i = 0; i < Cli::num_cmds; i++) { + cli->port->print(" "); + cli->port->print(Cli::cmds[i].name); + } + cli->port->println(); + } + }, + { + .name = "help", + .help = "I suppose you think that was terribly clever.", + .arghelp = "[command]", + .num_args = -1, + .callback = [](Cli* cli){ + if (cli->cmd[0] == '\0') { + cli->port->println("ok use `commands` to get a list of commands and then `help `"); + return; + } + const Cli::CmdDescr* cmd = cli->hop_cmd(); + if (!cmd) { + cli->port->println("fail (unknown command; use `commands` for list of commands)"); + return; + } + cli->port->print("ok "); + cli->print_usage(*cmd); + cli->port->println(); + } + }, + { + .name = "END" + } +}; diff --git a/hardware/firmware/audio_board/src/cli/commands.h b/hardware/firmware/audio_board/src/cli/commands.h new file mode 100644 index 00000000..e69de29b From 98deae9898159c5d5403bf5485d825ed52d7b00f Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 17:05:48 +0100 Subject: [PATCH 24/67] proper debug logging --- hardware/firmware/audio_board/CMakeLists.txt | 1 + hardware/firmware/audio_board/src/cli/cli.h | 2 +- .../firmware/audio_board/src/cli/commands.cpp | 13 ++++ hardware/firmware/audio_board/src/main.cpp | 15 ++--- hardware/firmware/audio_board/src/taa3040.cpp | 66 +++++++++---------- 5 files changed, 51 insertions(+), 46 deletions(-) diff --git a/hardware/firmware/audio_board/CMakeLists.txt b/hardware/firmware/audio_board/CMakeLists.txt index 4b8648db..1b1d0521 100644 --- a/hardware/firmware/audio_board/CMakeLists.txt +++ b/hardware/firmware/audio_board/CMakeLists.txt @@ -117,6 +117,7 @@ set(SOURCES src/taa3040.cpp src/cli/cli.cpp src/cli/commands.cpp + src/debug.cpp ) # Build this project diff --git a/hardware/firmware/audio_board/src/cli/cli.h b/hardware/firmware/audio_board/src/cli/cli.h index a629230b..91b29b8e 100644 --- a/hardware/firmware/audio_board/src/cli/cli.h +++ b/hardware/firmware/audio_board/src/cli/cli.h @@ -15,7 +15,7 @@ class Cli { std::function callback; }; - static const uint8_t num_cmds = 15; + static const uint8_t num_cmds = 16; static const CmdDescr cmds[num_cmds + 1]; char input_buf[100]; diff --git a/hardware/firmware/audio_board/src/cli/commands.cpp b/hardware/firmware/audio_board/src/cli/commands.cpp index 63a16251..24d4982a 100644 --- a/hardware/firmware/audio_board/src/cli/commands.cpp +++ b/hardware/firmware/audio_board/src/cli/commands.cpp @@ -3,6 +3,7 @@ #include "../config.h" #include "../teensyaudio.h" #include "../helpers.h" +#include "../debug.h" const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { { @@ -281,6 +282,18 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { cli->port->println(); } }, + { + .name = "dbgtest", + .help = "print some log messages to check your debug logging", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli){ + for (uint8_t i = 0; i < 5; i++) { + debug_print("foo "); + debug_printf("bar %d\nbaz\n", i); + } + } + }, { .name = "END" } diff --git a/hardware/firmware/audio_board/src/main.cpp b/hardware/firmware/audio_board/src/main.cpp index 75ba97aa..cc573995 100644 --- a/hardware/firmware/audio_board/src/main.cpp +++ b/hardware/firmware/audio_board/src/main.cpp @@ -4,6 +4,7 @@ #include "teensyaudio.h" #include "cli/cli.h" +#include "debug.h" #ifdef USE_EEPROM @@ -28,12 +29,9 @@ ChanInfo channel_info[] = { }; void setup() { - if (CrashReport) { - // Wait until the debug interface is ready - while (!SerialUSB1 && millis() < 1500) - ; - SerialUSB1.print(CrashReport); - } + SerialUSB.begin(115200); + debug_init(); + debug_print("board ready"); #ifdef USE_DISPLAY display_setup(); @@ -41,9 +39,6 @@ void setup() { audio_load_state(); audio_setup(); - - SerialUSB.begin(115200); - SerialUSB1.println("board ready"); } Cli the_cli(&SerialUSB); @@ -75,7 +70,7 @@ void loop() { size = audio_eeprom_save_all(); last_save = millis(); - SerialUSB1.printf("eeprom: wrote %d bytes\n", size); + debug_printf("eeprom: wrote %d bytes\n", size); } #endif } diff --git a/hardware/firmware/audio_board/src/taa3040.cpp b/hardware/firmware/audio_board/src/taa3040.cpp index d5fca31e..f6813d17 100644 --- a/hardware/firmware/audio_board/src/taa3040.cpp +++ b/hardware/firmware/audio_board/src/taa3040.cpp @@ -1,5 +1,6 @@ #include "taa3040.h" +#include "debug.h" #include bool AudioControlTAA3040::enable(void) { @@ -37,82 +38,82 @@ void AudioControlTAA3040::getAsiStatus() { last_asi = raw; uint8_t ratio = raw & 0x0F; uint8_t rate = raw >> 4; - SerialUSB1.print("ASI Status: "); + debug_print("ASI Status: "); switch(ratio) { case 0: - SerialUSB1.print("ratio 16, "); + debug_print("ratio 16, "); break; case 1: - SerialUSB1.print("ratio 24, "); + debug_print("ratio 24, "); break; case 2: - SerialUSB1.print("ratio 32, "); + debug_print("ratio 32, "); break; case 3: - SerialUSB1.print("ratio 48, "); + debug_print("ratio 48, "); break; case 4: - SerialUSB1.print("ratio 64, "); + debug_print("ratio 64, "); break; case 5: - SerialUSB1.print("ratio 96, "); + debug_print("ratio 96, "); break; case 6: - SerialUSB1.print("ratio 128, "); + debug_print("ratio 128, "); break; case 7: - SerialUSB1.print("ratio 192, "); + debug_print("ratio 192, "); break; case 8: - SerialUSB1.print("ratio 256, "); + debug_print("ratio 256, "); break; case 9: - SerialUSB1.print("ratio 384, "); + debug_print("ratio 384, "); break; case 10: - SerialUSB1.print("ratio 512, "); + debug_print("ratio 512, "); break; case 11: - SerialUSB1.print("ratio 1024, "); + debug_print("ratio 1024, "); break; case 12: - SerialUSB1.print("ratio 2048, "); + debug_print("ratio 2048, "); break; case 13: case 14: - SerialUSB1.printf("ratio RESERVED(%d), ", ratio); + debug_printf("ratio RESERVED(%d), ", ratio); break; case 15: - SerialUSB1.print("ratio INVALID, "); + debug_print("ratio INVALID, "); break; } switch(rate) { case 0: - SerialUSB1.println("rate 7.35-8Khz"); + debug_println("rate 7.35-8Khz"); break; case 1: - SerialUSB1.println("rate 14.7-16Khz"); + debug_println("rate 14.7-16Khz"); break; case 2: - SerialUSB1.println("rate 22.05-24Khz"); + debug_println("rate 22.05-24Khz"); break; case 3: - SerialUSB1.println("rate 29.4-32Khz"); + debug_println("rate 29.4-32Khz"); break; case 4: - SerialUSB1.println("rate 44.1-48Khz"); + debug_println("rate 44.1-48Khz"); break; case 5: - SerialUSB1.println("rate 88.2-96Khz"); + debug_println("rate 88.2-96Khz"); break; case 6: - SerialUSB1.println("rate 176.4-192Khz"); + debug_println("rate 176.4-192Khz"); break; case 7: - SerialUSB1.println("rate 352.8-384Khz"); + debug_println("rate 352.8-384Khz"); break; case 8: - SerialUSB1.println("rate 705.6-768Khz"); + debug_println("rate 705.6-768Khz"); break; case 9: case 10: @@ -120,10 +121,10 @@ void AudioControlTAA3040::getAsiStatus() { case 12: case 13: case 14: - SerialUSB1.printf("rate RESERVED(%d)\n", rate); + debug_printf("rate RESERVED(%d)\n", rate); break; case 15: - SerialUSB1.println("rate INVALID"); + debug_println("rate INVALID"); break; } @@ -134,17 +135,12 @@ void AudioControlTAA3040::setRegister(uint8_t reg, uint8_t value) { Wire1.write(reg); Wire1.write(value); if (Wire1.endTransmission() != 0) { - SerialUSB1.println("I2C error"); + debug_println("I2C error"); } } void AudioControlTAA3040::setRegister(uint8_t page, uint8_t reg, uint8_t value) { - SerialUSB1.print("REG "); - SerialUSB1.print(page, HEX); - SerialUSB1.print(":"); - SerialUSB1.print(reg, HEX); - SerialUSB1.print(" = "); - SerialUSB1.println(value, HEX); + debug_printf("REG %02x:%02x = %02x\n", page, reg, value); if (page != currentPage) { setRegister(0, page); @@ -164,7 +160,7 @@ uint8_t AudioControlTAA3040::getRegister(uint8_t page, uint8_t reg) { Wire1.requestFrom(0x4e, 1, true); const uint8_t res = Wire.read(); if (Wire1.endTransmission() != 0) { - SerialUSB1.println("I2C error"); + debug_println("I2C error"); } return res; } From fb60fd69a87602737d8e385958a3bd0e9fcca764 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 17:10:40 +0100 Subject: [PATCH 25/67] more cli improvements --- hardware/firmware/audio_board/src/cli/cli.cpp | 8 +-- .../firmware/audio_board/src/cli/commands.cpp | 62 +++++++++---------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp index 6d5fb6f0..8d69def8 100644 --- a/hardware/firmware/audio_board/src/cli/cli.cpp +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -4,13 +4,13 @@ Cli::Cli(Stream* _port) : port(_port) {} void Cli::exec_cmd() { if (this->cmds[this->num_cmds].name == nullptr || strncmp("END", this->cmds[this->num_cmds].name, 3) != 0) { - this->port->println("fail (internal bug: num_cmds in cli.h is probably wrong)"); + this->port->println("[fail] internal bug: num_cmds in cli.h is probably wrong"); return; } const Cli::CmdDescr* cmd = this->hop_cmd(); if (!cmd) { - this->port->println("fail (unknown command; use `help` for help)"); + this->port->println("[fail] unknown command; use `help` for help"); return; } @@ -22,7 +22,7 @@ void Cli::exec_cmd() { } if (cmd->num_args >= 0 && cmd->num_args != num_spaces) { - this->port->print("fail ("); + this->port->print("[fail] "); this->print_usage(*cmd); this->port->println(")"); return; @@ -46,7 +46,7 @@ const Cli::CmdDescr* Cli::hop_cmd() { for (uint8_t i = 0; i < Cli::num_cmds; i++) { const Cli::CmdDescr& cmd = this->cmds[i]; if (cmd.name == nullptr || cmd.help == nullptr || cmd.arghelp == nullptr) { - this->port->printf("fail (internal bug: entry %d in commands.cpp is corrupted)", i); + this->port->printf("[fail] internal bug: entry %d in commands.cpp is corrupted", i); return nullptr; } diff --git a/hardware/firmware/audio_board/src/cli/commands.cpp b/hardware/firmware/audio_board/src/cli/commands.cpp index 24d4982a..2ff48f29 100644 --- a/hardware/firmware/audio_board/src/cli/commands.cpp +++ b/hardware/firmware/audio_board/src/cli/commands.cpp @@ -22,7 +22,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .num_args = 0, .callback = [](Cli* cli){ Levels &levels = audio_get_levels(); - cli->port->print("ok"); + cli->port->print("[ok]"); for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { cli->port->print(" "); cli->print_float_fixed(rmsToDb(levels.rms[i]), 3, 5); @@ -41,7 +41,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .num_args = 0, .callback = [](Cli* cli){ Levels &levels = audio_get_levels(); - cli->port->print("ok"); + cli->port->print("[ok]"); for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { cli->port->print(" "); cli->print_float_fixed(levels.rms[i], 3, 5); @@ -59,7 +59,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .arghelp = "", .num_args = 0, .callback = [](Cli* cli){ - cli->port->print("ok"); + cli->port->print("[ok]"); for (uint8_t chan = 0; chan < CHANNELS; chan++) { for (uint8_t bus = 0; bus < BUSES; bus++) { if (is_muted(chan, bus)) { @@ -79,7 +79,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .arghelp = "", .num_args = 0, .callback = [](Cli* cli){ - cli->port->print("ok"); + cli->port->print("[ok]"); for (uint8_t chan = 0; chan < CHANNELS; chan++) { cli->port->print(" "); cli->print_float_fixed(get_channel_input_gain_db(chan), 3, 3); @@ -93,7 +93,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .arghelp = "", .num_args = 0, .callback = [](Cli* cli){ - cli->port->print("ok"); + cli->port->print("[ok]"); for (uint8_t chan = 0; chan < CHANNELS; chan++) { if (is_phantom_on(chan)) { cli->port->print(" 1"); @@ -110,7 +110,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .arghelp = "", .num_args = 0, .callback = [](Cli* cli){ - cli->port->print("ok"); + cli->port->print("[ok]"); for (uint8_t bus = 0; bus < BUSES; bus++) { cli->port->print(" "); cli->print_float_fixed(get_bus_volume(bus), 3, 3); @@ -119,7 +119,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { } }, { - .name = "set-send", + .name = "send.set", .help = "for the given channel/bus crosspoint, set the send bit in the matrix", .arghelp = " (0|1)", .num_args = 3, @@ -128,11 +128,11 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { uint16_t bus = cli->hop_uint(); uint16_t want_send = cli->hop_uint(); if (chan >= CHANNELS) { - cli->port->printf("fail (chan %d is invalid)\n", chan); + cli->port->printf("[fail] chan %d is invalid\n", chan); return; } if (bus >= BUSES) { - cli->port->printf("fail (bus %d is invalid)\n", bus); + cli->port->printf("[fail] bus %d is invalid\n", bus); return; } if (want_send > 0) { @@ -140,11 +140,11 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { } else { mute(chan, bus); } - cli->port->println("ok"); + cli->port->println("[ok]"); } }, { - .name = "set-phantom", + .name = "phantom.set", .help = "for the given input channel, set the phantom power on/off bit", .arghelp = " (0|1)", .num_args = 2, @@ -152,7 +152,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { uint16_t chan = cli->hop_uint(); uint16_t want_phantom = cli->hop_uint(); if (chan >= CHANNELS) { - cli->port->printf("fail (chan %d is invalid)\n", chan); + cli->port->printf("[fail] chan %d is invalid\n", chan); return; } if (want_phantom > 0) { @@ -160,11 +160,11 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { } else { set_phantom_off(chan); } - cli->port->println("ok"); + cli->port->println("[ok]"); } }, { - .name = "set-volume", + .name = "volume.set", .help = "for the given channel/bus crosspoint, set the volume in the matrix", .arghelp = " ", .num_args = 3, @@ -174,25 +174,25 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { float vol = cli->hop_float(); if (chan >= CHANNELS) { - cli->port->printf("fail (chan %d is invalid)\n", chan); + cli->port->printf("[fail] chan %d is invalid\n", chan); return; } if (bus >= BUSES) { - cli->port->printf("fail (bus %d is invalid)\n", bus); + cli->port->printf("[fail] bus %d is invalid\n", bus); return; } if (vol < 0) { - cli->port->printf("fail (vol should not be negative)\n"); + cli->port->printf("[fail] vol should not be negative\n"); return; } set_volume(chan, bus, vol); - cli->port->println("ok"); + cli->port->println("[ok]"); }, }, { - .name = "set-in-gain", + .name = "in-gain.set", .help = "for the given input channel, set the input gain in decibels", .arghelp = " ", .num_args = 2, @@ -201,21 +201,21 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { float gain = cli->hop_float(); if (chan >= CHANNELS) { - cli->port->printf("fail (chan %d is invalid)\n", chan); + cli->port->printf("[fail] chan %d is invalid\n", chan); return; } if (gain < 0) { - cli->port->printf("fail (gain should not be negative)\n"); + cli->port->printf("[fail] gain should not be negative\n"); return; } set_channel_input_gain_db(chan, gain); - cli->port->println("ok"); + cli->port->println("[ok]"); } }, { - .name = "set-bus-volume", + .name = "bus-volume.set", .help = "for the given out bus, set its global volume to the given value", .arghelp = " ", .num_args = 2, @@ -224,17 +224,17 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { float vol = cli->hop_float(); if (bus >= BUSES) { - cli->port->printf("fail (bus %d is invalid)\n", bus); + cli->port->printf("[fail] bus %d is invalid\n", bus); return; } if (vol < 0) { - cli->port->printf("fail (vol should not be negative)\n"); + cli->port->printf("[fail] vol should not be negative\n"); return; } set_bus_volume(bus, vol); - cli->port->println("ok"); + cli->port->println("[ok]"); } }, { @@ -245,7 +245,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .callback = [](Cli* cli){ audio_reset_default_state(); audio_eeprom_save_all(); - cli->port->println("ok"); + cli->port->println("[ok]"); } }, { @@ -254,7 +254,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .arghelp = "", .num_args = 0, .callback = [](Cli* cli){ - cli->port->print("ok"); + cli->port->print("[ok]"); for (uint8_t i = 0; i < Cli::num_cmds; i++) { cli->port->print(" "); cli->port->print(Cli::cmds[i].name); @@ -269,15 +269,15 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .num_args = -1, .callback = [](Cli* cli){ if (cli->cmd[0] == '\0') { - cli->port->println("ok use `commands` to get a list of commands and then `help `"); + cli->port->println("[ok] use `commands` to get a list of commands and then `help `"); return; } const Cli::CmdDescr* cmd = cli->hop_cmd(); if (!cmd) { - cli->port->println("fail (unknown command; use `commands` for list of commands)"); + cli->port->println("[fail] unknown command; use `commands` for list of commands"); return; } - cli->port->print("ok "); + cli->port->print("[ok] "); cli->print_usage(*cmd); cli->port->println(); } From 365001e5fca6d6499c164957effebcd721e147d7 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 17:25:56 +0100 Subject: [PATCH 26/67] slugs! --- hardware/firmware/audio_board/src/cli/cli.cpp | 72 +++++++++++++++---- hardware/firmware/audio_board/src/cli/cli.h | 9 ++- .../firmware/audio_board/src/cli/commands.cpp | 64 ++++++++++------- 3 files changed, 105 insertions(+), 40 deletions(-) diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp index 8d69def8..823fe9e6 100644 --- a/hardware/firmware/audio_board/src/cli/cli.cpp +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -3,26 +3,38 @@ Cli::Cli(Stream* _port) : port(_port) {} void Cli::exec_cmd() { + uint8_t num_args = 0; + for (uint8_t i = 0; this->input_buf[i] != '\0'; i++) { + if (this->input_buf[i] == ' ') { + num_args++; + } + } + + if (this->cmd[0] >= '0' && this->cmd[0] <= '9') { + if (num_args == 0) { + this->prefix_fail(); + this->port->println("I don't speak in numbers (say `help`)"); + return; + } + this->slug = this->hop_uint(); + num_args--; + } + if (this->cmds[this->num_cmds].name == nullptr || strncmp("END", this->cmds[this->num_cmds].name, 3) != 0) { - this->port->println("[fail] internal bug: num_cmds in cli.h is probably wrong"); + this->prefix_fail(); + this->port->println("internal bug: num_cmds in cli.h is probably wrong"); return; } const Cli::CmdDescr* cmd = this->hop_cmd(); if (!cmd) { - this->port->println("[fail] unknown command; use `help` for help"); + this->prefix_fail(); + this->port->println("unknown command; use `help` for help"); return; } - uint8_t num_spaces = 0; - for (uint8_t i = 0; this->input_buf[i] != '\0'; i++) { - if (this->input_buf[i] == ' ') { - num_spaces++; - } - } - - if (cmd->num_args >= 0 && cmd->num_args != num_spaces) { - this->port->print("[fail] "); + if (cmd->num_args >= 0 && cmd->num_args != num_args) { + this->prefix_fail(); this->print_usage(*cmd); this->port->println(")"); return; @@ -46,7 +58,8 @@ const Cli::CmdDescr* Cli::hop_cmd() { for (uint8_t i = 0; i < Cli::num_cmds; i++) { const Cli::CmdDescr& cmd = this->cmds[i]; if (cmd.name == nullptr || cmd.help == nullptr || cmd.arghelp == nullptr) { - this->port->printf("[fail] internal bug: entry %d in commands.cpp is corrupted", i); + this->prefix_fail(); + this->port->printf("internal bug: entry %d in commands.cpp is corrupted", i); return nullptr; } @@ -198,7 +211,7 @@ void Cli::eat(char chr) { if (is_terminator(chr)) { this->input_pos = 0; this->input_buf[0] = '\0'; - this->port->write("fail line too long\n"); + this->port->write("[fail] line too long\n"); } return; } @@ -226,3 +239,36 @@ void Cli::update() { } } } + +void Cli::print_fail() { + if (this->slug > 0) { + this->port->printf("[%d fail]", this->slug); + this->slug = 0; + } else { + this->port->printf("[fail]"); + } +} + +void Cli::prefix_fail() { + this->print_fail(); + this->port->print(" "); +} + +void Cli::print_ok() { + if (this->slug > 0) { + this->port->printf("[%d ok]", this->slug); + this->slug = 0; + } else { + this->port->printf("[ok]"); + } +} + +void Cli::prefix_ok() { + this->print_ok(); + this->port->print(" "); +} + +void Cli::report_ok() { + this->print_ok(); + this->port->println(); +} diff --git a/hardware/firmware/audio_board/src/cli/cli.h b/hardware/firmware/audio_board/src/cli/cli.h index 91b29b8e..1d328b57 100644 --- a/hardware/firmware/audio_board/src/cli/cli.h +++ b/hardware/firmware/audio_board/src/cli/cli.h @@ -37,6 +37,13 @@ class Cli { void print_float_fixed(float x, uint8_t whole_digits, uint8_t frac_digits); void print_usage(const Cli::CmdDescr& cmd); - // state used only during exec_cmd(): char* cmd; + + uint16_t slug; + + void print_fail(); + void prefix_fail(); + void print_ok(); + void prefix_ok(); + void report_ok(); }; diff --git a/hardware/firmware/audio_board/src/cli/commands.cpp b/hardware/firmware/audio_board/src/cli/commands.cpp index 2ff48f29..1b3014c4 100644 --- a/hardware/firmware/audio_board/src/cli/commands.cpp +++ b/hardware/firmware/audio_board/src/cli/commands.cpp @@ -22,7 +22,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .num_args = 0, .callback = [](Cli* cli){ Levels &levels = audio_get_levels(); - cli->port->print("[ok]"); + cli->prefix_ok(); for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { cli->port->print(" "); cli->print_float_fixed(rmsToDb(levels.rms[i]), 3, 5); @@ -41,7 +41,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .num_args = 0, .callback = [](Cli* cli){ Levels &levels = audio_get_levels(); - cli->port->print("[ok]"); + cli->prefix_ok(); for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { cli->port->print(" "); cli->print_float_fixed(levels.rms[i], 3, 5); @@ -59,7 +59,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .arghelp = "", .num_args = 0, .callback = [](Cli* cli){ - cli->port->print("[ok]"); + cli->prefix_ok(); for (uint8_t chan = 0; chan < CHANNELS; chan++) { for (uint8_t bus = 0; bus < BUSES; bus++) { if (is_muted(chan, bus)) { @@ -79,7 +79,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .arghelp = "", .num_args = 0, .callback = [](Cli* cli){ - cli->port->print("[ok]"); + cli->prefix_ok(); for (uint8_t chan = 0; chan < CHANNELS; chan++) { cli->port->print(" "); cli->print_float_fixed(get_channel_input_gain_db(chan), 3, 3); @@ -93,7 +93,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .arghelp = "", .num_args = 0, .callback = [](Cli* cli){ - cli->port->print("[ok]"); + cli->prefix_ok(); for (uint8_t chan = 0; chan < CHANNELS; chan++) { if (is_phantom_on(chan)) { cli->port->print(" 1"); @@ -110,7 +110,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .arghelp = "", .num_args = 0, .callback = [](Cli* cli){ - cli->port->print("[ok]"); + cli->prefix_ok(); for (uint8_t bus = 0; bus < BUSES; bus++) { cli->port->print(" "); cli->print_float_fixed(get_bus_volume(bus), 3, 3); @@ -128,11 +128,13 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { uint16_t bus = cli->hop_uint(); uint16_t want_send = cli->hop_uint(); if (chan >= CHANNELS) { - cli->port->printf("[fail] chan %d is invalid\n", chan); + cli->prefix_fail(); + cli->port->printf("chan %d is invalid\n", chan); return; } if (bus >= BUSES) { - cli->port->printf("[fail] bus %d is invalid\n", bus); + cli->prefix_fail(); + cli->port->printf("bus %d is invalid\n", bus); return; } if (want_send > 0) { @@ -140,7 +142,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { } else { mute(chan, bus); } - cli->port->println("[ok]"); + cli->report_ok(); } }, { @@ -152,7 +154,8 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { uint16_t chan = cli->hop_uint(); uint16_t want_phantom = cli->hop_uint(); if (chan >= CHANNELS) { - cli->port->printf("[fail] chan %d is invalid\n", chan); + cli->prefix_fail(); + cli->port->printf("chan %d is invalid\n", chan); return; } if (want_phantom > 0) { @@ -160,7 +163,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { } else { set_phantom_off(chan); } - cli->port->println("[ok]"); + cli->report_ok(); } }, { @@ -174,21 +177,24 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { float vol = cli->hop_float(); if (chan >= CHANNELS) { - cli->port->printf("[fail] chan %d is invalid\n", chan); + cli->prefix_fail(); + cli->port->printf("chan %d is invalid\n", chan); return; } if (bus >= BUSES) { - cli->port->printf("[fail] bus %d is invalid\n", bus); + cli->prefix_fail(); + cli->port->printf("bus %d is invalid\n", bus); return; } if (vol < 0) { - cli->port->printf("[fail] vol should not be negative\n"); + cli->prefix_fail(); + cli->port->printf("vol should not be negative\n"); return; } set_volume(chan, bus, vol); - cli->port->println("[ok]"); + cli->report_ok(); }, }, { @@ -201,17 +207,19 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { float gain = cli->hop_float(); if (chan >= CHANNELS) { - cli->port->printf("[fail] chan %d is invalid\n", chan); + cli->prefix_fail(); + cli->port->printf("chan %d is invalid\n", chan); return; } if (gain < 0) { - cli->port->printf("[fail] gain should not be negative\n"); + cli->prefix_fail(); + cli->port->printf("gain should not be negative\n"); return; } set_channel_input_gain_db(chan, gain); - cli->port->println("[ok]"); + cli->report_ok(); } }, { @@ -224,17 +232,19 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { float vol = cli->hop_float(); if (bus >= BUSES) { - cli->port->printf("[fail] bus %d is invalid\n", bus); + cli->prefix_fail(); + cli->port->printf("bus %d is invalid\n", bus); return; } if (vol < 0) { - cli->port->printf("[fail] vol should not be negative\n"); + cli->prefix_fail(); + cli->port->printf("vol should not be negative\n"); return; } set_bus_volume(bus, vol); - cli->port->println("[ok]"); + cli->report_ok(); } }, { @@ -245,7 +255,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .callback = [](Cli* cli){ audio_reset_default_state(); audio_eeprom_save_all(); - cli->port->println("[ok]"); + cli->report_ok(); } }, { @@ -254,7 +264,7 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .arghelp = "", .num_args = 0, .callback = [](Cli* cli){ - cli->port->print("[ok]"); + cli->prefix_ok(); for (uint8_t i = 0; i < Cli::num_cmds; i++) { cli->port->print(" "); cli->port->print(Cli::cmds[i].name); @@ -269,15 +279,17 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { .num_args = -1, .callback = [](Cli* cli){ if (cli->cmd[0] == '\0') { - cli->port->println("[ok] use `commands` to get a list of commands and then `help `"); + cli->prefix_ok(); + cli->port->println("use `commands` to get a list of commands and then `help `; when calling a command, prefix it with a number between 1 and 65535 to use as a slug that will be printed back with the response"); return; } const Cli::CmdDescr* cmd = cli->hop_cmd(); if (!cmd) { - cli->port->println("[fail] unknown command; use `commands` for list of commands"); + cli->prefix_fail(); + cli->port->println("unknown command; use `commands` for list of commands"); return; } - cli->port->print("[ok] "); + cli->prefix_ok(); cli->print_usage(*cmd); cli->port->println(); } From c634b5134cc3d74d32204bcce0bcc762a5201b40 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 17:41:23 +0100 Subject: [PATCH 27/67] debug print infra --- hardware/firmware/audio_board/src/debug.cpp | 49 +++++++++++++++++++++ hardware/firmware/audio_board/src/debug.h | 6 +++ 2 files changed, 55 insertions(+) create mode 100644 hardware/firmware/audio_board/src/debug.cpp create mode 100644 hardware/firmware/audio_board/src/debug.h diff --git a/hardware/firmware/audio_board/src/debug.cpp b/hardware/firmware/audio_board/src/debug.cpp new file mode 100644 index 00000000..4906b890 --- /dev/null +++ b/hardware/firmware/audio_board/src/debug.cpp @@ -0,0 +1,49 @@ +#include "debug.h" + +#include +#include + +#include + +void debug_init() { + if (CrashReport) { + while (!SerialUSB && millis() < 1500); + SerialUSB.print(CrashReport); + } +} + +void debug_printf(const char* format, ...) { + char buf[256]; + va_list list; + va_start(list, format); + vsnprintf(buf, sizeof(buf), format, list); + va_end(list); + debug_print(buf); +} + +void debug_println(const char* buf) { + debug_print(buf); + debug_print("\n"); +} + +static uint16_t cur_line_idx = 0; +static char cur_line[256]; + +void debug_print_line(const char* buf) { + SerialUSB.print("[log] "); + SerialUSB.println(buf); +} + +void debug_print(const char* buf) { + for (uint8_t i = 0; buf[i] != '\0'; i++) { + char c = buf[i]; + if (c == '\r' || c == '\n') { + cur_line[cur_line_idx] = '\0'; + debug_print_line(cur_line); + cur_line_idx = 0; + } else { + cur_line[cur_line_idx] = c; + cur_line_idx++; + } + } +} diff --git a/hardware/firmware/audio_board/src/debug.h b/hardware/firmware/audio_board/src/debug.h new file mode 100644 index 00000000..212055ac --- /dev/null +++ b/hardware/firmware/audio_board/src/debug.h @@ -0,0 +1,6 @@ +#pragma once + +void debug_init(); +void debug_printf(const char* format, ...); +void debug_print(const char* buf); +void debug_println(const char* buf); From 964bdb7dfdedde1f340968e668b1c1302703f9f4 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 17:41:31 +0100 Subject: [PATCH 28/67] retrieve channels --- hardware/firmware/audio_board/CMakeLists.txt | 1 + .../firmware/audio_board/src/channels.cpp | 15 +++++++ hardware/firmware/audio_board/src/channels.h | 10 +++-- hardware/firmware/audio_board/src/cli/cli.h | 2 +- .../firmware/audio_board/src/cli/commands.cpp | 41 +++++++++++++++++++ hardware/firmware/audio_board/src/config.h | 8 +--- hardware/firmware/audio_board/src/display.cpp | 7 ++-- hardware/firmware/audio_board/src/display.h | 3 +- hardware/firmware/audio_board/src/main.cpp | 12 +----- hardware/firmware/audio_board/src/types.h | 1 + 10 files changed, 71 insertions(+), 29 deletions(-) create mode 100644 hardware/firmware/audio_board/src/channels.cpp diff --git a/hardware/firmware/audio_board/CMakeLists.txt b/hardware/firmware/audio_board/CMakeLists.txt index 1b1d0521..dd460f38 100644 --- a/hardware/firmware/audio_board/CMakeLists.txt +++ b/hardware/firmware/audio_board/CMakeLists.txt @@ -118,6 +118,7 @@ set(SOURCES src/cli/cli.cpp src/cli/commands.cpp src/debug.cpp + src/channels.cpp ) # Build this project diff --git a/hardware/firmware/audio_board/src/channels.cpp b/hardware/firmware/audio_board/src/channels.cpp new file mode 100644 index 00000000..a759bd4f --- /dev/null +++ b/hardware/firmware/audio_board/src/channels.cpp @@ -0,0 +1,15 @@ +#include "channels.h" + +static constexpr ChanInfo the_channel_info[] = { + {CHAN_WHITE, "1", "IN1", 0}, {CHAN_WHITE, "2", "IN2", 0}, + {CHAN_WHITE, "3", "IN3", 0}, {CHAN_YELLOW, "P", "PC", 0}, + {CHAN_MAGENTA, "USB", "USB1", 1}, {CHAN_MAGENTA, "USB", "USB2", 2}, + + {CHAN_WHITE, "1", "OUT1", 0}, {CHAN_WHITE, "2", "OUT2", 0}, + {CHAN_GREEN, "AFL", "HP1", 1}, {CHAN_GREEN, "AFL", "HP2", 2}, + {CHAN_MAGENTA, "USB", "USB1", 1}, {CHAN_MAGENTA, "USB", "USB2", 2}, +}; + +const ChanInfo& channel_info(uint8_t chan_id) { + return the_channel_info[chan_id]; +} diff --git a/hardware/firmware/audio_board/src/channels.h b/hardware/firmware/audio_board/src/channels.h index 16042d83..a5fe60d1 100644 --- a/hardware/firmware/audio_board/src/channels.h +++ b/hardware/firmware/audio_board/src/channels.h @@ -1,8 +1,10 @@ -#ifndef _CHANNELS_H_ -#define _CHANNELS_H_ +#pragma once #include +#define CHANNELS 6 +#define BUSES 6 + typedef struct ChanInfo { uint16_t color; char label[4]; @@ -10,11 +12,11 @@ typedef struct ChanInfo { uint8_t link; } ChanInfo; +const ChanInfo& channel_info(uint8_t chan_id); + #define RGB(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)) #define CHAN_WHITE RGB(255, 255, 255) #define CHAN_YELLOW RGB(255, 255, 80) #define CHAN_MAGENTA RGB(255, 140, 255) #define CHAN_GREEN RGB(100, 255, 100) - -#endif diff --git a/hardware/firmware/audio_board/src/cli/cli.h b/hardware/firmware/audio_board/src/cli/cli.h index 1d328b57..b470e73a 100644 --- a/hardware/firmware/audio_board/src/cli/cli.h +++ b/hardware/firmware/audio_board/src/cli/cli.h @@ -15,7 +15,7 @@ class Cli { std::function callback; }; - static const uint8_t num_cmds = 16; + static const uint8_t num_cmds = 18; static const CmdDescr cmds[num_cmds + 1]; char input_buf[100]; diff --git a/hardware/firmware/audio_board/src/cli/commands.cpp b/hardware/firmware/audio_board/src/cli/commands.cpp index 1b3014c4..db9b471a 100644 --- a/hardware/firmware/audio_board/src/cli/commands.cpp +++ b/hardware/firmware/audio_board/src/cli/commands.cpp @@ -4,6 +4,7 @@ #include "../teensyaudio.h" #include "../helpers.h" #include "../debug.h" +#include "../channels.h" const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { { @@ -15,6 +16,46 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { cli->port->printf("pong %s\n", cli->cmd); } }, + { + .name = "channel.labels", + .help = "returns a list of the short labels of inputs and buses", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli){ + cli->prefix_ok(); + cli->port->print("channels:"); + for (uint8_t i = 0; i < CHANNELS; i++) { + cli->port->print(" "); + cli->port->print(channel_info(i).label); + } + cli->port->print("; buses:"); + for (uint8_t i = 0; i < BUSES; i++) { + cli->port->print(" "); + cli->port->print(channel_info(CHANNELS + i).label); + } + cli->port->println(); + } + }, + { + .name = "channel.names", + .help = "returns a list of the names of inputs and buses", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli){ + cli->prefix_ok(); + cli->port->print("channels:"); + for (uint8_t i = 0; i < CHANNELS; i++) { + cli->port->print(" "); + cli->port->print(channel_info(i).desc); + } + cli->port->print("; buses:"); + for (uint8_t i = 0; i < BUSES; i++) { + cli->port->print(" "); + cli->port->print(channel_info(CHANNELS + i).desc); + } + cli->port->println(); + } + }, { .name = "levels.db", .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels in db", diff --git a/hardware/firmware/audio_board/src/config.h b/hardware/firmware/audio_board/src/config.h index 13c13abc..0a254706 100644 --- a/hardware/firmware/audio_board/src/config.h +++ b/hardware/firmware/audio_board/src/config.h @@ -1,8 +1,4 @@ -#ifndef _MIXER_CONFIG_H_ -#define _MIXER_CONFIG_H_ - -#define CHANNELS 6 -#define BUSES 6 +#pragma once #define SCREEN_WIDTH 80 #define SCREEN_HEIGHT 160 @@ -10,5 +6,3 @@ #define PIN_PHANTOM_IN1 37 #define PIN_PHANTOM_IN2 36 #define PIN_PHANTOM_IN3 35 - -#endif diff --git a/hardware/firmware/audio_board/src/display.cpp b/hardware/firmware/audio_board/src/display.cpp index c2b8bb0b..7cc5b699 100644 --- a/hardware/firmware/audio_board/src/display.cpp +++ b/hardware/firmware/audio_board/src/display.cpp @@ -47,7 +47,7 @@ void draw_meter(int16_t x, int16_t y, int16_t w, int16_t h, float level) { display.fillRect(x, y, w, h - red_thresh - rfill, RGB(100, 0, 0)); } -void draw_channel(float rms, int id, ChanInfo &channel_info) { +void draw_channel(float rms, int id, const ChanInfo &channel_info) { uint16_t offset = 0; if (id < CHANNELS) { } else { @@ -67,15 +67,14 @@ void draw_channel(float rms, int id, ChanInfo &channel_info) { DbtoLevel(rmsToDb(rms))); } -void display_update_vu(float levels_rms[CHANNELS + BUSES], - ChanInfo channel_info[CHANNELS + BUSES]) { +void display_update_vu(float levels_rms[CHANNELS + BUSES]) { display.fillScreen(RGB(0, 0, 0)); display.setTextSize(1); display.setTextColor(RGB(0, 0, 0)); for (int i = 0; i < 12; i++) { - draw_channel(levels_rms[i], i, channel_info[i]); + draw_channel(levels_rms[i], i, channel_info(i)); } } diff --git a/hardware/firmware/audio_board/src/display.h b/hardware/firmware/audio_board/src/display.h index f6ec2bc7..4de22621 100644 --- a/hardware/firmware/audio_board/src/display.h +++ b/hardware/firmware/audio_board/src/display.h @@ -12,8 +12,7 @@ void display_setup(); -void display_update_vu(float levels_rms[CHANNELS + BUSES], - ChanInfo channel_info[CHANNELS + BUSES]); +void display_update_vu(float levels_rms[CHANNELS + BUSES]); void display_update_screen(); diff --git a/hardware/firmware/audio_board/src/main.cpp b/hardware/firmware/audio_board/src/main.cpp index cc573995..13d36ad2 100644 --- a/hardware/firmware/audio_board/src/main.cpp +++ b/hardware/firmware/audio_board/src/main.cpp @@ -18,16 +18,6 @@ #endif -ChanInfo channel_info[] = { - {CHAN_WHITE, "1", "IN1", 0}, {CHAN_WHITE, "2", "IN2", 0}, - {CHAN_WHITE, "3", "IN3", 0}, {CHAN_YELLOW, "P", "PC", 0}, - {CHAN_MAGENTA, "USB", "USB1", 1}, {CHAN_MAGENTA, "USB", "USB2", 2}, - - {CHAN_WHITE, "1", "OUT1", 0}, {CHAN_WHITE, "2", "OUT2", 0}, - {CHAN_GREEN, "AFL", "HP1", 1}, {CHAN_GREEN, "AFL", "HP2", 2}, - {CHAN_MAGENTA, "USB", "USB1", 1}, {CHAN_MAGENTA, "USB", "USB2", 2}, -}; - void setup() { SerialUSB.begin(115200); debug_init(); @@ -54,7 +44,7 @@ void loop() { audio_update_levels(levels); #ifdef USE_DISPLAY - display_update_vu(levels.rms, channel_info); + display_update_vu(levels.rms); if (last_draw < (millis() - 16)) { display_update_screen(); diff --git a/hardware/firmware/audio_board/src/types.h b/hardware/firmware/audio_board/src/types.h index 8c172205..e2d01b93 100644 --- a/hardware/firmware/audio_board/src/types.h +++ b/hardware/firmware/audio_board/src/types.h @@ -2,6 +2,7 @@ #define _MIXER_TYPES_H_ #include "config.h" +#include "channels.h" #include typedef struct { From a75b7ae747659b7706e18b0aace89a33bd8e3b97 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 17:46:02 +0100 Subject: [PATCH 29/67] nuke the OSC stuff --- .gitmodules | 3 --- hardware/firmware/audio_board/CMakeLists.txt | 1 - hardware/firmware/audio_board/vendor/OSC | 1 - 3 files changed, 5 deletions(-) delete mode 160000 hardware/firmware/audio_board/vendor/OSC diff --git a/.gitmodules b/.gitmodules index cde6a384..9164addf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,9 +16,6 @@ [submodule "hardware/firmware/audio_board/vendor/SPI"] path = hardware/firmware/audio_board/vendor/SPI url = https://github.com/PaulStoffregen/SPI.git -[submodule "hardware/firmware/audio_board/vendor/OSC"] - path = hardware/firmware/audio_board/vendor/OSC - url = https://github.com/CNMAT/OSC [submodule "hardware/firmware/audio_board/vendor/ST7735_t3"] path = hardware/firmware/audio_board/vendor/ST7735_t3 url = https://github.com/PaulStoffregen/ST7735_t3 diff --git a/hardware/firmware/audio_board/CMakeLists.txt b/hardware/firmware/audio_board/CMakeLists.txt index dd460f38..b026b1c9 100644 --- a/hardware/firmware/audio_board/CMakeLists.txt +++ b/hardware/firmware/audio_board/CMakeLists.txt @@ -91,7 +91,6 @@ set_property(SOURCE ${VENDOR_ROOT}/Audio/memcpy_audio.S PROPERTY LANGUAGE C) #import_arduino_library(${VENDOR_ROOT} Adafruit_BusIO) #import_arduino_library(${VENDOR_ROOT} Adafruit_SSD1306) import_arduino_library(${VENDOR_ROOT} ST7735_t3/src) -import_arduino_library(${VENDOR_ROOT} OSC) import_arduino_library(${VENDOR_ROOT} SD/src) import_arduino_library(${VENDOR_ROOT} SdFat/src) import_arduino_library(${VENDOR_ROOT} SdFat/src/ExFatLib) diff --git a/hardware/firmware/audio_board/vendor/OSC b/hardware/firmware/audio_board/vendor/OSC deleted file mode 160000 index 778dd98a..00000000 --- a/hardware/firmware/audio_board/vendor/OSC +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 778dd98ab517f7a878492c8a22ba53928801e865 From 718d111121627d13743d492e5b637ff0cd7eb754 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 17:55:07 +0100 Subject: [PATCH 30/67] format code --- hardware/firmware/audio_board/.clang-format | 34 +- hardware/firmware/audio_board/format.sh | 25 + .../firmware/audio_board/src/channels.cpp | 20 +- hardware/firmware/audio_board/src/channels.h | 16 +- hardware/firmware/audio_board/src/cli/cli.cpp | 425 +++++++------ hardware/firmware/audio_board/src/cli/cli.h | 91 +-- .../firmware/audio_board/src/cli/commands.cpp | 568 ++++++++---------- hardware/firmware/audio_board/src/config.h | 4 +- hardware/firmware/audio_board/src/debug.cpp | 33 +- hardware/firmware/audio_board/src/display.cpp | 106 ++-- hardware/firmware/audio_board/src/display.h | 2 +- hardware/firmware/audio_board/src/helpers.cpp | 34 +- hardware/firmware/audio_board/src/main.cpp | 50 +- hardware/firmware/audio_board/src/storage.cpp | 36 +- hardware/firmware/audio_board/src/storage.h | 8 +- hardware/firmware/audio_board/src/taa3040.cpp | 291 +++++---- hardware/firmware/audio_board/src/taa3040.h | 67 +-- .../firmware/audio_board/src/teensyaudio.cpp | 304 +++++----- .../firmware/audio_board/src/teensyaudio.h | 14 +- .../audio_board/src/teensyaudio_defaults.cpp | 31 +- hardware/firmware/audio_board/src/types.h | 16 +- 21 files changed, 1073 insertions(+), 1102 deletions(-) create mode 100755 hardware/firmware/audio_board/format.sh diff --git a/hardware/firmware/audio_board/.clang-format b/hardware/firmware/audio_board/.clang-format index 1676b661..8ab383c2 100644 --- a/hardware/firmware/audio_board/.clang-format +++ b/hardware/firmware/audio_board/.clang-format @@ -1,8 +1,34 @@ --- BasedOnStyle: LLVM IndentWidth: 4 -ColumnLimit: 80 ---- -Language: Cpp +TabWidth: 4 +UseTab: AlignWithSpaces +BreakBeforeBraces: Attach + +InsertBraces: true + +SpacesInParentheses: false + +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: Consecutive +AlignTrailingComments: true +AlignAfterOpenBracket: BlockIndent +IndentAccessModifiers: true + DerivePointerAlignment: false -PointerAlignment: Right +PointerAlignment: Left + +PackConstructorInitializers: BinPack +BreakConstructorInitializers: AfterColon + +ColumnLimit: 0 + +SortIncludes: false + +LineEnding: LF diff --git a/hardware/firmware/audio_board/format.sh b/hardware/firmware/audio_board/format.sh new file mode 100755 index 00000000..8823f92f --- /dev/null +++ b/hardware/firmware/audio_board/format.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -euo pipefail + +cdir="$(dirname "$(readlink -f "${0}")")" + +if [[ $# -eq 1 ]]; then + dirs=( "$(readlink -f "${1}")" ) +elif [[ $# -eq 0 ]]; then + dirs=( "${cdir}"/src ) +else + echo 'what?' >&2 + exit 1 +fi + +cd "${cdir}" + +for dir in "${dirs[@]}"; do + echo "formatting in ${dir}" >&2 + find "${dir}" -type f -regextype egrep -iregex '.*\.[ch]|.*\.cpp' \ + -not -path '*/sys/*' \ + -not -path '*/build/*' \ + -not -path '*/vendor/*' \ + -exec clang-format -i -style=file '{}' \+ +done diff --git a/hardware/firmware/audio_board/src/channels.cpp b/hardware/firmware/audio_board/src/channels.cpp index a759bd4f..3f8b3756 100644 --- a/hardware/firmware/audio_board/src/channels.cpp +++ b/hardware/firmware/audio_board/src/channels.cpp @@ -1,15 +1,21 @@ #include "channels.h" static constexpr ChanInfo the_channel_info[] = { - {CHAN_WHITE, "1", "IN1", 0}, {CHAN_WHITE, "2", "IN2", 0}, - {CHAN_WHITE, "3", "IN3", 0}, {CHAN_YELLOW, "P", "PC", 0}, - {CHAN_MAGENTA, "USB", "USB1", 1}, {CHAN_MAGENTA, "USB", "USB2", 2}, + {CHAN_WHITE, "1", "IN1", 0}, + {CHAN_WHITE, "2", "IN2", 0}, + {CHAN_WHITE, "3", "IN3", 0}, + {CHAN_YELLOW, "P", "PC", 0}, + {CHAN_MAGENTA, "USB", "USB1", 1}, + {CHAN_MAGENTA, "USB", "USB2", 2}, - {CHAN_WHITE, "1", "OUT1", 0}, {CHAN_WHITE, "2", "OUT2", 0}, - {CHAN_GREEN, "AFL", "HP1", 1}, {CHAN_GREEN, "AFL", "HP2", 2}, - {CHAN_MAGENTA, "USB", "USB1", 1}, {CHAN_MAGENTA, "USB", "USB2", 2}, + {CHAN_WHITE, "1", "OUT1", 0}, + {CHAN_WHITE, "2", "OUT2", 0}, + {CHAN_GREEN, "AFL", "HP1", 1}, + {CHAN_GREEN, "AFL", "HP2", 2}, + {CHAN_MAGENTA, "USB", "USB1", 1}, + {CHAN_MAGENTA, "USB", "USB2", 2}, }; const ChanInfo& channel_info(uint8_t chan_id) { - return the_channel_info[chan_id]; + return the_channel_info[chan_id]; } diff --git a/hardware/firmware/audio_board/src/channels.h b/hardware/firmware/audio_board/src/channels.h index a5fe60d1..81707af5 100644 --- a/hardware/firmware/audio_board/src/channels.h +++ b/hardware/firmware/audio_board/src/channels.h @@ -3,20 +3,20 @@ #include #define CHANNELS 6 -#define BUSES 6 +#define BUSES 6 typedef struct ChanInfo { - uint16_t color; - char label[4]; - char desc[16]; - uint8_t link; + uint16_t color; + char label[4]; + char desc[16]; + uint8_t link; } ChanInfo; const ChanInfo& channel_info(uint8_t chan_id); #define RGB(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)) -#define CHAN_WHITE RGB(255, 255, 255) -#define CHAN_YELLOW RGB(255, 255, 80) +#define CHAN_WHITE RGB(255, 255, 255) +#define CHAN_YELLOW RGB(255, 255, 80) #define CHAN_MAGENTA RGB(255, 140, 255) -#define CHAN_GREEN RGB(100, 255, 100) +#define CHAN_GREEN RGB(100, 255, 100) diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp index 823fe9e6..b310f9d1 100644 --- a/hardware/firmware/audio_board/src/cli/cli.cpp +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -3,272 +3,271 @@ Cli::Cli(Stream* _port) : port(_port) {} void Cli::exec_cmd() { - uint8_t num_args = 0; - for (uint8_t i = 0; this->input_buf[i] != '\0'; i++) { - if (this->input_buf[i] == ' ') { - num_args++; - } - } - - if (this->cmd[0] >= '0' && this->cmd[0] <= '9') { - if (num_args == 0) { - this->prefix_fail(); - this->port->println("I don't speak in numbers (say `help`)"); - return; - } - this->slug = this->hop_uint(); - num_args--; - } - - if (this->cmds[this->num_cmds].name == nullptr || strncmp("END", this->cmds[this->num_cmds].name, 3) != 0) { - this->prefix_fail(); - this->port->println("internal bug: num_cmds in cli.h is probably wrong"); - return; - } - - const Cli::CmdDescr* cmd = this->hop_cmd(); - if (!cmd) { - this->prefix_fail(); - this->port->println("unknown command; use `help` for help"); - return; - } - - if (cmd->num_args >= 0 && cmd->num_args != num_args) { - this->prefix_fail(); - this->print_usage(*cmd); - this->port->println(")"); - return; - } - - cmd->callback(this); + uint8_t num_args = 0; + for (uint8_t i = 0; this->input_buf[i] != '\0'; i++) { + if (this->input_buf[i] == ' ') { + num_args++; + } + } + + if (this->cmd[0] >= '0' && this->cmd[0] <= '9') { + if (num_args == 0) { + this->prefix_fail(); + this->port->println("I don't speak in numbers (say `help`)"); + return; + } + this->slug = this->hop_uint(); + num_args--; + } + + if (this->cmds[this->num_cmds].name == nullptr || strncmp("END", this->cmds[this->num_cmds].name, 3) != 0) { + this->prefix_fail(); + this->port->println("internal bug: num_cmds in cli.h is probably wrong"); + return; + } + + const Cli::CmdDescr* cmd = this->hop_cmd(); + if (!cmd) { + this->prefix_fail(); + this->port->println("unknown command; use `help` for help"); + return; + } + + if (cmd->num_args >= 0 && cmd->num_args != num_args) { + this->prefix_fail(); + this->print_usage(*cmd); + this->port->println(")"); + return; + } + + cmd->callback(this); } void Cli::print_usage(const Cli::CmdDescr& cmd) { - this->port->print("usage: "); - this->port->print(cmd.name); - if (cmd.arghelp[0] != '\0') { - this->port->print(" "); - this->port->print(cmd.arghelp); - } - this->port->print(" -- "); - this->port->print(cmd.help); + this->port->print("usage: "); + this->port->print(cmd.name); + if (cmd.arghelp[0] != '\0') { + this->port->print(" "); + this->port->print(cmd.arghelp); + } + this->port->print(" -- "); + this->port->print(cmd.help); } const Cli::CmdDescr* Cli::hop_cmd() { - for (uint8_t i = 0; i < Cli::num_cmds; i++) { - const Cli::CmdDescr& cmd = this->cmds[i]; - if (cmd.name == nullptr || cmd.help == nullptr || cmd.arghelp == nullptr) { - this->prefix_fail(); - this->port->printf("internal bug: entry %d in commands.cpp is corrupted", i); - return nullptr; - } - - if (this->hop_word(cmd.name)) { - return &cmd; - } - } - return nullptr; + for (uint8_t i = 0; i < Cli::num_cmds; i++) { + const Cli::CmdDescr& cmd = this->cmds[i]; + if (cmd.name == nullptr || cmd.help == nullptr || cmd.arghelp == nullptr) { + this->prefix_fail(); + this->port->printf("internal bug: entry %d in commands.cpp is corrupted", i); + return nullptr; + } + + if (this->hop_word(cmd.name)) { + return &cmd; + } + } + return nullptr; } void Cli::print_float_fixed(float x, uint8_t whole_digits, uint8_t frac_digits) { - char buf[whole_digits + frac_digits + 3]; - - bool sign = x < 0; - if (sign) { - x = -x; - } - - buf[0] = sign ? '-' : '+'; - - float limit = 1; - for (uint8_t i = 0; i < whole_digits; i++) { - limit *= 10; - } - - if (x >= limit) { - buf[1] = 'i'; - buf[2] = 'n'; - buf[3] = 'f'; - buf[4] = '\0'; - } else { - uint32_t whole = (uint32_t)x; - float frac = x - whole; - - for (uint8_t i = whole_digits; i >= 1; i--) { - buf[i] = '0' + (whole % 10); - whole /= 10; - } - - buf[whole_digits + 1] = '.'; - - for (uint8_t i = 0; i < frac_digits; i++) { - frac *= 10; - buf[whole_digits + 2 + i] = '0' + (uint32_t)frac; - frac -= (uint32_t)frac; - } - - buf[frac_digits + whole_digits + 2] = '\0'; - } - - // FIXME: give this function to a year 1 uni student to make prettier - if (sign) { - this->port->print(buf); - } else { - this->port->print(buf + 1); - } + char buf[whole_digits + frac_digits + 3]; + + bool sign = x < 0; + if (sign) { + x = -x; + } + + buf[0] = sign ? '-' : '+'; + + float limit = 1; + for (uint8_t i = 0; i < whole_digits; i++) { + limit *= 10; + } + + if (x >= limit) { + buf[1] = 'i'; + buf[2] = 'n'; + buf[3] = 'f'; + buf[4] = '\0'; + } else { + uint32_t whole = (uint32_t)x; + float frac = x - whole; + + for (uint8_t i = whole_digits; i >= 1; i--) { + buf[i] = '0' + (whole % 10); + whole /= 10; + } + + buf[whole_digits + 1] = '.'; + + for (uint8_t i = 0; i < frac_digits; i++) { + frac *= 10; + buf[whole_digits + 2 + i] = '0' + (uint32_t)frac; + frac -= (uint32_t)frac; + } + + buf[frac_digits + whole_digits + 2] = '\0'; + } + + // FIXME: give this function to a year 1 uni student to make prettier + if (sign) { + this->port->print(buf); + } else { + this->port->print(buf + 1); + } } bool Cli::is_terminator(char c) { - return c == '\n' || c == '\r' || c == '\0'; + return c == '\n' || c == '\r' || c == '\0'; } bool Cli::is_terminator_or_whitespace(char c) { - return is_terminator(c) || c == ' ' || c == '\t'; + return is_terminator(c) || c == ' ' || c == '\t'; } void Cli::skip_whitespace_in(char** buf) { - while (**buf == ' ') { - (*buf)++; - } + while (**buf == ' ') { + (*buf)++; + } } void Cli::skip_whitespace() { - skip_whitespace_in(&this->cmd); + skip_whitespace_in(&this->cmd); } uint16_t Cli::hop_uint() { - skip_whitespace(); - uint16_t result = 0; - while (*this->cmd >= '0' && *this->cmd <= '9') { - result *= 10; - result += (*this->cmd) - '0'; - this->cmd++; - } - - if (*this->cmd == ' ') { - this->cmd++; - } - return result; + skip_whitespace(); + uint16_t result = 0; + while (*this->cmd >= '0' && *this->cmd <= '9') { + result *= 10; + result += (*this->cmd) - '0'; + this->cmd++; + } + + if (*this->cmd == ' ') { + this->cmd++; + } + return result; } float Cli::hop_float() { - skip_whitespace(); - - float sign = 1.0f; - if (*this->cmd == '-') { - sign = -1.0f; - this->cmd++; - } else if (*this->cmd == '+') { - this->cmd++; - } - - float result = 0.0f; - while (*this->cmd >= '0' && *this->cmd <= '9') { - result *= 10.0f; - result += (*this->cmd) - '0'; - this->cmd++; - } - - if (*this->cmd == '.') { - this->cmd++; - float place = 0.1f; - while (*this->cmd >= '0' && *this->cmd <= '9') { - result += ((*this->cmd) - '0') * place; - place *= 0.1f; - this->cmd++; - } - } - - if (*this->cmd == ' ') { - this->cmd++; - } - - return sign * result; + skip_whitespace(); + + float sign = 1.0f; + if (*this->cmd == '-') { + sign = -1.0f; + this->cmd++; + } else if (*this->cmd == '+') { + this->cmd++; + } + + float result = 0.0f; + while (*this->cmd >= '0' && *this->cmd <= '9') { + result *= 10.0f; + result += (*this->cmd) - '0'; + this->cmd++; + } + + if (*this->cmd == '.') { + this->cmd++; + float place = 0.1f; + while (*this->cmd >= '0' && *this->cmd <= '9') { + result += ((*this->cmd) - '0') * place; + place *= 0.1f; + this->cmd++; + } + } + + if (*this->cmd == ' ') { + this->cmd++; + } + + return sign * result; } bool Cli::hop_word(const char* word) { - char* buf = this->cmd; - skip_whitespace_in(&buf); + char* buf = this->cmd; + skip_whitespace_in(&buf); - while (*word != 0 && !is_terminator_or_whitespace(*buf) && *buf == *word) { - buf++; - word++; - } + while (*word != 0 && !is_terminator_or_whitespace(*buf) && *buf == *word) { + buf++; + word++; + } + if (*word != '\0' || !is_terminator_or_whitespace(*buf)) { + return false; + } - if (*word != '\0' || !is_terminator_or_whitespace(*buf)) { - return false; - } + skip_whitespace_in(&buf); - skip_whitespace_in(&buf); - - this->cmd = buf; - return true; + this->cmd = buf; + return true; } void Cli::eat(char chr) { - if (this->input_pos > sizeof(this->input_buf) - 2) { - if (is_terminator(chr)) { - this->input_pos = 0; - this->input_buf[0] = '\0'; - this->port->write("[fail] line too long\n"); - } - return; - } - this->input_buf[this->input_pos] = chr; - if (!is_terminator(chr)) { - this->input_pos++; - return; - } - this->input_buf[this->input_pos] = '\0'; - this->input_pos = 0; - if (this->input_buf[0] == '\0') { - return; // empty line - } - - this->cmd = &this->input_buf[0]; - - exec_cmd(); + if (this->input_pos > sizeof(this->input_buf) - 2) { + if (is_terminator(chr)) { + this->input_pos = 0; + this->input_buf[0] = '\0'; + this->port->write("[fail] line too long\n"); + } + return; + } + this->input_buf[this->input_pos] = chr; + if (!is_terminator(chr)) { + this->input_pos++; + return; + } + this->input_buf[this->input_pos] = '\0'; + this->input_pos = 0; + if (this->input_buf[0] == '\0') { + return; // empty line + } + + this->cmd = &this->input_buf[0]; + + exec_cmd(); } void Cli::update() { - while (this->port->available()) { - int c = this->port->read(); - if (c > 0) { - this->eat((char)c); - } - } + while (this->port->available()) { + int c = this->port->read(); + if (c > 0) { + this->eat((char)c); + } + } } void Cli::print_fail() { - if (this->slug > 0) { - this->port->printf("[%d fail]", this->slug); - this->slug = 0; - } else { - this->port->printf("[fail]"); - } + if (this->slug > 0) { + this->port->printf("[%d fail]", this->slug); + this->slug = 0; + } else { + this->port->printf("[fail]"); + } } void Cli::prefix_fail() { - this->print_fail(); - this->port->print(" "); + this->print_fail(); + this->port->print(" "); } void Cli::print_ok() { - if (this->slug > 0) { - this->port->printf("[%d ok]", this->slug); - this->slug = 0; - } else { - this->port->printf("[ok]"); - } + if (this->slug > 0) { + this->port->printf("[%d ok]", this->slug); + this->slug = 0; + } else { + this->port->printf("[ok]"); + } } void Cli::prefix_ok() { - this->print_ok(); - this->port->print(" "); + this->print_ok(); + this->port->print(" "); } void Cli::report_ok() { - this->print_ok(); - this->port->println(); + this->print_ok(); + this->port->println(); } diff --git a/hardware/firmware/audio_board/src/cli/cli.h b/hardware/firmware/audio_board/src/cli/cli.h index b470e73a..3c381eb6 100644 --- a/hardware/firmware/audio_board/src/cli/cli.h +++ b/hardware/firmware/audio_board/src/cli/cli.h @@ -1,49 +1,50 @@ #include class Cli { - public: - Cli(Stream*); - void update(); - private: - Stream* port; - - struct CmdDescr{ - const char* name; - const char* help; - const char* arghelp; - int32_t num_args; - std::function callback; - }; - - static const uint8_t num_cmds = 18; - static const CmdDescr cmds[num_cmds + 1]; - - char input_buf[100]; - uint8_t input_pos = 0; - - void exec_cmd(); - bool is_terminator(char c); - bool is_terminator_or_whitespace(char c); - void skip_whitespace_in(char** buf); - void skip_whitespace(); - - uint16_t hop_uint(); - float hop_float(); - bool hop_word(const char* word); - const Cli::CmdDescr* hop_cmd(); - - void eat(char chr); - - void print_float_fixed(float x, uint8_t whole_digits, uint8_t frac_digits); - void print_usage(const Cli::CmdDescr& cmd); - - char* cmd; - - uint16_t slug; - - void print_fail(); - void prefix_fail(); - void print_ok(); - void prefix_ok(); - void report_ok(); + public: + Cli(Stream*); + void update(); + + private: + Stream* port; + + struct CmdDescr { + const char* name; + const char* help; + const char* arghelp; + int32_t num_args; + std::function callback; + }; + + static const uint8_t num_cmds = 18; + static const CmdDescr cmds[num_cmds + 1]; + + char input_buf[100]; + uint8_t input_pos = 0; + + void exec_cmd(); + bool is_terminator(char c); + bool is_terminator_or_whitespace(char c); + void skip_whitespace_in(char** buf); + void skip_whitespace(); + + uint16_t hop_uint(); + float hop_float(); + bool hop_word(const char* word); + const Cli::CmdDescr* hop_cmd(); + + void eat(char chr); + + void print_float_fixed(float x, uint8_t whole_digits, uint8_t frac_digits); + void print_usage(const Cli::CmdDescr& cmd); + + char* cmd; + + uint16_t slug; + + void print_fail(); + void prefix_fail(); + void print_ok(); + void prefix_ok(); + void report_ok(); }; diff --git a/hardware/firmware/audio_board/src/cli/commands.cpp b/hardware/firmware/audio_board/src/cli/commands.cpp index db9b471a..531f333a 100644 --- a/hardware/firmware/audio_board/src/cli/commands.cpp +++ b/hardware/firmware/audio_board/src/cli/commands.cpp @@ -7,347 +7,247 @@ #include "../channels.h" const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { - { - .name = "ping", - .help = "returns `pong `", - .arghelp = "", - .num_args = -1, - .callback = [](Cli* cli){ - cli->port->printf("pong %s\n", cli->cmd); - } - }, - { - .name = "channel.labels", - .help = "returns a list of the short labels of inputs and buses", - .arghelp = "", - .num_args = 0, - .callback = [](Cli* cli){ - cli->prefix_ok(); - cli->port->print("channels:"); - for (uint8_t i = 0; i < CHANNELS; i++) { - cli->port->print(" "); - cli->port->print(channel_info(i).label); - } - cli->port->print("; buses:"); - for (uint8_t i = 0; i < BUSES; i++) { - cli->port->print(" "); - cli->port->print(channel_info(CHANNELS + i).label); - } - cli->port->println(); - } - }, - { - .name = "channel.names", - .help = "returns a list of the names of inputs and buses", - .arghelp = "", - .num_args = 0, - .callback = [](Cli* cli){ - cli->prefix_ok(); - cli->port->print("channels:"); - for (uint8_t i = 0; i < CHANNELS; i++) { - cli->port->print(" "); - cli->port->print(channel_info(i).desc); - } - cli->port->print("; buses:"); - for (uint8_t i = 0; i < BUSES; i++) { - cli->port->print(" "); - cli->port->print(channel_info(CHANNELS + i).desc); - } - cli->port->println(); - } - }, - { - .name = "levels.db", - .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels in db", - .arghelp = "", - .num_args = 0, - .callback = [](Cli* cli){ - Levels &levels = audio_get_levels(); - cli->prefix_ok(); - for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { - cli->port->print(" "); - cli->print_float_fixed(rmsToDb(levels.rms[i]), 3, 5); - cli->port->print(" "); - cli->print_float_fixed(rmsToDb(levels.peak[i]), 3, 5); - cli->port->print(" "); - cli->print_float_fixed(rmsToDb(levels.smooth[i]), 3, 5); - } - cli->port->println(); - } - }, - { - .name = "levels", - .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels", - .arghelp = "", - .num_args = 0, - .callback = [](Cli* cli){ - Levels &levels = audio_get_levels(); - cli->prefix_ok(); - for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { - cli->port->print(" "); - cli->print_float_fixed(levels.rms[i], 3, 5); - cli->port->print(" "); - cli->print_float_fixed(levels.peak[i], 3, 5); - cli->port->print(" "); - cli->print_float_fixed(levels.smooth[i], 3, 5); - } - cli->port->println(); - } - }, - { - .name = "matrix", - .help = "for each input channel x outpub bus combination, outputs an item in the form *, where send is 0/1 and volume is a float", - .arghelp = "", - .num_args = 0, - .callback = [](Cli* cli){ - cli->prefix_ok(); - for (uint8_t chan = 0; chan < CHANNELS; chan++) { - for (uint8_t bus = 0; bus < BUSES; bus++) { - if (is_muted(chan, bus)) { - cli->port->print(" 0*"); - } else { - cli->port->print(" 1*"); - } - cli->print_float_fixed(get_volume(chan, bus), 3, 3); - } - } - cli->port->println(); - } - }, - { - .name = "gains", - .help = "for each input channel, returns its gain in db", - .arghelp = "", - .num_args = 0, - .callback = [](Cli* cli){ - cli->prefix_ok(); - for (uint8_t chan = 0; chan < CHANNELS; chan++) { - cli->port->print(" "); - cli->print_float_fixed(get_channel_input_gain_db(chan), 3, 3); - } - cli->port->println(); - } - }, - { - .name = "phantoms", - .help = "for each input channel, returns its phantom power on/off status (0/1)", - .arghelp = "", - .num_args = 0, - .callback = [](Cli* cli){ - cli->prefix_ok(); - for (uint8_t chan = 0; chan < CHANNELS; chan++) { - if (is_phantom_on(chan)) { - cli->port->print(" 1"); - } else { - cli->port->print(" 0"); - } - } - cli->port->println(); - } - }, - { - .name = "bus-volumes", - .help = "for each output bus, returns its volume", - .arghelp = "", - .num_args = 0, - .callback = [](Cli* cli){ - cli->prefix_ok(); - for (uint8_t bus = 0; bus < BUSES; bus++) { - cli->port->print(" "); - cli->print_float_fixed(get_bus_volume(bus), 3, 3); - } - cli->port->println(); - } - }, - { - .name = "send.set", - .help = "for the given channel/bus crosspoint, set the send bit in the matrix", - .arghelp = " (0|1)", - .num_args = 3, - .callback = [](Cli* cli){ - uint16_t chan = cli->hop_uint(); - uint16_t bus = cli->hop_uint(); - uint16_t want_send = cli->hop_uint(); - if (chan >= CHANNELS) { - cli->prefix_fail(); - cli->port->printf("chan %d is invalid\n", chan); - return; - } - if (bus >= BUSES) { - cli->prefix_fail(); - cli->port->printf("bus %d is invalid\n", bus); - return; - } - if (want_send > 0) { - unmute(chan, bus); - } else { - mute(chan, bus); - } - cli->report_ok(); - } - }, - { - .name = "phantom.set", - .help = "for the given input channel, set the phantom power on/off bit", - .arghelp = " (0|1)", - .num_args = 2, - .callback = [](Cli* cli){ - uint16_t chan = cli->hop_uint(); - uint16_t want_phantom = cli->hop_uint(); - if (chan >= CHANNELS) { - cli->prefix_fail(); - cli->port->printf("chan %d is invalid\n", chan); - return; - } - if (want_phantom > 0) { - set_phantom_on(chan); - } else { - set_phantom_off(chan); - } - cli->report_ok(); - } - }, - { - .name = "volume.set", - .help = "for the given channel/bus crosspoint, set the volume in the matrix", - .arghelp = " ", - .num_args = 3, - .callback = [](Cli* cli){ - uint16_t chan = cli->hop_uint(); - uint16_t bus = cli->hop_uint(); - float vol = cli->hop_float(); + {.name = "ping", + .help = "returns `pong `", + .arghelp = "", + .num_args = -1, + .callback = [](Cli* cli) { + cli->port->printf("pong %s\n", cli->cmd); + }}, + {.name = "channel.labels", .help = "returns a list of the short labels of inputs and buses", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + cli->prefix_ok(); + cli->port->print("channels:"); + for (uint8_t i = 0; i < CHANNELS; i++) { + cli->port->print(" "); + cli->port->print(channel_info(i).label); + } + cli->port->print("; buses:"); + for (uint8_t i = 0; i < BUSES; i++) { + cli->port->print(" "); + cli->port->print(channel_info(CHANNELS + i).label); + } + cli->port->println(); + }}, + {.name = "channel.names", .help = "returns a list of the names of inputs and buses", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + cli->prefix_ok(); + cli->port->print("channels:"); + for (uint8_t i = 0; i < CHANNELS; i++) { + cli->port->print(" "); + cli->port->print(channel_info(i).desc); + } + cli->port->print("; buses:"); + for (uint8_t i = 0; i < BUSES; i++) { + cli->port->print(" "); + cli->port->print(channel_info(CHANNELS + i).desc); + } + cli->port->println(); + }}, + {.name = "levels.db", .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels in db", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + Levels& levels = audio_get_levels(); + cli->prefix_ok(); + for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { + cli->port->print(" "); + cli->print_float_fixed(rmsToDb(levels.rms[i]), 3, 5); + cli->port->print(" "); + cli->print_float_fixed(rmsToDb(levels.peak[i]), 3, 5); + cli->port->print(" "); + cli->print_float_fixed(rmsToDb(levels.smooth[i]), 3, 5); + } + cli->port->println(); + }}, + {.name = "levels", .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + Levels& levels = audio_get_levels(); + cli->prefix_ok(); + for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { + cli->port->print(" "); + cli->print_float_fixed(levels.rms[i], 3, 5); + cli->port->print(" "); + cli->print_float_fixed(levels.peak[i], 3, 5); + cli->port->print(" "); + cli->print_float_fixed(levels.smooth[i], 3, 5); + } + cli->port->println(); + }}, + {.name = "matrix", .help = "for each input channel x outpub bus combination, outputs an item in the form *, where send is 0/1 and volume is a float", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + cli->prefix_ok(); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + for (uint8_t bus = 0; bus < BUSES; bus++) { + if (is_muted(chan, bus)) { + cli->port->print(" 0*"); + } else { + cli->port->print(" 1*"); + } + cli->print_float_fixed(get_volume(chan, bus), 3, 3); + } + } + cli->port->println(); + }}, + {.name = "gains", .help = "for each input channel, returns its gain in db", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + cli->prefix_ok(); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + cli->port->print(" "); + cli->print_float_fixed(get_channel_input_gain_db(chan), 3, 3); + } + cli->port->println(); + }}, + {.name = "phantoms", .help = "for each input channel, returns its phantom power on/off status (0/1)", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + cli->prefix_ok(); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + if (is_phantom_on(chan)) { + cli->port->print(" 1"); + } else { + cli->port->print(" 0"); + } + } + cli->port->println(); + }}, + {.name = "bus-volumes", .help = "for each output bus, returns its volume", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + cli->prefix_ok(); + for (uint8_t bus = 0; bus < BUSES; bus++) { + cli->port->print(" "); + cli->print_float_fixed(get_bus_volume(bus), 3, 3); + } + cli->port->println(); + }}, + {.name = "send.set", .help = "for the given channel/bus crosspoint, set the send bit in the matrix", .arghelp = " (0|1)", .num_args = 3, .callback = [](Cli* cli) { + uint16_t chan = cli->hop_uint(); + uint16_t bus = cli->hop_uint(); + uint16_t want_send = cli->hop_uint(); + if (chan >= CHANNELS) { + cli->prefix_fail(); + cli->port->printf("chan %d is invalid\n", chan); + return; + } + if (bus >= BUSES) { + cli->prefix_fail(); + cli->port->printf("bus %d is invalid\n", bus); + return; + } + if (want_send > 0) { + unmute(chan, bus); + } else { + mute(chan, bus); + } + cli->report_ok(); + }}, + {.name = "phantom.set", .help = "for the given input channel, set the phantom power on/off bit", .arghelp = " (0|1)", .num_args = 2, .callback = [](Cli* cli) { + uint16_t chan = cli->hop_uint(); + uint16_t want_phantom = cli->hop_uint(); + if (chan >= CHANNELS) { + cli->prefix_fail(); + cli->port->printf("chan %d is invalid\n", chan); + return; + } + if (want_phantom > 0) { + set_phantom_on(chan); + } else { + set_phantom_off(chan); + } + cli->report_ok(); + }}, + { + .name = "volume.set", + .help = "for the given channel/bus crosspoint, set the volume in the matrix", + .arghelp = " ", + .num_args = 3, + .callback = [](Cli* cli) { + uint16_t chan = cli->hop_uint(); + uint16_t bus = cli->hop_uint(); + float vol = cli->hop_float(); - if (chan >= CHANNELS) { - cli->prefix_fail(); - cli->port->printf("chan %d is invalid\n", chan); - return; - } - if (bus >= BUSES) { - cli->prefix_fail(); - cli->port->printf("bus %d is invalid\n", bus); - return; - } - if (vol < 0) { - cli->prefix_fail(); - cli->port->printf("vol should not be negative\n"); - return; - } + if (chan >= CHANNELS) { + cli->prefix_fail(); + cli->port->printf("chan %d is invalid\n", chan); + return; + } + if (bus >= BUSES) { + cli->prefix_fail(); + cli->port->printf("bus %d is invalid\n", bus); + return; + } + if (vol < 0) { + cli->prefix_fail(); + cli->port->printf("vol should not be negative\n"); + return; + } - set_volume(chan, bus, vol); + set_volume(chan, bus, vol); - cli->report_ok(); - }, - }, - { - .name = "in-gain.set", - .help = "for the given input channel, set the input gain in decibels", - .arghelp = " ", - .num_args = 2, - .callback = [](Cli* cli){ - uint16_t chan = cli->hop_uint(); - float gain = cli->hop_float(); + cli->report_ok(); + }, + }, + {.name = "in-gain.set", .help = "for the given input channel, set the input gain in decibels", .arghelp = " ", .num_args = 2, .callback = [](Cli* cli) { + uint16_t chan = cli->hop_uint(); + float gain = cli->hop_float(); - if (chan >= CHANNELS) { - cli->prefix_fail(); - cli->port->printf("chan %d is invalid\n", chan); - return; - } - if (gain < 0) { - cli->prefix_fail(); - cli->port->printf("gain should not be negative\n"); - return; - } + if (chan >= CHANNELS) { + cli->prefix_fail(); + cli->port->printf("chan %d is invalid\n", chan); + return; + } + if (gain < 0) { + cli->prefix_fail(); + cli->port->printf("gain should not be negative\n"); + return; + } - set_channel_input_gain_db(chan, gain); + set_channel_input_gain_db(chan, gain); - cli->report_ok(); - } - }, - { - .name = "bus-volume.set", - .help = "for the given out bus, set its global volume to the given value", - .arghelp = " ", - .num_args = 2, - .callback = [](Cli* cli){ - uint16_t bus = cli->hop_uint(); - float vol = cli->hop_float(); + cli->report_ok(); + }}, + {.name = "bus-volume.set", .help = "for the given out bus, set its global volume to the given value", .arghelp = " ", .num_args = 2, .callback = [](Cli* cli) { + uint16_t bus = cli->hop_uint(); + float vol = cli->hop_float(); - if (bus >= BUSES) { - cli->prefix_fail(); - cli->port->printf("bus %d is invalid\n", bus); - return; - } - if (vol < 0) { - cli->prefix_fail(); - cli->port->printf("vol should not be negative\n"); - return; - } + if (bus >= BUSES) { + cli->prefix_fail(); + cli->port->printf("bus %d is invalid\n", bus); + return; + } + if (vol < 0) { + cli->prefix_fail(); + cli->port->printf("vol should not be negative\n"); + return; + } - set_bus_volume(bus, vol); + set_bus_volume(bus, vol); - cli->report_ok(); - } - }, - { - .name = "factory-reset", - .help = "clear all settings and state stored in the EEPROM", - .arghelp = "", - .num_args = 0, - .callback = [](Cli* cli){ - audio_reset_default_state(); - audio_eeprom_save_all(); - cli->report_ok(); - } - }, - { - .name = "commands", - .help = "get a list of available commands", - .arghelp = "", - .num_args = 0, - .callback = [](Cli* cli){ - cli->prefix_ok(); - for (uint8_t i = 0; i < Cli::num_cmds; i++) { - cli->port->print(" "); - cli->port->print(Cli::cmds[i].name); - } - cli->port->println(); - } - }, - { - .name = "help", - .help = "I suppose you think that was terribly clever.", - .arghelp = "[command]", - .num_args = -1, - .callback = [](Cli* cli){ - if (cli->cmd[0] == '\0') { - cli->prefix_ok(); - cli->port->println("use `commands` to get a list of commands and then `help `; when calling a command, prefix it with a number between 1 and 65535 to use as a slug that will be printed back with the response"); - return; - } - const Cli::CmdDescr* cmd = cli->hop_cmd(); - if (!cmd) { - cli->prefix_fail(); - cli->port->println("unknown command; use `commands` for list of commands"); - return; - } - cli->prefix_ok(); - cli->print_usage(*cmd); - cli->port->println(); - } - }, - { - .name = "dbgtest", - .help = "print some log messages to check your debug logging", - .arghelp = "", - .num_args = 0, - .callback = [](Cli* cli){ - for (uint8_t i = 0; i < 5; i++) { - debug_print("foo "); - debug_printf("bar %d\nbaz\n", i); - } - } - }, - { - .name = "END" - } + cli->report_ok(); + }}, + {.name = "factory-reset", .help = "clear all settings and state stored in the EEPROM", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + audio_reset_default_state(); + audio_eeprom_save_all(); + cli->report_ok(); + }}, + {.name = "commands", .help = "get a list of available commands", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + cli->prefix_ok(); + for (uint8_t i = 0; i < Cli::num_cmds; i++) { + cli->port->print(" "); + cli->port->print(Cli::cmds[i].name); + } + cli->port->println(); + }}, + {.name = "help", .help = "I suppose you think that was terribly clever.", .arghelp = "[command]", .num_args = -1, .callback = [](Cli* cli) { + if (cli->cmd[0] == '\0') { + cli->prefix_ok(); + cli->port->println("use `commands` to get a list of commands and then `help `; when calling a command, prefix it with a number between 1 and 65535 to use as a slug that will be printed back with the response"); + return; + } + const Cli::CmdDescr* cmd = cli->hop_cmd(); + if (!cmd) { + cli->prefix_fail(); + cli->port->println("unknown command; use `commands` for list of commands"); + return; + } + cli->prefix_ok(); + cli->print_usage(*cmd); + cli->port->println(); + }}, + {.name = "dbgtest", .help = "print some log messages to check your debug logging", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + for (uint8_t i = 0; i < 5; i++) { + debug_print("foo "); + debug_printf("bar %d\nbaz\n", i); + } + }}, + {.name = "END"} }; diff --git a/hardware/firmware/audio_board/src/config.h b/hardware/firmware/audio_board/src/config.h index 0a254706..6930bdf2 100644 --- a/hardware/firmware/audio_board/src/config.h +++ b/hardware/firmware/audio_board/src/config.h @@ -1,7 +1,7 @@ #pragma once -#define SCREEN_WIDTH 80 -#define SCREEN_HEIGHT 160 +#define SCREEN_WIDTH 80 +#define SCREEN_HEIGHT 160 #define PIN_PHANTOM_IN1 37 #define PIN_PHANTOM_IN2 36 diff --git a/hardware/firmware/audio_board/src/debug.cpp b/hardware/firmware/audio_board/src/debug.cpp index 4906b890..d988a226 100644 --- a/hardware/firmware/audio_board/src/debug.cpp +++ b/hardware/firmware/audio_board/src/debug.cpp @@ -6,10 +6,11 @@ #include void debug_init() { - if (CrashReport) { - while (!SerialUSB && millis() < 1500); - SerialUSB.print(CrashReport); - } + if (CrashReport) { + while (!SerialUSB && millis() < 1500) + ; + SerialUSB.print(CrashReport); + } } void debug_printf(const char* format, ...) { @@ -22,12 +23,12 @@ void debug_printf(const char* format, ...) { } void debug_println(const char* buf) { - debug_print(buf); - debug_print("\n"); + debug_print(buf); + debug_print("\n"); } static uint16_t cur_line_idx = 0; -static char cur_line[256]; +static char cur_line[256]; void debug_print_line(const char* buf) { SerialUSB.print("[log] "); @@ -36,14 +37,14 @@ void debug_print_line(const char* buf) { void debug_print(const char* buf) { for (uint8_t i = 0; buf[i] != '\0'; i++) { - char c = buf[i]; - if (c == '\r' || c == '\n') { - cur_line[cur_line_idx] = '\0'; - debug_print_line(cur_line); - cur_line_idx = 0; - } else { - cur_line[cur_line_idx] = c; - cur_line_idx++; - } + char c = buf[i]; + if (c == '\r' || c == '\n') { + cur_line[cur_line_idx] = '\0'; + debug_print_line(cur_line); + cur_line_idx = 0; + } else { + cur_line[cur_line_idx] = c; + cur_line_idx++; + } } } diff --git a/hardware/firmware/audio_board/src/display.cpp b/hardware/firmware/audio_board/src/display.cpp index 7cc5b699..75a6f1d6 100644 --- a/hardware/firmware/audio_board/src/display.cpp +++ b/hardware/firmware/audio_board/src/display.cpp @@ -4,78 +4,72 @@ #include -#define TFT_DC (12) -#define TFT_CS (10) -#define TFT_MOSI (11) -#define TFT_RST (24) -#define TFT_SCK (13) +#define TFT_DC (12) +#define TFT_CS (10) +#define TFT_MOSI (11) +#define TFT_RST (24) +#define TFT_SCK (13) #define SCREEN_ADDRESS 0x3C -#define OLED_RESET (-1) +#define OLED_RESET (-1) ST7735_t3 display = ST7735_t3(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCK, TFT_RST); void display_setup() { - display.initR(INITR_MINI160x80_ST7735S); - display.useFrameBuffer(true); - display.fillScreen(ST7735_RED); - display.updateScreen(); + display.initR(INITR_MINI160x80_ST7735S); + display.useFrameBuffer(true); + display.fillScreen(ST7735_RED); + display.updateScreen(); } void draw_meter(int16_t x, int16_t y, int16_t w, int16_t h, float level) { - int16_t red_thresh = h / 4 * 3; - int16_t yellow_thresh = h / 2; - int16_t value = (int16_t)(level * (float)h); - - // display.drawLine(x - 1, y, x - 1, y + h, ST7735_CYAN); - - // Green section - int gfill = max(min(value, yellow_thresh), 0); - display.fillRect(x, y + h - gfill, w, gfill, ST7735_GREEN); - display.fillRect(x, y + h - yellow_thresh, w, yellow_thresh - gfill, - RGB(0, 100, 0)); - - // Yellow section - int yfill = max(min(value, red_thresh), yellow_thresh) - yellow_thresh; - display.fillRect(x, y + h - yellow_thresh - yfill, w, yfill, ST7735_YELLOW); - display.fillRect(x, y + h - red_thresh, w, - red_thresh - yellow_thresh - yfill, RGB(100, 100, 0)); - - // Red section - int rfill = max(min(value, h), red_thresh) - red_thresh; - display.fillRect(x, y + h - red_thresh - rfill, w, rfill, ST7735_RED); - display.fillRect(x, y, w, h - red_thresh - rfill, RGB(100, 0, 0)); + int16_t red_thresh = h / 4 * 3; + int16_t yellow_thresh = h / 2; + int16_t value = (int16_t)(level * (float)h); + + // display.drawLine(x - 1, y, x - 1, y + h, ST7735_CYAN); + + // Green section + int gfill = max(min(value, yellow_thresh), 0); + display.fillRect(x, y + h - gfill, w, gfill, ST7735_GREEN); + display.fillRect(x, y + h - yellow_thresh, w, yellow_thresh - gfill, RGB(0, 100, 0)); + + // Yellow section + int yfill = max(min(value, red_thresh), yellow_thresh) - yellow_thresh; + display.fillRect(x, y + h - yellow_thresh - yfill, w, yfill, ST7735_YELLOW); + display.fillRect(x, y + h - red_thresh, w, red_thresh - yellow_thresh - yfill, RGB(100, 100, 0)); + + // Red section + int rfill = max(min(value, h), red_thresh) - red_thresh; + display.fillRect(x, y + h - red_thresh - rfill, w, rfill, ST7735_RED); + display.fillRect(x, y, w, h - red_thresh - rfill, RGB(100, 0, 0)); } -void draw_channel(float rms, int id, const ChanInfo &channel_info) { - uint16_t offset = 0; - if (id < CHANNELS) { - } else { - // Outputs - offset += (SCREEN_HEIGHT / 2); - } - uint16_t w = channel_info.link == 0 ? 12 : 24; - if (channel_info.link != 2) { - display.fillRoundRect(4 + ((id % 6) * 12), - offset + (SCREEN_HEIGHT / 2) - 11, w, 10, 1, - channel_info.color); - - display.drawString(channel_info.label, 5 + ((id % 6) * 12), - offset + (SCREEN_HEIGHT / 2) - 9); - } - draw_meter(6 + (12 * (id % 6)), offset + 1, 10, (SCREEN_HEIGHT / 2) - 13, - DbtoLevel(rmsToDb(rms))); +void draw_channel(float rms, int id, const ChanInfo& channel_info) { + uint16_t offset = 0; + if (id < CHANNELS) { + } else { + // Outputs + offset += (SCREEN_HEIGHT / 2); + } + uint16_t w = channel_info.link == 0 ? 12 : 24; + if (channel_info.link != 2) { + display.fillRoundRect(4 + ((id % 6) * 12), offset + (SCREEN_HEIGHT / 2) - 11, w, 10, 1, channel_info.color); + + display.drawString(channel_info.label, 5 + ((id % 6) * 12), offset + (SCREEN_HEIGHT / 2) - 9); + } + draw_meter(6 + (12 * (id % 6)), offset + 1, 10, (SCREEN_HEIGHT / 2) - 13, DbtoLevel(rmsToDb(rms))); } void display_update_vu(float levels_rms[CHANNELS + BUSES]) { - display.fillScreen(RGB(0, 0, 0)); + display.fillScreen(RGB(0, 0, 0)); - display.setTextSize(1); - display.setTextColor(RGB(0, 0, 0)); + display.setTextSize(1); + display.setTextColor(RGB(0, 0, 0)); - for (int i = 0; i < 12; i++) { - draw_channel(levels_rms[i], i, channel_info(i)); - } + for (int i = 0; i < 12; i++) { + draw_channel(levels_rms[i], i, channel_info(i)); + } } void display_update_screen() { display.updateScreen(); } diff --git a/hardware/firmware/audio_board/src/display.h b/hardware/firmware/audio_board/src/display.h index 4de22621..43a2a738 100644 --- a/hardware/firmware/audio_board/src/display.h +++ b/hardware/firmware/audio_board/src/display.h @@ -7,7 +7,7 @@ #include -#define SCREEN_WIDTH 80 // OLED display width, in pixels +#define SCREEN_WIDTH 80 // OLED display width, in pixels #define SCREEN_HEIGHT 160 // OLED display height, in pixels void display_setup(); diff --git a/hardware/firmware/audio_board/src/helpers.cpp b/hardware/firmware/audio_board/src/helpers.cpp index 9e314dbd..bf22c56a 100644 --- a/hardware/firmware/audio_board/src/helpers.cpp +++ b/hardware/firmware/audio_board/src/helpers.cpp @@ -8,27 +8,27 @@ float db(float vrms) { return 20.0f * log10f(vrms / 0.775f); } float vrms(float db) { return powf(10, (db / 20.0f)) * 0.775f; } float rmsToDb(float in_v) { - // 1.002 == +4 dBu - // 0.796 == +2 dBu - // 0.632 == +0 dBu - // 0.502 == -2 dBu - // 0.399 == -4 dBu - // 0.317 == -6 dBu - // 0.252 == -8 dBu - // 0.200 == -10 dBu - // 0.159 == -12 dBu - // 0.126 == -14 dBu - // 0.100 == -16 dBu - // 0.080 == -18 dBu - // 0.063 == -20 dBu - // 0.050 == -22 dBu - return db(in_v * GAIN); + // 1.002 == +4 dBu + // 0.796 == +2 dBu + // 0.632 == +0 dBu + // 0.502 == -2 dBu + // 0.399 == -4 dBu + // 0.317 == -6 dBu + // 0.252 == -8 dBu + // 0.200 == -10 dBu + // 0.159 == -12 dBu + // 0.126 == -14 dBu + // 0.100 == -16 dBu + // 0.080 == -18 dBu + // 0.063 == -20 dBu + // 0.050 == -22 dBu + return db(in_v * GAIN); } // the inverse of rmsToDb float dbToRms(float db) { return vrms(db) / GAIN; } float DbtoLevel(float db) { - float e = 2.71828f; - return 0.79306f * powf(e, db * 0.0527087f); + float e = 2.71828f; + return 0.79306f * powf(e, db * 0.0527087f); } diff --git a/hardware/firmware/audio_board/src/main.cpp b/hardware/firmware/audio_board/src/main.cpp index 13d36ad2..8ca27904 100644 --- a/hardware/firmware/audio_board/src/main.cpp +++ b/hardware/firmware/audio_board/src/main.cpp @@ -19,16 +19,16 @@ #endif void setup() { - SerialUSB.begin(115200); - debug_init(); - debug_print("board ready"); + SerialUSB.begin(115200); + debug_init(); + debug_print("board ready"); #ifdef USE_DISPLAY - display_setup(); + display_setup(); #endif - audio_load_state(); + audio_load_state(); - audio_setup(); + audio_setup(); } Cli the_cli(&SerialUSB); @@ -37,37 +37,37 @@ unsigned long last_draw = 0; unsigned long last_save = 0; void loop() { - int size; + int size; - Levels &levels = audio_get_levels(); + Levels& levels = audio_get_levels(); - audio_update_levels(levels); + audio_update_levels(levels); #ifdef USE_DISPLAY - display_update_vu(levels.rms); + display_update_vu(levels.rms); - if (last_draw < (millis() - 16)) { - display_update_screen(); - last_draw = millis(); - } + if (last_draw < (millis() - 16)) { + display_update_screen(); + last_draw = millis(); + } #endif - the_cli.update(); + the_cli.update(); #ifdef USE_EEPROM - // save to EEPROM every 60 seconds - if (last_save + 60000 < millis()) { - size = audio_eeprom_save_all(); - last_save = millis(); + // save to EEPROM every 60 seconds + if (last_save + 60000 < millis()) { + size = audio_eeprom_save_all(); + last_save = millis(); - debug_printf("eeprom: wrote %d bytes\n", size); - } + debug_printf("eeprom: wrote %d bytes\n", size); + } #endif } int main() { - setup(); - while (1) { - loop(); - } + setup(); + while (1) { + loop(); + } }; diff --git a/hardware/firmware/audio_board/src/storage.cpp b/hardware/firmware/audio_board/src/storage.cpp index 4c3ce9c2..e6312a22 100644 --- a/hardware/firmware/audio_board/src/storage.cpp +++ b/hardware/firmware/audio_board/src/storage.cpp @@ -3,30 +3,30 @@ #include "config.h" #include "storage.h" -uint8_t eeprom_update_byte(uint8_t *addr_ptr, uint8_t data) { - if (data != eeprom_read_byte(addr_ptr)) { - eeprom_write_byte(addr_ptr, data); - return 1; - } - return 0; +uint8_t eeprom_update_byte(uint8_t* addr_ptr, uint8_t data) { + if (data != eeprom_read_byte(addr_ptr)) { + eeprom_write_byte(addr_ptr, data); + return 1; + } + return 0; } // Reimplements the avr lib function, as the Teensyduino lib does not provide // a conditional write function -uint8_t eeprom_update_block(const void *buf, void *addr, uint32_t len) { - uint8_t written = 0; - uint8_t *p = (uint8_t *)addr; - const uint8_t *src = (const uint8_t *)buf; - while (len--) { - written += eeprom_update_byte(p++, *src++); - } +uint8_t eeprom_update_block(const void* buf, void* addr, uint32_t len) { + uint8_t written = 0; + uint8_t* p = (uint8_t*)addr; + const uint8_t* src = (const uint8_t*)buf; + while (len--) { + written += eeprom_update_byte(p++, *src++); + } - return written; + return written; } -uint8_t eeprom_save_all(AudioState &state, uint8_t *offset) { - return eeprom_update_block(&state, (uint8_t *)offset, sizeof(state)); +uint8_t eeprom_save_all(AudioState& state, uint8_t* offset) { + return eeprom_update_block(&state, (uint8_t*)offset, sizeof(state)); } -void eeprom_load_all(AudioState &state, uint8_t *offset) { - eeprom_read_block(&state, (uint8_t *)offset, sizeof(state)); +void eeprom_load_all(AudioState& state, uint8_t* offset) { + eeprom_read_block(&state, (uint8_t*)offset, sizeof(state)); } diff --git a/hardware/firmware/audio_board/src/storage.h b/hardware/firmware/audio_board/src/storage.h index 91eea6a0..084b3b88 100644 --- a/hardware/firmware/audio_board/src/storage.h +++ b/hardware/firmware/audio_board/src/storage.h @@ -6,9 +6,9 @@ #include "config.h" #include "types.h" -uint8_t eeprom_update_byte(uint8_t *addr_ptr, uint8_t data); -uint8_t eeprom_update_block(const void *buf, void *addr, uint32_t len); +uint8_t eeprom_update_byte(uint8_t* addr_ptr, uint8_t data); +uint8_t eeprom_update_block(const void* buf, void* addr, uint32_t len); -uint8_t eeprom_save_all(AudioState &state, uint8_t *offset); -void eeprom_load_all(AudioState &state, uint8_t *offset); +uint8_t eeprom_save_all(AudioState& state, uint8_t* offset); +void eeprom_load_all(AudioState& state, uint8_t* offset); #endif diff --git a/hardware/firmware/audio_board/src/taa3040.cpp b/hardware/firmware/audio_board/src/taa3040.cpp index f6813d17..3c65720f 100644 --- a/hardware/firmware/audio_board/src/taa3040.cpp +++ b/hardware/firmware/audio_board/src/taa3040.cpp @@ -4,173 +4,172 @@ #include bool AudioControlTAA3040::enable(void) { - setRegister(0, REG_P0_SW_RESET, 1); - delay(100); - setRegister(0, REG_P0_SW_RESET, 0); - delay(100); - setRegister(0, REG_P0_SLEEP_CFG, bit(0) | bit(7)); // Wake from sleep, use internal AREG generator - delay(10); - setRegister(0, REG_P0_ASI_OUT_CH_EN, 0xF0); // Enable ASI output channel 0-3 - configASI0(0, 0, 0, 0, 0, 1); + setRegister(0, REG_P0_SW_RESET, 1); + delay(100); + setRegister(0, REG_P0_SW_RESET, 0); + delay(100); + setRegister(0, REG_P0_SLEEP_CFG, bit(0) | bit(7)); // Wake from sleep, use internal AREG generator + delay(10); + setRegister(0, REG_P0_ASI_OUT_CH_EN, 0xF0); // Enable ASI output channel 0-3 + configASI0(0, 0, 0, 0, 0, 1); - setRegister(0, REG_P0_PWR_CFG, 0xE0); // Power on PLL and ADC - delay(10); - setRegister(0, REG_P0_MST_CFG0, bit(3)); // 44.1Khz mode, TDM slave - return true; + setRegister(0, REG_P0_PWR_CFG, 0xE0); // Power on PLL and ADC + delay(10); + setRegister(0, REG_P0_MST_CFG0, bit(3)); // 44.1Khz mode, TDM slave + return true; } bool AudioControlTAA3040::disable(void) { - return true; + return true; } bool AudioControlTAA3040::gain(uint8_t channel, uint8_t gain, uint8_t impedance, uint8_t mode, uint8_t coupling) { - uint8_t offset = channel * 5; - setRegister(REG_P0_CH1_CFG0+offset, (impedance << 2)|(mode << 5)|(coupling<<4)); - setRegister(REG_P0_CH1_CFG1+offset, gain << 2); - return true; + uint8_t offset = channel * 5; + setRegister(REG_P0_CH1_CFG0 + offset, (impedance << 2) | (mode << 5) | (coupling << 4)); + setRegister(REG_P0_CH1_CFG1 + offset, gain << 2); + return true; } void AudioControlTAA3040::getAsiStatus() { - uint8_t raw = getRegister(0, REG_P0_ASI_STS); - if (raw == last_asi) { - return; - } - last_asi = raw; - uint8_t ratio = raw & 0x0F; - uint8_t rate = raw >> 4; - debug_print("ASI Status: "); - switch(ratio) { - case 0: - debug_print("ratio 16, "); - break; - case 1: - debug_print("ratio 24, "); - break; - case 2: - debug_print("ratio 32, "); - break; - case 3: - debug_print("ratio 48, "); - break; - case 4: - debug_print("ratio 64, "); - break; - case 5: - debug_print("ratio 96, "); - break; - case 6: - debug_print("ratio 128, "); - break; - case 7: - debug_print("ratio 192, "); - break; - case 8: - debug_print("ratio 256, "); - break; - case 9: - debug_print("ratio 384, "); - break; - case 10: - debug_print("ratio 512, "); - break; - case 11: - debug_print("ratio 1024, "); - break; - case 12: - debug_print("ratio 2048, "); - break; - case 13: - case 14: - debug_printf("ratio RESERVED(%d), ", ratio); - break; - case 15: - debug_print("ratio INVALID, "); - break; - } - switch(rate) { - case 0: - debug_println("rate 7.35-8Khz"); - break; - case 1: - debug_println("rate 14.7-16Khz"); - break; - case 2: - debug_println("rate 22.05-24Khz"); - break; - case 3: - debug_println("rate 29.4-32Khz"); - break; - case 4: - debug_println("rate 44.1-48Khz"); - break; - case 5: - debug_println("rate 88.2-96Khz"); - break; - case 6: - debug_println("rate 176.4-192Khz"); - break; - case 7: - debug_println("rate 352.8-384Khz"); - break; - case 8: - debug_println("rate 705.6-768Khz"); - break; - case 9: - case 10: - case 11: - case 12: - case 13: - case 14: - debug_printf("rate RESERVED(%d)\n", rate); - break; - case 15: - debug_println("rate INVALID"); - break; - } - + uint8_t raw = getRegister(0, REG_P0_ASI_STS); + if (raw == last_asi) { + return; + } + last_asi = raw; + uint8_t ratio = raw & 0x0F; + uint8_t rate = raw >> 4; + debug_print("ASI Status: "); + switch (ratio) { + case 0: + debug_print("ratio 16, "); + break; + case 1: + debug_print("ratio 24, "); + break; + case 2: + debug_print("ratio 32, "); + break; + case 3: + debug_print("ratio 48, "); + break; + case 4: + debug_print("ratio 64, "); + break; + case 5: + debug_print("ratio 96, "); + break; + case 6: + debug_print("ratio 128, "); + break; + case 7: + debug_print("ratio 192, "); + break; + case 8: + debug_print("ratio 256, "); + break; + case 9: + debug_print("ratio 384, "); + break; + case 10: + debug_print("ratio 512, "); + break; + case 11: + debug_print("ratio 1024, "); + break; + case 12: + debug_print("ratio 2048, "); + break; + case 13: + case 14: + debug_printf("ratio RESERVED(%d), ", ratio); + break; + case 15: + debug_print("ratio INVALID, "); + break; + } + switch (rate) { + case 0: + debug_println("rate 7.35-8Khz"); + break; + case 1: + debug_println("rate 14.7-16Khz"); + break; + case 2: + debug_println("rate 22.05-24Khz"); + break; + case 3: + debug_println("rate 29.4-32Khz"); + break; + case 4: + debug_println("rate 44.1-48Khz"); + break; + case 5: + debug_println("rate 88.2-96Khz"); + break; + case 6: + debug_println("rate 176.4-192Khz"); + break; + case 7: + debug_println("rate 352.8-384Khz"); + break; + case 8: + debug_println("rate 705.6-768Khz"); + break; + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + debug_printf("rate RESERVED(%d)\n", rate); + break; + case 15: + debug_println("rate INVALID"); + break; + } } void AudioControlTAA3040::setRegister(uint8_t reg, uint8_t value) { - Wire1.beginTransmission(0x4e); - Wire1.write(reg); - Wire1.write(value); - if (Wire1.endTransmission() != 0) { - debug_println("I2C error"); - } + Wire1.beginTransmission(0x4e); + Wire1.write(reg); + Wire1.write(value); + if (Wire1.endTransmission() != 0) { + debug_println("I2C error"); + } } void AudioControlTAA3040::setRegister(uint8_t page, uint8_t reg, uint8_t value) { - debug_printf("REG %02x:%02x = %02x\n", page, reg, value); + debug_printf("REG %02x:%02x = %02x\n", page, reg, value); - if (page != currentPage) { - setRegister(0, page); - currentPage = page; - } - setRegister(reg, value); + if (page != currentPage) { + setRegister(0, page); + currentPage = page; + } + setRegister(reg, value); } uint8_t AudioControlTAA3040::getRegister(uint8_t page, uint8_t reg) { - if (page != currentPage) { - setRegister(0, page); - currentPage = page; - } - Wire1.beginTransmission(0x4e); - Wire1.write(reg); - Wire1.endTransmission(false); - Wire1.requestFrom(0x4e, 1, true); - const uint8_t res = Wire.read(); - if (Wire1.endTransmission() != 0) { - debug_println("I2C error"); - } - return res; + if (page != currentPage) { + setRegister(0, page); + currentPage = page; + } + Wire1.beginTransmission(0x4e); + Wire1.write(reg); + Wire1.endTransmission(false); + Wire1.requestFrom(0x4e, 1, true); + const uint8_t res = Wire.read(); + if (Wire1.endTransmission() != 0) { + debug_println("I2C error"); + } + return res; } void AudioControlTAA3040::configASI0(uint8_t format, uint8_t wlen, uint8_t fsync_pol, uint8_t bclk_pol, uint8_t tx_edge, uint8_t tx_fill) { - uint8_t val = tx_fill; - val |= (tx_edge & 1) << 1; - val |= (bclk_pol & 1) << 2; - val |= (fsync_pol & 1) << 3; - val |= (wlen & 3) << 4; - val |= (format & 3) << 6; - setRegister(0, REG_P0_ASI_CFG0, val); + uint8_t val = tx_fill; + val |= (tx_edge & 1) << 1; + val |= (bclk_pol & 1) << 2; + val |= (fsync_pol & 1) << 3; + val |= (wlen & 3) << 4; + val |= (format & 3) << 6; + setRegister(0, REG_P0_ASI_CFG0, val); } diff --git a/hardware/firmware/audio_board/src/taa3040.h b/hardware/firmware/audio_board/src/taa3040.h index 89ed5968..b6265c31 100644 --- a/hardware/firmware/audio_board/src/taa3040.h +++ b/hardware/firmware/audio_board/src/taa3040.h @@ -2,41 +2,40 @@ #define TEENSY_AUDIO_TAA3040_H #include -#define REG_P0_SW_RESET (0x01) -#define REG_P0_SLEEP_CFG (0x02) -#define REG_P0_ASI_CFG0 (0x07) -#define REG_P0_ASI_CFG1 (0x08) -#define REG_P0_MST_CFG0 (0x13) -#define REG_P0_ASI_STS (0x15) +#define REG_P0_SW_RESET (0x01) +#define REG_P0_SLEEP_CFG (0x02) +#define REG_P0_ASI_CFG0 (0x07) +#define REG_P0_ASI_CFG1 (0x08) +#define REG_P0_MST_CFG0 (0x13) +#define REG_P0_ASI_STS (0x15) #define REG_P0_ASI_OUT_CH_EN (0x74) -#define REG_P0_PWR_CFG (0x75) -#define REG_P0_DEV_STS1 (0x77) -#define REG_P0_CH1_CFG0 (0x3C) -#define REG_P0_CH1_CFG1 (0x3D) - -#define IMPEDANCE_2k5 (0b00) -#define IMPEDANCE_10k (0b01) -#define IMPEDANCE_20k (0b10) - -class AudioControlTAA3040 -{ - public: - bool enable(); - bool disable(); - bool gain(uint8_t channel, uint8_t gain, uint8_t impedance,uint8_t mode, uint8_t coupling); - - void getAsiStatus(); - -private: - void setRegister(uint8_t reg, uint8_t value); - void setRegister(uint8_t page, uint8_t reg, uint8_t value); - uint8_t getRegister(uint8_t page, uint8_t reg); - - void configASI0(uint8_t format, uint8_t wlen, uint8_t fsync_pol, uint8_t bclk_pol, uint8_t tx_edge, uint8_t tx_fill); - - uint8_t currentPage = 0; - uint8_t last_sts1 = 0; - uint8_t last_asi = 0; +#define REG_P0_PWR_CFG (0x75) +#define REG_P0_DEV_STS1 (0x77) +#define REG_P0_CH1_CFG0 (0x3C) +#define REG_P0_CH1_CFG1 (0x3D) + +#define IMPEDANCE_2k5 (0b00) +#define IMPEDANCE_10k (0b01) +#define IMPEDANCE_20k (0b10) + +class AudioControlTAA3040 { + public: + bool enable(); + bool disable(); + bool gain(uint8_t channel, uint8_t gain, uint8_t impedance, uint8_t mode, uint8_t coupling); + + void getAsiStatus(); + + private: + void setRegister(uint8_t reg, uint8_t value); + void setRegister(uint8_t page, uint8_t reg, uint8_t value); + uint8_t getRegister(uint8_t page, uint8_t reg); + + void configASI0(uint8_t format, uint8_t wlen, uint8_t fsync_pol, uint8_t bclk_pol, uint8_t tx_edge, uint8_t tx_fill); + + uint8_t currentPage = 0; + uint8_t last_sts1 = 0; + uint8_t last_asi = 0; }; #endif // TEENSY_AUDIO_TAA3040_H diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index f0c13e4e..23581b64 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -18,257 +18,279 @@ AudioControlTAA3040 taa3040; -AudioMixer4 *matrix[6][2] = { - {&mixer1, &mixer2}, {&mixer4, &mixer5}, {&mixer7, &mixer8}, - {&mixer10, &mixer11}, {&mixer13, &mixer14}, {&mixer16, &mixer17}, +AudioMixer4* matrix[6][2] = { + {&mixer1, &mixer2}, + {&mixer4, &mixer5}, + {&mixer7, &mixer8}, + {&mixer10, &mixer11}, + {&mixer13, &mixer14}, + {&mixer16, &mixer17}, }; -AudioAnalyzeRMS *ent_rms[12] = { - &rms1, &rms2, &rms3, &rms4, &rms5, &rms6, - &rms7, &rms8, &rms9, &rms10, &rms11, &rms12, +AudioAnalyzeRMS* ent_rms[12] = { + &rms1, + &rms2, + &rms3, + &rms4, + &rms5, + &rms6, + &rms7, + &rms8, + &rms9, + &rms10, + &rms11, + &rms12, }; -AudioAnalyzePeak *ent_peak[12] = { - &peak1, &peak2, &peak3, &peak4, &peak5, &peak6, - &peak7, &peak8, &peak9, &peak10, &peak11, &peak12, +AudioAnalyzePeak* ent_peak[12] = { + &peak1, + &peak2, + &peak3, + &peak4, + &peak5, + &peak6, + &peak7, + &peak8, + &peak9, + &peak10, + &peak11, + &peak12, }; -Levels levels; +Levels levels; AudioState state; void audio_setup() { - AudioMemory(80); + AudioMemory(80); - Wire.begin(); - Wire1.begin(); + Wire.begin(); + Wire1.begin(); - pinMode(PIN_PHANTOM_IN1, OUTPUT); - pinMode(PIN_PHANTOM_IN2, OUTPUT); - pinMode(PIN_PHANTOM_IN3, OUTPUT); + pinMode(PIN_PHANTOM_IN1, OUTPUT); + pinMode(PIN_PHANTOM_IN2, OUTPUT); + pinMode(PIN_PHANTOM_IN3, OUTPUT); - taa3040.enable(); + taa3040.enable(); } -void audio_update_levels(Levels &levels) { - float temp; - if (rms1.available()) { - for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { - temp = ent_rms[i]->read(); - levels.smooth[i] = ((levels.smooth[i] * 9) + temp) / 10; - temp = levels.smooth[i]; - // VU meter drains slowly after a peak - if (temp < levels.rms[i]) { - levels.rms[i] *= 0.97; - } else { - levels.rms[i] = temp; - } - levels.peak[i] = ent_peak[i]->read(); - } - - taa3040.getAsiStatus(); - } +void audio_update_levels(Levels& levels) { + float temp; + if (rms1.available()) { + for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { + temp = ent_rms[i]->read(); + levels.smooth[i] = ((levels.smooth[i] * 9) + temp) / 10; + temp = levels.smooth[i]; + // VU meter drains slowly after a peak + if (temp < levels.rms[i]) { + levels.rms[i] *= 0.97; + } else { + levels.rms[i] = temp; + } + levels.peak[i] = ent_peak[i]->read(); + } + + taa3040.getAsiStatus(); + } } -Levels &audio_get_levels() { return levels; } +Levels& audio_get_levels() { return levels; } void raw_set_crosspoint(uint8_t channel, uint8_t bus, float volume) { - matrix[bus][channel / 4]->gain(channel % 4, volume); + matrix[bus][channel / 4]->gain(channel % 4, volume); } float raw_get_crosspoint(uint8_t channel, uint8_t bus) { - return matrix[bus][channel / 4]->getGain(channel % 4); + return matrix[bus][channel / 4]->getGain(channel % 4); } uint64_t mute_mask(uint64_t channel, uint64_t bus) { - return (uint64_t)1 << (uint64_t)((channel * CHANNELS) + bus); + return (uint64_t)1 << (uint64_t)((channel * CHANNELS) + bus); } void apply_phantom(uint8_t channel) { - bool is_on = (state.phantoms & (1 << channel)) > 0; - switch (channel) { - case 0: - digitalWrite(PIN_PHANTOM_IN1, is_on ? HIGH : LOW); - break; - case 1: - digitalWrite(PIN_PHANTOM_IN2, is_on ? HIGH : LOW); - break; - case 2: - digitalWrite(PIN_PHANTOM_IN3, is_on ? HIGH : LOW); - break; - } + bool is_on = (state.phantoms & (1 << channel)) > 0; + switch (channel) { + case 0: + digitalWrite(PIN_PHANTOM_IN1, is_on ? HIGH : LOW); + break; + case 1: + digitalWrite(PIN_PHANTOM_IN2, is_on ? HIGH : LOW); + break; + case 2: + digitalWrite(PIN_PHANTOM_IN3, is_on ? HIGH : LOW); + break; + } } bool is_phantom_on(uint8_t channel) { - return !!(state.phantoms & (1 << channel)); + return !!(state.phantoms & (1 << channel)); } void set_phantom_on(uint8_t channel) { - if (channel > 2) { - return; - } - state.phantoms |= (1 << channel); - apply_phantom(channel); + if (channel > 2) { + return; + } + state.phantoms |= (1 << channel); + apply_phantom(channel); } void set_phantom_off(uint8_t channel) { - if (channel > 2) { - return; - } - state.phantoms &= ~(1 << channel); - apply_phantom(channel); + if (channel > 2) { + return; + } + state.phantoms &= ~(1 << channel); + apply_phantom(channel); } bool is_muted(uint8_t channel, uint8_t bus) { - return !!(state.mutes & mute_mask(channel, bus)); + return !!(state.mutes & mute_mask(channel, bus)); } float calc_real_volume(uint8_t channel, uint8_t bus, float volume) { - return volume * !is_muted(channel, bus) * state.bus_volumes[bus]; + return volume * !is_muted(channel, bus) * state.bus_volumes[bus]; } void apply_volume(uint8_t channel, uint8_t bus) { - float volume = state.matrix[channel][bus]; - raw_set_crosspoint(channel, bus, calc_real_volume(channel, bus, volume)); + float volume = state.matrix[channel][bus]; + raw_set_crosspoint(channel, bus, calc_real_volume(channel, bus, volume)); } void set_volume(uint8_t channel, uint8_t bus, float volume) { - state.matrix[channel][bus] = volume; - apply_volume(channel, bus); + state.matrix[channel][bus] = volume; + apply_volume(channel, bus); } float get_volume(uint8_t channel, uint8_t bus) { return state.matrix[channel][bus]; } void mute(uint8_t channel, uint8_t bus) { - state.mutes |= mute_mask(channel, bus); - apply_volume(channel, bus); + state.mutes |= mute_mask(channel, bus); + apply_volume(channel, bus); } void unmute(uint8_t channel, uint8_t bus) { - state.mutes &= ~mute_mask(channel, bus); - apply_volume(channel, bus); + state.mutes &= ~mute_mask(channel, bus); + apply_volume(channel, bus); } void set_bus_volume(uint8_t bus, float vol) { - state.bus_volumes[bus] = vol; - for (uint8_t channel = 0; channel < CHANNELS; ++channel) { - apply_volume(channel, bus); - } + state.bus_volumes[bus] = vol; + for (uint8_t channel = 0; channel < CHANNELS; ++channel) { + apply_volume(channel, bus); + } } float get_bus_volume(uint8_t bus) { return state.bus_volumes[bus]; } void set_channel_input_gain_db(uint8_t channel, float gain) { - uint8_t whole_gain = (uint8_t)(gain + 0.5); - if (gain < 1) { - whole_gain = 1; - } - if (gain > 42) { - whole_gain = 42; - } - - state.channel_input_gains[channel] = (float)whole_gain; - taa3040.gain(channel, whole_gain, IMPEDANCE_10k, 0, 0); + uint8_t whole_gain = (uint8_t)(gain + 0.5); + if (gain < 1) { + whole_gain = 1; + } + if (gain > 42) { + whole_gain = 42; + } + + state.channel_input_gains[channel] = (float)whole_gain; + taa3040.gain(channel, whole_gain, IMPEDANCE_10k, 0, 0); } void apply_channel_input_gain(uint8_t channel) { - set_channel_input_gain_db(channel, state.channel_input_gains[channel]); + set_channel_input_gain_db(channel, state.channel_input_gains[channel]); } float get_channel_input_gain_db(uint8_t channel) { - return state.channel_input_gains[channel]; + return state.channel_input_gains[channel]; } void reset_matrix() { memcpy(state.matrix, default_matrix, sizeof(state.matrix)); } void reset_mutes() { - memcpy(&state.mutes, &default_mutes, sizeof(state.mutes)); + memcpy(&state.mutes, &default_mutes, sizeof(state.mutes)); } void reset_phantoms() { - memcpy(&state.phantoms, &default_phantoms, sizeof(state.phantoms)); + memcpy(&state.phantoms, &default_phantoms, sizeof(state.phantoms)); } void reset_bus_volumes() { - memcpy(state.bus_volumes, default_bus_volumes, - BUSES * sizeof(float)); + memcpy(state.bus_volumes, default_bus_volumes, BUSES * sizeof(float)); } void reset_channel_input_gains() { - memcpy(state.channel_input_gains, default_channel_input_gains_db, - CHANNELS * sizeof(float)); + memcpy(state.channel_input_gains, default_channel_input_gains_db, CHANNELS * sizeof(float)); } void apply_all() { - uint8_t i, j; - for (i = 0; i < CHANNELS; ++i) { - apply_channel_input_gain(i); - apply_phantom(i); - for (j = 0; j < BUSES; ++j) { - apply_volume(i, j); - } - } + uint8_t i, j; + for (i = 0; i < CHANNELS; ++i) { + apply_channel_input_gain(i); + apply_phantom(i); + for (j = 0; j < BUSES; ++j) { + apply_volume(i, j); + } + } } void audio_reset_default_state() { - reset_matrix(); - reset_mutes(); - reset_phantoms(); - reset_bus_volumes(); - reset_channel_input_gains(); + reset_matrix(); + reset_mutes(); + reset_phantoms(); + reset_bus_volumes(); + reset_channel_input_gains(); } bool matrix_ok() { - uint8_t i, j; - for (i = 0; i < CHANNELS; ++i) { - for (j = 0; j < BUSES; ++j) { - if (isnan(state.matrix[i][j])) { - return false; - } - } - } - - return true; + uint8_t i, j; + for (i = 0; i < CHANNELS; ++i) { + for (j = 0; j < BUSES; ++j) { + if (isnan(state.matrix[i][j])) { + return false; + } + } + } + + return true; } bool bus_volumes_ok() { - uint8_t i; - for (i = 0; i < BUSES; ++i) { - if (isnan(state.bus_volumes[i])) { - return false; - } - } - - return true; + uint8_t i; + for (i = 0; i < BUSES; ++i) { + if (isnan(state.bus_volumes[i])) { + return false; + } + } + + return true; } bool channel_input_gains_ok() { - uint8_t i; - for (i = 0; i < CHANNELS; ++i) { - if (isnan(state.channel_input_gains[i])) { - return false; - } - } - - return true; + uint8_t i; + for (i = 0; i < CHANNELS; ++i) { + if (isnan(state.channel_input_gains[i])) { + return false; + } + } + + return true; } #ifdef USE_EEPROM uint8_t audio_eeprom_save_all() { - return eeprom_save_all(state, STATE_EEPROM_OFFSET); + return eeprom_save_all(state, STATE_EEPROM_OFFSET); } #endif void audio_load_state() { #ifdef USE_EEPROM - eeprom_load_all(state, STATE_EEPROM_OFFSET); + eeprom_load_all(state, STATE_EEPROM_OFFSET); - if (!matrix_ok() || !bus_volumes_ok() || !channel_input_gains_ok()) { - audio_reset_default_state(); - audio_eeprom_save_all(); - } + if (!matrix_ok() || !bus_volumes_ok() || !channel_input_gains_ok()) { + audio_reset_default_state(); + audio_eeprom_save_all(); + } #else - audio_reset_default_state(); + audio_reset_default_state(); #endif - apply_all(); + apply_all(); } diff --git a/hardware/firmware/audio_board/src/teensyaudio.h b/hardware/firmware/audio_board/src/teensyaudio.h index a1e0be19..09a1317e 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.h +++ b/hardware/firmware/audio_board/src/teensyaudio.h @@ -10,28 +10,26 @@ void audio_setup(); void audio_load_state(); -void update_levels(float levels_smooth[CHANNELS + BUSES], - float levels_rms[CHANNELS + BUSES], - float levels_peak[CHANNELS + BUSES]); +void update_levels(float levels_smooth[CHANNELS + BUSES], float levels_rms[CHANNELS + BUSES], float levels_peak[CHANNELS + BUSES]); bool is_muted(uint8_t channel, uint8_t bus); void mute(uint8_t channel, uint8_t bus); void unmute(uint8_t channel, uint8_t bus); -void set_volume(uint8_t channel, uint8_t bus, float gain); +void set_volume(uint8_t channel, uint8_t bus, float gain); float get_volume(uint8_t channel, uint8_t bus); float get_bus_volume(uint8_t bus); -void set_bus_volume(uint8_t bus, float volume); +void set_bus_volume(uint8_t bus, float volume); float get_channel_input_gain_db(uint8_t channel); -void set_channel_input_gain_db(uint8_t channel, float gain); +void set_channel_input_gain_db(uint8_t channel, float gain); // void raw_set_crosspoint(uint8_t channel, uint8_t bus, float gain); float raw_get_crosspoint(uint8_t channel, uint8_t bus); -void audio_update_levels(Levels &levels); -Levels &audio_get_levels(); +void audio_update_levels(Levels& levels); +Levels& audio_get_levels(); void audio_reset_default_state(); diff --git a/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp b/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp index 73819c11..f3195ee1 100644 --- a/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp @@ -1,11 +1,11 @@ #define MIC (5.0f) const PROGMEM float default_bus_volumes[BUSES] = { - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; const PROGMEM float default_channel_input_gains_db[CHANNELS] = { - MIC, MIC, MIC, 1.0f, 1.0f, 1.0f + MIC, MIC, MIC, 1.0f, 1.0f, 1.0f }; // see helpers/generate_mutes.py const PROGMEM uint64_t default_mutes = 52361428992; @@ -13,16 +13,17 @@ const PROGMEM uint64_t default_mutes = 52361428992; const PROGMEM uint16_t default_phantoms = 0; const PROGMEM float default_matrix[CHANNELS][BUSES] = { - // outputs: OUT1, OUT2, HP1, HP2, USB1, USB2 - // IN1 - {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, - // IN2 - {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, - // IN3 - {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, - // PC - {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}, - // USB1 - {1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f}, - // USB2 - {1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f}}; + // outputs: OUT1, OUT2, HP1, HP2, USB1, USB2 + // IN1 + {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, + // IN2 + {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, + // IN3 + {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, + // PC + {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}, + // USB1 + {1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f}, + // USB2 + {1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f} +}; diff --git a/hardware/firmware/audio_board/src/types.h b/hardware/firmware/audio_board/src/types.h index e2d01b93..c3b15943 100644 --- a/hardware/firmware/audio_board/src/types.h +++ b/hardware/firmware/audio_board/src/types.h @@ -6,19 +6,19 @@ #include typedef struct { - float matrix[CHANNELS][BUSES]; + float matrix[CHANNELS][BUSES]; - uint64_t mutes; - uint16_t phantoms; + uint64_t mutes; + uint16_t phantoms; - float channel_input_gains[CHANNELS]; - float bus_volumes[BUSES]; + float channel_input_gains[CHANNELS]; + float bus_volumes[BUSES]; } AudioState; typedef struct { - float smooth[CHANNELS + BUSES]; - float peak[CHANNELS + BUSES]; - float rms[CHANNELS + BUSES]; + float smooth[CHANNELS + BUSES]; + float peak[CHANNELS + BUSES]; + float rms[CHANNELS + BUSES]; } Levels; #endif From b2e5fe62ef10161d26dc01c102162c7669435dea Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 17:56:41 +0100 Subject: [PATCH 31/67] line length optimisation --- hardware/firmware/audio_board/src/cli/cli.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hardware/firmware/audio_board/src/cli/cli.cpp b/hardware/firmware/audio_board/src/cli/cli.cpp index b310f9d1..f0a7445a 100644 --- a/hardware/firmware/audio_board/src/cli/cli.cpp +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -210,7 +210,7 @@ void Cli::eat(char chr) { if (is_terminator(chr)) { this->input_pos = 0; this->input_buf[0] = '\0'; - this->port->write("[fail] line too long\n"); + this->port->write("[err] line too long\n"); } return; } @@ -241,10 +241,10 @@ void Cli::update() { void Cli::print_fail() { if (this->slug > 0) { - this->port->printf("[%d fail]", this->slug); + this->port->printf("[%d err]", this->slug); this->slug = 0; } else { - this->port->printf("[fail]"); + this->port->printf("[err]"); } } @@ -258,7 +258,7 @@ void Cli::print_ok() { this->port->printf("[%d ok]", this->slug); this->slug = 0; } else { - this->port->printf("[ok]"); + this->port->printf(" [ok]"); } } From 93f662fe3abe999e5da7c117826006146c46b40b Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 18:05:58 +0100 Subject: [PATCH 32/67] disable second serial --- .../vendor/cores/teensy4/usb_desc.h | 93 ++++++++----------- .../vendor/cores/teensy4/yield.cpp | 2 +- 2 files changed, 40 insertions(+), 55 deletions(-) diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.h b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.h index 03bd124d..c8d2968e 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.h +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/usb_desc.h @@ -771,60 +771,45 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports #define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_ISOCHRONOUS #elif defined(USB_MIDI_AUDIO_SERIAL) - // 0 CDC 1 status - // 1 CDC 1 data - // 2 CDC 2 status - // 3 CDC 2 data - // 4 MIDI - // 5 audio - // 6 audio - // 7 audio - #define VENDOR_ID 0x16C0 - #define PRODUCT_ID 0x048A - #define MANUFACTURER_NAME {'F','O','S','D','E','M'} - #define MANUFACTURER_NAME_LEN 6 - #define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I','/','A','u','d','i','o'} - #define PRODUCT_NAME_LEN 17 - #define EP0_SIZE 64 - #define NUM_ENDPOINTS 9 - #define NUM_INTERFACE 8 - #define CDC_IAD_DESCRIPTOR 1 - #define CDC_STATUS_INTERFACE 0 - #define CDC_DATA_INTERFACE 1 // Serial - #define CDC_ACM_ENDPOINT 2 - #define CDC_RX_ENDPOINT 3 - #define CDC_TX_ENDPOINT 3 - #define CDC_ACM_SIZE 16 - #define CDC_RX_SIZE_480 512 - #define CDC_TX_SIZE_480 512 - #define CDC_RX_SIZE_12 64 - #define CDC_TX_SIZE_12 64 -#define CDC2_STATUS_INTERFACE 2 // SerialUSB1 -#define CDC2_DATA_INTERFACE 3 -#define CDC2_ACM_ENDPOINT 4 -#define CDC2_RX_ENDPOINT 5 -#define CDC2_TX_ENDPOINT 5 - #define MIDI_INTERFACE 4 // MIDI - #define MIDI_NUM_CABLES 1 - #define MIDI_TX_ENDPOINT 6 - #define MIDI_TX_SIZE_12 64 - #define MIDI_TX_SIZE_480 512 - #define MIDI_RX_ENDPOINT 6 - #define MIDI_RX_SIZE_12 64 - #define MIDI_RX_SIZE_480 512 - #define AUDIO_INTERFACE 5 // Audio (uses 3 consecutive interfaces) - #define AUDIO_TX_ENDPOINT 7 - #define AUDIO_TX_SIZE 180 - #define AUDIO_RX_ENDPOINT 7 - #define AUDIO_RX_SIZE 180 - #define AUDIO_SYNC_ENDPOINT 8 - #define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT - #define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK - #define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT - #define ENDPOINT5_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK - #define ENDPOINT6_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK - #define ENDPOINT7_CONFIG ENDPOINT_RECEIVE_ISOCHRONOUS + ENDPOINT_TRANSMIT_ISOCHRONOUS - #define ENDPOINT8_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_ISOCHRONOUS +#define VENDOR_ID 0x16C0 +#define PRODUCT_ID 0x048A +#define MANUFACTURER_NAME {'T','e','e','n','s','y','d','u','i','n','o'} +#define MANUFACTURER_NAME_LEN 11 +#define PRODUCT_NAME {'T','e','e','n','s','y',' ','M','I','D','I','/','A','u','d','i','o'} +#define PRODUCT_NAME_LEN 17 +#define EP0_SIZE 64 +#define NUM_ENDPOINTS 6 +#define NUM_INTERFACE 6 +#define CDC_IAD_DESCRIPTOR 1 +#define CDC_STATUS_INTERFACE 0 +#define CDC_DATA_INTERFACE 1 // Serial +#define CDC_ACM_ENDPOINT 2 +#define CDC_RX_ENDPOINT 3 +#define CDC_TX_ENDPOINT 3 +#define CDC_ACM_SIZE 16 +#define CDC_RX_SIZE_480 512 +#define CDC_TX_SIZE_480 512 +#define CDC_RX_SIZE_12 64 +#define CDC_TX_SIZE_12 64 +#define MIDI_INTERFACE 2 // MIDI +#define MIDI_NUM_CABLES 1 +#define MIDI_TX_ENDPOINT 4 +#define MIDI_TX_SIZE_12 64 +#define MIDI_TX_SIZE_480 512 +#define MIDI_RX_ENDPOINT 4 +#define MIDI_RX_SIZE_12 64 +#define MIDI_RX_SIZE_480 512 +#define AUDIO_INTERFACE 3 // Audio (uses 3 consecutive interfaces) +#define AUDIO_TX_ENDPOINT 5 +#define AUDIO_TX_SIZE 180 +#define AUDIO_RX_ENDPOINT 5 +#define AUDIO_RX_SIZE 180 +#define AUDIO_SYNC_ENDPOINT 6 +#define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_INTERRUPT +#define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK +#define ENDPOINT4_CONFIG ENDPOINT_RECEIVE_BULK + ENDPOINT_TRANSMIT_BULK +#define ENDPOINT5_CONFIG ENDPOINT_RECEIVE_ISOCHRONOUS + ENDPOINT_TRANSMIT_ISOCHRONOUS +#define ENDPOINT6_CONFIG ENDPOINT_RECEIVE_UNUSED + ENDPOINT_TRANSMIT_ISOCHRONOUS #elif defined(USB_MIDI16_AUDIO_SERIAL) #define VENDOR_ID 0x16C0 diff --git a/hardware/firmware/audio_board/vendor/cores/teensy4/yield.cpp b/hardware/firmware/audio_board/vendor/cores/teensy4/yield.cpp index 7d4d00b3..11840938 100644 --- a/hardware/firmware/audio_board/vendor/cores/teensy4/yield.cpp +++ b/hardware/firmware/audio_board/vendor/cores/teensy4/yield.cpp @@ -51,7 +51,7 @@ void yield(void) if (Serial.available()) serialEvent(); } -#if defined(USB_DUAL_SERIAL) || defined(USB_TRIPLE_SERIAL) || defined(USB_MIDI_AUDIO_SERIAL) +#if defined(USB_DUAL_SERIAL) || defined(USB_TRIPLE_SERIAL) if (check_flags & YIELD_CHECK_USB_SERIALUSB1) { if (SerialUSB1.available()) serialEventUSB1(); } From 326bae4928c330ce4894c1f5eb5b1b26d563a057 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 19:55:52 +0100 Subject: [PATCH 33/67] config file parsing --- software/audioctl/api/api.go | 51 +++++++++++++++++++++++ software/audioctl/cmd/audioctl/main.go | 32 +++++++++++++++ software/audioctl/config/api_cfg.go | 14 +++++++ software/audioctl/config/config.go | 57 ++++++++++++++++++++++++++ software/audioctl/example_config.yaml | 2 + software/audioctl/go.mod | 12 ++++++ software/audioctl/go.sum | 4 ++ 7 files changed, 172 insertions(+) create mode 100644 software/audioctl/api/api.go create mode 100644 software/audioctl/cmd/audioctl/main.go create mode 100644 software/audioctl/config/api_cfg.go create mode 100644 software/audioctl/config/config.go create mode 100644 software/audioctl/example_config.yaml create mode 100644 software/audioctl/go.mod create mode 100644 software/audioctl/go.sum diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go new file mode 100644 index 00000000..215c5164 --- /dev/null +++ b/software/audioctl/api/api.go @@ -0,0 +1,51 @@ +package api + +import ( + "log/slog" + "net/http" + "time" + + "github.com/dexterlb/misirka/go/misirka" + "github.com/fosdem/video/software/audioctl/config" +) + +type Api struct { + srv http.Server + m *misirka.Misirka + logger *slog.Logger + cfg *config.ApiCfg +} + +func New(logger *slog.Logger, cfg *config.ApiCfg) *Api { + a := &Api{} + a.cfg = cfg + a.logger = logger + + a.m = misirka.New("/", func(err error) { + logger.Error("API error", "err", err) + }) + a.m.AddTopic("heartbeat") + + a.srv.Handler = a.m.Handler() + a.srv.Addr = a.cfg.Bind + return a +} + +func (a *Api) Serve() error { + go a.doHeartbeat() + a.logger.Info("starting server", "addr", a.cfg.Bind) + return a.srv.ListenAndServe() +} + +type Heartbeat struct { + Now time.Time `json:"now"` +} + +func (a *Api) doHeartbeat() { + var h Heartbeat + for { + h.Now = time.Now() + misirka.Publish(a.m, "heartbeat", h) + time.Sleep(1 * time.Second) + } +} diff --git a/software/audioctl/cmd/audioctl/main.go b/software/audioctl/cmd/audioctl/main.go new file mode 100644 index 00000000..72724039 --- /dev/null +++ b/software/audioctl/cmd/audioctl/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "log/slog" + "os" + + "github.com/fosdem/video/software/audioctl/api" + "github.com/fosdem/video/software/audioctl/config" +) + +func main() { + if len(os.Args) != 2 { + fmt.Fprintf(os.Stderr, "usage: %s \n", os.Args[0]) + os.Exit(1) + } + + logger := slog.Default() + + cfg, err := config.Parse(os.Args[1]) + if err != nil { + logger.Error("Could not parse config server", "err", err) + os.Exit(1) + } + + a := api.New(logger.With("prefix", "api"), cfg.Api) + err = a.Serve() + if err != nil { + logger.Error("Could not start server", "err", err) + os.Exit(1) + } +} diff --git a/software/audioctl/config/api_cfg.go b/software/audioctl/config/api_cfg.go new file mode 100644 index 00000000..7df05ac7 --- /dev/null +++ b/software/audioctl/config/api_cfg.go @@ -0,0 +1,14 @@ +package config + +import "fmt" + +type ApiCfg struct { + Bind string +} + +func (a *ApiCfg) Validate() error { + if a.Bind == "" { + return fmt.Errorf("`bind` should be nonempty") + } + return nil +} diff --git a/software/audioctl/config/config.go b/software/audioctl/config/config.go new file mode 100644 index 00000000..28a4a667 --- /dev/null +++ b/software/audioctl/config/config.go @@ -0,0 +1,57 @@ +package config + +import ( + "fmt" + "os" + + "github.com/goccy/go-yaml" +) + +type Config struct { + Api *ApiCfg +} + +func Parse(filename string) (*Config, error) { + f, err := os.Open(filename) + if err != nil { + return nil, fmt.Errorf("could not open %s: %s", filename, err) + } + defer func(f *os.File) { + err := f.Close() + if err != nil { + _ = fmt.Errorf("could not close %s: %s", filename, err) + } + }(f) + + // uncomment for path hacks (check fazantix source for ideas) + // absFilename, err := filepath.Abs(filename) + // if err != nil { + // return nil, fmt.Errorf("somehow, %s is malformed: %w", filename, err) + // } + // UnmarshalBase = filepath.Dir(absFilename) + + m := yaml.NewDecoder(f, yaml.Strict()) + + cfg := &Config{} + err = m.Decode(cfg) + if err != nil { + return nil, err + } + err = cfg.Validate() + if err != nil { + return nil, err + } + return cfg, err +} + +func (c *Config) Validate() error { + var err error + if c.Api == nil { + return fmt.Errorf("`api` section missing") + } + err = c.Api.Validate() + if err != nil { + return fmt.Errorf("error in `api` section: %w", err) + } + return nil +} diff --git a/software/audioctl/example_config.yaml b/software/audioctl/example_config.yaml new file mode 100644 index 00000000..0894d48e --- /dev/null +++ b/software/audioctl/example_config.yaml @@ -0,0 +1,2 @@ +api: + bind: ':8811' diff --git a/software/audioctl/go.mod b/software/audioctl/go.mod new file mode 100644 index 00000000..292c671f --- /dev/null +++ b/software/audioctl/go.mod @@ -0,0 +1,12 @@ +module github.com/fosdem/video/software/audioctl + +go 1.25.5 + +replace github.com/dexterlb/misirka/go => /home/human/s/git/misirka/go + +require ( + github.com/dexterlb/misirka/go v0.0.0-00010101000000-000000000000 + github.com/goccy/go-yaml v1.19.2 +) + +require github.com/goccy/go-json v0.10.6 // indirect diff --git a/software/audioctl/go.sum b/software/audioctl/go.sum new file mode 100644 index 00000000..f844edcc --- /dev/null +++ b/software/audioctl/go.sum @@ -0,0 +1,4 @@ +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= From 548a98ecdbe4ba4da41da7b79c8e948f0b2df049 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 20:13:24 +0100 Subject: [PATCH 34/67] open serial port --- software/audioctl/api/api.go | 5 +++- software/audioctl/cmd/audioctl/main.go | 13 ++++++++- software/audioctl/config/config.go | 8 ++++++ software/audioctl/config/ctl_cfg.go | 14 ++++++++++ software/audioctl/ctl/ctl.go | 38 ++++++++++++++++++++++++++ software/audioctl/example_config.yaml | 2 ++ software/audioctl/go.mod | 7 ++++- software/audioctl/go.sum | 14 ++++++++++ 8 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 software/audioctl/config/ctl_cfg.go create mode 100644 software/audioctl/ctl/ctl.go diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go index 215c5164..dd685e71 100644 --- a/software/audioctl/api/api.go +++ b/software/audioctl/api/api.go @@ -7,6 +7,7 @@ import ( "github.com/dexterlb/misirka/go/misirka" "github.com/fosdem/video/software/audioctl/config" + "github.com/fosdem/video/software/audioctl/ctl" ) type Api struct { @@ -14,12 +15,14 @@ type Api struct { m *misirka.Misirka logger *slog.Logger cfg *config.ApiCfg + ctl *ctl.Ctl } -func New(logger *slog.Logger, cfg *config.ApiCfg) *Api { +func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { a := &Api{} a.cfg = cfg a.logger = logger + a.ctl = ctl a.m = misirka.New("/", func(err error) { logger.Error("API error", "err", err) diff --git a/software/audioctl/cmd/audioctl/main.go b/software/audioctl/cmd/audioctl/main.go index 72724039..67de3689 100644 --- a/software/audioctl/cmd/audioctl/main.go +++ b/software/audioctl/cmd/audioctl/main.go @@ -7,6 +7,7 @@ import ( "github.com/fosdem/video/software/audioctl/api" "github.com/fosdem/video/software/audioctl/config" + "github.com/fosdem/video/software/audioctl/ctl" ) func main() { @@ -23,7 +24,17 @@ func main() { os.Exit(1) } - a := api.New(logger.With("prefix", "api"), cfg.Api) + c := ctl.New(logger.With("prefix", "ctl"), cfg.Ctl) + go func() { + err := c.Loop() + if err != nil { + logger.Error("Control loop exited", "err", err) + os.Exit(1) + } + os.Exit(0) + }() + + a := api.New(logger.With("prefix", "api"), cfg.Api, c) err = a.Serve() if err != nil { logger.Error("Could not start server", "err", err) diff --git a/software/audioctl/config/config.go b/software/audioctl/config/config.go index 28a4a667..dc28cab3 100644 --- a/software/audioctl/config/config.go +++ b/software/audioctl/config/config.go @@ -9,6 +9,7 @@ import ( type Config struct { Api *ApiCfg + Ctl *CtlCfg } func Parse(filename string) (*Config, error) { @@ -53,5 +54,12 @@ func (c *Config) Validate() error { if err != nil { return fmt.Errorf("error in `api` section: %w", err) } + if c.Ctl == nil { + return fmt.Errorf("`ctl` section missing") + } + err = c.Ctl.Validate() + if err != nil { + return fmt.Errorf("error in `ctl` section: %w", err) + } return nil } diff --git a/software/audioctl/config/ctl_cfg.go b/software/audioctl/config/ctl_cfg.go new file mode 100644 index 00000000..92e9645e --- /dev/null +++ b/software/audioctl/config/ctl_cfg.go @@ -0,0 +1,14 @@ +package config + +import "fmt" + +type CtlCfg struct { + PortDevice string `yaml:"port_device"` +} + +func (a *CtlCfg) Validate() error { + if a.PortDevice == "" { + return fmt.Errorf("`port_device` should be nonempty") + } + return nil +} diff --git a/software/audioctl/ctl/ctl.go b/software/audioctl/ctl/ctl.go new file mode 100644 index 00000000..26e33c62 --- /dev/null +++ b/software/audioctl/ctl/ctl.go @@ -0,0 +1,38 @@ +package ctl + +import ( + "fmt" + "log/slog" + + "github.com/fosdem/video/software/audioctl/config" + "go.bug.st/serial" +) + +type Ctl struct { + cfg *config.CtlCfg + logger *slog.Logger + port serial.Port +} + +func New(logger *slog.Logger, cfg *config.CtlCfg) *Ctl { + c := &Ctl{} + c.logger = logger + c.cfg = cfg + return c +} + +func (c *Ctl) Loop() error { + var err error + mode := &serial.Mode{ + BaudRate: 115200, + } + c.port, err = serial.Open(c.cfg.PortDevice, mode) + if err != nil { + return fmt.Errorf("could not open %s: %w", c.cfg.PortDevice, err) + } + defer c.port.Close() + + c.logger.Info("successfully opened serial", "port", c.cfg.PortDevice) + + return fmt.Errorf("not implemented") +} diff --git a/software/audioctl/example_config.yaml b/software/audioctl/example_config.yaml index 0894d48e..2dfb93da 100644 --- a/software/audioctl/example_config.yaml +++ b/software/audioctl/example_config.yaml @@ -1,2 +1,4 @@ api: bind: ':8811' +ctl: + port_device: '/dev/ttyACM0' diff --git a/software/audioctl/go.mod b/software/audioctl/go.mod index 292c671f..3b964993 100644 --- a/software/audioctl/go.mod +++ b/software/audioctl/go.mod @@ -7,6 +7,11 @@ replace github.com/dexterlb/misirka/go => /home/human/s/git/misirka/go require ( github.com/dexterlb/misirka/go v0.0.0-00010101000000-000000000000 github.com/goccy/go-yaml v1.19.2 + go.bug.st/serial v1.6.4 ) -require github.com/goccy/go-json v0.10.6 // indirect +require ( + github.com/creack/goselect v0.1.2 // indirect + github.com/goccy/go-json v0.10.6 // indirect + golang.org/x/sys v0.19.0 // indirect +) diff --git a/software/audioctl/go.sum b/software/audioctl/go.sum index f844edcc..5ec1f6d0 100644 --- a/software/audioctl/go.sum +++ b/software/audioctl/go.sum @@ -1,4 +1,18 @@ +github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= +github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +go.bug.st/serial v1.6.4 h1:7FmqNPgVp3pu2Jz5PoPtbZ9jJO5gnEnZIvnI1lzve8A= +go.bug.st/serial v1.6.4/go.mod h1:nofMJxTeNVny/m6+KaafC6vJGj3miwQZ6vW4BZUGJPI= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From c38ad4ddb5a5dece2c8bd9e88a3cb9a75c9cf468 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 21:40:11 +0100 Subject: [PATCH 35/67] fix deadlocks --- software/audioctl/api/api.go | 2 + software/audioctl/api/handlers.go | 14 +++++ software/audioctl/ctl/ctl.go | 87 ++++++++++++++++++++++++++++++- 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 software/audioctl/api/handlers.go diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go index dd685e71..44b6c81d 100644 --- a/software/audioctl/api/api.go +++ b/software/audioctl/api/api.go @@ -27,7 +27,9 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { a.m = misirka.New("/", func(err error) { logger.Error("API error", "err", err) }) + a.m.AddTopic("heartbeat") + misirka.HandleCall(a.m, "raw-cmd", a.handleRawCmd) a.srv.Handler = a.m.Handler() a.srv.Addr = a.cfg.Bind diff --git a/software/audioctl/api/handlers.go b/software/audioctl/api/handlers.go new file mode 100644 index 00000000..d85c8400 --- /dev/null +++ b/software/audioctl/api/handlers.go @@ -0,0 +1,14 @@ +package api + +import "github.com/dexterlb/misirka/go/misirka" + +func (a *Api) handleRawCmd(param string) (string, *misirka.MErr) { + resp, err := a.ctl.RawCmd(param) + if err != nil { + return "", &misirka.MErr{ + Code: -42, + Err: err, + } + } + return resp, nil +} diff --git a/software/audioctl/ctl/ctl.go b/software/audioctl/ctl/ctl.go index 26e33c62..65fcf021 100644 --- a/software/audioctl/ctl/ctl.go +++ b/software/audioctl/ctl/ctl.go @@ -1,8 +1,11 @@ package ctl import ( + "bufio" "fmt" "log/slog" + "strings" + "time" "github.com/fosdem/video/software/audioctl/config" "go.bug.st/serial" @@ -12,12 +15,21 @@ type Ctl struct { cfg *config.CtlCfg logger *slog.Logger port serial.Port + + slug uint16 + + reqs chan *requestSync + input chan string + readerErr chan error } func New(logger *slog.Logger, cfg *config.CtlCfg) *Ctl { c := &Ctl{} c.logger = logger c.cfg = cfg + c.input = make(chan string) + c.readerErr = make(chan error) + c.reqs = make(chan *requestSync) return c } @@ -34,5 +46,78 @@ func (c *Ctl) Loop() error { c.logger.Info("successfully opened serial", "port", c.cfg.PortDevice) - return fmt.Errorf("not implemented") + go c.reader() + + for { + select { + case err := <-c.readerErr: + return fmt.Errorf("could not read from port: %w", err) + case data := <-c.input: + c.handleUnsolicitedInput(data) + case req := <-c.reqs: + <-req.Finish + } + } +} + +func (c *Ctl) RawCmd(argstr string) (string, error) { + req := newReq() + defer close(req.Finish) + c.reqs <- req + + c.slug += 1 + if c.slug == 0 { + c.slug = 1 + } + + fmt.Fprintf(c.port, "%d %s\n", c.slug, argstr) + + timeout := time.After(1 * time.Second) + + for { + select { + case data := <-c.input: + if resp, ok := c.parseResponse(data); ok { + return resp, nil + } + c.handleUnsolicitedInput(data) + case <-timeout: + return "", fmt.Errorf("request to hardware timed out") + } + } +} + +// requestSync is used to serialise requests +type requestSync struct { + Finish chan struct{} +} + +func newReq() *requestSync { + return &requestSync{ + Finish: make(chan struct{}), + } +} + +func (c *Ctl) parseResponse(data string) (string, bool) { + return "not implemented", true +} + +func (c *Ctl) handleUnsolicitedInput(data string) { + if msg, ok := strings.CutPrefix(data, "[log] "); ok { + c.logger.Info("log", "msg", msg) + return + } + c.logger.Error("unsolicited data received", "data", data) +} + +func (c *Ctl) reader() { + scanner := bufio.NewScanner(c.port) + for scanner.Scan() { + c.input <- scanner.Text() + } + err := scanner.Err() + if err == nil { + err = fmt.Errorf("port was probably closed") + } + c.readerErr <- err } From eb15335bba4d742d3566b14d334d659296cd1c7b Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 21:47:44 +0100 Subject: [PATCH 36/67] proper failure handling --- software/audioctl/ctl/ctl.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/software/audioctl/ctl/ctl.go b/software/audioctl/ctl/ctl.go index 65fcf021..405b79e4 100644 --- a/software/audioctl/ctl/ctl.go +++ b/software/audioctl/ctl/ctl.go @@ -77,8 +77,8 @@ func (c *Ctl) RawCmd(argstr string) (string, error) { for { select { case data := <-c.input: - if resp, ok := c.parseResponse(data); ok { - return resp, nil + if resp, err, ok := c.parseResponse(data); ok { + return resp, err } c.handleUnsolicitedInput(data) case <-timeout: @@ -98,8 +98,18 @@ func newReq() *requestSync { } } -func (c *Ctl) parseResponse(data string) (string, bool) { - return "not implemented", true +func (c *Ctl) parseResponse(data string) (string, error, bool) { + okprefix := fmt.Sprintf("[%d ok]", c.slug) + if resp, ok := strings.CutPrefix(data, okprefix); ok { + return strings.TrimSpace(resp), nil, true + } + + failprefix := fmt.Sprintf("[%d err]", c.slug) + if resp, ok := strings.CutPrefix(data, failprefix); ok { + err := fmt.Errorf("hardware responded with error: %s", strings.TrimSpace(resp)) + return "", err, true + } + return "", nil, false } func (c *Ctl) handleUnsolicitedInput(data string) { From 79f36e149df778b1a835dd56e300d66543cf1ab5 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 23:03:56 +0100 Subject: [PATCH 37/67] state polling --- software/audioctl/api/api.go | 36 ++++- software/audioctl/ctl/getstate.go | 237 ++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 software/audioctl/ctl/getstate.go diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go index 44b6c81d..af7596b8 100644 --- a/software/audioctl/api/api.go +++ b/software/audioctl/api/api.go @@ -16,6 +16,7 @@ type Api struct { logger *slog.Logger cfg *config.ApiCfg ctl *ctl.Ctl + dying chan struct{} } func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { @@ -23,12 +24,14 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { a.cfg = cfg a.logger = logger a.ctl = ctl + a.dying = make(chan struct{}) a.m = misirka.New("/", func(err error) { logger.Error("API error", "err", err) }) a.m.AddTopic("heartbeat") + a.m.AddTopic("state") misirka.HandleCall(a.m, "raw-cmd", a.handleRawCmd) a.srv.Handler = a.m.Handler() @@ -37,7 +40,9 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { } func (a *Api) Serve() error { + defer close(a.dying) go a.doHeartbeat() + go a.statePoller() a.logger.Info("starting server", "addr", a.cfg.Bind) return a.srv.ListenAndServe() } @@ -47,10 +52,35 @@ type Heartbeat struct { } func (a *Api) doHeartbeat() { + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + var h Heartbeat for { - h.Now = time.Now() - misirka.Publish(a.m, "heartbeat", h) - time.Sleep(1 * time.Second) + select { + case <-a.dying: + return + case t := <-ticker.C: + h.Now = t + misirka.Publish(a.m, "heartbeat", h) + } + } +} + +func (a *Api) statePoller() { + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + + for { + select { + case <-a.dying: + return + case <-ticker.C: + state, err := a.ctl.GetFullState() + if err != nil { + a.logger.Error("could not poll state", "err", err) + } + misirka.Publish(a.m, "state", state) + } } } diff --git a/software/audioctl/ctl/getstate.go b/software/audioctl/ctl/getstate.go new file mode 100644 index 00000000..c9be866a --- /dev/null +++ b/software/audioctl/ctl/getstate.go @@ -0,0 +1,237 @@ +package ctl + +import ( + "fmt" + "strconv" + "strings" +) + +type MixerState struct { + Channels []ChannelState `json:"channels"` + Buses []BusState `json:"buses"` +} + +type ChannelState struct { + Label string `json:"label "` + Name string `json:"name"` + Gain float32 `json:"gain"` + Phantom bool `json:"phantom"` + Sends []SendState `json:"sends"` +} + +type BusState struct { + Label string `json:"label"` + Name string `json:"name"` + Volume float32 `json:"volume"` +} + +type SendState struct { + Unmuted bool `json:"unmuted"` + Volume float32 `json:"volume"` +} + +func (c *Ctl) GetFullState() (*MixerState, error) { + chanNames, busNames, err := c.GetChannelNames() + if err != nil { + return nil, fmt.Errorf("could not get channel names: %w", err) + } + chanLabels, busLabels, err := c.GetChannelLabels() + if err != nil { + return nil, fmt.Errorf("could not get channel labels: %w", err) + } + + gains, err := c.GetInputGains() + if err != nil { + return nil, fmt.Errorf("could not get input gains: %w", err) + } + + busVolumes, err := c.GetBusVolumes() + if err != nil { + return nil, fmt.Errorf("could not get bus volumes: %w", err) + } + + phantoms, err := c.GetPhantoms() + if err != nil { + return nil, fmt.Errorf("could not get phantoms: %w", err) + } + + sends, err := c.GetMatrix() + if err != nil { + return nil, fmt.Errorf("could not get matrix: %w", err) + } + + numChans := len(chanNames) + numBuses := len(busNames) + if len(chanLabels) != numChans || len(busLabels) != numBuses { + return nil, fmt.Errorf("item length mismatch (channel.labels and channel.names returned mismatching data)") + } + + m := &MixerState{ + Channels: make([]ChannelState, numChans), + Buses: make([]BusState, numBuses), + } + + for i := range m.Channels { + m.Channels[i].Label = chanLabels[i] + m.Channels[i].Name = chanNames[i] + m.Channels[i].Gain = gains[i] + m.Channels[i].Phantom = phantoms[i] + m.Channels[i].Sends = make([]SendState, numBuses) + for j := range m.Buses { + m.Channels[i].Sends[j] = sends[i*numBuses+j] + } + } + + for j := range m.Buses { + m.Buses[j].Label = busLabels[j] + m.Buses[j].Name = busNames[j] + m.Buses[j].Volume = busVolumes[j] + } + + return m, nil +} + +func (c *Ctl) GetChannelNames() ([]string, []string, error) { + resp, err := c.RawCmd("channel.names") + if err != nil { + return nil, nil, err + } + return parseChannelBusList(resp, "channels", "buses") +} + +func (c *Ctl) GetChannelLabels() ([]string, []string, error) { + resp, err := c.RawCmd("channel.labels") + if err != nil { + return nil, nil, err + } + return parseChannelBusList(resp, "channels", "buses") +} + +func (c *Ctl) GetInputGains() ([]float32, error) { + resp, err := c.RawCmd("gains") + if err != nil { + return nil, err + } + return parseFloatList(resp) +} + +func (c *Ctl) GetBusVolumes() ([]float32, error) { + resp, err := c.RawCmd("bus-volumes") + if err != nil { + return nil, err + } + return parseFloatList(resp) +} + +func (c *Ctl) GetPhantoms() ([]bool, error) { + resp, err := c.RawCmd("phantoms") + if err != nil { + return nil, err + } + return parseBoolList(resp) +} + +func (c *Ctl) GetMatrix() ([]SendState, error) { + resp, err := c.RawCmd("matrix") + if err != nil { + return nil, err + } + + fields := strings.Fields(resp) + result := make([]SendState, len(fields)) + for i, f := range fields { + result[i], err = parseSend(f) + if err != nil { + return nil, err + } + } + + return result, nil +} + +func parseSend(s string) (SendState, error) { + var ss SendState + var err error + + fields := strings.Split(s, "*") + if len(fields) != 2 { + return ss, fmt.Errorf("%s is not in the form *", s) + } + + if fields[0] == "0" { + ss.Unmuted = false + } else if fields[0] == "1" { + ss.Unmuted = true + } else { + return ss, fmt.Errorf("%s is not a valid mute/unmute value (0|1)", fields[0]) + } + + vol, err := strconv.ParseFloat(fields[1], 32) + if err != nil { + return ss, fmt.Errorf("invalid float (%s): %w", fields[1], err) + } + ss.Volume = float32(vol) + + return ss, nil +} + +func parseBoolList(s string) ([]bool, error) { + fields := strings.Fields(s) + result := make([]bool, len(fields)) + for i, f := range fields { + if f == "0" { + result[i] = false + } else if f == "1" { + result[i] = true + } else { + return nil, fmt.Errorf("%s is not a bool", f) + } + } + return result, nil +} + +func parseFloatList(s string) ([]float32, error) { + fields := strings.Fields(s) + result := make([]float32, len(fields)) + for i, f := range fields { + v, err := strconv.ParseFloat(f, 32) + if err != nil { + return nil, fmt.Errorf("invalid float at index %d: %w", i, err) + } + result[i] = float32(v) + } + return result, nil +} + +func parseChannelBusList(resp, channelKey, busKey string) ([]string, []string, error) { + // Expected structure: ": ...; : ..." + parts := strings.SplitN(resp, ";", 2) + if len(parts) != 2 { + return nil, nil, fmt.Errorf("expected two semicolon-separated sections, got %q", resp) + } + + parseSection := func(s, key string) ([]string, error) { + s = strings.TrimSpace(s) + prefix := key + ":" + if !strings.HasPrefix(s, prefix) { + return nil, fmt.Errorf("expected section to start with %q, got %q", prefix, s) + } + s = strings.TrimPrefix(s, prefix) + s = strings.TrimSpace(s) + if s == "" { + return []string{}, nil + } + return strings.Fields(s), nil + } + + channels, err := parseSection(parts[0], channelKey) + if err != nil { + return nil, nil, err + } + buses, err := parseSection(parts[1], busKey) + if err != nil { + return nil, nil, err + } + + return channels, buses, nil +} From f0f38fb7c3b949e06c3572d5be1cd08e708e060f Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 21 Mar 2026 23:44:40 +0100 Subject: [PATCH 38/67] poll state and levels --- software/audioctl/api/api.go | 39 ++++++++++---- software/audioctl/config/api_cfg.go | 10 +++- software/audioctl/config/ctl_cfg.go | 6 ++- software/audioctl/ctl/ctl.go | 3 ++ software/audioctl/ctl/getlevels.go | 75 +++++++++++++++++++++++++++ software/audioctl/ctl/getstate.go | 30 ++++++++--- software/audioctl/example_config.yaml | 3 ++ 7 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 software/audioctl/ctl/getlevels.go diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go index af7596b8..c75868e0 100644 --- a/software/audioctl/api/api.go +++ b/software/audioctl/api/api.go @@ -32,6 +32,7 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { a.m.AddTopic("heartbeat") a.m.AddTopic("state") + a.m.AddTopic("levels") misirka.HandleCall(a.m, "raw-cmd", a.handleRawCmd) a.srv.Handler = a.m.Handler() @@ -42,7 +43,7 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { func (a *Api) Serve() error { defer close(a.dying) go a.doHeartbeat() - go a.statePoller() + go a.poller() a.logger.Info("starting server", "addr", a.cfg.Bind) return a.srv.ListenAndServe() } @@ -67,20 +68,38 @@ func (a *Api) doHeartbeat() { } } -func (a *Api) statePoller() { - ticker := time.NewTicker(1 * time.Second) - defer ticker.Stop() +func (a *Api) poller() { + pollState := time.NewTicker(time.Duration(a.cfg.StatePollIntervalMsec) * time.Millisecond) + defer pollState.Stop() + + pollLevels := time.NewTicker(time.Duration(a.cfg.LevelsPollIntervalMsec) * time.Millisecond) + defer pollLevels.Stop() + a.pollState() for { select { case <-a.dying: return - case <-ticker.C: - state, err := a.ctl.GetFullState() - if err != nil { - a.logger.Error("could not poll state", "err", err) - } - misirka.Publish(a.m, "state", state) + case <-pollState.C: + a.pollState() + case <-pollLevels.C: + a.pollLevels() } } } + +func (a *Api) pollState() { + state, err := a.ctl.GetFullState() + if err != nil { + a.logger.Error("could not poll state", "err", err) + } + misirka.Publish(a.m, "state", state) +} + +func (a *Api) pollLevels() { + levels, err := a.ctl.GetLevels() + if err != nil { + a.logger.Error("could not poll levels", "err", err) + } + misirka.Publish(a.m, "levels", levels) +} diff --git a/software/audioctl/config/api_cfg.go b/software/audioctl/config/api_cfg.go index 7df05ac7..97197340 100644 --- a/software/audioctl/config/api_cfg.go +++ b/software/audioctl/config/api_cfg.go @@ -3,12 +3,20 @@ package config import "fmt" type ApiCfg struct { - Bind string + Bind string + StatePollIntervalMsec uint `yaml:"state_poll_interval_msec"` + LevelsPollIntervalMsec uint `yaml:"levels_poll_interval_msec"` } func (a *ApiCfg) Validate() error { if a.Bind == "" { return fmt.Errorf("`bind` should be nonempty") } + if a.StatePollIntervalMsec < 5 { + return fmt.Errorf("`state_poll_interval_msec` should be >= 5") + } + if a.LevelsPollIntervalMsec < 5 { + return fmt.Errorf("`levels_poll_interval_msec` should be >= 5") + } return nil } diff --git a/software/audioctl/config/ctl_cfg.go b/software/audioctl/config/ctl_cfg.go index 92e9645e..57c62336 100644 --- a/software/audioctl/config/ctl_cfg.go +++ b/software/audioctl/config/ctl_cfg.go @@ -3,12 +3,16 @@ package config import "fmt" type CtlCfg struct { - PortDevice string `yaml:"port_device"` + PortDevice string `yaml:"port_device"` + UseDBLevels *bool `yaml:"use_db_levels"` } func (a *CtlCfg) Validate() error { if a.PortDevice == "" { return fmt.Errorf("`port_device` should be nonempty") } + if a.UseDBLevels == nil { + return fmt.Errorf("`use_db_levels` should be true or false") + } return nil } diff --git a/software/audioctl/ctl/ctl.go b/software/audioctl/ctl/ctl.go index 405b79e4..10698764 100644 --- a/software/audioctl/ctl/ctl.go +++ b/software/audioctl/ctl/ctl.go @@ -21,6 +21,9 @@ type Ctl struct { reqs chan *requestSync input chan string readerErr chan error + + numChans int + numBuses int } func New(logger *slog.Logger, cfg *config.CtlCfg) *Ctl { diff --git a/software/audioctl/ctl/getlevels.go b/software/audioctl/ctl/getlevels.go new file mode 100644 index 00000000..c2a035dd --- /dev/null +++ b/software/audioctl/ctl/getlevels.go @@ -0,0 +1,75 @@ +package ctl + +import "fmt" + +type Levels struct { + RMS LevelsBlock `json:"rms"` + Peak LevelsBlock `json:"peak"` + Smooth LevelsBlock `json:"smooth"` +} + +type LevelsBlock struct { + Input []float32 `json:"inputs"` + Bus []float32 `json:"buses"` +} + +func (c *Ctl) GetLevels() (*Levels, error) { + if c.numChans == 0 || c.numBuses == 0 { + return nil, fmt.Errorf("no channels/buses known (maybe GetFullState() did not get called)") + } + + cmd := "levels" + if *c.cfg.UseDBLevels { + cmd = "levels.db" + } + + resp, err := c.RawCmd(cmd) + if err != nil { + return nil, err + } + + vals, err := parseFloatList(resp) + if err != nil { + return nil, err + } + + if *c.cfg.UseDBLevels { + for i := range vals { + if vals[i] < -120 { + vals[i] = -120 + } + } + } + + if len(vals) != (c.numChans+c.numBuses)*3 { + return nil, fmt.Errorf("got %d values instead of %d", len(vals), (c.numChans+c.numBuses)*3) + } + levels := &Levels{ + RMS: LevelsBlock{ + Input: make([]float32, c.numChans), + Bus: make([]float32, c.numBuses), + }, + Peak: LevelsBlock{ + Input: make([]float32, c.numChans), + Bus: make([]float32, c.numBuses), + }, + Smooth: LevelsBlock{ + Input: make([]float32, c.numChans), + Bus: make([]float32, c.numBuses), + }, + } + + for i := range c.numChans { + levels.RMS.Input[i] = vals[i*3+0] + levels.Peak.Input[i] = vals[i*3+1] + levels.Smooth.Input[i] = vals[i*3+1] + } + for i := range c.numBuses { + j := c.numChans + i + levels.RMS.Bus[i] = vals[j*3+0] + levels.Peak.Bus[i] = vals[j*3+1] + levels.Smooth.Bus[i] = vals[j*3+1] + } + + return levels, nil +} diff --git a/software/audioctl/ctl/getstate.go b/software/audioctl/ctl/getstate.go index c9be866a..886421b8 100644 --- a/software/audioctl/ctl/getstate.go +++ b/software/audioctl/ctl/getstate.go @@ -60,15 +60,31 @@ func (c *Ctl) GetFullState() (*MixerState, error) { return nil, fmt.Errorf("could not get matrix: %w", err) } - numChans := len(chanNames) - numBuses := len(busNames) - if len(chanLabels) != numChans || len(busLabels) != numBuses { + c.numChans = len(chanNames) + c.numBuses = len(busNames) + if len(chanLabels) != c.numChans || len(busLabels) != c.numBuses { return nil, fmt.Errorf("item length mismatch (channel.labels and channel.names returned mismatching data)") } + if len(phantoms) != c.numChans { + return nil, fmt.Errorf("wrong number of phantoms (got %d, should be %d)", len(phantoms), c.numChans) + } + + if len(gains) != c.numChans { + return nil, fmt.Errorf("wrong number of gains (got %d, should be %d)", len(gains), c.numChans) + } + + if len(sends) != c.numChans*c.numBuses { + return nil, fmt.Errorf("wrong number of matrix items (got %d, should be %d)", len(sends), c.numChans*c.numBuses) + } + + if len(busVolumes) != c.numBuses { + return nil, fmt.Errorf("wrong number of busVolumes (got %d, should be %d)", len(busVolumes), c.numBuses) + } + m := &MixerState{ - Channels: make([]ChannelState, numChans), - Buses: make([]BusState, numBuses), + Channels: make([]ChannelState, c.numChans), + Buses: make([]BusState, c.numBuses), } for i := range m.Channels { @@ -76,9 +92,9 @@ func (c *Ctl) GetFullState() (*MixerState, error) { m.Channels[i].Name = chanNames[i] m.Channels[i].Gain = gains[i] m.Channels[i].Phantom = phantoms[i] - m.Channels[i].Sends = make([]SendState, numBuses) + m.Channels[i].Sends = make([]SendState, c.numBuses) for j := range m.Buses { - m.Channels[i].Sends[j] = sends[i*numBuses+j] + m.Channels[i].Sends[j] = sends[i*c.numBuses+j] } } diff --git a/software/audioctl/example_config.yaml b/software/audioctl/example_config.yaml index 2dfb93da..e41592fd 100644 --- a/software/audioctl/example_config.yaml +++ b/software/audioctl/example_config.yaml @@ -1,4 +1,7 @@ api: bind: ':8811' + state_poll_interval_msec: 10000 + levels_poll_interval_msec: 100 ctl: port_device: '/dev/ttyACM0' + use_db_levels: true From 41947c9b081cb7c550c4722139e354e254b35f6a Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 00:27:03 +0100 Subject: [PATCH 39/67] set-send --- software/audioctl/api/api.go | 17 +----- software/audioctl/api/handlers.go | 92 ++++++++++++++++++++++++++++++- software/audioctl/ctl/ctl.go | 6 +- software/audioctl/ctl/getstate.go | 3 + 4 files changed, 99 insertions(+), 19 deletions(-) diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go index c75868e0..27dc0fad 100644 --- a/software/audioctl/api/api.go +++ b/software/audioctl/api/api.go @@ -34,6 +34,7 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { a.m.AddTopic("state") a.m.AddTopic("levels") misirka.HandleCall(a.m, "raw-cmd", a.handleRawCmd) + misirka.HandleCall(a.m, "set-send", a.handleSetSend) a.srv.Handler = a.m.Handler() a.srv.Addr = a.cfg.Bind @@ -87,19 +88,3 @@ func (a *Api) poller() { } } } - -func (a *Api) pollState() { - state, err := a.ctl.GetFullState() - if err != nil { - a.logger.Error("could not poll state", "err", err) - } - misirka.Publish(a.m, "state", state) -} - -func (a *Api) pollLevels() { - levels, err := a.ctl.GetLevels() - if err != nil { - a.logger.Error("could not poll levels", "err", err) - } - misirka.Publish(a.m, "levels", levels) -} diff --git a/software/audioctl/api/handlers.go b/software/audioctl/api/handlers.go index d85c8400..0e62bb77 100644 --- a/software/audioctl/api/handlers.go +++ b/software/audioctl/api/handlers.go @@ -1,6 +1,10 @@ package api -import "github.com/dexterlb/misirka/go/misirka" +import ( + "fmt" + + "github.com/dexterlb/misirka/go/misirka" +) func (a *Api) handleRawCmd(param string) (string, *misirka.MErr) { resp, err := a.ctl.RawCmd(param) @@ -12,3 +16,89 @@ func (a *Api) handleRawCmd(param string) (string, *misirka.MErr) { } return resp, nil } + +type SetSendParam struct { + Chan *uint8 `json:"channel"` + Bus *uint8 `json:"bus"` + ChanName *string `json:"channel_name"` + BusName *string `json:"bus_name"` + Unmuted *bool `json:"unmuted"` +} + +func (a *Api) handleSetSend(param SetSendParam) (string, *misirka.MErr) { + err := a.getChanByName(¶m.Chan, param.ChanName) + if err != nil { + return "", &misirka.MErr{ + Code: -1000, + Err: err, + } + } + err = a.getBusByName(¶m.Bus, param.BusName) + if err != nil { + return "", &misirka.MErr{ + Code: -1000, + Err: err, + } + } + + if param.Chan == nil || param.Bus == nil || param.Unmuted == nil { + return "", &misirka.MErr{ + Code: -1000, + Err: fmt.Errorf("missing fields (need channel, bus, unmuted)"), + } + } + err = a.ctl.SetMatrixSend(*param.Chan, *param.Bus, *param.Unmuted) + if err != nil { + return "", &misirka.MErr{ + Code: -42, + Err: err, + } + } + return "ok", nil +} + +func (a *Api) pollState() { + state, err := a.ctl.GetFullState() + if err != nil { + a.logger.Error("could not poll state", "err", err) + } + misirka.Publish(a.m, "state", state) +} + +func (a *Api) pollLevels() { + levels, err := a.ctl.GetLevels() + if err != nil { + a.logger.Error("could not poll levels", "err", err) + } + misirka.Publish(a.m, "levels", levels) +} + +func (a *Api) getChanByName(idx **uint8, name *string) error { + if name == nil { + return nil + } + for i := range a.ctl.ChanNames { + if *name == a.ctl.ChanNames[i] { + j := uint8(i) + *idx = &j + return nil + } + } + + return fmt.Errorf("no such chan: %s", *name) +} + +func (a *Api) getBusByName(idx **uint8, name *string) error { + if name == nil { + return nil + } + for i := range a.ctl.BusNames { + if *name == a.ctl.BusNames[i] { + j := uint8(i) + *idx = &j + return nil + } + } + + return fmt.Errorf("no such bus: %s", *name) +} diff --git a/software/audioctl/ctl/ctl.go b/software/audioctl/ctl/ctl.go index 10698764..e1d9a917 100644 --- a/software/audioctl/ctl/ctl.go +++ b/software/audioctl/ctl/ctl.go @@ -22,8 +22,10 @@ type Ctl struct { input chan string readerErr chan error - numChans int - numBuses int + numChans int + numBuses int + ChanNames []string + BusNames []string } func New(logger *slog.Logger, cfg *config.CtlCfg) *Ctl { diff --git a/software/audioctl/ctl/getstate.go b/software/audioctl/ctl/getstate.go index 886421b8..d1f8e0b2 100644 --- a/software/audioctl/ctl/getstate.go +++ b/software/audioctl/ctl/getstate.go @@ -82,6 +82,9 @@ func (c *Ctl) GetFullState() (*MixerState, error) { return nil, fmt.Errorf("wrong number of busVolumes (got %d, should be %d)", len(busVolumes), c.numBuses) } + c.ChanNames = chanNames + c.BusNames = busNames + m := &MixerState{ Channels: make([]ChannelState, c.numChans), Buses: make([]BusState, c.numBuses), From c2fa138350bf6f62e5a1a9ea3894164687a41d1a Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 00:42:09 +0100 Subject: [PATCH 40/67] setters for matrix --- software/audioctl/api/api.go | 24 +++++++++++----- software/audioctl/api/handlers.go | 46 +++++++++++++++++++++++++++++-- software/audioctl/ctl/setters.go | 29 +++++++++++++++++++ 3 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 software/audioctl/ctl/setters.go diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go index 27dc0fad..6b0c4cfe 100644 --- a/software/audioctl/api/api.go +++ b/software/audioctl/api/api.go @@ -11,12 +11,13 @@ import ( ) type Api struct { - srv http.Server - m *misirka.Misirka - logger *slog.Logger - cfg *config.ApiCfg - ctl *ctl.Ctl - dying chan struct{} + srv http.Server + m *misirka.Misirka + logger *slog.Logger + cfg *config.ApiCfg + ctl *ctl.Ctl + dying chan struct{} + refreshState chan struct{} } func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { @@ -25,6 +26,7 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { a.logger = logger a.ctl = ctl a.dying = make(chan struct{}) + a.refreshState = make(chan struct{}) a.m = misirka.New("/", func(err error) { logger.Error("API error", "err", err) @@ -34,7 +36,8 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { a.m.AddTopic("state") a.m.AddTopic("levels") misirka.HandleCall(a.m, "raw-cmd", a.handleRawCmd) - misirka.HandleCall(a.m, "set-send", a.handleSetSend) + misirka.HandleCall(a.m, "set-matrix-send", a.handleSetMatrixSend) + misirka.HandleCall(a.m, "set-matrix-volume", a.handleSetMatrixVolume) a.srv.Handler = a.m.Handler() a.srv.Addr = a.cfg.Bind @@ -81,6 +84,8 @@ func (a *Api) poller() { select { case <-a.dying: return + case <-a.refreshState: + a.pollState() case <-pollState.C: a.pollState() case <-pollLevels.C: @@ -88,3 +93,8 @@ func (a *Api) poller() { } } } + +func (a *Api) forceRefresh() { + // TODO: throttling + a.refreshState <- struct{}{} +} diff --git a/software/audioctl/api/handlers.go b/software/audioctl/api/handlers.go index 0e62bb77..76b51d96 100644 --- a/software/audioctl/api/handlers.go +++ b/software/audioctl/api/handlers.go @@ -17,7 +17,48 @@ func (a *Api) handleRawCmd(param string) (string, *misirka.MErr) { return resp, nil } -type SetSendParam struct { +type SetMatrixVolumeParam struct { + Chan *uint8 `json:"channel"` + Bus *uint8 `json:"bus"` + ChanName *string `json:"channel_name"` + BusName *string `json:"bus_name"` + Volume *float32 `json:"volume"` +} + +func (a *Api) handleSetMatrixVolume(param SetMatrixVolumeParam) (string, *misirka.MErr) { + err := a.getChanByName(¶m.Chan, param.ChanName) + if err != nil { + return "", &misirka.MErr{ + Code: -1000, + Err: err, + } + } + err = a.getBusByName(¶m.Bus, param.BusName) + if err != nil { + return "", &misirka.MErr{ + Code: -1000, + Err: err, + } + } + + if param.Chan == nil || param.Bus == nil || param.Volume == nil { + return "", &misirka.MErr{ + Code: -1000, + Err: fmt.Errorf("missing fields (need channel, bus, unmuted)"), + } + } + err = a.ctl.SetMatrixVolume(*param.Chan, *param.Bus, *param.Volume) + if err != nil { + return "", &misirka.MErr{ + Code: -42, + Err: err, + } + } + a.forceRefresh() + return "ok", nil +} + +type SetMatrixSendParam struct { Chan *uint8 `json:"channel"` Bus *uint8 `json:"bus"` ChanName *string `json:"channel_name"` @@ -25,7 +66,7 @@ type SetSendParam struct { Unmuted *bool `json:"unmuted"` } -func (a *Api) handleSetSend(param SetSendParam) (string, *misirka.MErr) { +func (a *Api) handleSetMatrixSend(param SetMatrixSendParam) (string, *misirka.MErr) { err := a.getChanByName(¶m.Chan, param.ChanName) if err != nil { return "", &misirka.MErr{ @@ -54,6 +95,7 @@ func (a *Api) handleSetSend(param SetSendParam) (string, *misirka.MErr) { Err: err, } } + a.forceRefresh() return "ok", nil } diff --git a/software/audioctl/ctl/setters.go b/software/audioctl/ctl/setters.go new file mode 100644 index 00000000..53c3a5b5 --- /dev/null +++ b/software/audioctl/ctl/setters.go @@ -0,0 +1,29 @@ +package ctl + +import "fmt" + +func (c *Ctl) SetMatrixSend(ch uint8, bus uint8, unmuted bool) error { + if int(ch) >= c.numChans || int(bus) > c.numBuses { + return fmt.Errorf("malformed input") + } + + _, err := c.RawCmd(fmt.Sprintf("send.set %d %d %d", ch, bus, unmuted)) + if err != nil { + return err + } + + return nil +} + +func (c *Ctl) SetMatrixVolume(ch uint8, bus uint8, volume float32) error { + if int(ch) >= c.numChans || int(bus) > c.numBuses { + return fmt.Errorf("malformed input") + } + + _, err := c.RawCmd(fmt.Sprintf("volume.set %d %d %.4f", ch, bus, volume)) + if err != nil { + return err + } + + return nil +} From 9207cc88024401c29927635d77d9f69ec17bb609 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 00:59:12 +0100 Subject: [PATCH 41/67] moar setters --- software/audioctl/api/api.go | 3 + software/audioctl/api/handlers.go | 96 +++++++++++++++++++++++++++++++ software/audioctl/ctl/ctl.go | 1 + software/audioctl/ctl/setters.go | 49 +++++++++++++++- 4 files changed, 148 insertions(+), 1 deletion(-) diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go index 6b0c4cfe..1e253c98 100644 --- a/software/audioctl/api/api.go +++ b/software/audioctl/api/api.go @@ -38,6 +38,9 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { misirka.HandleCall(a.m, "raw-cmd", a.handleRawCmd) misirka.HandleCall(a.m, "set-matrix-send", a.handleSetMatrixSend) misirka.HandleCall(a.m, "set-matrix-volume", a.handleSetMatrixVolume) + misirka.HandleCall(a.m, "set-phantom", a.handleSetPhantom) + misirka.HandleCall(a.m, "set-in-gain", a.handleSetInGain) + misirka.HandleCall(a.m, "set-bus-volume", a.handleSetBusVolume) a.srv.Handler = a.m.Handler() a.srv.Addr = a.cfg.Bind diff --git a/software/audioctl/api/handlers.go b/software/audioctl/api/handlers.go index 76b51d96..5914d550 100644 --- a/software/audioctl/api/handlers.go +++ b/software/audioctl/api/handlers.go @@ -99,6 +99,102 @@ func (a *Api) handleSetMatrixSend(param SetMatrixSendParam) (string, *misirka.ME return "ok", nil } +type SetInGainParam struct { + Chan *uint8 `json:"channel"` + ChanName *string `json:"channel_name"` + Gain *float32 `json:"gain"` +} + +func (a *Api) handleSetInGain(param SetInGainParam) (string, *misirka.MErr) { + err := a.getChanByName(¶m.Chan, param.ChanName) + if err != nil { + return "", &misirka.MErr{ + Code: -1000, + Err: err, + } + } + + if param.Chan == nil || param.Gain == nil { + return "", &misirka.MErr{ + Code: -1000, + Err: fmt.Errorf("missing fields (need channel, gain)"), + } + } + err = a.ctl.SetInGain(*param.Chan, *param.Gain) + if err != nil { + return "", &misirka.MErr{ + Code: -42, + Err: err, + } + } + a.forceRefresh() + return "ok", nil +} + +type SetPhantomParam struct { + Chan *uint8 `json:"channel"` + ChanName *string `json:"channel_name"` + Phantom *bool `json:"phantom"` +} + +func (a *Api) handleSetPhantom(param SetPhantomParam) (string, *misirka.MErr) { + err := a.getChanByName(¶m.Chan, param.ChanName) + if err != nil { + return "", &misirka.MErr{ + Code: -1000, + Err: err, + } + } + + if param.Chan == nil || param.Phantom == nil { + return "", &misirka.MErr{ + Code: -1000, + Err: fmt.Errorf("missing fields (need channel, phantom)"), + } + } + err = a.ctl.SetPhantom(*param.Chan, *param.Phantom) + if err != nil { + return "", &misirka.MErr{ + Code: -42, + Err: err, + } + } + a.forceRefresh() + return "ok", nil +} + +type SetBusVolumeParam struct { + Bus *uint8 `json:"bus"` + BusName *string `json:"bus_name"` + Volume *float32 `json:"volume"` +} + +func (a *Api) handleSetBusVolume(param SetBusVolumeParam) (string, *misirka.MErr) { + err := a.getBusByName(¶m.Bus, param.BusName) + if err != nil { + return "", &misirka.MErr{ + Code: -1000, + Err: err, + } + } + + if param.Bus == nil || param.Volume == nil { + return "", &misirka.MErr{ + Code: -1000, + Err: fmt.Errorf("missing fields (need bus, volume)"), + } + } + err = a.ctl.SetBusVolume(*param.Bus, *param.Volume) + if err != nil { + return "", &misirka.MErr{ + Code: -42, + Err: err, + } + } + a.forceRefresh() + return "ok", nil +} + func (a *Api) pollState() { state, err := a.ctl.GetFullState() if err != nil { diff --git a/software/audioctl/ctl/ctl.go b/software/audioctl/ctl/ctl.go index e1d9a917..7a7343fb 100644 --- a/software/audioctl/ctl/ctl.go +++ b/software/audioctl/ctl/ctl.go @@ -83,6 +83,7 @@ func (c *Ctl) RawCmd(argstr string) (string, error) { select { case data := <-c.input: if resp, err, ok := c.parseResponse(data); ok { + c.logger.Debug("cmd", "argstr", argstr, "resp", resp) return resp, err } c.handleUnsolicitedInput(data) diff --git a/software/audioctl/ctl/setters.go b/software/audioctl/ctl/setters.go index 53c3a5b5..fe500073 100644 --- a/software/audioctl/ctl/setters.go +++ b/software/audioctl/ctl/setters.go @@ -7,7 +7,7 @@ func (c *Ctl) SetMatrixSend(ch uint8, bus uint8, unmuted bool) error { return fmt.Errorf("malformed input") } - _, err := c.RawCmd(fmt.Sprintf("send.set %d %d %d", ch, bus, unmuted)) + _, err := c.RawCmd(fmt.Sprintf("send.set %d %d %d", ch, bus, boolToNum(unmuted))) if err != nil { return err } @@ -27,3 +27,50 @@ func (c *Ctl) SetMatrixVolume(ch uint8, bus uint8, volume float32) error { return nil } + +func (c *Ctl) SetInGain(ch uint8, gain float32) error { + if int(ch) >= c.numChans { + return fmt.Errorf("malformed input") + } + + _, err := c.RawCmd(fmt.Sprintf("in-gain.set %d %.4f", ch, gain)) + if err != nil { + return err + } + + return nil +} + +func (c *Ctl) SetPhantom(ch uint8, phantom bool) error { + if int(ch) >= c.numChans { + return fmt.Errorf("malformed input") + } + + _, err := c.RawCmd(fmt.Sprintf("phantom.set %d %d", ch, boolToNum(phantom))) + if err != nil { + return err + } + + return nil +} + +func (c *Ctl) SetBusVolume(bus uint8, volume float32) error { + if int(bus) > c.numBuses { + return fmt.Errorf("malformed input") + } + + _, err := c.RawCmd(fmt.Sprintf("bus-volume.set %d %.4f", bus, volume)) + if err != nil { + return err + } + + return nil +} + +func boolToNum(b bool) int { + if b { + return 1 + } else { + return 0 + } +} From 538705a93acd0f8b4002f2942a08bc15f549958f Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 01:13:27 +0100 Subject: [PATCH 42/67] factory reset --- software/audioctl/api/api.go | 1 + software/audioctl/api/handlers.go | 15 +++++++++++++++ software/audioctl/ctl/setters.go | 9 +++++++++ 3 files changed, 25 insertions(+) diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go index 1e253c98..688a828b 100644 --- a/software/audioctl/api/api.go +++ b/software/audioctl/api/api.go @@ -41,6 +41,7 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { misirka.HandleCall(a.m, "set-phantom", a.handleSetPhantom) misirka.HandleCall(a.m, "set-in-gain", a.handleSetInGain) misirka.HandleCall(a.m, "set-bus-volume", a.handleSetBusVolume) + misirka.HandleCall(a.m, "factory-reset", a.handleFactoryReset) a.srv.Handler = a.m.Handler() a.srv.Addr = a.cfg.Bind diff --git a/software/audioctl/api/handlers.go b/software/audioctl/api/handlers.go index 5914d550..f49efab3 100644 --- a/software/audioctl/api/handlers.go +++ b/software/audioctl/api/handlers.go @@ -163,6 +163,21 @@ func (a *Api) handleSetPhantom(param SetPhantomParam) (string, *misirka.MErr) { return "ok", nil } +type FactoryResetParam struct { +} + +func (a *Api) handleFactoryReset(param FactoryResetParam) (string, *misirka.MErr) { + err := a.ctl.FactoryReset() + if err != nil { + return "", &misirka.MErr{ + Code: -42, + Err: err, + } + } + a.forceRefresh() + return "ok", nil +} + type SetBusVolumeParam struct { Bus *uint8 `json:"bus"` BusName *string `json:"bus_name"` diff --git a/software/audioctl/ctl/setters.go b/software/audioctl/ctl/setters.go index fe500073..f2169d5e 100644 --- a/software/audioctl/ctl/setters.go +++ b/software/audioctl/ctl/setters.go @@ -67,6 +67,15 @@ func (c *Ctl) SetBusVolume(bus uint8, volume float32) error { return nil } +func (c *Ctl) FactoryReset() error { + _, err := c.RawCmd("factory-reset") + if err != nil { + return err + } + + return nil +} + func boolToNum(b bool) int { if b { return 1 From 747df2a2ff411934a2a908e9ad775b595edb2496 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 02:12:36 +0100 Subject: [PATCH 43/67] websocket support --- software/audioctl/go.mod | 1 + software/audioctl/go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/software/audioctl/go.mod b/software/audioctl/go.mod index 3b964993..fcf15c4b 100644 --- a/software/audioctl/go.mod +++ b/software/audioctl/go.mod @@ -13,5 +13,6 @@ require ( require ( github.com/creack/goselect v0.1.2 // indirect github.com/goccy/go-json v0.10.6 // indirect + github.com/gorilla/websocket v1.5.3 // indirect golang.org/x/sys v0.19.0 // indirect ) diff --git a/software/audioctl/go.sum b/software/audioctl/go.sum index 5ec1f6d0..40379532 100644 --- a/software/audioctl/go.sum +++ b/software/audioctl/go.sum @@ -6,6 +6,8 @@ github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= From be8fce3ba6e88735691b226946929185f591348c Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 02:12:57 +0100 Subject: [PATCH 44/67] remove replace line --- software/audioctl/go.mod | 2 -- 1 file changed, 2 deletions(-) diff --git a/software/audioctl/go.mod b/software/audioctl/go.mod index fcf15c4b..07bb0e00 100644 --- a/software/audioctl/go.mod +++ b/software/audioctl/go.mod @@ -2,8 +2,6 @@ module github.com/fosdem/video/software/audioctl go 1.25.5 -replace github.com/dexterlb/misirka/go => /home/human/s/git/misirka/go - require ( github.com/dexterlb/misirka/go v0.0.0-00010101000000-000000000000 github.com/goccy/go-yaml v1.19.2 From b56ff9b9fe9b5c2c264ff609fd4e5745c9e1713c Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 15:01:04 +0100 Subject: [PATCH 45/67] add web ui --- software/audioctl_ui/.gitignore | 2 + software/audioctl_ui/assets/favicon.png | Bin 0 -> 73873 bytes software/audioctl_ui/build.sh | 17 + software/audioctl_ui/index.html | 14 + software/audioctl_ui/package-lock.json | 1435 +++++++++++++++++++++++ software/audioctl_ui/package.json | 24 + software/audioctl_ui/tsconfig.app.json | 19 + software/audioctl_ui/tsconfig.json | 7 + software/audioctl_ui/tsconfig.node.json | 25 + 9 files changed, 1543 insertions(+) create mode 100644 software/audioctl_ui/.gitignore create mode 100644 software/audioctl_ui/assets/favicon.png create mode 100755 software/audioctl_ui/build.sh create mode 100644 software/audioctl_ui/index.html create mode 100644 software/audioctl_ui/package-lock.json create mode 100644 software/audioctl_ui/package.json create mode 100644 software/audioctl_ui/tsconfig.app.json create mode 100644 software/audioctl_ui/tsconfig.json create mode 100644 software/audioctl_ui/tsconfig.node.json diff --git a/software/audioctl_ui/.gitignore b/software/audioctl_ui/.gitignore new file mode 100644 index 00000000..f06235c4 --- /dev/null +++ b/software/audioctl_ui/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/software/audioctl_ui/assets/favicon.png b/software/audioctl_ui/assets/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..3228e30bc552fc6035ebbc0b473daaa1c464b4c4 GIT binary patch literal 73873 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 + + + + + + + Audio control + + +
+ + + diff --git a/software/audioctl_ui/package-lock.json b/software/audioctl_ui/package-lock.json new file mode 100644 index 00000000..a9edba2a --- /dev/null +++ b/software/audioctl_ui/package-lock.json @@ -0,0 +1,1435 @@ +{ + "name": "audioctl_ui", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "audioctl_ui", + "version": "0.0.0", + "devDependencies": { + "@types/node": "^25.2.2", + "node": "^25.6.0", + "prettier": "^3.8.1", + "typescript": "~5.9.3", + "vite": "^7.3.1", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-singlefile": "^2.3.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", + "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", + "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", + "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", + "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", + "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", + "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", + "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", + "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", + "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", + "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", + "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", + "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", + "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", + "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", + "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", + "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", + "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", + "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", + "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", + "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", + "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", + "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", + "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", + "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", + "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node": { + "version": "25.8.1", + "resolved": "https://registry.npmjs.org/node/-/node-25.8.1.tgz", + "integrity": "sha512-AG1iDDN7Uzku6rFQTjfrqZZRjur8YPkO73PnfDfoj5348DQmFACA8D4X8EhvO8ObIntENK5e/aVviy+sFsAbOA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-bin-setup": "^1.0.0" + }, + "bin": { + "node": "bin/node" + }, + "engines": { + "npm": ">=5.0.0" + } + }, + "node_modules/node-bin-setup": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/node-bin-setup/-/node-bin-setup-1.1.4.tgz", + "integrity": "sha512-vWNHOne0ZUavArqPP5LJta50+S8R261Fr5SvGul37HbEDcowvLjwdvd0ZeSr0r2lTSrPxl6okq9QUw8BFGiAxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/rollup": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", + "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.0", + "@rollup/rollup-android-arm64": "4.60.0", + "@rollup/rollup-darwin-arm64": "4.60.0", + "@rollup/rollup-darwin-x64": "4.60.0", + "@rollup/rollup-freebsd-arm64": "4.60.0", + "@rollup/rollup-freebsd-x64": "4.60.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", + "@rollup/rollup-linux-arm-musleabihf": "4.60.0", + "@rollup/rollup-linux-arm64-gnu": "4.60.0", + "@rollup/rollup-linux-arm64-musl": "4.60.0", + "@rollup/rollup-linux-loong64-gnu": "4.60.0", + "@rollup/rollup-linux-loong64-musl": "4.60.0", + "@rollup/rollup-linux-ppc64-gnu": "4.60.0", + "@rollup/rollup-linux-ppc64-musl": "4.60.0", + "@rollup/rollup-linux-riscv64-gnu": "4.60.0", + "@rollup/rollup-linux-riscv64-musl": "4.60.0", + "@rollup/rollup-linux-s390x-gnu": "4.60.0", + "@rollup/rollup-linux-x64-gnu": "4.60.0", + "@rollup/rollup-linux-x64-musl": "4.60.0", + "@rollup/rollup-openbsd-x64": "4.60.0", + "@rollup/rollup-openharmony-arm64": "4.60.0", + "@rollup/rollup-win32-arm64-msvc": "4.60.0", + "@rollup/rollup-win32-ia32-msvc": "4.60.0", + "@rollup/rollup-win32-x64-gnu": "4.60.0", + "@rollup/rollup-win32-x64-msvc": "4.60.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-compression": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz", + "integrity": "sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "debug": "^4.3.3", + "fs-extra": "^10.0.0" + }, + "peerDependencies": { + "vite": ">=2.0.0" + } + }, + "node_modules/vite-plugin-singlefile": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.3.2.tgz", + "integrity": "sha512-b8SxCi/gG7K298oJDcKOuZeU6gf6wIcCJAaEqUmmZXdjfuONlkyNyWZC3tEbN6QockRCNUd3it9eGTtpHGoYmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">18.0.0" + }, + "peerDependencies": { + "rollup": "^4.59.0", + "vite": "^5.4.11 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + } + } +} diff --git a/software/audioctl_ui/package.json b/software/audioctl_ui/package.json new file mode 100644 index 00000000..e7d3fe25 --- /dev/null +++ b/software/audioctl_ui/package.json @@ -0,0 +1,24 @@ +{ + "name": "audioctl_ui", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite --host", + "build": "npm run check && vite build", + "preview": "vite preview", + "check": "tsc -p tsconfig.node.json", + "format": "prettier --write ." + }, + "devDependencies": { + "@types/node": "^25.2.2", + "node": "^25.6.0", + "prettier": "^3.8.1", + "typescript": "~5.9.3", + "vite": "^7.3.1", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-singlefile": "^2.3.0" + }, + "dependencies": { + } +} diff --git a/software/audioctl_ui/tsconfig.app.json b/software/audioctl_ui/tsconfig.app.json new file mode 100644 index 00000000..6cbf02c2 --- /dev/null +++ b/software/audioctl_ui/tsconfig.app.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "resolveJsonModule": true, + /** + * Typecheck JS in `.js` files by default. + * Disable checkJs if you'd like to use dynamic types in JS. + * Note that setting allowJs false does not prevent the use + * of JS in `.svelte` files. + */ + "allowJs": true, + "checkJs": true, + "isolatedModules": true, + "moduleDetection": "force" + }, + "include": ["src/**/*.ts", "src/**/*.js"] +} diff --git a/software/audioctl_ui/tsconfig.json b/software/audioctl_ui/tsconfig.json new file mode 100644 index 00000000..1ffef600 --- /dev/null +++ b/software/audioctl_ui/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/software/audioctl_ui/tsconfig.node.json b/software/audioctl_ui/tsconfig.node.json new file mode 100644 index 00000000..f85a3990 --- /dev/null +++ b/software/audioctl_ui/tsconfig.node.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} From e15e45d921536b7dce957293375e9ddc8fdb7a0c Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 16:05:57 +0100 Subject: [PATCH 46/67] fix bugs --- software/audioctl/ctl/getstate.go | 2 +- software/audioctl_ui/package.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/software/audioctl/ctl/getstate.go b/software/audioctl/ctl/getstate.go index d1f8e0b2..2fab2676 100644 --- a/software/audioctl/ctl/getstate.go +++ b/software/audioctl/ctl/getstate.go @@ -12,7 +12,7 @@ type MixerState struct { } type ChannelState struct { - Label string `json:"label "` + Label string `json:"label"` Name string `json:"name"` Gain float32 `json:"gain"` Phantom bool `json:"phantom"` diff --git a/software/audioctl_ui/package.json b/software/audioctl_ui/package.json index e7d3fe25..746f86af 100644 --- a/software/audioctl_ui/package.json +++ b/software/audioctl_ui/package.json @@ -19,6 +19,5 @@ "vite-plugin-compression": "^0.5.1", "vite-plugin-singlefile": "^2.3.0" }, - "dependencies": { - } + "dependencies": {} } From 919420a845951a8bf38f65b8e840df73bf80b410 Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Sun, 22 Mar 2026 21:19:10 +0000 Subject: [PATCH 47/67] Add digital gain control for the TAA3040 --- hardware/firmware/audio_board/src/taa3040.cpp | 28 +++++++++++++++++++ hardware/firmware/audio_board/src/taa3040.h | 3 ++ 2 files changed, 31 insertions(+) diff --git a/hardware/firmware/audio_board/src/taa3040.cpp b/hardware/firmware/audio_board/src/taa3040.cpp index 3c65720f..bcf722fb 100644 --- a/hardware/firmware/audio_board/src/taa3040.cpp +++ b/hardware/firmware/audio_board/src/taa3040.cpp @@ -23,6 +23,10 @@ bool AudioControlTAA3040::disable(void) { return true; } +/* + * Set the gain in the PGA in steps of 1dB. The gain rainge is from 0 to 42 dB. This also controls the + * analog frontend configuration. + */ bool AudioControlTAA3040::gain(uint8_t channel, uint8_t gain, uint8_t impedance, uint8_t mode, uint8_t coupling) { uint8_t offset = channel * 5; setRegister(REG_P0_CH1_CFG0 + offset, (impedance << 2) | (mode << 5) | (coupling << 4)); @@ -30,6 +34,30 @@ bool AudioControlTAA3040::gain(uint8_t channel, uint8_t gain, uint8_t impedance, return true; } +/* + * Set the digital volume control for the channel. This ranges from -100dB to +27dB with a + * precision of 0.1 dB. + */ +bool AudioControlTAA3040::digitalGain(uint8_t channel, float gainDb) { + uint8_t offset = channel * 5; + float halfDbs = gainDb * 2; + + uint8_t steps = (uint8_t)(halfDbs + 0.5); + // 1.3 -> 2.6 -> 3 (1.5dB) + uint8_t remain = (uint8_t)(halfDbs - (float)steps * 5); + // 3 - 2.6 == 0.4 -> 2 0.1dB steps -> 1.5dB - 0.2dB == 1.3dB + + // The DigitalVolume does large adjustments in 0.5dB steps + uint8_t val = 201 + steps; + setRegister(REG_P0_CH1_CFG2 + offset, val); + + // Use the Calibration register to do fine adjustments in 0.1dB steps + val = 8 + remain; + setRegister(REG_P0_CH1_CFG3 + offset, val << 4); + + return true; +} + void AudioControlTAA3040::getAsiStatus() { uint8_t raw = getRegister(0, REG_P0_ASI_STS); if (raw == last_asi) { diff --git a/hardware/firmware/audio_board/src/taa3040.h b/hardware/firmware/audio_board/src/taa3040.h index b6265c31..d9c30d27 100644 --- a/hardware/firmware/audio_board/src/taa3040.h +++ b/hardware/firmware/audio_board/src/taa3040.h @@ -13,6 +13,8 @@ #define REG_P0_DEV_STS1 (0x77) #define REG_P0_CH1_CFG0 (0x3C) #define REG_P0_CH1_CFG1 (0x3D) +#define REG_P0_CH1_CFG2 (0x3E) +#define REG_P0_CH1_CFG3 (0x3F) #define IMPEDANCE_2k5 (0b00) #define IMPEDANCE_10k (0b01) @@ -23,6 +25,7 @@ class AudioControlTAA3040 { bool enable(); bool disable(); bool gain(uint8_t channel, uint8_t gain, uint8_t impedance, uint8_t mode, uint8_t coupling); + bool digitalGain(uint8_t channel, float gainDb); void getAsiStatus(); From 531c346df8c78a474cab271b84efd856173ea256 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 17:33:50 +0100 Subject: [PATCH 48/67] update go.mod --- software/audioctl/go.mod | 2 +- software/audioctl/go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/software/audioctl/go.mod b/software/audioctl/go.mod index 07bb0e00..4840093d 100644 --- a/software/audioctl/go.mod +++ b/software/audioctl/go.mod @@ -3,7 +3,7 @@ module github.com/fosdem/video/software/audioctl go 1.25.5 require ( - github.com/dexterlb/misirka/go v0.0.0-00010101000000-000000000000 + github.com/dexterlb/misirka/go v0.0.0-20260322141321-b53f25447e7f github.com/goccy/go-yaml v1.19.2 go.bug.st/serial v1.6.4 ) diff --git a/software/audioctl/go.sum b/software/audioctl/go.sum index 40379532..7ebd6952 100644 --- a/software/audioctl/go.sum +++ b/software/audioctl/go.sum @@ -2,6 +2,8 @@ github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0 github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dexterlb/misirka/go v0.0.0-20260322141321-b53f25447e7f h1:KyjyFdnmFIaoVShfGmBgFMkIe2wCaLU7kHnwkO6YVDg= +github.com/dexterlb/misirka/go v0.0.0-20260322141321-b53f25447e7f/go.mod h1:VZeXV8DAty9fQUq9+W3W8IRcTEI/NEElYDBj8hBOx4E= github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= From a8019384592ada021e6259d41ed5cf8d1028d2cd Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 20:53:12 +0100 Subject: [PATCH 49/67] implement sending of full state --- software/audioctl/api/api.go | 3 ++- software/audioctl/api/handlers.go | 13 ++++++++++++ software/audioctl/ctl/setters.go | 35 +++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go index 688a828b..e0e7a41a 100644 --- a/software/audioctl/api/api.go +++ b/software/audioctl/api/api.go @@ -35,12 +35,13 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { a.m.AddTopic("heartbeat") a.m.AddTopic("state") a.m.AddTopic("levels") - misirka.HandleCall(a.m, "raw-cmd", a.handleRawCmd) + misirka.HandleCall(a.m, "set-full-state", a.handleSetFullState) misirka.HandleCall(a.m, "set-matrix-send", a.handleSetMatrixSend) misirka.HandleCall(a.m, "set-matrix-volume", a.handleSetMatrixVolume) misirka.HandleCall(a.m, "set-phantom", a.handleSetPhantom) misirka.HandleCall(a.m, "set-in-gain", a.handleSetInGain) misirka.HandleCall(a.m, "set-bus-volume", a.handleSetBusVolume) + misirka.HandleCall(a.m, "raw-cmd", a.handleRawCmd) misirka.HandleCall(a.m, "factory-reset", a.handleFactoryReset) a.srv.Handler = a.m.Handler() diff --git a/software/audioctl/api/handlers.go b/software/audioctl/api/handlers.go index f49efab3..818fe04d 100644 --- a/software/audioctl/api/handlers.go +++ b/software/audioctl/api/handlers.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/dexterlb/misirka/go/misirka" + "github.com/fosdem/video/software/audioctl/ctl" ) func (a *Api) handleRawCmd(param string) (string, *misirka.MErr) { @@ -17,6 +18,18 @@ func (a *Api) handleRawCmd(param string) (string, *misirka.MErr) { return resp, nil } +func (a *Api) handleSetFullState(param *ctl.MixerState) (string, *misirka.MErr) { + err := a.ctl.SetFullState(param) + if err != nil { + return "", &misirka.MErr{ + Code: -42, + Err: err, + } + } + a.forceRefresh() + return "ok", nil +} + type SetMatrixVolumeParam struct { Chan *uint8 `json:"channel"` Bus *uint8 `json:"bus"` diff --git a/software/audioctl/ctl/setters.go b/software/audioctl/ctl/setters.go index f2169d5e..a7b1941e 100644 --- a/software/audioctl/ctl/setters.go +++ b/software/audioctl/ctl/setters.go @@ -2,6 +2,41 @@ package ctl import "fmt" +func (c *Ctl) SetFullState(state *MixerState) error { + var err error + for i := range c.numChans { + for j := range c.numBuses { + err = c.SetMatrixSend(uint8(i), uint8(j), state.Channels[i].Sends[j].Unmuted) + if err != nil { + return fmt.Errorf("could not set send %dx%d: %w", i, j, err) + } + err = c.SetMatrixVolume(uint8(i), uint8(j), state.Channels[i].Sends[j].Volume) + if err != nil { + return fmt.Errorf("could not set volume %dx%d: %w", i, j, err) + } + } + + err = c.SetInGain(uint8(i), state.Channels[i].Gain) + if err != nil { + return fmt.Errorf("could not set gain on channel %d: %w", i, err) + } + + err = c.SetPhantom(uint8(i), state.Channels[i].Phantom) + if err != nil { + return fmt.Errorf("could not set phantom on channel %d: %w", i, err) + } + } + + for j := range c.numBuses { + err = c.SetBusVolume(uint8(j), state.Buses[j].Volume) + if err != nil { + return fmt.Errorf("could not set volume on bus %d: %w", j, err) + } + } + + return nil +} + func (c *Ctl) SetMatrixSend(ch uint8, bus uint8, unmuted bool) error { if int(ch) >= c.numChans || int(bus) > c.numBuses { return fmt.Errorf("malformed input") From 412943069e14926280fc2ab20ba8c5e5225bb5db Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 21:17:26 +0100 Subject: [PATCH 50/67] split off serial ctl in preparation of fake ctl --- software/audioctl/api/api.go | 6 +- software/audioctl/api/handlers.go | 23 ++- software/audioctl/cmd/audioctl/main.go | 4 +- software/audioctl/ctl/ctl.go | 154 ++---------------- software/audioctl/ctl/state.go | 36 ++++ software/audioctl/serialctl/ctl.go | 137 ++++++++++++++++ .../audioctl/{ctl => serialctl}/getlevels.go | 27 ++- .../audioctl/{ctl => serialctl}/getstate.go | 59 ++----- .../audioctl/{ctl => serialctl}/setters.go | 22 ++- 9 files changed, 256 insertions(+), 212 deletions(-) create mode 100644 software/audioctl/ctl/state.go create mode 100644 software/audioctl/serialctl/ctl.go rename software/audioctl/{ctl => serialctl}/getlevels.go (75%) rename software/audioctl/{ctl => serialctl}/getstate.go (79%) rename software/audioctl/{ctl => serialctl}/setters.go (79%) diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go index e0e7a41a..9a98c418 100644 --- a/software/audioctl/api/api.go +++ b/software/audioctl/api/api.go @@ -15,12 +15,14 @@ type Api struct { m *misirka.Misirka logger *slog.Logger cfg *config.ApiCfg - ctl *ctl.Ctl + ctl ctl.Ctl dying chan struct{} refreshState chan struct{} + chanNames []string + busNames []string } -func New(logger *slog.Logger, cfg *config.ApiCfg, ctl *ctl.Ctl) *Api { +func New(logger *slog.Logger, cfg *config.ApiCfg, ctl ctl.Ctl) *Api { a := &Api{} a.cfg = cfg a.logger = logger diff --git a/software/audioctl/api/handlers.go b/software/audioctl/api/handlers.go index 818fe04d..db5dac43 100644 --- a/software/audioctl/api/handlers.go +++ b/software/audioctl/api/handlers.go @@ -228,6 +228,21 @@ func (a *Api) pollState() { if err != nil { a.logger.Error("could not poll state", "err", err) } + + if len(a.chanNames) != len(state.Channels) { + a.chanNames = make([]string, len(state.Channels)) + } + for i := range state.Channels { + a.chanNames[i] = state.Channels[i].Name + } + + if len(a.busNames) != len(state.Buses) { + a.busNames = make([]string, len(state.Buses)) + } + for i := range state.Buses { + a.busNames[i] = state.Buses[i].Name + } + misirka.Publish(a.m, "state", state) } @@ -243,8 +258,8 @@ func (a *Api) getChanByName(idx **uint8, name *string) error { if name == nil { return nil } - for i := range a.ctl.ChanNames { - if *name == a.ctl.ChanNames[i] { + for i := range a.chanNames { + if *name == a.chanNames[i] { j := uint8(i) *idx = &j return nil @@ -258,8 +273,8 @@ func (a *Api) getBusByName(idx **uint8, name *string) error { if name == nil { return nil } - for i := range a.ctl.BusNames { - if *name == a.ctl.BusNames[i] { + for i := range a.busNames { + if *name == a.busNames[i] { j := uint8(i) *idx = &j return nil diff --git a/software/audioctl/cmd/audioctl/main.go b/software/audioctl/cmd/audioctl/main.go index 67de3689..6d33a21c 100644 --- a/software/audioctl/cmd/audioctl/main.go +++ b/software/audioctl/cmd/audioctl/main.go @@ -7,7 +7,7 @@ import ( "github.com/fosdem/video/software/audioctl/api" "github.com/fosdem/video/software/audioctl/config" - "github.com/fosdem/video/software/audioctl/ctl" + "github.com/fosdem/video/software/audioctl/serialctl" ) func main() { @@ -24,7 +24,7 @@ func main() { os.Exit(1) } - c := ctl.New(logger.With("prefix", "ctl"), cfg.Ctl) + c := serialctl.New(logger.With("prefix", "ctl"), cfg.Ctl) go func() { err := c.Loop() if err != nil { diff --git a/software/audioctl/ctl/ctl.go b/software/audioctl/ctl/ctl.go index 7a7343fb..f1795a00 100644 --- a/software/audioctl/ctl/ctl.go +++ b/software/audioctl/ctl/ctl.go @@ -1,139 +1,21 @@ package ctl -import ( - "bufio" - "fmt" - "log/slog" - "strings" - "time" - - "github.com/fosdem/video/software/audioctl/config" - "go.bug.st/serial" -) - -type Ctl struct { - cfg *config.CtlCfg - logger *slog.Logger - port serial.Port - - slug uint16 - - reqs chan *requestSync - input chan string - readerErr chan error - - numChans int - numBuses int - ChanNames []string - BusNames []string -} - -func New(logger *slog.Logger, cfg *config.CtlCfg) *Ctl { - c := &Ctl{} - c.logger = logger - c.cfg = cfg - c.input = make(chan string) - c.readerErr = make(chan error) - c.reqs = make(chan *requestSync) - return c -} - -func (c *Ctl) Loop() error { - var err error - mode := &serial.Mode{ - BaudRate: 115200, - } - c.port, err = serial.Open(c.cfg.PortDevice, mode) - if err != nil { - return fmt.Errorf("could not open %s: %w", c.cfg.PortDevice, err) - } - defer c.port.Close() - - c.logger.Info("successfully opened serial", "port", c.cfg.PortDevice) - - go c.reader() - - for { - select { - case err := <-c.readerErr: - return fmt.Errorf("could not read from port: %w", err) - case data := <-c.input: - c.handleUnsolicitedInput(data) - case req := <-c.reqs: - <-req.Finish - } - } -} - -func (c *Ctl) RawCmd(argstr string) (string, error) { - req := newReq() - defer close(req.Finish) - c.reqs <- req - - c.slug += 1 - if c.slug == 0 { - c.slug = 1 - } - - fmt.Fprintf(c.port, "%d %s\n", c.slug, argstr) - - timeout := time.After(1 * time.Second) - - for { - select { - case data := <-c.input: - if resp, err, ok := c.parseResponse(data); ok { - c.logger.Debug("cmd", "argstr", argstr, "resp", resp) - return resp, err - } - c.handleUnsolicitedInput(data) - case <-timeout: - return "", fmt.Errorf("request to hardware timed out") - } - } -} - -// requestSync is used to serialise requests -type requestSync struct { - Finish chan struct{} -} - -func newReq() *requestSync { - return &requestSync{ - Finish: make(chan struct{}), - } -} - -func (c *Ctl) parseResponse(data string) (string, error, bool) { - okprefix := fmt.Sprintf("[%d ok]", c.slug) - if resp, ok := strings.CutPrefix(data, okprefix); ok { - return strings.TrimSpace(resp), nil, true - } - - failprefix := fmt.Sprintf("[%d err]", c.slug) - if resp, ok := strings.CutPrefix(data, failprefix); ok { - err := fmt.Errorf("hardware responded with error: %s", strings.TrimSpace(resp)) - return "", err, true - } - return "", nil, false -} - -func (c *Ctl) handleUnsolicitedInput(data string) { - if msg, ok := strings.CutPrefix(data, "[log] "); ok { - c.logger.Info("log", "msg", msg) - return - } - c.logger.Error("unsolicited data received", "data", data) -} - -func (c *Ctl) reader() { - scanner := bufio.NewScanner(c.port) - for scanner.Scan() { - c.input <- scanner.Text() - } - err := scanner.Err() - if err == nil { - err = fmt.Errorf("port was probably closed") - } - c.readerErr <- err +type Ctl interface { + GetFullState() (*MixerState, error) + GetChannelNames() ([]string, []string, error) + GetChannelLabels() ([]string, []string, error) + GetInputGains() ([]float32, error) + GetBusVolumes() ([]float32, error) + GetPhantoms() ([]bool, error) + GetMatrix() ([]SendState, error) + SetFullState(state *MixerState) error + SetMatrixSend(ch uint8, bus uint8, unmuted bool) error + SetMatrixVolume(ch uint8, bus uint8, volume float32) error + SetInGain(ch uint8, gain float32) error + SetPhantom(ch uint8, phantom bool) error + SetBusVolume(bus uint8, volume float32) error + FactoryReset() error + Loop() error + RawCmd(argstr string) (string, error) + GetLevels() (*Levels, error) } diff --git a/software/audioctl/ctl/state.go b/software/audioctl/ctl/state.go new file mode 100644 index 00000000..0bde9331 --- /dev/null +++ b/software/audioctl/ctl/state.go @@ -0,0 +1,36 @@ +package ctl + +type MixerState struct { + Channels []ChannelState `json:"channels"` + Buses []BusState `json:"buses"` +} + +type ChannelState struct { + Label string `json:"label"` + Name string `json:"name"` + Gain float32 `json:"gain"` + Phantom bool `json:"phantom"` + Sends []SendState `json:"sends"` +} + +type BusState struct { + Label string `json:"label"` + Name string `json:"name"` + Volume float32 `json:"volume"` +} + +type SendState struct { + Unmuted bool `json:"unmuted"` + Volume float32 `json:"volume"` +} + +type Levels struct { + RMS LevelsBlock `json:"rms"` + Peak LevelsBlock `json:"peak"` + Smooth LevelsBlock `json:"smooth"` +} + +type LevelsBlock struct { + Input []float32 `json:"inputs"` + Bus []float32 `json:"buses"` +} diff --git a/software/audioctl/serialctl/ctl.go b/software/audioctl/serialctl/ctl.go new file mode 100644 index 00000000..2b4c12c5 --- /dev/null +++ b/software/audioctl/serialctl/ctl.go @@ -0,0 +1,137 @@ +package serialctl + +import ( + "bufio" + "fmt" + "log/slog" + "strings" + "time" + + "github.com/fosdem/video/software/audioctl/config" + "go.bug.st/serial" +) + +type SerialCtl struct { + cfg *config.CtlCfg + logger *slog.Logger + port serial.Port + + slug uint16 + + reqs chan *requestSync + input chan string + readerErr chan error + + numChans int + numBuses int +} + +func New(logger *slog.Logger, cfg *config.CtlCfg) *SerialCtl { + c := &SerialCtl{} + c.logger = logger + c.cfg = cfg + c.input = make(chan string) + c.readerErr = make(chan error) + c.reqs = make(chan *requestSync) + return c +} + +func (c *SerialCtl) Loop() error { + var err error + mode := &serial.Mode{ + BaudRate: 115200, + } + c.port, err = serial.Open(c.cfg.PortDevice, mode) + if err != nil { + return fmt.Errorf("could not open %s: %w", c.cfg.PortDevice, err) + } + defer c.port.Close() + + c.logger.Info("successfully opened serial", "port", c.cfg.PortDevice) + + go c.reader() + + for { + select { + case err := <-c.readerErr: + return fmt.Errorf("could not read from port: %w", err) + case data := <-c.input: + c.handleUnsolicitedInput(data) + case req := <-c.reqs: + <-req.Finish + } + } +} + +func (c *SerialCtl) RawCmd(argstr string) (string, error) { + req := newReq() + defer close(req.Finish) + c.reqs <- req + + c.slug += 1 + if c.slug == 0 { + c.slug = 1 + } + + fmt.Fprintf(c.port, "%d %s\n", c.slug, argstr) + + timeout := time.After(1 * time.Second) + + for { + select { + case data := <-c.input: + if resp, err, ok := c.parseResponse(data); ok { + c.logger.Debug("cmd", "argstr", argstr, "resp", resp) + return resp, err + } + c.handleUnsolicitedInput(data) + case <-timeout: + return "", fmt.Errorf("request to hardware timed out") + } + } +} + +// requestSync is used to serialise requests +type requestSync struct { + Finish chan struct{} +} + +func newReq() *requestSync { + return &requestSync{ + Finish: make(chan struct{}), + } +} + +func (c *SerialCtl) parseResponse(data string) (string, error, bool) { + okprefix := fmt.Sprintf("[%d ok]", c.slug) + if resp, ok := strings.CutPrefix(data, okprefix); ok { + return strings.TrimSpace(resp), nil, true + } + + failprefix := fmt.Sprintf("[%d err]", c.slug) + if resp, ok := strings.CutPrefix(data, failprefix); ok { + err := fmt.Errorf("hardware responded with error: %s", strings.TrimSpace(resp)) + return "", err, true + } + return "", nil, false +} + +func (c *SerialCtl) handleUnsolicitedInput(data string) { + if msg, ok := strings.CutPrefix(data, "[log] "); ok { + c.logger.Info("log", "msg", msg) + return + } + c.logger.Error("unsolicited data received", "data", data) +} + +func (c *SerialCtl) reader() { + scanner := bufio.NewScanner(c.port) + for scanner.Scan() { + c.input <- scanner.Text() + } + err := scanner.Err() + if err == nil { + err = fmt.Errorf("port was probably closed") + } + c.readerErr <- err +} diff --git a/software/audioctl/ctl/getlevels.go b/software/audioctl/serialctl/getlevels.go similarity index 75% rename from software/audioctl/ctl/getlevels.go rename to software/audioctl/serialctl/getlevels.go index c2a035dd..6cb61bb2 100644 --- a/software/audioctl/ctl/getlevels.go +++ b/software/audioctl/serialctl/getlevels.go @@ -1,19 +1,12 @@ -package ctl +package serialctl -import "fmt" +import ( + "fmt" -type Levels struct { - RMS LevelsBlock `json:"rms"` - Peak LevelsBlock `json:"peak"` - Smooth LevelsBlock `json:"smooth"` -} - -type LevelsBlock struct { - Input []float32 `json:"inputs"` - Bus []float32 `json:"buses"` -} + "github.com/fosdem/video/software/audioctl/ctl" +) -func (c *Ctl) GetLevels() (*Levels, error) { +func (c *SerialCtl) GetLevels() (*ctl.Levels, error) { if c.numChans == 0 || c.numBuses == 0 { return nil, fmt.Errorf("no channels/buses known (maybe GetFullState() did not get called)") } @@ -44,16 +37,16 @@ func (c *Ctl) GetLevels() (*Levels, error) { if len(vals) != (c.numChans+c.numBuses)*3 { return nil, fmt.Errorf("got %d values instead of %d", len(vals), (c.numChans+c.numBuses)*3) } - levels := &Levels{ - RMS: LevelsBlock{ + levels := &ctl.Levels{ + RMS: ctl.LevelsBlock{ Input: make([]float32, c.numChans), Bus: make([]float32, c.numBuses), }, - Peak: LevelsBlock{ + Peak: ctl.LevelsBlock{ Input: make([]float32, c.numChans), Bus: make([]float32, c.numBuses), }, - Smooth: LevelsBlock{ + Smooth: ctl.LevelsBlock{ Input: make([]float32, c.numChans), Bus: make([]float32, c.numBuses), }, diff --git a/software/audioctl/ctl/getstate.go b/software/audioctl/serialctl/getstate.go similarity index 79% rename from software/audioctl/ctl/getstate.go rename to software/audioctl/serialctl/getstate.go index 2fab2676..ed1ac769 100644 --- a/software/audioctl/ctl/getstate.go +++ b/software/audioctl/serialctl/getstate.go @@ -1,36 +1,14 @@ -package ctl +package serialctl import ( "fmt" "strconv" "strings" -) - -type MixerState struct { - Channels []ChannelState `json:"channels"` - Buses []BusState `json:"buses"` -} - -type ChannelState struct { - Label string `json:"label"` - Name string `json:"name"` - Gain float32 `json:"gain"` - Phantom bool `json:"phantom"` - Sends []SendState `json:"sends"` -} -type BusState struct { - Label string `json:"label"` - Name string `json:"name"` - Volume float32 `json:"volume"` -} - -type SendState struct { - Unmuted bool `json:"unmuted"` - Volume float32 `json:"volume"` -} + "github.com/fosdem/video/software/audioctl/ctl" +) -func (c *Ctl) GetFullState() (*MixerState, error) { +func (c *SerialCtl) GetFullState() (*ctl.MixerState, error) { chanNames, busNames, err := c.GetChannelNames() if err != nil { return nil, fmt.Errorf("could not get channel names: %w", err) @@ -82,12 +60,9 @@ func (c *Ctl) GetFullState() (*MixerState, error) { return nil, fmt.Errorf("wrong number of busVolumes (got %d, should be %d)", len(busVolumes), c.numBuses) } - c.ChanNames = chanNames - c.BusNames = busNames - - m := &MixerState{ - Channels: make([]ChannelState, c.numChans), - Buses: make([]BusState, c.numBuses), + m := &ctl.MixerState{ + Channels: make([]ctl.ChannelState, c.numChans), + Buses: make([]ctl.BusState, c.numBuses), } for i := range m.Channels { @@ -95,7 +70,7 @@ func (c *Ctl) GetFullState() (*MixerState, error) { m.Channels[i].Name = chanNames[i] m.Channels[i].Gain = gains[i] m.Channels[i].Phantom = phantoms[i] - m.Channels[i].Sends = make([]SendState, c.numBuses) + m.Channels[i].Sends = make([]ctl.SendState, c.numBuses) for j := range m.Buses { m.Channels[i].Sends[j] = sends[i*c.numBuses+j] } @@ -110,7 +85,7 @@ func (c *Ctl) GetFullState() (*MixerState, error) { return m, nil } -func (c *Ctl) GetChannelNames() ([]string, []string, error) { +func (c *SerialCtl) GetChannelNames() ([]string, []string, error) { resp, err := c.RawCmd("channel.names") if err != nil { return nil, nil, err @@ -118,7 +93,7 @@ func (c *Ctl) GetChannelNames() ([]string, []string, error) { return parseChannelBusList(resp, "channels", "buses") } -func (c *Ctl) GetChannelLabels() ([]string, []string, error) { +func (c *SerialCtl) GetChannelLabels() ([]string, []string, error) { resp, err := c.RawCmd("channel.labels") if err != nil { return nil, nil, err @@ -126,7 +101,7 @@ func (c *Ctl) GetChannelLabels() ([]string, []string, error) { return parseChannelBusList(resp, "channels", "buses") } -func (c *Ctl) GetInputGains() ([]float32, error) { +func (c *SerialCtl) GetInputGains() ([]float32, error) { resp, err := c.RawCmd("gains") if err != nil { return nil, err @@ -134,7 +109,7 @@ func (c *Ctl) GetInputGains() ([]float32, error) { return parseFloatList(resp) } -func (c *Ctl) GetBusVolumes() ([]float32, error) { +func (c *SerialCtl) GetBusVolumes() ([]float32, error) { resp, err := c.RawCmd("bus-volumes") if err != nil { return nil, err @@ -142,7 +117,7 @@ func (c *Ctl) GetBusVolumes() ([]float32, error) { return parseFloatList(resp) } -func (c *Ctl) GetPhantoms() ([]bool, error) { +func (c *SerialCtl) GetPhantoms() ([]bool, error) { resp, err := c.RawCmd("phantoms") if err != nil { return nil, err @@ -150,14 +125,14 @@ func (c *Ctl) GetPhantoms() ([]bool, error) { return parseBoolList(resp) } -func (c *Ctl) GetMatrix() ([]SendState, error) { +func (c *SerialCtl) GetMatrix() ([]ctl.SendState, error) { resp, err := c.RawCmd("matrix") if err != nil { return nil, err } fields := strings.Fields(resp) - result := make([]SendState, len(fields)) + result := make([]ctl.SendState, len(fields)) for i, f := range fields { result[i], err = parseSend(f) if err != nil { @@ -168,8 +143,8 @@ func (c *Ctl) GetMatrix() ([]SendState, error) { return result, nil } -func parseSend(s string) (SendState, error) { - var ss SendState +func parseSend(s string) (ctl.SendState, error) { + var ss ctl.SendState var err error fields := strings.Split(s, "*") diff --git a/software/audioctl/ctl/setters.go b/software/audioctl/serialctl/setters.go similarity index 79% rename from software/audioctl/ctl/setters.go rename to software/audioctl/serialctl/setters.go index a7b1941e..3f3f8dcb 100644 --- a/software/audioctl/ctl/setters.go +++ b/software/audioctl/serialctl/setters.go @@ -1,8 +1,12 @@ -package ctl +package serialctl -import "fmt" +import ( + "fmt" -func (c *Ctl) SetFullState(state *MixerState) error { + "github.com/fosdem/video/software/audioctl/ctl" +) + +func (c *SerialCtl) SetFullState(state *ctl.MixerState) error { var err error for i := range c.numChans { for j := range c.numBuses { @@ -37,7 +41,7 @@ func (c *Ctl) SetFullState(state *MixerState) error { return nil } -func (c *Ctl) SetMatrixSend(ch uint8, bus uint8, unmuted bool) error { +func (c *SerialCtl) SetMatrixSend(ch uint8, bus uint8, unmuted bool) error { if int(ch) >= c.numChans || int(bus) > c.numBuses { return fmt.Errorf("malformed input") } @@ -50,7 +54,7 @@ func (c *Ctl) SetMatrixSend(ch uint8, bus uint8, unmuted bool) error { return nil } -func (c *Ctl) SetMatrixVolume(ch uint8, bus uint8, volume float32) error { +func (c *SerialCtl) SetMatrixVolume(ch uint8, bus uint8, volume float32) error { if int(ch) >= c.numChans || int(bus) > c.numBuses { return fmt.Errorf("malformed input") } @@ -63,7 +67,7 @@ func (c *Ctl) SetMatrixVolume(ch uint8, bus uint8, volume float32) error { return nil } -func (c *Ctl) SetInGain(ch uint8, gain float32) error { +func (c *SerialCtl) SetInGain(ch uint8, gain float32) error { if int(ch) >= c.numChans { return fmt.Errorf("malformed input") } @@ -76,7 +80,7 @@ func (c *Ctl) SetInGain(ch uint8, gain float32) error { return nil } -func (c *Ctl) SetPhantom(ch uint8, phantom bool) error { +func (c *SerialCtl) SetPhantom(ch uint8, phantom bool) error { if int(ch) >= c.numChans { return fmt.Errorf("malformed input") } @@ -89,7 +93,7 @@ func (c *Ctl) SetPhantom(ch uint8, phantom bool) error { return nil } -func (c *Ctl) SetBusVolume(bus uint8, volume float32) error { +func (c *SerialCtl) SetBusVolume(bus uint8, volume float32) error { if int(bus) > c.numBuses { return fmt.Errorf("malformed input") } @@ -102,7 +106,7 @@ func (c *Ctl) SetBusVolume(bus uint8, volume float32) error { return nil } -func (c *Ctl) FactoryReset() error { +func (c *SerialCtl) FactoryReset() error { _, err := c.RawCmd("factory-reset") if err != nil { return err From dbe7cbb4a7f7b09272ecb0f5e5081859994d53a9 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sun, 22 Mar 2026 22:29:55 +0100 Subject: [PATCH 51/67] implement fake audio ctl --- software/audioctl/cmd/audioctl/main.go | 10 +- software/audioctl/fakectl/default_state.go | 68 ++++++ software/audioctl/fakectl/fakectl.go | 248 +++++++++++++++++++++ 3 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 software/audioctl/fakectl/default_state.go create mode 100644 software/audioctl/fakectl/fakectl.go diff --git a/software/audioctl/cmd/audioctl/main.go b/software/audioctl/cmd/audioctl/main.go index 6d33a21c..6319ebbe 100644 --- a/software/audioctl/cmd/audioctl/main.go +++ b/software/audioctl/cmd/audioctl/main.go @@ -7,6 +7,8 @@ import ( "github.com/fosdem/video/software/audioctl/api" "github.com/fosdem/video/software/audioctl/config" + "github.com/fosdem/video/software/audioctl/ctl" + "github.com/fosdem/video/software/audioctl/fakectl" "github.com/fosdem/video/software/audioctl/serialctl" ) @@ -24,7 +26,13 @@ func main() { os.Exit(1) } - c := serialctl.New(logger.With("prefix", "ctl"), cfg.Ctl) + var c ctl.Ctl + if cfg.Ctl.PortDevice == "fake" { + logger.Info("using fake ctl") + c = fakectl.New(cfg.Ctl) + } else { + c = serialctl.New(logger.With("prefix", "ctl"), cfg.Ctl) + } go func() { err := c.Loop() if err != nil { diff --git a/software/audioctl/fakectl/default_state.go b/software/audioctl/fakectl/default_state.go new file mode 100644 index 00000000..f9342bb9 --- /dev/null +++ b/software/audioctl/fakectl/default_state.go @@ -0,0 +1,68 @@ +package fakectl + +import "github.com/fosdem/video/software/audioctl/ctl" + +var defaultState *ctl.MixerState = &ctl.MixerState{ + Channels: []ctl.ChannelState{ + { + Name: "fubalina", + Label: "foo", + Gain: 3, + Phantom: false, + Sends: []ctl.SendState{ + { + Unmuted: true, + Volume: 1, + }, + { + Unmuted: false, + Volume: 1, + }, + }, + }, + { + Name: "barabela", + Label: "bar", + Gain: 8, + Phantom: false, + Sends: []ctl.SendState{ + { + Unmuted: false, + Volume: 1, + }, + { + Unmuted: true, + Volume: 0.42, + }, + }, + }, + { + Name: "bazinga", + Label: "baz", + Gain: 42, + Phantom: true, + Sends: []ctl.SendState{ + { + Unmuted: false, + Volume: 1, + }, + { + Unmuted: true, + Volume: 0.82, + }, + }, + }, + }, + Buses: []ctl.BusState{ + { + Name: "penka", + Label: "pe", + Volume: 0.4, + }, + { + Name: "donka", + Label: "do", + Volume: 0.4, + }, + }, +} diff --git a/software/audioctl/fakectl/fakectl.go b/software/audioctl/fakectl/fakectl.go new file mode 100644 index 00000000..9f908cd6 --- /dev/null +++ b/software/audioctl/fakectl/fakectl.go @@ -0,0 +1,248 @@ +package fakectl + +import ( + "fmt" + "math/rand" + "time" + + "github.com/fosdem/video/software/audioctl/config" + "github.com/fosdem/video/software/audioctl/ctl" +) + +type FakeCtl struct { + state *ctl.MixerState + cfg *config.CtlCfg + levels *ctl.Levels +} + +func New(cfg *config.CtlCfg) *FakeCtl { + return &FakeCtl{ + state: defaultState, + cfg: cfg, + levels: &ctl.Levels{ + RMS: ctl.LevelsBlock{ + Input: make([]float32, len(defaultState.Channels)), + Bus: make([]float32, len(defaultState.Buses)), + }, + Peak: ctl.LevelsBlock{ + Input: make([]float32, len(defaultState.Channels)), + Bus: make([]float32, len(defaultState.Buses)), + }, + Smooth: ctl.LevelsBlock{ + Input: make([]float32, len(defaultState.Channels)), + Bus: make([]float32, len(defaultState.Buses)), + }, + }, + } +} + +func (c *FakeCtl) GetFullState() (*ctl.MixerState, error) { + return c.state, nil +} + +func (c *FakeCtl) GetChannelNames() ([]string, []string, error) { + chanNames := make([]string, len(c.state.Channels)) + for i := range c.state.Channels { + chanNames[i] = c.state.Channels[i].Name + } + + busNames := make([]string, len(c.state.Buses)) + for i := range c.state.Buses { + busNames[i] = c.state.Buses[i].Name + } + + return chanNames, busNames, nil +} + +func (c *FakeCtl) GetChannelLabels() ([]string, []string, error) { + chanLabels := make([]string, len(c.state.Channels)) + for i := range c.state.Channels { + chanLabels[i] = c.state.Channels[i].Label + } + + busLabels := make([]string, len(c.state.Buses)) + for i := range c.state.Buses { + busLabels[i] = c.state.Buses[i].Label + } + + return chanLabels, busLabels, nil +} + +func (c *FakeCtl) GetInputGains() ([]float32, error) { + gains := make([]float32, len(c.state.Channels)) + for i := range c.state.Channels { + gains[i] = c.state.Channels[i].Gain + } + return gains, nil +} + +func (c *FakeCtl) GetBusVolumes() ([]float32, error) { + volumes := make([]float32, len(c.state.Buses)) + for i := range c.state.Buses { + volumes[i] = c.state.Buses[i].Volume + } + return volumes, nil +} + +func (c *FakeCtl) GetPhantoms() ([]bool, error) { + phantoms := make([]bool, len(c.state.Channels)) + for i := range c.state.Channels { + phantoms[i] = c.state.Channels[i].Phantom + } + return phantoms, nil + +} + +func (c *FakeCtl) GetMatrix() ([]ctl.SendState, error) { + result := make([]ctl.SendState, len(c.state.Channels)*len(c.state.Buses)) + for i := range c.state.Channels { + for j := range c.state.Channels[i].Sends { + result[i*len(c.state.Buses)+j] = c.state.Channels[i].Sends[j] + } + } + return result, nil +} + +func (c *FakeCtl) SetFullState(state *ctl.MixerState) error { + if len(state.Channels) != len(c.state.Channels) { + return fmt.Errorf("trying to change number of channels") + } + if len(state.Buses) != len(c.state.Buses) { + return fmt.Errorf("trying to change number of buses") + } + for i := range state.Channels { + if len(state.Channels[i].Sends) != len(state.Buses) { + return fmt.Errorf("matrix is not rectangular") + } + } + c.state = state + return nil +} + +func (c *FakeCtl) SetMatrixSend(ch uint8, bus uint8, unmuted bool) error { + if int(ch) >= len(c.state.Channels) || int(bus) > len(c.state.Buses) { + return fmt.Errorf("malformed input") + } + + c.state.Channels[ch].Sends[bus].Unmuted = unmuted + return nil +} + +func (c *FakeCtl) SetMatrixVolume(ch uint8, bus uint8, volume float32) error { + if int(ch) >= len(c.state.Channels) || int(bus) > len(c.state.Buses) { + return fmt.Errorf("malformed input") + } + + c.state.Channels[ch].Sends[bus].Volume = volume + return nil +} + +func (c *FakeCtl) SetInGain(ch uint8, gain float32) error { + if int(ch) >= len(c.state.Channels) { + return fmt.Errorf("malformed input") + } + + c.state.Channels[ch].Gain = gain + return nil +} + +func (c *FakeCtl) SetPhantom(ch uint8, phantom bool) error { + if int(ch) >= len(c.state.Channels) { + return fmt.Errorf("malformed input") + } + + c.state.Channels[ch].Phantom = phantom + return nil +} + +func (c *FakeCtl) SetBusVolume(bus uint8, volume float32) error { + if int(bus) > len(c.state.Buses) { + return fmt.Errorf("malformed input") + } + + c.state.Buses[bus].Volume = volume + return nil +} + +func (c *FakeCtl) FactoryReset() error { + c.state = defaultState + return nil +} + +func (c *FakeCtl) Loop() error { + lowThresholds := make([]float32, len(c.state.Channels)+len(c.state.Buses)) + highThresholds := make([]float32, len(c.state.Channels)+len(c.state.Buses)) + diffs := make([]float32, len(c.state.Channels)+len(c.state.Buses)) + + for { + time.Sleep(10 * time.Millisecond) + + for i := range diffs { + var curVal *float32 + if i < len(c.state.Channels) { + curVal = &c.levels.RMS.Input[i] + } else { + curVal = &c.levels.RMS.Bus[i-len(c.state.Channels)] + } + + if diffs[i] == 0 { + diffs[i] = -0.4 - rand.Float32() + } + + if diffs[i] > 0 && *curVal > highThresholds[i] { + diffs[i] = -0.4 - rand.Float32() + highThresholds[i] = -100*rand.Float32() + 4 + } + if diffs[i] < 0 && *curVal < lowThresholds[i] { + diffs[i] = 0.4 + rand.Float32() + lowThresholds[i] = -50*rand.Float32() - 70 + } + + *curVal += diffs[i] + } + + for i := range c.levels.RMS.Input { + // TODO: prettier fake peaks and smooths + c.levels.Peak.Input[i] = c.levels.RMS.Input[i] + c.levels.Smooth.Input[i] = c.levels.RMS.Input[i] + } + for i := range c.levels.RMS.Bus { + // TODO: prettier fake peaks and smooths + c.levels.Peak.Bus[i] = c.levels.RMS.Bus[i] + c.levels.Smooth.Bus[i] = c.levels.RMS.Bus[i] + } + } +} + +func (c *FakeCtl) RawCmd(argstr string) (string, error) { + return "", fmt.Errorf("this API does not support raw input (unlike yo mama)") +} + +func (c *FakeCtl) GetLevels() (*ctl.Levels, error) { + return &ctl.Levels{ + RMS: ctl.LevelsBlock{ + Input: c.copyLevels(c.levels.RMS.Input), + Bus: c.copyLevels(c.levels.RMS.Bus), + }, + Peak: ctl.LevelsBlock{ + Input: c.copyLevels(c.levels.Peak.Input), + Bus: c.copyLevels(c.levels.Peak.Bus), + }, + Smooth: ctl.LevelsBlock{ + Input: c.copyLevels(c.levels.Smooth.Input), + Bus: c.copyLevels(c.levels.Smooth.Bus), + }, + }, nil +} + +func (c *FakeCtl) copyLevels(levels []float32) []float32 { + result := make([]float32, len(levels)) + for i := range result { + if *c.cfg.UseDBLevels { + result[i] = levels[i] + } else { + panic("not implemented: calculate linear from db") + } + } + return result +} From 928088854f3605fc2abec9d56f7c20d549899e1b Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 28 Mar 2026 02:53:39 +0200 Subject: [PATCH 52/67] rename all db-related functions and clear up dbu/dbv etc --- hardware/firmware/audio_board/CMakeLists.txt | 2 +- .../firmware/audio_board/src/cli/commands.cpp | 10 ++--- hardware/firmware/audio_board/src/config.h | 2 + .../audio_board/src/db_conversion.cpp | 45 +++++++++++++++++++ .../firmware/audio_board/src/db_conversion.h | 15 +++++++ hardware/firmware/audio_board/src/display.cpp | 4 +- hardware/firmware/audio_board/src/helpers.cpp | 34 -------------- hardware/firmware/audio_board/src/helpers.h | 7 --- hardware/firmware/audio_board/src/main.cpp | 1 - 9 files changed, 70 insertions(+), 50 deletions(-) create mode 100644 hardware/firmware/audio_board/src/db_conversion.cpp create mode 100644 hardware/firmware/audio_board/src/db_conversion.h delete mode 100644 hardware/firmware/audio_board/src/helpers.cpp delete mode 100644 hardware/firmware/audio_board/src/helpers.h diff --git a/hardware/firmware/audio_board/CMakeLists.txt b/hardware/firmware/audio_board/CMakeLists.txt index b026b1c9..9fdc8c2f 100644 --- a/hardware/firmware/audio_board/CMakeLists.txt +++ b/hardware/firmware/audio_board/CMakeLists.txt @@ -111,7 +111,7 @@ set(SOURCES src/main.cpp src/storage.cpp src/display.cpp - src/helpers.cpp + src/db_conversion.cpp src/teensyaudio.cpp src/taa3040.cpp src/cli/cli.cpp diff --git a/hardware/firmware/audio_board/src/cli/commands.cpp b/hardware/firmware/audio_board/src/cli/commands.cpp index 531f333a..42118dda 100644 --- a/hardware/firmware/audio_board/src/cli/commands.cpp +++ b/hardware/firmware/audio_board/src/cli/commands.cpp @@ -2,7 +2,7 @@ #include "../config.h" #include "../teensyaudio.h" -#include "../helpers.h" +#include "../db_conversion.h" #include "../debug.h" #include "../channels.h" @@ -42,16 +42,16 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { } cli->port->println(); }}, - {.name = "levels.db", .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels in db", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { + {.name = "levels.db", .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels in dBu", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { Levels& levels = audio_get_levels(); cli->prefix_ok(); for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { cli->port->print(" "); - cli->print_float_fixed(rmsToDb(levels.rms[i]), 3, 5); + cli->print_float_fixed(out_level_to_dBu(levels.rms[i]), 3, 5); cli->port->print(" "); - cli->print_float_fixed(rmsToDb(levels.peak[i]), 3, 5); + cli->print_float_fixed(out_level_to_dBu(levels.peak[i]), 3, 5); cli->port->print(" "); - cli->print_float_fixed(rmsToDb(levels.smooth[i]), 3, 5); + cli->print_float_fixed(out_level_to_dBu(levels.smooth[i]), 3, 5); } cli->port->println(); }}, diff --git a/hardware/firmware/audio_board/src/config.h b/hardware/firmware/audio_board/src/config.h index 6930bdf2..f3e2c152 100644 --- a/hardware/firmware/audio_board/src/config.h +++ b/hardware/firmware/audio_board/src/config.h @@ -6,3 +6,5 @@ #define PIN_PHANTOM_IN1 37 #define PIN_PHANTOM_IN2 36 #define PIN_PHANTOM_IN3 35 + +#define OUT_SPAN_VOLTS (1.2258f) diff --git a/hardware/firmware/audio_board/src/db_conversion.cpp b/hardware/firmware/audio_board/src/db_conversion.cpp new file mode 100644 index 00000000..32fc718d --- /dev/null +++ b/hardware/firmware/audio_board/src/db_conversion.cpp @@ -0,0 +1,45 @@ +#include +#include "config.h" + +float coef_to_dB(float x) { + // convert coefficient to dB (amplitude, not power) + return 20.0f * log10f(x); +} + +float coef_from_dB(float db) { + return powf(10, (db / 20.0f)); +} + +float volts_to_dBv(float v) { + return coef_to_dB(v); +} + +float volts_from_dBv(float dbv) { + return coef_from_dB(dbv); +} + +float volts_to_dBu(float v) { + return coef_to_dB(v / 0.775f); +} + +float volts_from_dBu(float dbu) { + return 0.775 * coef_from_dB(dbu); +} + +float scale_from_dB(float db) { + // This cursed formula is used to convert a db value + // to a 0-1 value for the purpose of displaying on a + // vu meter. It is almost linear but compresses the + // low end a bit so that e.g. -100db is just a bit lower than + // -80db, but not so that they are both zero + float e = 2.71828f; + return 0.79306f * powf(e, db * 0.0527087f); +} + +float out_level_to_dBu(float level) { + return volts_to_dBu(level * OUT_SPAN_VOLTS); +} + +float out_level_to_scale(float level) { + return scale_from_dB(out_level_to_dBu(level)); +} diff --git a/hardware/firmware/audio_board/src/db_conversion.h b/hardware/firmware/audio_board/src/db_conversion.h new file mode 100644 index 00000000..5905d96b --- /dev/null +++ b/hardware/firmware/audio_board/src/db_conversion.h @@ -0,0 +1,15 @@ +#pragma once + +float coef_to_dB(float x); +float coef_from_dB(float db); + +float volts_to_dBv(float v); +float volts_from_dBv(float dbv); + +float volts_to_dBu(float v); +float volts_from_dBu(float dbu); + +float scale_from_dB(float db); + +float out_level_to_dBu(float level); +float out_level_to_scale(float level); diff --git a/hardware/firmware/audio_board/src/display.cpp b/hardware/firmware/audio_board/src/display.cpp index 75a6f1d6..7c6cb1e0 100644 --- a/hardware/firmware/audio_board/src/display.cpp +++ b/hardware/firmware/audio_board/src/display.cpp @@ -1,6 +1,6 @@ #include "display.h" #include "channels.h" -#include "helpers.h" +#include "db_conversion.h" #include @@ -58,7 +58,7 @@ void draw_channel(float rms, int id, const ChanInfo& channel_info) { display.drawString(channel_info.label, 5 + ((id % 6) * 12), offset + (SCREEN_HEIGHT / 2) - 9); } - draw_meter(6 + (12 * (id % 6)), offset + 1, 10, (SCREEN_HEIGHT / 2) - 13, DbtoLevel(rmsToDb(rms))); + draw_meter(6 + (12 * (id % 6)), offset + 1, 10, (SCREEN_HEIGHT / 2) - 13, out_level_to_scale(rms)); } void display_update_vu(float levels_rms[CHANNELS + BUSES]) { diff --git a/hardware/firmware/audio_board/src/helpers.cpp b/hardware/firmware/audio_board/src/helpers.cpp deleted file mode 100644 index bf22c56a..00000000 --- a/hardware/firmware/audio_board/src/helpers.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include - -#define GAIN (1.2258f) - -float db(float vrms) { return 20.0f * log10f(vrms / 0.775f); } - -// the inverse eof db(vrms) -float vrms(float db) { return powf(10, (db / 20.0f)) * 0.775f; } - -float rmsToDb(float in_v) { - // 1.002 == +4 dBu - // 0.796 == +2 dBu - // 0.632 == +0 dBu - // 0.502 == -2 dBu - // 0.399 == -4 dBu - // 0.317 == -6 dBu - // 0.252 == -8 dBu - // 0.200 == -10 dBu - // 0.159 == -12 dBu - // 0.126 == -14 dBu - // 0.100 == -16 dBu - // 0.080 == -18 dBu - // 0.063 == -20 dBu - // 0.050 == -22 dBu - return db(in_v * GAIN); -} - -// the inverse of rmsToDb -float dbToRms(float db) { return vrms(db) / GAIN; } - -float DbtoLevel(float db) { - float e = 2.71828f; - return 0.79306f * powf(e, db * 0.0527087f); -} diff --git a/hardware/firmware/audio_board/src/helpers.h b/hardware/firmware/audio_board/src/helpers.h deleted file mode 100644 index d8b0e9ec..00000000 --- a/hardware/firmware/audio_board/src/helpers.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _HELPERS_H_ -#define _HELPERS_H_ - -float rmsToDb(float rms_in); -float DbtoLevel(float db); - -#endif diff --git a/hardware/firmware/audio_board/src/main.cpp b/hardware/firmware/audio_board/src/main.cpp index 8ca27904..b0464bd4 100644 --- a/hardware/firmware/audio_board/src/main.cpp +++ b/hardware/firmware/audio_board/src/main.cpp @@ -1,5 +1,4 @@ #include "config.h" -#include "helpers.h" #include #include "teensyaudio.h" From a2ca75099e84e284e0b11c0cbe0fa8136863d469 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 28 Mar 2026 20:19:29 +0200 Subject: [PATCH 53/67] convert everything to decibels --- .../firmware/audio_board/src/cli/commands.cpp | 516 +++++++++++------- hardware/firmware/audio_board/src/config.h | 2 +- .../firmware/audio_board/src/teensyaudio.cpp | 51 +- .../firmware/audio_board/src/teensyaudio.h | 15 +- .../audio_board/src/teensyaudio_defaults.cpp | 20 +- hardware/firmware/audio_board/src/types.h | 6 +- 6 files changed, 357 insertions(+), 253 deletions(-) diff --git a/hardware/firmware/audio_board/src/cli/commands.cpp b/hardware/firmware/audio_board/src/cli/commands.cpp index 42118dda..2c0454dd 100644 --- a/hardware/firmware/audio_board/src/cli/commands.cpp +++ b/hardware/firmware/audio_board/src/cli/commands.cpp @@ -7,148 +7,210 @@ #include "../channels.h" const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { - {.name = "ping", - .help = "returns `pong `", - .arghelp = "", - .num_args = -1, - .callback = [](Cli* cli) { - cli->port->printf("pong %s\n", cli->cmd); - }}, - {.name = "channel.labels", .help = "returns a list of the short labels of inputs and buses", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { - cli->prefix_ok(); - cli->port->print("channels:"); - for (uint8_t i = 0; i < CHANNELS; i++) { - cli->port->print(" "); - cli->port->print(channel_info(i).label); - } - cli->port->print("; buses:"); - for (uint8_t i = 0; i < BUSES; i++) { - cli->port->print(" "); - cli->port->print(channel_info(CHANNELS + i).label); - } - cli->port->println(); - }}, - {.name = "channel.names", .help = "returns a list of the names of inputs and buses", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { - cli->prefix_ok(); - cli->port->print("channels:"); - for (uint8_t i = 0; i < CHANNELS; i++) { - cli->port->print(" "); - cli->port->print(channel_info(i).desc); - } - cli->port->print("; buses:"); - for (uint8_t i = 0; i < BUSES; i++) { - cli->port->print(" "); - cli->port->print(channel_info(CHANNELS + i).desc); - } - cli->port->println(); - }}, - {.name = "levels.db", .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels in dBu", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { - Levels& levels = audio_get_levels(); - cli->prefix_ok(); - for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { - cli->port->print(" "); - cli->print_float_fixed(out_level_to_dBu(levels.rms[i]), 3, 5); - cli->port->print(" "); - cli->print_float_fixed(out_level_to_dBu(levels.peak[i]), 3, 5); - cli->port->print(" "); - cli->print_float_fixed(out_level_to_dBu(levels.smooth[i]), 3, 5); - } - cli->port->println(); - }}, - {.name = "levels", .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { - Levels& levels = audio_get_levels(); - cli->prefix_ok(); - for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { - cli->port->print(" "); - cli->print_float_fixed(levels.rms[i], 3, 5); - cli->port->print(" "); - cli->print_float_fixed(levels.peak[i], 3, 5); - cli->port->print(" "); - cli->print_float_fixed(levels.smooth[i], 3, 5); - } - cli->port->println(); - }}, - {.name = "matrix", .help = "for each input channel x outpub bus combination, outputs an item in the form *, where send is 0/1 and volume is a float", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { - cli->prefix_ok(); - for (uint8_t chan = 0; chan < CHANNELS; chan++) { - for (uint8_t bus = 0; bus < BUSES; bus++) { - if (is_muted(chan, bus)) { - cli->port->print(" 0*"); - } else { - cli->port->print(" 1*"); - } - cli->print_float_fixed(get_volume(chan, bus), 3, 3); - } - } - cli->port->println(); - }}, - {.name = "gains", .help = "for each input channel, returns its gain in db", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { - cli->prefix_ok(); - for (uint8_t chan = 0; chan < CHANNELS; chan++) { - cli->port->print(" "); - cli->print_float_fixed(get_channel_input_gain_db(chan), 3, 3); - } - cli->port->println(); - }}, - {.name = "phantoms", .help = "for each input channel, returns its phantom power on/off status (0/1)", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { - cli->prefix_ok(); - for (uint8_t chan = 0; chan < CHANNELS; chan++) { - if (is_phantom_on(chan)) { - cli->port->print(" 1"); - } else { - cli->port->print(" 0"); - } - } - cli->port->println(); - }}, - {.name = "bus-volumes", .help = "for each output bus, returns its volume", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { - cli->prefix_ok(); - for (uint8_t bus = 0; bus < BUSES; bus++) { - cli->port->print(" "); - cli->print_float_fixed(get_bus_volume(bus), 3, 3); - } - cli->port->println(); - }}, - {.name = "send.set", .help = "for the given channel/bus crosspoint, set the send bit in the matrix", .arghelp = " (0|1)", .num_args = 3, .callback = [](Cli* cli) { - uint16_t chan = cli->hop_uint(); - uint16_t bus = cli->hop_uint(); - uint16_t want_send = cli->hop_uint(); - if (chan >= CHANNELS) { - cli->prefix_fail(); - cli->port->printf("chan %d is invalid\n", chan); - return; - } - if (bus >= BUSES) { - cli->prefix_fail(); - cli->port->printf("bus %d is invalid\n", bus); - return; - } - if (want_send > 0) { - unmute(chan, bus); - } else { - mute(chan, bus); - } - cli->report_ok(); - }}, - {.name = "phantom.set", .help = "for the given input channel, set the phantom power on/off bit", .arghelp = " (0|1)", .num_args = 2, .callback = [](Cli* cli) { - uint16_t chan = cli->hop_uint(); - uint16_t want_phantom = cli->hop_uint(); - if (chan >= CHANNELS) { - cli->prefix_fail(); - cli->port->printf("chan %d is invalid\n", chan); - return; - } - if (want_phantom > 0) { - set_phantom_on(chan); - } else { - set_phantom_off(chan); - } - cli->report_ok(); - }}, + { + .name = "ping", + .help = "returns `pong `", + .arghelp = "", + .num_args = -1, + .callback = [](Cli* cli) { + cli->port->printf("pong %s\n", cli->cmd); + }, + }, + { + .name = "channel.labels", + .help = "returns a list of the short labels of inputs and buses", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli) { + cli->prefix_ok(); + cli->port->print("channels:"); + for (uint8_t i = 0; i < CHANNELS; i++) { + cli->port->print(" "); + cli->port->print(channel_info(i).label); + } + cli->port->print("; buses:"); + for (uint8_t i = 0; i < BUSES; i++) { + cli->port->print(" "); + cli->port->print(channel_info(CHANNELS + i).label); + } + cli->port->println(); + }, + }, + { + .name = "channel.names", + .help = "returns a list of the names of inputs and buses", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli) { + cli->prefix_ok(); + cli->port->print("channels:"); + for (uint8_t i = 0; i < CHANNELS; i++) { + cli->port->print(" "); + cli->port->print(channel_info(i).desc); + } + cli->port->print("; buses:"); + for (uint8_t i = 0; i < BUSES; i++) { + cli->port->print(" "); + cli->port->print(channel_info(CHANNELS + i).desc); + } + cli->port->println(); + }, + }, + { + .name = "levels.db", + .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels in dBu", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli) { + Levels& levels = audio_get_levels(); + cli->prefix_ok(); + for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { + cli->port->print(" "); + cli->print_float_fixed(out_level_to_dBu(levels.rms[i]), 3, 5); + cli->port->print(" "); + cli->print_float_fixed(out_level_to_dBu(levels.peak[i]), 3, 5); + cli->port->print(" "); + cli->print_float_fixed(out_level_to_dBu(levels.smooth[i]), 3, 5); + } + cli->port->println(); + }, + }, + { + .name = "levels", + .help = "for each input channel and then for each output bus, returns 3 numbers that are the rms, peak and smooth levels", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli) { + Levels& levels = audio_get_levels(); + cli->prefix_ok(); + for (uint8_t i = 0; i < CHANNELS + BUSES; i++) { + cli->port->print(" "); + cli->print_float_fixed(levels.rms[i], 3, 5); + cli->port->print(" "); + cli->print_float_fixed(levels.peak[i], 3, 5); + cli->port->print(" "); + cli->print_float_fixed(levels.smooth[i], 3, 5); + } + cli->port->println(); + }, + }, + { + .name = "matrix", + .help = "for each input channel x outpub bus combination, outputs an item in the form *, where send is 0/1 and volume is in decibels", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli) { + cli->prefix_ok(); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + for (uint8_t bus = 0; bus < BUSES; bus++) { + if (is_muted(chan, bus)) { + cli->port->print(" 0*"); + } else { + cli->port->print(" 1*"); + } + cli->print_float_fixed(get_volume_dB(chan, bus), 3, 3); + } + } + cli->port->println(); + }, + }, + { + .name = "gains", + .help = "for each input channel, returns its gain in db", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli) { + cli->prefix_ok(); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + cli->port->print(" "); + cli->print_float_fixed(get_channel_input_gain_dB(chan), 3, 3); + } + cli->port->println(); + }, + }, + { + .name = "phantoms", + .help = "for each input channel, returns its phantom power on/off status (0/1)", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli) { + cli->prefix_ok(); + for (uint8_t chan = 0; chan < CHANNELS; chan++) { + if (is_phantom_on(chan)) { + cli->port->print(" 1"); + } else { + cli->port->print(" 0"); + } + } + cli->port->println(); + }, + }, + { + .name = "bus-volumes", + .help = "for each output bus, returns its volume in dB", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli) { + cli->prefix_ok(); + for (uint8_t bus = 0; bus < BUSES; bus++) { + cli->port->print(" "); + cli->print_float_fixed(get_bus_volume_dB(bus), 3, 3); + } + cli->port->println(); + }, + }, + { + .name = "send.set", + .help = "for the given channel/bus crosspoint, set the send bit in the matrix", + .arghelp = " (0|1)", + .num_args = 3, + .callback = [](Cli* cli) { + uint16_t chan = cli->hop_uint(); + uint16_t bus = cli->hop_uint(); + uint16_t want_send = cli->hop_uint(); + if (chan >= CHANNELS) { + cli->prefix_fail(); + cli->port->printf("chan %d is invalid\n", chan); + return; + } + if (bus >= BUSES) { + cli->prefix_fail(); + cli->port->printf("bus %d is invalid\n", bus); + return; + } + if (want_send > 0) { + unmute(chan, bus); + } else { + mute(chan, bus); + } + cli->report_ok(); + }, + }, + { + .name = "phantom.set", + .help = "for the given input channel, set the phantom power on/off bit", + .arghelp = " (0|1)", + .num_args = 2, + .callback = [](Cli* cli) { + uint16_t chan = cli->hop_uint(); + uint16_t want_phantom = cli->hop_uint(); + if (chan >= CHANNELS) { + cli->prefix_fail(); + cli->port->printf("chan %d is invalid\n", chan); + return; + } + if (want_phantom > 0) { + set_phantom_on(chan); + } else { + set_phantom_off(chan); + } + cli->report_ok(); + }, + }, { .name = "volume.set", .help = "for the given channel/bus crosspoint, set the volume in the matrix", - .arghelp = " ", + .arghelp = " ", .num_args = 3, .callback = [](Cli* cli) { uint16_t chan = cli->hop_uint(); @@ -171,83 +233,121 @@ const Cli::CmdDescr Cli::cmds[Cli::num_cmds + 1] = { return; } - set_volume(chan, bus, vol); + set_volume_dB(chan, bus, vol); cli->report_ok(); }, }, - {.name = "in-gain.set", .help = "for the given input channel, set the input gain in decibels", .arghelp = " ", .num_args = 2, .callback = [](Cli* cli) { - uint16_t chan = cli->hop_uint(); - float gain = cli->hop_float(); + { + .name = "in-gain.set", + .help = "for the given input channel, set the input gain in decibels", + .arghelp = " ", + .num_args = 2, + .callback = [](Cli* cli) { + uint16_t chan = cli->hop_uint(); + float gain = cli->hop_float(); - if (chan >= CHANNELS) { - cli->prefix_fail(); - cli->port->printf("chan %d is invalid\n", chan); - return; - } - if (gain < 0) { - cli->prefix_fail(); - cli->port->printf("gain should not be negative\n"); - return; - } + if (chan >= CHANNELS) { + cli->prefix_fail(); + cli->port->printf("chan %d is invalid\n", chan); + return; + } + if (gain < 0) { + cli->prefix_fail(); + cli->port->printf("gain should not be negative\n"); + return; + } - set_channel_input_gain_db(chan, gain); + set_channel_input_gain_dB(chan, gain); - cli->report_ok(); - }}, - {.name = "bus-volume.set", .help = "for the given out bus, set its global volume to the given value", .arghelp = " ", .num_args = 2, .callback = [](Cli* cli) { - uint16_t bus = cli->hop_uint(); - float vol = cli->hop_float(); + cli->report_ok(); + }, + }, + { + .name = "bus-volume.set", + .help = "for the given out bus, set its global volume to the given value", + .arghelp = " ", + .num_args = 2, + .callback = [](Cli* cli) { + uint16_t bus = cli->hop_uint(); + float vol = cli->hop_float(); - if (bus >= BUSES) { - cli->prefix_fail(); - cli->port->printf("bus %d is invalid\n", bus); - return; - } - if (vol < 0) { - cli->prefix_fail(); - cli->port->printf("vol should not be negative\n"); - return; - } + if (bus >= BUSES) { + cli->prefix_fail(); + cli->port->printf("bus %d is invalid\n", bus); + return; + } + if (vol < 0) { + cli->prefix_fail(); + cli->port->printf("vol should not be negative\n"); + return; + } - set_bus_volume(bus, vol); + set_bus_volume_dB(bus, vol); - cli->report_ok(); - }}, - {.name = "factory-reset", .help = "clear all settings and state stored in the EEPROM", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { - audio_reset_default_state(); - audio_eeprom_save_all(); - cli->report_ok(); - }}, - {.name = "commands", .help = "get a list of available commands", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { - cli->prefix_ok(); - for (uint8_t i = 0; i < Cli::num_cmds; i++) { - cli->port->print(" "); - cli->port->print(Cli::cmds[i].name); - } - cli->port->println(); - }}, - {.name = "help", .help = "I suppose you think that was terribly clever.", .arghelp = "[command]", .num_args = -1, .callback = [](Cli* cli) { - if (cli->cmd[0] == '\0') { - cli->prefix_ok(); - cli->port->println("use `commands` to get a list of commands and then `help `; when calling a command, prefix it with a number between 1 and 65535 to use as a slug that will be printed back with the response"); - return; - } - const Cli::CmdDescr* cmd = cli->hop_cmd(); - if (!cmd) { - cli->prefix_fail(); - cli->port->println("unknown command; use `commands` for list of commands"); - return; - } - cli->prefix_ok(); - cli->print_usage(*cmd); - cli->port->println(); - }}, - {.name = "dbgtest", .help = "print some log messages to check your debug logging", .arghelp = "", .num_args = 0, .callback = [](Cli* cli) { - for (uint8_t i = 0; i < 5; i++) { - debug_print("foo "); - debug_printf("bar %d\nbaz\n", i); - } - }}, - {.name = "END"} + cli->report_ok(); + }, + }, + { + .name = "factory-reset", + .help = "clear all settings and state stored in the EEPROM", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli) { + audio_reset_default_state(); + audio_eeprom_save_all(); + cli->report_ok(); + }, + }, + { + .name = "commands", + .help = "get a list of available commands", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli) { + cli->prefix_ok(); + for (uint8_t i = 0; i < Cli::num_cmds; i++) { + cli->port->print(" "); + cli->port->print(Cli::cmds[i].name); + } + cli->port->println(); + }, + }, + { + .name = "help", + .help = "I suppose you think that was terribly clever.", + .arghelp = "[command]", + .num_args = -1, + .callback = [](Cli* cli) { + if (cli->cmd[0] == '\0') { + cli->prefix_ok(); + cli->port->println("use `commands` to get a list of commands and then `help `; when calling a command, prefix it with a number between 1 and 65535 to use as a slug that will be printed back with the response"); + return; + } + const Cli::CmdDescr* cmd = cli->hop_cmd(); + if (!cmd) { + cli->prefix_fail(); + cli->port->println("unknown command; use `commands` for list of commands"); + return; + } + cli->prefix_ok(); + cli->print_usage(*cmd); + cli->port->println(); + }, + }, + { + .name = "dbgtest", + .help = "print some log messages to check your debug logging", + .arghelp = "", + .num_args = 0, + .callback = [](Cli* cli) { + for (uint8_t i = 0; i < 5; i++) { + debug_print("foo "); + debug_printf("bar %d\nbaz\n", i); + } + }, + }, + { + .name = "END", + }, }; diff --git a/hardware/firmware/audio_board/src/config.h b/hardware/firmware/audio_board/src/config.h index f3e2c152..7c56731d 100644 --- a/hardware/firmware/audio_board/src/config.h +++ b/hardware/firmware/audio_board/src/config.h @@ -7,4 +7,4 @@ #define PIN_PHANTOM_IN2 36 #define PIN_PHANTOM_IN3 35 -#define OUT_SPAN_VOLTS (1.2258f) +#define OUT_SPAN_VOLTS (1.2258f) diff --git a/hardware/firmware/audio_board/src/teensyaudio.cpp b/hardware/firmware/audio_board/src/teensyaudio.cpp index 23581b64..375a98ae 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio.cpp @@ -2,7 +2,7 @@ #include #include "config.h" - +#include "db_conversion.h" #include "teensyaudio.h" #ifdef USE_EEPROM @@ -146,21 +146,24 @@ bool is_muted(uint8_t channel, uint8_t bus) { return !!(state.mutes & mute_mask(channel, bus)); } -float calc_real_volume(uint8_t channel, uint8_t bus, float volume) { - return volume * !is_muted(channel, bus) * state.bus_volumes[bus]; +float calc_real_volume(uint8_t channel, uint8_t bus) { + float volume_dB = state.matrix_dB[channel][bus] + state.bus_volumes_dB[bus]; + float volume = coef_from_dB(volume_dB); + return volume * !is_muted(channel, bus); } void apply_volume(uint8_t channel, uint8_t bus) { - float volume = state.matrix[channel][bus]; - raw_set_crosspoint(channel, bus, calc_real_volume(channel, bus, volume)); + raw_set_crosspoint(channel, bus, calc_real_volume(channel, bus)); } -void set_volume(uint8_t channel, uint8_t bus, float volume) { - state.matrix[channel][bus] = volume; +void set_volume_dB(uint8_t channel, uint8_t bus, float volume) { + state.matrix_dB[channel][bus] = volume; apply_volume(channel, bus); } -float get_volume(uint8_t channel, uint8_t bus) { return state.matrix[channel][bus]; } +float get_volume_dB(uint8_t channel, uint8_t bus) { + return state.matrix_dB[channel][bus]; +} void mute(uint8_t channel, uint8_t bus) { state.mutes |= mute_mask(channel, bus); @@ -172,16 +175,18 @@ void unmute(uint8_t channel, uint8_t bus) { apply_volume(channel, bus); } -void set_bus_volume(uint8_t bus, float vol) { - state.bus_volumes[bus] = vol; +void set_bus_volume_dB(uint8_t bus, float vol) { + state.bus_volumes_dB[bus] = vol; for (uint8_t channel = 0; channel < CHANNELS; ++channel) { apply_volume(channel, bus); } } -float get_bus_volume(uint8_t bus) { return state.bus_volumes[bus]; } +float get_bus_volume_dB(uint8_t bus) { + return state.bus_volumes_dB[bus]; +} -void set_channel_input_gain_db(uint8_t channel, float gain) { +void set_channel_input_gain_dB(uint8_t channel, float gain) { uint8_t whole_gain = (uint8_t)(gain + 0.5); if (gain < 1) { whole_gain = 1; @@ -190,19 +195,21 @@ void set_channel_input_gain_db(uint8_t channel, float gain) { whole_gain = 42; } - state.channel_input_gains[channel] = (float)whole_gain; + state.channel_input_gains_dB[channel] = (float)whole_gain; taa3040.gain(channel, whole_gain, IMPEDANCE_10k, 0, 0); } void apply_channel_input_gain(uint8_t channel) { - set_channel_input_gain_db(channel, state.channel_input_gains[channel]); + set_channel_input_gain_dB(channel, state.channel_input_gains_dB[channel]); } -float get_channel_input_gain_db(uint8_t channel) { - return state.channel_input_gains[channel]; +float get_channel_input_gain_dB(uint8_t channel) { + return state.channel_input_gains_dB[channel]; } -void reset_matrix() { memcpy(state.matrix, default_matrix, sizeof(state.matrix)); } +void reset_matrix() { + memcpy(state.matrix_dB, default_matrix_dB, sizeof(state.matrix_dB)); +} void reset_mutes() { memcpy(&state.mutes, &default_mutes, sizeof(state.mutes)); @@ -213,11 +220,11 @@ void reset_phantoms() { } void reset_bus_volumes() { - memcpy(state.bus_volumes, default_bus_volumes, BUSES * sizeof(float)); + memcpy(state.bus_volumes_dB, default_bus_volumes_dB, BUSES * sizeof(float)); } void reset_channel_input_gains() { - memcpy(state.channel_input_gains, default_channel_input_gains_db, CHANNELS * sizeof(float)); + memcpy(state.channel_input_gains_dB, default_channel_input_gains_dB, CHANNELS * sizeof(float)); } void apply_all() { @@ -243,7 +250,7 @@ bool matrix_ok() { uint8_t i, j; for (i = 0; i < CHANNELS; ++i) { for (j = 0; j < BUSES; ++j) { - if (isnan(state.matrix[i][j])) { + if (isnan(state.matrix_dB[i][j])) { return false; } } @@ -255,7 +262,7 @@ bool matrix_ok() { bool bus_volumes_ok() { uint8_t i; for (i = 0; i < BUSES; ++i) { - if (isnan(state.bus_volumes[i])) { + if (isnan(state.bus_volumes_dB[i])) { return false; } } @@ -266,7 +273,7 @@ bool bus_volumes_ok() { bool channel_input_gains_ok() { uint8_t i; for (i = 0; i < CHANNELS; ++i) { - if (isnan(state.channel_input_gains[i])) { + if (isnan(state.channel_input_gains_dB[i])) { return false; } } diff --git a/hardware/firmware/audio_board/src/teensyaudio.h b/hardware/firmware/audio_board/src/teensyaudio.h index 09a1317e..28e96dcf 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.h +++ b/hardware/firmware/audio_board/src/teensyaudio.h @@ -16,17 +16,14 @@ bool is_muted(uint8_t channel, uint8_t bus); void mute(uint8_t channel, uint8_t bus); void unmute(uint8_t channel, uint8_t bus); -void set_volume(uint8_t channel, uint8_t bus, float gain); -float get_volume(uint8_t channel, uint8_t bus); +void set_volume_dB(uint8_t channel, uint8_t bus, float vol_dB); +float get_volume_dB(uint8_t channel, uint8_t bus); -float get_bus_volume(uint8_t bus); -void set_bus_volume(uint8_t bus, float volume); +float get_bus_volume_dB(uint8_t bus); +void set_bus_volume_dB(uint8_t bus, float vol_dB); -float get_channel_input_gain_db(uint8_t channel); -void set_channel_input_gain_db(uint8_t channel, float gain); - -// void raw_set_crosspoint(uint8_t channel, uint8_t bus, float gain); -float raw_get_crosspoint(uint8_t channel, uint8_t bus); +float get_channel_input_gain_dB(uint8_t channel); +void set_channel_input_gain_dB(uint8_t channel, float gain); void audio_update_levels(Levels& levels); Levels& audio_get_levels(); diff --git a/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp b/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp index f3195ee1..a74a8916 100644 --- a/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp @@ -1,10 +1,10 @@ #define MIC (5.0f) -const PROGMEM float default_bus_volumes[BUSES] = { - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f +const PROGMEM float default_bus_volumes_dB[BUSES] = { + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; -const PROGMEM float default_channel_input_gains_db[CHANNELS] = { +const PROGMEM float default_channel_input_gains_dB[CHANNELS] = { MIC, MIC, MIC, 1.0f, 1.0f, 1.0f }; // see helpers/generate_mutes.py @@ -12,18 +12,18 @@ const PROGMEM uint64_t default_mutes = 52361428992; const PROGMEM uint16_t default_phantoms = 0; -const PROGMEM float default_matrix[CHANNELS][BUSES] = { +const PROGMEM float default_matrix_dB[CHANNELS][BUSES] = { // outputs: OUT1, OUT2, HP1, HP2, USB1, USB2 // IN1 - {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // IN2 - {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // IN3 - {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // PC - {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // USB1 - {1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // USB2 - {1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f} + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f} }; diff --git a/hardware/firmware/audio_board/src/types.h b/hardware/firmware/audio_board/src/types.h index c3b15943..1948caef 100644 --- a/hardware/firmware/audio_board/src/types.h +++ b/hardware/firmware/audio_board/src/types.h @@ -6,13 +6,13 @@ #include typedef struct { - float matrix[CHANNELS][BUSES]; + float matrix_dB[CHANNELS][BUSES]; uint64_t mutes; uint16_t phantoms; - float channel_input_gains[CHANNELS]; - float bus_volumes[BUSES]; + float channel_input_gains_dB[CHANNELS]; + float bus_volumes_dB[BUSES]; } AudioState; typedef struct { From c75395dec8d299eaea6afff53f9984de4368d17c Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 28 Mar 2026 20:28:56 +0200 Subject: [PATCH 54/67] format --- hardware/firmware/audio_board/src/taa3040.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hardware/firmware/audio_board/src/taa3040.cpp b/hardware/firmware/audio_board/src/taa3040.cpp index bcf722fb..321ee8e7 100644 --- a/hardware/firmware/audio_board/src/taa3040.cpp +++ b/hardware/firmware/audio_board/src/taa3040.cpp @@ -24,7 +24,7 @@ bool AudioControlTAA3040::disable(void) { } /* - * Set the gain in the PGA in steps of 1dB. The gain rainge is from 0 to 42 dB. This also controls the + * Set the gain in the PGA in steps of 1dB. The gain rainge is from 0 to 42 dB. This also controls the * analog frontend configuration. */ bool AudioControlTAA3040::gain(uint8_t channel, uint8_t gain, uint8_t impedance, uint8_t mode, uint8_t coupling) { @@ -36,11 +36,11 @@ bool AudioControlTAA3040::gain(uint8_t channel, uint8_t gain, uint8_t impedance, /* * Set the digital volume control for the channel. This ranges from -100dB to +27dB with a - * precision of 0.1 dB. + * precision of 0.1 dB. */ bool AudioControlTAA3040::digitalGain(uint8_t channel, float gainDb) { - uint8_t offset = channel * 5; - float halfDbs = gainDb * 2; + uint8_t offset = channel * 5; + float halfDbs = gainDb * 2; uint8_t steps = (uint8_t)(halfDbs + 0.5); // 1.3 -> 2.6 -> 3 (1.5dB) From 2c5060512984a91601dcc6ed1b9037dc879e3a2e Mon Sep 17 00:00:00 2001 From: dexterlb Date: Tue, 31 Mar 2026 00:18:32 +0300 Subject: [PATCH 55/67] fix segfault --- software/audioctl/api/handlers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/software/audioctl/api/handlers.go b/software/audioctl/api/handlers.go index db5dac43..b4f31ecc 100644 --- a/software/audioctl/api/handlers.go +++ b/software/audioctl/api/handlers.go @@ -227,6 +227,7 @@ func (a *Api) pollState() { state, err := a.ctl.GetFullState() if err != nil { a.logger.Error("could not poll state", "err", err) + return } if len(a.chanNames) != len(state.Channels) { From cea0b694a8bf5a8148d45e7c574629057de4e8db Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 4 Apr 2026 17:28:36 +0300 Subject: [PATCH 56/67] properly handle dbs in fakectl --- software/audioctl/fakectl/default_state.go | 24 +++++----- software/audioctl/fakectl/fakectl.go | 54 ++++++++++++++++------ 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/software/audioctl/fakectl/default_state.go b/software/audioctl/fakectl/default_state.go index f9342bb9..15ebda61 100644 --- a/software/audioctl/fakectl/default_state.go +++ b/software/audioctl/fakectl/default_state.go @@ -7,48 +7,48 @@ var defaultState *ctl.MixerState = &ctl.MixerState{ { Name: "fubalina", Label: "foo", - Gain: 3, + Gain: -60, Phantom: false, Sends: []ctl.SendState{ { Unmuted: true, - Volume: 1, + Volume: 0, }, { Unmuted: false, - Volume: 1, + Volume: 0, }, }, }, { Name: "barabela", Label: "bar", - Gain: 8, + Gain: 0, Phantom: false, Sends: []ctl.SendState{ { - Unmuted: false, - Volume: 1, + Unmuted: true, + Volume: -18, }, { Unmuted: true, - Volume: 0.42, + Volume: -30, }, }, }, { Name: "bazinga", Label: "baz", - Gain: 42, + Gain: 0, Phantom: true, Sends: []ctl.SendState{ { Unmuted: false, - Volume: 1, + Volume: 0, }, { Unmuted: true, - Volume: 0.82, + Volume: -20, }, }, }, @@ -57,12 +57,12 @@ var defaultState *ctl.MixerState = &ctl.MixerState{ { Name: "penka", Label: "pe", - Volume: 0.4, + Volume: 0, }, { Name: "donka", Label: "do", - Volume: 0.4, + Volume: -10, }, }, } diff --git a/software/audioctl/fakectl/fakectl.go b/software/audioctl/fakectl/fakectl.go index 9f908cd6..f1d8998a 100644 --- a/software/audioctl/fakectl/fakectl.go +++ b/software/audioctl/fakectl/fakectl.go @@ -2,6 +2,7 @@ package fakectl import ( "fmt" + "math" "math/rand" "time" @@ -170,35 +171,52 @@ func (c *FakeCtl) FactoryReset() error { } func (c *FakeCtl) Loop() error { - lowThresholds := make([]float32, len(c.state.Channels)+len(c.state.Buses)) - highThresholds := make([]float32, len(c.state.Channels)+len(c.state.Buses)) - diffs := make([]float32, len(c.state.Channels)+len(c.state.Buses)) + lowThresholds := make([]float32, len(c.state.Channels)) + highThresholds := make([]float32, len(c.state.Channels)) + diffs := make([]float32, len(c.state.Channels)) + inputLevels := make([]float32, len(c.state.Channels)) for { time.Sleep(10 * time.Millisecond) for i := range diffs { - var curVal *float32 - if i < len(c.state.Channels) { - curVal = &c.levels.RMS.Input[i] - } else { - curVal = &c.levels.RMS.Bus[i-len(c.state.Channels)] - } - if diffs[i] == 0 { diffs[i] = -0.4 - rand.Float32() } - if diffs[i] > 0 && *curVal > highThresholds[i] { + if diffs[i] > 0 && c.levels.RMS.Input[i] > highThresholds[i] { diffs[i] = -0.4 - rand.Float32() - highThresholds[i] = -100*rand.Float32() + 4 + highThresholds[i] = -20*rand.Float32() + 4 } - if diffs[i] < 0 && *curVal < lowThresholds[i] { + if diffs[i] < 0 && c.levels.RMS.Input[i] < lowThresholds[i] { diffs[i] = 0.4 + rand.Float32() lowThresholds[i] = -50*rand.Float32() - 70 } - *curVal += diffs[i] + inputLevels[i] += diffs[i] + + c.levels.RMS.Input[i] = inputLevels[i] + c.state.Channels[i].Gain + if c.levels.RMS.Input[i] < -120 { + c.levels.RMS.Input[i] = -120 + } + } + + for j := range c.levels.RMS.Bus { + busLevel := float32(0) + for i := range c.levels.RMS.Input { + mul := float32(0) + send := c.state.Channels[i].Sends[j] + if send.Unmuted { + gain_dB := send.Volume + c.state.Buses[j].Volume + mul = coef_from_dB(gain_dB) + } + busLevel += coef_from_dB(c.levels.RMS.Input[i]) * mul + } + busLevel_dB := coef_to_dB(busLevel) + if busLevel_dB < -120 { + busLevel_dB = -120 + } + c.levels.RMS.Bus[j] = busLevel_dB } for i := range c.levels.RMS.Input { @@ -246,3 +264,11 @@ func (c *FakeCtl) copyLevels(levels []float32) []float32 { } return result } + +func coef_to_dB(x float32) float32 { + return 20.0 * float32(math.Log10(float64(x))) +} + +func coef_from_dB(db float32) float32 { + return float32(math.Pow(10, float64(db/20.0))) +} From e82ca59e7ab0d227aa48b058012cc5c82d36b7c4 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Sat, 4 Apr 2026 19:04:46 +0300 Subject: [PATCH 57/67] some comments --- software/audioctl/ctl/state.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/software/audioctl/ctl/state.go b/software/audioctl/ctl/state.go index 0bde9331..3117a898 100644 --- a/software/audioctl/ctl/state.go +++ b/software/audioctl/ctl/state.go @@ -6,22 +6,22 @@ type MixerState struct { } type ChannelState struct { - Label string `json:"label"` - Name string `json:"name"` - Gain float32 `json:"gain"` + Name string `json:"name"` // descriptive name of the input + Label string `json:"label"` // short label + Gain float32 `json:"gain"` // input gain in dB (0 means identity) Phantom bool `json:"phantom"` Sends []SendState `json:"sends"` } type BusState struct { - Label string `json:"label"` - Name string `json:"name"` - Volume float32 `json:"volume"` + Name string `json:"name"` // descriptive name of the output bus + Label string `json:"label"` // short label + Volume float32 `json:"volume"` // volume in dB (0 means identity) } type SendState struct { Unmuted bool `json:"unmuted"` - Volume float32 `json:"volume"` + Volume float32 `json:"volume"` // crosspoint volume in dB (0 means identity) } type Levels struct { From 603d4d1ff25171be978d10daca0bcb23a92ebc48 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Mon, 13 Apr 2026 13:44:27 +0300 Subject: [PATCH 58/67] successfully perform a request --- software/audioctl_ui/index.html | 2 +- software/audioctl_ui/package-lock.json | 929 +++++++++++++++--------- software/audioctl_ui/package.json | 15 +- software/audioctl_ui/styles/app.css | 0 software/audioctl_ui/tsconfig.app.json | 19 - software/audioctl_ui/tsconfig.json | 30 +- software/audioctl_ui/tsconfig.node.json | 25 - 7 files changed, 616 insertions(+), 404 deletions(-) create mode 100644 software/audioctl_ui/styles/app.css delete mode 100644 software/audioctl_ui/tsconfig.app.json delete mode 100644 software/audioctl_ui/tsconfig.node.json diff --git a/software/audioctl_ui/index.html b/software/audioctl_ui/index.html index 25920c39..7fdd4e5b 100644 --- a/software/audioctl_ui/index.html +++ b/software/audioctl_ui/index.html @@ -4,7 +4,7 @@ - + Audio control diff --git a/software/audioctl_ui/package-lock.json b/software/audioctl_ui/package-lock.json index a9edba2a..1a4b236b 100644 --- a/software/audioctl_ui/package-lock.json +++ b/software/audioctl_ui/package-lock.json @@ -8,87 +8,83 @@ "name": "audioctl_ui", "version": "0.0.0", "devDependencies": { - "@types/node": "^25.2.2", - "node": "^25.6.0", - "prettier": "^3.8.1", - "typescript": "~5.9.3", - "vite": "^7.3.1", + "@types/node": "^25.6.0", + "misirka": "github:dexterlb/misirka", + "node": "^25.9.0", + "prettier": "^3.8.2", + "typescript": "~6.0.2", + "vite": "^8.0.8", "vite-plugin-compression": "^0.5.1", - "vite-plugin-singlefile": "^2.3.0" + "vite-plugin-singlefile": "^2.3.2" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", - "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", - "cpu": [ - "ppc64" - ], + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", - "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", - "cpu": [ - "arm" - ], + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", - "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", - "cpu": [ - "arm64" - ], + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", - "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", - "cpu": [ - "x64" - ], + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", + "integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", - "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "node_modules/@oxc-project/types": { + "version": "0.124.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", + "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", "cpu": [ "arm64" ], @@ -96,18 +92,18 @@ "license": "MIT", "optional": true, "os": [ - "darwin" + "android" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", - "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", @@ -116,30 +112,30 @@ "darwin" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", - "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "freebsd" + "darwin" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", - "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", "cpu": [ "x64" ], @@ -150,13 +146,13 @@ "freebsd" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", - "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", + "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", "cpu": [ "arm" ], @@ -167,217 +163,133 @@ "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", - "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "libc": [ + "glibc" ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", - "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", - "cpu": [ - "ia32" - ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", - "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", "cpu": [ - "loong64" + "arm64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", - "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", - "cpu": [ - "mips64el" + "libc": [ + "musl" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", - "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "libc": [ + "glibc" ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", - "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", - "cpu": [ - "riscv64" - ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", - "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", - "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", - "cpu": [ - "x64" + "libc": [ + "glibc" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", - "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", - "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" + "libc": [ + "glibc" ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", - "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", - "cpu": [ - "arm64" - ], - "dev": true, "license": "MIT", "optional": true, "os": [ - "openbsd" + "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", - "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ - "openbsd" + "linux" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", - "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", "cpu": [ "arm64" ], @@ -388,30 +300,32 @@ "openharmony" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", - "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", + "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", "cpu": [ - "x64" + "wasm32" ], "dev": true, "license": "MIT", "optional": true, - "os": [ - "sunos" - ], + "dependencies": { + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.3" + }, "engines": { - "node": ">=18" + "node": ">=14.0.0" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", - "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", "cpu": [ "arm64" ], @@ -422,15 +336,15 @@ "win32" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", - "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", "cpu": [ - "ia32" + "x64" ], "dev": true, "license": "MIT", @@ -439,25 +353,15 @@ "win32" ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", - "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", - "cpu": [ - "x64" - ], + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", + "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } + "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.60.0", @@ -471,7 +375,8 @@ "optional": true, "os": [ "android" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-android-arm64": { "version": "4.60.0", @@ -485,7 +390,8 @@ "optional": true, "os": [ "android" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.60.0", @@ -499,7 +405,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-darwin-x64": { "version": "4.60.0", @@ -513,7 +420,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-freebsd-arm64": { "version": "4.60.0", @@ -527,7 +435,8 @@ "optional": true, "os": [ "freebsd" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-freebsd-x64": { "version": "4.60.0", @@ -541,7 +450,8 @@ "optional": true, "os": [ "freebsd" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { "version": "4.60.0", @@ -555,7 +465,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { "version": "4.60.0", @@ -569,7 +480,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm64-gnu": { "version": "4.60.0", @@ -583,7 +495,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm64-musl": { "version": "4.60.0", @@ -597,7 +510,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-loong64-gnu": { "version": "4.60.0", @@ -611,7 +525,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-loong64-musl": { "version": "4.60.0", @@ -625,7 +540,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { "version": "4.60.0", @@ -639,7 +555,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-ppc64-musl": { "version": "4.60.0", @@ -653,7 +570,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { "version": "4.60.0", @@ -667,7 +585,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-riscv64-musl": { "version": "4.60.0", @@ -681,7 +600,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-s390x-gnu": { "version": "4.60.0", @@ -695,7 +615,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.60.0", @@ -709,7 +630,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.60.0", @@ -723,7 +645,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-openbsd-x64": { "version": "4.60.0", @@ -737,7 +660,8 @@ "optional": true, "os": [ "openbsd" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-openharmony-arm64": { "version": "4.60.0", @@ -751,7 +675,8 @@ "optional": true, "os": [ "openharmony" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-arm64-msvc": { "version": "4.60.0", @@ -765,7 +690,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-ia32-msvc": { "version": "4.60.0", @@ -779,7 +705,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-x64-gnu": { "version": "4.60.0", @@ -793,7 +720,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-x64-msvc": { "version": "4.60.0", @@ -807,23 +735,36 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.18.0" + "undici-types": "~7.19.0" } }, "node_modules/ansi-styles": { @@ -910,46 +851,14 @@ } } }, - "node_modules/esbuild": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", - "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, + "license": "Apache-2.0", "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.4", - "@esbuild/android-arm": "0.27.4", - "@esbuild/android-arm64": "0.27.4", - "@esbuild/android-x64": "0.27.4", - "@esbuild/darwin-arm64": "0.27.4", - "@esbuild/darwin-x64": "0.27.4", - "@esbuild/freebsd-arm64": "0.27.4", - "@esbuild/freebsd-x64": "0.27.4", - "@esbuild/linux-arm": "0.27.4", - "@esbuild/linux-arm64": "0.27.4", - "@esbuild/linux-ia32": "0.27.4", - "@esbuild/linux-loong64": "0.27.4", - "@esbuild/linux-mips64el": "0.27.4", - "@esbuild/linux-ppc64": "0.27.4", - "@esbuild/linux-riscv64": "0.27.4", - "@esbuild/linux-s390x": "0.27.4", - "@esbuild/linux-x64": "0.27.4", - "@esbuild/netbsd-arm64": "0.27.4", - "@esbuild/netbsd-x64": "0.27.4", - "@esbuild/openbsd-arm64": "0.27.4", - "@esbuild/openbsd-x64": "0.27.4", - "@esbuild/openharmony-arm64": "0.27.4", - "@esbuild/sunos-x64": "0.27.4", - "@esbuild/win32-arm64": "0.27.4", - "@esbuild/win32-ia32": "0.27.4", - "@esbuild/win32-x64": "0.27.4" + "node": ">=8" } }, "node_modules/fdir": { @@ -1053,6 +962,279 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -1080,6 +1262,11 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/misirka": { + "version": "0.1.0", + "resolved": "git+ssh://git@github.com/dexterlb/misirka.git#f7e8d7a4fd7b6101c7b5b1a8e28acffa7aaab9b2", + "dev": true + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1107,9 +1294,9 @@ } }, "node_modules/node": { - "version": "25.8.1", - "resolved": "https://registry.npmjs.org/node/-/node-25.8.1.tgz", - "integrity": "sha512-AG1iDDN7Uzku6rFQTjfrqZZRjur8YPkO73PnfDfoj5348DQmFACA8D4X8EhvO8ObIntENK5e/aVviy+sFsAbOA==", + "version": "25.9.0", + "resolved": "https://registry.npmjs.org/node/-/node-25.9.0.tgz", + "integrity": "sha512-tfIfIRJVc32gUI+cQrxwAGWLwTy/EENnB3vF95RuAFosqBGjMTY+o0/3T4oPOipUFrsebQ5WDMsRyAqn65RMFQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1138,9 +1325,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -1180,9 +1367,9 @@ } }, "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.2.tgz", + "integrity": "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q==", "dev": true, "license": "MIT", "bin": { @@ -1195,12 +1382,47 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/rolldown": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", + "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.124.0", + "@rolldown/pluginutils": "1.0.0-rc.15" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-x64": "1.0.0-rc.15", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" + } + }, "node_modules/rollup": { "version": "4.60.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -1293,10 +1515,18 @@ "node": ">=8.0" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1308,9 +1538,9 @@ } }, "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", "dev": true, "license": "MIT" }, @@ -1325,17 +1555,16 @@ } }, "node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", + "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "bin": { @@ -1352,9 +1581,10 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -1367,13 +1597,16 @@ "@types/node": { "optional": true }, - "jiti": { + "@vitejs/devtools": { "optional": true }, - "less": { + "esbuild": { "optional": true }, - "lightningcss": { + "jiti": { + "optional": true + }, + "less": { "optional": true }, "sass": { diff --git a/software/audioctl_ui/package.json b/software/audioctl_ui/package.json index 746f86af..a03265ce 100644 --- a/software/audioctl_ui/package.json +++ b/software/audioctl_ui/package.json @@ -7,17 +7,18 @@ "dev": "vite --host", "build": "npm run check && vite build", "preview": "vite preview", - "check": "tsc -p tsconfig.node.json", + "check": "tsc -p tsconfig.json", "format": "prettier --write ." }, "devDependencies": { - "@types/node": "^25.2.2", - "node": "^25.6.0", - "prettier": "^3.8.1", - "typescript": "~5.9.3", - "vite": "^7.3.1", + "@types/node": "^25.6.0", + "node": "^25.9.0", + "prettier": "^3.8.2", + "typescript": "~6.0.2", + "misirka": "github:dexterlb/misirka", + "vite": "^8.0.8", "vite-plugin-compression": "^0.5.1", - "vite-plugin-singlefile": "^2.3.0" + "vite-plugin-singlefile": "^2.3.2" }, "dependencies": {} } diff --git a/software/audioctl_ui/styles/app.css b/software/audioctl_ui/styles/app.css new file mode 100644 index 00000000..e69de29b diff --git a/software/audioctl_ui/tsconfig.app.json b/software/audioctl_ui/tsconfig.app.json deleted file mode 100644 index 6cbf02c2..00000000 --- a/software/audioctl_ui/tsconfig.app.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "useDefineForClassFields": true, - "module": "ESNext", - "resolveJsonModule": true, - /** - * Typecheck JS in `.js` files by default. - * Disable checkJs if you'd like to use dynamic types in JS. - * Note that setting allowJs false does not prevent the use - * of JS in `.svelte` files. - */ - "allowJs": true, - "checkJs": true, - "isolatedModules": true, - "moduleDetection": "force" - }, - "include": ["src/**/*.ts", "src/**/*.js"] -} diff --git a/software/audioctl_ui/tsconfig.json b/software/audioctl_ui/tsconfig.json index 1ffef600..0025a032 100644 --- a/software/audioctl_ui/tsconfig.json +++ b/software/audioctl_ui/tsconfig.json @@ -1,7 +1,29 @@ { - "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo", + "target": "ES2025", + "lib": ["ES2025", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": [ + "vite.config.ts", + "src/**/*.ts", + "src/**/*.js", + "node_modules/misirka/ts/**/*.ts" ] } diff --git a/software/audioctl_ui/tsconfig.node.json b/software/audioctl_ui/tsconfig.node.json deleted file mode 100644 index f85a3990..00000000 --- a/software/audioctl_ui/tsconfig.node.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "target": "ES2023", - "lib": ["ES2023"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "erasableSyntaxOnly": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true - }, - "include": ["vite.config.ts"] -} From 3335fa5a36d02f25d81505a04cdf0cae80d66559 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Mon, 13 Apr 2026 16:12:45 +0300 Subject: [PATCH 59/67] update deps --- software/audioctl_ui/package-lock.json | 15 +++++++++++++-- software/audioctl_ui/package.json | 8 ++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/software/audioctl_ui/package-lock.json b/software/audioctl_ui/package-lock.json index 1a4b236b..3c67c9bd 100644 --- a/software/audioctl_ui/package-lock.json +++ b/software/audioctl_ui/package-lock.json @@ -15,7 +15,8 @@ "typescript": "~6.0.2", "vite": "^8.0.8", "vite-plugin-compression": "^0.5.1", - "vite-plugin-singlefile": "^2.3.2" + "vite-plugin-singlefile": "^2.3.2", + "zod": "^4.3.6" } }, "node_modules/@emnapi/core": { @@ -1264,7 +1265,7 @@ }, "node_modules/misirka": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/dexterlb/misirka.git#f7e8d7a4fd7b6101c7b5b1a8e28acffa7aaab9b2", + "resolved": "git+ssh://git@github.com/dexterlb/misirka.git#8e98e48287dfc07ee338c5836d85b4479ecd8481", "dev": true }, "node_modules/ms": { @@ -1663,6 +1664,16 @@ "rollup": "^4.59.0", "vite": "^5.4.11 || ^6.0.0 || ^7.0.0 || ^8.0.0" } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/software/audioctl_ui/package.json b/software/audioctl_ui/package.json index a03265ce..e0f0c04e 100644 --- a/software/audioctl_ui/package.json +++ b/software/audioctl_ui/package.json @@ -12,13 +12,13 @@ }, "devDependencies": { "@types/node": "^25.6.0", + "misirka": "github:dexterlb/misirka", "node": "^25.9.0", "prettier": "^3.8.2", "typescript": "~6.0.2", - "misirka": "github:dexterlb/misirka", "vite": "^8.0.8", "vite-plugin-compression": "^0.5.1", - "vite-plugin-singlefile": "^2.3.2" - }, - "dependencies": {} + "vite-plugin-singlefile": "^2.3.2", + "zod": "^4.3.6" + } } From fa91366009a9f935e7575be783ab1a9bcd2481e3 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Mon, 13 Apr 2026 22:24:53 +0300 Subject: [PATCH 60/67] initial working version of mixer frontend/backend connection --- .gitignore | 1 - software/audioctl_ui/package-lock.json | 1766 ++++++++++++++++++++--- software/audioctl_ui/package.json | 10 +- software/audioctl_ui/src/api_data.ts | 52 + software/audioctl_ui/src/main.ts | 23 + software/audioctl_ui/src/mixer_ui.ts | 46 + software/audioctl_ui/src/mixerclient.ts | 24 + software/audioctl_ui/src/vite-env.d.ts | 1 + 8 files changed, 1748 insertions(+), 175 deletions(-) create mode 100644 software/audioctl_ui/src/api_data.ts create mode 100644 software/audioctl_ui/src/main.ts create mode 100644 software/audioctl_ui/src/mixer_ui.ts create mode 100644 software/audioctl_ui/src/mixerclient.ts create mode 100644 software/audioctl_ui/src/vite-env.d.ts diff --git a/.gitignore b/.gitignore index d0edea0b..657fedc8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ *.bak *.mp4 -*.ts xml diff --git a/software/audioctl_ui/package-lock.json b/software/audioctl_ui/package-lock.json index 3c67c9bd..942ed558 100644 --- a/software/audioctl_ui/package-lock.json +++ b/software/audioctl_ui/package-lock.json @@ -8,17 +8,49 @@ "name": "audioctl_ui", "version": "0.0.0", "devDependencies": { + "@eslint/css": "^1.1.0", + "@eslint/js": "^10.0.1", "@types/node": "^25.6.0", + "eslint": "^10.2.0", + "globals": "^17.5.0", + "jiti": "^2.6.1", "misirka": "github:dexterlb/misirka", "node": "^25.9.0", "prettier": "^3.8.2", "typescript": "~6.0.2", + "typescript-eslint": "^8.58.2", "vite": "^8.0.8", + "vite-plugin-checker": "^0.12.0", "vite-plugin-compression": "^0.5.1", "vite-plugin-singlefile": "^2.3.2", "zod": "^4.3.6" } }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@emnapi/core": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", @@ -53,6 +85,229 @@ "tslib": "^2.4.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/css": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@eslint/css/-/css-1.1.0.tgz", + "integrity": "sha512-sNwfLcU3nKXv/v2YglqujwMU4Iv3BDhxldNUd/2FckVab0zdvc9pPlKWxjR6Ap/EU+Y8Pdu853iwvcUpemRhRw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.1", + "@eslint/css-tree": "^3.6.9", + "@eslint/plugin-kit": "^0.6.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/css-tree": { + "version": "3.6.9", + "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-3.6.9.tgz", + "integrity": "sha512-3D5/OHibNEGk+wKwNwMbz63NMf367EoR4mVNNpxddCHKEb2Nez7z62J2U6YjtErSsZDoY0CsccmoUpdEbkogNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.23.0", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/@eslint/css/node_modules/@eslint/plugin-kit": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz", + "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", @@ -365,9 +620,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", - "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", "cpu": [ "arm" ], @@ -380,9 +635,9 @@ "peer": true }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", - "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", "cpu": [ "arm64" ], @@ -395,9 +650,9 @@ "peer": true }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", - "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", "cpu": [ "arm64" ], @@ -410,9 +665,9 @@ "peer": true }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", - "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", "cpu": [ "x64" ], @@ -425,9 +680,9 @@ "peer": true }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", - "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", "cpu": [ "arm64" ], @@ -440,9 +695,9 @@ "peer": true }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", - "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", "cpu": [ "x64" ], @@ -455,13 +710,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", - "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -470,13 +728,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", - "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -485,13 +746,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", - "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -500,13 +764,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", - "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -515,13 +782,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", - "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -530,13 +800,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", - "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -545,13 +818,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", - "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -560,13 +836,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", - "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -575,13 +854,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", - "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -590,13 +872,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", - "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -605,13 +890,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", - "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -620,13 +908,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", - "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -635,13 +926,16 @@ "peer": true }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", - "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -650,9 +944,9 @@ "peer": true }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", - "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", "cpu": [ "x64" ], @@ -665,9 +959,9 @@ "peer": true }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", - "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", "cpu": [ "arm64" ], @@ -680,9 +974,9 @@ "peer": true }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", - "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", "cpu": [ "arm64" ], @@ -695,9 +989,9 @@ "peer": true }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", - "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", "cpu": [ "ia32" ], @@ -710,9 +1004,9 @@ "peer": true }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", - "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", "cpu": [ "x64" ], @@ -725,9 +1019,9 @@ "peer": true }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", - "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", "cpu": [ "x64" ], @@ -750,13 +1044,26 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { "version": "25.6.0", @@ -768,104 +1075,631 @@ "undici-types": "~7.19.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", + "integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/type-utils": "8.58.2", + "@typescript-eslint/utils": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, "engines": { - "node": ">=8" + "node": ">= 4" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@typescript-eslint/parser": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz", + "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", + "debug": "^4.4.3" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz", + "integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@typescript-eslint/tsconfig-utils": "^8.58.2", + "@typescript-eslint/types": "^8.58.2", + "debug": "^4.4.3" }, "engines": { - "node": ">=7.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz", + "integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2" }, "engines": { - "node": ">=6.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz", + "integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz", + "integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz", + "integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz", + "integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.2", + "@typescript-eslint/tsconfig-utils": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz", + "integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz", + "integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.2", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", + "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.4", + "@eslint/config-helpers": "^0.5.4", + "@eslint/core": "^1.2.0", + "@eslint/plugin-kit": "^0.7.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", "engines": { @@ -880,6 +1714,19 @@ } } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -893,6 +1740,44 @@ "node": ">=8" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -923,6 +1808,32 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz", + "integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -937,7 +1848,50 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, "node_modules/is-number": { @@ -950,6 +1904,51 @@ "node": ">=0.12.0" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/jsonfile": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", @@ -963,6 +1962,30 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -1236,6 +2259,29 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdn-data": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.23.0.tgz", + "integrity": "sha512-786vq1+4079JSeu2XdcDjrhi/Ry7BWtjDl9WtGPWLiIHb2T66GvIVflZTBoSNZ5JqTtJGYEVMuFA/lbQlMOyDQ==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -1251,9 +2297,9 @@ } }, "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -1263,9 +2309,25 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/misirka": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/dexterlb/misirka.git#8e98e48287dfc07ee338c5836d85b4479ecd8481", + "resolved": "git+ssh://git@github.com/dexterlb/misirka.git#e038e511f3562dfdb0a5583a521786a2d3fed346", "dev": true }, "node_modules/ms": { @@ -1294,6 +2356,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/node": { "version": "25.9.0", "resolved": "https://registry.npmjs.org/node/-/node-25.9.0.tgz", @@ -1318,6 +2387,96 @@ "dev": true, "license": "ISC" }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1339,9 +2498,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", "dev": true, "funding": [ { @@ -1367,6 +2526,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prettier": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.2.tgz", @@ -1383,6 +2552,30 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/rolldown": { "version": "1.0.0-rc.15", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", @@ -1418,9 +2611,9 @@ } }, "node_modules/rollup": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", - "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", "dev": true, "license": "MIT", "peer": true, @@ -1435,34 +2628,70 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.0", - "@rollup/rollup-android-arm64": "4.60.0", - "@rollup/rollup-darwin-arm64": "4.60.0", - "@rollup/rollup-darwin-x64": "4.60.0", - "@rollup/rollup-freebsd-arm64": "4.60.0", - "@rollup/rollup-freebsd-x64": "4.60.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", - "@rollup/rollup-linux-arm-musleabihf": "4.60.0", - "@rollup/rollup-linux-arm64-gnu": "4.60.0", - "@rollup/rollup-linux-arm64-musl": "4.60.0", - "@rollup/rollup-linux-loong64-gnu": "4.60.0", - "@rollup/rollup-linux-loong64-musl": "4.60.0", - "@rollup/rollup-linux-ppc64-gnu": "4.60.0", - "@rollup/rollup-linux-ppc64-musl": "4.60.0", - "@rollup/rollup-linux-riscv64-gnu": "4.60.0", - "@rollup/rollup-linux-riscv64-musl": "4.60.0", - "@rollup/rollup-linux-s390x-gnu": "4.60.0", - "@rollup/rollup-linux-x64-gnu": "4.60.0", - "@rollup/rollup-linux-x64-musl": "4.60.0", - "@rollup/rollup-openbsd-x64": "4.60.0", - "@rollup/rollup-openharmony-arm64": "4.60.0", - "@rollup/rollup-win32-arm64-msvc": "4.60.0", - "@rollup/rollup-win32-ia32-msvc": "4.60.0", - "@rollup/rollup-win32-x64-gnu": "4.60.0", - "@rollup/rollup-win32-x64-msvc": "4.60.0", + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" } }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -1486,15 +2715,22 @@ "node": ">=8" } }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -1516,6 +2752,19 @@ "node": ">=8.0" } }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -1524,6 +2773,19 @@ "license": "0BSD", "optional": true }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/typescript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", @@ -1538,6 +2800,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz", + "integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/undici-types": { "version": "7.19.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", @@ -1545,6 +2831,19 @@ "dev": true, "license": "MIT" }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -1555,6 +2854,16 @@ "node": ">= 10.0.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/vite": { "version": "8.0.8", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", @@ -1633,6 +2942,71 @@ } } }, + "node_modules/vite-plugin-checker": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.12.0.tgz", + "integrity": "sha512-CmdZdDOGss7kdQwv73UyVgLPv0FVYe5czAgnmRX2oKljgEvSrODGuClaV3PDR2+3ou7N/OKGauDDBjy2MB07Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "chokidar": "^4.0.3", + "npm-run-path": "^6.0.0", + "picocolors": "^1.1.1", + "picomatch": "^4.0.3", + "tiny-invariant": "^1.3.3", + "tinyglobby": "^0.2.15", + "vscode-uri": "^3.1.0" + }, + "engines": { + "node": ">=16.11" + }, + "peerDependencies": { + "@biomejs/biome": ">=1.7", + "eslint": ">=9.39.1", + "meow": "^13.2.0", + "optionator": "^0.9.4", + "oxlint": ">=1", + "stylelint": ">=16", + "typescript": "*", + "vite": ">=5.4.21", + "vls": "*", + "vti": "*", + "vue-tsc": "~2.2.10 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@biomejs/biome": { + "optional": true + }, + "eslint": { + "optional": true + }, + "meow": { + "optional": true + }, + "optionator": { + "optional": true + }, + "oxlint": { + "optional": true + }, + "stylelint": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vls": { + "optional": true + }, + "vti": { + "optional": true + }, + "vue-tsc": { + "optional": true + } + } + }, "node_modules/vite-plugin-compression": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz", @@ -1665,6 +3039,52 @@ "vite": "^5.4.11 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", diff --git a/software/audioctl_ui/package.json b/software/audioctl_ui/package.json index e0f0c04e..b38e147f 100644 --- a/software/audioctl_ui/package.json +++ b/software/audioctl_ui/package.json @@ -7,16 +7,24 @@ "dev": "vite --host", "build": "npm run check && vite build", "preview": "vite preview", - "check": "tsc -p tsconfig.json", + "check": "tsc -p tsconfig.json && eslint && eslint node_modules/misirka/ts/index.ts", + "lint": "eslint --fix && eslint --fix node_modules/misirka/ts/index.ts", "format": "prettier --write ." }, "devDependencies": { + "@eslint/css": "^1.1.0", + "@eslint/js": "^10.0.1", "@types/node": "^25.6.0", + "eslint": "^10.2.0", + "globals": "^17.5.0", + "jiti": "^2.6.1", "misirka": "github:dexterlb/misirka", "node": "^25.9.0", "prettier": "^3.8.2", "typescript": "~6.0.2", + "typescript-eslint": "^8.58.2", "vite": "^8.0.8", + "vite-plugin-checker": "^0.12.0", "vite-plugin-compression": "^0.5.1", "vite-plugin-singlefile": "^2.3.2", "zod": "^4.3.6" diff --git a/software/audioctl_ui/src/api_data.ts b/software/audioctl_ui/src/api_data.ts new file mode 100644 index 00000000..828b5cb9 --- /dev/null +++ b/software/audioctl_ui/src/api_data.ts @@ -0,0 +1,52 @@ +import { z } from "zod" + +const SendStateSchema = z.object({ + unmuted: z.boolean(), + volume: z.number(), +}) + +const ChannelStateSchema = z.object({ + name: z.string(), + label: z.string(), + gain: z.number(), + phantom: z.boolean(), + sends: z.array(SendStateSchema), +}) + +const BusStateSchema = z.object({ + name: z.string(), + label: z.string(), + volume: z.number(), +}) + +const MixerStateSchema = z.object({ + channels: z.array(ChannelStateSchema), + buses: z.array(BusStateSchema), +}) + +const LevelsBlockSchema = z.object({ + inputs: z.array(z.number()), + buses: z.array(z.number()), +}) + +const LevelsSchema = z.object({ + rms: LevelsBlockSchema, + peak: LevelsBlockSchema, + smooth: LevelsBlockSchema, +}) + +export type SendState = z.infer; +export type ChannelState = z.infer; +export type BusState = z.infer; +export type MixerState = z.infer; +export type LevelsBlock = z.infer; +export type Levels = z.infer; + +export { + SendStateSchema, + ChannelStateSchema, + BusStateSchema, + MixerStateSchema, + LevelsBlockSchema, + LevelsSchema, +} diff --git a/software/audioctl_ui/src/main.ts b/software/audioctl_ui/src/main.ts new file mode 100644 index 00000000..fd8c0e7b --- /dev/null +++ b/software/audioctl_ui/src/main.ts @@ -0,0 +1,23 @@ +import favicon_dataurl from "../assets/favicon.png?url&inline" +import { MixerUI } from "./mixer_ui.ts" +import { WSClient } from "misirka" + +function setup_favicon() { + const link = document.querySelector("#favicon")! + link.rel = "icon" + link.type = "image/png" + link.href = favicon_dataurl +} + +function main() { + setup_favicon() + const mclient = new WSClient(ws_url()) + new MixerUI(mclient, document.querySelector('body')!) +} + +function ws_url() { + const protocol = location.protocol === "https:" ? "wss" : "ws" + return `${protocol}://${location.host}/ws` +} + +main() diff --git a/software/audioctl_ui/src/mixer_ui.ts b/software/audioctl_ui/src/mixer_ui.ts new file mode 100644 index 00000000..f03935b9 --- /dev/null +++ b/software/audioctl_ui/src/mixer_ui.ts @@ -0,0 +1,46 @@ +import { MixerClient } from "./mixerclient.ts" +import type { MisirkaClient } from "misirka" +import type { MixerState, Levels } from "./api_data.ts" + +export class MixerUI { + constructor(mclient: MisirkaClient, private container: HTMLElement) { + this.client = new MixerClient(mclient) + mclient.on_alive(() => { this.on_alive() }) + mclient.on_dead(() => { this.on_dead() }) + + this.container.textContent = 'connecting' + } + + private async on_alive() { + const initial_state = await this.client.get_state() + this.build_ui(initial_state) + await this.client.subscribe_state( + (state: MixerState) => { this.handle_state(state) }, + ) + await this.client.subscribe_levels( + (levels: Levels) => { this.handle_levels(levels) }, + ) + } + + private async on_dead() { + this.destroy_ui() + } + + private build_ui(_state: MixerState) { + this.container.textContent = 'connected' + } + + private destroy_ui() { + this.container.textContent = 'reconnecting' + } + + private handle_state(state: MixerState) { + console.log('got state: ', state) + } + + private handle_levels(levels: Levels) { + this.container.textContent = `levels: ${JSON.stringify(levels, null, 2)}` + } + + private client: MixerClient +} diff --git a/software/audioctl_ui/src/mixerclient.ts b/software/audioctl_ui/src/mixerclient.ts new file mode 100644 index 00000000..fd859ed8 --- /dev/null +++ b/software/audioctl_ui/src/mixerclient.ts @@ -0,0 +1,24 @@ +import type { MisirkaClient } from "misirka" +import { MixerStateSchema, LevelsSchema } from "./api_data.ts" +import type { MixerState, Levels } from "./api_data.ts" + +export class MixerClient { + constructor(private client: MisirkaClient) { + } + + async get_state(): Promise { + return await this.client.get('state', MixerStateSchema) + } + + async subscribe_state(handler: (state: MixerState) => void) { + await this.client.subscribe( + ['state'], MixerStateSchema, handler, + ) + } + + async subscribe_levels(handler: (levels: Levels) => void) { + await this.client.subscribe( + ['levels'], LevelsSchema, handler, + ) + } +} diff --git a/software/audioctl_ui/src/vite-env.d.ts b/software/audioctl_ui/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/software/audioctl_ui/src/vite-env.d.ts @@ -0,0 +1 @@ +/// From 68b78ae833ecf8a1f59c0853c2ca675d21a2eea0 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Tue, 14 Apr 2026 17:51:08 +0300 Subject: [PATCH 61/67] ui works! --- software/audioctl/go.mod | 2 +- software/audioctl/go.sum | 4 +- software/audioctl_ui/eslint.config.ts | 38 ++++ software/audioctl_ui/package-lock.json | 72 +++---- software/audioctl_ui/src/api_data.ts | 4 + software/audioctl_ui/src/mixer_ui.ts | 250 +++++++++++++++++++++++- software/audioctl_ui/src/mixerclient.ts | 43 +++- software/audioctl_ui/styles/app.css | 29 +++ software/audioctl_ui/vite.config.ts | 49 +++++ 9 files changed, 447 insertions(+), 44 deletions(-) create mode 100644 software/audioctl_ui/eslint.config.ts create mode 100644 software/audioctl_ui/vite.config.ts diff --git a/software/audioctl/go.mod b/software/audioctl/go.mod index 4840093d..30319de9 100644 --- a/software/audioctl/go.mod +++ b/software/audioctl/go.mod @@ -3,7 +3,7 @@ module github.com/fosdem/video/software/audioctl go 1.25.5 require ( - github.com/dexterlb/misirka/go v0.0.0-20260322141321-b53f25447e7f + github.com/dexterlb/misirka/go v0.0.0-20260414144551-62b2df7bf08d github.com/goccy/go-yaml v1.19.2 go.bug.st/serial v1.6.4 ) diff --git a/software/audioctl/go.sum b/software/audioctl/go.sum index 7ebd6952..631e4549 100644 --- a/software/audioctl/go.sum +++ b/software/audioctl/go.sum @@ -2,8 +2,8 @@ github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0 github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dexterlb/misirka/go v0.0.0-20260322141321-b53f25447e7f h1:KyjyFdnmFIaoVShfGmBgFMkIe2wCaLU7kHnwkO6YVDg= -github.com/dexterlb/misirka/go v0.0.0-20260322141321-b53f25447e7f/go.mod h1:VZeXV8DAty9fQUq9+W3W8IRcTEI/NEElYDBj8hBOx4E= +github.com/dexterlb/misirka/go v0.0.0-20260414144551-62b2df7bf08d h1:s2XSKeoNxXRRC0lBJZLSreY0dONqL3QMjhfAUyVOwo8= +github.com/dexterlb/misirka/go v0.0.0-20260414144551-62b2df7bf08d/go.mod h1:VZeXV8DAty9fQUq9+W3W8IRcTEI/NEElYDBj8hBOx4E= github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= diff --git a/software/audioctl_ui/eslint.config.ts b/software/audioctl_ui/eslint.config.ts new file mode 100644 index 00000000..16b80d6a --- /dev/null +++ b/software/audioctl_ui/eslint.config.ts @@ -0,0 +1,38 @@ +import js from "@eslint/js" +import globals from "globals" +import tseslint from "typescript-eslint" +import css from "@eslint/css" +import { defineConfig, globalIgnores } from "eslint/config" + +export default defineConfig([ + globalIgnores([ + "!node_modules/", + "node_modules/*", + "!node_modules/misirka/", + ]), + { + files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], + plugins: { js }, + extends: ["js/recommended"], + languageOptions: { globals: globals.browser }, + rules: { + "indent": ["error", 2], + "semi": ["error", "never"], + "no-unused-vars": "off", + "comma-dangle": ["error", "always-multiline"], + }, + }, + { + extends: [tseslint.configs.recommended], + rules: { + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-explicit-any": "off", + }, + }, + { + files: ["**/*.css"], + plugins: { css }, + language: "css/css", + extends: ["css/recommended"], + }, +]) diff --git a/software/audioctl_ui/package-lock.json b/software/audioctl_ui/package-lock.json index 942ed558..4803b80b 100644 --- a/software/audioctl_ui/package-lock.json +++ b/software/audioctl_ui/package-lock.json @@ -197,20 +197,6 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/@eslint/css/node_modules/@eslint/plugin-kit": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz", - "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.1.1", - "levn": "^0.4.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, "node_modules/@eslint/js": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", @@ -243,13 +229,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", - "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz", + "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.2.1", + "@eslint/core": "^1.1.1", "levn": "^0.4.1" }, "engines": { @@ -1465,16 +1451,6 @@ "node": ">= 8" } }, - "node_modules/cross-spawn/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1611,6 +1587,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, "node_modules/espree": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", @@ -2327,7 +2317,7 @@ }, "node_modules/misirka": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/dexterlb/misirka.git#e038e511f3562dfdb0a5583a521786a2d3fed346", + "resolved": "git+ssh://git@github.com/dexterlb/misirka.git#62b2df7bf08d316da136eb532c3cfac4aedaf8a6", "dev": true }, "node_modules/ms": { @@ -2404,6 +2394,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -2465,16 +2468,13 @@ } }, "node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/picocolors": { diff --git a/software/audioctl_ui/src/api_data.ts b/software/audioctl_ui/src/api_data.ts index 828b5cb9..80df8bcf 100644 --- a/software/audioctl_ui/src/api_data.ts +++ b/software/audioctl_ui/src/api_data.ts @@ -35,12 +35,15 @@ const LevelsSchema = z.object({ smooth: LevelsBlockSchema, }) +const OkRespSchema = z.literal("ok") + export type SendState = z.infer; export type ChannelState = z.infer; export type BusState = z.infer; export type MixerState = z.infer; export type LevelsBlock = z.infer; export type Levels = z.infer; +export type OkResp = z.infer; export { SendStateSchema, @@ -49,4 +52,5 @@ export { MixerStateSchema, LevelsBlockSchema, LevelsSchema, + OkRespSchema, } diff --git a/software/audioctl_ui/src/mixer_ui.ts b/software/audioctl_ui/src/mixer_ui.ts index f03935b9..3f9abcde 100644 --- a/software/audioctl_ui/src/mixer_ui.ts +++ b/software/audioctl_ui/src/mixer_ui.ts @@ -25,9 +25,217 @@ export class MixerUI { private async on_dead() { this.destroy_ui() } + private build_ui(state: MixerState) { + this.container.innerHTML = '' - private build_ui(_state: MixerState) { - this.container.textContent = 'connected' + this.phantomChecks = [] + this.inputGainSliders = [] + this.sendUnmuteChecks = [] + this.sendVolumeSliders = [] + this.busVolumeSliders = [] + this.levelIndicators = [] + this.levelValues = [] + + const mixer = document.createElement('div') + mixer.className = 'mixer-container' + + const chSection = document.createElement('div') + chSection.className = 'channels section' + + const chSectionNameEl = document.createElement('div') + chSectionNameEl.className = 'section-header' + chSectionNameEl.textContent = 'input channels' + + chSection.append(chSectionNameEl) + + for (const [ci, ch] of state.channels.entries()) { + const strip = document.createElement('div') + strip.className = 'channel-box section' + + const nameEl = document.createElement('div') + nameEl.className = 'section-header' + nameEl.textContent = `${ch.label}: ${ch.name}` + nameEl.title = ch.name + + const inputBox = document.createElement('div') + inputBox.className = 'input-box slot' + + const gainLabel = document.createElement('div') + gainLabel.className = 'strip-label' + gainLabel.textContent = 'gain: ' + + const gainSlider = document.createElement('input') + gainSlider.type = 'range' + gainSlider.className = 'gain-slider' + gainSlider.min = String(-80) + gainSlider.max = String(60) + gainSlider.step = '0.6' + gainSlider.value = String(ch.gain) + + const gainVal = document.createElement('div') + gainVal.className = 'db-value' + gainVal.textContent = formatDb(ch.gain) + gainSlider.oninput = async () => { + await this.client.set_in_gain(ci, parseFloat(gainSlider.value)) + gainVal.textContent = formatDb(parseFloat(gainSlider.value)) + } + this.inputGainSliders.push([gainSlider, gainVal]) + + const phantomBox = document.createElement('span') + phantomBox.className = 'enable-box' + + const phantomCheck = document.createElement('input') + phantomCheck.type = 'checkbox' + phantomCheck.id = `phantom-${ci}` + phantomCheck.checked = ch.phantom + phantomCheck.oninput = async () => { + await this.client.set_phantom(ci, phantomCheck.checked) + } + this.phantomChecks.push(phantomCheck) + + const phantomLabel = document.createElement('label') + phantomLabel.htmlFor = phantomCheck.id + phantomLabel.className = 'phantom-label' + phantomLabel.textContent = '👻' + + phantomBox.append(phantomLabel, phantomCheck) + + inputBox.append(gainLabel, gainSlider, gainVal, phantomBox) + + const sendsSection = document.createElement('div') + sendsSection.className = 'sends-section' + + const chanSendUnmuteChecks: Array = [] + const chanSendVolumeSliders: Array<[HTMLInputElement, HTMLElement]> = [] + for (const [bi, send] of ch.sends.entries()) { + const bus = state.buses[bi] + const slot = document.createElement('div') + slot.className = 'send-box slot' + + const busTag = document.createElement('div') + busTag.className = 'send-bus-tag' + busTag.textContent = `${ch.label} → ${bus.label}: ` + + // mute toggle + const unmuteBox = document.createElement('span') + unmuteBox.className = 'enable-box' + + const sendCheck = document.createElement('input') + sendCheck.type = 'checkbox' + sendCheck.id = `unmute-${ci}-${bi}` + sendCheck.checked = send.unmuted + sendCheck.oninput = async () => { + await this.client.set_matrix_send(ci, bi, sendCheck.checked) + } + chanSendUnmuteChecks.push(sendCheck) + + const sendCheckLabel = document.createElement('label') + sendCheckLabel.htmlFor = sendCheck.id + sendCheckLabel.textContent = '🔈️' + + unmuteBox.append(sendCheckLabel, sendCheck) + + // send level + const sendSlider = document.createElement('input') + sendSlider.type = 'range' + sendSlider.min = '-80' + sendSlider.max = '20' + sendSlider.step = '0.5' + sendSlider.value = String(send.volume) + sendSlider.oninput = async () => { + await this.client.set_matrix_volume(ci, bi, parseFloat(sendSlider.value)) + sendVal.textContent = formatDb(parseFloat(sendSlider.value)) + } + + const sendVal = document.createElement('div') + sendVal.className = 'db-value' + sendVal.textContent = formatDb(send.volume) + chanSendVolumeSliders.push([sendSlider, sendVal]) + + slot.append(busTag, unmuteBox, sendSlider, sendVal) + sendsSection.append(slot) + } + this.sendUnmuteChecks.push(chanSendUnmuteChecks) + this.sendVolumeSliders.push(chanSendVolumeSliders) + + const levelIndicator = this.makeLevelIndicator() + strip.append(nameEl, inputBox, levelIndicator, sendsSection) + chSection.append(strip) + } + + const busesSection = document.createElement('div') + busesSection.className = 'buses section' + + const busesSectionNameEl = document.createElement('div') + busesSectionNameEl.className = 'section-header' + busesSectionNameEl.textContent = 'output buses' + + busesSection.append(busesSectionNameEl) + + for (const [bi, bus] of state.buses.entries()) { + const busSection = document.createElement('div') + busSection.className = 'bus section' + + const nameEl = document.createElement('div') + nameEl.className = 'bus-name section-header' + nameEl.textContent = `${bus.label}: ${bus.name}` + nameEl.title = bus.name + + busSection.append(nameEl) + + const strip = document.createElement('div') + strip.className = 'bus-strip slot' + + const volSlider = document.createElement('input') + volSlider.type = 'range' + volSlider.className = 'fader vertical' + volSlider.min = '-80' + volSlider.max = '20' + volSlider.step = '0.5' + volSlider.value = String(bus.volume) + + const volVal = document.createElement('div') + volVal.className = 'db-value' + volVal.textContent = formatDb(bus.volume) + volSlider.oninput = async () => { + await this.client.set_bus_volume(bi, parseFloat(volSlider.value)) + volVal.textContent = formatDb(parseFloat(volSlider.value)) + } + this.busVolumeSliders.push([volSlider, volVal]) + + strip.append(volSlider, volVal) + const levelIndicator = this.makeLevelIndicator() + busSection.append(strip, levelIndicator) + busesSection.append(busSection) + } + + mixer.append(chSection, busesSection) + this.container.append(mixer) + } + + private makeLevelIndicator(): HTMLElement { + const slot = document.createElement('div') + slot.className = 'level-indicator slot' + + const label = document.createElement('div') + label.textContent = 'level: ' + + const container = document.createElement('div') + container.className = 'container' + + const inner = document.createElement('div') + inner.className = 'inner' + inner.setAttribute('style', 'width: 0%') + container.append(inner) + this.levelIndicators.push(inner) + + const val = document.createElement('div') + val.className = 'db-value' + this.levelValues.push(val) + + slot.append(label, container, val) + + return slot } private destroy_ui() { @@ -35,12 +243,46 @@ export class MixerUI { } private handle_state(state: MixerState) { - console.log('got state: ', state) + // TODO: if the shape of the arrays or names, etc, is different from + // the current ones, rebuild UI + for (const [i, ch] of state.channels.entries()) { + this.inputGainSliders[i][0].value = String(ch.gain) + this.inputGainSliders[i][1].textContent = formatDb(ch.gain) + this.phantomChecks[i].checked = ch.phantom + for (const [j, _] of state.buses.entries()) { + this.sendUnmuteChecks[i][j].checked = ch.sends[j].unmuted + this.sendVolumeSliders[i][j][0].value = String(ch.sends[j].volume) + this.sendVolumeSliders[i][j][1].textContent = formatDb(ch.sends[j].volume) + } + } + for (const [j, bus] of state.buses.entries()) { + this.busVolumeSliders[j][0].value = String(bus.volume) + this.busVolumeSliders[j][1].textContent = formatDb(bus.volume) + } } private handle_levels(levels: Levels) { - this.container.textContent = `levels: ${JSON.stringify(levels, null, 2)}` + for (const [i, db] of [...levels.smooth.inputs, ...levels.smooth.buses].entries()) { + const pct = 100 * 0.79306 * Math.exp(db * 0.0527087) + this.levelIndicators[i].style = `width: ${pct}%` + this.levelValues[i].textContent = formatDb(db) + } } + private phantomChecks: Array = [] + private inputGainSliders: Array<[HTMLInputElement, HTMLElement]> = [] + private sendUnmuteChecks: Array> = [] + private sendVolumeSliders: Array> = [] + private busVolumeSliders: Array<[HTMLInputElement, HTMLElement]> = [] + private levelIndicators: Array = [] + private levelValues: Array = [] private client: MixerClient } + +function formatDb(n: number): string { + const sign = n < 0 ? '-' : '\u00A0' + const abs = Math.abs(n) + const intPart = String(Math.floor(abs)).padStart(3, '0') + const decPart = Math.floor((abs % 1) * 10) + return `${sign}${intPart}.${decPart} dB` +} diff --git a/software/audioctl_ui/src/mixerclient.ts b/software/audioctl_ui/src/mixerclient.ts index fd859ed8..eef40b95 100644 --- a/software/audioctl_ui/src/mixerclient.ts +++ b/software/audioctl_ui/src/mixerclient.ts @@ -1,5 +1,5 @@ import type { MisirkaClient } from "misirka" -import { MixerStateSchema, LevelsSchema } from "./api_data.ts" +import { MixerStateSchema, LevelsSchema, OkRespSchema } from "./api_data.ts" import type { MixerState, Levels } from "./api_data.ts" export class MixerClient { @@ -10,6 +10,43 @@ export class MixerClient { return await this.client.get('state', MixerStateSchema) } + async set_in_gain(chan_idx: number, gain: number) { + await this.call_expecting_ok('set-in-gain', { + channel: chan_idx, + gain: gain, + }) + } + + async set_phantom(chan_idx: number, phantom: boolean) { + await this.call_expecting_ok('set-phantom', { + channel: chan_idx, + phantom: phantom, + }) + } + + async set_matrix_send(chan_idx: number, bus_idx: number, unmuted: boolean) { + await this.call_expecting_ok('set-matrix-send', { + channel: chan_idx, + bus: bus_idx, + unmuted: unmuted, + }) + } + + async set_matrix_volume(chan_idx: number, bus_idx: number, volume: number) { + await this.call_expecting_ok('set-matrix-volume', { + channel: chan_idx, + bus: bus_idx, + volume: volume, + }) + } + + async set_bus_volume(bus_idx: number, volume: number) { + await this.call_expecting_ok('set-bus-volume', { + bus: bus_idx, + volume: volume, + }) + } + async subscribe_state(handler: (state: MixerState) => void) { await this.client.subscribe( ['state'], MixerStateSchema, handler, @@ -21,4 +58,8 @@ export class MixerClient { ['levels'], LevelsSchema, handler, ) } + + private async call_expecting_ok(method: string, param: any) { + await this.client.request(method, param, OkRespSchema) + } } diff --git a/software/audioctl_ui/styles/app.css b/software/audioctl_ui/styles/app.css index e69de29b..171612dc 100644 --- a/software/audioctl_ui/styles/app.css +++ b/software/audioctl_ui/styles/app.css @@ -0,0 +1,29 @@ +.section > *:not(.section-header) { + /* indent */ + margin-left: 1.5em; +} + +.enable-box, .db-value { + margin-left: 1em; +} + +.db-value { + font-family: monospace; +} + +.level-indicator { + .container { + border: 1px solid #ccc; + width: 100px; + height: 15px; + } + .inner { + height: 100%; + background-color: #ebac54; + } +} + +.slot { + display: flex; + flex-direction: row; +} diff --git a/software/audioctl_ui/vite.config.ts b/software/audioctl_ui/vite.config.ts new file mode 100644 index 00000000..573ae278 --- /dev/null +++ b/software/audioctl_ui/vite.config.ts @@ -0,0 +1,49 @@ +import { defineConfig } from "vite" +import { viteSingleFile } from "vite-plugin-singlefile" +import viteCompression from "vite-plugin-compression" +import checker from 'vite-plugin-checker' + +export default defineConfig(({ mode }) => { + let server_cfg = {} + let optimize_deps = {} + + if (mode === "development") { + const api_url = process.env.API_URL + if (!api_url) { + throw new Error( + "please specify the API_URL env var to, for example, http(s)://[IP]:[port]", + ) + } + server_cfg = { + watch: { + ignored: ['!**/node_modules/misirka/**'], + }, + proxy: { + "/ws": { + target: api_url, + ws: true, + changeOrigin: true, + secure: false, + rewrite: (path: string) => path, + }, + }, + } + + optimize_deps = { + exclude: ['misirka'], + } + } + + const checker_cfg = { + typescript: true, + } + + return { + optimizeDeps: optimize_deps, + plugins: [viteSingleFile(), viteCompression(), checker(checker_cfg)], + build: { + assetsInlineLimit: Number.MAX_SAFE_INTEGER, + }, + server: server_cfg, + } +}) From 1f79b7ece3bee565db6b02826e053dcc84bfa2d9 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Thu, 16 Apr 2026 20:23:01 +0300 Subject: [PATCH 62/67] documented api calls --- software/audioctl/api/api.go | 83 +++++++++++++++++----- software/audioctl/api/examples.go | 66 +++++++++++++++++ software/audioctl/api/handlers.go | 16 ++--- software/audioctl/fakectl/default_state.go | 19 ++++- software/audioctl/fakectl/fakectl.go | 21 ++---- 5 files changed, 163 insertions(+), 42 deletions(-) create mode 100644 software/audioctl/api/examples.go diff --git a/software/audioctl/api/api.go b/software/audioctl/api/api.go index 9a98c418..207e8a21 100644 --- a/software/audioctl/api/api.go +++ b/software/audioctl/api/api.go @@ -8,6 +8,7 @@ import ( "github.com/dexterlb/misirka/go/misirka" "github.com/fosdem/video/software/audioctl/config" "github.com/fosdem/video/software/audioctl/ctl" + "github.com/fosdem/video/software/audioctl/fakectl" ) type Api struct { @@ -30,23 +31,73 @@ func New(logger *slog.Logger, cfg *config.ApiCfg, ctl ctl.Ctl) *Api { a.dying = make(chan struct{}) a.refreshState = make(chan struct{}) - a.m = misirka.New("/", func(err error) { + a.m = misirka.New(func(err error) { logger.Error("API error", "err", err) - }) - - a.m.AddTopic("heartbeat") - a.m.AddTopic("state") - a.m.AddTopic("levels") - misirka.HandleCall(a.m, "set-full-state", a.handleSetFullState) - misirka.HandleCall(a.m, "set-matrix-send", a.handleSetMatrixSend) - misirka.HandleCall(a.m, "set-matrix-volume", a.handleSetMatrixVolume) - misirka.HandleCall(a.m, "set-phantom", a.handleSetPhantom) - misirka.HandleCall(a.m, "set-in-gain", a.handleSetInGain) - misirka.HandleCall(a.m, "set-bus-volume", a.handleSetBusVolume) - misirka.HandleCall(a.m, "raw-cmd", a.handleRawCmd) - misirka.HandleCall(a.m, "factory-reset", a.handleFactoryReset) - - a.srv.Handler = a.m.Handler() + }).Descr("control API for the FOSDEM audio board") + + misirka.AddTopic(a.m, "heartbeat"). + Descr("sends a heartbeat every now and then"). + Example(Heartbeat{Now: time.Now()}) + + misirka.AddTopic(a.m, "state"). + Descr("sends the full audio control state"). + Example(fakectl.DefaultState) + + misirka.AddTopic(a.m, "levels"). + Descr("sends the audio levels of all inputs and outputs, in decibels"). + Example(fakectl.DefaultLevels()) + + misirka.HandleCall(a.m, "set-full-state", a.handleSetFullState). + Descr("set the full state of the audio mixer at once"). + Example(fakectl.DefaultState, "ok") + + misirka.HandleCall(a.m, "set-matrix-send", a.handleSetMatrixSend). + Descr("set the unmuted status of the given matrix cross-point"). + Example(exampleMatrixSendParam1, "ok"). + Example(exampleMatrixSendParam2, "ok"). + PathValueAlias("set-matrix-send/i/{channel}/{bus}/{unmuted}"). + PathValueAlias("set-matrix-send/{channel_name}/{bus_name}/{unmuted}") + + misirka.HandleCall(a.m, "set-matrix-volume", a.handleSetMatrixVolume). + Descr("set the volume (in decibels) of the given matrix cross-point"). + Example(exampleMatrixVolumeParam1, "ok"). + Example(exampleMatrixVolumeParam2, "ok"). + PathValueAlias("set-matrix-volume/i/{channel}/{bus}/{volume}"). + PathValueAlias("set-matrix-volume/{channel_name}/{bus_name}/{volume}") + + misirka.HandleCall(a.m, "set-phantom", a.handleSetPhantom). + Descr("turn phantom power for the given input on or off"). + Example(examplePhantomParam1, "ok"). + Example(examplePhantomParam2, "ok"). + PathValueAlias("set-phantom/i/{channel}/{phantom}"). + PathValueAlias("set-phantom/{channel_name}/{phantom}") + + misirka.HandleCall(a.m, "set-in-gain", a.handleSetInGain). + Descr("set the input gain (in decibels) of the given input channel"). + Example(exampleInGainParam1, "ok"). + Example(exampleInGainParam2, "ok"). + PathValueAlias("set-in-gain/i/{channel}/{volume}"). + PathValueAlias("set-in-gain/{channel_name}/{volume}") + + misirka.HandleCall(a.m, "set-bus-volume", a.handleSetBusVolume). + Descr("set the volume (in decibels) of the given output bus"). + Example(exampleBusVolumeParam1, "ok"). + Example(exampleBusVolumeParam2, "ok"). + PathValueAlias("set-bus-volume/i/{bus}/{volume}"). + PathValueAlias("set-bus-volume/{bus_name}/{volume}") + + misirka.HandleCall(a.m, "raw-cmd", a.handleRawCmd). + Descr("execute a raw command on the audio hardware"). + Example("volume.set 0 1 2.5", "ok") + + misirka.HandleCall(a.m, "factory-reset", a.handleFactoryReset). + Descr("factory reset the audio hardware"). + Example(FactoryResetParam{}, "ok") + + a.m.HandleDoc() + a.m.HandleWebsocket() + + a.srv.Handler = a.m.HTTPHandler() a.srv.Addr = a.cfg.Bind return a } diff --git a/software/audioctl/api/examples.go b/software/audioctl/api/examples.go new file mode 100644 index 00000000..67a9d275 --- /dev/null +++ b/software/audioctl/api/examples.go @@ -0,0 +1,66 @@ +package api + +var exampleMatrixSendParam1 = SetMatrixSendParam{ + Chan: &exampleOne, + Bus: &exampleThree, + Unmuted: &exampleFalse, +} + +var exampleMatrixSendParam2 = SetMatrixSendParam{ + ChanName: &exampleFoo, + BusName: &exampleBar, + Unmuted: &exampleTrue, +} + +var exampleInGainParam1 = SetInGainParam{ + Chan: &exampleOne, + Gain: &exampleFourTwo, +} + +var exampleInGainParam2 = SetInGainParam{ + ChanName: &exampleFoo, + Gain: &exampleFourTwo, +} + +var examplePhantomParam1 = SetPhantomParam{ + Chan: &exampleOne, + Phantom: &exampleTrue, +} + +var examplePhantomParam2 = SetPhantomParam{ + ChanName: &exampleFoo, + Phantom: &exampleFalse, +} + +var exampleMatrixVolumeParam1 = SetMatrixVolumeParam{ + Chan: &exampleOne, + Bus: &exampleZero, + Volume: &exampleFourTwo, +} + +var exampleMatrixVolumeParam2 = SetMatrixVolumeParam{ + ChanName: &exampleFoo, + BusName: &examplePenka, + Volume: &exampleFourTwo, +} + +var exampleBusVolumeParam1 = SetBusVolumeParam{ + Bus: &exampleOne, + Volume: &exampleFourTwo, +} + +var exampleBusVolumeParam2 = SetBusVolumeParam{ + BusName: &examplePenka, + Volume: &exampleFourTwo, +} + +var exampleTrue = true +var exampleFalse = false +var exampleFoo = "foo" +var exampleBar = "bar" +var examplePenka = "penka" +var exampleDonka = "donka" +var exampleThree = uint8(3) +var exampleOne = uint8(1) +var exampleZero = uint8(0) +var exampleFourTwo = float32(42) diff --git a/software/audioctl/api/handlers.go b/software/audioctl/api/handlers.go index b4f31ecc..106aeb36 100644 --- a/software/audioctl/api/handlers.go +++ b/software/audioctl/api/handlers.go @@ -31,10 +31,10 @@ func (a *Api) handleSetFullState(param *ctl.MixerState) (string, *misirka.MErr) } type SetMatrixVolumeParam struct { - Chan *uint8 `json:"channel"` - Bus *uint8 `json:"bus"` - ChanName *string `json:"channel_name"` - BusName *string `json:"bus_name"` + Chan *uint8 `json:"channel,omitempty"` + Bus *uint8 `json:"bus,omitempty"` + ChanName *string `json:"channel_name,omitempty"` + BusName *string `json:"bus_name,omitempty"` Volume *float32 `json:"volume"` } @@ -145,8 +145,8 @@ func (a *Api) handleSetInGain(param SetInGainParam) (string, *misirka.MErr) { } type SetPhantomParam struct { - Chan *uint8 `json:"channel"` - ChanName *string `json:"channel_name"` + Chan *uint8 `json:"channel,omitempty"` + ChanName *string `json:"channel_name,omitempty"` Phantom *bool `json:"phantom"` } @@ -192,8 +192,8 @@ func (a *Api) handleFactoryReset(param FactoryResetParam) (string, *misirka.MErr } type SetBusVolumeParam struct { - Bus *uint8 `json:"bus"` - BusName *string `json:"bus_name"` + Bus *uint8 `json:"bus,omitempty"` + BusName *string `json:"bus_name,omitempty"` Volume *float32 `json:"volume"` } diff --git a/software/audioctl/fakectl/default_state.go b/software/audioctl/fakectl/default_state.go index 15ebda61..6039cc35 100644 --- a/software/audioctl/fakectl/default_state.go +++ b/software/audioctl/fakectl/default_state.go @@ -2,7 +2,7 @@ package fakectl import "github.com/fosdem/video/software/audioctl/ctl" -var defaultState *ctl.MixerState = &ctl.MixerState{ +var DefaultState *ctl.MixerState = &ctl.MixerState{ Channels: []ctl.ChannelState{ { Name: "fubalina", @@ -66,3 +66,20 @@ var defaultState *ctl.MixerState = &ctl.MixerState{ }, }, } + +func DefaultLevels() *ctl.Levels { + return &ctl.Levels{ + RMS: ctl.LevelsBlock{ + Input: make([]float32, len(DefaultState.Channels)), + Bus: make([]float32, len(DefaultState.Buses)), + }, + Peak: ctl.LevelsBlock{ + Input: make([]float32, len(DefaultState.Channels)), + Bus: make([]float32, len(DefaultState.Buses)), + }, + Smooth: ctl.LevelsBlock{ + Input: make([]float32, len(DefaultState.Channels)), + Bus: make([]float32, len(DefaultState.Buses)), + }, + } +} diff --git a/software/audioctl/fakectl/fakectl.go b/software/audioctl/fakectl/fakectl.go index f1d8998a..5176058f 100644 --- a/software/audioctl/fakectl/fakectl.go +++ b/software/audioctl/fakectl/fakectl.go @@ -18,22 +18,9 @@ type FakeCtl struct { func New(cfg *config.CtlCfg) *FakeCtl { return &FakeCtl{ - state: defaultState, - cfg: cfg, - levels: &ctl.Levels{ - RMS: ctl.LevelsBlock{ - Input: make([]float32, len(defaultState.Channels)), - Bus: make([]float32, len(defaultState.Buses)), - }, - Peak: ctl.LevelsBlock{ - Input: make([]float32, len(defaultState.Channels)), - Bus: make([]float32, len(defaultState.Buses)), - }, - Smooth: ctl.LevelsBlock{ - Input: make([]float32, len(defaultState.Channels)), - Bus: make([]float32, len(defaultState.Buses)), - }, - }, + state: DefaultState, + cfg: cfg, + levels: DefaultLevels(), } } @@ -166,7 +153,7 @@ func (c *FakeCtl) SetBusVolume(bus uint8, volume float32) error { } func (c *FakeCtl) FactoryReset() error { - c.state = defaultState + c.state = DefaultState return nil } From e1d54c2ae99cf1be3707de12b62b112ebd0a6995 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Wed, 22 Apr 2026 13:11:38 +0300 Subject: [PATCH 63/67] remove old audiomixer readme --- software/audiomixer/.gitignore | 5 ----- software/audiomixer/README.md | 2 -- 2 files changed, 7 deletions(-) delete mode 100644 software/audiomixer/.gitignore delete mode 100644 software/audiomixer/README.md diff --git a/software/audiomixer/.gitignore b/software/audiomixer/.gitignore deleted file mode 100644 index c6ca7d55..00000000 --- a/software/audiomixer/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.idea/ -__pycache__/ - -.mixer_history -venv/ diff --git a/software/audiomixer/README.md b/software/audiomixer/README.md deleted file mode 100644 index cff7cf28..00000000 --- a/software/audiomixer/README.md +++ /dev/null @@ -1,2 +0,0 @@ -### Audio mixer control -Migrated [here](https://github.com/fosdem/video-amixcontrol) From 34c24ab39b9891be5b5dc56f1b1db6e116c18e45 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Wed, 22 Apr 2026 13:35:41 +0300 Subject: [PATCH 64/67] update go deps --- software/audioctl/go.mod | 2 +- software/audioctl/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/software/audioctl/go.mod b/software/audioctl/go.mod index 30319de9..074dbb49 100644 --- a/software/audioctl/go.mod +++ b/software/audioctl/go.mod @@ -3,7 +3,7 @@ module github.com/fosdem/video/software/audioctl go 1.25.5 require ( - github.com/dexterlb/misirka/go v0.0.0-20260414144551-62b2df7bf08d + github.com/dexterlb/misirka/go v0.0.0-20260422100930-e5a62c25b8a8 github.com/goccy/go-yaml v1.19.2 go.bug.st/serial v1.6.4 ) diff --git a/software/audioctl/go.sum b/software/audioctl/go.sum index 631e4549..c219c047 100644 --- a/software/audioctl/go.sum +++ b/software/audioctl/go.sum @@ -2,8 +2,8 @@ github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0 github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dexterlb/misirka/go v0.0.0-20260414144551-62b2df7bf08d h1:s2XSKeoNxXRRC0lBJZLSreY0dONqL3QMjhfAUyVOwo8= -github.com/dexterlb/misirka/go v0.0.0-20260414144551-62b2df7bf08d/go.mod h1:VZeXV8DAty9fQUq9+W3W8IRcTEI/NEElYDBj8hBOx4E= +github.com/dexterlb/misirka/go v0.0.0-20260422100930-e5a62c25b8a8 h1:ZNcbOyW3RAWjwgg1t4wz46wQraOrYc8j55HD1VTDYbg= +github.com/dexterlb/misirka/go v0.0.0-20260422100930-e5a62c25b8a8/go.mod h1:VZeXV8DAty9fQUq9+W3W8IRcTEI/NEElYDBj8hBOx4E= github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= From 1d99b1d06ee3e07d2ad3cb02b18e8dc330c1ae22 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Wed, 22 Apr 2026 15:49:50 +0300 Subject: [PATCH 65/67] add some readmes and todos --- software/audioctl/README.md | 22 ++++++++++++++++++++++ software/audioctl/TODO.md | 7 +++++++ software/audioctl_ui/README.md | 12 ++++++++++++ software/audioctl_ui/TODO.md | 9 +++++++++ 4 files changed, 50 insertions(+) create mode 100644 software/audioctl/README.md create mode 100644 software/audioctl/TODO.md create mode 100644 software/audioctl_ui/README.md create mode 100644 software/audioctl_ui/TODO.md diff --git a/software/audioctl/README.md b/software/audioctl/README.md new file mode 100644 index 00000000..efce096f --- /dev/null +++ b/software/audioctl/README.md @@ -0,0 +1,22 @@ +## audioctl + +This application is used for controlling the rev-E and later +[FOSDEM audio boards](../../hardware/pcbs/audio_board). The +[firmware](../../hardware/firmware/audio_board) of these +boards uses a custom simple text-based protocol to communicate +over serial over USB. + +For earlier revision boards, which are controlled using hybrid +dual USB serial endpoints with OSC over SLIP over serial over USB, +see [video-amixcontrol](https://github.com/fosdem/video-amixcontrol). + +### Running + +``` +$ cp example_config.yaml my_config.yaml + +$ go run 'github.com/fosdem/video/software/audioctl/cmd/audioctl' my_config.yaml +``` + +For debug purposes, you can set `port_device: fake` in the config +to start the server without actual hardware. diff --git a/software/audioctl/TODO.md b/software/audioctl/TODO.md new file mode 100644 index 00000000..0def7cde --- /dev/null +++ b/software/audioctl/TODO.md @@ -0,0 +1,7 @@ +- [x] Communication with audio board +- [x] Fake audio board +- [x] Web API +- [ ] Expose metrics + - [ ] Generic metrics like uptime, etc + - [ ] Audio levels averaged over long periods (configurable period, default 10 seconds) + - This will be used for voc monitoring diff --git a/software/audioctl_ui/README.md b/software/audioctl_ui/README.md new file mode 100644 index 00000000..67ef4f66 --- /dev/null +++ b/software/audioctl_ui/README.md @@ -0,0 +1,12 @@ +## audioctl_ui + +Web UI that connects to [audioctl](../audioctl) for controlling the +rev-G and later [FOSDEM audio boards](../../hardware/pcbs/audio_board). + +For earlier revisions see [this mixer UI](https://github.com/FOSDEM/infrastructure/blob/master/ansible/playbooks/roles/video-control-server/files/web/mixer.js) + +### development + +- Start [audioctl](../audioctl) (assuming it listens on port 8811) +- Run `API_URL='http://localhost:8811' ./build.sh serve` +- Navigate your browser to `http://localhost:5173` diff --git a/software/audioctl_ui/TODO.md b/software/audioctl_ui/TODO.md new file mode 100644 index 00000000..fcc93bb5 --- /dev/null +++ b/software/audioctl_ui/TODO.md @@ -0,0 +1,9 @@ +- [ ] Make it pretty +- [ ] Make sliders usable with high frontend-backend latency + - Moving the slider should do calls to the backend in a throttled way + (don't send a new value if we haven't yet received a response from the previous value) + - The slider should not jump around while receiving new values while being + dragged by the user +- [ ] Make it usable from other UIs + - Other UIs should be able to embed this one and pass their own + `MisirkaClient` object From ea9e9f802a0ec43e04a56bec90a6e786eeee5180 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Wed, 22 Apr 2026 16:12:12 +0300 Subject: [PATCH 66/67] fix bug in fakectl --- software/audioctl/fakectl/fakectl.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/software/audioctl/fakectl/fakectl.go b/software/audioctl/fakectl/fakectl.go index 5176058f..a4a1045d 100644 --- a/software/audioctl/fakectl/fakectl.go +++ b/software/audioctl/fakectl/fakectl.go @@ -171,11 +171,11 @@ func (c *FakeCtl) Loop() error { diffs[i] = -0.4 - rand.Float32() } - if diffs[i] > 0 && c.levels.RMS.Input[i] > highThresholds[i] { + if diffs[i] > 0 && inputLevels[i] > highThresholds[i] { diffs[i] = -0.4 - rand.Float32() highThresholds[i] = -20*rand.Float32() + 4 } - if diffs[i] < 0 && c.levels.RMS.Input[i] < lowThresholds[i] { + if diffs[i] < 0 && inputLevels[i] < lowThresholds[i] { diffs[i] = 0.4 + rand.Float32() lowThresholds[i] = -50*rand.Float32() - 70 } From 49ae2cf4aa1cd15ff4e9cd52dbfa4c86e6da2148 Mon Sep 17 00:00:00 2001 From: dexterlb Date: Wed, 22 Apr 2026 17:33:43 +0300 Subject: [PATCH 67/67] update deps --- software/audioctl/go.mod | 2 +- software/audioctl/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/software/audioctl/go.mod b/software/audioctl/go.mod index 074dbb49..3b3cbf0e 100644 --- a/software/audioctl/go.mod +++ b/software/audioctl/go.mod @@ -3,7 +3,7 @@ module github.com/fosdem/video/software/audioctl go 1.25.5 require ( - github.com/dexterlb/misirka/go v0.0.0-20260422100930-e5a62c25b8a8 + github.com/dexterlb/misirka/go v0.0.0-20260422143033-240dae907622 github.com/goccy/go-yaml v1.19.2 go.bug.st/serial v1.6.4 ) diff --git a/software/audioctl/go.sum b/software/audioctl/go.sum index c219c047..0037c5b8 100644 --- a/software/audioctl/go.sum +++ b/software/audioctl/go.sum @@ -2,8 +2,8 @@ github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0 github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dexterlb/misirka/go v0.0.0-20260422100930-e5a62c25b8a8 h1:ZNcbOyW3RAWjwgg1t4wz46wQraOrYc8j55HD1VTDYbg= -github.com/dexterlb/misirka/go v0.0.0-20260422100930-e5a62c25b8a8/go.mod h1:VZeXV8DAty9fQUq9+W3W8IRcTEI/NEElYDBj8hBOx4E= +github.com/dexterlb/misirka/go v0.0.0-20260422143033-240dae907622 h1:fXoQDvy2HroPbotp/nsALl6KsfHbNLDcfYztqUS9Ga8= +github.com/dexterlb/misirka/go v0.0.0-20260422143033-240dae907622/go.mod h1:VZeXV8DAty9fQUq9+W3W8IRcTEI/NEElYDBj8hBOx4E= github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=