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/.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/.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/CMakeLists.txt b/hardware/firmware/audio_board/CMakeLists.txt index 5d442389..9fdc8c2f 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) @@ -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) @@ -107,13 +106,18 @@ set(FEATURES USE_DISPLAY ) -set(SOURCES +set(SOURCES teensy4_usb_descriptor.c 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 + src/cli/commands.cpp + src/debug.cpp + src/channels.cpp ) # Build this project 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 new file mode 100644 index 00000000..3f8b3756 --- /dev/null +++ b/hardware/firmware/audio_board/src/channels.cpp @@ -0,0 +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", "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..81707af5 100644 --- a/hardware/firmware/audio_board/src/channels.h +++ b/hardware/firmware/audio_board/src/channels.h @@ -1,20 +1,22 @@ -#ifndef _CHANNELS_H_ -#define _CHANNELS_H_ +#pragma once #include +#define CHANNELS 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) - -#endif +#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 new file mode 100644 index 00000000..f0a7445a --- /dev/null +++ b/hardware/firmware/audio_board/src/cli/cli.cpp @@ -0,0 +1,273 @@ +#include "cli.h" + +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); +} + +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); +} + +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; +} + +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); + } +} + +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::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; +} + +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; +} + +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("[err] 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); + } + } +} + +void Cli::print_fail() { + if (this->slug > 0) { + this->port->printf("[%d err]", this->slug); + this->slug = 0; + } else { + this->port->printf("[err]"); + } +} + +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 new file mode 100644 index 00000000..3c381eb6 --- /dev/null +++ b/hardware/firmware/audio_board/src/cli/cli.h @@ -0,0 +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(); +}; 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..2c0454dd --- /dev/null +++ b/hardware/firmware/audio_board/src/cli/commands.cpp @@ -0,0 +1,353 @@ +#include "cli.h" + +#include "../config.h" +#include "../teensyaudio.h" +#include "../db_conversion.h" +#include "../debug.h" +#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 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 = " ", + .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; + } + + 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(); + + 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); + + 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; + } + + 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", + }, +}; 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 diff --git a/hardware/firmware/audio_board/src/config.h b/hardware/firmware/audio_board/src/config.h index 4f9a7762..7c56731d 100644 --- a/hardware/firmware/audio_board/src/config.h +++ b/hardware/firmware/audio_board/src/config.h @@ -1,10 +1,10 @@ -#ifndef _MIXER_CONFIG_H_ -#define _MIXER_CONFIG_H_ +#pragma once -#define CHANNELS 6 -#define BUSES 6 +#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 +#define PIN_PHANTOM_IN3 35 -#endif +#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/debug.cpp b/hardware/firmware/audio_board/src/debug.cpp new file mode 100644 index 00000000..d988a226 --- /dev/null +++ b/hardware/firmware/audio_board/src/debug.cpp @@ -0,0 +1,50 @@ +#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); diff --git a/hardware/firmware/audio_board/src/display.cpp b/hardware/firmware/audio_board/src/display.cpp index c2b8bb0b..7c6cb1e0 100644 --- a/hardware/firmware/audio_board/src/display.cpp +++ b/hardware/firmware/audio_board/src/display.cpp @@ -1,82 +1,75 @@ #include "display.h" #include "channels.h" -#include "helpers.h" +#include "db_conversion.h" #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, 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, out_level_to_scale(rms)); } -void display_update_vu(float levels_rms[CHANNELS + BUSES], - ChanInfo channel_info[CHANNELS + BUSES]) { - display.fillScreen(RGB(0, 0, 0)); +void display_update_vu(float levels_rms[CHANNELS + BUSES]) { + 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 f6ec2bc7..43a2a738 100644 --- a/hardware/firmware/audio_board/src/display.h +++ b/hardware/firmware/audio_board/src/display.h @@ -7,13 +7,12 @@ #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(); -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/helpers.cpp b/hardware/firmware/audio_board/src/helpers.cpp deleted file mode 100644 index 9e314dbd..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 55ca22b7..b0464bd4 100644 --- a/hardware/firmware/audio_board/src/main.cpp +++ b/hardware/firmware/audio_board/src/main.cpp @@ -1,12 +1,9 @@ -#include -#include -#include - #include "config.h" -#include "helpers.h" #include #include "teensyaudio.h" +#include "cli/cli.h" +#include "debug.h" #ifdef USE_EEPROM @@ -20,387 +17,56 @@ #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}, -}; - -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) { - 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 - while (!SerialUSB1 && millis() < 1500) - ; - SerialUSB1.print(CrashReport); - } + SerialUSB.begin(115200); + debug_init(); + debug_print("board ready"); #ifdef USE_DISPLAY - display_setup(); + display_setup(); #endif - audio_load_state(); - - audio_setup(); + audio_load_state(); - slip.begin(115200); - SerialUSB1.println("board ready"); + audio_setup(); } +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); - } - } + 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, channel_info); + 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(); + #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(); - SerialUSB1.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 new file mode 100644 index 00000000..321ee8e7 --- /dev/null +++ b/hardware/firmware/audio_board/src/taa3040.cpp @@ -0,0 +1,203 @@ + +#include "taa3040.h" +#include "debug.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; +} + +/* + * 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)); + setRegister(REG_P0_CH1_CFG1 + offset, gain << 2); + 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) { + 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"); + } +} + +void AudioControlTAA3040::setRegister(uint8_t page, uint8_t reg, uint8_t value) { + debug_printf("REG %02x:%02x = %02x\n", page, 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; +} + +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); +} diff --git a/hardware/firmware/audio_board/src/taa3040.h b/hardware/firmware/audio_board/src/taa3040.h new file mode 100644 index 00000000..d9c30d27 --- /dev/null +++ b/hardware/firmware/audio_board/src/taa3040.h @@ -0,0 +1,44 @@ +#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 REG_P0_CH1_CFG2 (0x3E) +#define REG_P0_CH1_CFG3 (0x3F) + +#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); + bool digitalGain(uint8_t channel, float gainDb); + + 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 5bfb4d4b..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 @@ -12,215 +12,292 @@ #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}, - {&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(64); + AudioMemory(80); - 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(); + pinMode(PIN_PHANTOM_IN1, OUTPUT); + pinMode(PIN_PHANTOM_IN2, OUTPUT); + pinMode(PIN_PHANTOM_IN3, OUTPUT); - sgtl5000_2.setWire(1); + taa3040.enable(); +} - 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(); +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 (int 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(); - } - } +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); } -Levels &audio_get_levels() { return levels; } +float raw_get_crosspoint(uint8_t channel, uint8_t bus) { + return matrix[bus][channel / 4]->getGain(channel % 4); +} -void raw_set_crosspoint(int channel, int bus, float gain) { - matrix[bus][channel / 4]->gain(channel % 4, gain); +uint64_t mute_mask(uint64_t channel, uint64_t bus) { + return (uint64_t)1 << (uint64_t)((channel * CHANNELS) + bus); } -float raw_get_crosspoint(int channel, int bus) { - return matrix[bus][channel / 4]->getGain(channel % 4); +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; + } } -void raw_set_mix(int 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); +bool is_phantom_on(uint8_t channel) { + return !!(state.phantoms & (1 << channel)); } -uint64_t mute_mask(uint64_t channel, uint64_t bus) { - return (uint64_t)1 << (uint64_t)((channel * CHANNELS) + bus); +void set_phantom_on(uint8_t channel) { + if (channel > 2) { + return; + } + state.phantoms |= (1 << channel); + apply_phantom(channel); } -bool is_muted(int channel, int bus) { - return !!(state.mutes & mute_mask(channel, bus)); +void set_phantom_off(uint8_t channel) { + if (channel > 2) { + return; + } + state.phantoms &= ~(1 << channel); + apply_phantom(channel); } -float calc_real_gain(int channel, int bus, int gain) { - return gain * !is_muted(channel, bus) * state.bus_multipliers[bus] * - state.channel_multipliers[channel]; +bool is_muted(uint8_t channel, uint8_t bus) { + return !!(state.mutes & mute_mask(channel, bus)); } -void set_gain(int channel, int bus, int gain) { - state.gains[channel][bus] = gain; - raw_set_crosspoint(channel, bus, calc_real_gain(channel, bus, gain)); +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); } -float get_gain(int channel, int bus) { return state.gains[channel][bus]; } +void apply_volume(uint8_t channel, uint8_t bus) { + raw_set_crosspoint(channel, bus, calc_real_volume(channel, bus)); +} -void mute(int channel, int bus) { - state.mutes |= mute_mask(channel, bus); - set_gain(channel, bus, state.gains[channel][bus]); +void set_volume_dB(uint8_t channel, uint8_t bus, float volume) { + state.matrix_dB[channel][bus] = volume; + apply_volume(channel, bus); } -void unmute(int channel, int bus) { - state.mutes &= ~mute_mask(channel, bus); - set_gain(channel, bus, state.gains[channel][bus]); +float get_volume_dB(uint8_t channel, uint8_t bus) { + return state.matrix_dB[channel][bus]; } -void set_bus_multiplier(int bus, float multiplier) { - state.bus_multipliers[bus] = multiplier; - for (int channel = 0; channel < CHANNELS; ++channel) - set_gain(channel, bus, state.gains[channel][bus]); +void mute(uint8_t channel, uint8_t bus) { + state.mutes |= mute_mask(channel, bus); + apply_volume(channel, bus); } -float get_bus_multiplier(int bus) { return state.bus_multipliers[bus]; } +void unmute(uint8_t channel, uint8_t bus) { + state.mutes &= ~mute_mask(channel, bus); + apply_volume(channel, bus); +} -void set_channel_multiplier(int channel, float multiplier) { - state.channel_multipliers[channel] = multiplier; - for (int bus = 0; bus < BUSES; ++bus) - set_gain(channel, bus, state.gains[channel][bus]); +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_channel_multiplier(int channel) { - return state.channel_multipliers[channel]; +float get_bus_volume_dB(uint8_t bus) { + return state.bus_volumes_dB[bus]; } -void reset_gains() { memcpy(state.gains, default_gains, sizeof(state.gains)); } +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_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_dB[channel]); +} + +float get_channel_input_gain_dB(uint8_t channel) { + return state.channel_input_gains_dB[channel]; +} + +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)); + memcpy(&state.mutes, &default_mutes, sizeof(state.mutes)); } -void reset_bus_multipliers() { - memcpy(state.bus_multipliers, default_bus_multipliers, - BUSES * sizeof(float)); +void reset_phantoms() { + memcpy(&state.phantoms, &default_phantoms, sizeof(state.phantoms)); } -void reset_channel_multipliers() { - memcpy(state.channel_multipliers, default_channel_multipliers, - CHANNELS * sizeof(float)); + +void reset_bus_volumes() { + memcpy(state.bus_volumes_dB, default_bus_volumes_dB, BUSES * sizeof(float)); +} + +void reset_channel_input_gains() { + memcpy(state.channel_input_gains_dB, default_channel_input_gains_dB, CHANNELS * sizeof(float)); } void apply_all() { - int i, j; - for (i = 0; i < CHANNELS; ++i) - for (j = 0; j < BUSES; ++j) - set_gain(i, j, state.gains[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_gains(); - reset_mutes(); - reset_bus_multipliers(); - reset_channel_multipliers(); + reset_matrix(); + reset_mutes(); + reset_phantoms(); + reset_bus_volumes(); + reset_channel_input_gains(); } -bool gains_ok() { - int i, j; - for (i = 0; i < CHANNELS; ++i) - for (j = 0; j < BUSES; ++j) - if (isnan(state.gains[i][j])) - return false; - - return true; +bool matrix_ok() { + uint8_t i, j; + for (i = 0; i < CHANNELS; ++i) { + for (j = 0; j < BUSES; ++j) { + if (isnan(state.matrix_dB[i][j])) { + return false; + } + } + } + + return true; } -bool bus_multipliers_ok() { - int i; - for (i = 0; i < BUSES; ++i) - if (isnan(state.bus_multipliers[i])) - return false; +bool bus_volumes_ok() { + uint8_t i; + for (i = 0; i < BUSES; ++i) { + if (isnan(state.bus_volumes_dB[i])) { + return false; + } + } - return true; + return true; } -bool channel_multipliers_ok() { - int i; - for (i = 0; i < CHANNELS; ++i) - if (isnan(state.channel_multipliers[i])) - return false; +bool channel_input_gains_ok() { + uint8_t i; + for (i = 0; i < CHANNELS; ++i) { + if (isnan(state.channel_input_gains_dB[i])) { + return false; + } + } - return true; + 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 (!gains_ok() || !bus_multipliers_ok() || !channel_multipliers_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 5e3b85ae..28e96dcf 100644 --- a/hardware/firmware/audio_board/src/teensyaudio.h +++ b/hardware/firmware/audio_board/src/teensyaudio.h @@ -10,33 +10,30 @@ 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(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, int gain); -float get_gain(int channel, int 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_multiplier(int bus); -void set_bus_multiplier(int bus, float multiplier); +float get_bus_volume_dB(uint8_t bus); +void set_bus_volume_dB(uint8_t bus, float vol_dB); -float get_channel_multiplier(int channel); -void set_channel_multiplier(int 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(int channel, int bus, float gain); -float raw_get_crosspoint(int channel, int bus); - -// void raw_set_mix(int bus, float channel_gains[CHANNELS]); - -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(); +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 e3eaceac..a74a8916 100644 --- a/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio_defaults.cpp @@ -1,23 +1,29 @@ -#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_dB[BUSES] = { + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.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] = { - // 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}}; +const PROGMEM uint16_t default_phantoms = 0; + +const PROGMEM float default_matrix_dB[CHANNELS][BUSES] = { + // outputs: OUT1, OUT2, HP1, HP2, USB1, USB2 + // IN1 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + // IN2 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + // IN3 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + // PC + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + // USB1 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + // USB2 + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f} +}; diff --git a/hardware/firmware/audio_board/src/teensyaudio_generated.cpp b/hardware/firmware/audio_board/src/teensyaudio_generated.cpp index d67ecc01..51ea5ac1 100644 --- a/hardware/firmware/audio_board/src/teensyaudio_generated.cpp +++ b/hardware/firmware/audio_board/src/teensyaudio_generated.cpp @@ -1,144 +1,142 @@ // 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); +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); +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, i2s_quad1, 0); +AudioConnection patchCord76(mixer6, rms8); +AudioConnection patchCord77(mixer6, peak8); +AudioConnection patchCord78(mixer6, 0, i2s_quad1, 1); +AudioConnection patchCord79(mixer9, rms9); +AudioConnection patchCord80(mixer9, peak9); +AudioConnection patchCord81(mixer9, 0, i2s_quad1, 2); +AudioConnection patchCord82(mixer12, rms10); +AudioConnection patchCord83(mixer12, peak10); +AudioConnection patchCord84(mixer12, 0, i2s_quad1, 3); +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/src/types.h b/hardware/firmware/audio_board/src/types.h index 0b6cb4aa..1948caef 100644 --- a/hardware/firmware/audio_board/src/types.h +++ b/hardware/firmware/audio_board/src/types.h @@ -2,21 +2,23 @@ #define _MIXER_TYPES_H_ #include "config.h" +#include "channels.h" #include typedef struct { - float gains[CHANNELS][BUSES]; + float matrix_dB[CHANNELS][BUSES]; - uint64_t mutes; + uint64_t mutes; + uint16_t phantoms; - float channel_multipliers[CHANNELS]; - float bus_multipliers[BUSES]; + float channel_input_gains_dB[CHANNELS]; + float bus_volumes_dB[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 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); -} +} diff --git a/hardware/firmware/audio_board/vendor/Audio/output_tdm.cpp b/hardware/firmware/audio_board/vendor/Audio/output_tdm.cpp index 8f3a6158..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); @@ -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 } 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 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..c8d2968e 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 @@ -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 @@ -898,7 +883,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. 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(); } 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/api/api.go b/software/audioctl/api/api.go new file mode 100644 index 00000000..207e8a21 --- /dev/null +++ b/software/audioctl/api/api.go @@ -0,0 +1,158 @@ +package api + +import ( + "log/slog" + "net/http" + "time" + + "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 { + srv http.Server + m *misirka.Misirka + logger *slog.Logger + cfg *config.ApiCfg + 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 { + a := &Api{} + a.cfg = cfg + 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) + }).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 +} + +func (a *Api) Serve() error { + defer close(a.dying) + go a.doHeartbeat() + go a.poller() + 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() { + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + + var h Heartbeat + for { + select { + case <-a.dying: + return + case t := <-ticker.C: + h.Now = t + misirka.Publish(a.m, "heartbeat", h) + } + } +} + +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 <-a.refreshState: + a.pollState() + case <-pollState.C: + a.pollState() + case <-pollLevels.C: + a.pollLevels() + } + } +} + +func (a *Api) forceRefresh() { + // TODO: throttling + a.refreshState <- struct{}{} +} 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 new file mode 100644 index 00000000..106aeb36 --- /dev/null +++ b/software/audioctl/api/handlers.go @@ -0,0 +1,286 @@ +package api + +import ( + "fmt" + + "github.com/dexterlb/misirka/go/misirka" + "github.com/fosdem/video/software/audioctl/ctl" +) + +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 +} + +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,omitempty"` + Bus *uint8 `json:"bus,omitempty"` + ChanName *string `json:"channel_name,omitempty"` + BusName *string `json:"bus_name,omitempty"` + 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"` + BusName *string `json:"bus_name"` + Unmuted *bool `json:"unmuted"` +} + +func (a *Api) handleSetMatrixSend(param SetMatrixSendParam) (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, + } + } + a.forceRefresh() + 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,omitempty"` + ChanName *string `json:"channel_name,omitempty"` + 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 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,omitempty"` + BusName *string `json:"bus_name,omitempty"` + 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 { + a.logger.Error("could not poll state", "err", err) + return + } + + 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) +} + +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.chanNames { + if *name == a.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.busNames { + if *name == a.busNames[i] { + j := uint8(i) + *idx = &j + return nil + } + } + + return fmt.Errorf("no such bus: %s", *name) +} diff --git a/software/audioctl/cmd/audioctl/main.go b/software/audioctl/cmd/audioctl/main.go new file mode 100644 index 00000000..6319ebbe --- /dev/null +++ b/software/audioctl/cmd/audioctl/main.go @@ -0,0 +1,51 @@ +package main + +import ( + "fmt" + "log/slog" + "os" + + "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" +) + +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) + } + + 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 { + 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) + 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..97197340 --- /dev/null +++ b/software/audioctl/config/api_cfg.go @@ -0,0 +1,22 @@ +package config + +import "fmt" + +type ApiCfg struct { + 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/config.go b/software/audioctl/config/config.go new file mode 100644 index 00000000..dc28cab3 --- /dev/null +++ b/software/audioctl/config/config.go @@ -0,0 +1,65 @@ +package config + +import ( + "fmt" + "os" + + "github.com/goccy/go-yaml" +) + +type Config struct { + Api *ApiCfg + Ctl *CtlCfg +} + +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) + } + 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..57c62336 --- /dev/null +++ b/software/audioctl/config/ctl_cfg.go @@ -0,0 +1,18 @@ +package config + +import "fmt" + +type CtlCfg struct { + 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 new file mode 100644 index 00000000..f1795a00 --- /dev/null +++ b/software/audioctl/ctl/ctl.go @@ -0,0 +1,21 @@ +package ctl + +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..3117a898 --- /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 { + 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 { + 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"` // crosspoint volume in dB (0 means identity) +} + +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/example_config.yaml b/software/audioctl/example_config.yaml new file mode 100644 index 00000000..e41592fd --- /dev/null +++ b/software/audioctl/example_config.yaml @@ -0,0 +1,7 @@ +api: + bind: ':8811' + state_poll_interval_msec: 10000 + levels_poll_interval_msec: 100 +ctl: + port_device: '/dev/ttyACM0' + use_db_levels: true diff --git a/software/audioctl/fakectl/default_state.go b/software/audioctl/fakectl/default_state.go new file mode 100644 index 00000000..6039cc35 --- /dev/null +++ b/software/audioctl/fakectl/default_state.go @@ -0,0 +1,85 @@ +package fakectl + +import "github.com/fosdem/video/software/audioctl/ctl" + +var DefaultState *ctl.MixerState = &ctl.MixerState{ + Channels: []ctl.ChannelState{ + { + Name: "fubalina", + Label: "foo", + Gain: -60, + Phantom: false, + Sends: []ctl.SendState{ + { + Unmuted: true, + Volume: 0, + }, + { + Unmuted: false, + Volume: 0, + }, + }, + }, + { + Name: "barabela", + Label: "bar", + Gain: 0, + Phantom: false, + Sends: []ctl.SendState{ + { + Unmuted: true, + Volume: -18, + }, + { + Unmuted: true, + Volume: -30, + }, + }, + }, + { + Name: "bazinga", + Label: "baz", + Gain: 0, + Phantom: true, + Sends: []ctl.SendState{ + { + Unmuted: false, + Volume: 0, + }, + { + Unmuted: true, + Volume: -20, + }, + }, + }, + }, + Buses: []ctl.BusState{ + { + Name: "penka", + Label: "pe", + Volume: 0, + }, + { + Name: "donka", + Label: "do", + Volume: -10, + }, + }, +} + +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 new file mode 100644 index 00000000..a4a1045d --- /dev/null +++ b/software/audioctl/fakectl/fakectl.go @@ -0,0 +1,261 @@ +package fakectl + +import ( + "fmt" + "math" + "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: DefaultLevels(), + } +} + +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)) + 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 { + if diffs[i] == 0 { + diffs[i] = -0.4 - rand.Float32() + } + + if diffs[i] > 0 && inputLevels[i] > highThresholds[i] { + diffs[i] = -0.4 - rand.Float32() + highThresholds[i] = -20*rand.Float32() + 4 + } + if diffs[i] < 0 && inputLevels[i] < lowThresholds[i] { + diffs[i] = 0.4 + rand.Float32() + lowThresholds[i] = -50*rand.Float32() - 70 + } + + 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 { + // 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 +} + +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))) +} diff --git a/software/audioctl/go.mod b/software/audioctl/go.mod new file mode 100644 index 00000000..3b3cbf0e --- /dev/null +++ b/software/audioctl/go.mod @@ -0,0 +1,16 @@ +module github.com/fosdem/video/software/audioctl + +go 1.25.5 + +require ( + 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 +) + +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 new file mode 100644 index 00000000..0037c5b8 --- /dev/null +++ b/software/audioctl/go.sum @@ -0,0 +1,22 @@ +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-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= +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= +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= 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/serialctl/getlevels.go b/software/audioctl/serialctl/getlevels.go new file mode 100644 index 00000000..6cb61bb2 --- /dev/null +++ b/software/audioctl/serialctl/getlevels.go @@ -0,0 +1,68 @@ +package serialctl + +import ( + "fmt" + + "github.com/fosdem/video/software/audioctl/ctl" +) + +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)") + } + + 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 := &ctl.Levels{ + RMS: ctl.LevelsBlock{ + Input: make([]float32, c.numChans), + Bus: make([]float32, c.numBuses), + }, + Peak: ctl.LevelsBlock{ + Input: make([]float32, c.numChans), + Bus: make([]float32, c.numBuses), + }, + Smooth: ctl.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/serialctl/getstate.go b/software/audioctl/serialctl/getstate.go new file mode 100644 index 00000000..ed1ac769 --- /dev/null +++ b/software/audioctl/serialctl/getstate.go @@ -0,0 +1,231 @@ +package serialctl + +import ( + "fmt" + "strconv" + "strings" + + "github.com/fosdem/video/software/audioctl/ctl" +) + +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) + } + 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) + } + + 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 := &ctl.MixerState{ + Channels: make([]ctl.ChannelState, c.numChans), + Buses: make([]ctl.BusState, c.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([]ctl.SendState, c.numBuses) + for j := range m.Buses { + m.Channels[i].Sends[j] = sends[i*c.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 *SerialCtl) GetChannelNames() ([]string, []string, error) { + resp, err := c.RawCmd("channel.names") + if err != nil { + return nil, nil, err + } + return parseChannelBusList(resp, "channels", "buses") +} + +func (c *SerialCtl) GetChannelLabels() ([]string, []string, error) { + resp, err := c.RawCmd("channel.labels") + if err != nil { + return nil, nil, err + } + return parseChannelBusList(resp, "channels", "buses") +} + +func (c *SerialCtl) GetInputGains() ([]float32, error) { + resp, err := c.RawCmd("gains") + if err != nil { + return nil, err + } + return parseFloatList(resp) +} + +func (c *SerialCtl) GetBusVolumes() ([]float32, error) { + resp, err := c.RawCmd("bus-volumes") + if err != nil { + return nil, err + } + return parseFloatList(resp) +} + +func (c *SerialCtl) GetPhantoms() ([]bool, error) { + resp, err := c.RawCmd("phantoms") + if err != nil { + return nil, err + } + return parseBoolList(resp) +} + +func (c *SerialCtl) GetMatrix() ([]ctl.SendState, error) { + resp, err := c.RawCmd("matrix") + if err != nil { + return nil, err + } + + fields := strings.Fields(resp) + result := make([]ctl.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) (ctl.SendState, error) { + var ss ctl.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 +} diff --git a/software/audioctl/serialctl/setters.go b/software/audioctl/serialctl/setters.go new file mode 100644 index 00000000..3f3f8dcb --- /dev/null +++ b/software/audioctl/serialctl/setters.go @@ -0,0 +1,124 @@ +package serialctl + +import ( + "fmt" + + "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 { + 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 *SerialCtl) 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, boolToNum(unmuted))) + if err != nil { + return err + } + + return nil +} + +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") + } + + _, err := c.RawCmd(fmt.Sprintf("volume.set %d %d %.4f", ch, bus, volume)) + if err != nil { + return err + } + + return nil +} + +func (c *SerialCtl) 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 *SerialCtl) 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 *SerialCtl) 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 (c *SerialCtl) FactoryReset() error { + _, err := c.RawCmd("factory-reset") + if err != nil { + return err + } + + return nil +} + +func boolToNum(b bool) int { + if b { + return 1 + } else { + return 0 + } +} 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/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 diff --git a/software/audioctl_ui/assets/favicon.png b/software/audioctl_ui/assets/favicon.png new file mode 100644 index 00000000..3228e30b Binary files /dev/null and b/software/audioctl_ui/assets/favicon.png differ diff --git a/software/audioctl_ui/build.sh b/software/audioctl_ui/build.sh new file mode 100755 index 00000000..8eb01c1c --- /dev/null +++ b/software/audioctl_ui/build.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -euo pipefail + +cdir="$(dirname "$(readlink -f "${0}")")" + +cd "${cdir}" + +if ! [[ package.json -ot node_modules/marker ]]; then + npm install && touch node_modules/marker +fi + +if [[ $# -eq 0 || "${1}" == build ]]; then + npm run build +elif [[ "${1}" == serve ]]; then + npm run dev +fi 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/index.html b/software/audioctl_ui/index.html new file mode 100644 index 00000000..7fdd4e5b --- /dev/null +++ b/software/audioctl_ui/index.html @@ -0,0 +1,14 @@ + + + + + + + + Audio control + + +
+ + + diff --git a/software/audioctl_ui/package-lock.json b/software/audioctl_ui/package-lock.json new file mode 100644 index 00000000..4803b80b --- /dev/null +++ b/software/audioctl_ui/package-lock.json @@ -0,0 +1,3099 @@ +{ + "name": "audioctl_ui", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "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", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "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, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "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, + "dependencies": { + "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/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.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/@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", + "integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==", + "dev": true, + "license": "MIT", + "optional": true, + "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/@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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/@rollup/rollup-android-arm64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@rollup/rollup-darwin-x64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "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": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "peer": true + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "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" + ], + "dev": true, + "license": "MIT", + "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/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" + }, + "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", + "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.19.0" + } + }, + "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": { + "@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": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "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/@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", + "engines": { + "node": ">= 4" + } + }, + "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": { + "@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": "^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/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": { + "@typescript-eslint/tsconfig-utils": "^8.58.2", + "@typescript-eslint/types": "^8.58.2", + "debug": "^4.4.3" + }, + "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/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": { + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "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": "MIT", + "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/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/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/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", + "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": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "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", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "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", + "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/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", + "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/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": { + "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/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", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "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", + "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/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", + "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.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": { + "node": ">=8.6" + }, + "funding": { + "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#62b2df7bf08d316da136eb532c3cfac4aedaf8a6", + "dev": true + }, + "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/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", + "integrity": "sha512-tfIfIRJVc32gUI+cQrxwAGWLwTy/EENnB3vF95RuAFosqBGjMTY+o0/3T4oPOipUFrsebQ5WDMsRyAqn65RMFQ==", + "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/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/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", + "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": "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/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.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "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": [ + { + "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/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", + "integrity": "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "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", + "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.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "peer": true, + "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.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", + "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/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.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.4" + }, + "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/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", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "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", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "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", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "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", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "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", + "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.15", + "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", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.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 + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "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-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", + "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" + } + }, + "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", + "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 new file mode 100644 index 00000000..b38e147f --- /dev/null +++ b/software/audioctl_ui/package.json @@ -0,0 +1,32 @@ +{ + "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.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..80df8bcf --- /dev/null +++ b/software/audioctl_ui/src/api_data.ts @@ -0,0 +1,56 @@ +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, +}) + +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, + ChannelStateSchema, + BusStateSchema, + MixerStateSchema, + LevelsBlockSchema, + LevelsSchema, + OkRespSchema, +} 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..3f9abcde --- /dev/null +++ b/software/audioctl_ui/src/mixer_ui.ts @@ -0,0 +1,288 @@ +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.innerHTML = '' + + 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() { + this.container.textContent = 'reconnecting' + } + + private handle_state(state: MixerState) { + // 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) { + 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 new file mode 100644 index 00000000..eef40b95 --- /dev/null +++ b/software/audioctl_ui/src/mixerclient.ts @@ -0,0 +1,65 @@ +import type { MisirkaClient } from "misirka" +import { MixerStateSchema, LevelsSchema, OkRespSchema } 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 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, + ) + } + + async subscribe_levels(handler: (levels: Levels) => void) { + await this.client.subscribe( + ['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/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 @@ +/// diff --git a/software/audioctl_ui/styles/app.css b/software/audioctl_ui/styles/app.css new file mode 100644 index 00000000..171612dc --- /dev/null +++ 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/tsconfig.json b/software/audioctl_ui/tsconfig.json new file mode 100644 index 00000000..0025a032 --- /dev/null +++ b/software/audioctl_ui/tsconfig.json @@ -0,0 +1,29 @@ +{ + "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/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, + } +}) 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)