xyzModem.c 17.7 KB
Newer Older
Wolfgang Denk's avatar
Wolfgang Denk committed
1 2 3 4 5 6 7 8
/*
 *==========================================================================
 *
 *      xyzModem.c
 *
 *      RedBoot stream handler for xyzModem protocol
 *
 *==========================================================================
9
 * SPDX-License-Identifier:	eCos-2.0
Wolfgang Denk's avatar
Wolfgang Denk committed
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *==========================================================================
 *#####DESCRIPTIONBEGIN####
 *
 * Author(s):    gthomas
 * Contributors: gthomas, tsmith, Yoshinori Sato
 * Date:         2000-07-14
 * Purpose:
 * Description:
 *
 * This code is part of RedBoot (tm).
 *
 *####DESCRIPTIONEND####
 *
 *==========================================================================
 */
25 26 27 28 29
#include <common.h>
#include <xyzModem.h>
#include <stdarg.h>
#include <crc.h>

Wolfgang Denk's avatar
Wolfgang Denk committed
30
/* Assumption - run xyzModem protocol over the console port */
31

Wolfgang Denk's avatar
Wolfgang Denk committed
32
/* Values magic to the protocol */
33 34 35 36 37 38 39
#define SOH 0x01
#define STX 0x02
#define EOT 0x04
#define ACK 0x06
#define BSP 0x08
#define NAK 0x15
#define CAN 0x18
Wolfgang Denk's avatar
Wolfgang Denk committed
40
#define EOF 0x1A		/* ^Z for DOS officionados */
41 42 43

#define USE_YMODEM_LENGTH

Wolfgang Denk's avatar
Wolfgang Denk committed
44
/* Data & state local to the protocol */
Wolfgang Denk's avatar
Wolfgang Denk committed
45 46
static struct
{
47
#ifdef REDBOOT
Wolfgang Denk's avatar
Wolfgang Denk committed
48
  hal_virtual_comm_table_t *__chan;
49
#else
Wolfgang Denk's avatar
Wolfgang Denk committed
50
  int *__chan;
51
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
52 53 54 55 56 57
  unsigned char pkt[1024], *bufp;
  unsigned char blk, cblk, crc1, crc2;
  unsigned char next_blk;	/* Expected block */
  int len, mode, total_retries;
  int total_SOH, total_STX, total_CAN;
  bool crc_mode, at_eof, tx_ack;
58
#ifdef USE_YMODEM_LENGTH
Wolfgang Denk's avatar
Wolfgang Denk committed
59
  unsigned long file_length, read_length;
60 61 62
#endif
} xyz;

Wolfgang Denk's avatar
Wolfgang Denk committed
63
#define xyzModem_CHAR_TIMEOUT            2000	/* 2 seconds */
64 65
#define xyzModem_MAX_RETRIES             20
#define xyzModem_MAX_RETRIES_WITH_CRC    10
Wolfgang Denk's avatar
Wolfgang Denk committed
66
#define xyzModem_CAN_COUNT                3	/* Wait for 3 CAN before quitting */
67 68


Wolfgang Denk's avatar
Wolfgang Denk committed
69
#ifndef REDBOOT			/*SB */
70
typedef int cyg_int32;
Kim Phillips's avatar
Kim Phillips committed
71
static int
Wolfgang Denk's avatar
Wolfgang Denk committed
72 73
CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c)
{
74 75 76

  ulong now = get_timer(0);
  while (!tstc ())
Wolfgang Denk's avatar
Wolfgang Denk committed
77
    {
78 79
      if (get_timer(now) > xyzModem_CHAR_TIMEOUT)
        break;
Wolfgang Denk's avatar
Wolfgang Denk committed
80 81 82 83 84 85 86
    }
  if (tstc ())
    {
      *c = getc ();
      return 1;
    }
  return 0;
87 88
}

Kim Phillips's avatar
Kim Phillips committed
89
static void
Wolfgang Denk's avatar
Wolfgang Denk committed
90 91 92
CYGACC_COMM_IF_PUTC (char x, char y)
{
  putc (y);
93 94
}

Wolfgang Denk's avatar
Wolfgang Denk committed
95
/* Validate a hex character */
96
__inline__ static bool
Wolfgang Denk's avatar
Wolfgang Denk committed
97
_is_hex (char c)
98
{
Wolfgang Denk's avatar
Wolfgang Denk committed
99 100
  return (((c >= '0') && (c <= '9')) ||
	  ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')));
101 102
}

Wolfgang Denk's avatar
Wolfgang Denk committed
103
/* Convert a single hex nibble */
104
__inline__ static int
Wolfgang Denk's avatar
Wolfgang Denk committed
105
_from_hex (char c)
106
{
Wolfgang Denk's avatar
Wolfgang Denk committed
107 108 109 110 111 112 113 114 115
  int ret = 0;

  if ((c >= '0') && (c <= '9'))
    {
      ret = (c - '0');
    }
  else if ((c >= 'a') && (c <= 'f'))
    {
      ret = (c - 'a' + 0x0a);
116
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
117 118 119 120 121
  else if ((c >= 'A') && (c <= 'F'))
    {
      ret = (c - 'A' + 0x0A);
    }
  return ret;
122 123
}

Wolfgang Denk's avatar
Wolfgang Denk committed
124
/* Convert a character to lower case */
125
__inline__ static char
Wolfgang Denk's avatar
Wolfgang Denk committed
126
_tolower (char c)
127
{
Wolfgang Denk's avatar
Wolfgang Denk committed
128 129 130
  if ((c >= 'A') && (c <= 'Z'))
    {
      c = (c - 'A') + 'a';
131
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
132
  return c;
133 134
}

Wolfgang Denk's avatar
Wolfgang Denk committed
135
/* Parse (scan) a number */
Kim Phillips's avatar
Kim Phillips committed
136
static bool
Wolfgang Denk's avatar
Wolfgang Denk committed
137
parse_num (char *s, unsigned long *val, char **es, char *delim)
138
{
Wolfgang Denk's avatar
Wolfgang Denk committed
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
  bool first = true;
  int radix = 10;
  char c;
  unsigned long result = 0;
  int digit;

  while (*s == ' ')
    s++;
  while (*s)
    {
      if (first && (s[0] == '0') && (_tolower (s[1]) == 'x'))
	{
	  radix = 16;
	  s += 2;
	}
      first = false;
      c = *s++;
      if (_is_hex (c) && ((digit = _from_hex (c)) < radix))
	{
	  /* Valid digit */
159
#ifdef CYGPKG_HAL_MIPS
Wolfgang Denk's avatar
Wolfgang Denk committed
160 161 162 163 164 165 166
	  /* FIXME: tx49 compiler generates 0x2539018 for MUL which */
	  /* isn't any good. */
	  if (16 == radix)
	    result = result << 4;
	  else
	    result = 10 * result;
	  result += digit;
167
#else
Wolfgang Denk's avatar
Wolfgang Denk committed
168
	  result = (result * radix) + digit;
169
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183
	}
      else
	{
	  if (delim != (char *) 0)
	    {
	      /* See if this character is one of the delimiters */
	      char *dp = delim;
	      while (*dp && (c != *dp))
		dp++;
	      if (*dp)
		break;		/* Found a good delimiter */
	    }
	  return false;		/* Malformatted number */
	}
184
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
185 186 187 188
  *val = result;
  if (es != (char **) 0)
    {
      *es = s;
189
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
190
  return true;
191 192 193 194 195 196 197
}

#endif

#define USE_SPRINTF
#ifdef DEBUG
#ifndef USE_SPRINTF
Wolfgang Denk's avatar
Wolfgang Denk committed
198 199 200 201 202
/*
 * Note: this debug setup only works if the target platform has two serial ports
 * available so that the other one (currently only port 1) can be used for debug
 * messages.
 */
203
static int
Wolfgang Denk's avatar
Wolfgang Denk committed
204
zm_dprintf (char *fmt, ...)
205
{
Wolfgang Denk's avatar
Wolfgang Denk committed
206 207
  int cur_console;
  va_list args;
208

Wolfgang Denk's avatar
Wolfgang Denk committed
209
  va_start (args, fmt);
210
#ifdef REDBOOT
Wolfgang Denk's avatar
Wolfgang Denk committed
211 212 213 214
  cur_console =
    CYGACC_CALL_IF_SET_CONSOLE_COMM
    (CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
  CYGACC_CALL_IF_SET_CONSOLE_COMM (1);
215
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
216
  diag_vprintf (fmt, args);
217
#ifdef REDBOOT
Wolfgang Denk's avatar
Wolfgang Denk committed
218
  CYGACC_CALL_IF_SET_CONSOLE_COMM (cur_console);
219 220 221 222
#endif
}

static void
Wolfgang Denk's avatar
Wolfgang Denk committed
223
zm_flush (void)
224 225 226 227
{
}

#else
Wolfgang Denk's avatar
Wolfgang Denk committed
228 229 230
/*
 * Note: this debug setup works by storing the strings in a fixed buffer
 */
231 232
#define FINAL
#ifdef FINAL
Wolfgang Denk's avatar
Wolfgang Denk committed
233 234
static char *zm_out = (char *) 0x00380000;
static char *zm_out_start = (char *) 0x00380000;
235 236
#else
static char zm_buf[8192];
Wolfgang Denk's avatar
Wolfgang Denk committed
237
static char *zm_out = zm_buf;
238 239 240 241
static char *zm_out_start = zm_buf;

#endif
static int
Wolfgang Denk's avatar
Wolfgang Denk committed
242
zm_dprintf (char *fmt, ...)
243
{
Wolfgang Denk's avatar
Wolfgang Denk committed
244 245
  int len;
  va_list args;
246

Wolfgang Denk's avatar
Wolfgang Denk committed
247 248 249 250
  va_start (args, fmt);
  len = diag_vsprintf (zm_out, fmt, args);
  zm_out += len;
  return len;
251 252 253
}

static void
Wolfgang Denk's avatar
Wolfgang Denk committed
254
zm_flush (void)
255 256
{
#ifdef REDBOOT
Wolfgang Denk's avatar
Wolfgang Denk committed
257 258 259
  char *p = zm_out_start;
  while (*p)
    mon_write_char (*p++);
260
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
261
  zm_out = zm_out_start;
262 263 264 265
}
#endif

static void
Wolfgang Denk's avatar
Wolfgang Denk committed
266
zm_dump_buf (void *buf, int len)
267 268
{
#ifdef REDBOOT
Wolfgang Denk's avatar
Wolfgang Denk committed
269
  diag_vdump_buf_with_offset (zm_dprintf, buf, len, 0);
270 271 272 273 274 275 276 277 278
#else

#endif
}

static unsigned char zm_buf[2048];
static unsigned char *zm_bp;

static void
Wolfgang Denk's avatar
Wolfgang Denk committed
279
zm_new (void)
280
{
Wolfgang Denk's avatar
Wolfgang Denk committed
281
  zm_bp = zm_buf;
282 283 284
}

static void
Wolfgang Denk's avatar
Wolfgang Denk committed
285
zm_save (unsigned char c)
286
{
Wolfgang Denk's avatar
Wolfgang Denk committed
287
  *zm_bp++ = c;
288 289 290
}

static void
Wolfgang Denk's avatar
Wolfgang Denk committed
291
zm_dump (int line)
292
{
Wolfgang Denk's avatar
Wolfgang Denk committed
293 294
  zm_dprintf ("Packet at line: %d\n", line);
  zm_dump_buf (zm_buf, zm_bp - zm_buf);
295 296 297 298 299 300 301
}

#define ZM_DEBUG(x) x
#else
#define ZM_DEBUG(x)
#endif

Wolfgang Denk's avatar
Wolfgang Denk committed
302
/* Wait for the line to go idle */
303
static void
Wolfgang Denk's avatar
Wolfgang Denk committed
304
xyzModem_flush (void)
305
{
Wolfgang Denk's avatar
Wolfgang Denk committed
306 307 308 309 310 311 312
  int res;
  char c;
  while (true)
    {
      res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
      if (!res)
	return;
313 314 315 316
    }
}

static int
Wolfgang Denk's avatar
Wolfgang Denk committed
317
xyzModem_get_hdr (void)
318
{
Wolfgang Denk's avatar
Wolfgang Denk committed
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
  char c;
  int res;
  bool hdr_found = false;
  int i, can_total, hdr_chars;
  unsigned short cksum;

  ZM_DEBUG (zm_new ());
  /* Find the start of a header */
  can_total = 0;
  hdr_chars = 0;

  if (xyz.tx_ack)
    {
      CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
      xyz.tx_ack = false;
334
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
  while (!hdr_found)
    {
      res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
      ZM_DEBUG (zm_save (c));
      if (res)
	{
	  hdr_chars++;
	  switch (c)
	    {
	    case SOH:
	      xyz.total_SOH++;
	    case STX:
	      if (c == STX)
		xyz.total_STX++;
	      hdr_found = true;
	      break;
	    case CAN:
	      xyz.total_CAN++;
	      ZM_DEBUG (zm_dump (__LINE__));
	      if (++can_total == xyzModem_CAN_COUNT)
		{
		  return xyzModem_cancel;
		}
	      else
		{
		  /* Wait for multiple CAN to avoid early quits */
		  break;
		}
	    case EOT:
	      /* EOT only supported if no noise */
	      if (hdr_chars == 1)
		{
		  CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
		  ZM_DEBUG (zm_dprintf ("ACK on EOT #%d\n", __LINE__));
		  ZM_DEBUG (zm_dump (__LINE__));
		  return xyzModem_eof;
		}
	    default:
	      /* Ignore, waiting for start of header */
	      ;
	    }
	}
      else
	{
	  /* Data stream timed out */
	  xyzModem_flush ();	/* Toss any current input */
	  ZM_DEBUG (zm_dump (__LINE__));
	  CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
	  return xyzModem_timeout;
	}
385 386
    }

Wolfgang Denk's avatar
Wolfgang Denk committed
387 388 389 390 391 392 393
  /* Header found, now read the data */
  res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.blk);
  ZM_DEBUG (zm_save (xyz.blk));
  if (!res)
    {
      ZM_DEBUG (zm_dump (__LINE__));
      return xyzModem_timeout;
394
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
395 396 397 398 399 400
  res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.cblk);
  ZM_DEBUG (zm_save (xyz.cblk));
  if (!res)
    {
      ZM_DEBUG (zm_dump (__LINE__));
      return xyzModem_timeout;
401
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
  xyz.len = (c == SOH) ? 128 : 1024;
  xyz.bufp = xyz.pkt;
  for (i = 0; i < xyz.len; i++)
    {
      res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
      ZM_DEBUG (zm_save (c));
      if (res)
	{
	  xyz.pkt[i] = c;
	}
      else
	{
	  ZM_DEBUG (zm_dump (__LINE__));
	  return xyzModem_timeout;
	}
417
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
418 419 420 421 422 423
  res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc1);
  ZM_DEBUG (zm_save (xyz.crc1));
  if (!res)
    {
      ZM_DEBUG (zm_dump (__LINE__));
      return xyzModem_timeout;
424
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
425 426 427 428 429 430 431 432 433
  if (xyz.crc_mode)
    {
      res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc2);
      ZM_DEBUG (zm_save (xyz.crc2));
      if (!res)
	{
	  ZM_DEBUG (zm_dump (__LINE__));
	  return xyzModem_timeout;
	}
434
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
435 436 437 438 439 440 441 442 443 444
  ZM_DEBUG (zm_dump (__LINE__));
  /* Validate the message */
  if ((xyz.blk ^ xyz.cblk) != (unsigned char) 0xFF)
    {
      ZM_DEBUG (zm_dprintf
		("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk,
		 (xyz.blk ^ xyz.cblk)));
      ZM_DEBUG (zm_dump_buf (xyz.pkt, xyz.len));
      xyzModem_flush ();
      return xyzModem_frame;
445
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
446 447 448
  /* Verify checksum/CRC */
  if (xyz.crc_mode)
    {
449
      cksum = crc16_ccitt(0, xyz.pkt, xyz.len);
Wolfgang Denk's avatar
Wolfgang Denk committed
450 451 452 453 454 455
      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;
	}
456
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
  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;
474 475
}

Wolfgang Denk's avatar
Wolfgang Denk committed
476
int
Wolfgang Denk's avatar
Wolfgang Denk committed
477
xyzModem_stream_open (connection_info_t * info, int *err)
478
{
479
#ifdef REDBOOT
Wolfgang Denk's avatar
Wolfgang Denk committed
480
  int console_chan;
481
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
482 483 484
  int stat = 0;
  int retries = xyzModem_MAX_RETRIES;
  int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
485

Wolfgang Denk's avatar
Wolfgang Denk committed
486
/*    ZM_DEBUG(zm_out = zm_out_start); */
487
#ifdef xyzModem_zmodem
Wolfgang Denk's avatar
Wolfgang Denk committed
488 489 490 491
  if (info->mode == xyzModem_zmodem)
    {
      *err = xyzModem_noZmodem;
      return -1;
492 493 494 495
    }
#endif

#ifdef REDBOOT
Wolfgang Denk's avatar
Wolfgang Denk committed
496 497 498 499 500 501 502
  /* 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);
503
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
504 505 506 507 508
  else
    {
      CYGACC_CALL_IF_SET_CONSOLE_COMM (console_chan);
    }
  xyz.__chan = CYGACC_CALL_IF_CONSOLE_PROCS ();
509

Wolfgang Denk's avatar
Wolfgang Denk committed
510 511 512
  CYGACC_CALL_IF_SET_CONSOLE_COMM (console_chan);
  CYGACC_COMM_IF_CONTROL (*xyz.__chan, __COMMCTL_SET_TIMEOUT,
			  xyzModem_CHAR_TIMEOUT);
513
#else
Wolfgang Denk's avatar
Wolfgang Denk committed
514
/* TODO: CHECK ! */
515
  int dummy = 0;
Wolfgang Denk's avatar
Wolfgang Denk committed
516
  xyz.__chan = &dummy;
517
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
518 519 520 521 522 523 524 525 526
  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;
527
#ifdef USE_YMODEM_LENGTH
Wolfgang Denk's avatar
Wolfgang Denk committed
528 529
  xyz.read_length = 0;
  xyz.file_length = 0;
530
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
531

Wolfgang Denk's avatar
Wolfgang Denk committed
532
  CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
533

Wolfgang Denk's avatar
Wolfgang Denk committed
534 535 536 537 538
  if (xyz.mode == xyzModem_xmodem)
    {
      /* X-modem doesn't have an information header - exit here */
      xyz.next_blk = 1;
      return 0;
539 540
    }

Wolfgang Denk's avatar
Wolfgang Denk committed
541 542 543 544 545 546 547 548
  while (retries-- > 0)
    {
      stat = xyzModem_get_hdr ();
      if (stat == 0)
	{
	  /* Y-modem file information header */
	  if (xyz.blk == 0)
	    {
549
#ifdef USE_YMODEM_LENGTH
Wolfgang Denk's avatar
Wolfgang Denk committed
550 551 552 553
	      /* skip filename */
	      while (*xyz.bufp++);
	      /* get the length */
	      parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " ");
554
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
	      /* 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;
	}
575
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
576 577 578
  *err = stat;
  ZM_DEBUG (zm_flush ());
  return -1;
579 580
}

Wolfgang Denk's avatar
Wolfgang Denk committed
581
int
Wolfgang Denk's avatar
Wolfgang Denk committed
582
xyzModem_stream_read (char *buf, int size, int *err)
583
{
Wolfgang Denk's avatar
Wolfgang Denk committed
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
  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;
606 607

#if defined(xyzModem_zmodem) || defined(USE_YMODEM_LENGTH)
Wolfgang Denk's avatar
Wolfgang Denk committed
608 609
		      if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0)
			{
610
#else
Wolfgang Denk's avatar
Wolfgang Denk committed
611 612
		      if (1)
			{
613
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
614 615 616 617 618 619 620 621 622 623 624 625 626
			  /* Data blocks can be padded with ^Z (EOF) characters */
			  /* This code tries to detect and remove them */
			  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--;
				}
			    }
			}
627 628

#ifdef USE_YMODEM_LENGTH
Wolfgang Denk's avatar
Wolfgang Denk committed
629 630 631 632 633 634 635 636 637 638 639 640 641 642
		      /*
		       * 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);
			    }
			}
643
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
		      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;
	}
702
    }
Wolfgang Denk's avatar
Wolfgang Denk committed
703
  return total;
704 705 706
}

void
Wolfgang Denk's avatar
Wolfgang Denk committed
707
xyzModem_stream_close (int *err)
708
{
Wolfgang Denk's avatar
Wolfgang Denk committed
709 710 711 712 713
  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 ());
714 715
}

Wolfgang Denk's avatar
Wolfgang Denk committed
716 717
/* Need to be able to clean out the input buffer, so have to take the */
/* getc */
Wolfgang Denk's avatar
Wolfgang Denk committed
718 719
void
xyzModem_stream_terminate (bool abort, int (*getc) (void))
720 721 722
{
  int c;

Wolfgang Denk's avatar
Wolfgang Denk committed
723 724 725 726 727
  if (abort)
    {
      ZM_DEBUG (zm_dprintf ("!!!! TRANSFER ABORT !!!!\n"));
      switch (xyz.mode)
	{
728 729
	case xyzModem_xmodem:
	case xyzModem_ymodem:
Wolfgang Denk's avatar
Wolfgang Denk committed
730 731
	  /* 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. */
Wolfgang Denk's avatar
Wolfgang Denk committed
732 733 734 735 736 737 738 739
	  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);
Wolfgang Denk's avatar
Wolfgang Denk committed
740
	  /* Now consume the rest of what's waiting on the line. */
Wolfgang Denk's avatar
Wolfgang Denk committed
741 742 743 744
	  ZM_DEBUG (zm_dprintf ("Flushing serial line.\n"));
	  xyzModem_flush ();
	  xyz.at_eof = true;
	  break;
745 746
#ifdef xyzModem_zmodem
	case xyzModem_zmodem:
Wolfgang Denk's avatar
Wolfgang Denk committed
747
	  /* Might support it some day I suppose. */
748
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
749 750 751 752 753 754
	  break;
	}
    }
  else
    {
      ZM_DEBUG (zm_dprintf ("Engaging cleanup mode...\n"));
Wolfgang Denk's avatar
Wolfgang Denk committed
755 756
      /*
       * Consume any trailing crap left in the inbuffer from
757
       * previous received blocks. Since very few files are an exact multiple
Wolfgang Denk's avatar
Wolfgang Denk committed
758 759 760
       * 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.
       */
Wolfgang Denk's avatar
Wolfgang Denk committed
761
      ZM_DEBUG (zm_dprintf ("Trailing gunk:\n"));
762 763
      while ((c = (*getc) ()) > -1)
        ;
Wolfgang Denk's avatar
Wolfgang Denk committed
764
      ZM_DEBUG (zm_dprintf ("\n"));
Wolfgang Denk's avatar
Wolfgang Denk committed
765 766 767 768 769
      /*
       * Make a small delay to give terminal programs like minicom
       * time to get control again after their file transfer program
       * exits.
       */
Wolfgang Denk's avatar
Wolfgang Denk committed
770 771
      CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
    }
772 773 774
}

char *
Wolfgang Denk's avatar
Wolfgang Denk committed
775
xyzModem_error (int err)
776
{
Wolfgang Denk's avatar
Wolfgang Denk committed
777 778
  switch (err)
    {
779
    case xyzModem_access:
Wolfgang Denk's avatar
Wolfgang Denk committed
780 781
      return "Can't access file";
      break;
782
    case xyzModem_noZmodem:
Wolfgang Denk's avatar
Wolfgang Denk committed
783 784
      return "Sorry, zModem not available yet";
      break;
785
    case xyzModem_timeout:
Wolfgang Denk's avatar
Wolfgang Denk committed
786 787
      return "Timed out";
      break;
788
    case xyzModem_eof:
Wolfgang Denk's avatar
Wolfgang Denk committed
789 790
      return "End of file";
      break;
791
    case xyzModem_cancel:
Wolfgang Denk's avatar
Wolfgang Denk committed
792 793
      return "Cancelled";
      break;
794
    case xyzModem_frame:
Wolfgang Denk's avatar
Wolfgang Denk committed
795 796
      return "Invalid framing";
      break;
797
    case xyzModem_cksum:
Wolfgang Denk's avatar
Wolfgang Denk committed
798 799
      return "CRC/checksum error";
      break;
800
    case xyzModem_sequence:
Wolfgang Denk's avatar
Wolfgang Denk committed
801 802
      return "Block sequence error";
      break;
803
    default:
Wolfgang Denk's avatar
Wolfgang Denk committed
804 805
      return "Unknown error";
      break;
806 807 808
    }
}

Wolfgang Denk's avatar
Wolfgang Denk committed
809 810 811
/*
 * RedBoot interface
 */
Wolfgang Denk's avatar
Wolfgang Denk committed
812 813 814 815 816 817
#if 0				/* SB */
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);
Wolfgang Denk's avatar
Wolfgang Denk committed
818
#endif