Merged from external project:
[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  * \version $Id$
42  *
43  * \author Bernie Innocenti <bernie@codewiz.org>
44  * \author Francesco Sacchi <batt@develer.com>
45  */
46
47
48 #include "xmodem.h"
49
50 #include <cfg/debug.h>
51 #include "cfg/cfg_xmodem.h"
52
53 #include <drv/ser.h>
54
55 #include <algo/crc.h>
56
57 #include <string.h> /* for memset() */
58
59 /**
60  * \name Protocol control codes
61  * \{
62  */
63 #define XM_SOH  0x01  /**< Start Of Header (128-byte block) */
64 #define XM_STX  0x02  /**< Start Of Header (1024-byte block) */
65 #define XM_EOT  0x04  /**< End Of Transmission */
66 #define XM_ACK  0x06  /**< Acknowledge block */
67 #define XM_NAK  0x15  /**< Negative Acknowledge */
68 #define XM_C    0x43  /**< Request CRC-16 transmission */
69 #define XM_CAN  0x18  /**< CANcel transmission */
70 /*\}*/
71
72 #if CONFIG_XMODEM_1KCRC == 1
73         #define XM_BUFSIZE       1024  /**< 1024 bytes of block buffer */
74 #else
75         #define XM_BUFSIZE       128   /**< 128 bytes of block buffer */
76 #endif
77
78
79 #if CONFIG_XMODEM_RECV
80 /**
81  * \brief Receive a file using the XModem protocol.
82  *
83  * \param port Serial port to use for transfer
84  * \param fd Destination file
85  *
86  * \note This function allocates a large amount of stack (\see XM_BUFSIZE).
87  */
88 bool xmodem_recv(struct Serial *port, KFile *fd)
89 {
90         char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
91         int c, i, blocksize;
92         int blocknr = 0, last_block_done = 0, retries = 0;
93         char *buf;
94         uint8_t checksum;
95         uint16_t crc;
96         bool purge = false;
97         bool usecrc = true;
98
99
100         XMODEM_PROGRESS("Starting Transfer...\n");
101         purge = true;
102         kfile_clearerr(&port->fd);
103
104         /* Send initial NAK to start transmission */
105         for(;;)
106         {
107                 if (XMODEM_CHECK_ABORT)
108                 {
109                         kfile_putc(XM_CAN, &port->fd);
110                         kfile_putc(XM_CAN, &port->fd);
111                         XMODEM_PROGRESS("Transfer aborted\n");
112                         return false;
113                 }
114
115                 /*
116                  * Discard incoming input until a timeout occurs, then send
117                  * a NAK to the transmitter.
118                  */
119                 if (purge)
120                 {
121                         purge = false;
122
123                         if (kfile_error(&port->fd))
124                                 XMODEM_PROGRESS("Retries %d\n", retries);
125
126                         ser_resync(port, 200);
127                         retries++;
128
129                         if (retries >= CONFIG_XMODEM_MAXRETRIES)
130                         {
131                                 kfile_putc(XM_CAN, &port->fd);
132                                 kfile_putc(XM_CAN, &port->fd);
133                                 XMODEM_PROGRESS("Transfer aborted\n");
134                                 return false;
135                         }
136
137                         /* Transmission start? */
138                         if (blocknr == 0)
139                         {
140                                 if (retries < CONFIG_XMODEM_MAXCRCRETRIES)
141                                 {
142                                         XMODEM_PROGRESS("Request Tx (CRC)\n");
143                                         kfile_putc(XM_C, &port->fd);
144                                 }
145                                 else
146                                 {
147                                         /* Give up with CRC and fall back to checksum */
148                                         usecrc = false;
149                                         XMODEM_PROGRESS("Request Tx (BCC)\n");
150                                         kfile_putc(XM_NAK, &port->fd);
151                                 }
152                         }
153                         else
154                                 kfile_putc(XM_NAK, &port->fd);
155                 }
156
157                 switch (kfile_getc(&port->fd))
158                 {
159                 #if XM_BUFSIZE >= 1024
160                 case XM_STX:  /* Start of header (1024-byte block) */
161                         blocksize = 1024;
162                         goto getblock;
163                 #endif
164
165                 case XM_SOH:  /* Start of header (128-byte block) */
166                         blocksize = 128;
167                         /* Needed to avoid warning if XM_BUFSIZE < 1024 */
168
169                 getblock:
170                         /* Get block number */
171                         c = kfile_getc(&port->fd);
172
173                         /* Check complemented block number */
174                         if ((~c & 0xff) != kfile_getc(&port->fd))
175                         {
176                                 XMODEM_PROGRESS("Bad blk (%d)\n", c);
177                                 purge = true;
178                                 break;
179                         }
180
181                         /* Determine which block is being sent */
182                         if (c == (blocknr & 0xff))
183                                 /* Last block repeated */
184                                 XMODEM_PROGRESS("Repeat blk %d\n", blocknr);
185                         else if (c == ((blocknr + 1) & 0xff))
186                                 /* Next block */
187                                 XMODEM_PROGRESS("Recv blk %d\n", ++blocknr);
188                         else
189                         {
190                                 /* Sync lost */
191                                 XMODEM_PROGRESS("Sync lost (%d/%d)\n", c, blocknr);
192                                 purge = true;
193                                 break;
194                         }
195
196                         buf = block_buffer;     /* Reset pointer to start of buffer */
197                         checksum = 0;
198                         crc = 0;
199                         for (i = 0; i < blocksize; i++)
200                         {
201                                 if ((c = kfile_getc(&port->fd)) == EOF)
202                                 {
203                                         purge = true;
204                                         break;
205                                 }
206
207                                 /* Store in buffer */
208                                 *buf++ = (char)c;
209
210                                 /* Calculate block checksum or CRC */
211                                 if (usecrc)
212                                         crc = UPDCRC16(c, crc);
213                                 else
214                                         checksum += (char)c;
215                         }
216
217                         if (purge)
218                                 break;
219
220                         /* Get the checksum byte or the CRC-16 MSB */
221                         if ((c = kfile_getc(&port->fd)) == EOF)
222                         {
223                                 purge = true;
224                                 break;
225                         }
226
227                         if (usecrc)
228                         {
229                                 crc = UPDCRC16(c, crc);
230
231                                 /* Get CRC-16 LSB */
232                                 if ((c = kfile_getc(&port->fd)) == EOF)
233                                 {
234                                         purge = true;
235                                         break;
236                                 }
237
238                                 crc = UPDCRC16(c, crc);
239
240                                 if (crc)
241                                 {
242                                         XMODEM_PROGRESS("Bad CRC: %04x\n", crc);
243                                         purge = true;
244                                         break;
245                                 }
246                         }
247                         /* Compare the checksum */
248                         else if (c != checksum)
249                         {
250                                 XMODEM_PROGRESS("Bad sum: %04x/%04x\n", checksum, c);
251                                 purge = true;
252                                 break;
253                         }
254
255                         /*
256                          * Avoid flushing the same block twice.
257                          * This could happen when the sender does not receive our
258                          * acknowledge and resends the same block.
259                          */
260                         if (last_block_done < blocknr)
261                         {
262                                 /* Call user function to flush the buffer */
263                                 if (kfile_write(fd, block_buffer, blocksize))
264                                 {
265                                         /* Acknowledge block and clear error counter */
266                                         kfile_putc(XM_ACK, &port->fd);
267                                         retries = 0;
268                                         last_block_done = blocknr;
269                                 }
270                                 else
271                                 {
272                                         /* User callback failed: abort transfer immediately */
273                                         retries = CONFIG_XMODEM_MAXRETRIES;
274                                         purge = true;
275                                 }
276                         }
277                         break;
278
279                 case XM_EOT:    /* End of transmission */
280                         kfile_putc(XM_ACK, &port->fd);
281                         XMODEM_PROGRESS("Transfer completed\n");
282                         return true;
283
284                 case EOF: /* Timeout or serial error */
285                         purge = true;
286                         break;
287
288                 default:
289                         XMODEM_PROGRESS("Skipping garbage\n");
290                         purge = true;
291                         break;
292                 }
293         } /* End forever */
294 }
295 #endif
296
297
298 #if CONFIG_XMODEM_SEND
299 /**
300  * \brief Transmit some data using the XModem protocol.
301  *
302  * \param port Serial port to use for transfer
303  * \param fd Source file
304  *
305  * \note This function allocates a large amount of stack for
306  *       the XModem transfer buffer (\see XM_BUFSIZE).
307  */
308 bool xmodem_send(struct Serial *port, KFile *fd)
309 {
310         char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */
311         size_t size = -1;
312         int blocknr = 1, retries = 0, c, i;
313         bool proceed, usecrc = false;
314         uint16_t crc;
315         uint8_t sum;
316
317         /*
318          * Reading a block can be very slow, so we read the first block early
319          * to avoid receiving double XM_C char.
320          * This could happen if we check for XM_C and then read the block, giving
321          * the receiving device time to send another XM_C char misinterpretating
322          * the blocks sent.
323          */
324         size = kfile_read(fd, block_buffer, XM_BUFSIZE);
325
326         kfile_clearerr(&port->fd);
327         ser_purge(port);
328         XMODEM_PROGRESS("Wait remote host\n");
329
330         for(;;)
331         {
332                 proceed = false;
333                 do
334                 {
335                         if (XMODEM_CHECK_ABORT)
336                                 return false;
337
338                         switch (c = kfile_getc(&port->fd))
339                         {
340                         case XM_NAK:
341                                 XMODEM_PROGRESS("Resend blk %d\n", blocknr);
342                                 proceed = true;
343                                 break;
344
345                         case XM_C:
346                                 if (c == XM_C)
347                                 {
348                                         XMODEM_PROGRESS("Tx start (CRC)\n");
349                                         usecrc = true;
350                                 }
351                                 else
352                                         XMODEM_PROGRESS("Tx start (BCC)\n");
353
354                                 proceed = true;
355                                 break;
356
357                         case XM_ACK:
358                                 /* End of transfer? */
359                                 if (!size)
360                                         return true;
361
362                                 /* Call user function to read in one block */
363                                 size = kfile_read(fd, block_buffer, XM_BUFSIZE);
364                                 XMODEM_PROGRESS("Send blk %d\n", blocknr);
365                                 blocknr++;
366                                 retries = 0;
367                                 proceed = true;
368                                 break;
369
370                         case EOF:
371                                 kfile_clearerr(&port->fd);
372                                 retries++;
373                                 XMODEM_PROGRESS("Retries %d\n", retries);
374                                 if (retries <= CONFIG_XMODEM_MAXRETRIES)
375                                         break;
376                                 /* falling through! */
377
378                         case XM_CAN:
379                                 XMODEM_PROGRESS("Transfer aborted\n");
380                                 return false;
381
382                         default:
383                                 XMODEM_PROGRESS("Skipping garbage\n");
384                                 break;
385                         }
386                 }
387                 while (!proceed);
388
389                 if (!size)
390                 {
391                         kfile_putc(XM_EOT, &port->fd);
392                         continue;
393                 }
394
395                 /* Pad block with 0xFF if it's partially full */
396                 memset(block_buffer + size, 0xFF, XM_BUFSIZE - size);
397
398                 /* Send block header (STX, blocknr, ~blocknr) */
399                 #if XM_BUFSIZE == 128
400                         kfile_putc(XM_SOH, &port->fd);
401                 #else
402                         kfile_putc(XM_STX, &port->fd);
403                 #endif
404                 kfile_putc(blocknr & 0xFF, &port->fd);
405                 kfile_putc(~blocknr & 0xFF, &port->fd);
406
407                 /* Send block and compute its CRC/checksum */
408                 sum = 0;
409                 crc = 0;
410                 for (i = 0; i < XM_BUFSIZE; i++)
411                 {
412                         kfile_putc(block_buffer[i], &port->fd);
413                         crc = UPDCRC16(block_buffer[i], crc);
414                         sum += block_buffer[i];
415                 }
416
417                 /* Send CRC/Checksum */
418                 if (usecrc)
419                 {
420                         crc = UPDCRC16(0, crc);
421                         crc = UPDCRC16(0, crc);
422                         kfile_putc(crc >> 8, &port->fd);
423                         kfile_putc(crc & 0xFF, &port->fd);
424                 }
425                 else
426                         kfile_putc(sum, &port->fd);
427         }
428 }
429 #endif