Clean up the code. Manage the reconnection. Use the lwip error, insted
[bertos.git] / bertos / net / xmodem.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2004, 2005, 2006, 2007 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 1999, 2001 Bernie Innocenti <bernie@codewiz.org>
31  *
32  * -->
33  *
34  * \brief X-Modem serial transmission protocol (implementation)
35  *
36  * Supports the CRC-16 and 1K-blocks variants of the standard.
37  * \see ymodem.txt for the protocol description.
38  *
39  * \todo Break xmodem_send() and xmodem_recv() in smaller functions.
40  *
41  * \author Bernie Innocenti <bernie@codewiz.org>
42  * \author Francesco Sacchi <batt@develer.com>
43  */
44
45
46 #include "xmodem.h"
47
48 #include "cfg/cfg_xmodem.h"
49
50 #include <cfg/debug.h>
51 // Define log settings for cfg/log.h
52 #define LOG_LEVEL    CONFIG_XMODEM_LOG_LEVEL
53 #define LOG_FORMAT   CONFIG_XMODEM_LOG_FORMAT
54 #include <cfg/log.h>
55
56
57 #include <algo/crc.h>
58
59 #include <string.h> /* for memset() */
60
61 /**
62  * \name Protocol control codes
63  * \{
64  */
65 #define XM_SOH  0x01  /**< Start Of Header (128-byte block) */
66 #define XM_STX  0x02  /**< Start Of Header (1024-byte block) */
67 #define XM_EOT  0x04  /**< End Of Transmission */
68 #define XM_ACK  0x06  /**< Acknowledge block */
69 #define XM_NAK  0x15  /**< Negative Acknowledge */
70 #define XM_C    0x43  /**< Request CRC-16 transmission */
71 #define XM_CAN  0x18  /**< CANcel transmission */
72 /*\}*/
73
74 #if CONFIG_XMODEM_1KCRC == 1
75         #define XM_BUFSIZE       1024  /**< 1024 bytes of block buffer */
76 #else
77         #define XM_BUFSIZE       128   /**< 128 bytes of block buffer */
78 #endif
79
80
81 #if CONFIG_XMODEM_RECV
82 /**
83  * \brief Receive a file using the XModem protocol.
84  *
85  * \param ch Channel to use for transfer
86  * \param fd Destination file
87  *
88  * \note This function allocates a large amount of stack (\see XM_BUFSIZE).
89  */
90 bool xmodem_recv(KFile *ch, KFile *fd)
91 {
92         char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
93         int c, i, blocksize;
94         int blocknr = 0, last_block_done = 0, retries = 0;
95         char *buf;
96         uint8_t checksum;
97         uint16_t crc;
98         bool purge = false;
99         bool usecrc = true;
100
101
102         LOG_INFO("Starting Transfer...\n");
103         purge = true;
104         kfile_clearerr(ch);
105
106         /* Send initial NAK to start transmission */
107         for(;;)
108         {
109                 if (XMODEM_CHECK_ABORT)
110                 {
111                         kfile_putc(XM_CAN, ch);
112                         kfile_putc(XM_CAN, ch);
113                         LOG_INFO("Transfer aborted\n");
114                         return false;
115                 }
116
117                 /*
118                  * Discard incoming input until a timeout occurs, then send
119                  * a NAK to the transmitter.
120                  */
121                 if (purge)
122                 {
123                         purge = false;
124
125                         if (kfile_error(ch))
126                         {
127                                 LOG_ERR("Retries %d\n", retries);
128                         }
129
130                         kfile_resync(ch, 200);
131                         retries++;
132
133                         if (retries >= CONFIG_XMODEM_MAXRETRIES)
134                         {
135                                 kfile_putc(XM_CAN, ch);
136                                 kfile_putc(XM_CAN, ch);
137                                 LOG_INFO("Transfer aborted\n");
138                                 return false;
139                         }
140
141                         /* Transmission start? */
142                         if (blocknr == 0)
143                         {
144                                 if (retries < CONFIG_XMODEM_MAXCRCRETRIES)
145                                 {
146                                         LOG_INFO("Request Tx (CRC)\n");
147                                         kfile_putc(XM_C, ch);
148                                 }
149                                 else
150                                 {
151                                         /* Give up with CRC and fall back to checksum */
152                                         usecrc = false;
153                                         LOG_INFO("Request Tx (BCC)\n");
154                                         kfile_putc(XM_NAK, ch);
155                                 }
156                         }
157                         else
158                                 kfile_putc(XM_NAK, ch);
159                 }
160
161                 switch (kfile_getc(ch))
162                 {
163                 #if XM_BUFSIZE >= 1024
164                 case XM_STX:  /* Start of header (1024-byte block) */
165                         blocksize = 1024;
166                         goto getblock;
167                 #endif
168
169                 case XM_SOH:  /* Start of header (128-byte block) */
170                         blocksize = 128;
171                         /* Needed to avoid warning if XM_BUFSIZE < 1024 */
172
173                 getblock:
174                         /* Get block number */
175                         c = kfile_getc(ch);
176
177                         /* Check complemented block number */
178                         if ((~c & 0xff) != kfile_getc(ch))
179                         {
180                                 LOG_WARN("Bad blk (%d)\n", c);
181                                 purge = true;
182                                 break;
183                         }
184
185                         /* Determine which block is being sent */
186                         if (c == (blocknr & 0xff))
187                         {
188                                 /* Last block repeated */
189                                 LOG_INFO("Repeat blk %d\n", blocknr);
190                         }
191                         else if (c == ((blocknr + 1) & 0xff))
192                         {
193                                 /* Next block */
194                                 LOG_INFO("Recv blk %d\n", ++blocknr);
195                         }
196                         else
197                         {
198                                 /* Sync lost */
199                                 LOG_WARN("Sync lost (%d/%d)\n", c, blocknr);
200                                 purge = true;
201                                 break;
202                         }
203
204                         buf = block_buffer;     /* Reset pointer to start of buffer */
205                         checksum = 0;
206                         crc = 0;
207                         for (i = 0; i < blocksize; i++)
208                         {
209                                 if ((c = kfile_getc(ch)) == EOF)
210                                 {
211                                         purge = true;
212                                         break;
213                                 }
214
215                                 /* Store in buffer */
216                                 *buf++ = (char)c;
217
218                                 /* Calculate block checksum or CRC */
219                                 if (usecrc)
220                                         crc = UPDCRC16(c, crc);
221                                 else
222                                         checksum += (char)c;
223                         }
224
225                         if (purge)
226                                 break;
227
228                         /* Get the checksum byte or the CRC-16 MSB */
229                         if ((c = kfile_getc(ch)) == EOF)
230                         {
231                                 purge = true;
232                                 break;
233                         }
234
235                         if (usecrc)
236                         {
237                                 crc = UPDCRC16(c, crc);
238
239                                 /* Get CRC-16 LSB */
240                                 if ((c = kfile_getc(ch)) == EOF)
241                                 {
242                                         purge = true;
243                                         break;
244                                 }
245
246                                 crc = UPDCRC16(c, crc);
247
248                                 if (crc)
249                                 {
250                                         LOG_ERR("Bad CRC: %04x\n", crc);
251                                         purge = true;
252                                         break;
253                                 }
254                         }
255                         /* Compare the checksum */
256                         else if (c != checksum)
257                         {
258                                 LOG_ERR("Bad sum: %04x/%04x\n", checksum, c);
259                                 purge = true;
260                                 break;
261                         }
262
263                         /*
264                          * Avoid flushing the same block twice.
265                          * This could happen when the sender does not receive our
266                          * acknowledge and resends the same block.
267                          */
268                         if (last_block_done < blocknr)
269                         {
270                                 /* Call user function to flush the buffer */
271                                 if (kfile_write(fd, block_buffer, blocksize) == (size_t)blocksize)
272                                 {
273                                         /* Acknowledge block and clear error counter */
274                                         kfile_putc(XM_ACK, ch);
275                                         retries = 0;
276                                         last_block_done = blocknr;
277                                 }
278                                 else
279                                 {
280                                         /* User callback failed: abort transfer immediately */
281                                         retries = CONFIG_XMODEM_MAXRETRIES;
282                                         purge = true;
283                                 }
284                         }
285                         break;
286
287                 case XM_EOT:    /* End of transmission */
288                         kfile_putc(XM_ACK, ch);
289                         LOG_INFO("Transfer completed\n");
290                         return true;
291
292                 case EOF: /* Timeout or serial error */
293                         purge = true;
294                         break;
295
296                 default:
297                         LOG_INFO("Skipping garbage\n");
298                         purge = true;
299                         break;
300                 }
301         } /* End forever */
302 }
303 #endif
304
305
306 #if CONFIG_XMODEM_SEND
307 /**
308  * \brief Transmit some data using the XModem protocol.
309  *
310  * \param ch Channel to use for transfer
311  * \param fd Source file
312  *
313  * \note This function allocates a large amount of stack for
314  *       the XModem transfer buffer (\see XM_BUFSIZE).
315  */
316 bool xmodem_send(KFile *ch, KFile *fd)
317 {
318         char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
319         size_t size = -1;
320         int blocknr = 1, retries = 0, c, i;
321         bool proceed, usecrc = false;
322         uint16_t crc;
323         uint8_t sum;
324
325         /*
326          * Reading a block can be very slow, so we read the first block early
327          * to avoid receiving double XM_C char.
328          * This could happen if we check for XM_C and then read the block, giving
329          * the receiving device time to send another XM_C char misinterpretating
330          * the blocks sent.
331          */
332         size = kfile_read(fd, block_buffer, XM_BUFSIZE);
333
334         kfile_clearerr(ch);
335         LOG_INFO("Wait remote host\n");
336
337         for(;;)
338         {
339                 proceed = false;
340                 do
341                 {
342                         if (XMODEM_CHECK_ABORT)
343                                 return false;
344
345                         switch (c = kfile_getc(ch))
346                         {
347                         case XM_NAK:
348                                 LOG_INFO("Resend blk %d\n", blocknr);
349                                 proceed = true;
350                                 break;
351
352                         case XM_C:
353                                 if (c == XM_C)
354                                 {
355                                         LOG_INFO("Tx start (CRC)\n");
356                                         usecrc = true;
357                                 }
358                                 else
359                                 {
360                                         LOG_INFO("Tx start (BCC)\n");
361                                 }
362
363                                 proceed = true;
364                                 break;
365
366                         case XM_ACK:
367                                 /* End of transfer? */
368                                 if (!size)
369                                         return true;
370
371                                 /* Call user function to read in one block */
372                                 size = kfile_read(fd, block_buffer, XM_BUFSIZE);
373                                 LOG_INFO("Send blk %d\n", blocknr);
374                                 blocknr++;
375                                 retries = 0;
376                                 proceed = true;
377                                 break;
378
379                         case EOF:
380                                 kfile_clearerr(ch);
381                                 retries++;
382                                 LOG_INFO("Retries %d\n", retries);
383                                 if (retries <= CONFIG_XMODEM_MAXRETRIES)
384                                         break;
385                                 /* falling through! */
386
387                         case XM_CAN:
388                                 LOG_INFO("Transfer aborted\n");
389                                 return false;
390
391                         default:
392                                 LOG_INFO("Skipping garbage\n");
393                                 break;
394                         }
395                 }
396                 while (!proceed);
397
398                 if (!size)
399                 {
400                         kfile_putc(XM_EOT, ch);
401                         continue;
402                 }
403
404                 /* Pad block with 0xFF if it's partially full */
405                 memset(block_buffer + size, 0xFF, XM_BUFSIZE - size);
406
407                 /* Send block header (STX, blocknr, ~blocknr) */
408                 #if XM_BUFSIZE == 128
409                         kfile_putc(XM_SOH, ch);
410                 #else
411                         kfile_putc(XM_STX, ch);
412                 #endif
413                 kfile_putc(blocknr & 0xFF, ch);
414                 kfile_putc(~blocknr & 0xFF, ch);
415
416                 /* Send block and compute its CRC/checksum */
417                 sum = 0;
418                 crc = 0;
419                 for (i = 0; i < XM_BUFSIZE; i++)
420                 {
421                         kfile_putc(block_buffer[i], ch);
422                         crc = UPDCRC16(block_buffer[i], crc);
423                         sum += block_buffer[i];
424                 }
425
426                 /* Send CRC/Checksum */
427                 if (usecrc)
428                 {
429                         kfile_putc(crc >> 8, ch);
430                         kfile_putc(crc & 0xFF, ch);
431                 }
432                 else
433                         kfile_putc(sum, ch);
434         }
435 }
436 #endif