mirror of
https://github.com/tio/tio.git
synced 2026-05-01 23:07:58 +02:00
X/YModem final updates
This commit is contained in:
parent
9a2f93a04d
commit
13c631bfdd
4 changed files with 54 additions and 39 deletions
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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'.
|
||||||
|
|
@ -69,18 +69,18 @@ static int xmodem(int sio, const void *data, size_t len, int seq)
|
||||||
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 */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue