mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-07 01:13:18 +00:00
Substantially refactor ~everything to separate concerns and improve flow clarity
Highlights: - Remove all use of malloc and friends - Refactor message handling functions to work with references - Move core message read => triage => respond logic loop to main function - Improve message triage and support follow-up messages - Refactor hex byte parsing - Disable periodic interrupt when not playing
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
{
|
{
|
||||||
"name": "AVR toolchain",
|
"name": "AVR toolchain",
|
||||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||||
"image": "mcr.microsoft.com/devcontainers/base:alpine-3.18",
|
"image": "mcr.microsoft.com/devcontainers/base:alpine-3.22",
|
||||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
"features": {},
|
"features": {},
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
@@ -14,12 +14,9 @@
|
|||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"ms-vscode.cpptools",
|
|
||||||
"ms-vscode.cmake-tools",
|
|
||||||
"rockcat.avr-support",
|
|
||||||
"harikrishnan94.cxx-compiler-explorer",
|
|
||||||
"ms-vscode.cpptools-extension-pack",
|
"ms-vscode.cpptools-extension-pack",
|
||||||
"twxs.cmake"
|
"rockcat.avr-support",
|
||||||
|
"harikrishnan94.cxx-compiler-explorer"
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ set(AVR_UPLOADTOOL_BASE_OPTIONS ${AVR_UPLOADTOOL_BASE_OPTIONS} -U syscfg1:w:0x4:
|
|||||||
add_avr_executable(mockingboard
|
add_avr_executable(mockingboard
|
||||||
src/sniffer.c
|
src/sniffer.c
|
||||||
src/com232.c
|
src/com232.c
|
||||||
|
src/queue.c
|
||||||
src/avclandrv.c)
|
src/avclandrv.c)
|
||||||
|
|
||||||
target_link_options(mockingboard PUBLIC
|
target_link_options(mockingboard PUBLIC
|
||||||
|
|||||||
+200
-229
@@ -96,7 +96,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define VAR_DECLS
|
|
||||||
#include "avclandrv.h"
|
#include "avclandrv.h"
|
||||||
#include "com232.h"
|
#include "com232.h"
|
||||||
|
|
||||||
@@ -130,8 +129,6 @@ uint8_t *cd_Track;
|
|||||||
uint8_t *cd_Time_Min;
|
uint8_t *cd_Time_Min;
|
||||||
uint8_t *cd_Time_Sec;
|
uint8_t *cd_Time_Sec;
|
||||||
|
|
||||||
uint8_t answerReq;
|
|
||||||
|
|
||||||
cd_modes CD_Mode;
|
cd_modes CD_Mode;
|
||||||
|
|
||||||
#ifdef SOFTWARE_DEBUG
|
#ifdef SOFTWARE_DEBUG
|
||||||
@@ -148,17 +145,21 @@ const uint8_t list_functions_resp[] = {0x00, dev_COMM_CTRL, dev_COMM_v1,
|
|||||||
uint8_t ping_resp[] = {0x00, dev_COMM_CTRL, dev_COMM_v1, Ping_Resp, 0xFF, 0x00};
|
uint8_t ping_resp[] = {0x00, dev_COMM_CTRL, dev_COMM_v1, Ping_Resp, 0xFF, 0x00};
|
||||||
uint8_t function_change_resp[] = {0x00, dev_CD_CHANGER, dev_COMM_v1, 0xFF,
|
uint8_t function_change_resp[] = {0x00, dev_CD_CHANGER, dev_COMM_v1, 0xFF,
|
||||||
0x01};
|
0x01};
|
||||||
uint8_t cdstatus_resp[] = {dev_CD_CHANGER,
|
|
||||||
dev_STATUS,
|
#define STATUS_REPORT_DATA \
|
||||||
Status_Report,
|
{dev_CD_CHANGER, \
|
||||||
0x01,
|
dev_STATUS, \
|
||||||
cd_SEEKING_TRACK,
|
Status_Report, \
|
||||||
0x01,
|
0x01, \
|
||||||
0x00,
|
cd_SEEKING_TRACK, \
|
||||||
0xFF,
|
0x01, \
|
||||||
0x7F,
|
0x00, \
|
||||||
0x00,
|
0xFF, \
|
||||||
0x80};
|
0x7F, \
|
||||||
|
0x00, \
|
||||||
|
0x80}
|
||||||
|
|
||||||
|
uint8_t cdstatus_resp[] = STATUS_REPORT_DATA;
|
||||||
|
|
||||||
uint8_t cdinitreport_resp[] = {
|
uint8_t cdinitreport_resp[] = {
|
||||||
dev_CD_CHANGER, dev_STATUS, Initial_Report_Response, 0x01, 0x31, 0x10,
|
dev_CD_CHANGER, dev_STATUS, Initial_Report_Response, 0x01, 0x31, 0x10,
|
||||||
@@ -173,21 +174,19 @@ uint8_t cdloading_resp[] = {dev_CD_CHANGER,
|
|||||||
0x01,
|
0x01,
|
||||||
0x00,
|
0x00,
|
||||||
0x01,
|
0x01,
|
||||||
0x00};
|
0x02};
|
||||||
|
|
||||||
uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame);
|
|
||||||
void AVCLAN_updateCDStatus();
|
|
||||||
|
|
||||||
/* Disable serial and periodic interrupts during AVCLAN reads.
|
/* Disable serial and periodic interrupts during AVCLAN reads.
|
||||||
Not using cli() because AVCLAN reads depend on other interrupts. */
|
Not using cli() because AVCLAN reads depend on other interrupts. */
|
||||||
static inline void stopEvent() {
|
static inline void stopEvent() {
|
||||||
RTC.PITINTCTRL = 0x00; // PITINTCTRL allows resetting with full zero write.
|
cbi(RTC.PITINTCTRL, RTC_PI_bp);
|
||||||
cbi(USART0.CTRLA, USART_RXCIE_bp);
|
cbi(USART0.CTRLA, USART_RXCIE_bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-enable serial and periodic interrupts.
|
// Re-enable serial and periodic interrupts.
|
||||||
static inline void startEvent() {
|
static inline void startEvent() {
|
||||||
sbi(RTC.PITINTCTRL, RTC_PI_bp); // Reenable PIT interrupt
|
if (AVCLAN_isPlaying()) // Reenable PIT interrupt if currently playing
|
||||||
|
sbi(RTC.PITINTCTRL, RTC_PI_bp);
|
||||||
sbi(USART0.CTRLA, USART_RXCIE_bp);
|
sbi(USART0.CTRLA, USART_RXCIE_bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +207,50 @@ static inline void AVCLAN_setBusDriven() {
|
|||||||
}
|
}
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
// Returns true if device TX is muted on AVCLAN bus
|
||||||
|
static inline uint8_t AVCLAN_ismuted() {
|
||||||
|
return (((VPORTA_DIR & PIN4_bm) | (VPORTA_DIR & PIN0_bm)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mute device TX on AVCLAN bus
|
||||||
|
void AVCLAN_muteDevice(uint8_t mute) {
|
||||||
|
if (mute) {
|
||||||
|
// clang-format off
|
||||||
|
__asm__ __volatile__("cbi %[vporta_dir], 4; \n\t" // set as INPUT (output values ignored)
|
||||||
|
"cbi %[vportc_dir], 0; \n\t" // set as INPUT (output values ignored)
|
||||||
|
::
|
||||||
|
[vporta_dir] "I"(_SFR_IO_ADDR(VPORTA_DIR)),
|
||||||
|
[vportc_dir] "I"(_SFR_IO_ADDR(VPORTC_DIR)));
|
||||||
|
// clang-format on
|
||||||
|
} else {
|
||||||
|
// clang-format off
|
||||||
|
__asm__ __volatile__("sbi %[vporta_dir], 4; \n\t"
|
||||||
|
"sbi %[vportc_dir], 0; \n\t"
|
||||||
|
::
|
||||||
|
[vporta_dir] "I"(_SFR_IO_ADDR(VPORTA_DIR)),
|
||||||
|
[vportc_dir] "I"(_SFR_IO_ADDR(VPORTC_DIR)));
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets CD_mode to play and resets timer count (so that the next interrupt is in
|
||||||
|
// 1 sec)
|
||||||
|
void AVCLAN_startPlaying() {
|
||||||
|
CD_Mode = stPlay;
|
||||||
|
cli();
|
||||||
|
loop_until_bit_is_clear(RTC_PITSTATUS, RTC_CNTBUSY_bp);
|
||||||
|
RTC.CNT = 0;
|
||||||
|
sbi(RTC.PITINTCTRL, RTC_PI_bp);
|
||||||
|
sei();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets CD_mode to play and resets timer count (so that the next interrupt is in
|
||||||
|
// 1 sec)
|
||||||
|
void AVCLAN_stopPlaying() {
|
||||||
|
CD_Mode = stStop;
|
||||||
|
cbi(RTC.PITINTCTRL, RTC_PI_bp);
|
||||||
|
}
|
||||||
|
|
||||||
void AVCLAN_init() {
|
void AVCLAN_init() {
|
||||||
// Pull-ups are disabled by default
|
// Pull-ups are disabled by default
|
||||||
// Set pin 6 and 7 as input
|
// Set pin 6 and 7 as input
|
||||||
@@ -248,8 +291,6 @@ void AVCLAN_init() {
|
|||||||
|
|
||||||
AVCLAN_muteDevice(0); // unmute AVCLAN bus TX
|
AVCLAN_muteDevice(0); // unmute AVCLAN bus TX
|
||||||
|
|
||||||
answerReq = cm_Null;
|
|
||||||
|
|
||||||
cd_status.cd1 = 1;
|
cd_status.cd1 = 1;
|
||||||
cd_status.disc = 1;
|
cd_status.disc = 1;
|
||||||
cd_status.cd2 = cd_status.cd3 = cd_status.cd4 = cd_status.cd5 =
|
cd_status.cd2 = cd_status.cd3 = cd_status.cd4 = cd_status.cd5 =
|
||||||
@@ -282,46 +323,22 @@ void incBCD(uint8_t *data) {
|
|||||||
*data += 1;
|
*data += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Periodic interrupt with a 1 sec period
|
uint8_t AVCLAN_isPlaying() { return (CD_Mode == stPlay); }
|
||||||
ISR(RTC_PIT_vect) {
|
|
||||||
if (CD_Mode == stPlay) {
|
void AVCLAN_incrementTime() {
|
||||||
if (*cd_Time_Sec == 0x59) {
|
if (*cd_Time_Sec == 0x59) {
|
||||||
*cd_Time_Sec = 0;
|
*cd_Time_Sec = 0;
|
||||||
if (*cd_Time_Min == 0x99) {
|
if (*cd_Time_Min == 0x99) {
|
||||||
*cd_Time_Min = 0;
|
*cd_Time_Min = 0;
|
||||||
} else
|
|
||||||
incBCD(cd_Time_Min);
|
|
||||||
} else
|
} else
|
||||||
incBCD(cd_Time_Sec);
|
incBCD(cd_Time_Min);
|
||||||
answerReq = cm_CDStatus;
|
} else
|
||||||
}
|
incBCD(cd_Time_Sec);
|
||||||
RTC.PITINTFLAGS |= RTC_PI_bm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mute device TX on AVCLAN bus
|
void AVCLAN_setTime(uint8_t mins, uint8_t secs) {
|
||||||
void AVCLAN_muteDevice(uint8_t mute) {
|
*cd_Time_Min = mins;
|
||||||
if (mute) {
|
*cd_Time_Sec = secs;
|
||||||
// clang-format off
|
|
||||||
__asm__ __volatile__("cbi %[vporta_dir], 4; \n\t"
|
|
||||||
"cbi %[vportc_dir], 0; \n\t"
|
|
||||||
::
|
|
||||||
[vporta_dir] "I"(_SFR_IO_ADDR(VPORTA_DIR)),
|
|
||||||
[vportc_dir] "I"(_SFR_IO_ADDR(VPORTC_DIR)));
|
|
||||||
// clang-format on
|
|
||||||
} else {
|
|
||||||
// clang-format off
|
|
||||||
__asm__ __volatile__("sbi %[vporta_dir], 4; \n\t"
|
|
||||||
"sbi %[vportc_dir], 0; \n\t"
|
|
||||||
::
|
|
||||||
[vporta_dir] "I"(_SFR_IO_ADDR(VPORTA_DIR)),
|
|
||||||
[vportc_dir] "I"(_SFR_IO_ADDR(VPORTC_DIR)));
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if device TX is muted on AVCLAN bus
|
|
||||||
static inline uint8_t AVCLAN_ismuted() {
|
|
||||||
return (((VPORTA_DIR & PIN4_bm) | (VPORTA_DIR & PIN0_bm)) == 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set AVC bus to `val` (logical 1 or 0) for `period` ticks of TCB1
|
// Set AVC bus to `val` (logical 1 or 0) for `period` ticks of TCB1
|
||||||
@@ -539,7 +556,7 @@ uint8_t AVCLAN_readbyte(uint8_t *byte) {
|
|||||||
return (parity & 1);
|
return (parity & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t AVCLAN_readframe() {
|
uint8_t AVCLAN_readframe(AVCLAN_frame_t *frame) {
|
||||||
struct errtype {
|
struct errtype {
|
||||||
enum : uint8_t {
|
enum : uint8_t {
|
||||||
STARTBIT_TIMEOUT = 0x01,
|
STARTBIT_TIMEOUT = 0x01,
|
||||||
@@ -562,16 +579,6 @@ uint8_t AVCLAN_readframe() {
|
|||||||
|
|
||||||
stopEvent(); // disable timer1 interrupt
|
stopEvent(); // disable timer1 interrupt
|
||||||
|
|
||||||
uint8_t data[MAXMSGLEN] = {0};
|
|
||||||
AVCLAN_frame_t frame = {
|
|
||||||
.broadcast = BROADCAST,
|
|
||||||
.controller_addr = 0x000,
|
|
||||||
.peripheral_addr = 0x000,
|
|
||||||
.control = 0xF,
|
|
||||||
.length = 0,
|
|
||||||
.data = data,
|
|
||||||
};
|
|
||||||
|
|
||||||
uint8_t parity = 0;
|
uint8_t parity = 0;
|
||||||
uint8_t tmp = 0;
|
uint8_t tmp = 0;
|
||||||
|
|
||||||
@@ -589,44 +596,44 @@ uint8_t AVCLAN_readframe() {
|
|||||||
}
|
}
|
||||||
// Otherwise that was a start bit
|
// Otherwise that was a start bit
|
||||||
|
|
||||||
AVCLAN_readbits((uint8_t *)&frame.broadcast, 1);
|
AVCLAN_readbits(&frame->broadcast, 1);
|
||||||
|
|
||||||
parity = AVCLAN_readbits(&frame.controller_addr, 12);
|
parity = AVCLAN_readbits(&frame->controller_addr, 12);
|
||||||
AVCLAN_readbits(&tmp, 1);
|
AVCLAN_readbits(&tmp, 1);
|
||||||
if (parity != (tmp & 1)) {
|
if (parity != (tmp & 1)) {
|
||||||
err.errno = BAD_CONTROLLER_PARITY;
|
err.errno = BAD_CONTROLLER_PARITY;
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
err.read_val = frame.controller_addr;
|
err.read_val = frame->controller_addr;
|
||||||
err.parity = tmp & 1;
|
err.parity = tmp & 1;
|
||||||
}
|
}
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
parity = AVCLAN_readbits(&frame.peripheral_addr, 12);
|
parity = AVCLAN_readbits(&frame->peripheral_addr, 12);
|
||||||
AVCLAN_readbits(&tmp, 1);
|
AVCLAN_readbits(&tmp, 1);
|
||||||
if (parity != (tmp & 1)) {
|
if (parity != (tmp & 1)) {
|
||||||
err.errno = BAD_PERIPHERAL_PARITY;
|
err.errno = BAD_PERIPHERAL_PARITY;
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
err.read_val = frame.peripheral_addr;
|
err.read_val = frame->peripheral_addr;
|
||||||
err.parity = tmp & 1;
|
err.parity = tmp & 1;
|
||||||
}
|
}
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t shouldACK =
|
uint8_t shouldACK =
|
||||||
!AVCLAN_ismuted() && (frame.peripheral_addr == DEVICE_ADDR);
|
!AVCLAN_ismuted() && (frame->peripheral_addr == DEVICE_ADDR);
|
||||||
|
|
||||||
if (shouldACK)
|
if (shouldACK)
|
||||||
AVCLAN_sendbit_ACK();
|
AVCLAN_sendbit_ACK();
|
||||||
else
|
else
|
||||||
AVCLAN_readbits(&tmp, 1);
|
AVCLAN_readbits(&tmp, 1);
|
||||||
|
|
||||||
parity = AVCLAN_readbits(&frame.control, 4);
|
parity = AVCLAN_readbits(&frame->control, 4);
|
||||||
AVCLAN_readbits(&tmp, 1);
|
AVCLAN_readbits(&tmp, 1);
|
||||||
if (parity != (tmp & 1)) {
|
if (parity != (tmp & 1)) {
|
||||||
err.errno = BAD_CONTROL_PARITY;
|
err.errno = BAD_CONTROL_PARITY;
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
err.read_val = frame.control;
|
err.read_val = frame->control;
|
||||||
err.parity = tmp & 1;
|
err.parity = tmp & 1;
|
||||||
}
|
}
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
@@ -636,12 +643,12 @@ uint8_t AVCLAN_readframe() {
|
|||||||
AVCLAN_readbits(&tmp, 1);
|
AVCLAN_readbits(&tmp, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
parity = AVCLAN_readbyte(&frame.length);
|
parity = AVCLAN_readbyte(&frame->length);
|
||||||
AVCLAN_readbits(&tmp, 1);
|
AVCLAN_readbits(&tmp, 1);
|
||||||
if (parity != (tmp & 1)) {
|
if (parity != (tmp & 1)) {
|
||||||
err.errno = BAD_LENGTH_PARITY;
|
err.errno = BAD_LENGTH_PARITY;
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
err.read_val = frame.length;
|
err.read_val = frame->length;
|
||||||
err.parity = tmp & 1;
|
err.parity = tmp & 1;
|
||||||
}
|
}
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
@@ -651,19 +658,19 @@ uint8_t AVCLAN_readframe() {
|
|||||||
AVCLAN_readbits(&tmp, 1);
|
AVCLAN_readbits(&tmp, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame.length == 0 || frame.length > MAXMSGLEN) {
|
if (frame->length == 0 || frame->length > MAXMSGLEN) {
|
||||||
err.errno = BAD_LENGTH_RANGE;
|
err.errno = BAD_LENGTH_RANGE;
|
||||||
err.val = frame.length;
|
err.val = frame->length;
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t i = 0; i < frame.length; i++) {
|
for (uint8_t i = 0; i < frame->length; i++) {
|
||||||
parity = AVCLAN_readbyte(&frame.data[i]);
|
parity = AVCLAN_readbyte(&frame->data[i]);
|
||||||
AVCLAN_readbits(&tmp, 1);
|
AVCLAN_readbits(&tmp, 1);
|
||||||
if (parity != (tmp & 1)) {
|
if (parity != (tmp & 1)) {
|
||||||
err.errno = BAD_DATA_PARITY;
|
err.errno = BAD_DATA_PARITY;
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
err.read_val = frame.data[i];
|
err.read_val = frame->data[i];
|
||||||
err.parity = tmp & 1;
|
err.parity = tmp & 1;
|
||||||
}
|
}
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
@@ -704,7 +711,6 @@ uint8_t AVCLAN_readframe() {
|
|||||||
RS232_PrintHex4(err.parity);
|
RS232_PrintHex4(err.parity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RS232_Print("\n");
|
RS232_Print("\n");
|
||||||
} else {
|
} else {
|
||||||
startEvent();
|
startEvent();
|
||||||
@@ -713,12 +719,7 @@ uint8_t AVCLAN_readframe() {
|
|||||||
if (printAllFrames &&
|
if (printAllFrames &&
|
||||||
(!err.errno ||
|
(!err.errno ||
|
||||||
err.errno > STARTBIT_LENGTH)) // At least partially successful read
|
err.errno > STARTBIT_LENGTH)) // At least partially successful read
|
||||||
AVCLAN_printframe(&frame, printBinary);
|
AVCLAN_printframe(frame, printBinary);
|
||||||
|
|
||||||
if (!!err.errno && !AVCLAN_ismuted()) // Only handle if successful
|
|
||||||
AVCLAN_handleframe(&frame);
|
|
||||||
|
|
||||||
answerReq = cm_Null;
|
|
||||||
|
|
||||||
return err.errno;
|
return err.errno;
|
||||||
}
|
}
|
||||||
@@ -736,8 +737,10 @@ uint8_t AVCLAN_sendframe(const AVCLAN_frame_t *frame) {
|
|||||||
uint8_t val;
|
uint8_t val;
|
||||||
} err = {0};
|
} err = {0};
|
||||||
|
|
||||||
if ((err.errno = AVCLAN_ismuted()))
|
if (AVCLAN_ismuted()) {
|
||||||
|
err.errno = MUTED;
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
|
}
|
||||||
|
|
||||||
stopEvent();
|
stopEvent();
|
||||||
|
|
||||||
@@ -854,53 +857,20 @@ uint8_t AVCLAN_sendframe(const AVCLAN_frame_t *frame) {
|
|||||||
return err.errno;
|
return err.errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AVCLAN_frame_t *frameQueue[4];
|
response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *resp) {
|
||||||
|
response_t respond = r_Nothing;
|
||||||
|
|
||||||
static inline uint8_t qFull() {
|
if (AVCLAN_ismuted())
|
||||||
return ((qWrite - qRead) == sizeof(frameQueue));
|
return respond;
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint8_t qMask(uint8_t pos) {
|
|
||||||
return pos & (sizeof(frameQueue) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t qPush(const AVCLAN_frame_t *frame) {
|
|
||||||
if (qFull())
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
frameQueue[qMask(qWrite++)] = frame;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AVCLAN_frame_t *qPeek() {
|
|
||||||
if (qEmpty())
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return frameQueue[qMask(qRead)];
|
|
||||||
}
|
|
||||||
|
|
||||||
const AVCLAN_frame_t *qPop() {
|
|
||||||
if (qEmpty())
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return frameQueue[qMask(qRead++)];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) {
|
|
||||||
uint8_t respond = 0;
|
|
||||||
AVCLAN_frame_t *resp = malloc(sizeof(AVCLAN_frame_t));
|
|
||||||
if (!resp)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
resp->controller_addr = DEVICE_ADDR;
|
resp->controller_addr = DEVICE_ADDR;
|
||||||
resp->control = 0xF;
|
resp->control = 0xF;
|
||||||
|
|
||||||
uint8_t *data = frame->data;
|
uint8_t *data = in->data;
|
||||||
uint8_t from;
|
uint8_t from;
|
||||||
|
|
||||||
// BROADCAST
|
// BROADCAST
|
||||||
if (frame->broadcast == 0) {
|
if (in->broadcast == 0) {
|
||||||
// skip confirming peripheral_addr, because it will be 0xFFF or 0x1FF based
|
// skip confirming peripheral_addr, because it will be 0xFFF or 0x1FF based
|
||||||
// on all currently known examples
|
// on all currently known examples
|
||||||
switch (*data++ /* data[0] == "from" device */) {
|
switch (*data++ /* data[0] == "from" device */) {
|
||||||
@@ -927,8 +897,8 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) {
|
|||||||
LAN_RESPONSE:
|
LAN_RESPONSE:
|
||||||
resp->broadcast = UNICAST;
|
resp->broadcast = UNICAST;
|
||||||
resp->peripheral_addr = HU_ADDR;
|
resp->peripheral_addr = HU_ADDR;
|
||||||
resp->data = (uint8_t *)lancheck_resp;
|
memcpy(resp->data, lancheck_resp, sizeof(lancheck_resp));
|
||||||
respond = 1;
|
respond = r_Handled;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -939,23 +909,30 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) {
|
|||||||
if (*data++ /* data[1] == "to" device */ == dev_COMM_CTRL) {
|
if (*data++ /* data[1] == "to" device */ == dev_COMM_CTRL) {
|
||||||
switch (*data++ /* data[2] == device action */) {
|
switch (*data++ /* data[2] == device action */) {
|
||||||
case Current_Function:
|
case Current_Function:
|
||||||
CD_Mode =
|
if ((*data++ /* data[2] */ == dev_CD_CHANGER) &&
|
||||||
(*data++ /* data[2] */ == dev_CD_CHANGER) ? stPlay : stStop;
|
!AVCLAN_isPlaying()) {
|
||||||
|
cd_status.state = cd_SEEKING | cd_SEEKING_TRACK;
|
||||||
|
cd_status.flags2 = 0x80;
|
||||||
|
AVCLAN_startPlaying();
|
||||||
|
AVCLAN_generateStatus(resp);
|
||||||
|
respond = r_NormalizeState;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Ping_Req:
|
case Ping_Req:
|
||||||
resp->broadcast = UNICAST;
|
resp->broadcast = UNICAST;
|
||||||
resp->peripheral_addr = HU_ADDR;
|
resp->peripheral_addr = HU_ADDR;
|
||||||
resp->length = sizeof(ping_resp);
|
resp->length = sizeof(ping_resp);
|
||||||
ping_resp[4] = *data++ /* data[2] */;
|
ping_resp[4] = *data++ /* data[2] */;
|
||||||
resp->data = (uint8_t *)&ping_resp;
|
memcpy(resp->data, ping_resp, sizeof(ping_resp));
|
||||||
respond = 1;
|
respond = r_Handled;
|
||||||
break;
|
break;
|
||||||
case List_Functions_Req:
|
case List_Functions_Req:
|
||||||
resp->broadcast = UNICAST;
|
resp->broadcast = UNICAST;
|
||||||
resp->peripheral_addr = HU_ADDR;
|
resp->peripheral_addr = HU_ADDR;
|
||||||
resp->length = sizeof(list_functions_resp);
|
resp->length = sizeof(list_functions_resp);
|
||||||
resp->data = (uint8_t *)&list_functions_resp;
|
memcpy(resp->data, list_functions_resp,
|
||||||
respond = 1;
|
sizeof(list_functions_resp));
|
||||||
|
respond = r_Handled;
|
||||||
break;
|
break;
|
||||||
// case Restart_Lan:
|
// case Restart_Lan:
|
||||||
// break;
|
// break;
|
||||||
@@ -965,7 +942,7 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
} else if (frame->peripheral_addr == DEVICE_ADDR) { // unicast to CD changer
|
} else if (in->peripheral_addr == DEVICE_ADDR) { // unicast to CD changer
|
||||||
if (*data++ == 0) { // unicasts begin with a zero-byte
|
if (*data++ == 0) { // unicasts begin with a zero-byte
|
||||||
from = *data++; /* data[1] */
|
from = *data++; /* data[1] */
|
||||||
switch (from) {
|
switch (from) {
|
||||||
@@ -976,22 +953,21 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) {
|
|||||||
switch (*data++ /* data[3] == device action */) {
|
switch (*data++ /* data[3] == device action */) {
|
||||||
case Enable_Function_Req:
|
case Enable_Function_Req:
|
||||||
function_change_resp[3] = Enable_Function_Resp;
|
function_change_resp[3] = Enable_Function_Resp;
|
||||||
cd_status.state = cd_SEEKING | cd_PLAYBACK | cd_SEEKING_TRACK;
|
cd_status.state = cd_SEEKING | cd_SEEKING_TRACK;
|
||||||
cd_status.flags2 = 0x80;
|
cd_status.flags2 = 0xc0;
|
||||||
*cd_Time_Min = 0xff;
|
// *cd_Time_Min = 0xff;
|
||||||
*cd_Time_Sec = 0x7f;
|
// *cd_Time_Sec = 0x7f;
|
||||||
CD_Mode = stPlay;
|
AVCLAN_startPlaying();
|
||||||
// trigger regular status update after
|
respond = r_StartPlaying;
|
||||||
answerReq = cm_CDStatus;
|
|
||||||
goto FUNCTION_CHANGE_RESPONSE;
|
goto FUNCTION_CHANGE_RESPONSE;
|
||||||
case Disable_Function_Req:
|
case Disable_Function_Req:
|
||||||
|
AVCLAN_stopPlaying();
|
||||||
function_change_resp[3] = Disable_Function_Resp;
|
function_change_resp[3] = Disable_Function_Resp;
|
||||||
CD_Mode = stStop;
|
cd_status.state = cd_PLAYBACK | cd_SEEKING_TRACK;
|
||||||
cd_status.state = 0;
|
// *cd_Time_Min = 0x00;
|
||||||
*cd_Time_Min = 0x00;
|
// *cd_Time_Sec = 0x00;
|
||||||
*cd_Time_Sec = 0x00;
|
cd_status.flags2 = 0x80;
|
||||||
// trigger regular status update after
|
respond = r_StatusReport;
|
||||||
answerReq = cm_CDStatus;
|
|
||||||
goto FUNCTION_CHANGE_RESPONSE;
|
goto FUNCTION_CHANGE_RESPONSE;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -999,8 +975,8 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) {
|
|||||||
resp->broadcast = UNICAST;
|
resp->broadcast = UNICAST;
|
||||||
resp->peripheral_addr = HU_ADDR;
|
resp->peripheral_addr = HU_ADDR;
|
||||||
resp->length = sizeof(function_change_resp);
|
resp->length = sizeof(function_change_resp);
|
||||||
resp->data = (uint8_t *)&function_change_resp;
|
memcpy(resp->data, function_change_resp,
|
||||||
respond = 1;
|
sizeof(function_change_resp));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1012,41 +988,47 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) {
|
|||||||
switch (*data++ /* data[3] == device action */) {
|
switch (*data++ /* data[3] == device action */) {
|
||||||
case Initial_Report_Request:
|
case Initial_Report_Request:
|
||||||
resp->length = sizeof(cdinitreport_resp);
|
resp->length = sizeof(cdinitreport_resp);
|
||||||
resp->data = (uint8_t *)&cdinitreport_resp;
|
memcpy(resp->data, cdinitreport_resp,
|
||||||
|
sizeof(cdinitreport_resp));
|
||||||
|
resp->data[1] = from; // respond to device that requested
|
||||||
goto CMD_SW_RESPONSE;
|
goto CMD_SW_RESPONSE;
|
||||||
case Playback_Request:
|
case Playback_Request:
|
||||||
cdstatus_resp[1] = from; // respond to device that requested
|
resp->data[1] = from; // respond to device that requested
|
||||||
cdstatus_resp[2] = Playback_Report;
|
resp->data[2] = Playback_Report;
|
||||||
resp->length = sizeof(cdstatus_resp);
|
resp->length = sizeof(cdstatus_resp);
|
||||||
memcpy(&cdstatus_resp[3], &cd_status, sizeof(cd_status));
|
memcpy(&resp->data[3], &cd_status, sizeof(cd_status));
|
||||||
resp->data = (uint8_t *)&cdstatus_resp;
|
|
||||||
goto CMD_SW_RESPONSE;
|
goto CMD_SW_RESPONSE;
|
||||||
case Loading_Request2:
|
case Loading_Request2:
|
||||||
cdloading_resp[1] = from;
|
|
||||||
cdloading_resp[2] = Loading_Response2;
|
|
||||||
resp->length = sizeof(cdloading_resp);
|
resp->length = sizeof(cdloading_resp);
|
||||||
resp->data = (uint8_t *)&cdloading_resp;
|
memcpy(&resp->data, &cdloading_resp, sizeof(cdloading_resp));
|
||||||
|
resp->data[1] = from;
|
||||||
|
resp->data[2] = Loading_Response2;
|
||||||
goto CMD_SW_RESPONSE;
|
goto CMD_SW_RESPONSE;
|
||||||
case Track_Seek_Up:
|
case Track_Seek_Up:
|
||||||
cd_status.state = cd_SEEKING_TRACK;
|
cd_status.state = cd_SEEKING_TRACK;
|
||||||
(*cd_Track)++;
|
(*cd_Track)++;
|
||||||
*cd_Time_Min = 0xff;
|
*cd_Time_Min = 0xff;
|
||||||
*cd_Time_Sec = 0x7f;
|
*cd_Time_Sec = 0x7f;
|
||||||
|
cd_status.scan = 1;
|
||||||
cd_status.flags2 = 0xc0;
|
cd_status.flags2 = 0xc0;
|
||||||
goto CMD_SW_RESPONSE;
|
respond = r_TrackChange;
|
||||||
|
AVCLAN_generateStatus(resp);
|
||||||
|
break;
|
||||||
case Track_Seek_Down:
|
case Track_Seek_Down:
|
||||||
cd_status.state = cd_SEEKING_TRACK;
|
cd_status.state = cd_SEEKING_TRACK;
|
||||||
(*cd_Track)--;
|
(*cd_Track)--;
|
||||||
*cd_Time_Min = 0xff;
|
*cd_Time_Min = 0xff;
|
||||||
*cd_Time_Sec = 0x7f;
|
*cd_Time_Sec = 0x7f;
|
||||||
|
cd_status.scan = 1;
|
||||||
cd_status.flags2 = 0xc0;
|
cd_status.flags2 = 0xc0;
|
||||||
goto CMD_SW_RESPONSE;
|
respond = r_TrackChange;
|
||||||
|
AVCLAN_generateStatus(resp);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
CMD_SW_RESPONSE:
|
CMD_SW_RESPONSE:
|
||||||
resp->broadcast = UNICAST;
|
resp->broadcast = UNICAST;
|
||||||
resp->peripheral_addr = frame->controller_addr;
|
resp->peripheral_addr = HU_ADDR;
|
||||||
respond = 1;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1058,27 +1040,27 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) {
|
|||||||
switch (*data++ /* data[3] == device action */) {
|
switch (*data++ /* data[3] == device action */) {
|
||||||
case Initial_Report_Request:
|
case Initial_Report_Request:
|
||||||
resp->length = sizeof(cdinitreport_resp);
|
resp->length = sizeof(cdinitreport_resp);
|
||||||
resp->data = (uint8_t *)&cdinitreport_resp;
|
memcpy(resp->data, cdinitreport_resp,
|
||||||
|
sizeof(cdinitreport_resp));
|
||||||
|
resp->data[1] = from; // respond to device that requested
|
||||||
goto STATUS_RESPONSE;
|
goto STATUS_RESPONSE;
|
||||||
case Playback_Request:
|
case Playback_Request:
|
||||||
cdstatus_resp[1] = from; // respond to device that requested
|
resp->data[1] = from; // respond to device that requested
|
||||||
cdstatus_resp[2] = Playback_Report;
|
resp->data[2] = Playback_Report;
|
||||||
resp->length = sizeof(cdstatus_resp);
|
resp->length = sizeof(cdstatus_resp);
|
||||||
memcpy(&cdstatus_resp[3], &cd_status, sizeof(cd_status));
|
memcpy(&resp->data[3], &cd_status, sizeof(cd_status));
|
||||||
resp->data = (uint8_t *)&cdstatus_resp;
|
|
||||||
goto STATUS_RESPONSE;
|
goto STATUS_RESPONSE;
|
||||||
case Loading_Request2:
|
case Loading_Request2:
|
||||||
cdloading_resp[1] = from;
|
|
||||||
cdloading_resp[2] = Loading_Response2;
|
|
||||||
resp->length = sizeof(cdloading_resp);
|
resp->length = sizeof(cdloading_resp);
|
||||||
resp->data = (uint8_t *)&cdloading_resp;
|
memcpy(&resp->data, &cdloading_resp, sizeof(cdloading_resp));
|
||||||
|
resp->data[1] = from;
|
||||||
|
resp->data[2] = Loading_Response2;
|
||||||
goto STATUS_RESPONSE;
|
goto STATUS_RESPONSE;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
STATUS_RESPONSE:
|
STATUS_RESPONSE:
|
||||||
resp->broadcast = UNICAST;
|
resp->broadcast = UNICAST;
|
||||||
resp->peripheral_addr = frame->controller_addr;
|
resp->peripheral_addr = HU_ADDR;
|
||||||
respond = 1;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1089,40 +1071,17 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!respond) {
|
|
||||||
free(resp);
|
|
||||||
} else {
|
|
||||||
qPush(resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return respond;
|
return respond;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t AVCLAN_respond() {
|
uint8_t AVCLAN_tryrespond(const AVCLAN_frame_t *resp) {
|
||||||
uint8_t r = 0;
|
uint8_t r = 0;
|
||||||
if (!qEmpty()) {
|
for (uint8_t i = 0; i < MAX_SEND_ATTEMPTS; i++) {
|
||||||
const AVCLAN_frame_t *resp = qPeek();
|
r = AVCLAN_sendframe(resp);
|
||||||
for (uint8_t i = 0; i < MAX_SEND_ATTEMPTS; i++) {
|
if (!r) // Send succeeded
|
||||||
r = AVCLAN_sendframe(resp);
|
break;
|
||||||
if (!r) { // Send succeeded
|
|
||||||
resp = qPop();
|
|
||||||
free((AVCLAN_frame_t *)resp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (r) { // Sending failed all attempts; give up sending frame
|
|
||||||
resp = qPop();
|
|
||||||
free((AVCLAN_frame_t *)resp);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (answerReq) {
|
|
||||||
case cm_Null: break;
|
|
||||||
case cm_CDStatus: AVCLAN_updateCDStatus(); break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
answerReq = cm_Null;
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1180,10 +1139,10 @@ uint8_t AVCLAN_parseframe(const uint8_t *bytes, uint8_t len,
|
|||||||
} err = {0};
|
} err = {0};
|
||||||
|
|
||||||
if (len < sizeof(AVCLAN_frame_t)) {
|
if (len < sizeof(AVCLAN_frame_t)) {
|
||||||
err.erno = TOO_SHORT;
|
err.errno = TOO_SHORT;
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
uint8_t *last = bytes + len;
|
const uint8_t *last = bytes + len;
|
||||||
|
|
||||||
frame->broadcast = *bytes++;
|
frame->broadcast = *bytes++;
|
||||||
frame->controller_addr = *(uint16_t *)bytes++;
|
frame->controller_addr = *(uint16_t *)bytes++;
|
||||||
@@ -1218,30 +1177,42 @@ uint8_t AVCLAN_parseframe(const uint8_t *bytes, uint8_t len,
|
|||||||
return err.errno;
|
return err.errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVCLAN_updateCDStatus() {
|
// Only used for regularly scheduled periodic updates
|
||||||
if (CD_Mode) {
|
AVCLAN_frame_t *AVCLAN_getStatusFrame() {
|
||||||
if (answerReq == cm_CDStatus) {
|
static uint8_t status_data[] = STATUS_REPORT_DATA;
|
||||||
cdstatus_resp[2] = Status_Report;
|
static AVCLAN_frame_t status = {.broadcast = BROADCAST,
|
||||||
memcpy(&cdstatus_resp[3], &cd_status, sizeof(cd_status));
|
.controller_addr = DEVICE_ADDR,
|
||||||
|
.peripheral_addr = 0x1FF,
|
||||||
|
.control = 0xF,
|
||||||
|
.length = sizeof(status_data),
|
||||||
|
.data = status_data};
|
||||||
|
|
||||||
AVCLAN_frame_t status = {.broadcast = BROADCAST,
|
return &status;
|
||||||
.controller_addr = DEVICE_ADDR,
|
}
|
||||||
.peripheral_addr = 0x1FF,
|
|
||||||
.control = 0xF,
|
|
||||||
.length = sizeof(cdstatus_resp),
|
|
||||||
.data = (uint8_t *)&cdstatus_resp};
|
|
||||||
|
|
||||||
AVCLAN_sendframe(&status);
|
// Used for changed status messages
|
||||||
}
|
void AVCLAN_generateStatus(AVCLAN_frame_t *status) {
|
||||||
|
*status = (AVCLAN_frame_t){
|
||||||
|
.broadcast = BROADCAST,
|
||||||
|
.controller_addr = DEVICE_ADDR,
|
||||||
|
.peripheral_addr = 0x1FF,
|
||||||
|
.control = 0xF,
|
||||||
|
.length = sizeof(cdstatus_resp),
|
||||||
|
.data = status->data, // don't overwrite data pointer
|
||||||
|
};
|
||||||
|
status->data[0] = dev_CD_CHANGER;
|
||||||
|
status->data[1] = dev_STATUS;
|
||||||
|
status->data[2] = Status_Report;
|
||||||
|
memcpy(&status->data[3], &cd_status, sizeof(cd_status));
|
||||||
|
}
|
||||||
|
|
||||||
if (cd_status.state != cd_PLAYBACK) {
|
void AVCLAN_normalizeState() {
|
||||||
cd_status.state = cd_PLAYBACK;
|
// if (cd_status.state != cd_PLAYBACK) {
|
||||||
cd_status.flags2 = 0x80;
|
cd_status.state = cd_PLAYBACK;
|
||||||
*cd_Time_Min = 0x00;
|
cd_status.disk_scan = 0;
|
||||||
*cd_Time_Sec = 0x00;
|
cd_status.scan = 0;
|
||||||
answerReq = cm_CDStatus;
|
cd_status.flags2 = 0x80;
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SOFTWARE_DEBUG
|
#ifdef SOFTWARE_DEBUG
|
||||||
|
|||||||
+27
-20
@@ -75,6 +75,8 @@ typedef enum {
|
|||||||
Lancheck_Scan_Resp = 0x1a,
|
Lancheck_Scan_Resp = 0x1a,
|
||||||
Lancheck_Req = 0x0c,
|
Lancheck_Req = 0x0c,
|
||||||
Lancheck_Resp = 0x1c,
|
Lancheck_Resp = 0x1c,
|
||||||
|
// Lancheck_UNK_Req = 0x0d,
|
||||||
|
// Lancheck_UNK_Resp = 0x1d,
|
||||||
Ping_Req = 0x20,
|
Ping_Req = 0x20,
|
||||||
Ping_Resp = 0x30,
|
Ping_Resp = 0x30,
|
||||||
|
|
||||||
@@ -169,6 +171,16 @@ typedef struct AVCLAN_CD_Status {
|
|||||||
|
|
||||||
typedef enum { stStop = 0, stPlay = 1 } cd_modes;
|
typedef enum { stStop = 0, stPlay = 1 } cd_modes;
|
||||||
|
|
||||||
|
typedef enum : uint8_t {
|
||||||
|
r_Nothing = 0x00,
|
||||||
|
r_Handled, // No follow-up needed
|
||||||
|
r_StatusReport = 0x02, // Needs follow-up status report
|
||||||
|
r_NormalizeState, // cd_status needs normalized and resent
|
||||||
|
r_StartPlaying, // started playing; send current status and then
|
||||||
|
// normalize
|
||||||
|
r_TrackChange, // Time needs reset
|
||||||
|
} response_t;
|
||||||
|
|
||||||
typedef enum MSG_TYPE { BROADCAST = 0, UNICAST = 1 } MSG_TYPE_t;
|
typedef enum MSG_TYPE { BROADCAST = 0, UNICAST = 1 } MSG_TYPE_t;
|
||||||
|
|
||||||
typedef struct AVCLAN_frame_struct {
|
typedef struct AVCLAN_frame_struct {
|
||||||
@@ -180,33 +192,28 @@ typedef struct AVCLAN_frame_struct {
|
|||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
} AVCLAN_frame_t;
|
} AVCLAN_frame_t;
|
||||||
|
|
||||||
|
typedef struct RFrame_struct {
|
||||||
|
response_t r;
|
||||||
|
AVCLAN_frame_t *frame;
|
||||||
|
} RFrame_t;
|
||||||
|
|
||||||
void AVCLAN_init();
|
void AVCLAN_init();
|
||||||
void AVCLAN_muteDevice(uint8_t mute);
|
void AVCLAN_muteDevice(uint8_t mute);
|
||||||
|
|
||||||
uint8_t AVCLAN_readframe();
|
uint8_t AVCLAN_readframe(AVCLAN_frame_t *frame);
|
||||||
|
response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out);
|
||||||
uint8_t AVCLAN_sendframe(const AVCLAN_frame_t *frame);
|
uint8_t AVCLAN_sendframe(const AVCLAN_frame_t *frame);
|
||||||
|
uint8_t AVCLAN_tryrespond(const AVCLAN_frame_t *frame);
|
||||||
// To allow inlining qEmpty and AVCLAN_responseNeeded
|
|
||||||
#ifndef VAR_DECLS
|
|
||||||
#define _DECL extern
|
|
||||||
#define _INIT(x)
|
|
||||||
#else
|
|
||||||
#define _DECL
|
|
||||||
#define _INIT(x) = x
|
|
||||||
#endif
|
|
||||||
_DECL uint8_t answerReq _INIT(0);
|
|
||||||
_DECL uint8_t qWrite _INIT(0);
|
|
||||||
_DECL uint8_t qRead _INIT(0);
|
|
||||||
extern cd_modes CD_Mode;
|
|
||||||
|
|
||||||
inline uint8_t qEmpty() { return (qWrite == qRead); }
|
|
||||||
inline uint8_t AVCLAN_responseNeeded() { return (answerReq != 0) || !qEmpty(); }
|
|
||||||
|
|
||||||
uint8_t AVCLAN_respond();
|
|
||||||
|
|
||||||
void AVCLAN_printframe(const AVCLAN_frame_t *frame, uint8_t binary);
|
void AVCLAN_printframe(const AVCLAN_frame_t *frame, uint8_t binary);
|
||||||
uint8_t AVCLAN_parseframe(const uint8_t *bytes, uint8_t len,
|
uint8_t AVCLAN_parseframe(const uint8_t *bytes, uint8_t len,
|
||||||
AVCLAN_frame_t *frame);
|
AVCLAN_frame_t *frame);
|
||||||
|
AVCLAN_frame_t *AVCLAN_getStatusFrame();
|
||||||
|
void AVCLAN_generateStatus(AVCLAN_frame_t *status);
|
||||||
|
|
||||||
|
uint8_t AVCLAN_isPlaying();
|
||||||
|
void AVCLAN_incrementTime();
|
||||||
|
void AVCLAN_setTime(uint8_t mins, uint8_t secs);
|
||||||
|
void AVCLAN_normalizeState();
|
||||||
|
|
||||||
#ifdef SOFTWARE_DEBUG
|
#ifdef SOFTWARE_DEBUG
|
||||||
void AVCLan_Measure();
|
void AVCLan_Measure();
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "avclandrv.h"
|
||||||
|
#include "queue.h"
|
||||||
|
|
||||||
|
void constructQueue(Queue_t *q, void *buf, uint8_t size, uint8_t len,
|
||||||
|
uint8_t constructFull) {
|
||||||
|
q->read = 0;
|
||||||
|
q->size = len;
|
||||||
|
if (!!constructFull) {
|
||||||
|
q->write = len;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < len; ++i) {
|
||||||
|
q->buf[i] = buf;
|
||||||
|
buf = (char *)buf + size;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
q->write = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void constructEmptyQueue(Queue_t *q, uint8_t size, uint8_t len) {
|
||||||
|
constructQueue(q, NULL, size, len, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t isEmpty(const Queue_t *q) { return (q->write == q->read); }
|
||||||
|
|
||||||
|
static inline uint8_t isFull(const Queue_t *q) {
|
||||||
|
return ((q->write - q->read) == q->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t qMask(const Queue_t *q, uint8_t pos) {
|
||||||
|
return pos & (q->size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t pushQueue(Queue_t *q, void *x) {
|
||||||
|
if (isFull(q))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
q->buf[qMask(q, q->write++)] = x;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *peekQueue(const Queue_t *q) {
|
||||||
|
if (isEmpty(q))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return q->buf[qMask(q, q->read)];
|
||||||
|
}
|
||||||
|
|
||||||
|
void *popQueue(Queue_t *q) {
|
||||||
|
if (isEmpty(q))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return q->buf[qMask(q, q->read++)];
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct Queue_struct {
|
||||||
|
uint8_t write;
|
||||||
|
uint8_t read;
|
||||||
|
uint8_t size;
|
||||||
|
void *buf[];
|
||||||
|
} Queue_t;
|
||||||
|
|
||||||
|
void constructQueue(Queue_t *q, void *buf, uint8_t size, uint8_t len,
|
||||||
|
uint8_t constructFull);
|
||||||
|
void constructEmptyQueue(Queue_t *q, uint8_t size, uint8_t len);
|
||||||
|
uint8_t isEmpty(const Queue_t *q);
|
||||||
|
uint8_t pushQueue(Queue_t *q, void *x);
|
||||||
|
const void *peekQueue(const Queue_t *q);
|
||||||
|
void *popQueue(Queue_t *q);
|
||||||
+251
-101
@@ -24,12 +24,15 @@
|
|||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
#include <avr/sfr_defs.h>
|
#include <avr/sfr_defs.h>
|
||||||
#include <avr/xmega.h>
|
#include <avr/xmega.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "avclandrv.h"
|
#include "avclandrv.h"
|
||||||
#include "com232.h"
|
#include "com232.h"
|
||||||
|
#include "queue.h"
|
||||||
|
|
||||||
uint8_t echoCharacters;
|
uint8_t echoCharacters;
|
||||||
uint8_t readBinary;
|
uint8_t readBinary;
|
||||||
@@ -38,23 +41,61 @@ uint8_t readkey;
|
|||||||
|
|
||||||
const char *const offon[] = {"OFF", "ON"};
|
const char *const offon[] = {"OFF", "ON"};
|
||||||
|
|
||||||
|
#define CACHE_SIZE 16
|
||||||
|
|
||||||
|
AVCLAN_frame_t frames[CACHE_SIZE];
|
||||||
|
RFrame_t responses[CACHE_SIZE];
|
||||||
|
uint8_t framesdata[CACHE_SIZE][MAXMSGLEN];
|
||||||
|
|
||||||
|
Queue_t cache, rcache, incoming, outgoing;
|
||||||
|
|
||||||
|
volatile uint8_t enqueueStatus = 0;
|
||||||
|
|
||||||
void Setup();
|
void Setup();
|
||||||
void general_GPIO_init();
|
void general_GPIO_init();
|
||||||
void print_help();
|
void print_help();
|
||||||
|
|
||||||
|
static uint8_t return_resp(RFrame_t *resp) {
|
||||||
|
uint8_t err;
|
||||||
|
AVCLAN_frame_t *out = resp->frame;
|
||||||
|
if ((out >= frames) && (out < &frames[CACHE_SIZE]))
|
||||||
|
// only return cache-owned frames (e.g. not status, etc)
|
||||||
|
err = pushQueue(&cache, out);
|
||||||
|
err = pushQueue(&rcache, resp);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t push_or_return_resp(RFrame_t *resp) {
|
||||||
|
uint8_t err = pushQueue(&outgoing, resp) && return_resp(resp);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
uint8_t readSeq = 0;
|
uint8_t readSeq = 0;
|
||||||
uint8_t s_len = 0;
|
uint8_t hexChars[2];
|
||||||
uint8_t s_dig = 0;
|
uint8_t hexDigit = 0; // current digit being written to hexChars
|
||||||
uint8_t s_c[2];
|
|
||||||
uint8_t i;
|
MSG_TYPE_t seqBroadcast = BROADCAST;
|
||||||
|
uint8_t lastPrintAllFrames = 1;
|
||||||
|
|
||||||
uint8_t data_tmp[MAXMSGLEN];
|
uint8_t data_tmp[MAXMSGLEN];
|
||||||
AVCLAN_frame_t msg = {
|
uint8_t seqLen = 0; // current length written to data_tmp
|
||||||
.broadcast = UNICAST,
|
|
||||||
.controller_addr = DEVICE_ADDR,
|
uint8_t err = 0;
|
||||||
.control = 0xF,
|
|
||||||
.data = data_tmp,
|
AVCLAN_frame_t *msg, *out;
|
||||||
};
|
RFrame_t *resp;
|
||||||
|
AVCLAN_frame_t *status = AVCLAN_getStatusFrame();
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < CACHE_SIZE; ++i) {
|
||||||
|
frames[i].data = framesdata[i];
|
||||||
|
frames[i].control = 0x0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructQueue(&cache, frames, sizeof(AVCLAN_frame_t), CACHE_SIZE, 1);
|
||||||
|
constructEmptyQueue(&incoming, sizeof(AVCLAN_frame_t), CACHE_SIZE);
|
||||||
|
constructQueue(&rcache, responses, sizeof(RFrame_t), CACHE_SIZE, 1);
|
||||||
|
constructEmptyQueue(&outgoing, sizeof(RFrame_t), CACHE_SIZE);
|
||||||
|
|
||||||
Setup();
|
Setup();
|
||||||
print_help();
|
print_help();
|
||||||
@@ -62,16 +103,89 @@ int main() {
|
|||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
if (!BUS_IS_IDLE) {
|
if (!BUS_IS_IDLE) {
|
||||||
AVCLAN_readframe();
|
msg = (AVCLAN_frame_t *)(&cache);
|
||||||
} else if (AVCLAN_responseNeeded()) {
|
if (!msg) {
|
||||||
AVCLAN_respond();
|
RS232_Print("!! Dropping an incoming message; cache is empty !!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = AVCLAN_readframe(msg);
|
||||||
|
if (!err)
|
||||||
|
err = pushQueue(&incoming, msg) && pushQueue(&cache, msg);
|
||||||
|
} else if (!isEmpty(&incoming)) {
|
||||||
|
out = (AVCLAN_frame_t *)popQueue(&cache);
|
||||||
|
if (!out) {
|
||||||
|
RS232_Print("!! Unable to respond; cache is empty !!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = (AVCLAN_frame_t *)popQueue(
|
||||||
|
&incoming); // prior !isempty(incoming) guarantees success
|
||||||
|
response_t respond = AVCLAN_handleframe(msg, out);
|
||||||
|
|
||||||
|
if (respond) {
|
||||||
|
resp = (RFrame_t *)popQueue(&rcache);
|
||||||
|
if (resp) {
|
||||||
|
*resp = (RFrame_t){.r = respond, .frame = out};
|
||||||
|
push_or_return_resp(resp);
|
||||||
|
} else
|
||||||
|
pushQueue(&cache, out);
|
||||||
|
} else // no response needed; return to circulation
|
||||||
|
pushQueue(&cache, out);
|
||||||
|
|
||||||
|
pushQueue(&cache, msg);
|
||||||
|
} else if (!isEmpty(&outgoing)) {
|
||||||
|
resp = (RFrame_t *)popQueue(
|
||||||
|
&outgoing); // prior !isempty(outgoing) guarantees success
|
||||||
|
out = resp->frame;
|
||||||
|
err = AVCLAN_sendframe(out);
|
||||||
|
if (err) {
|
||||||
|
RS232_Print("!! Failed to send frame; error code ");
|
||||||
|
RS232_PrintHex(err);
|
||||||
|
RS232_Print(" !!\n");
|
||||||
|
return_resp(resp);
|
||||||
|
} else {
|
||||||
|
// Re-use successful resp for sequence
|
||||||
|
switch (resp->r) {
|
||||||
|
case r_TrackChange: AVCLAN_setTime(0x00, 0x00); // fallthrough
|
||||||
|
case r_NormalizeState:
|
||||||
|
AVCLAN_normalizeState(out);
|
||||||
|
resp->r = r_Handled;
|
||||||
|
push_or_return_resp(resp);
|
||||||
|
break;
|
||||||
|
case r_StartPlaying:
|
||||||
|
AVCLAN_generateStatus(out);
|
||||||
|
resp->r = r_NormalizeState;
|
||||||
|
push_or_return_resp(resp);
|
||||||
|
break;
|
||||||
|
case r_StatusReport:
|
||||||
|
AVCLAN_generateStatus(out);
|
||||||
|
resp->r = r_Handled;
|
||||||
|
push_or_return_resp(resp);
|
||||||
|
break;
|
||||||
|
case r_Nothing: __builtin_unreachable();
|
||||||
|
case r_Handled: return_resp(resp); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (enqueueStatus) {
|
||||||
|
AVCLAN_generateStatus(status);
|
||||||
|
resp = (RFrame_t *)popQueue(&rcache);
|
||||||
|
if (resp) {
|
||||||
|
*resp = (RFrame_t){.r = r_Handled, .frame = status};
|
||||||
|
err = pushQueue(&outgoing, resp);
|
||||||
|
if (err) {
|
||||||
|
RS232_Print("Outgoing queue full; unable to send status update");
|
||||||
|
pushQueue(&rcache, resp);
|
||||||
|
} else
|
||||||
|
enqueueStatus = 0; // Only clear if successful
|
||||||
|
}
|
||||||
|
// no further error handling needed; status isn't part of the cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key handler
|
// Key handler
|
||||||
if (RS232_RxCharEnd) {
|
if (RS232_RxCharEnd) {
|
||||||
cli();
|
cli();
|
||||||
readkey = RS232_RxCharBuffer[RS232_RxCharBegin];
|
readkey = RS232_RxCharBuffer[RS232_RxCharBegin++];
|
||||||
RS232_RxCharBegin++;
|
|
||||||
if (RS232_RxCharBegin == RS232_RxCharEnd) // if buffer is consumed
|
if (RS232_RxCharBegin == RS232_RxCharEnd) // if buffer is consumed
|
||||||
RS232_RxCharBegin = RS232_RxCharEnd = 0; // reset buffer
|
RS232_RxCharBegin = RS232_RxCharEnd = 0; // reset buffer
|
||||||
sei();
|
sei();
|
||||||
@@ -84,6 +198,9 @@ int main() {
|
|||||||
RS232_Print("\n");
|
RS232_Print("\n");
|
||||||
break;
|
break;
|
||||||
case 'X':
|
case 'X':
|
||||||
|
// X/x isn't a single toggle interface because this is used
|
||||||
|
// programmatically and is simpler than reading the toggle
|
||||||
|
// state
|
||||||
printBinary = 1;
|
printBinary = 1;
|
||||||
RS232_Print("Binary: ");
|
RS232_Print("Binary: ");
|
||||||
RS232_Print(offon[1]);
|
RS232_Print(offon[1]);
|
||||||
@@ -95,30 +212,6 @@ int main() {
|
|||||||
RS232_Print(offon[0]);
|
RS232_Print(offon[0]);
|
||||||
RS232_Print("\n");
|
RS232_Print("\n");
|
||||||
break;
|
break;
|
||||||
case 'S': // Read sequence
|
|
||||||
printAllFrames = 0;
|
|
||||||
RS232_Print("READ SEQUENCE > \n");
|
|
||||||
readSeq = 1;
|
|
||||||
s_len = 0;
|
|
||||||
s_dig = 0;
|
|
||||||
s_c[0] = s_c[1] = 0;
|
|
||||||
break;
|
|
||||||
case 'W': // Send command
|
|
||||||
printAllFrames = 1;
|
|
||||||
readSeq = 0;
|
|
||||||
msg.broadcast = UNICAST;
|
|
||||||
msg.length = s_len;
|
|
||||||
AVCLAN_sendframe(&msg);
|
|
||||||
break;
|
|
||||||
case 'Q': // Send broadcast
|
|
||||||
printAllFrames = 1;
|
|
||||||
readSeq = 0;
|
|
||||||
msg.broadcast = BROADCAST;
|
|
||||||
msg.peripheral_addr = 0x1FF;
|
|
||||||
msg.length = s_len;
|
|
||||||
AVCLAN_sendframe(&msg);
|
|
||||||
msg.peripheral_addr = HU_ADDR;
|
|
||||||
break;
|
|
||||||
case 'l': // Print received messages
|
case 'l': // Print received messages
|
||||||
printAllFrames ^= 1;
|
printAllFrames ^= 1;
|
||||||
RS232_Print("Logging: ");
|
RS232_Print("Logging: ");
|
||||||
@@ -138,31 +231,43 @@ int main() {
|
|||||||
RS232_Print(offon[muteBus]);
|
RS232_Print(offon[muteBus]);
|
||||||
RS232_Print("\n");
|
RS232_Print("\n");
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'E': // Beep
|
||||||
case 'B': // Beep
|
out = (AVCLAN_frame_t *)popQueue(&cache);
|
||||||
{
|
if (out) {
|
||||||
const uint8_t beep[] = {0x00, dev_CD_CHANGER, dev_BEEP_SPEAKERS, 0x60,
|
resp = (RFrame_t *)popQueue(&rcache);
|
||||||
0x01};
|
if (resp) {
|
||||||
memcpy(data_tmp, beep, sizeof(beep));
|
out->broadcast = UNICAST;
|
||||||
msg.length = sizeof(beep);
|
out->controller_addr = DEVICE_ADDR;
|
||||||
}
|
out->peripheral_addr = HU_ADDR;
|
||||||
msg.broadcast = UNICAST;
|
{
|
||||||
msg.controller_addr = DEVICE_ADDR;
|
const uint8_t beep[] = {0x00, dev_CD_CHANGER, dev_BEEP_SPEAKERS,
|
||||||
msg.peripheral_addr = HU_ADDR;
|
0x60, 0x01};
|
||||||
AVCLAN_sendframe(&msg);
|
out->length = sizeof(beep);
|
||||||
break;
|
memcpy(out->data, beep, sizeof(beep));
|
||||||
case 'p':
|
}
|
||||||
CD_Mode = stPlay;
|
*resp = (RFrame_t){.r = r_Handled, .frame = out};
|
||||||
{
|
push_or_return_resp(resp);
|
||||||
const uint8_t play[] = {0x00, dev_COMM_CTRL, dev_COMM_v1,
|
}
|
||||||
Insertion, dev_CD_CHANGER, 0x01};
|
}
|
||||||
memcpy(data_tmp, play, sizeof(play));
|
break;
|
||||||
msg.length = sizeof(play);
|
case 'P':
|
||||||
|
out = (AVCLAN_frame_t *)popQueue(&cache);
|
||||||
|
if (out) {
|
||||||
|
resp = (RFrame_t *)popQueue(&rcache);
|
||||||
|
if (resp) {
|
||||||
|
out->broadcast = UNICAST;
|
||||||
|
out->controller_addr = DEVICE_ADDR;
|
||||||
|
out->peripheral_addr = HU_ADDR;
|
||||||
|
{
|
||||||
|
const uint8_t play[] = {0x00, dev_COMM_CTRL, dev_COMM_v1,
|
||||||
|
Insertion, dev_CD_CHANGER, 0x01};
|
||||||
|
out->length = sizeof(play);
|
||||||
|
memcpy(out->data, play, sizeof(play));
|
||||||
|
}
|
||||||
|
*resp = (RFrame_t){.r = r_Handled, .frame = out};
|
||||||
|
push_or_return_resp(resp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
msg.broadcast = UNICAST;
|
|
||||||
msg.controller_addr = DEVICE_ADDR;
|
|
||||||
msg.peripheral_addr = HU_ADDR;
|
|
||||||
AVCLAN_sendframe(&msg);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#ifdef SOFTWARE_DEBUG
|
#ifdef SOFTWARE_DEBUG
|
||||||
@@ -171,52 +276,90 @@ int main() {
|
|||||||
|
|
||||||
case 0x10: // Signals binary sequence incoming
|
case 0x10: // Signals binary sequence incoming
|
||||||
if (!readSeq && !readBinary) {
|
if (!readSeq && !readBinary) {
|
||||||
readSeq = 1;
|
readSeq = readBinary = 1;
|
||||||
readBinary = 1;
|
seqLen = 0;
|
||||||
s_len = 0;
|
|
||||||
break;
|
break;
|
||||||
} // else (readSeq || readBinary); fall through to default
|
} // otherwise we're reading binary and that's a data byte
|
||||||
|
case 'U': // Send command
|
||||||
|
RS232_Print("READ SEQUENCE (U)> \n");
|
||||||
|
lastPrintAllFrames = printAllFrames;
|
||||||
|
printAllFrames = 0;
|
||||||
|
readSeq = 1;
|
||||||
|
seqLen = hexDigit = 0;
|
||||||
|
hexChars[0] = hexChars[1] = 0;
|
||||||
|
seqBroadcast = UNICAST;
|
||||||
|
break;
|
||||||
|
case 'B': // Send broadcast
|
||||||
|
RS232_Print("READ SEQUENCE (B)> \n");
|
||||||
|
lastPrintAllFrames = printAllFrames;
|
||||||
|
printAllFrames = 0;
|
||||||
|
readSeq = 1;
|
||||||
|
seqLen = hexDigit = 0;
|
||||||
|
hexChars[0] = hexChars[1] = 0;
|
||||||
|
seqBroadcast = BROADCAST;
|
||||||
|
break;
|
||||||
case '\n':
|
case '\n':
|
||||||
if (readSeq && readBinary && data_tmp[s_len] == 0x17) {
|
if (readSeq) {
|
||||||
{
|
if (readBinary) {
|
||||||
uint8_t tmp[MAXMSGLEN];
|
if (data_tmp[seqLen] == 0x17) {
|
||||||
AVCLAN_frame_t frame = {.data = tmp};
|
out = (AVCLAN_frame_t *)popQueue(&cache);
|
||||||
err = AVCLAN_parseframe(data_tmp, --s_len, &frame);
|
if (out) {
|
||||||
if (!err) {
|
err = AVCLAN_parseframe(data_tmp, --seqLen, out);
|
||||||
AVCLAN_sendframe(frame);
|
if (!err) {
|
||||||
readSeq = 0;
|
resp = (RFrame_t *)popQueue(&rcache);
|
||||||
readBinary = 0;
|
if (resp) {
|
||||||
|
*resp = (RFrame_t){.r = r_Handled, .frame = out};
|
||||||
|
push_or_return_resp(resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readSeq = readBinary = 0;
|
||||||
|
} else
|
||||||
|
goto DEFAULT; // reading binary and this is a real data byte;
|
||||||
|
// fall through to default
|
||||||
|
} else {
|
||||||
|
out = (AVCLAN_frame_t *)popQueue(&cache);
|
||||||
|
if (out) {
|
||||||
|
resp = (RFrame_t *)popQueue(&rcache);
|
||||||
|
if (resp) {
|
||||||
|
out->broadcast = seqBroadcast;
|
||||||
|
out->controller_addr = DEVICE_ADDR;
|
||||||
|
switch (seqBroadcast) {
|
||||||
|
case UNICAST: out->peripheral_addr = HU_ADDR; break;
|
||||||
|
case BROADCAST: out->peripheral_addr = 0x1FF; break;
|
||||||
|
}
|
||||||
|
out->length = seqLen;
|
||||||
|
memcpy(out->data, data_tmp, seqLen);
|
||||||
|
*resp = (RFrame_t){.r = r_Handled, .frame = out};
|
||||||
|
push_or_return_resp(resp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
printAllFrames = lastPrintAllFrames;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} // else (readSeq || readBinary || most recent char != 0x17);
|
}
|
||||||
// fall through to default
|
DEFAULT:
|
||||||
default:
|
default:
|
||||||
if (readSeq) {
|
if (readSeq) {
|
||||||
if (readBinary) {
|
if (readBinary) {
|
||||||
data_tmp[s_len++] = readkey;
|
data_tmp[seqLen++] = readkey;
|
||||||
} else {
|
} else {
|
||||||
s_c[s_dig] = readkey;
|
hexChars[hexDigit++] = readkey;
|
||||||
|
|
||||||
s_dig++;
|
if (hexDigit == 2) {
|
||||||
if (s_dig == 2) {
|
char h, l;
|
||||||
if (s_c[0] < ':')
|
h = toupper(hexChars[0]);
|
||||||
s_c[0] -= 48;
|
h += (h < ':') ? 0xd0 : 0xc9; // digit or letter
|
||||||
else
|
|
||||||
s_c[0] -= 55;
|
l = toupper(hexChars[1]);
|
||||||
data_tmp[s_len] = 16 * s_c[0];
|
l += (l < ':') ? 0xd0 : 0xc9;
|
||||||
if (s_c[1] < ':')
|
|
||||||
s_c[1] -= 48;
|
data_tmp[seqLen++] = (h << 4) | l;
|
||||||
else
|
hexDigit = hexChars[0] = hexChars[1] = 0;
|
||||||
s_c[1] -= 55;
|
|
||||||
data_tmp[s_len] += s_c[1];
|
|
||||||
s_len++;
|
|
||||||
s_dig = 0;
|
|
||||||
s_c[0] = s_c[1] = 0;
|
|
||||||
}
|
}
|
||||||
if (echoCharacters) {
|
if (echoCharacters) {
|
||||||
RS232_Print("CURRENT SEQUENCE > ");
|
RS232_Print("CURRENT SEQUENCE > ");
|
||||||
for (i = 0; i < s_len; i++) {
|
for (uint8_t i = 0; i < seqLen; i++) {
|
||||||
RS232_PrintHex8(data_tmp[i]);
|
RS232_PrintHex8(data_tmp[i]);
|
||||||
RS232_SendByte(' ');
|
RS232_SendByte(' ');
|
||||||
}
|
}
|
||||||
@@ -279,14 +422,14 @@ void general_GPIO_init() {
|
|||||||
|
|
||||||
void print_help() {
|
void print_help() {
|
||||||
RS232_Print("AVCLAN Mockingboard v1\n");
|
RS232_Print("AVCLAN Mockingboard v1\n");
|
||||||
RS232_Print("S - read sequence\n"
|
RS232_Print("W - begin reading for unicast message\n"
|
||||||
"W - send command\n"
|
"Q - begin reading for broadcast message\n"
|
||||||
"Q - send broadcast\n"
|
|
||||||
"m - Toggle mute for mockingboard bus activity\n"
|
"m - Toggle mute for mockingboard bus activity\n"
|
||||||
"l - Toggle message logging\n"
|
"l - Toggle message logging\n"
|
||||||
"k - Toggle character echo\n"
|
"k - Toggle character echo\n"
|
||||||
"X/x - Turn binary ON or OFF, respectively\n"
|
"X/x - Turn binary printing ON or OFF, respectively\n"
|
||||||
"B - Beep\n"
|
"E - Beep\n"
|
||||||
|
"P - Play\n"
|
||||||
"v - Toggle verbose logging\n"
|
"v - Toggle verbose logging\n"
|
||||||
#ifdef SOFTWARE_DEBUG
|
#ifdef SOFTWARE_DEBUG
|
||||||
"M - Measure bit-timing (pulse-widths and periods)\n"
|
"M - Measure bit-timing (pulse-widths and periods)\n"
|
||||||
@@ -297,3 +440,10 @@ void print_help() {
|
|||||||
#endif
|
#endif
|
||||||
"? - Print this message\n");
|
"? - Print this message\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Periodic interrupt with a 1 sec period; only enabled when playing
|
||||||
|
ISR(RTC_PIT_vect) {
|
||||||
|
AVCLAN_incrementTime();
|
||||||
|
enqueueStatus = 1;
|
||||||
|
RTC.PITINTFLAGS = RTC_PI_bm;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user