// copyright (C) 2006 Marcin Slonicki // copyright (C) 2007 Louis Frigon // Copyright (C) 2015 Allen Hill // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "board.h" #include "cdchanger.hpp" #include "com232.h" #include "frame.hpp" #include "peripheral.hpp" #include "queue.hpp" const char *const offon[] = {"OFF", "ON"}; constexpr uint8_t CACHE_SIZE = 32; using namespace avclan; namespace { Frame frames[CACHE_SIZE]; constinit Queue cache(frames); constinit Queue incoming = cache; constinit Queue outgoing = cache; void toggle_flag(bool *flag, const char *msg) { *flag = !*flag; RS232_Print(msg); RS232_Print(offon[*flag]); RS232_Print("\n"); } void set_flag(bool *flag, bool val, const char *msg) { *flag = val; RS232_Print(msg); RS232_Print(offon[val]); RS232_Print("\n"); } void Setup(); void print_help(); } // namespace int main() { uint8_t hexChars[2]; uint8_t hexDigit = 0; // current digit being written to hexChars bool readSeq = false; bool seqIsUnicast = false; bool readBinary = false; bool verbose = true; bool printAllFrames = true; bool lastPrintAllFrames = true; bool printBinary = false; bool echoCharacters = true; bool muteBus = false; // Binary-mode REPL includes the full wire preamble (broadcast + 2*addr + // control + length), so size for the worst case. uint8_t data_tmp[Frame::MAXLENGTH + sizeof(Frame)]; uint8_t seqIdx = 0; // current index in data_tmp Bus phy; using enum Action; using enum Device; Peripheral peripheral(phy, 0x360); using Error = decltype(peripheral)::Error; using Print = Frame::Print; Setup(); print_help(); while (true) { if (AVCLAN_busActive()) { if (auto msg = cache.pop()) { auto err = peripheral.read(msg.get(), Print{.print = printAllFrames, .binary = printBinary, .verbose = verbose}); if (err == Error::Read{0x00}) incoming.push(std::move(msg)); } else { RS232_Print("!! Dropping an incoming message; cache is empty !!\n"); } } if (const auto *in = incoming.peek()) { if (auto out = cache.pop()) { peripheral.route(in, out.get()); incoming.pop(); if (out->reaction > 0) outgoing.push(std::move(out)); } else { RS232_Print("!! Unable to respond; cache is empty !!\n"); } } peripheral.poll_devices([&](auto &dev) { if (auto status = cache.pop()) { dev.emit(status.get(), peripheral.controller()); outgoing.push(std::move(status)); return true; } return false; }); if (auto out = outgoing.pop()) { auto err = peripheral.send( out.get(), Print{.print = printAllFrames, .binary = printBinary}); peripheral.react(out.get(), err); if (out->reaction > 0) outgoing.push(std::move(out)); } // Key handler if (RS232_hasChar()) { char readkey = RS232_getChar(); switch (readkey) { case '?': print_help(); break; case 'v': toggle_flag(&verbose, "Verbose errors: "); break; case 'l': toggle_flag(&printAllFrames, "Logging: "); break; case 'k': toggle_flag(&echoCharacters, "Echo characters: "); break; case 'm': toggle_flag(&muteBus, "Mute device: "); AVCLAN_muteDevice(muteBus); break; // X/x isn't a toggle interface because this is used // programmatically and is simpler than reading back the toggle // state case 'X': set_flag(&printBinary, true, "Binary: "); break; case 'x': set_flag(&printBinary, false, "Binary: "); break; case 'E': // Beep if (auto out = cache.pop()) { out->is_unicast = true; out->peripheral_addr = peripheral.controller(); { const uint8_t beep[] = {0x00, to_underlying(CD_CHANGER), to_underlying(BEEP_SPEAKERS), 0x60, 0x01}; out->length = sizeof(beep); memcpy(out->data, beep, sizeof(beep)); } out->reaction = 1; outgoing.push(std::move(out)); } else RS232_Print("!! Cache empty; unable to queue beep request"); break; case 'P': if (auto out = cache.pop()) { out->is_unicast = true; out->peripheral_addr = peripheral.controller(); { const uint8_t play[] = {0x00, to_underlying(COMM_CTRL), to_underlying(COMM_v1), to_underlying(Ejection), to_underlying(CD_CHANGER), 0x01}; out->length = sizeof(play); memcpy(out->data, play, sizeof(play)); } out->reaction = CDChanger::reaction_t::r_Ejection; outgoing.push(std::move(out)); } break; #ifndef NDEBUG case 'g': AVCLAN_micToggle(); break; case 'p': RS232_Print("First play/pause begin ... "); AVCLAN_mediaFunction(MEDIA_PLAY_PAUSE); while (AVCLAN_isMediaFunctioning()) {} RS232_Print("end\nSecond play/pause begin ... "); AVCLAN_mediaFunction(MEDIA_PLAY_PAUSE); while (AVCLAN_isMediaFunctioning()) {} RS232_Print("end\n"); break; case 's': RS232_Print("Skip begin ... "); AVCLAN_mediaFunction(MEDIA_SKIP_FORWARD); while (AVCLAN_isMediaFunctioning()) {} RS232_Print("end\n"); break; case 'b': RS232_Print("Skip back begin ... "); AVCLAN_mediaFunction(MEDIA_SKIP_BACKWARD); while (AVCLAN_isMediaFunctioning()) {} RS232_Print("end\n"); break; case 'M': AVCLan_Measure(); break; #endif case 0x10: // Signals binary sequence incoming if (!readSeq && !readBinary) { readSeq = readBinary = true; seqIdx = 0; break; } else goto DEFAULT; // reading binary and this is a real data byte case 'U': // Send command RS232_Print("READ SEQUENCE (U)> \n"); lastPrintAllFrames = printAllFrames; printAllFrames = false; readSeq = true; seqIdx = hexDigit = 0; hexChars[0] = hexChars[1] = 0; seqIsUnicast = true; break; case 'B': // Send broadcast RS232_Print("READ SEQUENCE (B)> \n"); lastPrintAllFrames = printAllFrames; printAllFrames = false; readSeq = true; seqIdx = hexDigit = 0; hexChars[0] = hexChars[1] = 0; seqIsUnicast = false; break; case '\n': if (readSeq) { if (readBinary) { if (data_tmp[seqIdx] == 0x17) { if (auto out = cache.pop()) { if (out->parse(data_tmp, --seqIdx) == Frame::Error::Parse{0}) { out->reaction = 1; outgoing.push(std::move(out)); } } readSeq = readBinary = false; } else goto DEFAULT; // reading binary and this is a real data byte; // fall through to default } else { if (auto out = cache.pop()) { out->is_unicast = seqIsUnicast; out->peripheral_addr = seqIsUnicast ? peripheral.controller() : 0x1FF; out->length = seqIdx; memcpy(out->data, data_tmp, seqIdx); out->reaction = 1; outgoing.push(std::move(out)); } printAllFrames = lastPrintAllFrames; } break; } DEFAULT: default: if (readSeq) { if (readBinary) { data_tmp[seqIdx++] = readkey; } else { hexChars[hexDigit++] = readkey; if (hexDigit == 2) { char h, l; h = toupper(hexChars[0]); h += (h < ':') ? 0xd0 : 0xc9; // digit or letter l = toupper(hexChars[1]); l += (l < ':') ? 0xd0 : 0xc9; data_tmp[seqIdx++] = (h << 4) | l; hexDigit = hexChars[0] = hexChars[1] = 0; } if (echoCharacters) { RS232_Print("CURRENT SEQUENCE > "); for (uint8_t i = 0; i < seqIdx; i++) { RS232_PrintHex8(data_tmp[i]); RS232_SendByte(' '); } RS232_Print("\n"); } } } } // switch (readkey) } // if (RS232_hasChar()) } return 0; } namespace { void Setup() { board_init(); // clock + GPIO bring-up (target-specific) RS232_Init(); board_interruptsEnable(); } void print_help() { RS232_Print("AVCLAN Mockingboard v1\n"); RS232_Print("U - begin reading for unicast message\n" "B - begin reading for broadcast message\n" "m - Toggle mute for mockingboard bus activity\n" "v - Toggle verbose error logging\n" "l - Toggle message logging\n" "X/x - Turn binary logging ON or OFF, respectively\n" "k - Toggle character echo\n" "E - Beep\n" "P - Play\n" #ifndef NDEBUG "g - Toggle MIC_CONTROL high/low\n" "p - double MIC play/pause pulse\n" // Confirm pulse function and // refractory timing "s - MIC skip forward\n" "b - MIC skip backward\n" "M - Measure bit-timing (pulse-widths and periods)\n" #endif "? - Print this message\n"); } } // namespace