📄 xyzmodem.c
字号:
if (cksum != ((xyz.crc1 << 8) | xyz.crc2)) {
ZM_DEBUG(zm_dprintf("CRC error - recvd: %02x%02x, computed: %x\n",
xyz.crc1, xyz.crc2, cksum & 0xFFFF));
return xyzModem_cksum;
}
} else {
cksum = 0;
for (i = 0; i < xyz.len; i++) {
cksum += xyz.pkt[i];
}
if (xyz.crc1 != (cksum & 0xFF)) {
ZM_DEBUG(zm_dprintf("Checksum error - recvd: %x, computed: %x\n", xyz.crc1, cksum & 0xFF));
return xyzModem_cksum;
}
}
// If we get here, the message passes [structural] muster
return 0;
}
int
xyzModem_stream_open(connection_info_t *info, int *err)
{
int console_chan, stat;
int retries = xyzModem_MAX_RETRIES;
int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
// ZM_DEBUG(zm_out = zm_out_start);
#ifdef xyzModem_zmodem
if (info->mode == xyzModem_zmodem) {
*err = xyzModem_noZmodem;
return -1;
}
#endif
// Set up the I/O channel. Note: this allows for using a different port in the future
console_chan = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
if (info->chan >= 0) {
CYGACC_CALL_IF_SET_CONSOLE_COMM(info->chan);
} else {
CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan);
}
xyz.__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan);
CYGACC_COMM_IF_CONTROL(*xyz.__chan, __COMMCTL_SET_TIMEOUT, xyzModem_CHAR_TIMEOUT);
xyz.len = 0;
xyz.crc_mode = true;
xyz.at_eof = false;
xyz.tx_ack = false;
xyz.mode = info->mode;
xyz.total_retries = 0;
xyz.total_SOH = 0;
xyz.total_STX = 0;
xyz.total_CAN = 0;
#ifdef USE_YMODEM_LENGTH
xyz.read_length = 0;
xyz.file_length = 0;
#endif
CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
if (xyz.mode == xyzModem_xmodem) {
// X-modem doesn't have an information header - exit here
xyz.next_blk = 1;
return 0;
}
while (retries-- > 0) {
stat = xyzModem_get_hdr();
if (stat == 0) {
// Y-modem file information header
if (xyz.blk == 0) {
#ifdef USE_YMODEM_LENGTH
// skip filename
while (*xyz.bufp++);
// get the length
parse_num(xyz.bufp, &xyz.file_length, NULL, " ");
#endif
// The rest of the file name data block quietly discarded
xyz.tx_ack = true;
}
xyz.next_blk = 1;
xyz.len = 0;
return 0;
} else
if (stat == xyzModem_timeout) {
if (--crc_retries <= 0) xyz.crc_mode = false;
CYGACC_CALL_IF_DELAY_US(5*100000); // Extra delay for startup
CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
xyz.total_retries++;
ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
}
if (stat == xyzModem_cancel) {
break;
}
}
*err = stat;
ZM_DEBUG(zm_flush());
return -1;
}
int
xyzModem_stream_read(char *buf, int size, int *err)
{
int stat, total, len;
int retries;
total = 0;
stat = xyzModem_cancel;
// Try and get 'size' bytes into the buffer
while (!xyz.at_eof && (size > 0)) {
if (xyz.len == 0) {
retries = xyzModem_MAX_RETRIES;
while (retries-- > 0) {
stat = xyzModem_get_hdr();
if (stat == 0) {
if (xyz.blk == xyz.next_blk) {
xyz.tx_ack = true;
ZM_DEBUG(zm_dprintf("ACK block %d (%d)\n", xyz.blk, __LINE__));
xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
// Data blocks can be padded with ^Z (EOF) characters
// This code tries to detect and remove them
#ifdef xyzModem_zmodem
if (xyz.mode != xyzModem_zmodem) {
#else
if (1) {
#endif
if ((xyz.bufp[xyz.len-1] == EOF) &&
(xyz.bufp[xyz.len-2] == EOF) &&
(xyz.bufp[xyz.len-3] == EOF)) {
while (xyz.len && (xyz.bufp[xyz.len-1] == EOF)) {
xyz.len--;
}
}
}
#ifdef USE_YMODEM_LENGTH
// See if accumulated length exceeds that of the file.
// If so, reduce size (i.e., cut out pad bytes)
// Only do this for Y-modem (and Z-modem should it ever
// be supported since it can fall back to Y-modem mode).
if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length) {
xyz.read_length += xyz.len;
if (xyz.read_length > xyz.file_length) {
xyz.len -= (xyz.read_length - xyz.file_length);
}
}
#endif
break;
} else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF)) {
// Just re-ACK this so sender will get on with it
CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
continue; // Need new header
} else {
stat = xyzModem_sequence;
}
}
if (stat == xyzModem_cancel) {
break;
}
if (stat == xyzModem_eof) {
CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
ZM_DEBUG(zm_dprintf("ACK (%d)\n", __LINE__));
if (xyz.mode == xyzModem_ymodem) {
CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
xyz.total_retries++;
ZM_DEBUG(zm_dprintf("Reading Final Header\n"));
stat = xyzModem_get_hdr();
CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
ZM_DEBUG(zm_dprintf("FINAL ACK (%d)\n", __LINE__));
}
xyz.at_eof = true;
break;
}
CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
xyz.total_retries++;
ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
}
if (stat < 0) {
*err = stat;
xyz.len = -1;
return total;
}
}
// Don't "read" data from the EOF protocol package
if (!xyz.at_eof) {
len = xyz.len;
if (size < len) len = size;
memcpy(buf, xyz.bufp, len);
size -= len;
buf += len;
total += len;
xyz.len -= len;
xyz.bufp += len;
}
}
return total;
}
void
xyzModem_stream_close(int *err)
{
diag_printf("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
xyz.crc_mode ? "CRC" : "Cksum",
xyz.total_SOH, xyz.total_STX, xyz.total_CAN,
xyz.total_retries);
// ZM_DEBUG(zm_flush());
}
// Need to be able to clean out the input buffer, so have to take the
// getc
void xyzModem_stream_terminate(bool abort, int (*getc)(void))
{
int c;
if (abort) {
ZM_DEBUG(zm_dprintf("!!!! TRANSFER ABORT !!!!\n"));
switch (xyz.mode) {
case xyzModem_xmodem:
case xyzModem_ymodem:
// The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal
// number of Backspaces is a friendly way to get the other end to abort.
CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
// Now consume the rest of what's waiting on the line.
ZM_DEBUG(zm_dprintf("Flushing serial line.\n"));
xyzModem_flush();
xyz.at_eof = true;
break;
#ifdef xyzModem_zmodem
case xyzModem_zmodem:
// Might support it some day I suppose.
#endif
break;
}
} else {
ZM_DEBUG(zm_dprintf("Engaging cleanup mode...\n"));
// Consume any trailing crap left in the inbuffer from
// previous recieved blocks. Since very few files are an exact multiple
// of the transfer block size, there will almost always be some gunk here.
// If we don't eat it now, RedBoot will think the user typed it.
ZM_DEBUG(zm_dprintf("Trailing gunk:\n"));
while ((c = (*getc)()) > -1) ;
ZM_DEBUG(zm_dprintf("\n"));
// Make a small delay to give terminal programs like minicom
// time to get control again after their file transfer program
// exits.
CYGACC_CALL_IF_DELAY_US((cyg_int32)250000);
}
}
char *
xyzModem_error(int err)
{
switch (err) {
case xyzModem_access:
return "Can't access file";
break;
case xyzModem_noZmodem:
return "Sorry, zModem not available yet";
break;
case xyzModem_timeout:
return "Timed out";
break;
case xyzModem_eof:
return "End of file";
break;
case xyzModem_cancel:
return "Cancelled";
break;
case xyzModem_frame:
return "Invalid framing";
break;
case xyzModem_cksum:
return "CRC/checksum error";
break;
case xyzModem_sequence:
return "Block sequence error";
break;
default:
return "Unknown error";
break;
}
}
//
// RedBoot interface
//
GETC_IO_FUNCS(xyzModem_io, xyzModem_stream_open, xyzModem_stream_close,
xyzModem_stream_terminate, xyzModem_stream_read, xyzModem_error);
RedBoot_load(xmodem, xyzModem_io, false, false, xyzModem_xmodem);
RedBoot_load(ymodem, xyzModem_io, false, false, xyzModem_ymodem);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -