| /* XMODEM support for GDB, the GNU debugger. | |
| Copyright 1995 Free Software Foundation, Inc. | |
| This file is part of GDB. | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program; if not, write to the Free Software | |
| Foundation, Inc., 59 Temple Place - Suite 330, | |
| Boston, MA 02111-1307, USA. */ | |
| #include "defs.h" | |
| #include "serial.h" | |
| #include "target.h" | |
| #include "xmodem.h" | |
| /* These definitions are for xmodem protocol. */ | |
| #define SOH 0x01 | |
| #define STX 0x02 | |
| #define ACK 0x06 | |
| #define NAK 0x15 | |
| #define EOT 0x04 | |
| #define CANCEL 0x18 | |
| static int blknum; /* XMODEM block number */ | |
| static int crcflag; /* Sez we are using CRC's instead of cksums */ | |
| static int | |
| readchar (desc, timeout) | |
| serial_t desc; | |
| int timeout; | |
| { | |
| int c; | |
| c = SERIAL_READCHAR (desc, timeout); | |
| if (remote_debug > 0) | |
| fputc_unfiltered (c, gdb_stdlog); | |
| if (c >= 0) | |
| return c; | |
| if (c == SERIAL_TIMEOUT) | |
| error ("Timeout reading from remote system."); | |
| perror_with_name ("xmodem.c:readchar()"); | |
| } | |
| #define CRC16 0x1021 /* Generator polynomial (X^16 + X^12 + X^5 + 1) */ | |
| static unsigned short *crctab; | |
| /* Call this to init the fast CRC-16 calculation table. */ | |
| static void | |
| crcinit () | |
| { | |
| static int crctab_inited = 0; | |
| int val; | |
| if (crctab_inited == 1) | |
| return; | |
| crctab = xmalloc (256 * sizeof (short)); | |
| for (val = 0; val <= 255; val++) | |
| { | |
| int i; | |
| unsigned int crc; | |
| crc = val << 8; | |
| for (i = 0; i < 8; ++i) | |
| { | |
| crc <<= 1; | |
| if (crc & 0x10000) | |
| crc ^= CRC16; | |
| } | |
| crctab[val] = crc; | |
| } | |
| crctab_inited = 1; | |
| } | |
| /* Calculate a CRC-16 for the LEN byte message pointed at by P. */ | |
| static unsigned short | |
| docrc (p, len) | |
| unsigned char *p; | |
| int len; | |
| { | |
| unsigned short crc = 0; | |
| while (len-- > 0) | |
| crc = (crc << 8) ^ crctab[(crc >> 8) ^ *p++]; | |
| return crc; | |
| } | |
| /* Start up the transmit process. Reset state variables. Wait for receiver to | |
| send NAK or CRC request. */ | |
| int | |
| xmodem_init_xfer (desc) | |
| serial_t desc; | |
| { | |
| int c; | |
| int i; | |
| blknum = 1; | |
| crcflag = 0; | |
| crcinit (); | |
| for (i = 1; i <= 10; i++) | |
| { | |
| c = readchar (desc, 6); | |
| switch (c) | |
| { | |
| case 'C': | |
| crcflag = 1; | |
| case NAK: | |
| return 0; | |
| default: | |
| fprintf_unfiltered (gdb_stderr, "xmodem_init_xfer: Got unexpected character %c (0%o)\n", c, c); | |
| continue; | |
| case CANCEL: /* target aborted load */ | |
| fprintf_unfiltered (gdb_stderr, "Got a CANCEL from the target.\n"); | |
| continue; | |
| } | |
| } | |
| error ("xmodem_init_xfer: Too many unexpected characters."); | |
| } | |
| /* Take 128 bytes of data and make a packet out of it. | |
| * Each packet looks like this: | |
| * +-----+-------+-------+------+-----+ | |
| * | SOH | Seq1. | Seq2. | data | SUM | | |
| * +-----+-------+-------+------+-----+ | |
| * SOH = 0x01 | |
| * Seq1 = The sequence number. | |
| * Seq2 = The complement of the sequence number. | |
| * Data = A 128 bytes of data. | |
| * SUM = Add the contents of the 128 bytes and use the low-order | |
| * 8 bits of the result. | |
| * | |
| * send_xmodem_packet fills in the XMODEM fields of PACKET and sends it to the | |
| * remote system. PACKET must be XMODEM_PACKETSIZE bytes long. The data must | |
| * start 3 bytes after the beginning of the packet to leave room for the | |
| * XMODEM header. LEN is the length of the data portion of the packet (and | |
| * must be <= 128 bytes). If it is < 128 bytes, ^Z padding will be added. | |
| */ | |
| void | |
| xmodem_send_packet (desc, packet, len, hashmark) | |
| serial_t desc; | |
| unsigned char *packet; | |
| int len; | |
| int hashmark; | |
| { | |
| int i; | |
| int retries; | |
| int pktlen; | |
| int datasize; | |
| /* build the packet header */ | |
| packet[1] = blknum; | |
| packet[2] = ~blknum; | |
| blknum++; | |
| if (len <= XMODEM_DATASIZE) | |
| { | |
| packet[0] = SOH; | |
| datasize = XMODEM_DATASIZE; | |
| } | |
| else if (len <= XMODEM_1KDATASIZE) | |
| { | |
| packet[0] = STX; | |
| datasize = XMODEM_1KDATASIZE; | |
| } | |
| else | |
| abort (); /* Packet way too large */ | |
| /* Add ^Z padding if packet < 128 (or 1024) bytes */ | |
| memset (packet + 3 + len, '\026', datasize - len); | |
| if (crcflag) | |
| { | |
| int crc; | |
| crc = docrc (packet + 3, datasize); | |
| packet[3 + datasize] = crc >> 8; | |
| packet[3 + datasize + 1] = crc; | |
| pktlen = datasize + 5; | |
| } | |
| else | |
| { | |
| int sum; | |
| sum = 0; | |
| for (i = 3; i < datasize + 3; i++) | |
| sum += packet[i]; | |
| packet[3 + datasize] = sum; /* add the checksum */ | |
| pktlen = datasize + 4; | |
| } | |
| for (retries = 3; retries >= 0; retries--) | |
| { | |
| int c; | |
| SERIAL_WRITE (desc, packet, pktlen); | |
| c = readchar (desc, 3); | |
| switch (c) | |
| { | |
| case ACK: | |
| return; | |
| case NAK: | |
| if (!hashmark) | |
| continue; | |
| putchar_unfiltered ('-'); | |
| gdb_flush (gdb_stdout); | |
| continue; | |
| case CANCEL: | |
| error ("xmodem_send_packet: Transfer aborted by receiver."); | |
| default: | |
| fprintf_unfiltered (gdb_stderr, "xmodem_send_packet: Got unexpected character %c (0%o)\n", c, c); | |
| continue; | |
| } | |
| } | |
| SERIAL_WRITE (desc, "\004", 1); /* Send an EOT */ | |
| error ("xmodem_send_packet: Excessive retries."); | |
| } | |
| /* Finish off the transfer. Send out the EOT, and wait for an ACK. */ | |
| void | |
| xmodem_finish_xfer (desc) | |
| serial_t desc; | |
| { | |
| int retries; | |
| for (retries = 10; retries >= 0; retries--) | |
| { | |
| int c; | |
| SERIAL_WRITE (desc, "\004", 1); /* Send an EOT */ | |
| c = readchar (desc, 3); | |
| switch (c) | |
| { | |
| case ACK: | |
| return; | |
| case NAK: | |
| continue; | |
| case CANCEL: | |
| error ("xmodem_finish_xfer: Transfer aborted by receiver."); | |
| default: | |
| fprintf_unfiltered (gdb_stderr, "xmodem_send_packet: Got unexpected character %c (0%o)\n", c, c); | |
| continue; | |
| } | |
| } | |
| error ("xmodem_finish_xfer: Excessive retries."); | |
| } |