diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d24dd68..8587b77 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,7 +3,7 @@ { "name": "AVR toolchain", // 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": {}, // Use 'forwardPorts' to make a list of ports inside the container available locally. @@ -14,12 +14,9 @@ "customizations": { "vscode": { "extensions": [ - "ms-vscode.cpptools", - "ms-vscode.cmake-tools", - "rockcat.avr-support", - "harikrishnan94.cxx-compiler-explorer", "ms-vscode.cpptools-extension-pack", - "twxs.cmake" + "rockcat.avr-support", + "harikrishnan94.cxx-compiler-explorer" ], "settings": { "editor.formatOnSave": true, diff --git a/CMakeLists.txt b/CMakeLists.txt index 64db775..75f48fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,6 +107,7 @@ set(AVR_UPLOADTOOL_BASE_OPTIONS ${AVR_UPLOADTOOL_BASE_OPTIONS} -U syscfg1:w:0x4: add_avr_executable(mockingboard src/sniffer.c src/com232.c + src/queue.c src/avclandrv.c) target_link_options(mockingboard PUBLIC diff --git a/src/avclandrv.c b/src/avclandrv.c index 5cfc4a7..9cc89cd 100644 --- a/src/avclandrv.c +++ b/src/avclandrv.c @@ -96,7 +96,6 @@ #include #include -#define VAR_DECLS #include "avclandrv.h" #include "com232.h" @@ -130,8 +129,6 @@ uint8_t *cd_Track; uint8_t *cd_Time_Min; uint8_t *cd_Time_Sec; -uint8_t answerReq; - cd_modes CD_Mode; #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 function_change_resp[] = {0x00, dev_CD_CHANGER, dev_COMM_v1, 0xFF, 0x01}; -uint8_t cdstatus_resp[] = {dev_CD_CHANGER, - dev_STATUS, - Status_Report, - 0x01, - cd_SEEKING_TRACK, - 0x01, - 0x00, - 0xFF, - 0x7F, - 0x00, - 0x80}; + +#define STATUS_REPORT_DATA \ + {dev_CD_CHANGER, \ + dev_STATUS, \ + Status_Report, \ + 0x01, \ + cd_SEEKING_TRACK, \ + 0x01, \ + 0x00, \ + 0xFF, \ + 0x7F, \ + 0x00, \ + 0x80} + +uint8_t cdstatus_resp[] = STATUS_REPORT_DATA; uint8_t cdinitreport_resp[] = { dev_CD_CHANGER, dev_STATUS, Initial_Report_Response, 0x01, 0x31, 0x10, @@ -173,21 +174,19 @@ uint8_t cdloading_resp[] = {dev_CD_CHANGER, 0x01, 0x00, 0x01, - 0x00}; - -uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame); -void AVCLAN_updateCDStatus(); + 0x02}; /* Disable serial and periodic interrupts during AVCLAN reads. Not using cli() because AVCLAN reads depend on other interrupts. */ 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); } // Re-enable serial and periodic interrupts. 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); } @@ -208,6 +207,50 @@ static inline void AVCLAN_setBusDriven() { } // 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() { // Pull-ups are disabled by default // Set pin 6 and 7 as input @@ -248,8 +291,6 @@ void AVCLAN_init() { AVCLAN_muteDevice(0); // unmute AVCLAN bus TX - answerReq = cm_Null; - cd_status.cd1 = 1; cd_status.disc = 1; cd_status.cd2 = cd_status.cd3 = cd_status.cd4 = cd_status.cd5 = @@ -282,46 +323,22 @@ void incBCD(uint8_t *data) { *data += 1; } -// Periodic interrupt with a 1 sec period -ISR(RTC_PIT_vect) { - if (CD_Mode == stPlay) { - if (*cd_Time_Sec == 0x59) { - *cd_Time_Sec = 0; - if (*cd_Time_Min == 0x99) { - *cd_Time_Min = 0; - } else - incBCD(cd_Time_Min); +uint8_t AVCLAN_isPlaying() { return (CD_Mode == stPlay); } + +void AVCLAN_incrementTime() { + if (*cd_Time_Sec == 0x59) { + *cd_Time_Sec = 0; + if (*cd_Time_Min == 0x99) { + *cd_Time_Min = 0; } else - incBCD(cd_Time_Sec); - answerReq = cm_CDStatus; - } - RTC.PITINTFLAGS |= RTC_PI_bm; + incBCD(cd_Time_Min); + } else + incBCD(cd_Time_Sec); } -// 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" - "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); +void AVCLAN_setTime(uint8_t mins, uint8_t secs) { + *cd_Time_Min = mins; + *cd_Time_Sec = secs; } // 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); } -uint8_t AVCLAN_readframe() { +uint8_t AVCLAN_readframe(AVCLAN_frame_t *frame) { struct errtype { enum : uint8_t { STARTBIT_TIMEOUT = 0x01, @@ -562,16 +579,6 @@ uint8_t AVCLAN_readframe() { 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 tmp = 0; @@ -589,44 +596,44 @@ uint8_t AVCLAN_readframe() { } // 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); if (parity != (tmp & 1)) { err.errno = BAD_CONTROLLER_PARITY; if (verbose) { - err.read_val = frame.controller_addr; + err.read_val = frame->controller_addr; err.parity = tmp & 1; } goto handle_err; } - parity = AVCLAN_readbits(&frame.peripheral_addr, 12); + parity = AVCLAN_readbits(&frame->peripheral_addr, 12); AVCLAN_readbits(&tmp, 1); if (parity != (tmp & 1)) { err.errno = BAD_PERIPHERAL_PARITY; if (verbose) { - err.read_val = frame.peripheral_addr; + err.read_val = frame->peripheral_addr; err.parity = tmp & 1; } goto handle_err; } uint8_t shouldACK = - !AVCLAN_ismuted() && (frame.peripheral_addr == DEVICE_ADDR); + !AVCLAN_ismuted() && (frame->peripheral_addr == DEVICE_ADDR); if (shouldACK) AVCLAN_sendbit_ACK(); else AVCLAN_readbits(&tmp, 1); - parity = AVCLAN_readbits(&frame.control, 4); + parity = AVCLAN_readbits(&frame->control, 4); AVCLAN_readbits(&tmp, 1); if (parity != (tmp & 1)) { err.errno = BAD_CONTROL_PARITY; if (verbose) { - err.read_val = frame.control; + err.read_val = frame->control; err.parity = tmp & 1; } goto handle_err; @@ -636,12 +643,12 @@ uint8_t AVCLAN_readframe() { AVCLAN_readbits(&tmp, 1); } - parity = AVCLAN_readbyte(&frame.length); + parity = AVCLAN_readbyte(&frame->length); AVCLAN_readbits(&tmp, 1); if (parity != (tmp & 1)) { err.errno = BAD_LENGTH_PARITY; if (verbose) { - err.read_val = frame.length; + err.read_val = frame->length; err.parity = tmp & 1; } goto handle_err; @@ -651,19 +658,19 @@ uint8_t AVCLAN_readframe() { AVCLAN_readbits(&tmp, 1); } - if (frame.length == 0 || frame.length > MAXMSGLEN) { + if (frame->length == 0 || frame->length > MAXMSGLEN) { err.errno = BAD_LENGTH_RANGE; - err.val = frame.length; + err.val = frame->length; goto handle_err; } - for (uint8_t i = 0; i < frame.length; i++) { - parity = AVCLAN_readbyte(&frame.data[i]); + for (uint8_t i = 0; i < frame->length; i++) { + parity = AVCLAN_readbyte(&frame->data[i]); AVCLAN_readbits(&tmp, 1); if (parity != (tmp & 1)) { err.errno = BAD_DATA_PARITY; if (verbose) { - err.read_val = frame.data[i]; + err.read_val = frame->data[i]; err.parity = tmp & 1; } goto handle_err; @@ -704,7 +711,6 @@ uint8_t AVCLAN_readframe() { RS232_PrintHex4(err.parity); } } - RS232_Print("\n"); } else { startEvent(); @@ -713,12 +719,7 @@ uint8_t AVCLAN_readframe() { if (printAllFrames && (!err.errno || err.errno > STARTBIT_LENGTH)) // At least partially successful read - AVCLAN_printframe(&frame, printBinary); - - if (!!err.errno && !AVCLAN_ismuted()) // Only handle if successful - AVCLAN_handleframe(&frame); - - answerReq = cm_Null; + AVCLAN_printframe(frame, printBinary); return err.errno; } @@ -736,8 +737,10 @@ uint8_t AVCLAN_sendframe(const AVCLAN_frame_t *frame) { uint8_t val; } err = {0}; - if ((err.errno = AVCLAN_ismuted())) + if (AVCLAN_ismuted()) { + err.errno = MUTED; goto handle_err; + } stopEvent(); @@ -854,53 +857,20 @@ uint8_t AVCLAN_sendframe(const AVCLAN_frame_t *frame) { 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() { - return ((qWrite - qRead) == sizeof(frameQueue)); -} - -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; + if (AVCLAN_ismuted()) + return respond; resp->controller_addr = DEVICE_ADDR; resp->control = 0xF; - uint8_t *data = frame->data; + uint8_t *data = in->data; uint8_t from; // BROADCAST - if (frame->broadcast == 0) { + if (in->broadcast == 0) { // skip confirming peripheral_addr, because it will be 0xFFF or 0x1FF based // on all currently known examples switch (*data++ /* data[0] == "from" device */) { @@ -927,8 +897,8 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) { LAN_RESPONSE: resp->broadcast = UNICAST; resp->peripheral_addr = HU_ADDR; - resp->data = (uint8_t *)lancheck_resp; - respond = 1; + memcpy(resp->data, lancheck_resp, sizeof(lancheck_resp)); + respond = r_Handled; } break; default: @@ -939,23 +909,30 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) { if (*data++ /* data[1] == "to" device */ == dev_COMM_CTRL) { switch (*data++ /* data[2] == device action */) { case Current_Function: - CD_Mode = - (*data++ /* data[2] */ == dev_CD_CHANGER) ? stPlay : stStop; + if ((*data++ /* data[2] */ == dev_CD_CHANGER) && + !AVCLAN_isPlaying()) { + cd_status.state = cd_SEEKING | cd_SEEKING_TRACK; + cd_status.flags2 = 0x80; + AVCLAN_startPlaying(); + AVCLAN_generateStatus(resp); + respond = r_NormalizeState; + } break; case Ping_Req: resp->broadcast = UNICAST; resp->peripheral_addr = HU_ADDR; resp->length = sizeof(ping_resp); ping_resp[4] = *data++ /* data[2] */; - resp->data = (uint8_t *)&ping_resp; - respond = 1; + memcpy(resp->data, ping_resp, sizeof(ping_resp)); + respond = r_Handled; break; case List_Functions_Req: resp->broadcast = UNICAST; resp->peripheral_addr = HU_ADDR; resp->length = sizeof(list_functions_resp); - resp->data = (uint8_t *)&list_functions_resp; - respond = 1; + memcpy(resp->data, list_functions_resp, + sizeof(list_functions_resp)); + respond = r_Handled; break; // case Restart_Lan: // break; @@ -965,7 +942,7 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) { break; 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 from = *data++; /* data[1] */ switch (from) { @@ -976,22 +953,21 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) { switch (*data++ /* data[3] == device action */) { case Enable_Function_Req: function_change_resp[3] = Enable_Function_Resp; - cd_status.state = cd_SEEKING | cd_PLAYBACK | cd_SEEKING_TRACK; - cd_status.flags2 = 0x80; - *cd_Time_Min = 0xff; - *cd_Time_Sec = 0x7f; - CD_Mode = stPlay; - // trigger regular status update after - answerReq = cm_CDStatus; + cd_status.state = cd_SEEKING | cd_SEEKING_TRACK; + cd_status.flags2 = 0xc0; + // *cd_Time_Min = 0xff; + // *cd_Time_Sec = 0x7f; + AVCLAN_startPlaying(); + respond = r_StartPlaying; goto FUNCTION_CHANGE_RESPONSE; case Disable_Function_Req: + AVCLAN_stopPlaying(); function_change_resp[3] = Disable_Function_Resp; - CD_Mode = stStop; - cd_status.state = 0; - *cd_Time_Min = 0x00; - *cd_Time_Sec = 0x00; - // trigger regular status update after - answerReq = cm_CDStatus; + cd_status.state = cd_PLAYBACK | cd_SEEKING_TRACK; + // *cd_Time_Min = 0x00; + // *cd_Time_Sec = 0x00; + cd_status.flags2 = 0x80; + respond = r_StatusReport; goto FUNCTION_CHANGE_RESPONSE; default: break; @@ -999,8 +975,8 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) { resp->broadcast = UNICAST; resp->peripheral_addr = HU_ADDR; resp->length = sizeof(function_change_resp); - resp->data = (uint8_t *)&function_change_resp; - respond = 1; + memcpy(resp->data, function_change_resp, + sizeof(function_change_resp)); } break; default: @@ -1012,41 +988,47 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) { switch (*data++ /* data[3] == device action */) { case Initial_Report_Request: 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; case Playback_Request: - cdstatus_resp[1] = from; // respond to device that requested - cdstatus_resp[2] = Playback_Report; + resp->data[1] = from; // respond to device that requested + resp->data[2] = Playback_Report; resp->length = sizeof(cdstatus_resp); - memcpy(&cdstatus_resp[3], &cd_status, sizeof(cd_status)); - resp->data = (uint8_t *)&cdstatus_resp; + memcpy(&resp->data[3], &cd_status, sizeof(cd_status)); goto CMD_SW_RESPONSE; case Loading_Request2: - cdloading_resp[1] = from; - cdloading_resp[2] = Loading_Response2; 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; case Track_Seek_Up: cd_status.state = cd_SEEKING_TRACK; (*cd_Track)++; *cd_Time_Min = 0xff; *cd_Time_Sec = 0x7f; + cd_status.scan = 1; cd_status.flags2 = 0xc0; - goto CMD_SW_RESPONSE; + respond = r_TrackChange; + AVCLAN_generateStatus(resp); + break; case Track_Seek_Down: cd_status.state = cd_SEEKING_TRACK; (*cd_Track)--; *cd_Time_Min = 0xff; *cd_Time_Sec = 0x7f; + cd_status.scan = 1; cd_status.flags2 = 0xc0; - goto CMD_SW_RESPONSE; + respond = r_TrackChange; + AVCLAN_generateStatus(resp); + break; default: break; CMD_SW_RESPONSE: resp->broadcast = UNICAST; - resp->peripheral_addr = frame->controller_addr; - respond = 1; + resp->peripheral_addr = HU_ADDR; } break; default: @@ -1058,27 +1040,27 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) { switch (*data++ /* data[3] == device action */) { case Initial_Report_Request: 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; case Playback_Request: - cdstatus_resp[1] = from; // respond to device that requested - cdstatus_resp[2] = Playback_Report; + resp->data[1] = from; // respond to device that requested + resp->data[2] = Playback_Report; resp->length = sizeof(cdstatus_resp); - memcpy(&cdstatus_resp[3], &cd_status, sizeof(cd_status)); - resp->data = (uint8_t *)&cdstatus_resp; + memcpy(&resp->data[3], &cd_status, sizeof(cd_status)); goto STATUS_RESPONSE; case Loading_Request2: - cdloading_resp[1] = from; - cdloading_resp[2] = Loading_Response2; 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; default: break; STATUS_RESPONSE: resp->broadcast = UNICAST; - resp->peripheral_addr = frame->controller_addr; - respond = 1; + resp->peripheral_addr = HU_ADDR; } break; default: @@ -1089,40 +1071,17 @@ uint8_t AVCLAN_handleframe(const AVCLAN_frame_t *frame) { } } - if (!respond) { - free(resp); - } else { - qPush(resp); - } - return respond; } -uint8_t AVCLAN_respond() { +uint8_t AVCLAN_tryrespond(const AVCLAN_frame_t *resp) { uint8_t r = 0; - if (!qEmpty()) { - const AVCLAN_frame_t *resp = qPeek(); - for (uint8_t i = 0; i < MAX_SEND_ATTEMPTS; i++) { - r = AVCLAN_sendframe(resp); - 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: - } + for (uint8_t i = 0; i < MAX_SEND_ATTEMPTS; i++) { + r = AVCLAN_sendframe(resp); + if (!r) // Send succeeded + break; } - answerReq = cm_Null; return r; } @@ -1180,10 +1139,10 @@ uint8_t AVCLAN_parseframe(const uint8_t *bytes, uint8_t len, } err = {0}; if (len < sizeof(AVCLAN_frame_t)) { - err.erno = TOO_SHORT; + err.errno = TOO_SHORT; goto handle_err; } - uint8_t *last = bytes + len; + const uint8_t *last = bytes + len; frame->broadcast = *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; } -void AVCLAN_updateCDStatus() { - if (CD_Mode) { - if (answerReq == cm_CDStatus) { - cdstatus_resp[2] = Status_Report; - memcpy(&cdstatus_resp[3], &cd_status, sizeof(cd_status)); +// Only used for regularly scheduled periodic updates +AVCLAN_frame_t *AVCLAN_getStatusFrame() { + static uint8_t status_data[] = STATUS_REPORT_DATA; + static AVCLAN_frame_t status = {.broadcast = BROADCAST, + .controller_addr = DEVICE_ADDR, + .peripheral_addr = 0x1FF, + .control = 0xF, + .length = sizeof(status_data), + .data = status_data}; - AVCLAN_frame_t status = {.broadcast = BROADCAST, - .controller_addr = DEVICE_ADDR, - .peripheral_addr = 0x1FF, - .control = 0xF, - .length = sizeof(cdstatus_resp), - .data = (uint8_t *)&cdstatus_resp}; + return &status; +} - 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) { - cd_status.state = cd_PLAYBACK; - cd_status.flags2 = 0x80; - *cd_Time_Min = 0x00; - *cd_Time_Sec = 0x00; - answerReq = cm_CDStatus; - } - } +void AVCLAN_normalizeState() { + // if (cd_status.state != cd_PLAYBACK) { + cd_status.state = cd_PLAYBACK; + cd_status.disk_scan = 0; + cd_status.scan = 0; + cd_status.flags2 = 0x80; + // } } #ifdef SOFTWARE_DEBUG diff --git a/src/avclandrv.h b/src/avclandrv.h index b309589..084542a 100644 --- a/src/avclandrv.h +++ b/src/avclandrv.h @@ -75,6 +75,8 @@ typedef enum { Lancheck_Scan_Resp = 0x1a, Lancheck_Req = 0x0c, Lancheck_Resp = 0x1c, + // Lancheck_UNK_Req = 0x0d, + // Lancheck_UNK_Resp = 0x1d, Ping_Req = 0x20, Ping_Resp = 0x30, @@ -169,6 +171,16 @@ typedef struct AVCLAN_CD_Status { 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 struct AVCLAN_frame_struct { @@ -180,33 +192,28 @@ typedef struct AVCLAN_frame_struct { uint8_t *data; } AVCLAN_frame_t; +typedef struct RFrame_struct { + response_t r; + AVCLAN_frame_t *frame; +} RFrame_t; + void AVCLAN_init(); 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); - -// 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(); - +uint8_t AVCLAN_tryrespond(const AVCLAN_frame_t *frame); void AVCLAN_printframe(const AVCLAN_frame_t *frame, uint8_t binary); uint8_t AVCLAN_parseframe(const uint8_t *bytes, uint8_t len, 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 void AVCLan_Measure(); diff --git a/src/queue.c b/src/queue.c new file mode 100644 index 0000000..520ddd8 --- /dev/null +++ b/src/queue.c @@ -0,0 +1,58 @@ +#include +#include + +#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++)]; +} diff --git a/src/queue.h b/src/queue.h new file mode 100644 index 0000000..250bd91 --- /dev/null +++ b/src/queue.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +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); diff --git a/src/sniffer.c b/src/sniffer.c index ba67b6a..2b4a7aa 100644 --- a/src/sniffer.c +++ b/src/sniffer.c @@ -24,12 +24,15 @@ #include #include #include +#include +#include #include #include #include #include "avclandrv.h" #include "com232.h" +#include "queue.h" uint8_t echoCharacters; uint8_t readBinary; @@ -38,23 +41,61 @@ uint8_t readkey; 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 general_GPIO_init(); 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() { uint8_t readSeq = 0; - uint8_t s_len = 0; - uint8_t s_dig = 0; - uint8_t s_c[2]; - uint8_t i; + uint8_t hexChars[2]; + uint8_t hexDigit = 0; // current digit being written to hexChars + + MSG_TYPE_t seqBroadcast = BROADCAST; + uint8_t lastPrintAllFrames = 1; + uint8_t data_tmp[MAXMSGLEN]; - AVCLAN_frame_t msg = { - .broadcast = UNICAST, - .controller_addr = DEVICE_ADDR, - .control = 0xF, - .data = data_tmp, - }; + uint8_t seqLen = 0; // current length written to data_tmp + + uint8_t err = 0; + + 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(); print_help(); @@ -62,16 +103,89 @@ int main() { while (1) { if (!BUS_IS_IDLE) { - AVCLAN_readframe(); - } else if (AVCLAN_responseNeeded()) { - AVCLAN_respond(); + msg = (AVCLAN_frame_t *)(&cache); + if (!msg) { + 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 if (RS232_RxCharEnd) { cli(); - readkey = RS232_RxCharBuffer[RS232_RxCharBegin]; - RS232_RxCharBegin++; + readkey = RS232_RxCharBuffer[RS232_RxCharBegin++]; if (RS232_RxCharBegin == RS232_RxCharEnd) // if buffer is consumed RS232_RxCharBegin = RS232_RxCharEnd = 0; // reset buffer sei(); @@ -84,6 +198,9 @@ int main() { RS232_Print("\n"); break; 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; RS232_Print("Binary: "); RS232_Print(offon[1]); @@ -95,30 +212,6 @@ int main() { RS232_Print(offon[0]); RS232_Print("\n"); 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 printAllFrames ^= 1; RS232_Print("Logging: "); @@ -138,31 +231,43 @@ int main() { RS232_Print(offon[muteBus]); RS232_Print("\n"); break; - case 'b': - case 'B': // Beep - { - const uint8_t beep[] = {0x00, dev_CD_CHANGER, dev_BEEP_SPEAKERS, 0x60, - 0x01}; - memcpy(data_tmp, beep, sizeof(beep)); - msg.length = sizeof(beep); - } - msg.broadcast = UNICAST; - msg.controller_addr = DEVICE_ADDR; - msg.peripheral_addr = HU_ADDR; - AVCLAN_sendframe(&msg); - break; - case 'p': - CD_Mode = stPlay; - { - const uint8_t play[] = {0x00, dev_COMM_CTRL, dev_COMM_v1, - Insertion, dev_CD_CHANGER, 0x01}; - memcpy(data_tmp, play, sizeof(play)); - msg.length = sizeof(play); + case 'E': // Beep + 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 beep[] = {0x00, dev_CD_CHANGER, dev_BEEP_SPEAKERS, + 0x60, 0x01}; + out->length = sizeof(beep); + memcpy(out->data, beep, sizeof(beep)); + } + *resp = (RFrame_t){.r = r_Handled, .frame = out}; + push_or_return_resp(resp); + } + } + break; + 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; #ifdef SOFTWARE_DEBUG @@ -171,52 +276,90 @@ int main() { case 0x10: // Signals binary sequence incoming if (!readSeq && !readBinary) { - readSeq = 1; - readBinary = 1; - s_len = 0; + readSeq = readBinary = 1; + seqLen = 0; 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': - if (readSeq && readBinary && data_tmp[s_len] == 0x17) { - { - uint8_t tmp[MAXMSGLEN]; - AVCLAN_frame_t frame = {.data = tmp}; - err = AVCLAN_parseframe(data_tmp, --s_len, &frame); - if (!err) { - AVCLAN_sendframe(frame); - readSeq = 0; - readBinary = 0; + if (readSeq) { + if (readBinary) { + if (data_tmp[seqLen] == 0x17) { + out = (AVCLAN_frame_t *)popQueue(&cache); + if (out) { + err = AVCLAN_parseframe(data_tmp, --seqLen, out); + if (!err) { + resp = (RFrame_t *)popQueue(&rcache); + 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; - } // else (readSeq || readBinary || most recent char != 0x17); - // fall through to default + } + DEFAULT: default: if (readSeq) { if (readBinary) { - data_tmp[s_len++] = readkey; + data_tmp[seqLen++] = readkey; } else { - s_c[s_dig] = readkey; + hexChars[hexDigit++] = readkey; - s_dig++; - if (s_dig == 2) { - if (s_c[0] < ':') - s_c[0] -= 48; - else - s_c[0] -= 55; - data_tmp[s_len] = 16 * s_c[0]; - if (s_c[1] < ':') - s_c[1] -= 48; - else - s_c[1] -= 55; - data_tmp[s_len] += s_c[1]; - s_len++; - s_dig = 0; - s_c[0] = s_c[1] = 0; + 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[seqLen++] = (h << 4) | l; + hexDigit = hexChars[0] = hexChars[1] = 0; } if (echoCharacters) { 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_SendByte(' '); } @@ -279,14 +422,14 @@ void general_GPIO_init() { void print_help() { RS232_Print("AVCLAN Mockingboard v1\n"); - RS232_Print("S - read sequence\n" - "W - send command\n" - "Q - send broadcast\n" + RS232_Print("W - begin reading for unicast message\n" + "Q - begin reading for broadcast message\n" "m - Toggle mute for mockingboard bus activity\n" "l - Toggle message logging\n" "k - Toggle character echo\n" - "X/x - Turn binary ON or OFF, respectively\n" - "B - Beep\n" + "X/x - Turn binary printing ON or OFF, respectively\n" + "E - Beep\n" + "P - Play\n" "v - Toggle verbose logging\n" #ifdef SOFTWARE_DEBUG "M - Measure bit-timing (pulse-widths and periods)\n" @@ -297,3 +440,10 @@ void print_help() { #endif "? - 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; +}