X/YModem final updates

This commit is contained in:
pnr 2023-09-15 16:00:37 +02:00
parent 9a2f93a04d
commit 13c631bfdd
4 changed files with 54 additions and 39 deletions

View file

@ -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 t Toggle line timestamp mode
[20:19:12.041] ctrl-t U Toggle conversion to uppercase [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 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 [20:19:12.041] ctrl-t ctrl-t Send ctrl-t character
``` ```

View file

@ -323,6 +323,10 @@ Toggle line timestamp mode
Toggle conversion to uppercase on output Toggle conversion to uppercase on output
.IP "\fBctrl-t v" .IP "\fBctrl-t v"
Show version 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" .IP "\fBctrl-t ctrl-t"
Send ctrl-t character Send ctrl-t character

View file

@ -303,6 +303,11 @@ void *tty_stdin_input_thread(void *arg)
byte_count = read(STDIN_FILENO, input_buffer, BUFSIZ); byte_count = read(STDIN_FILENO, input_buffer, BUFSIZ);
if (byte_count < 0) if (byte_count < 0)
{ {
/* No error actually occurred */
if (errno == EINTR)
{
continue;
}
tio_warning_printf("Could not read from stdin (%s)", strerror(errno)); tio_warning_printf("Could not read from stdin (%s)", strerror(errno));
} }
else if (byte_count == 0) else if (byte_count == 0)
@ -356,7 +361,7 @@ void *tty_stdin_input_thread(void *arg)
} }
// Write all bytes read to pipe // Write all bytes read to pipe
while (byte_count) while (byte_count > 0)
{ {
bytes_written = write(pipefd[1], input_buffer, byte_count); bytes_written = write(pipefd[1], input_buffer, byte_count);
if (bytes_written < 0) 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_X:
case KEY_Y: case KEY_Y:
tio_printf("Send file with %cMODEM", toupper(input_char)); tio_printf("Send file with %cMODEM", toupper(input_char));
fprintf(stdout, "Enter file name: "); tio_printf_raw("Enter file name: ");
if (tio_readln()) { if (tio_readln()) {
tio_printf("Sending file '%s'", line); tio_printf("Sending file '%s'", line);
tio_printf("Press any key to abort transfer"); tio_printf("Press any key to abort transfer");

View file

@ -3,6 +3,8 @@
* https://en.wikipedia.org/wiki/XMODEM * https://en.wikipedia.org/wiki/XMODEM
* https://en.wikipedia.org/wiki/YMODEM * https://en.wikipedia.org/wiki/YMODEM
* *
* SPDX-License-Identifier: GPL-2.0-or-later OR MIT-0
*
*/ */
#include <stdio.h> #include <stdio.h>
@ -18,21 +20,24 @@
#include <termios.h> #include <termios.h>
#include "misc.h" #include "misc.h"
#define X_STX 0x02 #define STX 0x02
#define X_ACK 0x06 #define ACK 0x06
#define X_NAK 0x15 #define NAK 0x15
#define X_CAN 0x18 #define CAN 0x18
#define EOT "\004" #define EOT "\004"
#define OK 0
#define ERR (-1)
#define min(a, b) ((a) < (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b))
struct xpacket { struct xpacket {
uint8_t start; uint8_t type;
uint8_t seq; uint8_t seq;
uint8_t nseq; uint8_t nseq;
uint8_t data[1024]; uint8_t data[1024];
uint16_t crc; uint8_t crc_hi;
uint8_t crc_lo;
} __attribute__((packed)); } __attribute__((packed));
/* See https://en.wikipedia.org/wiki/Computation_of_cyclic_redundancy_checks */ /* 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; 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) static int xmodem(int sio, const void *data, size_t len, int seq)
{ {
struct xpacket packet; struct xpacket packet;
const uint8_t *buf = data; const uint8_t *buf = data;
char resp = 0; char resp = 0;
int rc; int rc, crc;
/* Drain pending characters from serial line. Insist on the /* Drain pending characters from serial line. Insist on the
* last drained character being 'C'. * last drained character being 'C'.
@ -68,19 +68,19 @@ static int xmodem(int sio, const void *data, size_t len, int seq)
return -1; return -1;
if (read(sio, &resp, 1) < 0) { if (read(sio, &resp, 1) < 0) {
if (errno == EWOULDBLOCK) { if (errno == EWOULDBLOCK) {
if (resp == 'C') break; if (resp == 'C') break;
if (resp == X_CAN) return -1; if (resp == CAN) return ERR;
usleep(50000); usleep(50000);
continue; continue;
} }
perror("Read sync from serial failed"); perror("Read sync from serial failed");
return -1; return ERR;
} }
} }
/* Always work with 1K packets */ /* Always work with 1K packets */
packet.seq = seq; packet.seq = seq;
packet.start = X_STX; packet.type = STX;
while (len) { while (len) {
size_t sz, z = 0; 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)); z = min(len, sizeof(packet.data));
memcpy(packet.data, buf, z); memcpy(packet.data, buf, z);
memset(packet.data + z, 0, sizeof(packet.data) - 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; packet.nseq = 0xff - packet.seq;
/* Send packet */ /* Send packet */
@ -98,47 +100,49 @@ static int xmodem(int sio, const void *data, size_t len, int seq)
sz = sizeof(packet); sz = sizeof(packet);
while (sz) { while (sz) {
if (key_hit) if (key_hit)
return -1; return ERR;
if ((rc = write(sio, from, sz)) < 0 ) { if ((rc = write(sio, from, sz)) < 0 ) {
if (errno == EWOULDBLOCK) { if (errno == EWOULDBLOCK) {
usleep(1000); usleep(1000);
continue; continue;
} }
perror("Write packet to serial failed"); perror("Write packet to serial failed");
return -1; return ERR;
} }
from += rc; from += rc;
sz -= 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 */ /* Read receiver response, timeout 1 s */
resp = X_ACK;
for(int n=0; n < 20; n++) { for(int n=0; n < 20; n++) {
if (key_hit) if (key_hit)
return -1; return ERR;
if (read(sio, &resp, 1) < 0) { if (read(sio, &resp, 1) < 0) {
if (errno == EWOULDBLOCK) { if (errno == EWOULDBLOCK) {
usleep(50000); usleep(50000);
continue; continue;
} }
perror("Read ack/nak from serial failed"); perror("Read ack/nak from serial failed");
return -1; return ERR;
} }
break; break;
} }
/* Update "progress bar" */ /* Update "progress bar" */
switch (resp) { switch (resp) {
case X_NAK: status = 'N'; break; case NAK: status = 'N'; break;
case X_ACK: status = '.'; break; case ACK: status = '.'; break;
case 'C': status = 'C'; break; case 'C': status = 'C'; break;
case X_CAN: status = '!'; return -1; case CAN: status = '!'; return ERR;
default: status = '?'; default: status = '?';
} }
write(STDOUT_FILENO, &status, 1); write(STDOUT_FILENO, &status, 1);
/* Move to next block after ACK */ /* Move to next block after ACK */
if (resp == X_ACK) { if (resp == ACK) {
packet.seq++; packet.seq++;
len -= z; len -= z;
buf += 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 */ /* Send EOT at 1 Hz until ACK or CAN received */
while (seq) { while (seq) {
if (key_hit) if (key_hit)
return -1; return ERR;
if (write(sio, EOT, 1) < 0) { if (write(sio, EOT, 1) < 0) {
perror("Write EOT to serial failed"); perror("Write EOT to serial failed");
return -1; return ERR;
} }
write(STDOUT_FILENO, "|", 1); write(STDOUT_FILENO, "|", 1);
usleep(1000000); /* 1 s timeout*/ usleep(1000000); /* 1 s timeout*/
if (read(sio, &resp, 1) < 0) { if (read(sio, &resp, 1) < 0) {
if (errno == EWOULDBLOCK) continue; if (errno == EWOULDBLOCK) continue;
perror("Read from serial failed"); 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); write(STDOUT_FILENO, "\r\n", 2);
return (resp == X_ACK) ? 0 : -1; return (resp == ACK) ? OK : ERR;
} }
} }
return 0; /* not reached */ return 0; /* not reached */
@ -179,7 +183,7 @@ int xymodem_send(int sio, const char *filename, char mode)
fd = open(filename, O_RDONLY); fd = open(filename, O_RDONLY);
if (fd < 0) { if (fd < 0) {
perror("Could not open file"); perror("Could not open file");
return -1; return ERR;
} }
fstat(fd, &stat); fstat(fd, &stat);
len = stat.st_size; len = stat.st_size;
@ -187,7 +191,7 @@ int xymodem_send(int sio, const char *filename, char mode)
if (!buf) { if (!buf) {
close(fd); close(fd);
perror("Could not mmap file"); perror("Could not mmap file");
return -1; return ERR;
} }
/* Do transfer */ /* Do transfer */