joeylib2/examples/sertest/sertest.c

118 lines
3.6 KiB
C

// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
// See LICENSE.md for license information.
// SPDX-License-Identifier: MIT
//
// Minimal headless serial self-test -- NO jlInit (no video/graphics), so it
// runs even where the graphics HAL can't (e.g. AROS on the Amiga). Opens the
// DEFAULT port (or SERTEST_DEVICE) at 9600 8N1 (or SERTEST_BAUD), sends a banner (TX
// heartbeat), then echoes every received byte forever. A host serial peer
// verifies banner (TX) + echo (RX).
//
// A peer that sees the modem lines (DOSBox's nullmodem reports them in the
// byte stream as they change) can also send DTR_CHECK: the program then
// drops and raises DTR, closes and reopens the port, drops DTR and closes
// the port for good, with a marker line after each step, so the peer can
// check that jlSerialSetDtr reaches the line and that jlSerialClose leaves
// DTR as it is, up or down. After the check the port stays closed.
#include <joey/serial.h>
#define DTR_CHECK 0x04u // ASCII EOT: run the DTR check (not echoed)
#define DTR_ACK 0x06u // ASCII ACK: the peer has the whole marker line
// The port, overridable at build time (-DSERTEST_DEVICE=JL_SERIAL_SLOT
// -DSERTEST_UNIT=2u) so a gate can open one other than the default.
#ifndef SERTEST_DEVICE
#define SERTEST_DEVICE JL_SERIAL_DEFAULT
#endif
#ifndef SERTEST_UNIT
#define SERTEST_UNIT 0u
#endif
// The line rate, overridable the same way (-DSERTEST_BAUD=115200u: SERFAST,
// the Win16 throughput gate's build).
#ifndef SERTEST_BAUD
#define SERTEST_BAUD 9600u
#endif
static void dtrCheck(const jlSerialConfigT *cfg);
static void sendMark(const char *text);
static void dtrCheck(const jlSerialConfigT *cfg) {
jlSerialSetDtr(false);
sendMark("JLDTR-OFF\r\n");
jlSerialSetDtr(true);
sendMark("JLDTR-ON\r\n");
jlSerialClose();
if (!jlSerialOpen(SERTEST_DEVICE, cfg)) {
return;
}
sendMark("JLDTR-REOPENED\r\n");
jlSerialSetDtr(false);
sendMark("JLDTR-HANGUP\r\n");
jlSerialClose();
}
// A marker line, then the peer's DTR_ACK: the line has gone out whole, so
// the next line change comes after it (a changed line is reported at once,
// while bytes wait their turn in the UART), and a close cannot throw any of
// it away.
static void sendMark(const char *text) {
uint16_t len;
uint16_t sent;
uint8_t byte;
len = 0u;
while (text[len] != '\0') {
len++;
}
sent = 0u;
while (sent < len) {
jlSerialPoll();
sent = (uint16_t)(sent + jlSerialWrite((const uint8_t *)&text[sent], (uint16_t)(len - sent)));
}
for (;;) {
jlSerialPoll();
if (jlSerialRead(&byte, 1u) == 1u && byte == DTR_ACK) {
return;
}
}
}
int main(void) {
jlSerialConfigT cfg;
static const char banner[] = "JLSERTEST-READY\r\n";
cfg.baud = SERTEST_BAUD;
cfg.dataBits = 8u;
cfg.stopBits = 1u;
cfg.parity = JL_SERIAL_PARITY_NONE;
cfg.flow = JL_SERIAL_FLOW_NONE;
cfg.unit = SERTEST_UNIT;
if (!jlSerialOpen(SERTEST_DEVICE, &cfg)) {
return 1;
}
jlSerialWrite((const uint8_t *)banner, (uint16_t)(sizeof(banner) - 1u));
for (;;) {
uint8_t buf[64];
uint16_t n;
uint16_t i;
jlSerialPoll();
n = jlSerialRead(buf, (uint16_t)sizeof(buf));
for (i = 0u; i < n; i++) {
if (buf[i] == DTR_CHECK) {
break;
}
}
if (i > 0u) {
jlSerialWrite(buf, i);
}
if (i < n) {
dtrCheck(&cfg);
}
}
// not reached
}