mirror of
https://github.com/tio/tio.git
synced 2026-05-01 14:57:59 +02:00
Refactoring XYMODEM codes and Fix error code handling
Refactoring. - separete routines from xmodem_send_xxx, - xmsend_inirial_handshake - xmsend_wait_response, - xmsend_repeat_eot_and_wait_response - separete routines from xmodem_receive, - xmrecv_drain_pending_chars - separete routines from xymodem_send, - ymodem_send_1k - rename routines. - crc16 --> calculate_crc16 - update_CRC --> update_crc16 - xmodem_1k --> xmodem_send_1k - xmodem_send --> xmodem_send_128b - receive_packet --> xmrecv_receive_packet - rename struct types. - xpacket --> xpacket_128b - separete xpacket's header / footer - xpacket_hdr, xpacket_ftr_crc Fix error code handling. - use 'rc' for system call's return code, and add 'err' for xymodem function's return code (OK, ERR, ...) - add ERR_TMO to return codes - change USER_CAN to ERR_USER_CAN, because it is negative value Fix xmrecv_receive_packet()'s argument packet to pointer type.
This commit is contained in:
parent
b2a8c02d1d
commit
6cc0a58ed8
1 changed files with 349 additions and 298 deletions
647
src/xymodem.c
647
src/xymodem.c
|
|
@ -34,34 +34,42 @@
|
||||||
#define OK 0
|
#define OK 0
|
||||||
#define ERR (-1)
|
#define ERR (-1)
|
||||||
#define ERR_FATAL (-2)
|
#define ERR_FATAL (-2)
|
||||||
#define USER_CAN (-5)
|
#define ERR_TMO (-3)
|
||||||
|
#define ERR_USER_CAN (-5)
|
||||||
|
|
||||||
#define RX_IGNORE 5
|
#define RX_IGNORE 5
|
||||||
|
|
||||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
struct xpacket_1k
|
struct xpkt_hdr
|
||||||
{
|
{
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t seq;
|
uint8_t seq;
|
||||||
uint8_t nseq;
|
uint8_t nseq;
|
||||||
uint8_t data[1024];
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct xpkt_ftr_crc
|
||||||
|
{
|
||||||
uint8_t crc_hi;
|
uint8_t crc_hi;
|
||||||
uint8_t crc_lo;
|
uint8_t crc_lo;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct xpacket
|
struct xpacket_1k
|
||||||
{
|
{
|
||||||
uint8_t type;
|
struct xpkt_hdr hdr;
|
||||||
uint8_t seq;
|
uint8_t data[1024];
|
||||||
uint8_t nseq;
|
struct xpkt_ftr_crc ftr;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct xpacket_128b
|
||||||
|
{
|
||||||
|
struct xpkt_hdr hdr;
|
||||||
uint8_t data[128];
|
uint8_t data[128];
|
||||||
uint8_t crc_hi;
|
struct xpkt_ftr_crc ftr;
|
||||||
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 */
|
||||||
static uint16_t crc16(const uint8_t *data, uint16_t size)
|
static uint16_t calculate_crc16(const uint8_t *data, uint16_t size)
|
||||||
{
|
{
|
||||||
uint16_t crc, s;
|
uint16_t crc, s;
|
||||||
|
|
||||||
|
|
@ -74,27 +82,47 @@ static uint16_t crc16(const uint8_t *data, uint16_t size)
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xmodem_1k(int sio, const void *data, size_t len, int seq)
|
static uint16_t update_crc16(uint16_t crc, char data_char)
|
||||||
{
|
{
|
||||||
struct xpacket_1k packet;
|
uint8_t data = data_char;
|
||||||
const uint8_t *buf = data;
|
|
||||||
char resp = 0;
|
|
||||||
int rc, crc;
|
|
||||||
|
|
||||||
/* Drain pending characters from serial line. Insist on the
|
crc = crc ^ ((uint16_t)data << 8);
|
||||||
* last drained character being 'C'.
|
for (int ix = 0; (ix < 8); ix++)
|
||||||
*/
|
{
|
||||||
|
if (crc & 0x8000)
|
||||||
|
{
|
||||||
|
crc = (crc << 1) ^ 0x1021;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
crc <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Drain pending characters from serial line. Insist on the
|
||||||
|
* last drained character being initial character 'C':CRC,1K
|
||||||
|
*/
|
||||||
|
static int xmsend_initial_handshake(int sio, char init_ch)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
char resp = 0;
|
||||||
|
|
||||||
|
/* Wait for initial character */
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (key_hit)
|
if (key_hit)
|
||||||
return -1;
|
return ERR_USER_CAN;
|
||||||
|
|
||||||
rc = read_poll(sio, &resp, 1, 50);
|
rc = read_poll(sio, &resp, 1, 50);
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
{
|
{
|
||||||
/* timeout 50 ms
|
/* timeout 50 ms
|
||||||
resp has last received character beacuse read_poll() doesn't
|
resp has last received character beacuse read_poll() doesn't
|
||||||
destroy resp value in this case. */
|
destroy resp value in this case. */
|
||||||
if (resp == 'C') break;
|
if (resp == init_ch) break;
|
||||||
if (resp == CAN) return ERR;
|
if (resp == CAN) return ERR;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -104,10 +132,95 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq)
|
||||||
return ERR;
|
return ERR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read receiver response, timeout 1 s
|
||||||
|
*/
|
||||||
|
static int xmsend_wait_response(int sio, char *resp, char tmo_resp)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
for (int n = 0; n < 20; n++)
|
||||||
|
{
|
||||||
|
if (key_hit)
|
||||||
|
return ERR_USER_CAN;
|
||||||
|
|
||||||
|
rc = read_poll(sio, resp, 1, 50);
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
tio_error_print("Read ack/nak from serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
else if (rc > 0)
|
||||||
|
{
|
||||||
|
/* response received */
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* no response time-out */
|
||||||
|
*resp = tmo_resp;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send EOT at 1 Hz until ACK or CAN received
|
||||||
|
*/
|
||||||
|
static int xmsend_repeat_eot_and_wait_response(int sio)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
char resp;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (key_hit)
|
||||||
|
return ERR_USER_CAN;
|
||||||
|
|
||||||
|
if (write(sio, EOT_STR, 1) < 0)
|
||||||
|
{
|
||||||
|
tio_error_print("Write EOT to serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
write(STDOUT_FILENO, "|", 1);
|
||||||
|
/* 1s timeout */
|
||||||
|
rc = read_poll(sio, &resp, 1, 1000);
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
tio_error_print("Read from serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
else if (rc == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (resp == ACK || resp == CAN)
|
||||||
|
{
|
||||||
|
write(STDOUT_FILENO, "\r\n", 2);
|
||||||
|
return (resp == ACK) ? OK : ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OK; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xmodem_send_1k(int sio, const void *data, size_t len, int seq)
|
||||||
|
{
|
||||||
|
struct xpacket_1k packet;
|
||||||
|
const uint8_t *buf = data;
|
||||||
|
char resp = 0, tmo_resp;
|
||||||
|
int rc, crc, err;
|
||||||
|
|
||||||
|
/* Drain pending characters from serial line.
|
||||||
|
Insist on the last drained character being 'C' */
|
||||||
|
err = xmsend_initial_handshake(sio, 'C');
|
||||||
|
if (err != OK)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/* Always work with 1K packets */
|
/* Always work with 1K packets */
|
||||||
packet.seq = seq;
|
packet.hdr.seq = seq;
|
||||||
packet.type = STX;
|
packet.hdr.type = STX;
|
||||||
|
|
||||||
while (len)
|
while (len)
|
||||||
{
|
{
|
||||||
|
|
@ -118,10 +231,10 @@ static int xmodem_1k(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);
|
||||||
crc = crc16(packet.data, sizeof(packet.data));
|
crc = calculate_crc16(packet.data, sizeof(packet.data));
|
||||||
packet.crc_hi = crc >> 8;
|
packet.ftr.crc_hi = crc >> 8;
|
||||||
packet.crc_lo = crc;
|
packet.ftr.crc_lo = crc;
|
||||||
packet.nseq = 0xff - packet.seq;
|
packet.hdr.nseq = 0xff - packet.hdr.seq;
|
||||||
|
|
||||||
/* Send packet */
|
/* Send packet */
|
||||||
from = (char *) &packet;
|
from = (char *) &packet;
|
||||||
|
|
@ -129,7 +242,8 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq)
|
||||||
while (sz)
|
while (sz)
|
||||||
{
|
{
|
||||||
if (key_hit)
|
if (key_hit)
|
||||||
return ERR;
|
return ERR_USER_CAN;
|
||||||
|
|
||||||
if ((rc = write(sio, from, sz)) < 0 )
|
if ((rc = write(sio, from, sz)) < 0 )
|
||||||
{
|
{
|
||||||
if (errno == EWOULDBLOCK)
|
if (errno == EWOULDBLOCK)
|
||||||
|
|
@ -145,26 +259,16 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear response */
|
/* Clear response */
|
||||||
resp = 0;
|
tmo_resp = 0;
|
||||||
|
|
||||||
/* 'lrzsz' does not ACK ymodem's fin packet */
|
/* 'lrzsz' does not ACK ymodem's fin packet */
|
||||||
if (seq == 0 && packet.data[0] == 0) resp = ACK;
|
if (seq == 0 && packet.data[0] == 0) tmo_resp = ACK;
|
||||||
|
|
||||||
/* Read receiver response, timeout 1 s */
|
/* Read receiver response, timeout 1 s */
|
||||||
for (int n = 0; n < 20; n++)
|
err = xmsend_wait_response(sio, &resp, tmo_resp);
|
||||||
|
if (err != OK)
|
||||||
{
|
{
|
||||||
if (key_hit)
|
return err;
|
||||||
return ERR;
|
|
||||||
rc = read_poll(sio, &resp, 1, 50);
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
tio_error_print("Read ack/nak from serial failed");
|
|
||||||
return ERR;
|
|
||||||
}
|
|
||||||
else if (rc > 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update "progress bar" */
|
/* Update "progress bar" */
|
||||||
|
|
@ -181,190 +285,178 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq)
|
||||||
/* Move to next block after ACK */
|
/* Move to next block after ACK */
|
||||||
if (resp == ACK)
|
if (resp == ACK)
|
||||||
{
|
{
|
||||||
packet.seq++;
|
packet.hdr.seq++;
|
||||||
|
len -= z;
|
||||||
|
buf += z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seq != 0)
|
||||||
|
{
|
||||||
|
/* Send EOT at 1 Hz until ACK or CAN received */
|
||||||
|
err = xmsend_repeat_eot_and_wait_response(sio);
|
||||||
|
if (err != OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xmodem_send_128b(int sio, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
struct xpacket_128b packet;
|
||||||
|
const uint8_t *buf = data;
|
||||||
|
char resp = 0, tmo_resp;
|
||||||
|
int rc, crc, err;
|
||||||
|
|
||||||
|
/* Drain pending characters from serial line.
|
||||||
|
Insist on the last drained character being 'C' */
|
||||||
|
err = xmsend_initial_handshake(sio, 'C');
|
||||||
|
if (err != OK)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* Always work with 128b packets */
|
||||||
|
packet.hdr.seq = 1;
|
||||||
|
packet.hdr.type = SOH;
|
||||||
|
|
||||||
|
while (len)
|
||||||
|
{
|
||||||
|
size_t sz, z = 0;
|
||||||
|
char *from, status;
|
||||||
|
|
||||||
|
/* Build next packet, pad with 0 to full seq */
|
||||||
|
z = min(len, sizeof(packet.data));
|
||||||
|
memcpy(packet.data, buf, z);
|
||||||
|
memset(packet.data + z, 0, sizeof(packet.data) - z);
|
||||||
|
crc = calculate_crc16(packet.data, sizeof(packet.data));
|
||||||
|
packet.ftr.crc_hi = crc >> 8;
|
||||||
|
packet.ftr.crc_lo = crc;
|
||||||
|
packet.hdr.nseq = 0xff - packet.hdr.seq;
|
||||||
|
|
||||||
|
/* Send packet */
|
||||||
|
from = (char *) &packet;
|
||||||
|
sz = sizeof(packet);
|
||||||
|
while (sz)
|
||||||
|
{
|
||||||
|
if (key_hit)
|
||||||
|
return ERR_USER_CAN;
|
||||||
|
|
||||||
|
if ((rc = write(sio, from, sz)) < 0 )
|
||||||
|
{
|
||||||
|
if (errno == EWOULDBLOCK)
|
||||||
|
{
|
||||||
|
usleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tio_error_print("Write packet to serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
from += rc;
|
||||||
|
sz -= rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear response */
|
||||||
|
tmo_resp = 0;
|
||||||
|
|
||||||
|
/* Read receiver response, timeout 1 s */
|
||||||
|
err = xmsend_wait_response(sio, &resp, tmo_resp);
|
||||||
|
if (err != OK)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update "progress bar" */
|
||||||
|
switch (resp)
|
||||||
|
{
|
||||||
|
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 == ACK)
|
||||||
|
{
|
||||||
|
packet.hdr.seq++;
|
||||||
len -= z;
|
len -= z;
|
||||||
buf += z;
|
buf += z;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send EOT at 1 Hz until ACK or CAN received */
|
/* Send EOT at 1 Hz until ACK or CAN received */
|
||||||
while (seq)
|
err = xmsend_repeat_eot_and_wait_response(sio);
|
||||||
{
|
|
||||||
if (key_hit)
|
return err;
|
||||||
return ERR;
|
|
||||||
if (write(sio, EOT_STR, 1) < 0)
|
|
||||||
{
|
|
||||||
tio_error_print("Write EOT to serial failed");
|
|
||||||
return ERR;
|
|
||||||
}
|
|
||||||
write(STDOUT_FILENO, "|", 1);
|
|
||||||
/* 1s timeout */
|
|
||||||
rc = read_poll(sio, &resp, 1, 1000);
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
tio_error_print("Read from serial failed");
|
|
||||||
return ERR;
|
|
||||||
}
|
|
||||||
else if (rc == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (resp == ACK || resp == CAN)
|
|
||||||
{
|
|
||||||
write(STDOUT_FILENO, "\r\n", 2);
|
|
||||||
return (resp == ACK) ? OK : ERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0; /* not reached */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xmodem(int sio, const void *data, size_t len)
|
/*
|
||||||
|
* Ymodem: hdr + file + fin
|
||||||
|
*/
|
||||||
|
static int ymodem_send_1k(int sio, const void *data, size_t len, const char *filename, struct stat *stat)
|
||||||
{
|
{
|
||||||
struct xpacket packet;
|
int err;
|
||||||
const uint8_t *buf = data;
|
|
||||||
char resp = 0;
|
while (1)
|
||||||
int rc, crc;
|
{
|
||||||
|
char hdr[1024], *p;
|
||||||
|
|
||||||
|
err = ERR;
|
||||||
|
if (strlen(filename) > 977) break; /* hdr block overrun */
|
||||||
|
p = stpncpy(hdr, filename, 1024) + 1;
|
||||||
|
p += sprintf(p, "%ld %lo %o", len, stat->st_mtime, stat->st_mode);
|
||||||
|
|
||||||
|
if (xmodem_send_1k(sio, hdr, p - hdr, 0) < 0) break; /* hdr with metadata */
|
||||||
|
if (xmodem_send_1k(sio, data, len, 1) < 0) break; /* xmodem file */
|
||||||
|
if (xmodem_send_1k(sio, "", 1, 0) < 0) break; /* empty hdr = fin */
|
||||||
|
err = OK; break;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Drain pending characters from serial line.
|
||||||
|
*/
|
||||||
|
static int xmrecv_drain_pending_chars(int sio)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
char resp;
|
||||||
|
|
||||||
/* Drain pending characters from serial line. Insist on the
|
|
||||||
* last drained character being 'C'.
|
|
||||||
*/
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (key_hit)
|
if (key_hit)
|
||||||
return -1;
|
return ERR_USER_CAN;
|
||||||
|
|
||||||
rc = read_poll(sio, &resp, 1, 50);
|
rc = read_poll(sio, &resp, 1, 50);
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
{
|
{
|
||||||
/* timeout 50 ms
|
break;
|
||||||
resp has last received character beacuse read_poll() doesn't
|
|
||||||
destroy resp value in this case. */
|
|
||||||
if (resp == 'C') break;
|
|
||||||
if (resp == CAN) return ERR;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
else if (rc < 0)
|
else if (rc < 0)
|
||||||
{
|
{
|
||||||
tio_error_print("Read sync from serial failed");
|
tio_error_print("Read sync from serial failed");
|
||||||
return ERR;
|
return ERR;
|
||||||
}
|
}
|
||||||
}
|
if (resp == CAN)
|
||||||
|
|
||||||
/* Always work with 128b packets */
|
|
||||||
packet.seq = 1;
|
|
||||||
packet.type = SOH;
|
|
||||||
|
|
||||||
while (len)
|
|
||||||
{
|
|
||||||
size_t sz, z = 0;
|
|
||||||
char *from, status;
|
|
||||||
|
|
||||||
/* Build next packet, pad with 0 to full seq */
|
|
||||||
z = min(len, sizeof(packet.data));
|
|
||||||
memcpy(packet.data, buf, z);
|
|
||||||
memset(packet.data + z, 0, sizeof(packet.data) - z);
|
|
||||||
crc = crc16(packet.data, sizeof(packet.data));
|
|
||||||
packet.crc_hi = crc >> 8;
|
|
||||||
packet.crc_lo = crc;
|
|
||||||
packet.nseq = 0xff - packet.seq;
|
|
||||||
|
|
||||||
/* Send packet */
|
|
||||||
from = (char *) &packet;
|
|
||||||
sz = sizeof(packet);
|
|
||||||
while (sz)
|
|
||||||
{
|
{
|
||||||
if (key_hit)
|
|
||||||
return ERR;
|
|
||||||
if ((rc = write(sio, from, sz)) < 0 )
|
|
||||||
{
|
|
||||||
if (errno == EWOULDBLOCK)
|
|
||||||
{
|
|
||||||
usleep(1000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tio_error_print("Write packet to serial failed");
|
|
||||||
return ERR;
|
|
||||||
}
|
|
||||||
from += rc;
|
|
||||||
sz -= rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear response */
|
|
||||||
resp = 0;
|
|
||||||
|
|
||||||
/* Read receiver response, timeout 1 s */
|
|
||||||
for (int n = 0; n < 20; n++)
|
|
||||||
{
|
|
||||||
if (key_hit)
|
|
||||||
return ERR;
|
|
||||||
rc = read_poll(sio, &resp, 1, 50);
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
tio_error_print("Read ack/nak from serial failed");
|
|
||||||
return ERR;
|
|
||||||
}
|
|
||||||
else if (rc > 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update "progress bar" */
|
|
||||||
switch (resp)
|
|
||||||
{
|
|
||||||
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 == ACK)
|
|
||||||
{
|
|
||||||
packet.seq++;
|
|
||||||
len -= z;
|
|
||||||
buf += z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send EOT at 1 Hz until ACK or CAN received */
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (key_hit)
|
|
||||||
return ERR;
|
|
||||||
if (write(sio, EOT_STR, 1) < 0)
|
|
||||||
{
|
|
||||||
tio_error_print("Write EOT to serial failed");
|
|
||||||
return ERR;
|
return ERR;
|
||||||
}
|
}
|
||||||
write(STDOUT_FILENO, "|", 1);
|
|
||||||
/* 1s timeout */
|
|
||||||
rc = read_poll(sio, &resp, 1, 1000);
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
tio_error_print("Read from serial failed");
|
|
||||||
return ERR;
|
|
||||||
}
|
|
||||||
else if (rc == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (resp == ACK || resp == CAN)
|
|
||||||
{
|
|
||||||
write(STDOUT_FILENO, "\r\n", 2);
|
|
||||||
return (resp == ACK) ? OK : ERR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0; /* not reached */
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int start_receive(int sio)
|
/*
|
||||||
|
* Start Receive
|
||||||
|
*/
|
||||||
|
static int xmrecv_start_receive(int sio)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct pollfd fds;
|
struct pollfd fds;
|
||||||
fds.events = POLLIN;
|
fds.events = POLLIN;
|
||||||
fds.fd = sio;
|
fds.fd = sio;
|
||||||
|
|
||||||
for (int n = 0; n < 20; n++)
|
for (int n = 0; n < 20; n++)
|
||||||
{
|
{
|
||||||
/* Send the 'C' byte until the sender of the file responds with
|
/* Send the 'C' byte until the sender of the file responds with
|
||||||
|
|
@ -387,44 +479,29 @@ int start_receive(int sio)
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
{
|
{
|
||||||
tio_error_print("%s", strerror(errno));
|
tio_error_print("%s", strerror(errno));
|
||||||
return rc;
|
return ERR;
|
||||||
}
|
}
|
||||||
else if (rc > 0)
|
else if (rc > 0)
|
||||||
{
|
{
|
||||||
if (fds.revents & POLLIN)
|
if (fds.revents & POLLIN)
|
||||||
{
|
{
|
||||||
return rc;
|
return OK;
|
||||||
}
|
}
|
||||||
else /* if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) */
|
else /* if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) */
|
||||||
{
|
{
|
||||||
return -1;
|
return ERR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (key_hit)
|
if (key_hit)
|
||||||
return USER_CAN;
|
return ERR_USER_CAN;
|
||||||
}
|
}
|
||||||
return rc;
|
return ERR_TMO;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t update_CRC(uint16_t crc, char data_char)
|
/*
|
||||||
{
|
* Receive a packet
|
||||||
uint8_t data = data_char;
|
*/
|
||||||
crc = crc ^ ((uint16_t)data << 8);
|
static int xmrecv_receive_packet(int sio, struct xpacket_128b *packet, int fd)
|
||||||
for (int ix = 0; (ix < 8); ix++)
|
|
||||||
{
|
|
||||||
if (crc & 0x8000)
|
|
||||||
{
|
|
||||||
crc = (crc << 1) ^ 0x1021;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
crc <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int receive_packet(int sio, struct xpacket packet, int fd)
|
|
||||||
{
|
{
|
||||||
char rxSeq1, rxSeq2 = 0;
|
char rxSeq1, rxSeq2 = 0;
|
||||||
char resp = 0;
|
char resp = 0;
|
||||||
|
|
@ -441,7 +518,7 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
{
|
{
|
||||||
tio_error_print("Timeout waiting for first seq byte");
|
tio_error_print("Timeout waiting for first seq byte");
|
||||||
return ERR;
|
return ERR_TMO;
|
||||||
}
|
}
|
||||||
else if (rc < 0)
|
else if (rc < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -452,7 +529,7 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
{
|
{
|
||||||
tio_error_print("Timeout waiting for second seq byte");
|
tio_error_print("Timeout waiting for second seq byte");
|
||||||
return ERR;
|
return ERR_TMO;
|
||||||
}
|
}
|
||||||
else if (rc < 0)
|
else if (rc < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -460,10 +537,10 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
return ERR_FATAL;
|
return ERR_FATAL;
|
||||||
}
|
}
|
||||||
if (key_hit)
|
if (key_hit)
|
||||||
return USER_CAN;
|
return ERR_USER_CAN;
|
||||||
|
|
||||||
/* Read packet Data */
|
/* Read packet Data */
|
||||||
for (unsigned ix = 0; (ix < sizeof(packet.data)); ix++)
|
for (unsigned ix = 0; (ix < sizeof(packet->data)); ix++)
|
||||||
{
|
{
|
||||||
rc = read_poll(sio, &resp, 1, 3000);
|
rc = read_poll(sio, &resp, 1, 3000);
|
||||||
/* If the read times out or fails then fail this packet. */
|
/* If the read times out or fails then fail this packet. */
|
||||||
|
|
@ -476,7 +553,7 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
tio_error_print("Write cancel packet to serial failed");
|
tio_error_print("Write cancel packet to serial failed");
|
||||||
return ERR_FATAL;
|
return ERR_FATAL;
|
||||||
}
|
}
|
||||||
return ERR;
|
return ERR_TMO;
|
||||||
}
|
}
|
||||||
else if (rc < 0)
|
else if (rc < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -488,10 +565,10 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
}
|
}
|
||||||
return ERR_FATAL;
|
return ERR_FATAL;
|
||||||
}
|
}
|
||||||
packet.data[ix] = (uint8_t) resp;
|
packet->data[ix] = (uint8_t) resp;
|
||||||
calcCrc = update_CRC(calcCrc, resp);
|
calcCrc = update_crc16(calcCrc, resp);
|
||||||
if (key_hit)
|
if (key_hit)
|
||||||
return USER_CAN;
|
return ERR_USER_CAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read CRC */
|
/* Read CRC */
|
||||||
|
|
@ -499,7 +576,7 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
{
|
{
|
||||||
tio_error_print("Timeout waiting for first CRC byte");
|
tio_error_print("Timeout waiting for first CRC byte");
|
||||||
return ERR;
|
return ERR_TMO;
|
||||||
}
|
}
|
||||||
else if (rc < 0)
|
else if (rc < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -515,7 +592,7 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
{
|
{
|
||||||
tio_error_print("Timeout waiting for second CRC byte");
|
tio_error_print("Timeout waiting for second CRC byte");
|
||||||
return ERR;
|
return ERR_TMO;
|
||||||
}
|
}
|
||||||
else if (rc < 0)
|
else if (rc < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -528,10 +605,10 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
rxCrc |= uresp16;
|
rxCrc |= uresp16;
|
||||||
|
|
||||||
if (key_hit)
|
if (key_hit)
|
||||||
return USER_CAN;
|
return ERR_USER_CAN;
|
||||||
|
|
||||||
/* At this point in the code, there should not be anything in the receive buffer
|
/* At this point in the code, there should not be anything in the receive buffer
|
||||||
because the sender has just sent a complete packet and is waiting on a response. */
|
because the sender has just sent a complete packet and is waiting on a response. */
|
||||||
rc = poll(&fds, 1, 10);
|
rc = poll(&fds, 1, 10);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -564,7 +641,7 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
uint8_t seq1 = rxSeq1;
|
uint8_t seq1 = rxSeq1;
|
||||||
uint8_t seq2 = rxSeq2;
|
uint8_t seq2 = rxSeq2;
|
||||||
|
|
||||||
if ((calcCrc == rxCrc) && (seq1 == packet.seq - 1) && ((seq1 ^ seq2) == tester))
|
if ((calcCrc == rxCrc) && (seq1 == packet->hdr.seq - 1) && ((seq1 ^ seq2) == tester))
|
||||||
{
|
{
|
||||||
/* Resend of previously processed packet. */
|
/* Resend of previously processed packet. */
|
||||||
rc = write(sio, ACK_STR, 1);
|
rc = write(sio, ACK_STR, 1);
|
||||||
|
|
@ -575,7 +652,7 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
}
|
}
|
||||||
return RX_IGNORE;
|
return RX_IGNORE;
|
||||||
}
|
}
|
||||||
else if ((calcCrc != rxCrc) || (seq1 != packet.seq) || ((seq1 ^ seq2) != tester))
|
else if ((calcCrc != rxCrc) || (seq1 != packet->hdr.seq) || ((seq1 ^ seq2) != tester))
|
||||||
{
|
{
|
||||||
/* Fail if the CRC or sequence number is not correct or if the two received
|
/* Fail if the CRC or sequence number is not correct or if the two received
|
||||||
sequence numbers are not the complement of one another. */
|
sequence numbers are not the complement of one another. */
|
||||||
|
|
@ -583,14 +660,14 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
tio_debug_printf("CRC read: %u", rxCrc);
|
tio_debug_printf("CRC read: %u", rxCrc);
|
||||||
tio_debug_printf("CRC calculated: %u", calcCrc);
|
tio_debug_printf("CRC calculated: %u", calcCrc);
|
||||||
tio_debug_printf("Seq read: %hhu", rxSeq1);
|
tio_debug_printf("Seq read: %hhu", rxSeq1);
|
||||||
tio_debug_printf("Seq should be: %hhu", packet.seq);
|
tio_debug_printf("Seq should be: %hhu", packet->hdr.seq);
|
||||||
tio_debug_printf("inv seq: %hhu", rxSeq2);
|
tio_debug_printf("inv seq: %hhu", rxSeq2);
|
||||||
return ERR;
|
return ERR;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* The data is good. Process the packet then ACK it to the sender. */
|
/* The data is good. Process the packet then ACK it to the sender. */
|
||||||
rc = write(fd, packet.data, sizeof(packet.data));
|
rc = write(fd, packet->data, sizeof(packet->data));
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
{
|
{
|
||||||
tio_error_print("Problem writing to file");
|
tio_error_print("Problem writing to file");
|
||||||
|
|
@ -614,48 +691,36 @@ int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
|
|
||||||
int xmodem_receive(int sio, int fd)
|
int xmodem_receive(int sio, int fd)
|
||||||
{
|
{
|
||||||
struct xpacket packet;
|
struct xpacket_128b packet;
|
||||||
char resp = 0;
|
char resp = 0;
|
||||||
int rc;
|
int rc, err;
|
||||||
bool complete = false;
|
bool complete = false;
|
||||||
char status;
|
char status;
|
||||||
|
|
||||||
/* Drain pending characters from serial line.*/
|
/* Drain pending characters from serial line.*/
|
||||||
while (true)
|
err = xmrecv_drain_pending_chars(sio);
|
||||||
|
if (err != OK)
|
||||||
{
|
{
|
||||||
if (key_hit)
|
return err;
|
||||||
return -1;
|
|
||||||
rc = read_poll(sio, &resp, 1, 50);
|
|
||||||
if (rc == 0)
|
|
||||||
{
|
|
||||||
if (resp == CAN) return ERR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (rc < 0)
|
|
||||||
{
|
|
||||||
if (rc != USER_CAN)
|
|
||||||
{
|
|
||||||
tio_error_print("Read sync from serial failed");
|
|
||||||
}
|
|
||||||
return ERR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Always work with 128b packets */
|
/* Always work with 128b packets */
|
||||||
packet.seq = 1;
|
packet.hdr.seq = 1;
|
||||||
packet.type = SOH;
|
packet.hdr.type = SOH;
|
||||||
|
|
||||||
/* Start Receive*/
|
/* Start Receive*/
|
||||||
rc = start_receive(sio);
|
err = xmrecv_start_receive(sio);
|
||||||
if (rc == 0)
|
if (err != OK)
|
||||||
{
|
{
|
||||||
tio_error_print("Timeout waiting for transfer to start");
|
if (err == ERR_TMO)
|
||||||
return ERR;
|
{
|
||||||
}
|
tio_error_print("Timeout waiting for transfer to start");
|
||||||
else if (rc < 0)
|
}
|
||||||
{
|
else
|
||||||
tio_error_print("Error starting XMODEM receive");
|
{
|
||||||
return ERR;
|
tio_error_print("Error starting XMODEM receive");
|
||||||
|
}
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!complete)
|
while (!complete)
|
||||||
|
|
@ -665,7 +730,7 @@ int xmodem_receive(int sio, int fd)
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
{
|
{
|
||||||
tio_error_print("Timeout waiting for start of next packet");
|
tio_error_print("Timeout waiting for start of next packet");
|
||||||
return ERR;
|
return ERR_TMO;
|
||||||
}
|
}
|
||||||
else if (rc < 0)
|
else if (rc < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -673,19 +738,19 @@ int xmodem_receive(int sio, int fd)
|
||||||
return ERR;
|
return ERR;
|
||||||
}
|
}
|
||||||
if (key_hit)
|
if (key_hit)
|
||||||
return USER_CAN;
|
return ERR_USER_CAN;
|
||||||
|
|
||||||
switch (resp)
|
switch (resp)
|
||||||
{
|
{
|
||||||
case SOH:
|
case SOH:
|
||||||
/* Start of a packet */
|
/* Start of a packet */
|
||||||
rc = receive_packet(sio, packet, fd);
|
err = xmrecv_receive_packet(sio, &packet, fd);
|
||||||
if (rc == OK)
|
if (err == OK)
|
||||||
{
|
{
|
||||||
packet.seq++;
|
packet.hdr.seq++;
|
||||||
status = '.';
|
status = '.';
|
||||||
}
|
}
|
||||||
else if (rc == ERR)
|
else if (err == ERR || err == ERR_TMO)
|
||||||
{
|
{
|
||||||
rc = write(sio, NAK_STR, 1);
|
rc = write(sio, NAK_STR, 1);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
|
|
@ -695,12 +760,12 @@ int xmodem_receive(int sio, int fd)
|
||||||
}
|
}
|
||||||
status = 'N';
|
status = 'N';
|
||||||
}
|
}
|
||||||
else if (rc == ERR_FATAL)
|
else if (err == ERR_FATAL)
|
||||||
{
|
{
|
||||||
tio_error_print("Receive cancelled due to fatal error");
|
tio_error_print("Receive cancelled due to fatal error");
|
||||||
return ERR;
|
return ERR;
|
||||||
}
|
}
|
||||||
else if (rc == USER_CAN)
|
else if (err == ERR_USER_CAN)
|
||||||
{
|
{
|
||||||
rc = write(sio, CAN_STR, 1);
|
rc = write(sio, CAN_STR, 1);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
|
|
@ -708,9 +773,9 @@ int xmodem_receive(int sio, int fd)
|
||||||
tio_error_print("Writing cancel to serial failed");
|
tio_error_print("Writing cancel to serial failed");
|
||||||
return ERR;
|
return ERR;
|
||||||
}
|
}
|
||||||
return USER_CAN;
|
return ERR_USER_CAN;
|
||||||
}
|
}
|
||||||
else if (rc == RX_IGNORE)
|
else if (err == RX_IGNORE)
|
||||||
{
|
{
|
||||||
status = ':';
|
status = ':';
|
||||||
}
|
}
|
||||||
|
|
@ -750,7 +815,7 @@ int xmodem_receive(int sio, int fd)
|
||||||
int xymodem_send(int sio, const char *filename, modem_mode_t mode)
|
int xymodem_send(int sio, const char *filename, modem_mode_t mode)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
int rc, fd;
|
int err, fd;
|
||||||
struct stat stat;
|
struct stat stat;
|
||||||
const uint8_t *buf;
|
const uint8_t *buf;
|
||||||
|
|
||||||
|
|
@ -775,29 +840,15 @@ int xymodem_send(int sio, const char *filename, modem_mode_t mode)
|
||||||
key_hit = 0;
|
key_hit = 0;
|
||||||
if (mode == XMODEM_1K)
|
if (mode == XMODEM_1K)
|
||||||
{
|
{
|
||||||
rc = xmodem_1k(sio, buf, len, 1);
|
err = xmodem_send_1k(sio, buf, len, 1);
|
||||||
}
|
}
|
||||||
else if (mode == XMODEM_CRC)
|
else if (mode == XMODEM_CRC)
|
||||||
{
|
{
|
||||||
rc = xmodem(sio, buf, len);
|
err = xmodem_send_128b(sio, buf, len);
|
||||||
}
|
}
|
||||||
else
|
else /* if (mode == YMODEM) */
|
||||||
{
|
{
|
||||||
/* Ymodem: hdr + file + fin */
|
err = ymodem_send_1k(sio, buf, len, filename, &stat);
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
char hdr[1024], *p;
|
|
||||||
|
|
||||||
rc = -1;
|
|
||||||
if (strlen(filename) > 977) break; /* hdr block overrun */
|
|
||||||
p = stpncpy(hdr, filename, 1024) + 1;
|
|
||||||
p += sprintf(p, "%ld %lo %o", len, stat.st_mtime, stat.st_mode);
|
|
||||||
|
|
||||||
if (xmodem_1k(sio, hdr, p - hdr, 0) < 0) break; /* hdr with metadata */
|
|
||||||
if (xmodem_1k(sio, buf, len, 1) < 0) break; /* xmodem file */
|
|
||||||
if (xmodem_1k(sio, "", 1, 0) < 0) break; /* empty hdr = fin */
|
|
||||||
rc = 0; break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
key_hit = 0xff;
|
key_hit = 0xff;
|
||||||
|
|
||||||
|
|
@ -805,12 +856,12 @@ int xymodem_send(int sio, const char *filename, modem_mode_t mode)
|
||||||
tcflush(sio, TCIOFLUSH);
|
tcflush(sio, TCIOFLUSH);
|
||||||
munmap((void *)buf, len);
|
munmap((void *)buf, len);
|
||||||
close(fd);
|
close(fd);
|
||||||
return rc;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int xymodem_receive(int sio, const char *filename, modem_mode_t mode)
|
int xymodem_receive(int sio, const char *filename, modem_mode_t mode)
|
||||||
{
|
{
|
||||||
int rc, fd;
|
int err, fd;
|
||||||
|
|
||||||
/* Create new file */
|
/* Create new file */
|
||||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
|
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
|
||||||
|
|
@ -825,21 +876,21 @@ int xymodem_receive(int sio, const char *filename, modem_mode_t mode)
|
||||||
if (mode == XMODEM_1K)
|
if (mode == XMODEM_1K)
|
||||||
{
|
{
|
||||||
tio_error_print("Not supported");
|
tio_error_print("Not supported");
|
||||||
rc = -1;
|
err = ERR;
|
||||||
}
|
}
|
||||||
else if (mode == XMODEM_CRC)
|
else if (mode == XMODEM_CRC)
|
||||||
{
|
{
|
||||||
rc = xmodem_receive(sio, fd);
|
err = xmodem_receive(sio, fd);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tio_error_print("Not supported");
|
tio_error_print("Not supported");
|
||||||
rc = -1;
|
err = ERR;
|
||||||
}
|
}
|
||||||
key_hit = 0xff;
|
key_hit = 0xff;
|
||||||
|
|
||||||
/* Flush serial and release resources */
|
/* Flush serial and release resources */
|
||||||
tcflush(sio, TCIOFLUSH);
|
tcflush(sio, TCIOFLUSH);
|
||||||
close(fd);
|
close(fd);
|
||||||
return rc;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue