| /* XMODEM support for GDB, the GNU debugger. |
| Copyright 1995, 2000, 2001 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 (struct serial *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 (void) |
| { |
| 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 (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 (struct serial *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 (struct serial *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 |
| internal_error (__FILE__, __LINE__, "failed internal consistency check"); /* 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 (struct serial *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."); |
| } |