#include "defs.h"
#include "serial.h"
#include "target.h"
#include "xmodem.h"
#define SOH 0x01
#define STX 0x02
#define ACK 0x06
#define NAK 0x15
#define EOT 0x04
#define CANCEL 0x18
static int blknum;
static int crcflag;
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
static unsigned short *crctab;
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;
}
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;
}
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:
fprintf_unfiltered (gdb_stderr, "Got a CANCEL from the target.\n");
continue;
}
}
error ("xmodem_init_xfer: Too many unexpected characters.");
}
void
xmodem_send_packet (struct serial *desc, unsigned char *packet, int len, int hashmark)
{
int i;
int retries;
int pktlen;
int datasize;
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");
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;
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);
error ("xmodem_send_packet: Excessive retries.");
}
void
xmodem_finish_xfer (struct serial *desc)
{
int retries;
for (retries = 10; retries >= 0; retries--)
{
int c;
serial_write (desc, "\004", 1);
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.");
}