xref: /OK3568_Linux_fs/u-boot/common/xyzModem.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  *==========================================================================
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  *      xyzModem.c
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  *      RedBoot stream handler for xyzModem protocol
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  *==========================================================================
9*4882a593Smuzhiyun  * SPDX-License-Identifier:	eCos-2.0
10*4882a593Smuzhiyun  *==========================================================================
11*4882a593Smuzhiyun  *#####DESCRIPTIONBEGIN####
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * Author(s):    gthomas
14*4882a593Smuzhiyun  * Contributors: gthomas, tsmith, Yoshinori Sato
15*4882a593Smuzhiyun  * Date:         2000-07-14
16*4882a593Smuzhiyun  * Purpose:
17*4882a593Smuzhiyun  * Description:
18*4882a593Smuzhiyun  *
19*4882a593Smuzhiyun  * This code is part of RedBoot (tm).
20*4882a593Smuzhiyun  *
21*4882a593Smuzhiyun  *####DESCRIPTIONEND####
22*4882a593Smuzhiyun  *
23*4882a593Smuzhiyun  *==========================================================================
24*4882a593Smuzhiyun  */
25*4882a593Smuzhiyun #include <common.h>
26*4882a593Smuzhiyun #include <xyzModem.h>
27*4882a593Smuzhiyun #include <stdarg.h>
28*4882a593Smuzhiyun #include <crc.h>
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun /* Assumption - run xyzModem protocol over the console port */
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun /* Values magic to the protocol */
33*4882a593Smuzhiyun #define SOH 0x01
34*4882a593Smuzhiyun #define STX 0x02
35*4882a593Smuzhiyun #define EOT 0x04
36*4882a593Smuzhiyun #define ACK 0x06
37*4882a593Smuzhiyun #define BSP 0x08
38*4882a593Smuzhiyun #define NAK 0x15
39*4882a593Smuzhiyun #define CAN 0x18
40*4882a593Smuzhiyun #define EOF 0x1A		/* ^Z for DOS officionados */
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun /* Data & state local to the protocol */
43*4882a593Smuzhiyun static struct
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun   int *__chan;
46*4882a593Smuzhiyun   unsigned char pkt[1024], *bufp;
47*4882a593Smuzhiyun   unsigned char blk, cblk, crc1, crc2;
48*4882a593Smuzhiyun   unsigned char next_blk;	/* Expected block */
49*4882a593Smuzhiyun   int len, mode, total_retries;
50*4882a593Smuzhiyun   int total_SOH, total_STX, total_CAN;
51*4882a593Smuzhiyun   bool crc_mode, at_eof, tx_ack;
52*4882a593Smuzhiyun   unsigned long file_length, read_length;
53*4882a593Smuzhiyun } xyz;
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun #define _xyzModem_CHAR_TIMEOUT            2000	/* 2 seconds */
56*4882a593Smuzhiyun #define xyzModem_MAX_RETRIES             20
57*4882a593Smuzhiyun #define xyzModem_MAX_RETRIES_WITH_CRC    10
58*4882a593Smuzhiyun #define xyzModem_CAN_COUNT                3	/* Wait for 3 CAN before quitting */
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun int xyzModem_CHAR_TIMEOUT = _xyzModem_CHAR_TIMEOUT;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun typedef int cyg_int32;
63*4882a593Smuzhiyun int
CYGACC_COMM_IF_GETC_TIMEOUT(char chan,char * c)64*4882a593Smuzhiyun CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun   ulong now = get_timer(0);
68*4882a593Smuzhiyun   while (!tstc ())
69*4882a593Smuzhiyun     {
70*4882a593Smuzhiyun       if (get_timer(now) > xyzModem_CHAR_TIMEOUT)
71*4882a593Smuzhiyun         break;
72*4882a593Smuzhiyun     }
73*4882a593Smuzhiyun   if (tstc ())
74*4882a593Smuzhiyun     {
75*4882a593Smuzhiyun       *c = getc ();
76*4882a593Smuzhiyun       return 1;
77*4882a593Smuzhiyun     }
78*4882a593Smuzhiyun   return 0;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun void
CYGACC_COMM_IF_PUTC(char x,char y)82*4882a593Smuzhiyun CYGACC_COMM_IF_PUTC (char x, char y)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun   putc (y);
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun /* Validate a hex character */
88*4882a593Smuzhiyun __inline__ static bool
_is_hex(char c)89*4882a593Smuzhiyun _is_hex (char c)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun   return (((c >= '0') && (c <= '9')) ||
92*4882a593Smuzhiyun 	  ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')));
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun /* Convert a single hex nibble */
96*4882a593Smuzhiyun __inline__ static int
_from_hex(char c)97*4882a593Smuzhiyun _from_hex (char c)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun   int ret = 0;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun   if ((c >= '0') && (c <= '9'))
102*4882a593Smuzhiyun     {
103*4882a593Smuzhiyun       ret = (c - '0');
104*4882a593Smuzhiyun     }
105*4882a593Smuzhiyun   else if ((c >= 'a') && (c <= 'f'))
106*4882a593Smuzhiyun     {
107*4882a593Smuzhiyun       ret = (c - 'a' + 0x0a);
108*4882a593Smuzhiyun     }
109*4882a593Smuzhiyun   else if ((c >= 'A') && (c <= 'F'))
110*4882a593Smuzhiyun     {
111*4882a593Smuzhiyun       ret = (c - 'A' + 0x0A);
112*4882a593Smuzhiyun     }
113*4882a593Smuzhiyun   return ret;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun /* Convert a character to lower case */
117*4882a593Smuzhiyun __inline__ static char
_tolower(char c)118*4882a593Smuzhiyun _tolower (char c)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun   if ((c >= 'A') && (c <= 'Z'))
121*4882a593Smuzhiyun     {
122*4882a593Smuzhiyun       c = (c - 'A') + 'a';
123*4882a593Smuzhiyun     }
124*4882a593Smuzhiyun   return c;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun /* Parse (scan) a number */
128*4882a593Smuzhiyun static bool
parse_num(char * s,unsigned long * val,char ** es,char * delim)129*4882a593Smuzhiyun parse_num (char *s, unsigned long *val, char **es, char *delim)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun   bool first = true;
132*4882a593Smuzhiyun   int radix = 10;
133*4882a593Smuzhiyun   char c;
134*4882a593Smuzhiyun   unsigned long result = 0;
135*4882a593Smuzhiyun   int digit;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun   while (*s == ' ')
138*4882a593Smuzhiyun     s++;
139*4882a593Smuzhiyun   while (*s)
140*4882a593Smuzhiyun     {
141*4882a593Smuzhiyun       if (first && (s[0] == '0') && (_tolower (s[1]) == 'x'))
142*4882a593Smuzhiyun 	{
143*4882a593Smuzhiyun 	  radix = 16;
144*4882a593Smuzhiyun 	  s += 2;
145*4882a593Smuzhiyun 	}
146*4882a593Smuzhiyun       first = false;
147*4882a593Smuzhiyun       c = *s++;
148*4882a593Smuzhiyun       if (_is_hex (c) && ((digit = _from_hex (c)) < radix))
149*4882a593Smuzhiyun 	{
150*4882a593Smuzhiyun 	  /* Valid digit */
151*4882a593Smuzhiyun 	  result = (result * radix) + digit;
152*4882a593Smuzhiyun 	}
153*4882a593Smuzhiyun       else
154*4882a593Smuzhiyun 	{
155*4882a593Smuzhiyun 	  if (delim != (char *) 0)
156*4882a593Smuzhiyun 	    {
157*4882a593Smuzhiyun 	      /* See if this character is one of the delimiters */
158*4882a593Smuzhiyun 	      char *dp = delim;
159*4882a593Smuzhiyun 	      while (*dp && (c != *dp))
160*4882a593Smuzhiyun 		dp++;
161*4882a593Smuzhiyun 	      if (*dp)
162*4882a593Smuzhiyun 		break;		/* Found a good delimiter */
163*4882a593Smuzhiyun 	    }
164*4882a593Smuzhiyun 	  return false;		/* Malformatted number */
165*4882a593Smuzhiyun 	}
166*4882a593Smuzhiyun     }
167*4882a593Smuzhiyun   *val = result;
168*4882a593Smuzhiyun   if (es != (char **) 0)
169*4882a593Smuzhiyun     {
170*4882a593Smuzhiyun       *es = s;
171*4882a593Smuzhiyun     }
172*4882a593Smuzhiyun   return true;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun #ifdef DEBUG
177*4882a593Smuzhiyun /*
178*4882a593Smuzhiyun  * Note: this debug setup works by storing the strings in a fixed buffer
179*4882a593Smuzhiyun  */
180*4882a593Smuzhiyun static char zm_debug_buf[8192];
181*4882a593Smuzhiyun static char *zm_out = zm_debug_buf;
182*4882a593Smuzhiyun static char *zm_out_start = zm_debug_buf;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun static int
zm_dprintf(char * fmt,...)185*4882a593Smuzhiyun zm_dprintf (char *fmt, ...)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun   int len;
188*4882a593Smuzhiyun   va_list args;
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun   va_start (args, fmt);
191*4882a593Smuzhiyun   len = diag_vsprintf (zm_out, fmt, args);
192*4882a593Smuzhiyun   zm_out += len;
193*4882a593Smuzhiyun   return len;
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun static void
zm_flush(void)197*4882a593Smuzhiyun zm_flush (void)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun   zm_out = zm_out_start;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun static void
zm_dump_buf(void * buf,int len)203*4882a593Smuzhiyun zm_dump_buf (void *buf, int len)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun static unsigned char zm_buf[2048];
209*4882a593Smuzhiyun static unsigned char *zm_bp;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun static void
zm_new(void)212*4882a593Smuzhiyun zm_new (void)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun   zm_bp = zm_buf;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun static void
zm_save(unsigned char c)218*4882a593Smuzhiyun zm_save (unsigned char c)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun   *zm_bp++ = c;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun static void
zm_dump(int line)224*4882a593Smuzhiyun zm_dump (int line)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun   zm_dprintf ("Packet at line: %d\n", line);
227*4882a593Smuzhiyun   zm_dump_buf (zm_buf, zm_bp - zm_buf);
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun #define ZM_DEBUG(x) x
231*4882a593Smuzhiyun #else
232*4882a593Smuzhiyun #define ZM_DEBUG(x)
233*4882a593Smuzhiyun #endif
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun /* Wait for the line to go idle */
236*4882a593Smuzhiyun static void
xyzModem_flush(void)237*4882a593Smuzhiyun xyzModem_flush (void)
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun   int res;
240*4882a593Smuzhiyun   char c;
241*4882a593Smuzhiyun   while (true)
242*4882a593Smuzhiyun     {
243*4882a593Smuzhiyun       res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
244*4882a593Smuzhiyun       if (!res)
245*4882a593Smuzhiyun 	return;
246*4882a593Smuzhiyun     }
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun static int
xyzModem_get_hdr(void)250*4882a593Smuzhiyun xyzModem_get_hdr (void)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun   char c;
253*4882a593Smuzhiyun   int res;
254*4882a593Smuzhiyun   bool hdr_found = false;
255*4882a593Smuzhiyun   int i, can_total, hdr_chars;
256*4882a593Smuzhiyun   unsigned short cksum;
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun   ZM_DEBUG (zm_new ());
259*4882a593Smuzhiyun   /* Find the start of a header */
260*4882a593Smuzhiyun   can_total = 0;
261*4882a593Smuzhiyun   hdr_chars = 0;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun   if (xyz.tx_ack)
264*4882a593Smuzhiyun     {
265*4882a593Smuzhiyun       CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
266*4882a593Smuzhiyun       xyz.tx_ack = false;
267*4882a593Smuzhiyun     }
268*4882a593Smuzhiyun   while (!hdr_found)
269*4882a593Smuzhiyun     {
270*4882a593Smuzhiyun       res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
271*4882a593Smuzhiyun       ZM_DEBUG (zm_save (c));
272*4882a593Smuzhiyun       if (res)
273*4882a593Smuzhiyun 	{
274*4882a593Smuzhiyun 	  hdr_chars++;
275*4882a593Smuzhiyun 	  switch (c)
276*4882a593Smuzhiyun 	    {
277*4882a593Smuzhiyun 	    case SOH:
278*4882a593Smuzhiyun 	      xyz.total_SOH++;
279*4882a593Smuzhiyun 	    case STX:
280*4882a593Smuzhiyun 	      if (c == STX)
281*4882a593Smuzhiyun 		xyz.total_STX++;
282*4882a593Smuzhiyun 	      hdr_found = true;
283*4882a593Smuzhiyun 	      break;
284*4882a593Smuzhiyun 	    case CAN:
285*4882a593Smuzhiyun 	      xyz.total_CAN++;
286*4882a593Smuzhiyun 	      ZM_DEBUG (zm_dump (__LINE__));
287*4882a593Smuzhiyun 	      if (++can_total == xyzModem_CAN_COUNT)
288*4882a593Smuzhiyun 		{
289*4882a593Smuzhiyun 		  return xyzModem_cancel;
290*4882a593Smuzhiyun 		}
291*4882a593Smuzhiyun 	      else
292*4882a593Smuzhiyun 		{
293*4882a593Smuzhiyun 		  /* Wait for multiple CAN to avoid early quits */
294*4882a593Smuzhiyun 		  break;
295*4882a593Smuzhiyun 		}
296*4882a593Smuzhiyun 	    case EOT:
297*4882a593Smuzhiyun 	      /* EOT only supported if no noise */
298*4882a593Smuzhiyun 	      if (hdr_chars == 1)
299*4882a593Smuzhiyun 		{
300*4882a593Smuzhiyun 		  CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
301*4882a593Smuzhiyun 		  ZM_DEBUG (zm_dprintf ("ACK on EOT #%d\n", __LINE__));
302*4882a593Smuzhiyun 		  ZM_DEBUG (zm_dump (__LINE__));
303*4882a593Smuzhiyun 		  return xyzModem_eof;
304*4882a593Smuzhiyun 		}
305*4882a593Smuzhiyun 	    default:
306*4882a593Smuzhiyun 	      /* Ignore, waiting for start of header */
307*4882a593Smuzhiyun 	      ;
308*4882a593Smuzhiyun 	    }
309*4882a593Smuzhiyun 	}
310*4882a593Smuzhiyun       else
311*4882a593Smuzhiyun 	{
312*4882a593Smuzhiyun 	  /* Data stream timed out */
313*4882a593Smuzhiyun 	  xyzModem_flush ();	/* Toss any current input */
314*4882a593Smuzhiyun 	  ZM_DEBUG (zm_dump (__LINE__));
315*4882a593Smuzhiyun 	  CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
316*4882a593Smuzhiyun 	  return xyzModem_timeout;
317*4882a593Smuzhiyun 	}
318*4882a593Smuzhiyun     }
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun   /* Header found, now read the data */
321*4882a593Smuzhiyun   res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.blk);
322*4882a593Smuzhiyun   ZM_DEBUG (zm_save (xyz.blk));
323*4882a593Smuzhiyun   if (!res)
324*4882a593Smuzhiyun     {
325*4882a593Smuzhiyun       ZM_DEBUG (zm_dump (__LINE__));
326*4882a593Smuzhiyun       return xyzModem_timeout;
327*4882a593Smuzhiyun     }
328*4882a593Smuzhiyun   res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.cblk);
329*4882a593Smuzhiyun   ZM_DEBUG (zm_save (xyz.cblk));
330*4882a593Smuzhiyun   if (!res)
331*4882a593Smuzhiyun     {
332*4882a593Smuzhiyun       ZM_DEBUG (zm_dump (__LINE__));
333*4882a593Smuzhiyun       return xyzModem_timeout;
334*4882a593Smuzhiyun     }
335*4882a593Smuzhiyun   xyz.len = (c == SOH) ? 128 : 1024;
336*4882a593Smuzhiyun   xyz.bufp = xyz.pkt;
337*4882a593Smuzhiyun   for (i = 0; i < xyz.len; i++)
338*4882a593Smuzhiyun     {
339*4882a593Smuzhiyun       res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, &c);
340*4882a593Smuzhiyun       ZM_DEBUG (zm_save (c));
341*4882a593Smuzhiyun       if (res)
342*4882a593Smuzhiyun 	{
343*4882a593Smuzhiyun 	  xyz.pkt[i] = c;
344*4882a593Smuzhiyun 	}
345*4882a593Smuzhiyun       else
346*4882a593Smuzhiyun 	{
347*4882a593Smuzhiyun 	  ZM_DEBUG (zm_dump (__LINE__));
348*4882a593Smuzhiyun 	  return xyzModem_timeout;
349*4882a593Smuzhiyun 	}
350*4882a593Smuzhiyun     }
351*4882a593Smuzhiyun   res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc1);
352*4882a593Smuzhiyun   ZM_DEBUG (zm_save (xyz.crc1));
353*4882a593Smuzhiyun   if (!res)
354*4882a593Smuzhiyun     {
355*4882a593Smuzhiyun       ZM_DEBUG (zm_dump (__LINE__));
356*4882a593Smuzhiyun       return xyzModem_timeout;
357*4882a593Smuzhiyun     }
358*4882a593Smuzhiyun   if (xyz.crc_mode)
359*4882a593Smuzhiyun     {
360*4882a593Smuzhiyun       res = CYGACC_COMM_IF_GETC_TIMEOUT (*xyz.__chan, (char *) &xyz.crc2);
361*4882a593Smuzhiyun       ZM_DEBUG (zm_save (xyz.crc2));
362*4882a593Smuzhiyun       if (!res)
363*4882a593Smuzhiyun 	{
364*4882a593Smuzhiyun 	  ZM_DEBUG (zm_dump (__LINE__));
365*4882a593Smuzhiyun 	  return xyzModem_timeout;
366*4882a593Smuzhiyun 	}
367*4882a593Smuzhiyun     }
368*4882a593Smuzhiyun   ZM_DEBUG (zm_dump (__LINE__));
369*4882a593Smuzhiyun   /* Validate the message */
370*4882a593Smuzhiyun   if ((xyz.blk ^ xyz.cblk) != (unsigned char) 0xFF)
371*4882a593Smuzhiyun     {
372*4882a593Smuzhiyun       ZM_DEBUG (zm_dprintf
373*4882a593Smuzhiyun 		("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk,
374*4882a593Smuzhiyun 		 (xyz.blk ^ xyz.cblk)));
375*4882a593Smuzhiyun       ZM_DEBUG (zm_dump_buf (xyz.pkt, xyz.len));
376*4882a593Smuzhiyun       xyzModem_flush ();
377*4882a593Smuzhiyun       return xyzModem_frame;
378*4882a593Smuzhiyun     }
379*4882a593Smuzhiyun   /* Verify checksum/CRC */
380*4882a593Smuzhiyun   if (xyz.crc_mode)
381*4882a593Smuzhiyun     {
382*4882a593Smuzhiyun       cksum = crc16_ccitt(0, xyz.pkt, xyz.len);
383*4882a593Smuzhiyun       if (cksum != ((xyz.crc1 << 8) | xyz.crc2))
384*4882a593Smuzhiyun 	{
385*4882a593Smuzhiyun 	  ZM_DEBUG (zm_dprintf ("CRC error - recvd: %02x%02x, computed: %x\n",
386*4882a593Smuzhiyun 				xyz.crc1, xyz.crc2, cksum & 0xFFFF));
387*4882a593Smuzhiyun 	  return xyzModem_cksum;
388*4882a593Smuzhiyun 	}
389*4882a593Smuzhiyun     }
390*4882a593Smuzhiyun   else
391*4882a593Smuzhiyun     {
392*4882a593Smuzhiyun       cksum = 0;
393*4882a593Smuzhiyun       for (i = 0; i < xyz.len; i++)
394*4882a593Smuzhiyun 	{
395*4882a593Smuzhiyun 	  cksum += xyz.pkt[i];
396*4882a593Smuzhiyun 	}
397*4882a593Smuzhiyun       if (xyz.crc1 != (cksum & 0xFF))
398*4882a593Smuzhiyun 	{
399*4882a593Smuzhiyun 	  ZM_DEBUG (zm_dprintf
400*4882a593Smuzhiyun 		    ("Checksum error - recvd: %x, computed: %x\n", xyz.crc1,
401*4882a593Smuzhiyun 		     cksum & 0xFF));
402*4882a593Smuzhiyun 	  return xyzModem_cksum;
403*4882a593Smuzhiyun 	}
404*4882a593Smuzhiyun     }
405*4882a593Smuzhiyun   /* If we get here, the message passes [structural] muster */
406*4882a593Smuzhiyun   return 0;
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun int
xyzModem_stream_open(connection_info_t * info,int * err)410*4882a593Smuzhiyun xyzModem_stream_open (connection_info_t * info, int *err)
411*4882a593Smuzhiyun {
412*4882a593Smuzhiyun   int stat = 0;
413*4882a593Smuzhiyun   int retries = xyzModem_MAX_RETRIES;
414*4882a593Smuzhiyun   int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun /*    ZM_DEBUG(zm_out = zm_out_start); */
417*4882a593Smuzhiyun #ifdef xyzModem_zmodem
418*4882a593Smuzhiyun   if (info->mode == xyzModem_zmodem)
419*4882a593Smuzhiyun     {
420*4882a593Smuzhiyun       *err = xyzModem_noZmodem;
421*4882a593Smuzhiyun       return -1;
422*4882a593Smuzhiyun     }
423*4882a593Smuzhiyun #endif
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun /* TODO: CHECK ! */
426*4882a593Smuzhiyun   int dummy = 0;
427*4882a593Smuzhiyun   xyz.__chan = &dummy;
428*4882a593Smuzhiyun   xyz.len = 0;
429*4882a593Smuzhiyun   xyz.crc_mode = true;
430*4882a593Smuzhiyun   xyz.at_eof = false;
431*4882a593Smuzhiyun   xyz.tx_ack = false;
432*4882a593Smuzhiyun   xyz.mode = info->mode;
433*4882a593Smuzhiyun   xyz.total_retries = 0;
434*4882a593Smuzhiyun   xyz.total_SOH = 0;
435*4882a593Smuzhiyun   xyz.total_STX = 0;
436*4882a593Smuzhiyun   xyz.total_CAN = 0;
437*4882a593Smuzhiyun   xyz.read_length = 0;
438*4882a593Smuzhiyun   xyz.file_length = 0;
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun   CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun   if (xyz.mode == xyzModem_xmodem)
443*4882a593Smuzhiyun     {
444*4882a593Smuzhiyun       /* X-modem doesn't have an information header - exit here */
445*4882a593Smuzhiyun       xyz.next_blk = 1;
446*4882a593Smuzhiyun       return 0;
447*4882a593Smuzhiyun     }
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun   while (retries-- > 0)
450*4882a593Smuzhiyun     {
451*4882a593Smuzhiyun       stat = xyzModem_get_hdr ();
452*4882a593Smuzhiyun       if (stat == 0)
453*4882a593Smuzhiyun 	{
454*4882a593Smuzhiyun 	  /* Y-modem file information header */
455*4882a593Smuzhiyun 	  if (xyz.blk == 0)
456*4882a593Smuzhiyun 	    {
457*4882a593Smuzhiyun 	      /* skip filename */
458*4882a593Smuzhiyun 	      while (*xyz.bufp++);
459*4882a593Smuzhiyun 	      /* get the length */
460*4882a593Smuzhiyun 	      parse_num ((char *) xyz.bufp, &xyz.file_length, NULL, " ");
461*4882a593Smuzhiyun 	      /* The rest of the file name data block quietly discarded */
462*4882a593Smuzhiyun 	      xyz.tx_ack = true;
463*4882a593Smuzhiyun 	    }
464*4882a593Smuzhiyun 	  xyz.next_blk = 1;
465*4882a593Smuzhiyun 	  xyz.len = 0;
466*4882a593Smuzhiyun 	  return 0;
467*4882a593Smuzhiyun 	}
468*4882a593Smuzhiyun       else if (stat == xyzModem_timeout)
469*4882a593Smuzhiyun 	{
470*4882a593Smuzhiyun 	  if (--crc_retries <= 0)
471*4882a593Smuzhiyun 	    xyz.crc_mode = false;
472*4882a593Smuzhiyun 	  CYGACC_CALL_IF_DELAY_US (5 * 100000);	/* Extra delay for startup */
473*4882a593Smuzhiyun 	  CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
474*4882a593Smuzhiyun 	  xyz.total_retries++;
475*4882a593Smuzhiyun 	  ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
476*4882a593Smuzhiyun 	}
477*4882a593Smuzhiyun       if (stat == xyzModem_cancel)
478*4882a593Smuzhiyun 	{
479*4882a593Smuzhiyun 	  break;
480*4882a593Smuzhiyun 	}
481*4882a593Smuzhiyun     }
482*4882a593Smuzhiyun   *err = stat;
483*4882a593Smuzhiyun   ZM_DEBUG (zm_flush ());
484*4882a593Smuzhiyun   return -1;
485*4882a593Smuzhiyun }
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun int
xyzModem_stream_read(char * buf,int size,int * err)488*4882a593Smuzhiyun xyzModem_stream_read (char *buf, int size, int *err)
489*4882a593Smuzhiyun {
490*4882a593Smuzhiyun   int stat, total, len;
491*4882a593Smuzhiyun   int retries;
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun   total = 0;
494*4882a593Smuzhiyun   stat = xyzModem_cancel;
495*4882a593Smuzhiyun   /* Try and get 'size' bytes into the buffer */
496*4882a593Smuzhiyun   while (!xyz.at_eof && (size > 0))
497*4882a593Smuzhiyun     {
498*4882a593Smuzhiyun       if (xyz.len == 0)
499*4882a593Smuzhiyun 	{
500*4882a593Smuzhiyun 	  retries = xyzModem_MAX_RETRIES;
501*4882a593Smuzhiyun 	  while (retries-- > 0)
502*4882a593Smuzhiyun 	    {
503*4882a593Smuzhiyun 	      stat = xyzModem_get_hdr ();
504*4882a593Smuzhiyun 	      if (stat == 0)
505*4882a593Smuzhiyun 		{
506*4882a593Smuzhiyun 		  if (xyz.blk == xyz.next_blk)
507*4882a593Smuzhiyun 		    {
508*4882a593Smuzhiyun 		      xyz.tx_ack = true;
509*4882a593Smuzhiyun 		      ZM_DEBUG (zm_dprintf
510*4882a593Smuzhiyun 				("ACK block %d (%d)\n", xyz.blk, __LINE__));
511*4882a593Smuzhiyun 		      xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 		      if (xyz.mode == xyzModem_xmodem || xyz.file_length == 0)
514*4882a593Smuzhiyun 			{
515*4882a593Smuzhiyun 			  /* Data blocks can be padded with ^Z (EOF) characters */
516*4882a593Smuzhiyun 			  /* This code tries to detect and remove them */
517*4882a593Smuzhiyun 			  if ((xyz.bufp[xyz.len - 1] == EOF) &&
518*4882a593Smuzhiyun 			      (xyz.bufp[xyz.len - 2] == EOF) &&
519*4882a593Smuzhiyun 			      (xyz.bufp[xyz.len - 3] == EOF))
520*4882a593Smuzhiyun 			    {
521*4882a593Smuzhiyun 			      while (xyz.len
522*4882a593Smuzhiyun 				     && (xyz.bufp[xyz.len - 1] == EOF))
523*4882a593Smuzhiyun 				{
524*4882a593Smuzhiyun 				  xyz.len--;
525*4882a593Smuzhiyun 				}
526*4882a593Smuzhiyun 			    }
527*4882a593Smuzhiyun 			}
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 		      /*
530*4882a593Smuzhiyun 		       * See if accumulated length exceeds that of the file.
531*4882a593Smuzhiyun 		       * If so, reduce size (i.e., cut out pad bytes)
532*4882a593Smuzhiyun 		       * Only do this for Y-modem (and Z-modem should it ever
533*4882a593Smuzhiyun 		       * be supported since it can fall back to Y-modem mode).
534*4882a593Smuzhiyun 		       */
535*4882a593Smuzhiyun 		      if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length)
536*4882a593Smuzhiyun 			{
537*4882a593Smuzhiyun 			  xyz.read_length += xyz.len;
538*4882a593Smuzhiyun 			  if (xyz.read_length > xyz.file_length)
539*4882a593Smuzhiyun 			    {
540*4882a593Smuzhiyun 			      xyz.len -= (xyz.read_length - xyz.file_length);
541*4882a593Smuzhiyun 			    }
542*4882a593Smuzhiyun 			}
543*4882a593Smuzhiyun 		      break;
544*4882a593Smuzhiyun 		    }
545*4882a593Smuzhiyun 		  else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF))
546*4882a593Smuzhiyun 		    {
547*4882a593Smuzhiyun 		      /* Just re-ACK this so sender will get on with it */
548*4882a593Smuzhiyun 		      CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
549*4882a593Smuzhiyun 		      continue;	/* Need new header */
550*4882a593Smuzhiyun 		    }
551*4882a593Smuzhiyun 		  else
552*4882a593Smuzhiyun 		    {
553*4882a593Smuzhiyun 		      stat = xyzModem_sequence;
554*4882a593Smuzhiyun 		    }
555*4882a593Smuzhiyun 		}
556*4882a593Smuzhiyun 	      if (stat == xyzModem_cancel)
557*4882a593Smuzhiyun 		{
558*4882a593Smuzhiyun 		  break;
559*4882a593Smuzhiyun 		}
560*4882a593Smuzhiyun 	      if (stat == xyzModem_eof)
561*4882a593Smuzhiyun 		{
562*4882a593Smuzhiyun 		  CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
563*4882a593Smuzhiyun 		  ZM_DEBUG (zm_dprintf ("ACK (%d)\n", __LINE__));
564*4882a593Smuzhiyun 		  if (xyz.mode == xyzModem_ymodem)
565*4882a593Smuzhiyun 		    {
566*4882a593Smuzhiyun 		      CYGACC_COMM_IF_PUTC (*xyz.__chan,
567*4882a593Smuzhiyun 					   (xyz.crc_mode ? 'C' : NAK));
568*4882a593Smuzhiyun 		      xyz.total_retries++;
569*4882a593Smuzhiyun 		      ZM_DEBUG (zm_dprintf ("Reading Final Header\n"));
570*4882a593Smuzhiyun 		      stat = xyzModem_get_hdr ();
571*4882a593Smuzhiyun 		      CYGACC_COMM_IF_PUTC (*xyz.__chan, ACK);
572*4882a593Smuzhiyun 		      ZM_DEBUG (zm_dprintf ("FINAL ACK (%d)\n", __LINE__));
573*4882a593Smuzhiyun 		    }
574*4882a593Smuzhiyun 		  xyz.at_eof = true;
575*4882a593Smuzhiyun 		  break;
576*4882a593Smuzhiyun 		}
577*4882a593Smuzhiyun 	      CYGACC_COMM_IF_PUTC (*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
578*4882a593Smuzhiyun 	      xyz.total_retries++;
579*4882a593Smuzhiyun 	      ZM_DEBUG (zm_dprintf ("NAK (%d)\n", __LINE__));
580*4882a593Smuzhiyun 	    }
581*4882a593Smuzhiyun 	  if (stat < 0)
582*4882a593Smuzhiyun 	    {
583*4882a593Smuzhiyun 	      *err = stat;
584*4882a593Smuzhiyun 	      xyz.len = -1;
585*4882a593Smuzhiyun 	      return total;
586*4882a593Smuzhiyun 	    }
587*4882a593Smuzhiyun 	}
588*4882a593Smuzhiyun       /* Don't "read" data from the EOF protocol package */
589*4882a593Smuzhiyun       if (!xyz.at_eof)
590*4882a593Smuzhiyun 	{
591*4882a593Smuzhiyun 	  len = xyz.len;
592*4882a593Smuzhiyun 	  if (size < len)
593*4882a593Smuzhiyun 	    len = size;
594*4882a593Smuzhiyun 	  memcpy (buf, xyz.bufp, len);
595*4882a593Smuzhiyun 	  size -= len;
596*4882a593Smuzhiyun 	  buf += len;
597*4882a593Smuzhiyun 	  total += len;
598*4882a593Smuzhiyun 	  xyz.len -= len;
599*4882a593Smuzhiyun 	  xyz.bufp += len;
600*4882a593Smuzhiyun 	}
601*4882a593Smuzhiyun     }
602*4882a593Smuzhiyun   return total;
603*4882a593Smuzhiyun }
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun void
xyzModem_stream_close(int * err)606*4882a593Smuzhiyun xyzModem_stream_close (int *err)
607*4882a593Smuzhiyun {
608*4882a593Smuzhiyun   diag_printf
609*4882a593Smuzhiyun     ("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
610*4882a593Smuzhiyun      xyz.crc_mode ? "CRC" : "Cksum", xyz.total_SOH, xyz.total_STX,
611*4882a593Smuzhiyun      xyz.total_CAN, xyz.total_retries);
612*4882a593Smuzhiyun   ZM_DEBUG (zm_flush ());
613*4882a593Smuzhiyun }
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun /* Need to be able to clean out the input buffer, so have to take the */
616*4882a593Smuzhiyun /* getc */
617*4882a593Smuzhiyun void
xyzModem_stream_terminate(bool abort,int (* getc)(void))618*4882a593Smuzhiyun xyzModem_stream_terminate (bool abort, int (*getc) (void))
619*4882a593Smuzhiyun {
620*4882a593Smuzhiyun   int c;
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun   if (abort)
623*4882a593Smuzhiyun     {
624*4882a593Smuzhiyun       ZM_DEBUG (zm_dprintf ("!!!! TRANSFER ABORT !!!!\n"));
625*4882a593Smuzhiyun       switch (xyz.mode)
626*4882a593Smuzhiyun 	{
627*4882a593Smuzhiyun 	case xyzModem_xmodem:
628*4882a593Smuzhiyun 	case xyzModem_ymodem:
629*4882a593Smuzhiyun 	  /* The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal */
630*4882a593Smuzhiyun 	  /* number of Backspaces is a friendly way to get the other end to abort. */
631*4882a593Smuzhiyun 	  CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
632*4882a593Smuzhiyun 	  CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
633*4882a593Smuzhiyun 	  CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
634*4882a593Smuzhiyun 	  CYGACC_COMM_IF_PUTC (*xyz.__chan, CAN);
635*4882a593Smuzhiyun 	  CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
636*4882a593Smuzhiyun 	  CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
637*4882a593Smuzhiyun 	  CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
638*4882a593Smuzhiyun 	  CYGACC_COMM_IF_PUTC (*xyz.__chan, BSP);
639*4882a593Smuzhiyun 	  /* Now consume the rest of what's waiting on the line. */
640*4882a593Smuzhiyun 	  ZM_DEBUG (zm_dprintf ("Flushing serial line.\n"));
641*4882a593Smuzhiyun 	  xyzModem_flush ();
642*4882a593Smuzhiyun 	  xyz.at_eof = true;
643*4882a593Smuzhiyun 	  break;
644*4882a593Smuzhiyun #ifdef xyzModem_zmodem
645*4882a593Smuzhiyun 	case xyzModem_zmodem:
646*4882a593Smuzhiyun 	  /* Might support it some day I suppose. */
647*4882a593Smuzhiyun #endif
648*4882a593Smuzhiyun 	  break;
649*4882a593Smuzhiyun 	}
650*4882a593Smuzhiyun     }
651*4882a593Smuzhiyun   else
652*4882a593Smuzhiyun     {
653*4882a593Smuzhiyun       ZM_DEBUG (zm_dprintf ("Engaging cleanup mode...\n"));
654*4882a593Smuzhiyun       /*
655*4882a593Smuzhiyun        * Consume any trailing crap left in the inbuffer from
656*4882a593Smuzhiyun        * previous received blocks. Since very few files are an exact multiple
657*4882a593Smuzhiyun        * of the transfer block size, there will almost always be some gunk here.
658*4882a593Smuzhiyun        * If we don't eat it now, RedBoot will think the user typed it.
659*4882a593Smuzhiyun        */
660*4882a593Smuzhiyun       ZM_DEBUG (zm_dprintf ("Trailing gunk:\n"));
661*4882a593Smuzhiyun       while ((c = (*getc) ()) > -1)
662*4882a593Smuzhiyun         ;
663*4882a593Smuzhiyun       ZM_DEBUG (zm_dprintf ("\n"));
664*4882a593Smuzhiyun       /*
665*4882a593Smuzhiyun        * Make a small delay to give terminal programs like minicom
666*4882a593Smuzhiyun        * time to get control again after their file transfer program
667*4882a593Smuzhiyun        * exits.
668*4882a593Smuzhiyun        */
669*4882a593Smuzhiyun       CYGACC_CALL_IF_DELAY_US ((cyg_int32) 250000);
670*4882a593Smuzhiyun     }
671*4882a593Smuzhiyun }
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun char *
xyzModem_error(int err)674*4882a593Smuzhiyun xyzModem_error (int err)
675*4882a593Smuzhiyun {
676*4882a593Smuzhiyun   switch (err)
677*4882a593Smuzhiyun     {
678*4882a593Smuzhiyun     case xyzModem_access:
679*4882a593Smuzhiyun       return "Can't access file";
680*4882a593Smuzhiyun       break;
681*4882a593Smuzhiyun     case xyzModem_noZmodem:
682*4882a593Smuzhiyun       return "Sorry, zModem not available yet";
683*4882a593Smuzhiyun       break;
684*4882a593Smuzhiyun     case xyzModem_timeout:
685*4882a593Smuzhiyun       return "Timed out";
686*4882a593Smuzhiyun       break;
687*4882a593Smuzhiyun     case xyzModem_eof:
688*4882a593Smuzhiyun       return "End of file";
689*4882a593Smuzhiyun       break;
690*4882a593Smuzhiyun     case xyzModem_cancel:
691*4882a593Smuzhiyun       return "Cancelled";
692*4882a593Smuzhiyun       break;
693*4882a593Smuzhiyun     case xyzModem_frame:
694*4882a593Smuzhiyun       return "Invalid framing";
695*4882a593Smuzhiyun       break;
696*4882a593Smuzhiyun     case xyzModem_cksum:
697*4882a593Smuzhiyun       return "CRC/checksum error";
698*4882a593Smuzhiyun       break;
699*4882a593Smuzhiyun     case xyzModem_sequence:
700*4882a593Smuzhiyun       return "Block sequence error";
701*4882a593Smuzhiyun       break;
702*4882a593Smuzhiyun     default:
703*4882a593Smuzhiyun       return "Unknown error";
704*4882a593Smuzhiyun       break;
705*4882a593Smuzhiyun     }
706*4882a593Smuzhiyun }
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun /*
709*4882a593Smuzhiyun  * RedBoot interface
710*4882a593Smuzhiyun  */
711