From 13c631bfdd98bf04f4e03e06471ef0acd176ca70 Mon Sep 17 00:00:00 2001 From: pnr Date: Fri, 15 Sep 2023 16:00:37 +0200 Subject: [PATCH] X/YModem final updates --- README.md | 2 ++ man/tio.1.in | 4 +++ src/tty.c | 9 ++++-- src/xymodem.c | 78 +++++++++++++++++++++++++++------------------------ 4 files changed, 54 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 7f57ab7..dc70429 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,8 @@ ctrl-t ? to list the available key commands. [20:19:12.041] ctrl-t t Toggle line timestamp mode [20:19:12.041] ctrl-t U Toggle conversion to uppercase [20:19:12.041] ctrl-t v Show version +[20:19:12.041] ctrl-t x Send file using the XMODEM protocol +[20:19:12.041] ctrl-t y Send file using the YMODEM protocol [20:19:12.041] ctrl-t ctrl-t Send ctrl-t character ``` diff --git a/man/tio.1.in b/man/tio.1.in index 6b75422..b9cdf1a 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -323,6 +323,10 @@ Toggle line timestamp mode Toggle conversion to uppercase on output .IP "\fBctrl-t v" Show version +.IP "\fBctrl-t x" +Send a file using the XMODEM protocol (prompts for file name) +.IP "\fBctrl-t y" +Send a file using the YMODEM protocol (prompts for file name) .IP "\fBctrl-t ctrl-t" Send ctrl-t character diff --git a/src/tty.c b/src/tty.c index cf342c8..0f57d75 100644 --- a/src/tty.c +++ b/src/tty.c @@ -303,6 +303,11 @@ void *tty_stdin_input_thread(void *arg) byte_count = read(STDIN_FILENO, input_buffer, BUFSIZ); if (byte_count < 0) { + /* No error actually occurred */ + if (errno == EINTR) + { + continue; + } tio_warning_printf("Could not read from stdin (%s)", strerror(errno)); } else if (byte_count == 0) @@ -356,7 +361,7 @@ void *tty_stdin_input_thread(void *arg) } // Write all bytes read to pipe - while (byte_count) + while (byte_count > 0) { bytes_written = write(pipefd[1], input_buffer, byte_count); if (bytes_written < 0) @@ -759,7 +764,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case KEY_X: case KEY_Y: tio_printf("Send file with %cMODEM", toupper(input_char)); - fprintf(stdout, "Enter file name: "); + tio_printf_raw("Enter file name: "); if (tio_readln()) { tio_printf("Sending file '%s'", line); tio_printf("Press any key to abort transfer"); diff --git a/src/xymodem.c b/src/xymodem.c index ae8cbb7..1dbeddc 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -2,6 +2,8 @@ * Minimalistic implementation of the xmodem-1k and ymodem sender protocol. * https://en.wikipedia.org/wiki/XMODEM * https://en.wikipedia.org/wiki/YMODEM + * + * SPDX-License-Identifier: GPL-2.0-or-later OR MIT-0 * */ @@ -18,21 +20,24 @@ #include #include "misc.h" -#define X_STX 0x02 -#define X_ACK 0x06 -#define X_NAK 0x15 -#define X_CAN 0x18 - +#define STX 0x02 +#define ACK 0x06 +#define NAK 0x15 +#define CAN 0x18 #define EOT "\004" +#define OK 0 +#define ERR (-1) + #define min(a, b) ((a) < (b) ? (a) : (b)) struct xpacket { - uint8_t start; + uint8_t type; uint8_t seq; uint8_t nseq; uint8_t data[1024]; - uint16_t crc; + uint8_t crc_hi; + uint8_t crc_lo; } __attribute__((packed)); /* See https://en.wikipedia.org/wiki/Computation_of_cyclic_redundancy_checks */ @@ -48,17 +53,12 @@ static uint16_t crc16(const uint8_t *data, uint16_t size) return crc; } -static uint16_t swap16(uint16_t in) -{ - return (in >> 8) | ((in & 0xff) << 8); -} - static int xmodem(int sio, const void *data, size_t len, int seq) { struct xpacket packet; const uint8_t *buf = data; char resp = 0; - int rc; + int rc, crc; /* Drain pending characters from serial line. Insist on the * last drained character being 'C'. @@ -68,19 +68,19 @@ static int xmodem(int sio, const void *data, size_t len, int seq) return -1; if (read(sio, &resp, 1) < 0) { if (errno == EWOULDBLOCK) { - if (resp == 'C') break; - if (resp == X_CAN) return -1; + if (resp == 'C') break; + if (resp == CAN) return ERR; usleep(50000); continue; } perror("Read sync from serial failed"); - return -1; + return ERR; } } /* Always work with 1K packets */ - packet.seq = seq; - packet.start = X_STX; + packet.seq = seq; + packet.type = STX; while (len) { size_t sz, z = 0; @@ -90,7 +90,9 @@ static int xmodem(int sio, const void *data, size_t len, int seq) z = min(len, sizeof(packet.data)); memcpy(packet.data, buf, z); memset(packet.data + z, 0, sizeof(packet.data) - z); - packet.crc = swap16(crc16(packet.data, sizeof(packet.data))); + crc = crc16(packet.data, sizeof(packet.data)); + packet.crc_hi = crc >> 8; + packet.crc_lo = crc; packet.nseq = 0xff - packet.seq; /* Send packet */ @@ -98,47 +100,49 @@ static int xmodem(int sio, const void *data, size_t len, int seq) sz = sizeof(packet); while (sz) { if (key_hit) - return -1; + return ERR; if ((rc = write(sio, from, sz)) < 0 ) { if (errno == EWOULDBLOCK) { usleep(1000); continue; } perror("Write packet to serial failed"); - return -1; + return ERR; } from += rc; sz -= rc; } + /* 'lrzsz' does not ACK ymodem's fin packet */ + if (seq == 0 && packet.data[0] == 0) resp = ACK; + /* Read receiver response, timeout 1 s */ - resp = X_ACK; for(int n=0; n < 20; n++) { if (key_hit) - return -1; + return ERR; if (read(sio, &resp, 1) < 0) { if (errno == EWOULDBLOCK) { usleep(50000); continue; } perror("Read ack/nak from serial failed"); - return -1; + return ERR; } break; } /* Update "progress bar" */ switch (resp) { - case X_NAK: status = 'N'; break; - case X_ACK: status = '.'; break; - case 'C': status = 'C'; break; - case X_CAN: status = '!'; return -1; - default: status = '?'; + case NAK: status = 'N'; break; + case ACK: status = '.'; break; + case 'C': status = 'C'; break; + case CAN: status = '!'; return ERR; + default: status = '?'; } write(STDOUT_FILENO, &status, 1); /* Move to next block after ACK */ - if (resp == X_ACK) { + if (resp == ACK) { packet.seq++; len -= z; buf += z; @@ -148,21 +152,21 @@ static int xmodem(int sio, const void *data, size_t len, int seq) /* Send EOT at 1 Hz until ACK or CAN received */ while (seq) { if (key_hit) - return -1; + return ERR; if (write(sio, EOT, 1) < 0) { perror("Write EOT to serial failed"); - return -1; + return ERR; } write(STDOUT_FILENO, "|", 1); usleep(1000000); /* 1 s timeout*/ if (read(sio, &resp, 1) < 0) { if (errno == EWOULDBLOCK) continue; perror("Read from serial failed"); - return -1; + return ERR; } - if (resp == X_ACK || resp == X_CAN) { + if (resp == ACK || resp == CAN) { write(STDOUT_FILENO, "\r\n", 2); - return (resp == X_ACK) ? 0 : -1; + return (resp == ACK) ? OK : ERR; } } return 0; /* not reached */ @@ -179,7 +183,7 @@ int xymodem_send(int sio, const char *filename, char mode) fd = open(filename, O_RDONLY); if (fd < 0) { perror("Could not open file"); - return -1; + return ERR; } fstat(fd, &stat); len = stat.st_size; @@ -187,7 +191,7 @@ int xymodem_send(int sio, const char *filename, char mode) if (!buf) { close(fd); perror("Could not mmap file"); - return -1; + return ERR; } /* Do transfer */